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 */