summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/imx.c')
-rw-r--r--drivers/tty/serial/imx.c183
1 files changed, 146 insertions, 37 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 54fdc7866..d0388a071 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);
@@ -1810,36 +1817,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 +1878,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 +1933,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 +1981,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,
},
};