diff mbox series

[1/8] serial: core: only get RS485 termination gpio if supported

Message ID 20220622154659.8710-2-LinoSanfilippo@gmx.de (mailing list archive)
State New, archived
Headers show
Series Fixes and cleanup for RS485 | expand

Commit Message

Lino Sanfilippo June 22, 2022, 3:46 p.m. UTC
From: Lino Sanfilippo <l.sanfilippo@kunbus.com>

In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
termination is supported by the driver.

Signed-off-by: Lino Sanfilippo <l.sanfilippo@kunbus.com>
---
 drivers/tty/serial/serial_core.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

Comments

Andy Shevchenko June 22, 2022, 5:04 p.m. UTC | #1
On Wed, Jun 22, 2022 at 05:46:52PM +0200, Lino Sanfilippo wrote:
> From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
> 
> In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
> termination is supported by the driver.

I'm not sure I got the usefulness of this change.
We request GPIO line as optional, so if one is defined it in the DT/ACPI, then
they probably want to (opportunistically) have it.

The certain driver may or may not utilize this GPIO.

With your change it's possible to have a DTS where GPIO line defined in a
broken way and user won't ever know about it, if they are using platforms
without termination support.
Lino Sanfilippo June 23, 2022, 1:59 a.m. UTC | #2
Hi,

On 22.06.22 at 19:04, Andy Shevchenko wrote:
> On Wed, Jun 22, 2022 at 05:46:52PM +0200, Lino Sanfilippo wrote:
>> From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
>>
>> In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
>> termination is supported by the driver.
>
> I'm not sure I got the usefulness of this change.
> We request GPIO line as optional, so if one is defined it in the DT/ACPI, then
> they probably want to (opportunistically) have it>
>
> With your change it's possible to have a DTS where GPIO line defined in a
> broken way and user won't ever know about it, if they are using platforms
> without termination support.
>

This behavior is not introduced with this patch, also in the current code the driver
wont inform the user if it does not make use erroneous defined termination GPIO.

This patch at least prevents the driver from allocating and holding a GPIO descriptor across
the drivers lifetime that will never be used.

Furthermore it simplifies the code in patch 2 when we want to set the GPIO, since we can
skip the check whether or not the termination GPIO is supported by the driver.



Regards,
Lino
Andy Shevchenko June 23, 2022, 9:45 a.m. UTC | #3
On Thu, Jun 23, 2022 at 4:00 AM Lino Sanfilippo <LinoSanfilippo@gmx.de> wrote:
> On 22.06.22 at 19:04, Andy Shevchenko wrote:
> > On Wed, Jun 22, 2022 at 05:46:52PM +0200, Lino Sanfilippo wrote:
> >> From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
> >>
> >> In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
> >> termination is supported by the driver.
> >
> > I'm not sure I got the usefulness of this change.
> > We request GPIO line as optional, so if one is defined it in the DT/ACPI, then
> > they probably want to (opportunistically) have it>
> >
> > With your change it's possible to have a DTS where GPIO line defined in a
> > broken way and user won't ever know about it, if they are using platforms
> > without termination support.
>
> This behavior is not introduced with this patch, also in the current code the driver
> wont inform the user if it does not make use erroneous defined termination GPIO.

It does. If a previously stale GPIO resource may have deferred a probe
and hence one may debug why the driver is not working, after this
change one may put a stale GPIO resource into DT/ACPI and have nothing
in the result. Meaning the change relaxes validation which I consider
is not good.

> This patch at least prevents the driver from allocating and holding a GPIO descriptor across
> the drivers lifetime that will never be used.

But it's not your issue, if DTS defines it, so the platform has an
idea about its usage.

> Furthermore it simplifies the code in patch 2 when we want to set the GPIO, since we can
> skip the check whether or not the termination GPIO is supported by the driver.

That's fine.
Lino Sanfilippo June 23, 2022, 4:08 p.m. UTC | #4
On 23.06.22 at 11:45, Andy Shevchenko wrote:
> On Thu, Jun 23, 2022 at 4:00 AM Lino Sanfilippo <LinoSanfilippo@gmx.de> wrote:
>> On 22.06.22 at 19:04, Andy Shevchenko wrote:
>>> On Wed, Jun 22, 2022 at 05:46:52PM +0200, Lino Sanfilippo wrote:
>>>> From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
>>>>
>>>> In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
>>>> termination is supported by the driver.
>>>
>>> I'm not sure I got the usefulness of this change.
>>> We request GPIO line as optional, so if one is defined it in the DT/ACPI, then
>>> they probably want to (opportunistically) have it>
>>>
>>> With your change it's possible to have a DTS where GPIO line defined in a
>>> broken way and user won't ever know about it, if they are using platforms
>>> without termination support.
>>
>> This behavior is not introduced with this patch, also in the current code the driver
>> wont inform the user if it does not make use erroneous defined termination GPIO.
>
> It does. If a previously stale GPIO resource may have deferred a probe
> and hence one may debug why the driver is not working, after this
> change one may put a stale GPIO resource into DT/ACPI and have nothing
> in the result. Meaning the change relaxes validation which I consider
> is not good.
>

Ok I see the point. So what about changing it to:

	if (port->rs485_term_gpio &&
	    !(port->rs485_supported->flags & SER_RS485_TERMINATE_BUS)) {
		dev_warn(port->dev,
			"%s (%d): RS485 termination gpio not supported by driver\n",
			port->name, port->line);
		devm_gpiod_put(dev, port->rs485_term_gpio);
		port->rs485_term_gpio = NULL;
	}

This would also be consistent to the warnings we print in uart_sanitize_serial_rs485() for invalid
RS485 settings.


Regards,
Lino
Andy Shevchenko June 23, 2022, 4:32 p.m. UTC | #5
On Thu, Jun 23, 2022 at 06:08:56PM +0200, Lino Sanfilippo wrote:
> On 23.06.22 at 11:45, Andy Shevchenko wrote:
> > On Thu, Jun 23, 2022 at 4:00 AM Lino Sanfilippo <LinoSanfilippo@gmx.de> wrote:
> >> On 22.06.22 at 19:04, Andy Shevchenko wrote:
> >>> On Wed, Jun 22, 2022 at 05:46:52PM +0200, Lino Sanfilippo wrote:
> >>>> From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
> >>>>
> >>>> In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
> >>>> termination is supported by the driver.
> >>>
> >>> I'm not sure I got the usefulness of this change.
> >>> We request GPIO line as optional, so if one is defined it in the DT/ACPI, then
> >>> they probably want to (opportunistically) have it>
> >>>
> >>> With your change it's possible to have a DTS where GPIO line defined in a
> >>> broken way and user won't ever know about it, if they are using platforms
> >>> without termination support.
> >>
> >> This behavior is not introduced with this patch, also in the current code the driver
> >> wont inform the user if it does not make use erroneous defined termination GPIO.
> >
> > It does. If a previously stale GPIO resource may have deferred a probe
> > and hence one may debug why the driver is not working, after this
> > change one may put a stale GPIO resource into DT/ACPI and have nothing
> > in the result. Meaning the change relaxes validation which I consider
> > is not good.
> >
> 
> Ok I see the point. So what about changing it to:

You mean adding below after the existing code in the module?

> 	if (port->rs485_term_gpio &&
> 	    !(port->rs485_supported->flags & SER_RS485_TERMINATE_BUS)) {
> 		dev_warn(port->dev,
> 			"%s (%d): RS485 termination gpio not supported by driver\n",
> 			port->name, port->line);
> 		devm_gpiod_put(dev, port->rs485_term_gpio);
> 		port->rs485_term_gpio = NULL;
> 	}
> 
> This would also be consistent to the warnings we print in uart_sanitize_serial_rs485() for invalid
> RS485 settings.

Probably it's okay, but I dunno we have much on this to gain. Users may start
complaining of this (harmless) warning. I leave it to others to comment.
Lino Sanfilippo June 23, 2022, 8:19 p.m. UTC | #6
On 23.06.22 at 18:32, Andy Shevchenko wrote:

>>
>> Ok I see the point. So what about changing it to:
>
> You mean adding below after the existing code in the module?

Right, to be more precise between getting the gpio and the error check:


	port->rs485_term_gpio = devm_gpiod_get_optional(dev, "rs485-term",
							GPIOD_OUT_LOW);

	if (port->rs485_term_gpio &&
	    !(port->rs485_supported->flags & SER_RS485_TERMINATE_BUS)) {
		dev_warn(port->dev,
			"%s (%d): RS485 termination gpio not supported by driver\n",
			port->name, port->line);
		devm_gpiod_put(dev, port->rs485_term_gpio);
		port->rs485_term_gpio = NULL;
	}

	if (IS_ERR(port->rs485_term_gpio)) {
		ret = PTR_ERR(port->rs485_term_gpio);
		port->rs485_term_gpio = NULL;
		return dev_err_probe(dev, ret, "Cannot get rs485-term-gpios\n");
	}

Regards,
Lino

>
>> 	if (port->rs485_term_gpio &&
>> 	    !(port->rs485_supported->flags & SER_RS485_TERMINATE_BUS)) {
>> 		dev_warn(port->dev,
>> 			"%s (%d): RS485 termination gpio not supported by driver\n",
>> 			port->name, port->line);
>> 		devm_gpiod_put(dev, port->rs485_term_gpio);
>> 		port->rs485_term_gpio = NULL;
>> 	}
>>
>> This would also be consistent to the warnings we print in uart_sanitize_serial_rs485() for invalid
>> RS485 settings.
>
> Probably it's okay, but I dunno we have much on this to gain. Users may start
> complaining of this (harmless) warning. I leave it to others to comment.
>
Ilpo Järvinen June 27, 2022, 9:05 a.m. UTC | #7
On Sat, 25 Jun 2022, Lukas Wunner wrote:

> On Wed, Jun 22, 2022 at 05:46:52PM +0200, Lino Sanfilippo wrote:
> > In uart_get_rs485_mode() only try to get a termination GPIO if RS485 bus
> > termination is supported by the driver.
> [...]
> > --- a/drivers/tty/serial/serial_core.c
> > +++ b/drivers/tty/serial/serial_core.c
> > @@ -3384,17 +3384,20 @@ int uart_get_rs485_mode(struct uart_port *port)
> >  		rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
> >  	}
> >  
> > -	/*
> > -	 * Disabling termination by default is the safe choice:  Else if many
> > -	 * bus participants enable it, no communication is possible at all.
> > -	 * Works fine for short cables and users may enable for longer cables.
> > -	 */
> > -	port->rs485_term_gpio = devm_gpiod_get_optional(dev, "rs485-term",
> > -							GPIOD_OUT_LOW);
> > -	if (IS_ERR(port->rs485_term_gpio)) {
> > -		ret = PTR_ERR(port->rs485_term_gpio);
> > -		port->rs485_term_gpio = NULL;
> > -		return dev_err_probe(dev, ret, "Cannot get rs485-term-gpios\n");
> > +	if (port->rs485_supported->flags & SER_RS485_TERMINATE_BUS) {
> 
> So I think linux-next commit be2e2cb1d281 ("serial: Sanitize rs485_struct")
> contains a mistake in that it forces drivers to set SER_RS485_TERMINATE_BUS
> in their rs485_supported->flags to allow enabling bus termination.
> 
> That's wrong because *every* rs485-capable driver can enable bus
> termination if a GPIO has been defined for that in the DT.

Do you mean every em485 using driver? Otherwise I don't see this "forces 
drivers to set" happening anywhere in the code?

You're partially right because there are other bugs in this area such 
as the one you propose a fix below. While I was making the sanitization 
series, I entirely missed some parts related to termination because 
SER_RS485_TERMINATE_BUS is seemingly not set/handled correctly by the 
core.

Another thing that looks a bug is that on subsequent call to TIOCSRS485, 
w/o SER_RS485_TERMINATE_BUS nothing happens (for non-em485 driver, that 
is)? It seems to be taken care by 2/8 of this series though, I think. But 
it should be properly marked as Fixes: ... in that case although nobody 
has complained about it so likely not a huge issue to anyone.

> In fact, another commit which was applied as part of the same series,
> ebe2cf736a04 ("serial: pl011: Fill in rs485_supported") does not set
> SER_RS485_TERMINATE_BUS in amba-pl011.c's flags and thus forbids the
> driver from enabling bus termination, even though we know there are
> products out there which support bus termination on the pl011 through
> a GPIO (Revolution Pi RevPi Compact, Revpi Flat).
>
> I think what you want to do is amend uart_get_rs485_mode() to set
> SER_RS485_TERMINATE_BUS in port->rs485_supported_flags if a GPIO
> was found in the DT.  Instead of the change proposed above.

That seems appropriate (and is a fix).

What makes it a bit complicated though is that it's a pointer currently
and what it points to is shared per driver (besides being const):
	const struct serial_rs485       *rs485_supported;
While it could be embedded into uart_port, there's the .padding which we 
might not want to bloat uart_port with. Perhaps create non-uapi struct 
kserial_rs485 w/o .padding and add static_assert()s to ensure the 
layout is identical to serial_rs485?
Lino Sanfilippo July 2, 2022, 4:50 p.m. UTC | #8
Hi,

On 27.06.22 11:05, Ilpo Järvinen wrote:
> On Sat, 25 Jun 2022, Lukas Wunner wrote:

>>
>> I think what you want to do is amend uart_get_rs485_mode() to set
>> SER_RS485_TERMINATE_BUS in port->rs485_supported_flags if a GPIO
>> was found in the DT.  Instead of the change proposed above.

Agreed.

>
> That seems appropriate (and is a fix).
>
> What makes it a bit complicated though is that it's a pointer currently
> and what it points to is shared per driver (besides being const):
> 	const struct serial_rs485       *rs485_supported;
> While it could be embedded into uart_port, there's the .padding which we
> might not want to bloat uart_port with. Perhaps create non-uapi struct
> kserial_rs485 w/o .padding and add static_assert()s to ensure the
> layout is identical to serial_rs485?
>
>

This seems to be indeed the cleanest solution.

Regards,
Lino
diff mbox series

Patch

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 1368b0ef7d7f..015f4e1da647 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -3384,17 +3384,20 @@  int uart_get_rs485_mode(struct uart_port *port)
 		rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
 	}
 
-	/*
-	 * Disabling termination by default is the safe choice:  Else if many
-	 * bus participants enable it, no communication is possible at all.
-	 * Works fine for short cables and users may enable for longer cables.
-	 */
-	port->rs485_term_gpio = devm_gpiod_get_optional(dev, "rs485-term",
-							GPIOD_OUT_LOW);
-	if (IS_ERR(port->rs485_term_gpio)) {
-		ret = PTR_ERR(port->rs485_term_gpio);
-		port->rs485_term_gpio = NULL;
-		return dev_err_probe(dev, ret, "Cannot get rs485-term-gpios\n");
+	if (port->rs485_supported->flags & SER_RS485_TERMINATE_BUS) {
+		/*
+		 * Disabling termination by default is the safe choice:  Else if
+		 * many bus participants enable it, no communication is possible
+		 * at all. Works fine for short cables and users may enable for
+		 * longer cables.
+		 */
+		port->rs485_term_gpio = devm_gpiod_get_optional(dev, "rs485-term",
+								GPIOD_OUT_LOW);
+		if (IS_ERR(port->rs485_term_gpio)) {
+			ret = PTR_ERR(port->rs485_term_gpio);
+			port->rs485_term_gpio = NULL;
+			return dev_err_probe(dev, ret, "Cannot get rs485-term-gpios\n");
+		}
 	}
 
 	return 0;