diff options
Diffstat (limited to 'drivers/tty/serial/qcom_geni_serial.c')
-rw-r--r-- | drivers/tty/serial/qcom_geni_serial.c | 80 |
1 files changed, 55 insertions, 25 deletions
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 0300867eab7a..4610e391e886 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -103,11 +103,13 @@ #define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) #define IO_MACRO_IO2_IO3_SWAP 0x4640 -#ifdef CONFIG_CONSOLE_POLL -#define CONSOLE_RX_BYTES_PW 1 -#else -#define CONSOLE_RX_BYTES_PW 4 -#endif +struct qcom_geni_private_data { + /* NOTE: earlycon port will have NULL here */ + struct uart_driver *drv; + + u32 poll_cached_bytes; + unsigned int poll_cached_bytes_cnt; +}; struct qcom_geni_serial_port { struct uart_port uport; @@ -129,6 +131,8 @@ struct qcom_geni_serial_port { int wakeup_irq; bool rx_tx_swap; bool cts_rts_swap; + + struct qcom_geni_private_data private_data; }; static const struct uart_ops qcom_geni_console_pops; @@ -264,8 +268,9 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport, unsigned int baud; unsigned int fifo_bits; unsigned long timeout_us = 20000; + struct qcom_geni_private_data *private_data = uport->private_data; - if (uport->private_data) { + if (private_data->drv) { port = to_dev_port(uport, uport); baud = port->baud; if (!baud) @@ -331,23 +336,42 @@ static void qcom_geni_serial_abort_rx(struct uart_port *uport) } #ifdef CONFIG_CONSOLE_POLL + static int qcom_geni_serial_get_char(struct uart_port *uport) { - u32 rx_fifo; + struct qcom_geni_private_data *private_data = uport->private_data; u32 status; + u32 word_cnt; + int ret; + + if (!private_data->poll_cached_bytes_cnt) { + status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); + writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR); - status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); - writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR); + status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); + writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR); - status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); - writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR); + status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); + word_cnt = status & RX_FIFO_WC_MSK; + if (!word_cnt) + return NO_POLL_CHAR; - status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); - if (!(status & RX_FIFO_WC_MSK)) - return NO_POLL_CHAR; + if (word_cnt == 1 && (status & RX_LAST)) + private_data->poll_cached_bytes_cnt = + (status & RX_LAST_BYTE_VALID_MSK) >> + RX_LAST_BYTE_VALID_SHFT; + else + private_data->poll_cached_bytes_cnt = 4; - rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn); - return rx_fifo & 0xff; + private_data->poll_cached_bytes = + readl(uport->membase + SE_GENI_RX_FIFOn); + } + + private_data->poll_cached_bytes_cnt--; + ret = private_data->poll_cached_bytes & 0xff; + private_data->poll_cached_bytes >>= 8; + + return ret; } static void qcom_geni_serial_poll_put_char(struct uart_port *uport, @@ -837,13 +861,11 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) u32 proto; u32 pin_swap; - if (uart_console(uport)) { + if (uart_console(uport)) port->tx_bytes_pw = 1; - port->rx_bytes_pw = CONSOLE_RX_BYTES_PW; - } else { + else port->tx_bytes_pw = 4; - port->rx_bytes_pw = 4; - } + port->rx_bytes_pw = 4; proto = geni_se_read_proto(&port->se); if (proto != GENI_SE_UART) { @@ -1139,6 +1161,8 @@ static int qcom_geni_serial_earlycon_exit(struct console *con) return 0; } +static struct qcom_geni_private_data earlycon_private_data; + static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, const char *opt) { @@ -1154,6 +1178,8 @@ static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, if (!uport->membase) return -EINVAL; + uport->private_data = &earlycon_private_data; + memset(&se, 0, sizeof(se)); se.base = uport->membase; if (geni_se_read_proto(&se) != GENI_SE_UART) @@ -1172,6 +1198,7 @@ static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, qcom_geni_serial_poll_tx_done(uport); qcom_geni_serial_abort_rx(uport); geni_se_config_packing(&se, BITS_PER_BYTE, 1, false, true, false); + geni_se_config_packing(&se, BITS_PER_BYTE, 4, false, false, true); geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2); geni_se_select_mode(&se, GENI_SE_FIFO); @@ -1396,7 +1423,8 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return ret; } - uport->private_data = drv; + port->private_data.drv = drv; + uport->private_data = &port->private_data; platform_set_drvdata(pdev, port); port->handle_rx = console ? handle_rx_console : handle_rx_uart; @@ -1442,7 +1470,7 @@ err: static int qcom_geni_serial_remove(struct platform_device *pdev) { struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); - struct uart_driver *drv = port->uport.private_data; + struct uart_driver *drv = port->private_data.drv; if (port->se.has_opp_table) dev_pm_opp_of_remove_table(&pdev->dev); @@ -1458,16 +1486,18 @@ static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; + struct qcom_geni_private_data *private_data = uport->private_data; - return uart_suspend_port(uport->private_data, uport); + return uart_suspend_port(private_data->drv, uport); } static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; + struct qcom_geni_private_data *private_data = uport->private_data; - return uart_resume_port(uport->private_data, uport); + return uart_resume_port(private_data->drv, uport); } static const struct dev_pm_ops qcom_geni_serial_pm_ops = { |