From 97603680f9ae5dba08ff3d688b64de3bd587d2da Mon Sep 17 00:00:00 2001 From: Akash Asthana Date: Mon, 27 Apr 2020 17:09:04 +0530 Subject: [PATCH] serial: Propagate missing UART fix 4.19 to 5.4 msm_geni_serial: Use the common driver API for dma_alloc msm: msm-geni-se: Make DFS clock table specific to SE of QUP serial: msm_geni_serial: Don't depend on clock freq table to set baud rate msm-geni-serial: Correct the interrupt polling logic in uart serial: msm_geni_serial: Improve IPC logging in UART driver serial: msm_geni_serial: drop port->lock before tty_flip_buffer_push() call serial: msm_geni_serial: No need to stop_tx/rx on UART shutdown serial: msm_geni_serial: Do not check_transfers_inflight() post port_close serial: msm_geni_serial: Enable IRQ from port startup serial: msm_geni_serial: Fix geni_wait_for_cmd_done timeouts serial: msm_geni_serial: Do not drop port->lock in polling mode Also, fix stackoverflow issue seen after propagating above changes. Console TX is getting stuck in recursive call and leading to stackoverflow issue. Steps that leads to bad recursion call: 1) msm_geni_serial_handle_tx calls for stop_tx if all the data are sent and acknowledged. 2) stop_tx issue cancel command if the main sequencer is still active. 3) As part of cancel command execution msm_geni_serial_handle_tx is called again that takes us to step 1 and goes in a loop. To fix above scenario remove the stop_tx call from geni_serial_handle_tx because if the data is transmited and acknowledged main sequencer should automatically go to inactive state. Ideally we should never enter into this situation the first place because after receiving CMD_DONE we expect that sequencer will go IDLE/inactive. So, probably this can be a race btw updation of CMD_DONE and GENI_STATUS register because after adding some more IPC logs in the above code path we are not hitting this issue. It might be adding required delay. Change-Id: I85cfe87170ea347c4b8d98c858cc762da413624f Signed-off-by: Prudhvi Yarlagadda Signed-off-by: Chandana Kishori Chiluveru Signed-off-by: Mukesh Kumar Savaliya Signed-off-by: Akash Asthana --- drivers/platform/msm/msm-geni-se.c | 28 +- drivers/tty/serial/msm_geni_serial.c | 964 ++++++++++++++++++--------- include/linux/msm-geni-se.h | 13 +- 3 files changed, 675 insertions(+), 330 deletions(-) diff --git a/drivers/platform/msm/msm-geni-se.c b/drivers/platform/msm/msm-geni-se.c index 3437be6ac037..9b831e52d056 100644 --- a/drivers/platform/msm/msm-geni-se.c +++ b/drivers/platform/msm/msm-geni-se.c @@ -68,8 +68,6 @@ struct bus_vectors { * @bus_bw_set_noc: Clock plan for DDR path. * @cur_bus_bw_idx: Current index within the bus clock plan. * @cur_bus_bw_idx_noc: Current index within the DDR path clock plan. - * @num_clk_levels: Number of valid clock levels in clk_perf_tbl. - * @clk_perf_tbl: Table of clock frequency input to Serial Engine clock. * @log_ctx: Logging context to hold the debug information. * @vectors: Structure to store Master End and Slave End IDs for QUPv3 clock and DDR path bus BW request. @@ -101,8 +99,6 @@ struct geni_se_device { unsigned long *bus_bw_set_noc; int cur_bus_bw_idx; int cur_bus_bw_idx_noc; - unsigned int num_clk_levels; - unsigned long *clk_perf_tbl; void *log_ctx; struct bus_vectors *vectors; int num_paths; @@ -1118,31 +1114,31 @@ int geni_se_clk_tbl_get(struct se_geni_rsc *rsc, unsigned long **tbl) mutex_lock(&geni_se_dev->geni_dev_lock); *tbl = NULL; - if (geni_se_dev->clk_perf_tbl) { - *tbl = geni_se_dev->clk_perf_tbl; - ret = geni_se_dev->num_clk_levels; + if (rsc->clk_perf_tbl) { + *tbl = rsc->clk_perf_tbl; + ret = rsc->num_clk_levels; goto exit_se_clk_tbl_get; } - geni_se_dev->clk_perf_tbl = kzalloc(sizeof(*geni_se_dev->clk_perf_tbl) * + rsc->clk_perf_tbl = kzalloc(sizeof(*rsc->clk_perf_tbl) * MAX_CLK_PERF_LEVEL, GFP_KERNEL); - if (!geni_se_dev->clk_perf_tbl) { + if (!rsc->clk_perf_tbl) { ret = -ENOMEM; goto exit_se_clk_tbl_get; } for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) { - geni_se_dev->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk, + rsc->clk_perf_tbl[i] = clk_round_rate(rsc->se_clk, prev_freq + 1); - if (geni_se_dev->clk_perf_tbl[i] == prev_freq) { - geni_se_dev->clk_perf_tbl[i] = 0; + if (rsc->clk_perf_tbl[i] == prev_freq) { + rsc->clk_perf_tbl[i] = 0; break; } - prev_freq = geni_se_dev->clk_perf_tbl[i]; + prev_freq = rsc->clk_perf_tbl[i]; } - geni_se_dev->num_clk_levels = i; - *tbl = geni_se_dev->clk_perf_tbl; - ret = geni_se_dev->num_clk_levels; + rsc->num_clk_levels = i; + *tbl = rsc->clk_perf_tbl; + ret = rsc->num_clk_levels; exit_se_clk_tbl_get: mutex_unlock(&geni_se_dev->geni_dev_lock); return ret; diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index 0f43b939a4db..9c12bf965e90 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -113,11 +114,30 @@ #define WAIT_XFER_MIN_TIMEOUT_US (9000) #define IPC_LOG_PWR_PAGES (6) #define IPC_LOG_MISC_PAGES (10) -#define IPC_LOG_TX_RX_PAGES (8) +#define IPC_LOG_TX_RX_PAGES (10) #define DATA_BYTES_PER_LINE (32) -#define IPC_LOG_MSG(ctx, x...) ipc_log_string(ctx, x) +#define M_IRQ_BITS (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN |\ + M_CMD_CANCEL_EN | M_CMD_ABORT_EN) +#define S_IRQ_BITS (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN |\ + S_CMD_CANCEL_EN | S_CMD_ABORT_EN) +#define DMA_TX_IRQ_BITS (TX_RESET_DONE | TX_DMA_DONE |\ + TX_GENI_CANCEL_IRQ) +#define DMA_RX_IRQ_BITS (RX_EOT | RX_GENI_CANCEL_IRQ |\ + RX_RESET_DONE | UART_DMA_RX_ERRS |\ + UART_DMA_RX_PARITY_ERR | UART_DMA_RX_BREAK |\ + RX_DMA_DONE) +/* Required for polling for 100 msecs */ +#define POLL_WAIT_TIMEOUT_MSEC 100 + +/* + * Number of iterrations required while polling + * where each iterration has a delay of 100 usecs + */ +#define POLL_ITERATIONS 1000 + +#define IPC_LOG_MSG(ctx, x...) ipc_log_string(ctx, x) #define DMA_RX_BUF_SIZE (2048) #define UART_CONSOLE_RX_WM (2) @@ -131,7 +151,7 @@ struct msm_geni_serial_ver_info { struct msm_geni_serial_port { struct uart_port uport; - char name[20]; + const char *name; unsigned int tx_fifo_depth; unsigned int tx_fifo_width; unsigned int rx_fifo_depth; @@ -171,6 +191,12 @@ struct msm_geni_serial_port { bool startup_in_progress; bool is_console; bool rumi_platform; + bool m_cmd_done; + bool s_cmd_done; + bool m_cmd; + bool s_cmd; + struct completion m_cmd_timeout; + struct completion s_cmd_timeout; }; static const struct uart_ops msm_geni_serial_pops; @@ -202,6 +228,159 @@ static int uart_line_id; static struct msm_geni_serial_port msm_geni_console_port; static struct msm_geni_serial_port msm_geni_serial_ports[GENI_UART_NR_PORTS]; +static void msm_geni_serial_handle_isr(struct uart_port *uport, + unsigned long *flags, bool is_irq_masked); + +/* + * The below API is required to check if uport->lock (spinlock) + * is taken by the serial layer or not. If the lock is not taken + * then we can rely on the isr to be fired and if the lock is taken + * by the serial layer then we need to poll for the interrupts. + * + * Returns true(1) if spinlock is already taken by framework (serial layer) + * Return false(0) if spinlock is not taken by framework. + */ +static bool msm_geni_serial_spinlocked(struct uart_port *uport) +{ + unsigned long flags; + bool locked; + + locked = spin_trylock_irqsave(&uport->lock, flags); + if (locked) + spin_unlock_irqrestore(&uport->lock, flags); + + return !locked; +} + +/* + * We are enabling the interrupts once the polling operations + * is completed. + */ +static void msm_geni_serial_enable_interrupts(struct uart_port *uport) +{ + unsigned int geni_m_irq_en, geni_s_irq_en; + unsigned int dma_m_irq_en, dma_s_irq_en; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + + geni_m_irq_en = geni_read_reg_nolog(uport->membase, + SE_GENI_M_IRQ_EN); + geni_s_irq_en = geni_read_reg_nolog(uport->membase, + SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + dma_m_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_EN); + dma_s_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_EN); + } + + geni_m_irq_en |= M_IRQ_BITS; + geni_s_irq_en |= S_IRQ_BITS; + if (port->xfer_mode == SE_DMA) { + dma_m_irq_en |= DMA_TX_IRQ_BITS; + dma_s_irq_en |= DMA_RX_IRQ_BITS; + } + + geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN); + geni_write_reg_nolog(geni_s_irq_en, uport->membase, SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + geni_write_reg_nolog(dma_m_irq_en, uport->membase, + SE_DMA_TX_IRQ_EN); + geni_write_reg_nolog(dma_s_irq_en, uport->membase, + SE_DMA_RX_IRQ_EN); + } +} + +/* Try disabling interrupts in order to do polling in an atomic contexts. */ +static bool msm_serial_try_disable_interrupts(struct uart_port *uport) +{ + unsigned int geni_m_irq_en, geni_s_irq_en; + unsigned int dma_m_irq_en, dma_s_irq_en; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + + /* + * We don't need to disable interrupts if spinlock is not taken + * by framework as we can rely on ISR. + */ + if (!msm_geni_serial_spinlocked(uport)) + return false; + + geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN); + geni_s_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + dma_m_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_EN); + dma_s_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_EN); + } + + geni_m_irq_en &= ~M_IRQ_BITS; + geni_s_irq_en &= ~S_IRQ_BITS; + if (port->xfer_mode == SE_DMA) { + dma_m_irq_en &= ~DMA_TX_IRQ_BITS; + dma_s_irq_en &= ~DMA_RX_IRQ_BITS; + } + + geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN); + geni_write_reg_nolog(geni_s_irq_en, uport->membase, SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + geni_write_reg_nolog(dma_m_irq_en, uport->membase, + SE_DMA_TX_IRQ_EN); + geni_write_reg_nolog(dma_s_irq_en, uport->membase, + SE_DMA_RX_IRQ_EN); + } + + return true; +} + +/* + * We need to poll for interrupt if we are in an atomic context + * as serial framework might be taking spinlocks and depend on the isr + * in a non-atomic context. This API decides wheather to poll for + * interrupt or depend on the isr based on in_atomic() call. + */ +static bool geni_wait_for_cmd_done(struct uart_port *uport, bool is_irq_masked) +{ + struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); + unsigned long timeout = POLL_ITERATIONS; + unsigned long flags = 0; + + /* + * We need to do polling if spinlock is taken + * by framework as we cannot rely on ISR. + */ + if (is_irq_masked) { + /* + * Polling is done for 1000 iterrations with + * 10 usecs interval which in total accumulates + * to 10 msecs + */ + if (msm_port->m_cmd) { + while (!msm_port->m_cmd_done && timeout > 0) { + msm_geni_serial_handle_isr(uport, &flags, true); + timeout--; + udelay(100); + } + } else if (msm_port->s_cmd) { + while (!msm_port->s_cmd_done && timeout > 0) { + msm_geni_serial_handle_isr(uport, &flags, true); + timeout--; + udelay(100); + } + } + } else { + /* Waiting for 10 milli second for interrupt to be fired */ + if (msm_port->m_cmd) + timeout = wait_for_completion_timeout + (&msm_port->m_cmd_timeout, + msecs_to_jiffies(POLL_WAIT_TIMEOUT_MSEC)); + else if (msm_port->s_cmd) + timeout = wait_for_completion_timeout + (&msm_port->s_cmd_timeout, + msecs_to_jiffies(POLL_WAIT_TIMEOUT_MSEC)); + } + + return timeout ? 0 : 1; +} static void msm_geni_serial_config_port(struct uart_port *uport, int cfg_flags) { @@ -293,6 +472,12 @@ static void wait_for_transfers_inflight(struct uart_port *uport) { int iter = 0; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + unsigned int geni_status; + + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + /* Possible stop rx is called before this. */ + if (!(geni_status & S_GENI_CMD_ACTIVE)) + return; while (iter < WAIT_XFER_MAX_ITER) { if (check_transfers_inflight(uport)) { @@ -513,7 +698,6 @@ static int msm_geni_serial_power_on(struct uart_port *uport) } else { pm_runtime_get_noresume(uport->dev); pm_runtime_set_active(uport->dev); - enable_irq(uport->irq); } pm_runtime_enable(uport->dev); if (lock) @@ -558,7 +742,6 @@ static int msm_geni_serial_poll_bit(struct uart_port *uport, unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS; unsigned long total_iter = 1000; - if (uport->private_data && !uart_console(uport)) { port = GET_DEV_PORT(uport); baud = (port->cur_baud ? port->cur_baud : 115200); @@ -599,57 +782,33 @@ static void msm_geni_serial_setup_tx(struct uart_port *uport, mb(); } -static void msm_geni_serial_poll_cancel_tx(struct uart_port *uport) +static void msm_geni_serial_poll_tx_done(struct uart_port *uport) { int done = 0; - unsigned int irq_clear = M_CMD_DONE_EN; + unsigned int irq_clear = 0; done = msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_DONE_EN, true); if (!done) { - geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase, - SE_GENI_M_CMD_CTRL_REG); - irq_clear |= M_CMD_ABORT_EN; - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); + /* + * Failure IPC logs are not added as this API is + * used by early console and it doesn't have log handle. + */ + geni_write_reg(M_GENI_CMD_CANCEL, uport->membase, + SE_GENI_M_CMD_CTRL_REG); + done = msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_CANCEL_EN, true); + if (!done) { + geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase, + SE_GENI_M_CMD_CTRL_REG); + msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + } } + irq_clear = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_STATUS); geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR); } -static void msm_geni_serial_abort_rx(struct uart_port *uport) -{ - unsigned int irq_clear = S_CMD_DONE_EN; - - geni_abort_s_cmd(uport->membase); - /* Ensure this goes through before polling. */ - mb(); - irq_clear |= S_CMD_ABORT_EN; - msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, - S_GENI_CMD_ABORT, false); - geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR); - geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG); -} - -static void msm_geni_serial_complete_rx_eot(struct uart_port *uport) -{ - int poll_done = 0, tries = 0; - struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - - do { - poll_done = msm_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT, - RX_EOT, true); - tries++; - } while (!poll_done && tries < 5); - - if (!poll_done) - IPC_LOG_MSG(port->ipc_log_misc, - "%s: RX_EOT, GENI:0x%x, DMA_DEBUG:0x%x\n", __func__, - geni_read_reg_nolog(uport->membase, SE_GENI_STATUS), - geni_read_reg_nolog(uport->membase, SE_DMA_DEBUG_REG0)); - else - geni_write_reg_nolog(RX_EOT, uport->membase, SE_DMA_RX_IRQ_CLR); -} - #ifdef CONFIG_CONSOLE_POLL static int msm_geni_serial_get_char(struct uart_port *uport) { @@ -703,7 +862,9 @@ static void msm_geni_serial_poll_put_char(struct uart_port *uport, * Ensure FIFO write goes through before polling for status but. */ mb(); - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); } #endif @@ -740,6 +901,7 @@ __msm_geni_serial_console_write(struct uart_port *uport, const char *s, SE_GENI_TX_WATERMARK_REG); msm_geni_serial_setup_tx(uport, bytes_to_send); i = 0; + while (i < count) { u32 chars_to_write = 0; u32 avail_fifo_bytes = (fifo_depth - tx_wm); @@ -764,7 +926,9 @@ __msm_geni_serial_console_write(struct uart_port *uport, const char *s, mb(); i += chars_to_write; } - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); } static void msm_geni_serial_console_write(struct console *co, const char *s, @@ -775,6 +939,8 @@ static void msm_geni_serial_console_write(struct console *co, const char *s, bool locked = true; unsigned long flags; unsigned int geni_status; + bool timeout; + bool is_irq_masked; int irq_en; /* Max 1 port supported as of now */ @@ -793,24 +959,45 @@ static void msm_geni_serial_console_write(struct console *co, const char *s, geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); /* Cancel the current write to log the fault */ - if (!locked) { + if ((geni_status & M_GENI_CMD_ACTIVE) && !locked) { + port->m_cmd_done = false; + port->m_cmd = true; + reinit_completion(&port->m_cmd_timeout); + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_m_cmd(uport->membase); - if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { + + /* + * console should be in polling mode. Hence directly pass true + * as argument for wait_for_cmd_done here to handle cancel tx + * in polling mode. + */ + timeout = geni_wait_for_cmd_done(uport, true); + if (timeout) { + IPC_LOG_MSG(port->console_log, + "%s: tx_cancel failed 0x%x\n", + __func__, geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); + + reinit_completion(&port->m_cmd_timeout); geni_abort_m_cmd(uport->membase); - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + timeout = geni_wait_for_cmd_done(uport, true); + if (timeout) + IPC_LOG_MSG(port->console_log, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); } - writel_relaxed(M_CMD_CANCEL_EN, uport->membase + - SE_GENI_M_IRQ_CLEAR); + + msm_geni_serial_enable_interrupts(uport); + port->m_cmd = false; } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->cur_tx_remaining) { /* It seems we can interrupt existing transfers unless all data * has been sent, in which case we need to look for done first. */ - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); /* Enable WM interrupt for every new console write op */ if (uart_circ_chars_pending(&uport->state->xmit)) { @@ -865,8 +1052,6 @@ static int handle_rx_console(struct uart_port *uport, tty_insert_flip_char(tport, rx_char[c], flag); } } - if (!drop_rx) - tty_flip_buffer_push(tport); return 0; } #else @@ -886,6 +1071,8 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); struct circ_buf *xmit = &uport->state->xmit; unsigned int xmit_size; + unsigned int dma_dbg; + bool timeout, is_irq_masked; int ret = 0; xmit_size = uart_circ_chars_pending(xmit); @@ -903,27 +1090,82 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) msm_geni_serial_setup_tx(uport, xmit_size); ret = geni_se_tx_dma_prep(msm_port->wrapper_dev, uport->membase, &xmit->buf[xmit->tail], xmit_size, &msm_port->tx_dma); + if (!ret) { msm_port->xmit_size = xmit_size; } else { - geni_write_reg_nolog(0, uport->membase, - SE_UART_TX_TRANS_LEN); + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: TX DMA map Fail %d\n", __func__, ret); + + geni_write_reg_nolog(0, uport->membase, SE_UART_TX_TRANS_LEN); + msm_port->m_cmd_done = false; + msm_port->m_cmd = true; + reinit_completion(&msm_port->m_cmd_timeout); + + /* + * Try disabling interrupts before giving the + * cancel command as this might be in an atomic context. + */ + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_m_cmd(uport->membase); - if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(msm_port->console_log, + "%s: tx_cancel fail 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: tx_cancel failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + + msm_port->m_cmd_done = false; + reinit_completion(&msm_port->m_cmd_timeout); + /* Give abort command as cancel command failed */ geni_abort_m_cmd(uport->membase); - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + + timeout = geni_wait_for_cmd_done(uport, + is_irq_masked); + if (timeout) { + IPC_LOG_MSG(msm_port->console_log, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); + } + } + + if (msm_port->xfer_mode == SE_DMA) { + dma_dbg = geni_read_reg(uport->membase, + SE_DMA_DEBUG_REG0); + if (dma_dbg & DMA_TX_ACTIVE) { + msm_port->m_cmd_done = false; + reinit_completion(&msm_port->m_cmd_timeout); + geni_write_reg_nolog(1, uport->membase, + SE_DMA_TX_FSM_RST); + + timeout = geni_wait_for_cmd_done(uport, + is_irq_masked); + if (timeout) + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: tx fsm reset failed\n", __func__); + } + + if (msm_port->tx_dma) { + geni_se_tx_dma_unprep(msm_port->wrapper_dev, + msm_port->tx_dma, msm_port->xmit_size); + msm_port->tx_dma = (dma_addr_t)NULL; + } } - geni_write_reg_nolog(M_CMD_CANCEL_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); - IPC_LOG_MSG(msm_port->ipc_log_tx, "%s: DMA map failure %d\n", - __func__, ret); - msm_port->tx_dma = (dma_addr_t)NULL; msm_port->xmit_size = 0; + /* Enable the interrupts once the cancel operation is done. */ + msm_geni_serial_enable_interrupts(uport); + msm_port->m_cmd = false; } + return ret; } @@ -986,64 +1228,80 @@ exit_start_tx: msm_geni_serial_power_off(uport); } -static void msm_geni_serial_tx_fsm_rst(struct uart_port *uport) -{ - unsigned int tx_irq_en; - int done = 0; - int tries = 0; - - tx_irq_en = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_EN); - geni_write_reg_nolog(0, uport->membase, SE_DMA_TX_IRQ_EN_SET); - geni_write_reg_nolog(1, uport->membase, SE_DMA_TX_FSM_RST); - do { - done = msm_geni_serial_poll_bit(uport, SE_DMA_TX_IRQ_STAT, - TX_RESET_DONE, true); - tries++; - } while (!done && tries < 5); - geni_write_reg_nolog(TX_DMA_DONE | TX_RESET_DONE, uport->membase, - SE_DMA_TX_IRQ_CLR); - geni_write_reg_nolog(tx_irq_en, uport->membase, SE_DMA_TX_IRQ_EN_SET); -} - static void stop_tx_sequencer(struct uart_port *uport) { - unsigned int geni_m_irq_en; unsigned int geni_status; + bool timeout, is_irq_masked; + unsigned int dma_dbg; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN); - geni_m_irq_en &= ~M_CMD_DONE_EN; - if (port->xfer_mode == FIFO_MODE) { - geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN; - geni_write_reg_nolog(0, uport->membase, - SE_GENI_TX_WATERMARK_REG); - } else if (port->xfer_mode == SE_DMA) { - if (port->tx_dma) { - msm_geni_serial_tx_fsm_rst(uport); - geni_se_tx_dma_unprep(port->wrapper_dev, port->tx_dma, - port->xmit_size); - port->tx_dma = (dma_addr_t)NULL; - } - } - port->xmit_size = 0; - geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN); - geni_status = geni_read_reg_nolog(uport->membase, - SE_GENI_STATUS); + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop tx is called multiple times. */ if (!(geni_status & M_GENI_CMD_ACTIVE)) return; + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Start GENI: 0x%x\n", __func__, geni_status); + + port->m_cmd_done = false; + port->m_cmd = true; + reinit_completion(&port->m_cmd_timeout); + /* + * Try to mask the interrupts before giving the + * cancel command as this might be in an atomic context + * from framework driver. + */ + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_m_cmd(uport->membase); - if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(port->console_log, "%s: tx_cancel failed 0x%x\n", + __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + IPC_LOG_MSG(port->ipc_log_misc, "%s: tx_cancel failed 0x%x\n", + __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + + port->m_cmd_done = false; + reinit_completion(&port->m_cmd_timeout); geni_abort_m_cmd(uport->membase); - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(port->console_log, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + IPC_LOG_MSG(port->ipc_log_misc, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + } } - geni_write_reg_nolog(M_CMD_CANCEL_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + + if (port->xfer_mode == SE_DMA) { + dma_dbg = geni_read_reg(uport->membase, SE_DMA_DEBUG_REG0); + if (dma_dbg & DMA_TX_ACTIVE) { + port->m_cmd_done = false; + reinit_completion(&port->m_cmd_timeout); + geni_write_reg_nolog(1, uport->membase, + SE_DMA_TX_FSM_RST); + + timeout = geni_wait_for_cmd_done(uport, + is_irq_masked); + if (timeout) + IPC_LOG_MSG(port->ipc_log_misc, + "%s: tx fsm reset failed\n", __func__); + } + + if (port->tx_dma) { + geni_se_tx_dma_unprep(port->wrapper_dev, + port->tx_dma, port->xmit_size); + port->tx_dma = (dma_addr_t)NULL; + } + } + /* Unmask the interrupts once the cancel operation is done. */ + msm_geni_serial_enable_interrupts(uport); + port->m_cmd = false; + port->xmit_size = 0; + /* * If we end up having to cancel an on-going Tx for non-console usecase * then it means there was some unsent data in the Tx FIFO, consequently @@ -1055,7 +1313,10 @@ static void stop_tx_sequencer(struct uart_port *uport) IPC_LOG_MSG(port->ipc_log_misc, "%s:Removing vote\n", __func__); msm_geni_serial_power_off(uport); } - IPC_LOG_MSG(port->ipc_log_misc, "%s:\n", __func__); + + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, "%s: End GENI:0x%x\n", + __func__, geni_status); } static void msm_geni_serial_stop_tx(struct uart_port *uport) @@ -1073,8 +1334,6 @@ static void msm_geni_serial_stop_tx(struct uart_port *uport) static void start_rx_sequencer(struct uart_port *uport) { - unsigned int geni_s_irq_en; - unsigned int geni_m_irq_en; unsigned int geni_status; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); u32 geni_se_param = UART_PARAM_RFR_OPEN; @@ -1083,6 +1342,9 @@ static void start_rx_sequencer(struct uart_port *uport) return; geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x\n", + __func__, geni_status); + if (geni_status & S_GENI_CMD_ACTIVE) { if (port->xfer_mode == SE_DMA) { IPC_LOG_MSG(port->ipc_log_misc, @@ -1094,29 +1356,14 @@ static void start_rx_sequencer(struct uart_port *uport) } /* Start RX with the RFR_OPEN to keep RFR in always ready state */ + msm_geni_serial_enable_interrupts(uport); geni_setup_s_cmd(uport->membase, UART_START_READ, geni_se_param); - if (port->xfer_mode == FIFO_MODE) { - geni_s_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_S_IRQ_EN); - geni_m_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_M_IRQ_EN); - - geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; - geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; - - geni_write_reg_nolog(geni_s_irq_en, uport->membase, - SE_GENI_S_IRQ_EN); - geni_write_reg_nolog(geni_m_irq_en, uport->membase, - SE_GENI_M_IRQ_EN); - } else if (port->xfer_mode == SE_DMA) { + if (port->xfer_mode == SE_DMA) geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE, &port->rx_dma); - } - /* - * Ensure the writes to the secondary sequencer and interrupt enables - * go through. - */ + + /* Ensure that the above writes go through */ mb(); geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x, dma_dbg:0x%x\n", __func__, @@ -1176,79 +1423,104 @@ static void msm_geni_serial_set_manual_flow(bool enable, * doing a stop_rx. */ mb(); - IPC_LOG_MSG(port->ipc_log_pwr, + IPC_LOG_MSG(port->ipc_log_misc, "%s: Manual Flow Enabled, HW Flow OFF\n", __func__); } else { geni_write_reg_nolog(0, port->uport.membase, SE_UART_MANUAL_RFR); /* Ensure that the manual flow off writes go through */ mb(); - IPC_LOG_MSG(port->ipc_log_pwr, + IPC_LOG_MSG(port->ipc_log_misc, "%s: Manual Flow Disabled, HW Flow ON\n", __func__); } } static void stop_rx_sequencer(struct uart_port *uport) { - unsigned int geni_s_irq_en; - unsigned int geni_m_irq_en; unsigned int geni_status; + bool timeout, is_irq_masked; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - u32 irq_clear = S_CMD_CANCEL_EN; - bool done; + unsigned long flags = 0; IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__); - if (port->xfer_mode == FIFO_MODE) { - geni_s_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_S_IRQ_EN); - geni_m_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_M_IRQ_EN); - geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); - geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); - - geni_write_reg_nolog(geni_s_irq_en, uport->membase, - SE_GENI_S_IRQ_EN); - geni_write_reg_nolog(geni_m_irq_en, uport->membase, - SE_GENI_M_IRQ_EN); - } geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop rx is called multiple times. */ - if (!(geni_status & S_GENI_CMD_ACTIVE)) + if (!(geni_status & S_GENI_CMD_ACTIVE)) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s: RX is Inactive, geni_sts: 0x%x\n", + __func__, geni_status); goto exit_rx_seq; + } + port->s_cmd_done = false; + port->s_cmd = true; + reinit_completion(&port->s_cmd_timeout); + + IPC_LOG_MSG(port->ipc_log_misc, "%s: Start 0x%x\n", + __func__, geni_status); + /* + * Try disabling interrupts before giving the + * cancel command as this might be in an atomic context. + */ + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_s_cmd(uport->membase); + /* * Ensure that the cancel goes through before polling for the * cancel control bit. */ mb(); - if (!uart_console(uport)) - msm_geni_serial_complete_rx_eot(uport); + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + bool is_rx_active; - done = msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, - S_GENI_CMD_CANCEL, false); - if (done) { - geni_write_reg_nolog(irq_clear, uport->membase, - SE_GENI_S_IRQ_CLEAR); - goto exit_rx_seq; - } else { - IPC_LOG_MSG(port->ipc_log_misc, "%s Cancel fail 0x%x\n", - __func__, geni_status); - } + geni_status = geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS); + /* + * Possible that stop_rx is called from system resume context + * for console usecase. In early resume, irq remains disabled + * in the system. call msm_geni_serial_handle_isr to clear + * the interrupts. + */ + is_rx_active = geni_status & S_GENI_CMD_ACTIVE; + IPC_LOG_MSG(port->ipc_log_misc, + "%s cancel failed is_rx_active:%d 0x%x\n", + __func__, is_rx_active, geni_status); + IPC_LOG_MSG(port->console_log, + "%s cancel failed is_rx_active:%d 0x%x\n", + __func__, is_rx_active, geni_status); + if (uart_console(uport) && !is_rx_active) { + msm_geni_serial_handle_isr(uport, &flags, true); + goto exit_rx_seq; + } + port->s_cmd_done = false; + reinit_completion(&port->s_cmd_timeout); + geni_abort_s_cmd(uport->membase); + /* Ensure this goes through before polling. */ + mb(); - geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); - if ((geni_status & S_GENI_CMD_ACTIVE)) { - IPC_LOG_MSG(port->ipc_log_misc, "%s:Abort Rx, GENI:0x%x\n", - __func__, geni_status); - msm_geni_serial_abort_rx(uport); + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + geni_status = geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, + "%s abort fail 0x%x\n", __func__, geni_status); + IPC_LOG_MSG(port->console_log, + "%s abort fail 0x%x\n", __func__, geni_status); + } } + /* Enable the interrupts once the cancel operation is done. */ + msm_geni_serial_enable_interrupts(uport); + port->s_cmd = false; + exit_rx_seq: if (port->xfer_mode == SE_DMA && port->rx_dma) msm_geni_serial_rx_fsm_rst(uport); geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); - IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x\n", __func__, geni_status); + IPC_LOG_MSG(port->ipc_log_misc, "%s: End 0x%x\n", + __func__, geni_status); } static void msm_geni_serial_stop_rx(struct uart_port *uport) @@ -1318,7 +1590,7 @@ static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx) rx_last = rx_fifo_status & RX_LAST; if (rx_fifo_wc) ret = port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid, - rx_last, drop_rx); + rx_last, drop_rx); return ret; } @@ -1348,10 +1620,8 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done, pending = uart_circ_chars_pending(xmit); /* All data has been transmitted and acknowledged as received */ - if (!pending && !tx_fifo_status && done) { - msm_geni_serial_stop_tx(uport); + if (!pending && !tx_fifo_status && done) goto exit_handle_tx; - } avail_fifo_bytes = msm_port->tx_fifo_depth - (tx_fifo_status & TX_FIFO_WC); @@ -1432,8 +1702,11 @@ static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx) geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop rx is called */ - if (!(geni_status & S_GENI_CMD_ACTIVE)) + if (!(geni_status & S_GENI_CMD_ACTIVE)) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: GENI: 0x%x\n", __func__, geni_status); return 0; + } if (unlikely(!msm_port->rx_buf)) { IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: NULL Rx_buf\n", @@ -1463,8 +1736,6 @@ static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx) dump_ipc(msm_port->ipc_log_rx, "DMA Rx", (char *)msm_port->rx_buf, 0, rx_bytes); exit_handle_dma_rx: - geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE, - &msm_port->rx_dma); return ret; } @@ -1498,33 +1769,29 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport) return 0; } -static irqreturn_t msm_geni_serial_isr(int isr, void *dev) +static void msm_geni_serial_handle_isr(struct uart_port *uport, + unsigned long *flags, + bool is_irq_masked) { unsigned int m_irq_status; unsigned int s_irq_status; unsigned int dma; unsigned int dma_tx_status; unsigned int dma_rx_status; - struct uart_port *uport = dev; - unsigned long flags; unsigned int m_irq_en; unsigned int geni_status; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); struct tty_port *tport = &uport->state->port; bool drop_rx = false; + bool s_cmd_done = false; + bool m_cmd_done = false; - spin_lock_irqsave(&uport->lock, flags); if (uart_console(uport) && uport->suspended) { IPC_LOG_MSG(msm_port->console_log, "%s. Console in suspend state\n", __func__); goto exit_geni_serial_isr; } - if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) { - dev_err(uport->dev, "%s.Device is suspended.\n", __func__); - IPC_LOG_MSG(msm_port->ipc_log_misc, - "%s.Device is suspended.\n", __func__); - goto exit_geni_serial_isr; - } + m_irq_status = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_STATUS); s_irq_status = geni_read_reg_nolog(uport->membase, @@ -1532,16 +1799,13 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev) if (uart_console(uport)) IPC_LOG_MSG(msm_port->console_log, "%s. sirq 0x%x mirq:0x%x\n", __func__, s_irq_status, - m_irq_status); + m_irq_status); + + geni_write_reg_nolog(m_irq_status, uport->membase, + SE_GENI_M_IRQ_CLEAR); + geni_write_reg_nolog(s_irq_status, uport->membase, + SE_GENI_S_IRQ_CLEAR); m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN); - dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN); - dma_tx_status = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_STAT); - dma_rx_status = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_STAT); - geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); - - geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR); - geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR); - if ((m_irq_status & M_ILLEGAL_CMD_EN)) { WARN_ON(1); goto exit_geni_serial_isr; @@ -1555,49 +1819,88 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev) __func__, s_irq_status, uport->icount.buf_overrun); } + dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN); if (!dma) { + geni_status = geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS); + if ((m_irq_status & m_irq_en) & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) msm_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN, geni_status & M_GENI_CMD_ACTIVE); - if ((s_irq_status & S_GP_IRQ_0_EN) || - (s_irq_status & S_GP_IRQ_1_EN)) { + if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN)) + m_cmd_done = true; + + if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) { if (s_irq_status & S_GP_IRQ_0_EN) uport->icount.parity++; IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.sirq 0x%x parity:%d\n", __func__, s_irq_status, uport->icount.parity); drop_rx = true; - } else if ((s_irq_status & S_GP_IRQ_2_EN) || - (s_irq_status & S_GP_IRQ_3_EN)) { + } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) { uport->icount.brk++; IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.sirq 0x%x break:%d\n", __func__, s_irq_status, uport->icount.brk); } + /* + * In case of stop_rx handling there is a chance + * for RX data can come in parallel. set drop_rx to + * avoid data push to framework from handle_rx_console() + * API for stop_rx case. + */ + if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN)) { + s_cmd_done = true; + drop_rx = true; + } - if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) || - (s_irq_status & S_RX_FIFO_LAST_EN)) + if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | + S_RX_FIFO_LAST_EN)) { msm_geni_serial_handle_rx(uport, drop_rx); + if (!drop_rx && !is_irq_masked) { + spin_unlock_irqrestore(&uport->lock, *flags); + tty_flip_buffer_push(tport); + spin_lock_irqsave(&uport->lock, *flags); + } else if (!drop_rx) { + tty_flip_buffer_push(tport); + } + } } else { + dma_tx_status = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_STAT); + dma_rx_status = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_STAT); + if (dma_tx_status) { + geni_write_reg_nolog(dma_tx_status, uport->membase, - SE_DMA_TX_IRQ_CLR); + SE_DMA_TX_IRQ_CLR); + if (dma_tx_status & TX_DMA_DONE) msm_geni_serial_handle_dma_tx(uport); + + if (dma_tx_status & (TX_RESET_DONE | + TX_GENI_CANCEL_IRQ)) + m_cmd_done = true; + + if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN)) + m_cmd_done = true; } if (dma_rx_status) { geni_write_reg_nolog(dma_rx_status, uport->membase, - SE_DMA_RX_IRQ_CLR); + SE_DMA_RX_IRQ_CLR); + if (dma_rx_status & RX_RESET_DONE) { IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.Reset done. 0x%x.\n", __func__, dma_rx_status); goto exit_geni_serial_isr; } + if (dma_rx_status & UART_DMA_RX_ERRS) { if (dma_rx_status & UART_DMA_RX_PARITY_ERR) uport->icount.parity++; @@ -1613,12 +1916,52 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev) __func__, dma_rx_status, uport->icount.brk); } - if (dma_rx_status & RX_DMA_DONE) - msm_geni_serial_handle_dma_rx(uport, drop_rx); + + if (dma_rx_status & RX_EOT || + dma_rx_status & RX_DMA_DONE) { + msm_geni_serial_handle_dma_rx(uport, + drop_rx); + if (!(dma_rx_status & RX_GENI_CANCEL_IRQ)) { + geni_se_rx_dma_start(uport->membase, + DMA_RX_BUF_SIZE, &msm_port->rx_dma); + } + } + + if (dma_rx_status & RX_SBE) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Rx Errors. 0x%x\n", + __func__, dma_rx_status); + WARN_ON(1); + } + + if (dma_rx_status & (RX_EOT | RX_GENI_CANCEL_IRQ | + RX_DMA_DONE)) + s_cmd_done = true; + + if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN)) + s_cmd_done = true; } } exit_geni_serial_isr: + if (m_cmd_done) { + msm_port->m_cmd_done = true; + complete(&msm_port->m_cmd_timeout); + } + + if (s_cmd_done) { + msm_port->s_cmd_done = true; + complete(&msm_port->s_cmd_timeout); + } +} + +static irqreturn_t msm_geni_serial_isr(int isr, void *dev) +{ + struct uart_port *uport = dev; + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + msm_geni_serial_handle_isr(uport, &flags, false); spin_unlock_irqrestore(&uport->lock, flags); return IRQ_HANDLED; } @@ -1700,24 +2043,18 @@ static void set_rfr_wm(struct msm_geni_serial_port *port) static void msm_geni_serial_shutdown(struct uart_port *uport) { struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - unsigned long flags; int ret; + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s:\n", __func__); /* Stop the console before stopping the current tx */ if (uart_console(uport)) { console_stop(uport->cons); + disable_irq(uport->irq); } else { msm_geni_serial_power_on(uport); wait_for_transfers_inflight(uport); } - disable_irq(uport->irq); - free_irq(uport->irq, uport); - spin_lock_irqsave(&uport->lock, flags); - msm_geni_serial_stop_tx(uport); - msm_geni_serial_stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); - if (!uart_console(uport)) { if (msm_port->ioctl_count) { int i; @@ -1743,7 +2080,7 @@ static void msm_geni_serial_shutdown(struct uart_port *uport) free_irq(msm_port->wakeup_irq, uport); } } - IPC_LOG_MSG(msm_port->ipc_log_misc, "%s\n", __func__); + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: End\n", __func__); } static int msm_geni_serial_port_setup(struct uart_port *uport) @@ -1777,8 +2114,9 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) goto exit_portsetup; } - msm_port->rx_buf = dma_alloc_coherent(msm_port->wrapper_dev, - DMA_RX_BUF_SIZE, &dma_address, GFP_KERNEL); + msm_port->rx_buf = + geni_se_iommu_alloc_buf(msm_port->wrapper_dev, + &dma_address, DMA_RX_BUF_SIZE); if (!msm_port->rx_buf) { devm_kfree(uport->dev, msm_port->rx_fifo); msm_port->rx_fifo = NULL; @@ -1792,7 +2130,9 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) * it else we could end up in data loss scenarios. */ msm_port->xfer_mode = FIFO_MODE; - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); se_get_packing_config(8, 1, false, &cfg0, &cfg1); geni_write_reg_nolog(cfg0, uport->membase, SE_GENI_TX_PACKING_CFG0); @@ -1804,6 +2144,7 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) geni_write_reg_nolog(cfg1, uport->membase, SE_GENI_RX_PACKING_CFG1); } + ret = geni_se_init(uport->membase, msm_port->rx_wm, msm_port->rx_rfr); if (ret) { dev_err(uport->dev, "%s: Fail\n", __func__); @@ -1824,8 +2165,8 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) return 0; free_dma: if (msm_port->rx_dma) { - dma_free_coherent(msm_port->wrapper_dev, DMA_RX_BUF_SIZE, - msm_port->rx_buf, msm_port->rx_dma); + geni_se_iommu_free_buf(msm_port->wrapper_dev, + &msm_port->rx_dma, msm_port->rx_buf, DMA_RX_BUF_SIZE); msm_port->rx_dma = (dma_addr_t)NULL; } exit_portsetup: @@ -1837,8 +2178,7 @@ static int msm_geni_serial_startup(struct uart_port *uport) int ret = 0; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - scnprintf(msm_port->name, sizeof(msm_port->name), "msm_serial_geni%d", - uport->line); + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s:\n", __func__); msm_port->startup_in_progress = true; @@ -1853,8 +2193,13 @@ static int msm_geni_serial_startup(struct uart_port *uport) get_tx_fifo_size(msm_port); if (!msm_port->port_setup) { - if (msm_geni_serial_port_setup(uport)) + ret = msm_geni_serial_port_setup(uport); + if (ret) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: port_setup Fail ret:%d\n", + __func__, ret); goto exit_startup; + } } /* @@ -1862,13 +2207,16 @@ static int msm_geni_serial_startup(struct uart_port *uport) * before returning to the framework. */ mb(); - ret = request_irq(uport->irq, msm_geni_serial_isr, IRQF_TRIGGER_HIGH, - msm_port->name, uport); - if (unlikely(ret)) { - dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n", - __func__, ret); - goto exit_startup; - } + + /* Console usecase requires irq to be in enable state after early + * console switch from probe to handle RX data. Hence enable IRQ + * from starup and disable it form shutdown APIs for cosnole case. + * BT HSUART usecase, IRQ will be enabled from runtime_resume() + * and disabled in runtime_suspend to avoid spurious interrupts + * after suspend. + */ + if (uart_console(uport)) + enable_irq(uport->irq); if (msm_port->wakeup_irq > 0) { ret = request_irq(msm_port->wakeup_irq, msm_geni_wakeup_isr, @@ -1887,39 +2235,15 @@ static int msm_geni_serial_startup(struct uart_port *uport) goto exit_startup; } } - IPC_LOG_MSG(msm_port->ipc_log_misc, "%s\n", __func__); exit_startup: if (likely(!uart_console(uport))) msm_geni_serial_power_off(&msm_port->uport); msm_port->startup_in_progress = false; + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: ret:%d\n", __func__, ret); return ret; } -static int get_clk_cfg(unsigned long clk_freq, unsigned long *ser_clk) -{ - unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, - 32000000, 48000000, 64000000, 80000000, 96000000, 100000000, - 102400000, 112000000, 120000000, 128000000}; - int i; - int match = -1; - - for (i = 0; i < ARRAY_SIZE(root_freq); i++) { - if (clk_freq > root_freq[i]) - continue; - - if (!(root_freq[i] % clk_freq)) { - match = i; - break; - } - } - if (match != -1) - *ser_clk = root_freq[match]; - else - pr_err("clk_freq %ld\n", clk_freq); - return match; -} - static void geni_serial_write_term_regs(struct uart_port *uport, u32 loopback, u32 tx_trans_cfg, u32 tx_parity_cfg, u32 rx_trans_cfg, u32 rx_parity_cfg, u32 bits_per_char, u32 stop_bit_len, @@ -1946,27 +2270,6 @@ static void geni_serial_write_term_regs(struct uart_port *uport, u32 loopback, geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG); } -static int get_clk_div_rate(unsigned int baud, unsigned long *desired_clk_rate) -{ - unsigned long ser_clk; - int dfs_index; - int clk_div = 0; - - *desired_clk_rate = baud * UART_OVERSAMPLING; - dfs_index = get_clk_cfg(*desired_clk_rate, &ser_clk); - if (dfs_index < 0) { - pr_err("%s: Can't find matching DFS entry for baud %d\n", - __func__, baud); - clk_div = -EINVAL; - goto exit_get_clk_div_rate; - } - - clk_div = ser_clk / *desired_clk_rate; - *desired_clk_rate = ser_clk; -exit_get_clk_div_rate: - return clk_div; -} - static void msm_geni_serial_set_termios(struct uart_port *uport, struct ktermios *termios, struct ktermios *old) { @@ -1977,11 +2280,14 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, unsigned int rx_trans_cfg; unsigned int rx_parity_cfg; unsigned int stop_bit_len; - int clk_div; + int clk_div, ret; unsigned long ser_clk_cfg = 0; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); unsigned long clk_rate; - unsigned long flags; + unsigned long desired_rate; + unsigned int clk_idx; + int uart_sampling; + int clk_freq_diff; /* QUP_2.5.0 and older RUMI has sampling rate as 32 */ if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING) && @@ -2000,25 +2306,38 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, __func__, ret); return; } - disable_irq(uport->irq); msm_geni_serial_set_manual_flow(false, port); } - /* Take a spinlock else stop_rx causes a race with an ISR due to Cancel - * and FSM_RESET. This also has a potential race with the dma_map/unmap - * operations of ISR. - */ - spin_lock_irqsave(&uport->lock, flags); msm_geni_serial_stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); /* baud rate */ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); port->cur_baud = baud; - clk_div = get_clk_div_rate(baud, &clk_rate); + uart_sampling = IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING) ? + UART_OVERSAMPLING / 2 : UART_OVERSAMPLING; + desired_rate = baud * uart_sampling; + + /* + * Request for nearest possible required frequency instead of the exact + * required frequency. + */ + ret = geni_se_clk_freq_match(&port->serial_rsc, desired_rate, + &clk_idx, &clk_rate, false); + if (ret) { + dev_err(uport->dev, "%s: Failed(%d) to find src clk for 0x%x\n", + __func__, ret, baud); + goto exit_set_termios; + } + + clk_div = DIV_ROUND_UP(clk_rate, desired_rate); if (clk_div <= 0) goto exit_set_termios; - if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING)) - clk_div *= 2; + clk_freq_diff = (desired_rate - (clk_rate / clk_div)); + if (clk_freq_diff) + IPC_LOG_MSG(port->ipc_log_misc, + "src_clk freq_diff:%d baud:%d clk_rate:%d clk_div:%d\n", + clk_freq_diff, baud, clk_rate, clk_div); + uport->uartclk = clk_rate; clk_set_rate(port->serial_rsc.se_clk, clk_rate); ser_clk_cfg |= SER_CLK_EN; @@ -2109,10 +2428,9 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, IPC_LOG_MSG(port->ipc_log_misc, "BitsChar%d stop bit%d\n", bits_per_char, stop_bit_len); exit_set_termios: - if (!uart_console(uport)) { + if (!uart_console(uport)) msm_geni_serial_set_manual_flow(true, port); - enable_irq(uport->irq); - } + msm_geni_serial_start_rx(uport); if (!uart_console(uport)) msm_geni_serial_power_off(uport); @@ -2180,9 +2498,9 @@ static ssize_t xfer_mode_store(struct device *dev, return size; msm_geni_serial_power_on(uport); - spin_lock_irqsave(&uport->lock, flags); msm_geni_serial_stop_tx(uport); msm_geni_serial_stop_rx(uport); + spin_lock_irqsave(&uport->lock, flags); port->xfer_mode = xfer_mode; geni_se_select_mode(uport->membase, port->xfer_mode); spin_unlock_irqrestore(&uport->lock, flags); @@ -2262,6 +2580,7 @@ static int console_register(struct uart_driver *drv) { return uart_register_driver(drv); } + static void console_unregister(struct uart_driver *drv) { uart_unregister_driver(drv); @@ -2450,6 +2769,8 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport) __func__, msm_port->ver_info.hw_major_ver, msm_port->ver_info.hw_minor_ver, msm_port->ver_info.hw_step_ver); + + msm_geni_serial_enable_interrupts(uport); exit_ver_info: if (!msm_port->is_console) se_geni_clks_off(&msm_port->serial_rsc); @@ -2660,6 +2981,9 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; uport->fifosize = ((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3); + /* Complete signals to handle cancel cmd completion */ + init_completion(&dev_port->m_cmd_timeout); + init_completion(&dev_port->s_cmd_timeout); uport->irq = platform_get_irq(pdev, 0); if (uport->irq < 0) { @@ -2668,6 +2992,17 @@ static int msm_geni_serial_probe(struct platform_device *pdev) goto exit_geni_serial_probe; } + dev_port->name = devm_kasprintf(uport->dev, GFP_KERNEL, + "msm_serial_geni%d", uport->line); + irq_set_status_flags(uport->irq, IRQ_NOAUTOEN); + ret = devm_request_irq(uport->dev, uport->irq, msm_geni_serial_isr, + IRQF_TRIGGER_HIGH, dev_port->name, uport); + if (ret) { + dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n", + __func__, ret); + goto exit_geni_serial_probe; + } + uport->private_data = (void *)drv; platform_set_drvdata(pdev, dev_port); if (is_console) { @@ -2691,6 +3026,7 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Serial port%d added.FifoSize %d is_console%d\n", line, uport->fifosize, is_console); + device_create_file(uport->dev, &dev_attr_loopback); device_create_file(uport->dev, &dev_attr_xfer_mode); device_create_file(uport->dev, &dev_attr_ver_info); @@ -2702,8 +3038,11 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to Read FW ver: %d\n", ret); goto exit_geni_serial_probe; } - return uart_add_one_port(drv, uport); + + ret = uart_add_one_port(drv, uport); exit_geni_serial_probe: + IPC_LOG_MSG(dev_port->ipc_log_misc, "%s: ret:%d\n", + __func__, ret); return ret; } @@ -2717,8 +3056,8 @@ static int msm_geni_serial_remove(struct platform_device *pdev) wakeup_source_unregister(port->geni_wake); uart_remove_one_port(drv, &port->uport); if (port->rx_dma) { - dma_free_coherent(port->wrapper_dev, DMA_RX_BUF_SIZE, - port->rx_buf, port->rx_dma); + geni_se_iommu_free_buf(port->wrapper_dev, &port->rx_dma, + port->rx_buf, DMA_RX_BUF_SIZE); port->rx_dma = (dma_addr_t)NULL; } return 0; @@ -2736,21 +3075,24 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) wait_for_transfers_inflight(&port->uport); /* - * Disable Interrupt * Manual RFR On. * Stop Rx. + * Disable Interrupt * Resources off */ - disable_irq(port->uport.irq); stop_rx_sequencer(&port->uport); geni_status = geni_read_reg_nolog(port->uport.membase, SE_GENI_STATUS); + if ((geni_status & M_GENI_CMD_ACTIVE)) stop_tx_sequencer(&port->uport); + + disable_irq(port->uport.irq); ret = se_geni_resources_off(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret); goto exit_runtime_suspend; } + if (port->wakeup_irq > 0) { port->edge_count = 0; enable_irq(port->wakeup_irq); @@ -2790,12 +3132,9 @@ static int msm_geni_serial_runtime_resume(struct device *dev) start_rx_sequencer(&port->uport); /* Ensure that the Rx is running before enabling interrupts */ mb(); - /* - * Do not enable irq before interrupt registration which happens - * at port open time. - */ - if (pm_runtime_enabled(dev) && port->xfer_mode != INVALID) - enable_irq(port->uport.irq); + /* Enable interrupt */ + enable_irq(port->uport.irq); + IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__); exit_runtime_resume: return ret; @@ -2840,7 +3179,6 @@ static int msm_geni_serial_sys_resume_noirq(struct device *dev) console_suspend_enabled && uport->suspended) { uart_resume_port((struct uart_driver *)uport->private_data, uport); - disable_irq(uport->irq); } return 0; } diff --git a/include/linux/msm-geni-se.h b/include/linux/msm-geni-se.h index 9689d0c2c075..e8355caf1631 100644 --- a/include/linux/msm-geni-se.h +++ b/include/linux/msm-geni-se.h @@ -48,6 +48,8 @@ enum se_protocol_types { * @geni_pinctrl: Handle to the pinctrl configuration. * @geni_gpio_active: Handle to the default/active pinctrl state. * @geni_gpi_sleep: Handle to the sleep pinctrl state. + * @num_clk_levels: Number of valid clock levels in clk_perf_tbl. + * @clk_perf_tbl: Table of clock frequency input to Serial Engine clock. */ struct se_geni_rsc { struct device *ctrl_dev; @@ -67,6 +69,8 @@ struct se_geni_rsc { struct pinctrl_state *geni_gpio_active; struct pinctrl_state *geni_gpio_sleep; int clk_freq_out; + unsigned int num_clk_levels; + unsigned long *clk_perf_tbl; }; #define PINCTRL_DEFAULT "default" @@ -324,6 +328,7 @@ struct se_geni_rsc { #define TX_EOT (BIT(1)) #define TX_SBE (BIT(2)) #define TX_RESET_DONE (BIT(3)) +#define TX_GENI_CANCEL_IRQ (BIT(14)) /* SE_DMA_RX_IRQ_STAT Register fields */ #define RX_DMA_DONE (BIT(0)) @@ -332,9 +337,15 @@ struct se_geni_rsc { #define RX_RESET_DONE (BIT(3)) #define RX_FLUSH_DONE (BIT(4)) #define RX_GENI_GP_IRQ (GENMASK(10, 5)) -#define RX_GENI_CANCEL_IRQ (BIT(11)) +#define RX_GENI_CANCEL_IRQ (BIT(14)) #define RX_GENI_GP_IRQ_EXT (GENMASK(13, 12)) +/* DMA DEBUG Register fields */ +#define DMA_TX_ACTIVE (BIT(0)) +#define DMA_RX_ACTIVE (BIT(1)) +#define DMA_TX_STATE (GENMASK(7, 4)) +#define DMA_RX_STATE (GENMASK(11, 8)) + #define DEFAULT_BUS_WIDTH (4) /* GSI TRE fields */