diff options
Diffstat (limited to 'drivers/tty/serial/qcom_geni_serial.c')
-rw-r--r-- | drivers/tty/serial/qcom_geni_serial.c | 408 |
1 files changed, 295 insertions, 113 deletions
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 0bd1684cabb3..83b66b73303a 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. +/* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ +#define __DISABLE_TRACE_MMIO__ + #include <linux/clk.h> #include <linux/console.h> #include <linux/io.h> @@ -9,6 +12,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/pm_opp.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/pm_wakeirq.h> @@ -18,9 +22,11 @@ #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <dt-bindings/interconnect/qcom,icc.h> /* UART specific GENI registers */ #define SE_UART_LOOPBACK_CFG 0x22c +#define SE_UART_IO_MACRO_CTRL 0x240 #define SE_UART_TX_TRANS_CFG 0x25c #define SE_UART_TX_WORD_LEN 0x268 #define SE_UART_TX_STOP_BIT_LEN 0x26c @@ -95,11 +101,25 @@ #define CTS_RTS_SORTED BIT(1) #define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) -#ifdef CONFIG_CONSOLE_POLL -#define CONSOLE_RX_BYTES_PW 1 -#else -#define CONSOLE_RX_BYTES_PW 4 -#endif +/* UART pin swap value */ +#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0) +#define IO_MACRO_IO0_SEL 0x3 +#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) +#define IO_MACRO_IO2_IO3_SWAP 0x4640 + +/* We always configure 4 bytes per FIFO word */ +#define BYTES_PER_FIFO_WORD 4 + +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; + + u32 write_cached_bytes; + unsigned int write_cached_bytes_cnt; +}; struct qcom_geni_serial_port { struct uart_port uport; @@ -111,14 +131,16 @@ struct qcom_geni_serial_port { bool setup; int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop); unsigned int baud; - unsigned int tx_bytes_pw; - unsigned int rx_bytes_pw; - u32 *rx_fifo; + void *rx_fifo; u32 loopback; bool brk; unsigned int tx_remaining; 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; @@ -131,11 +153,6 @@ static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); static void qcom_geni_serial_stop_rx(struct uart_port *uport); static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop); -static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, - 32000000, 48000000, 64000000, 80000000, - 96000000, 100000000, 102400000, - 112000000, 120000000, 128000000}; - #define to_dev_port(ptr, member) \ container_of(ptr, struct qcom_geni_serial_port, member) @@ -223,7 +240,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport, if (mctrl & TIOCM_LOOP) port->loopback = RX_TX_CTS_RTS_SORTED; - if (!(mctrl & TIOCM_RTS)) + if (!(mctrl & TIOCM_RTS) && !uport->suspended) uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY; writel(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); } @@ -253,8 +270,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) @@ -320,23 +338,47 @@ 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; - status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); - writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR); + 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_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); - if (!(status & RX_FIFO_WC_MSK)) - return NO_POLL_CHAR; + status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS); + word_cnt = status & RX_FIFO_WC_MSK; + if (!word_cnt) + return NO_POLL_CHAR; - rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn); - return rx_fifo & 0xff; + if (word_cnt == 1 && (status & RX_LAST)) + /* + * NOTE: If RX_LAST_BYTE_VALID is 0 it needs to be + * treated as if it was BYTES_PER_FIFO_WORD. + */ + private_data->poll_cached_bytes_cnt = + (status & RX_LAST_BYTE_VALID_MSK) >> + RX_LAST_BYTE_VALID_SHFT; + + if (private_data->poll_cached_bytes_cnt == 0) + private_data->poll_cached_bytes_cnt = BYTES_PER_FIFO_WORD; + + 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, @@ -353,15 +395,27 @@ static void qcom_geni_serial_poll_put_char(struct uart_port *uport, #endif #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE -static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch) +static void qcom_geni_serial_wr_char(struct uart_port *uport, unsigned char ch) { - writel(ch, uport->membase + SE_GENI_TX_FIFOn); + struct qcom_geni_private_data *private_data = uport->private_data; + + private_data->write_cached_bytes = + (private_data->write_cached_bytes >> 8) | (ch << 24); + private_data->write_cached_bytes_cnt++; + + if (private_data->write_cached_bytes_cnt == BYTES_PER_FIFO_WORD) { + writel(private_data->write_cached_bytes, + uport->membase + SE_GENI_TX_FIFOn); + private_data->write_cached_bytes_cnt = 0; + } } static void __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, unsigned int count) { + struct qcom_geni_private_data *private_data = uport->private_data; + int i; u32 bytes_to_send = count; @@ -396,6 +450,15 @@ __qcom_geni_serial_console_write(struct uart_port *uport, const char *s, SE_GENI_M_IRQ_CLEAR); i += chars_to_write; } + + if (private_data->write_cached_bytes_cnt) { + private_data->write_cached_bytes >>= BITS_PER_BYTE * + (BYTES_PER_FIFO_WORD - private_data->write_cached_bytes_cnt); + writel(private_data->write_cached_bytes, + uport->membase + SE_GENI_TX_FIFOn); + private_data->write_cached_bytes_cnt = 0; + } + qcom_geni_serial_poll_tx_done(uport); } @@ -442,7 +505,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, */ qcom_geni_serial_poll_tx_done(uport); - if (uart_circ_chars_pending(&uport->state->xmit)) { + if (!uart_circ_empty(&uport->state->xmit)) { irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN); writel(irq_en | M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_EN); @@ -468,7 +531,7 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) tport = &uport->state->port; for (i = 0; i < bytes; ) { int c; - int chunk = min_t(int, bytes - i, port->rx_bytes_pw); + int chunk = min_t(int, bytes - i, BYTES_PER_FIFO_WORD); ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, buf, 1); i += chunk; @@ -505,7 +568,6 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) { - unsigned char *buf; struct tty_port *tport; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE; @@ -517,8 +579,7 @@ static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) if (drop) return 0; - buf = (unsigned char *)port->rx_fifo; - ret = tty_insert_flip_string(tport, buf, bytes); + ret = tty_insert_flip_string(tport, port->rx_fifo, bytes); if (ret != bytes) { dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n", __func__, ret, bytes); @@ -650,11 +711,11 @@ static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop) if (!word_cnt) return; - total_bytes = port->rx_bytes_pw * (word_cnt - 1); + total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1); if (last_word_partial && last_word_byte_cnt) total_bytes += last_word_byte_cnt; else - total_bytes += port->rx_bytes_pw; + total_bytes += BYTES_PER_FIFO_WORD; port->handle_rx(uport, total_bytes, drop); } @@ -687,7 +748,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, } avail = port->tx_fifo_depth - (status & TX_FIFO_WC); - avail *= port->tx_bytes_pw; + avail *= BYTES_PER_FIFO_WORD; tail = xmit->tail; chunk = min(avail, pending); @@ -710,8 +771,8 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, u8 buf[sizeof(u32)]; int c; - memset(buf, 0, ARRAY_SIZE(buf)); - tx_bytes = min_t(size_t, remaining, port->tx_bytes_pw); + memset(buf, 0, sizeof(buf)); + tx_bytes = min_t(size_t, remaining, BYTES_PER_FIFO_WORD); for (c = 0; c < tx_bytes ; c++) { buf[c] = xmit->buf[tail++]; @@ -755,7 +816,6 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) u32 s_irq_status; u32 geni_status; struct uart_port *uport = dev; - unsigned long flags; bool drop_rx = false; struct tty_port *tport = &uport->state->port; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); @@ -763,7 +823,8 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) if (uport->suspended) return IRQ_NONE; - spin_lock_irqsave(&uport->lock, flags); + spin_lock(&uport->lock); + m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS); geni_status = readl(uport->membase + SE_GENI_STATUS); @@ -798,7 +859,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) qcom_geni_serial_handle_rx(uport, drop_rx); out_unlock: - uart_unlock_and_check_sysrq(uport, flags); + uart_unlock_and_check_sysrq(uport); return IRQ_HANDLED; } @@ -818,17 +879,7 @@ static void get_tx_fifo_size(struct qcom_geni_serial_port *port) static void qcom_geni_serial_shutdown(struct uart_port *uport) { - unsigned long flags; - - /* Stop the console before stopping the current tx */ - if (uart_console(uport)) - console_stop(uport->cons); - disable_irq(uport->irq); - spin_lock_irqsave(&uport->lock, flags); - qcom_geni_serial_stop_tx(uport); - qcom_geni_serial_stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); } static int qcom_geni_serial_port_setup(struct uart_port *uport) @@ -836,14 +887,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) struct qcom_geni_serial_port *port = to_dev_port(uport, uport); u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; u32 proto; - - if (uart_console(uport)) { - port->tx_bytes_pw = 1; - port->rx_bytes_pw = CONSOLE_RX_BYTES_PW; - } else { - port->tx_bytes_pw = 4; - port->rx_bytes_pw = 4; - } + u32 pin_swap; proto = geni_se_read_proto(&port->se); if (proto != GENI_SE_UART) { @@ -856,24 +900,30 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) get_tx_fifo_size(port); writel(rxstale, uport->membase + SE_UART_RX_STALE_CNT); + + pin_swap = readl(uport->membase + SE_UART_IO_MACRO_CTRL); + if (port->rx_tx_swap) { + pin_swap &= ~DEFAULT_IO_MACRO_IO2_IO3_MASK; + pin_swap |= IO_MACRO_IO2_IO3_SWAP; + } + if (port->cts_rts_swap) { + pin_swap &= ~DEFAULT_IO_MACRO_IO0_IO1_MASK; + pin_swap |= IO_MACRO_IO0_SEL; + } + /* Configure this register if RX-TX, CTS-RTS pins are swapped */ + if (port->rx_tx_swap || port->cts_rts_swap) + writel(pin_swap, uport->membase + SE_UART_IO_MACRO_CTRL); + /* * Make an unconditional cancel on the main sequencer to reset * it else we could end up in data loss scenarios. */ if (uart_console(uport)) qcom_geni_serial_poll_tx_done(uport); - geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw, - false, true, false); - geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw, - false, false, true); + geni_se_config_packing(&port->se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD, + false, true, true); geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2); geni_se_select_mode(&port->se, GENI_SE_FIFO); - if (!uart_console(uport)) { - port->rx_fifo = devm_kcalloc(uport->dev, - port->rx_fifo_depth, sizeof(u32), GFP_KERNEL); - if (!port->rx_fifo) - return -ENOMEM; - } port->setup = true; return 0; @@ -894,37 +944,70 @@ static int qcom_geni_serial_startup(struct uart_port *uport) return 0; } -static unsigned long get_clk_cfg(unsigned long clk_freq) +static unsigned long find_clk_rate_in_tol(struct clk *clk, unsigned int desired_clk, + unsigned int *clk_div, unsigned int percent_tol) { - int i; + unsigned long freq; + unsigned long div, maxdiv; + u64 mult; + unsigned long offset, abs_tol, achieved; + + abs_tol = div_u64((u64)desired_clk * percent_tol, 100); + maxdiv = CLK_DIV_MSK >> CLK_DIV_SHFT; + div = 1; + while (div <= maxdiv) { + mult = (u64)div * desired_clk; + if (mult != (unsigned long)mult) + break; + + offset = div * abs_tol; + freq = clk_round_rate(clk, mult - offset); - for (i = 0; i < ARRAY_SIZE(root_freq); i++) { - if (!(root_freq[i] % clk_freq)) - return root_freq[i]; + /* Can only get lower if we're done */ + if (freq < mult - offset) + break; + + /* + * Re-calculate div in case rounding skipped rates but we + * ended up at a good one, then check for a match. + */ + div = DIV_ROUND_CLOSEST(freq, desired_clk); + achieved = DIV_ROUND_CLOSEST(freq, div); + if (achieved <= desired_clk + abs_tol && + achieved >= desired_clk - abs_tol) { + *clk_div = div; + return freq; + } + + div = DIV_ROUND_UP(freq, desired_clk); } + return 0; } -static unsigned long get_clk_div_rate(unsigned int baud, +static unsigned long get_clk_div_rate(struct clk *clk, unsigned int baud, unsigned int sampling_rate, unsigned int *clk_div) { unsigned long ser_clk; unsigned long desired_clk; desired_clk = baud * sampling_rate; - ser_clk = get_clk_cfg(desired_clk); - if (!ser_clk) { - pr_err("%s: Can't find matching DFS entry for baud %d\n", - __func__, baud); - return ser_clk; - } + if (!desired_clk) + return 0; + + /* + * try to find a clock rate within 2% tolerance, then within 5% + */ + ser_clk = find_clk_rate_in_tol(clk, desired_clk, clk_div, 2); + if (!ser_clk) + ser_clk = find_clk_rate_in_tol(clk, desired_clk, clk_div, 5); - *clk_div = ser_clk / desired_clk; return ser_clk; } static void qcom_geni_serial_set_termios(struct uart_port *uport, - struct ktermios *termios, struct ktermios *old) + struct ktermios *termios, + const struct ktermios *old) { unsigned int baud; u32 bits_per_char; @@ -938,6 +1021,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, struct qcom_geni_serial_port *port = to_dev_port(uport, uport); unsigned long clk_rate; u32 ver, sampling_rate; + unsigned int avg_bw_core; qcom_geni_serial_stop_rx(uport); /* baud rate */ @@ -947,18 +1031,36 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, sampling_rate = UART_OVERSAMPLING; /* Sampling rate is halved for IP versions >= 2.5 */ ver = geni_se_get_qup_hw_version(&port->se); - if (GENI_SE_VERSION_MAJOR(ver) >= 2 && GENI_SE_VERSION_MINOR(ver) >= 5) + if (ver >= QUP_SE_VERSION_2_5) sampling_rate /= 2; - clk_rate = get_clk_div_rate(baud, sampling_rate, &clk_div); - if (!clk_rate) + clk_rate = get_clk_div_rate(port->se.clk, baud, + sampling_rate, &clk_div); + if (!clk_rate) { + dev_err(port->se.dev, + "Couldn't find suitable clock rate for %u\n", + baud * sampling_rate); goto out_restart_rx; + } + + dev_dbg(port->se.dev, "desired_rate-%u, clk_rate-%lu, clk_div-%u\n", + baud * sampling_rate, clk_rate, clk_div); uport->uartclk = clk_rate; - clk_set_rate(port->se.clk, clk_rate); + dev_pm_opp_set_rate(uport->dev, clk_rate); ser_clk_cfg = SER_CLK_EN; ser_clk_cfg |= clk_div << CLK_DIV_SHFT; + /* + * Bump up BW vote on CPU and CORE path as driver supports FIFO mode + * only. + */ + avg_bw_core = (baud > 115200) ? Bps_to_icc(CORE_2X_50_MHZ) + : GENI_DEFAULT_BW; + port->se.icc_paths[GENI_TO_CORE].avg_bw = avg_bw_core; + port->se.icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(baud); + geni_icc_set_bw(&port->se); + /* parity */ tx_trans_cfg = readl(uport->membase + SE_UART_TX_TRANS_CFG); tx_parity_cfg = readl(uport->membase + SE_UART_TX_PARITY_CFG); @@ -987,21 +1089,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, } /* bits per char */ - switch (termios->c_cflag & CSIZE) { - case CS5: - bits_per_char = 5; - break; - case CS6: - bits_per_char = 6; - break; - case CS7: - bits_per_char = 7; - break; - case CS8: - default: - bits_per_char = 8; - break; - } + bits_per_char = tty_get_char_size(termios->c_cflag); /* stop bits */ if (termios->c_cflag & CSTOPB) @@ -1040,11 +1128,11 @@ static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport) } #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE -static int __init qcom_geni_console_setup(struct console *co, char *options) +static int qcom_geni_console_setup(struct console *co, char *options) { struct uart_port *uport; struct qcom_geni_serial_port *port; - int baud = 9600; + int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n'; @@ -1084,6 +1172,38 @@ static void qcom_geni_serial_earlycon_write(struct console *con, __qcom_geni_serial_console_write(&dev->port, s, n); } +#ifdef CONFIG_CONSOLE_POLL +static int qcom_geni_serial_earlycon_read(struct console *con, + char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + struct uart_port *uport = &dev->port; + int num_read = 0; + int ch; + + while (num_read < n) { + ch = qcom_geni_serial_get_char(uport); + if (ch == NO_POLL_CHAR) + break; + s[num_read++] = ch; + } + + return num_read; +} + +static void __init qcom_geni_serial_enable_early_read(struct geni_se *se, + struct console *con) +{ + geni_se_setup_s_cmd(se, UART_START_READ, 0); + con->read = qcom_geni_serial_earlycon_read; +} +#else +static inline void qcom_geni_serial_enable_early_read(struct geni_se *se, + struct console *con) { } +#endif + +static struct qcom_geni_private_data earlycon_private_data; + static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, const char *opt) { @@ -1099,6 +1219,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) @@ -1116,7 +1238,8 @@ 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, BYTES_PER_FIFO_WORD, + false, true, true); geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2); geni_se_select_mode(&se, GENI_SE_FIFO); @@ -1130,6 +1253,8 @@ static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev, dev->con->write = qcom_geni_serial_earlycon_write; dev->con->setup = NULL; + qcom_geni_serial_enable_early_read(&se, dev->con); + return 0; } OF_EARLYCON_DECLARE(qcom_geni, "qcom,geni-debug-uart", @@ -1189,11 +1314,14 @@ static void qcom_geni_serial_pm(struct uart_port *uport, if (old_state == UART_PM_STATE_UNDEFINED) old_state = UART_PM_STATE_OFF; - if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) { + geni_icc_enable(&port->se); geni_se_resources_on(&port->se); - else if (new_state == UART_PM_STATE_OFF && - old_state == UART_PM_STATE_ON) + } else if (new_state == UART_PM_STATE_OFF && + old_state == UART_PM_STATE_ON) { geni_se_resources_off(&port->se); + geni_icc_disable(&port->se); + } } static const struct uart_ops qcom_geni_console_pops = { @@ -1201,6 +1329,7 @@ static const struct uart_ops qcom_geni_console_pops = { .stop_tx = qcom_geni_serial_stop_tx, .start_tx = qcom_geni_serial_start_tx, .stop_rx = qcom_geni_serial_stop_rx, + .start_rx = qcom_geni_serial_start_rx, .set_termios = qcom_geni_serial_set_termios, .startup = qcom_geni_serial_startup, .request_port = qcom_geni_serial_request_port, @@ -1235,7 +1364,7 @@ static const struct uart_ops qcom_geni_uart_pops = { static int qcom_geni_serial_probe(struct platform_device *pdev) { int ret = 0; - int line = -1; + int line; struct qcom_geni_serial_port *port; struct uart_port *uport; struct resource *res; @@ -1251,7 +1380,9 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) line = of_alias_get_id(pdev->dev.of_node, "serial"); } else { drv = &qcom_geni_uart_driver; - line = of_alias_get_id(pdev->dev.of_node, "hsuart"); + line = of_alias_get_id(pdev->dev.of_node, "serial"); + if (line == -ENODEV) /* compat with non-standard aliases */ + line = of_alias_get_id(pdev->dev.of_node, "hsuart"); } port = get_port_from_line(line, console); @@ -1284,6 +1415,24 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; + if (!console) { + port->rx_fifo = devm_kcalloc(uport->dev, + port->rx_fifo_depth, sizeof(u32), GFP_KERNEL); + if (!port->rx_fifo) + return -ENOMEM; + } + + ret = geni_icc_get(&port->se, NULL); + if (ret) + return ret; + port->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW; + port->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW; + + /* Set BW for register access */ + ret = geni_icc_set_bw(&port->se); + if (ret) + return ret; + port->name = devm_kasprintf(uport->dev, GFP_KERNEL, "qcom_geni_serial_%s%d", uart_console(uport) ? "console" : "uart", uport->line); @@ -1299,7 +1448,24 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) if (!console) port->wakeup_irq = platform_get_irq_optional(pdev, 1); - uport->private_data = drv; + if (of_property_read_bool(pdev->dev.of_node, "rx-tx-swap")) + port->rx_tx_swap = true; + + if (of_property_read_bool(pdev->dev.of_node, "cts-rts-swap")) + port->cts_rts_swap = true; + + ret = devm_pm_opp_set_clkname(&pdev->dev, "se"); + if (ret) + return ret; + /* OPP table is optional */ + ret = devm_pm_opp_of_add_table(&pdev->dev); + if (ret && ret != -ENODEV) { + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); + return ret; + } + + 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; @@ -1340,7 +1506,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) 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; dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); @@ -1353,16 +1519,32 @@ 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); + /* + * This is done so we can hit the lowest possible state in suspend + * even with no_console_suspend + */ + if (uart_console(uport)) { + geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ACTIVE_ONLY); + geni_icc_set_bw(&port->se); + } + return uart_suspend_port(private_data->drv, uport); } static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev) { + int ret; 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); + ret = uart_resume_port(private_data->drv, uport); + if (uart_console(uport)) { + geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS); + geni_icc_set_bw(&port->se); + } + return ret; } static const struct dev_pm_ops qcom_geni_serial_pm_ops = { |