diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-08-31 14:28:07 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-08-31 14:28:07 -0700 |
| commit | c2078402e479f963168bfcf7a8de78ab63748a98 (patch) | |
| tree | 5aaf3182485eb0de49b16cc986fae274a3bad37c /drivers/tty/serial/imx.c | |
| parent | 2f37d65a6a5c360ba0c386a6aa0d2afcbda7060d (diff) | |
| parent | c868cbb7e5c6d3c74b8d38c7c356444f2c807c28 (diff) | |
Merge tag 'tty-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH:
"Here is the big tty/serial driver update for 4.3-rc1.
Not many major things, a number of driver updates and changes, and the
8250 driver got split up a bit to make it easier to work with by
moving some functions to a new file. Full details are in the
shortlog.
All have been in linux-next with no reported issues"
* tag 'tty-4.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (90 commits)
serial: imx: save and restore context in the suspend path
serial: imx: allow waking up on RTSD
serial: imx: introduce serial_imx_enable_wakeup()
serial: imx: remove unbalanced clk_prepare
serial: 8250: move rx_running out of the bitfield
tty: serial: 8250_omap: do not use RX DMA if pause is not supported
serial:8250_dw: do not alter CTS and DCTS since AFE is enabled
tty: serial: men_z135_uart.c: Don't initialize port->lock
tty: serial: men_z135_uart.c: Fix race between IRQ and set_termios()
serial: 8250: bind to ALi Fast Infrared Controller (ALI5123)
serial: 8250: don't bind to SMSC IrCC IR port
serial: mxs-auart: fix baud rate range
serial: mxs-auart: keep the AUART unit in reset state when not in use
serial: mxs-auart: use a function name to reflect what it really does
serial: 8250_pci: fix mode after S3/S4 resume for F81504/508/512
sc16is7xx: constify devtype
sc16is7xx: support multiple devices
sc16is7xx: save and use per-chip line number
uart: pl011: Add support to ZTE ZX296702 uart
uart: pl011: Improve LCRH register access decision
...
Diffstat (limited to 'drivers/tty/serial/imx.c')
| -rw-r--r-- | drivers/tty/serial/imx.c | 203 |
1 files changed, 152 insertions, 51 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 54fdc7866ea1..fe3d41cc8416 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -216,6 +216,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 { @@ -700,7 +702,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: @@ -766,7 +769,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); } @@ -921,8 +923,13 @@ static void dma_rx_callback(void *data) 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); start_rx_dma(sport); @@ -1624,12 +1631,12 @@ imx_console_write(struct console *co, const char *s, unsigned int count) int locked = 1; int retval; - retval = clk_enable(sport->clk_per); + retval = clk_prepare_enable(sport->clk_per); if (retval) return; - retval = clk_enable(sport->clk_ipg); + retval = clk_prepare_enable(sport->clk_ipg); if (retval) { - clk_disable(sport->clk_per); + clk_disable_unprepare(sport->clk_per); return; } @@ -1668,8 +1675,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count) if (locked) spin_unlock_irqrestore(&sport->port.lock, flags); - clk_disable(sport->clk_ipg); - clk_disable(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); + clk_disable_unprepare(sport->clk_per); } /* @@ -1770,15 +1777,7 @@ imx_console_setup(struct console *co, char *options) retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); - clk_disable(sport->clk_ipg); - if (retval) { - clk_unprepare(sport->clk_ipg); - goto error_console; - } - - retval = clk_prepare(sport->clk_per); - if (retval) - clk_disable_unprepare(sport->clk_ipg); + clk_disable_unprepare(sport->clk_ipg); error_console: return retval; @@ -1810,36 +1809,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 @@ -1901,7 +1870,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; @@ -1956,6 +1925,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. @@ -1991,16 +1973,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, }, }; |
