diff mbox series

tty: serial: msm_serial: Fix flow control

Message ID 20191019210616.41199-1-jeffrey.l.hugo@gmail.com (mailing list archive)
State Superseded
Headers show
Series tty: serial: msm_serial: Fix flow control | expand

Commit Message

Jeffrey Hugo Oct. 19, 2019, 9:06 p.m. UTC
hci_qca interfaces to the wcn3990 via a uart_dm on the msm8998 mtp and
Lenovo Miix 630 laptop.  As part of initializing the wcn3990, hci_qca
disables flow, configures the uart baudrate, and then reenables flow - at
which point an event is expected to be received over the uart from the
wcn3990.  It is observed that this event comes after the baudrate change
but before hci_qca re-enables flow. This is unexpected, and is a result of
msm_reset() being broken.

According to the uart_dm hardware documentation, it is recommended that
automatic hardware flow control be enabled by setting RX_RDY_CTL.  Auto
hw flow control will manage RFR based on the configured watermark.  When
there is space to receive data, the hw will assert RFR.  When the watermark
is hit, the hw will de-assert RFR.

The hardware documentation indicates that RFR can me manually managed via
CR when RX_RDY_CTL is not set.  SET_RFR asserts RFR, and RESET_RFR
de-asserts RFR.

msm_reset() is broken because after resetting the hardware, it
unconditionally asserts RFR via SET_RFR.  This enables flow regardless of
the current configuration, and would undo a previous flow disable
operation.  It should instead de-assert RFR via RESET_RFR to block flow
until the hardware is reconfigured.  msm_serial should rely on the client
to specify that flow should be enabled, either via mctrl() or the termios
structure, and only assert RFR in response to those triggers.

Fixes: 04896a77a97b ("msm_serial: serial driver for MSM7K onboard serial peripheral.")
Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
---
 drivers/tty/serial/msm_serial.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Bjorn Andersson Oct. 20, 2019, 6:28 a.m. UTC | #1
On Sat 19 Oct 14:06 PDT 2019, Jeffrey Hugo wrote:

> hci_qca interfaces to the wcn3990 via a uart_dm on the msm8998 mtp and
> Lenovo Miix 630 laptop.  As part of initializing the wcn3990, hci_qca
> disables flow, configures the uart baudrate, and then reenables flow - at
> which point an event is expected to be received over the uart from the
> wcn3990.  It is observed that this event comes after the baudrate change
> but before hci_qca re-enables flow. This is unexpected, and is a result of
> msm_reset() being broken.
> 
> According to the uart_dm hardware documentation, it is recommended that
> automatic hardware flow control be enabled by setting RX_RDY_CTL.  Auto
> hw flow control will manage RFR based on the configured watermark.  When
> there is space to receive data, the hw will assert RFR.  When the watermark
> is hit, the hw will de-assert RFR.
> 
> The hardware documentation indicates that RFR can me manually managed via
> CR when RX_RDY_CTL is not set.  SET_RFR asserts RFR, and RESET_RFR
> de-asserts RFR.
> 
> msm_reset() is broken because after resetting the hardware, it
> unconditionally asserts RFR via SET_RFR.  This enables flow regardless of
> the current configuration, and would undo a previous flow disable
> operation.  It should instead de-assert RFR via RESET_RFR to block flow
> until the hardware is reconfigured.  msm_serial should rely on the client
> to specify that flow should be enabled, either via mctrl() or the termios
> structure, and only assert RFR in response to those triggers.
> 

I traced msm_reset() and msm_set_mctrl() and I get the following:
       swapper/0-1     [000] d..1     3.091726: msm_set_mctrl: msm_set_mctrl() reset rfr
       swapper/0-1     [000] d..1     3.117046: msm_set_mctrl: msm_set_mctrl() reset rfr
       swapper/0-1     [000] d..1     3.125484: msm_set_termios: msm_reset() set rfr
       swapper/0-1     [003] d..1     4.491430: msm_set_termios: msm_reset() set rfr
       swapper/0-1     [003] d..1     4.491733: msm_set_mctrl: msm_set_mctrl() auto rfr
           login-313   [001] d..1    78.010785: msm_set_mctrl: msm_set_mctrl() reset rfr
           login-313   [001] d..1    78.011007: msm_set_termios: msm_reset() set rfr
           login-313   [001] d..1    78.011021: msm_set_mctrl: msm_set_mctrl() auto rfr
           login-313   [001] d..1    78.063330: msm_set_termios: msm_reset() set rfr
           login-313   [001] d..1    78.063641: msm_set_termios: msm_reset() set rfr

So while your change does make sense for BT, wouldn't this mean that
with your patch and a 4-pin UART for the console I would end this dance
with receive flow blocked?

Regards,
Bjorn

> Fixes: 04896a77a97b ("msm_serial: serial driver for MSM7K onboard serial peripheral.")
> Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
> ---
>  drivers/tty/serial/msm_serial.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
> index 3657a24913fc..aedabf7646f1 100644
> --- a/drivers/tty/serial/msm_serial.c
> +++ b/drivers/tty/serial/msm_serial.c
> @@ -987,7 +987,7 @@ static void msm_reset(struct uart_port *port)
>  	msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
>  	msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
>  	msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
> -	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
> +	msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR);
>  
>  	/* Disable DM modes */
>  	if (msm_port->is_uartdm)
> -- 
> 2.17.1
>
Jeffrey Hugo Oct. 21, 2019, 3:19 p.m. UTC | #2
On Sun, Oct 20, 2019 at 12:28 AM Bjorn Andersson
<bjorn.andersson@linaro.org> wrote:
>
> On Sat 19 Oct 14:06 PDT 2019, Jeffrey Hugo wrote:
>
> > hci_qca interfaces to the wcn3990 via a uart_dm on the msm8998 mtp and
> > Lenovo Miix 630 laptop.  As part of initializing the wcn3990, hci_qca
> > disables flow, configures the uart baudrate, and then reenables flow - at
> > which point an event is expected to be received over the uart from the
> > wcn3990.  It is observed that this event comes after the baudrate change
> > but before hci_qca re-enables flow. This is unexpected, and is a result of
> > msm_reset() being broken.
> >
> > According to the uart_dm hardware documentation, it is recommended that
> > automatic hardware flow control be enabled by setting RX_RDY_CTL.  Auto
> > hw flow control will manage RFR based on the configured watermark.  When
> > there is space to receive data, the hw will assert RFR.  When the watermark
> > is hit, the hw will de-assert RFR.
> >
> > The hardware documentation indicates that RFR can me manually managed via
> > CR when RX_RDY_CTL is not set.  SET_RFR asserts RFR, and RESET_RFR
> > de-asserts RFR.
> >
> > msm_reset() is broken because after resetting the hardware, it
> > unconditionally asserts RFR via SET_RFR.  This enables flow regardless of
> > the current configuration, and would undo a previous flow disable
> > operation.  It should instead de-assert RFR via RESET_RFR to block flow
> > until the hardware is reconfigured.  msm_serial should rely on the client
> > to specify that flow should be enabled, either via mctrl() or the termios
> > structure, and only assert RFR in response to those triggers.
> >
>
> I traced msm_reset() and msm_set_mctrl() and I get the following:
>        swapper/0-1     [000] d..1     3.091726: msm_set_mctrl: msm_set_mctrl() reset rfr
>        swapper/0-1     [000] d..1     3.117046: msm_set_mctrl: msm_set_mctrl() reset rfr
>        swapper/0-1     [000] d..1     3.125484: msm_set_termios: msm_reset() set rfr
>        swapper/0-1     [003] d..1     4.491430: msm_set_termios: msm_reset() set rfr
>        swapper/0-1     [003] d..1     4.491733: msm_set_mctrl: msm_set_mctrl() auto rfr
>            login-313   [001] d..1    78.010785: msm_set_mctrl: msm_set_mctrl() reset rfr
>            login-313   [001] d..1    78.011007: msm_set_termios: msm_reset() set rfr
>            login-313   [001] d..1    78.011021: msm_set_mctrl: msm_set_mctrl() auto rfr
>            login-313   [001] d..1    78.063330: msm_set_termios: msm_reset() set rfr
>            login-313   [001] d..1    78.063641: msm_set_termios: msm_reset() set rfr
>
> So while your change does make sense for BT, wouldn't this mean that
> with your patch and a 4-pin UART for the console I would end this dance
> with receive flow blocked?

No.  I don't think it occurred to you to consider how RX_RDY_CTL
factors into this.  RX_RDY_CTL allows the hardware to enable flow when
the hardware determines it is able to receive data.  RX_RDY_CTL gets
set in mctrl, and set_termios - essentially when the uart client has
indicated flow can be enabled.

Even though the console is a 2 wire console on msm8998, the driver
will go through the same flow.  I verified that RX_RDY_CTL is set on
the console uart after boot, so we won't end up with flow blocked.

However, this does bring up another issue.  RX_RDY_CTL doesn't get
unset in msm_reset.  RESET_RX will disable the rx path in the
hardware, and prevent RX_RDY_CTL from being active, but the rx path is
enabled in set_baud_rate() before RX_RDY_CTL can be masked out in
set_termios, so there may be a small window where flow can
inadvertently enabled.  I'll spin a v2 that also unsets RX_RDY_CTL in
msm_reset().

>
> Regards,
> Bjorn
>
> > Fixes: 04896a77a97b ("msm_serial: serial driver for MSM7K onboard serial peripheral.")
> > Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
> > ---
> >  drivers/tty/serial/msm_serial.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
> > index 3657a24913fc..aedabf7646f1 100644
> > --- a/drivers/tty/serial/msm_serial.c
> > +++ b/drivers/tty/serial/msm_serial.c
> > @@ -987,7 +987,7 @@ static void msm_reset(struct uart_port *port)
> >       msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
> >       msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
> >       msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
> > -     msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
> > +     msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR);
> >
> >       /* Disable DM modes */
> >       if (msm_port->is_uartdm)
> > --
> > 2.17.1
> >
Bjorn Andersson Oct. 21, 2019, 4:24 p.m. UTC | #3
On Mon 21 Oct 08:19 PDT 2019, Jeffrey Hugo wrote:

> On Sun, Oct 20, 2019 at 12:28 AM Bjorn Andersson
> <bjorn.andersson@linaro.org> wrote:
> >
> > On Sat 19 Oct 14:06 PDT 2019, Jeffrey Hugo wrote:
> >
> > > hci_qca interfaces to the wcn3990 via a uart_dm on the msm8998 mtp and
> > > Lenovo Miix 630 laptop.  As part of initializing the wcn3990, hci_qca
> > > disables flow, configures the uart baudrate, and then reenables flow - at
> > > which point an event is expected to be received over the uart from the
> > > wcn3990.  It is observed that this event comes after the baudrate change
> > > but before hci_qca re-enables flow. This is unexpected, and is a result of
> > > msm_reset() being broken.
> > >
> > > According to the uart_dm hardware documentation, it is recommended that
> > > automatic hardware flow control be enabled by setting RX_RDY_CTL.  Auto
> > > hw flow control will manage RFR based on the configured watermark.  When
> > > there is space to receive data, the hw will assert RFR.  When the watermark
> > > is hit, the hw will de-assert RFR.
> > >
> > > The hardware documentation indicates that RFR can me manually managed via
> > > CR when RX_RDY_CTL is not set.  SET_RFR asserts RFR, and RESET_RFR
> > > de-asserts RFR.
> > >
> > > msm_reset() is broken because after resetting the hardware, it
> > > unconditionally asserts RFR via SET_RFR.  This enables flow regardless of
> > > the current configuration, and would undo a previous flow disable
> > > operation.  It should instead de-assert RFR via RESET_RFR to block flow
> > > until the hardware is reconfigured.  msm_serial should rely on the client
> > > to specify that flow should be enabled, either via mctrl() or the termios
> > > structure, and only assert RFR in response to those triggers.
> > >
> >
> > I traced msm_reset() and msm_set_mctrl() and I get the following:
> >        swapper/0-1     [000] d..1     3.091726: msm_set_mctrl: msm_set_mctrl() reset rfr
> >        swapper/0-1     [000] d..1     3.117046: msm_set_mctrl: msm_set_mctrl() reset rfr
> >        swapper/0-1     [000] d..1     3.125484: msm_set_termios: msm_reset() set rfr
> >        swapper/0-1     [003] d..1     4.491430: msm_set_termios: msm_reset() set rfr
> >        swapper/0-1     [003] d..1     4.491733: msm_set_mctrl: msm_set_mctrl() auto rfr
> >            login-313   [001] d..1    78.010785: msm_set_mctrl: msm_set_mctrl() reset rfr
> >            login-313   [001] d..1    78.011007: msm_set_termios: msm_reset() set rfr
> >            login-313   [001] d..1    78.011021: msm_set_mctrl: msm_set_mctrl() auto rfr
> >            login-313   [001] d..1    78.063330: msm_set_termios: msm_reset() set rfr
> >            login-313   [001] d..1    78.063641: msm_set_termios: msm_reset() set rfr
> >
> > So while your change does make sense for BT, wouldn't this mean that
> > with your patch and a 4-pin UART for the console I would end this dance
> > with receive flow blocked?
> 
> No.  I don't think it occurred to you to consider how RX_RDY_CTL
> factors into this.  RX_RDY_CTL allows the hardware to enable flow when
> the hardware determines it is able to receive data.  RX_RDY_CTL gets
> set in mctrl, and set_termios - essentially when the uart client has
> indicated flow can be enabled.
> 

I see, so per above trace when login does it's rfr toggling RX_RDY_CTL
is already set (and will be maintained), so we'll allow flow at each
part of the process.

> Even though the console is a 2 wire console on msm8998, the driver
> will go through the same flow.  I verified that RX_RDY_CTL is set on
> the console uart after boot, so we won't end up with flow blocked.
> 
> However, this does bring up another issue.  RX_RDY_CTL doesn't get
> unset in msm_reset.  RESET_RX will disable the rx path in the
> hardware, and prevent RX_RDY_CTL from being active, but the rx path is
> enabled in set_baud_rate() before RX_RDY_CTL can be masked out in
> set_termios, so there may be a small window where flow can
> inadvertently enabled.  I'll spin a v2 that also unsets RX_RDY_CTL in
> msm_reset().
> 

Yes, that sounds reasonable. And for the console case the difference
will be that we deassert RFR while the serial is being reset and
reconfigured; which sounds like the right thing to do.

Regards,
Bjorn

> >
> > Regards,
> > Bjorn
> >
> > > Fixes: 04896a77a97b ("msm_serial: serial driver for MSM7K onboard serial peripheral.")
> > > Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
> > > ---
> > >  drivers/tty/serial/msm_serial.c | 2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
> > > index 3657a24913fc..aedabf7646f1 100644
> > > --- a/drivers/tty/serial/msm_serial.c
> > > +++ b/drivers/tty/serial/msm_serial.c
> > > @@ -987,7 +987,7 @@ static void msm_reset(struct uart_port *port)
> > >       msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
> > >       msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
> > >       msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
> > > -     msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
> > > +     msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR);
> > >
> > >       /* Disable DM modes */
> > >       if (msm_port->is_uartdm)
> > > --
> > > 2.17.1
> > >
diff mbox series

Patch

diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 3657a24913fc..aedabf7646f1 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -987,7 +987,7 @@  static void msm_reset(struct uart_port *port)
 	msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
 	msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
 	msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
-	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR);
 
 	/* Disable DM modes */
 	if (msm_port->is_uartdm)