From f70575805708cabdedea7498aaa3f710fde4d920 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 31 Jan 2024 03:29:01 +0000 Subject: [PATCH] add lvds1024*800 --- kernel/drivers/tty/serial/qcom_geni_serial.c | 518 ++++++++++++++++++++++++++++++++++++++------------------ 1 files changed, 348 insertions(+), 170 deletions(-) diff --git a/kernel/drivers/tty/serial/qcom_geni_serial.c b/kernel/drivers/tty/serial/qcom_geni_serial.c index cd0768c..7caba23 100644 --- a/kernel/drivers/tty/serial/qcom_geni_serial.c +++ b/kernel/drivers/tty/serial/qcom_geni_serial.c @@ -5,10 +5,14 @@ #include <linux/console.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/irq.h> #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> #include <linux/qcom-geni-se.h> #include <linux/serial.h> #include <linux/serial_core.h> @@ -18,6 +22,7 @@ /* 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 @@ -86,31 +91,53 @@ #define DEF_TX_WM 2 #define DEF_FIFO_WIDTH_BITS 32 #define UART_RX_WM 2 -#define MAX_LOOPBACK_CFG 3 -#ifdef CONFIG_CONSOLE_POLL -#define RX_BYTES_PW 1 -#else -#define RX_BYTES_PW 4 -#endif +/* SE_UART_LOOPBACK_CFG */ +#define RX_TX_SORTED BIT(0) +#define CTS_RTS_SORTED BIT(1) +#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) + +/* 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; struct geni_se se; - char name[20]; + const char *name; u32 tx_fifo_depth; u32 tx_fifo_width; u32 rx_fifo_depth; bool setup; + unsigned long clk_rate; 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; @@ -124,9 +151,10 @@ 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}; + 32000000, 48000000, 51200000, 64000000, + 80000000, 96000000, 100000000, + 102400000, 112000000, 120000000, + 128000000}; #define to_dev_port(ptr, member) \ container_of(ptr, struct qcom_geni_serial_port, member) @@ -158,32 +186,6 @@ }, }; -static ssize_t loopback_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); - - return snprintf(buf, sizeof(u32), "%d\n", port->loopback); -} - -static ssize_t loopback_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t size) -{ - struct platform_device *pdev = to_platform_device(dev); - struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); - u32 loopback; - - if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) { - dev_err(dev, "Invalid input\n"); - return -EINVAL; - } - port->loopback = loopback; - return size; -} -static DEVICE_ATTR_RW(loopback); - static struct qcom_geni_serial_port qcom_geni_console_port = { .uport = { .iotype = UPIO_MEM, @@ -197,10 +199,8 @@ { struct platform_device *pdev = to_platform_device(uport->dev); struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - struct resource *res; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - uport->membase = devm_ioremap_resource(&pdev->dev, res); + uport->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(uport->membase)) return PTR_ERR(uport->membase); port->se.base = uport->membase; @@ -235,11 +235,15 @@ unsigned int mctrl) { u32 uart_manual_rfr = 0; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); if (uart_console(uport)) return; - if (!(mctrl & TIOCM_RTS)) + if (mctrl & TIOCM_LOOP) + port->loopback = RX_TX_CTS_RTS_SORTED; + + 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); } @@ -269,8 +273,9 @@ 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) @@ -336,23 +341,47 @@ } #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, @@ -371,13 +400,25 @@ #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE static void qcom_geni_serial_wr_char(struct uart_port *uport, int 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; @@ -412,6 +453,15 @@ 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); } @@ -484,7 +534,7 @@ 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; @@ -501,7 +551,8 @@ continue; } - sysrq = uart_handle_sysrq_char(uport, buf[c]); + sysrq = uart_prepare_sysrq_char(uport, buf[c]); + if (!sysrq) tty_insert_flip_char(tport, buf[c], TTY_NORMAL); } @@ -520,7 +571,6 @@ 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; @@ -532,8 +582,7 @@ 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); @@ -665,11 +714,11 @@ 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); } @@ -702,7 +751,7 @@ } 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); @@ -725,8 +774,8 @@ 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++]; @@ -765,12 +814,12 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) { - unsigned int m_irq_status; - unsigned int s_irq_status; - unsigned int geni_status; + u32 m_irq_en; + u32 m_irq_status; + u32 s_irq_status; + u32 geni_status; struct uart_port *uport = dev; unsigned long flags; - unsigned int m_irq_en; bool drop_rx = false; struct tty_port *tport = &uport->state->port; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); @@ -813,13 +862,15 @@ qcom_geni_serial_handle_rx(uport, drop_rx); out_unlock: - spin_unlock_irqrestore(&uport->lock, flags); + uart_unlock_and_check_sysrq(uport, flags); + return IRQ_HANDLED; } -static void get_tx_fifo_size(struct qcom_geni_serial_port *port) +static int setup_fifos(struct qcom_geni_serial_port *port) { struct uart_port *uport; + u32 old_rx_fifo_depth = port->rx_fifo_depth; uport = &port->uport; port->tx_fifo_depth = geni_se_get_tx_fifo_depth(&port->se); @@ -827,35 +878,31 @@ port->rx_fifo_depth = geni_se_get_rx_fifo_depth(&port->se); uport->fifosize = (port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE; + + if (port->rx_fifo && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) { + port->rx_fifo = devm_krealloc(uport->dev, port->rx_fifo, + port->rx_fifo_depth * sizeof(u32), + GFP_KERNEL); + if (!port->rx_fifo) + return -ENOMEM; + } + + return 0; } 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); - - free_irq(uport->irq, uport); - spin_lock_irqsave(&uport->lock, flags); - qcom_geni_serial_stop_tx(uport); - qcom_geni_serial_stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); + disable_irq(uport->irq); } static int qcom_geni_serial_port_setup(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; + u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; u32 proto; - - if (uart_console(uport)) - port->tx_bytes_pw = 1; - else - port->tx_bytes_pw = 4; - port->rx_bytes_pw = RX_BYTES_PW; + u32 pin_swap; + int ret; proto = geni_se_read_proto(&port->se); if (proto != GENI_SE_UART) { @@ -865,27 +912,35 @@ qcom_geni_serial_stop_rx(uport); - get_tx_fifo_size(port); + ret = setup_fifos(port); + if (ret) + return ret; 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; @@ -896,21 +951,14 @@ int ret; struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - scnprintf(port->name, sizeof(port->name), - "qcom_serial_%s%d", - (uart_console(uport) ? "console" : "uart"), uport->line); - if (!port->setup) { ret = qcom_geni_serial_port_setup(uport); if (ret) return ret; } + enable_irq(uport->irq); - ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH, - port->name, uport); - if (ret) - dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret); - return ret; + return 0; } static unsigned long get_clk_cfg(unsigned long clk_freq) @@ -924,12 +972,13 @@ return 0; } -static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div) +static unsigned long get_clk_div_rate(unsigned int baud, + unsigned int sampling_rate, unsigned int *clk_div) { unsigned long ser_clk; unsigned long desired_clk; - desired_clk = baud * UART_OVERSAMPLING; + 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", @@ -945,29 +994,49 @@ struct ktermios *termios, struct ktermios *old) { unsigned int baud; - unsigned int bits_per_char; - unsigned int tx_trans_cfg; - unsigned int tx_parity_cfg; - unsigned int rx_trans_cfg; - unsigned int rx_parity_cfg; - unsigned int stop_bit_len; + u32 bits_per_char; + u32 tx_trans_cfg; + u32 tx_parity_cfg; + u32 rx_trans_cfg; + u32 rx_parity_cfg; + u32 stop_bit_len; unsigned int clk_div; - unsigned long ser_clk_cfg; + u32 ser_clk_cfg; 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 */ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); port->baud = baud; - clk_rate = get_clk_div_rate(baud, &clk_div); + + sampling_rate = UART_OVERSAMPLING; + /* Sampling rate is halved for IP versions >= 2.5 */ + ver = geni_se_get_qup_hw_version(&port->se); + if (ver >= QUP_SE_VERSION_2_5) + sampling_rate /= 2; + + clk_rate = get_clk_div_rate(baud, sampling_rate, &clk_div); if (!clk_rate) goto out_restart_rx; uport->uartclk = clk_rate; - clk_set_rate(port->se.clk, clk_rate); + port->clk_rate = 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); @@ -1054,7 +1123,7 @@ { 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'; @@ -1094,6 +1163,38 @@ __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) { @@ -1108,6 +1209,8 @@ if (!uport->membase) return -EINVAL; + + uport->private_data = &earlycon_private_data; memset(&se, 0, sizeof(se)); se.base = uport->membase; @@ -1126,7 +1229,8 @@ */ 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); @@ -1140,6 +1244,8 @@ 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", @@ -1199,11 +1305,17 @@ 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); + if (port->clk_rate) + dev_pm_opp_set_rate(uport->dev, port->clk_rate); 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); + dev_pm_opp_set_rate(uport->dev, 0); + geni_icc_disable(&port->se); + } } static const struct uart_ops qcom_geni_console_pops = { @@ -1256,14 +1368,12 @@ if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart")) console = true; - if (pdev->dev.of_node) { - if (console) { - drv = &qcom_geni_console_driver; - 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"); - } + if (console) { + drv = &qcom_geni_console_driver; + 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"); } port = get_port_from_line(line, console); @@ -1296,73 +1406,141 @@ port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ %d\n", irq); - return irq; + if (!console) { + port->rx_fifo = devm_kcalloc(uport->dev, + port->rx_fifo_depth, sizeof(u32), GFP_KERNEL); + if (!port->rx_fifo) + return -ENOMEM; } - uport->irq = irq; - uport->private_data = drv; + 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); + if (!port->name) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + uport->irq = irq; + uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE); + + if (!console) + port->wakeup_irq = platform_get_irq_optional(pdev, 1); + + 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; + + port->se.opp_table = dev_pm_opp_set_clkname(&pdev->dev, "se"); + if (IS_ERR(port->se.opp_table)) + return PTR_ERR(port->se.opp_table); + /* OPP table is optional */ + ret = dev_pm_opp_of_add_table(&pdev->dev); + if (ret && ret != -ENODEV) { + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); + goto put_clkname; + } + + 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; - if (!console) - device_create_file(uport->dev, &dev_attr_loopback); - return uart_add_one_port(drv, uport); + + ret = uart_add_one_port(drv, uport); + if (ret) + goto err; + + irq_set_status_flags(uport->irq, IRQ_NOAUTOEN); + ret = devm_request_irq(uport->dev, uport->irq, qcom_geni_serial_isr, + IRQF_TRIGGER_HIGH, port->name, uport); + if (ret) { + dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret); + uart_remove_one_port(drv, uport); + goto err; + } + + if (port->wakeup_irq > 0) { + device_init_wakeup(&pdev->dev, true); + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, + port->wakeup_irq); + if (ret) { + device_init_wakeup(&pdev->dev, false); + uart_remove_one_port(drv, uport); + goto err; + } + } + + return 0; +err: + dev_pm_opp_of_remove_table(&pdev->dev); +put_clkname: + dev_pm_opp_put_clkname(port->se.opp_table); + return ret; } 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_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(port->se.opp_table); + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); uart_remove_one_port(drv, &port->uport); + return 0; } -static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) +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; + /* + * This is done so we can hit the lowest possible state in suspend + * even with no_console_suspend + */ if (uart_console(uport)) { - uart_suspend_port(uport->private_data, uport); - } else { - struct uart_state *state = uport->state; - /* - * If the port is open, deny system suspend. - */ - if (state->pm_state == UART_PM_STATE_ON) - return -EBUSY; + geni_icc_set_tag(&port->se, 0x3); + geni_icc_set_bw(&port->se); } - - return 0; + return uart_suspend_port(private_data->drv, uport); } -static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) +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; - if (uart_console(uport) && - console_suspend_enabled && uport->suspended) { - uart_resume_port(uport->private_data, uport); - /* - * uart_suspend_port() invokes port shutdown which in turn - * frees the irq. uart_resume_port invokes port startup which - * performs request_irq. The request_irq auto-enables the IRQ. - * In addition, resume_noirq implicitly enables the IRQ and - * leads to an unbalanced IRQ enable warning. Disable the IRQ - * before returning so that the warning is suppressed. - */ - disable_irq(uport->irq); + ret = uart_resume_port(private_data->drv, uport); + if (uart_console(uport)) { + geni_icc_set_tag(&port->se, 0x7); + geni_icc_set_bw(&port->se); } - return 0; + return ret; } static const struct dev_pm_ops qcom_geni_serial_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq, - qcom_geni_serial_sys_resume_noirq) + SET_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend, + qcom_geni_serial_sys_resume) }; static const struct of_device_id qcom_geni_serial_match_table[] = { -- Gitblit v1.6.2