From 7cd5c82cbdb7851043f15028179f2e604eabed22 Mon Sep 17 00:00:00 2001 From: Prudhvi Yarlagadda Date: Wed, 21 Oct 2020 22:47:31 +0530 Subject: [PATCH] serial: msm_geni_serial: Pull RFR before stop_rx for uart Pull the RFR line before doing stop_rx in uart driver. This is to prevent any rx data to come into the uart rx fifo when stop_rx is being executed. Remove the rx length zero check in handling dma rx to prevent race conditions with EOT and DMA_DONE interrupt bits. Issue scenarios could be that during baud rate change/ runtime suspend or any other place where stop_rx is used, there might be a race between rx data entering uart rx fifo and availability of dma buffers in uart driver. In this scenarios there will be interrupt storm with rx water mark interrupt. Change-Id: I92163c4997c1709269dce67a1d492533f710abfb Signed-off-by: Prudhvi Yarlagadda --- drivers/tty/serial/msm_geni_serial.c | 71 +++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index 6d7e6fd35397..de9cef364dd2 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -101,6 +101,8 @@ #define UART_OVERSAMPLING (32) #define STALE_TIMEOUT (16) +#define STALE_COUNT (DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT) +#define SEC_TO_USEC (1000000) #define DEFAULT_BITS_PER_CHAR (10) #define GENI_UART_NR_PORTS (6) #define GENI_UART_CONS_PORTS (1) @@ -1397,8 +1399,11 @@ static void msm_geni_serial_set_manual_flow(bool enable, SE_UART_MANUAL_RFR); /* Ensure that the manual flow off writes go through */ mb(); + uart_manual_rfr = geni_read_reg_nolog(port->uport.membase, + SE_UART_MANUAL_RFR); IPC_LOG_MSG(port->ipc_log_misc, - "%s: Manual Flow Disabled, HW Flow ON\n", __func__); + "%s: Manual Flow Disabled, HW Flow ON rfr = 0x%x\n", + __func__, uart_manual_rfr); } } @@ -1409,6 +1414,7 @@ static int stop_rx_sequencer(struct uart_port *uport) struct msm_geni_serial_port *port = GET_DEV_PORT(uport); unsigned long flags = 0; bool is_rx_active; + unsigned int stale_delay; IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__); @@ -1421,9 +1427,18 @@ static int stop_rx_sequencer(struct uart_port *uport) return 0; } - port->s_cmd_done = false; - port->s_cmd = true; - reinit_completion(&port->s_cmd_timeout); + if (!uart_console(uport)) { + msm_geni_serial_set_manual_flow(false, port); + /* + * Wait for the stale timeout to happen if there + * is any data pending in the rx fifo. + * Have a safety factor of 2 to include the interrupt + * and system latencies. + */ + stale_delay = (STALE_COUNT * SEC_TO_USEC) / port->cur_baud; + stale_delay = (2 * stale_delay); + udelay(stale_delay); + } IPC_LOG_MSG(port->ipc_log_misc, "%s: Start 0x%x\n", __func__, geni_status); @@ -1432,6 +1447,11 @@ static int stop_rx_sequencer(struct uart_port *uport) * cancel command as this might be in an atomic context. */ is_irq_masked = msm_serial_try_disable_interrupts(uport); + + port->s_cmd_done = false; + port->s_cmd = true; + reinit_completion(&port->s_cmd_timeout); + geni_cancel_s_cmd(uport->membase); /* @@ -1494,6 +1514,9 @@ static int stop_rx_sequencer(struct uart_port *uport) port->s_cmd = false; exit_rx_seq: + if (!uart_console(uport)) + msm_geni_serial_set_manual_flow(true, port); + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); IPC_LOG_MSG(port->ipc_log_misc, "%s: End 0x%x\n", __func__, geni_status); @@ -1889,7 +1912,6 @@ static bool handle_tx_dma_xfer(u32 m_irq_status, struct uart_port *uport) static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) { - u32 rx_dma_len; bool ret = false; bool drop_rx = false; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); @@ -1926,16 +1948,13 @@ static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport) dma_rx_status & RX_DMA_DONE) { msm_geni_serial_handle_dma_rx(uport, drop_rx); - rx_dma_len = - geni_read_reg_nolog(uport->membase, - SE_DMA_RX_LEN_IN); - if (rx_dma_len != 0) { + if (!(dma_rx_status & RX_GENI_CANCEL_IRQ)) { geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE, &msm_port->rx_dma); } else { IPC_LOG_MSG(msm_port->ipc_log_misc, - "%s.stop dma Rx 0x%x\n", - __func__, dma_rx_status); + "%s. not mapping rx dma\n", + __func__); } } if (dma_rx_status & RX_SBE) { @@ -2174,7 +2193,7 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); unsigned long cfg0, cfg1; dma_addr_t dma_address; - unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; + unsigned int rxstale = STALE_COUNT; set_rfr_wm(msm_port); geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT); @@ -2465,7 +2484,6 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, __func__, ret); return; } - msm_geni_serial_set_manual_flow(false, port); } msm_geni_serial_stop_rx(uport); /* baud rate */ @@ -2512,9 +2530,6 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, msm_geni_serial_termios_cfg(uport, termios); IPC_LOG_MSG(port->ipc_log_misc, "%s: baud %d\n", __func__, baud); exit_set_termios: - if (!uart_console(uport)) - msm_geni_serial_set_manual_flow(true, port); - msm_geni_serial_start_rx(uport); if (!uart_console(uport)) msm_geni_serial_power_off(uport); @@ -3197,6 +3212,23 @@ static int msm_geni_serial_remove(struct platform_device *pdev) return 0; } +static void msm_geni_serial_allow_rx(struct msm_geni_serial_port *port) +{ + u32 uart_manual_rfr; + + uart_manual_rfr = (UART_MANUAL_RFR_EN | UART_RFR_READY); + geni_write_reg_nolog(uart_manual_rfr, port->uport.membase, + SE_UART_MANUAL_RFR); + /* Ensure that the manual flow off writes go through */ + mb(); + uart_manual_rfr = geni_read_reg_nolog(port->uport.membase, + SE_UART_MANUAL_RFR); + IPC_LOG_MSG(port->ipc_log_misc, "%s(): rfr = 0x%x\n", + __func__, uart_manual_rfr); + + /* To give control of RFR back to HW */ + msm_geni_serial_set_manual_flow(true, port); +} #ifdef CONFIG_PM static int msm_geni_serial_runtime_suspend(struct device *dev) @@ -3226,6 +3258,13 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) stop_tx_sequencer(&port->uport); disable_irq(port->uport.irq); + + /* + * Above stop_rx disabled the flow so we need to enable it here + * Make sure wake up interrupt is enabled before RFR is made low + */ + msm_geni_serial_allow_rx(port); + ret = se_geni_resources_off(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret);