| From b027ce258369cbfa88401a691c23dad01deb9f9b Mon Sep 17 00:00:00 2001 |
| From: Jeffrey Hugo <jeffrey.l.hugo@gmail.com> |
| Date: Mon, 21 Oct 2019 08:46:16 -0700 |
| Subject: tty: serial: msm_serial: Fix flow control |
| |
| From: Jeffrey Hugo <jeffrey.l.hugo@gmail.com> |
| |
| commit b027ce258369cbfa88401a691c23dad01deb9f9b upstream. |
| |
| 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> |
| Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> |
| Cc: stable <stable@vger.kernel.org> |
| Reviewed-by: Andy Gross <agross@kernel.org> |
| Link: https://lore.kernel.org/r/[email protected] |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/tty/serial/msm_serial.c | 6 +++++- |
| 1 file changed, 5 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/tty/serial/msm_serial.c |
| +++ b/drivers/tty/serial/msm_serial.c |
| @@ -321,6 +321,7 @@ static unsigned int msm_get_mctrl(struct |
| static void msm_reset(struct uart_port *port) |
| { |
| struct msm_port *msm_port = UART_TO_MSM(port); |
| + unsigned int mr; |
| |
| /* reset everything */ |
| msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); |
| @@ -328,7 +329,10 @@ static void msm_reset(struct uart_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); |
| + mr = msm_read(port, UART_MR1); |
| + mr &= ~UART_MR1_RX_RDY_CTL; |
| + msm_write(port, mr, UART_MR1); |
| |
| /* Disable DM modes */ |
| if (msm_port->is_uartdm) |