diff options
Diffstat (limited to 'kernel/drivers/tty/serial/imx.c')
-rw-r--r-- | kernel/drivers/tty/serial/imx.c | 364 |
1 files changed, 251 insertions, 113 deletions
diff --git a/kernel/drivers/tty/serial/imx.c b/kernel/drivers/tty/serial/imx.c index 01aa52f57..016e4be05 100644 --- a/kernel/drivers/tty/serial/imx.c +++ b/kernel/drivers/tty/serial/imx.c @@ -139,6 +139,7 @@ #define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ #define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ #define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ +#define USR1_AGTIM (1<<8) /* Ageing timer interrupt flag */ #define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ #define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ #define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ @@ -216,6 +217,8 @@ struct imx_port { unsigned int tx_bytes; unsigned int dma_tx_nents; wait_queue_head_t dma_wait; + unsigned int saved_reg[10]; + bool context_saved; }; struct imx_port_ucrs { @@ -239,7 +242,7 @@ static struct imx_uart_data imx_uart_devdata[] = { }, }; -static struct platform_device_id imx_uart_devtype[] = { +static const struct platform_device_id imx_uart_devtype[] = { { .name = "imx1-uart", .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART], @@ -700,7 +703,8 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) if (sport->port.ignore_status_mask & URXD_DUMMY_READ) goto out; - tty_insert_flip_char(port, rx, flg); + if (tty_insert_flip_char(port, rx, flg) == 0) + sport->port.icount.buf_overrun++; } out: @@ -725,11 +729,15 @@ static void imx_dma_rxint(struct imx_port *sport) if ((temp & USR2_RDR) && !sport->dma_is_rxing) { sport->dma_is_rxing = 1; - /* disable the `Recerver Ready Interrrupt` */ + /* disable the receiver ready and aging timer interrupts */ temp = readl(sport->port.membase + UCR1); temp &= ~(UCR1_RRDYEN); writel(temp, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR2); + temp &= ~(UCR2_ATEN); + writel(temp, sport->port.membase + UCR2); + /* tell the DMA to receive the data. */ start_rx_dma(sport); } @@ -746,7 +754,7 @@ static irqreturn_t imx_int(int irq, void *dev_id) sts = readl(sport->port.membase + USR1); sts2 = readl(sport->port.membase + USR2); - if (sts & USR1_RRDY) { + if (sts & (USR1_RRDY | USR1_AGTIM)) { if (sport->dma_is_enabled) imx_dma_rxint(sport); else @@ -766,7 +774,6 @@ static irqreturn_t imx_int(int irq, void *dev_id) writel(USR1_AWAKE, sport->port.membase + USR1); if (sts2 & USR2_ORE) { - dev_err(sport->port.dev, "Rx FIFO overrun\n"); sport->port.icount.overrun++; writel(USR2_ORE, sport->port.membase + USR2); } @@ -850,20 +857,6 @@ static void imx_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&sport->port.lock, flags); } -#define TXTL 2 /* reset default */ -#define RXTL 1 /* reset default */ - -static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) -{ - unsigned int val; - - /* set receiver / transmitter trigger level */ - val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); - val |= TXTL << UFCR_TXTL_SHF | RXTL; - writel(val, sport->port.membase + UFCR); - return 0; -} - #define RX_BUF_SIZE (PAGE_SIZE) static void imx_rx_dma_done(struct imx_port *sport) { @@ -872,11 +865,15 @@ static void imx_rx_dma_done(struct imx_port *sport) spin_lock_irqsave(&sport->port.lock, flags); - /* Enable this interrupt when the RXFIFO is empty. */ + /* re-enable interrupts to get notified when new symbols are incoming */ temp = readl(sport->port.membase + UCR1); temp |= UCR1_RRDYEN; writel(temp, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_ATEN; + writel(temp, sport->port.membase + UCR2); + sport->dma_is_rxing = 0; /* Is the shutdown waiting for us? */ @@ -887,14 +884,12 @@ static void imx_rx_dma_done(struct imx_port *sport) } /* - * There are three kinds of RX DMA interrupts(such as in the MX6Q): + * There are two kinds of RX DMA interrupts(such as in the MX6Q): * [1] the RX DMA buffer is full. - * [2] the Aging timer expires(wait for 8 bytes long) - * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN). + * [2] the aging timer expires * - * The [2] is trigger when a character was been sitting in the FIFO - * meanwhile [3] can wait for 32 bytes long when the RX line is - * on IDLE state and RxFIFO is empty. + * Condition [2] is triggered when a character has been sitting in the FIFO + * for at least 8 byte durations. */ static void dma_rx_callback(void *data) { @@ -912,36 +907,32 @@ static void dma_rx_callback(void *data) status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); count = RX_BUF_SIZE - state.residue; - if (readl(sport->port.membase + USR2) & USR2_IDLE) { - /* In condition [3] the SDMA counted up too early */ - count--; - - writel(USR2_IDLE, sport->port.membase + USR2); - } - dev_dbg(sport->port.dev, "We get %d bytes.\n", count); if (count) { - if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) - tty_insert_flip_string(port, sport->rx_buf, count); + if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) { + int bytes = tty_insert_flip_string(port, sport->rx_buf, + count); + + if (bytes != count) + sport->port.icount.buf_overrun++; + } tty_flip_buffer_push(port); + sport->port.icount.rx += count; + } + /* + * Restart RX DMA directly if more data is available in order to skip + * the roundtrip through the IRQ handler. If there is some data already + * in the FIFO, DMA needs to be restarted soon anyways. + * + * Otherwise stop the DMA and reactivate FIFO IRQs to restart DMA once + * data starts to arrive again. + */ + if (readl(sport->port.membase + USR2) & USR2_RDR) start_rx_dma(sport); - } else if (readl(sport->port.membase + USR2) & USR2_RDR) { - /* - * start rx_dma directly once data in RXFIFO, more efficient - * than before: - * 1. call imx_rx_dma_done to stop dma if no data received - * 2. wait next RDR interrupt to start dma transfer. - */ - start_rx_dma(sport); - } else { - /* - * stop dma to prevent too many IDLE event trigged if no data - * in RXFIFO - */ + else imx_rx_dma_done(sport); - } } static int start_rx_dma(struct imx_port *sport) @@ -974,6 +965,22 @@ static int start_rx_dma(struct imx_port *sport) return 0; } +#define TXTL_DEFAULT 2 /* reset default */ +#define RXTL_DEFAULT 1 /* reset default */ +#define TXTL_DMA 8 /* DMA burst setting */ +#define RXTL_DMA 9 /* DMA burst setting */ + +static void imx_setup_ufcr(struct imx_port *sport, + unsigned char txwl, unsigned char rxwl) +{ + unsigned int val; + + /* set receiver / transmitter trigger level */ + val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); + val |= txwl << UFCR_TXTL_SHF | rxwl; + writel(val, sport->port.membase + UFCR); +} + static void imx_uart_dma_exit(struct imx_port *sport) { if (sport->dma_chan_rx) { @@ -1009,7 +1016,8 @@ static int imx_uart_dma_init(struct imx_port *sport) slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = sport->port.mapbase + URXD0; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.src_maxburst = RXTL; + /* one byte less than the watermark level to enable the aging timer */ + slave_config.src_maxburst = RXTL_DMA - 1; ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config); if (ret) { dev_err(dev, "error in RX dma configuration.\n"); @@ -1033,7 +1041,7 @@ static int imx_uart_dma_init(struct imx_port *sport) slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = sport->port.mapbase + URTX0; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.dst_maxburst = TXTL; + slave_config.dst_maxburst = TXTL_DMA; ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config); if (ret) { dev_err(dev, "error in TX dma configuration."); @@ -1056,15 +1064,14 @@ static void imx_enable_dma(struct imx_port *sport) /* set UCR1 */ temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN | - /* wait for 32 idle frames for IDDMA interrupt */ - UCR1_ICD_REG(3); + temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN; writel(temp, sport->port.membase + UCR1); - /* set UCR4 */ - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_IDDMAEN; - writel(temp, sport->port.membase + UCR4); + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_ATEN; + writel(temp, sport->port.membase + UCR2); + + imx_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); sport->dma_is_enabled = 1; } @@ -1080,13 +1087,10 @@ static void imx_disable_dma(struct imx_port *sport) /* clear UCR2 */ temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_CTSC | UCR2_CTS); + temp &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN); writel(temp, sport->port.membase + UCR2); - /* clear UCR4 */ - temp = readl(sport->port.membase + UCR4); - temp &= ~UCR4_IDDMAEN; - writel(temp, sport->port.membase + UCR4); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); sport->dma_is_enabled = 0; } @@ -1109,7 +1113,7 @@ static int imx_startup(struct uart_port *port) return retval; } - imx_setup_ufcr(sport, 0); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); /* disable the DREN bit (Data Ready interrupt enable) before * requesting IRQs @@ -1122,6 +1126,12 @@ static int imx_startup(struct uart_port *port) writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); + /* Can we enable the DMA support? */ + if (is_imx6q_uart(sport) && !uart_console(port) && + !sport->dma_is_inited) + imx_uart_dma_init(sport); + + spin_lock_irqsave(&sport->port.lock, flags); /* Reset fifo's and state machines */ i = 100; @@ -1132,14 +1142,15 @@ static int imx_startup(struct uart_port *port) while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) udelay(1); - spin_lock_irqsave(&sport->port.lock, flags); - /* * Finally, clear and enable interrupts */ writel(USR1_RTSD, sport->port.membase + USR1); writel(USR2_ORE, sport->port.membase + USR2); + if (sport->dma_is_inited && !sport->dma_is_enabled) + imx_enable_dma(sport); + temp = readl(sport->port.membase + UCR1); temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; @@ -1273,7 +1284,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; + unsigned int ucr2, old_ucr1, old_ucr2, baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned int div, ufcr; unsigned long num, denom; @@ -1310,11 +1321,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, } else { ucr2 |= UCR2_CTSC; } - - /* Can we enable the DMA support? */ - if (is_imx6q_uart(sport) && !uart_console(port) - && !sport->dma_is_inited) - imx_uart_dma_init(sport); } else { termios->c_cflag &= ~CRTSCTS; } @@ -1382,10 +1388,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, barrier(); /* then, disable everything */ - old_txrxen = readl(sport->port.membase + UCR2); - writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN), + old_ucr2 = readl(sport->port.membase + UCR2); + writel(old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN), sport->port.membase + UCR2); - old_txrxen &= (UCR2_TXEN | UCR2_RXEN); + old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN); /* custom-baudrate handling */ div = sport->port.uartclk / (baud * 16); @@ -1426,13 +1432,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, writel(old_ucr1, sport->port.membase + UCR1); /* set the parity, stop bits and data size */ - writel(ucr2 | old_txrxen, sport->port.membase + UCR2); + writel(ucr2 | old_ucr2, sport->port.membase + UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); - if (sport->dma_is_inited && !sport->dma_is_enabled) - imx_enable_dma(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -1498,7 +1502,7 @@ static int imx_poll_init(struct uart_port *port) if (retval) clk_disable_unprepare(sport->clk_ipg); - imx_setup_ufcr(sport, 0); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); spin_lock_irqsave(&sport->port.lock, flags); @@ -1768,7 +1772,7 @@ imx_console_setup(struct console *co, char *options) else imx_console_get_options(sport, &baud, &parity, &bits); - imx_setup_ufcr(sport, 0); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); @@ -1798,6 +1802,38 @@ static struct console imx_console = { }; #define IMX_CONSOLE &imx_console + +#ifdef CONFIG_OF +static void imx_console_early_putchar(struct uart_port *port, int ch) +{ + while (readl_relaxed(port->membase + IMX21_UTS) & UTS_TXFULL) + cpu_relax(); + + writel_relaxed(ch, port->membase + URTX0); +} + +static void imx_console_early_write(struct console *con, const char *s, + unsigned count) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, count, imx_console_early_putchar); +} + +static int __init +imx_console_early_setup(struct earlycon_device *dev, const char *opt) +{ + if (!dev->port.membase) + return -ENODEV; + + dev->con->write = imx_console_early_write; + + return 0; +} +OF_EARLYCON_DECLARE(ec_imx6q, "fsl,imx6q-uart", imx_console_early_setup); +OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup); +#endif + #else #define IMX_CONSOLE NULL #endif @@ -1812,36 +1848,6 @@ static struct uart_driver imx_reg = { .cons = IMX_CONSOLE, }; -static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) -{ - struct imx_port *sport = platform_get_drvdata(dev); - unsigned int val; - - /* enable wakeup from i.MX UART */ - val = readl(sport->port.membase + UCR3); - val |= UCR3_AWAKEN; - writel(val, sport->port.membase + UCR3); - - uart_suspend_port(&imx_reg, &sport->port); - - return 0; -} - -static int serial_imx_resume(struct platform_device *dev) -{ - struct imx_port *sport = platform_get_drvdata(dev); - unsigned int val; - - /* disable wakeup from i.MX UART */ - val = readl(sport->port.membase + UCR3); - val &= ~UCR3_AWAKEN; - writel(val, sport->port.membase + UCR3); - - uart_resume_port(&imx_reg, &sport->port); - - return 0; -} - #ifdef CONFIG_OF /* * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it @@ -1903,7 +1909,7 @@ static int serial_imx_probe(struct platform_device *pdev) { struct imx_port *sport; void __iomem *base; - int ret = 0; + int ret = 0, reg; struct resource *res; int txirq, rxirq, rtsirq; @@ -1958,6 +1964,19 @@ static int serial_imx_probe(struct platform_device *pdev) sport->port.uartclk = clk_get_rate(sport->clk_per); + /* For register access, we only need to enable the ipg clock. */ + ret = clk_prepare_enable(sport->clk_ipg); + if (ret) + return ret; + + /* Disable interrupts before requesting them */ + reg = readl_relaxed(sport->port.membase + UCR1); + reg &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | + UCR1_TXMPTYEN | UCR1_RTSDEN); + writel_relaxed(reg, sport->port.membase + UCR1); + + clk_disable_unprepare(sport->clk_ipg); + /* * Allocate the IRQ(s) i.MX1 has three interrupts whereas later * chips only have one interrupt. @@ -1993,16 +2012,135 @@ static int serial_imx_remove(struct platform_device *pdev) return uart_remove_one_port(&imx_reg, &sport->port); } +static void serial_imx_restore_context(struct imx_port *sport) +{ + if (!sport->context_saved) + return; + + writel(sport->saved_reg[4], sport->port.membase + UFCR); + writel(sport->saved_reg[5], sport->port.membase + UESC); + writel(sport->saved_reg[6], sport->port.membase + UTIM); + writel(sport->saved_reg[7], sport->port.membase + UBIR); + writel(sport->saved_reg[8], sport->port.membase + UBMR); + writel(sport->saved_reg[9], sport->port.membase + IMX21_UTS); + writel(sport->saved_reg[0], sport->port.membase + UCR1); + writel(sport->saved_reg[1] | UCR2_SRST, sport->port.membase + UCR2); + writel(sport->saved_reg[2], sport->port.membase + UCR3); + writel(sport->saved_reg[3], sport->port.membase + UCR4); + sport->context_saved = false; +} + +static void serial_imx_save_context(struct imx_port *sport) +{ + /* Save necessary regs */ + sport->saved_reg[0] = readl(sport->port.membase + UCR1); + sport->saved_reg[1] = readl(sport->port.membase + UCR2); + sport->saved_reg[2] = readl(sport->port.membase + UCR3); + sport->saved_reg[3] = readl(sport->port.membase + UCR4); + sport->saved_reg[4] = readl(sport->port.membase + UFCR); + sport->saved_reg[5] = readl(sport->port.membase + UESC); + sport->saved_reg[6] = readl(sport->port.membase + UTIM); + sport->saved_reg[7] = readl(sport->port.membase + UBIR); + sport->saved_reg[8] = readl(sport->port.membase + UBMR); + sport->saved_reg[9] = readl(sport->port.membase + IMX21_UTS); + sport->context_saved = true; +} + +static void serial_imx_enable_wakeup(struct imx_port *sport, bool on) +{ + unsigned int val; + + val = readl(sport->port.membase + UCR3); + if (on) + val |= UCR3_AWAKEN; + else + val &= ~UCR3_AWAKEN; + writel(val, sport->port.membase + UCR3); + + val = readl(sport->port.membase + UCR1); + if (on) + val |= UCR1_RTSDEN; + else + val &= ~UCR1_RTSDEN; + writel(val, sport->port.membase + UCR1); +} + +static int imx_serial_port_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx_port *sport = platform_get_drvdata(pdev); + int ret; + + ret = clk_enable(sport->clk_ipg); + if (ret) + return ret; + + serial_imx_save_context(sport); + + clk_disable(sport->clk_ipg); + + return 0; +} + +static int imx_serial_port_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx_port *sport = platform_get_drvdata(pdev); + int ret; + + ret = clk_enable(sport->clk_ipg); + if (ret) + return ret; + + serial_imx_restore_context(sport); + + clk_disable(sport->clk_ipg); + + return 0; +} + +static int imx_serial_port_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx_port *sport = platform_get_drvdata(pdev); + + /* enable wakeup from i.MX UART */ + serial_imx_enable_wakeup(sport, true); + + uart_suspend_port(&imx_reg, &sport->port); + + return 0; +} + +static int imx_serial_port_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx_port *sport = platform_get_drvdata(pdev); + + /* disable wakeup from i.MX UART */ + serial_imx_enable_wakeup(sport, false); + + uart_resume_port(&imx_reg, &sport->port); + + return 0; +} + +static const struct dev_pm_ops imx_serial_port_pm_ops = { + .suspend_noirq = imx_serial_port_suspend_noirq, + .resume_noirq = imx_serial_port_resume_noirq, + .suspend = imx_serial_port_suspend, + .resume = imx_serial_port_resume, +}; + static struct platform_driver serial_imx_driver = { .probe = serial_imx_probe, .remove = serial_imx_remove, - .suspend = serial_imx_suspend, - .resume = serial_imx_resume, .id_table = imx_uart_devtype, .driver = { .name = "imx-uart", .of_match_table = imx_uart_dt_ids, + .pm = &imx_serial_port_pm_ops, }, }; |