serial: imx: implement shadow registers for UCRx and UFCR
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Fri, 2 Mar 2018 10:07:20 +0000 (11:07 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 9 Mar 2018 18:21:01 +0000 (10:21 -0800)
This reduces the amount of read accesses to the register space by
shadowing the values for five registers that only change on writing
them. There is a single bit in UCR2 that might change without being
written to it, this is handled accordingly.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/imx.c

index 659a949..57891d2 100644 (file)
@@ -204,6 +204,13 @@ struct imx_port {
 
        struct mctrl_gpios *gpios;
 
+       /* shadow registers */
+       unsigned int ucr1;
+       unsigned int ucr2;
+       unsigned int ucr3;
+       unsigned int ucr4;
+       unsigned int ufcr;
+
        /* DMA fields */
        unsigned int            dma_is_enabled:1;
        unsigned int            dma_is_rxing:1;
@@ -275,12 +282,56 @@ MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
 
 static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset)
 {
+       switch (offset) {
+       case UCR1:
+               sport->ucr1 = val;
+               break;
+       case UCR2:
+               sport->ucr2 = val;
+               break;
+       case UCR3:
+               sport->ucr3 = val;
+               break;
+       case UCR4:
+               sport->ucr4 = val;
+               break;
+       case UFCR:
+               sport->ufcr = val;
+               break;
+       default:
+               break;
+       }
        writel(val, sport->port.membase + offset);
 }
 
 static u32 imx_uart_readl(struct imx_port *sport, u32 offset)
 {
-       return readl(sport->port.membase + offset);
+       switch (offset) {
+       case UCR1:
+               return sport->ucr1;
+               break;
+       case UCR2:
+               /*
+                * UCR2_SRST is the only bit in the cached registers that might
+                * differ from the value that was last written. As it only
+                * clears after being set, reread conditionally.
+                */
+               if (sport->ucr2 & UCR2_SRST)
+                       sport->ucr2 = readl(sport->port.membase + offset);
+               return sport->ucr2;
+               break;
+       case UCR3:
+               return sport->ucr3;
+               break;
+       case UCR4:
+               return sport->ucr4;
+               break;
+       case UFCR:
+               return sport->ufcr;
+               break;
+       default:
+               return readl(sport->port.membase + offset);
+       }
 }
 
 static inline unsigned uts_reg(struct imx_port *sport)
@@ -2136,6 +2187,13 @@ static int serial_imx_probe(struct platform_device *pdev)
                return ret;
        }
 
+       /* initialize shadow register values */
+       sport->ucr1 = readl(sport->port.membase + UCR1);
+       sport->ucr2 = readl(sport->port.membase + UCR2);
+       sport->ucr3 = readl(sport->port.membase + UCR3);
+       sport->ucr4 = readl(sport->port.membase + UCR4);
+       sport->ufcr = readl(sport->port.membase + UFCR);
+
        uart_get_rs485_mode(&pdev->dev, &sport->port.rs485);
 
        if (sport->port.rs485.flags & SER_RS485_ENABLED &&