tty: serial: fsl_lpuart: do software reset for imx7ulp and imx8qxp
authorFugang Duan <fugang.duan@nxp.com>
Mon, 23 Aug 2021 09:18:01 +0000 (17:18 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 26 Aug 2021 12:49:20 +0000 (14:49 +0200)
Do software reset for communication port on imx7ulp and imx8qxp after
the port is registered if the UART controller support the feature.

Do partition reset with LPUART's power on, LPUART registers will
keep the previous status, like on i.MX8QXP platform, which is not
expected action, so need to set the RST bit of GLOBAL register to reset
all uart internal logic and registers.

Currently, only i.MX7ULP and i.MX8QXP LPUART controllers include
global register that support the software reset.

Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Link: https://lore.kernel.org/r/20210823091801.17447-1-sherry.sun@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/fsl_lpuart.c

index 55097e0..b1e7190 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/clk.h>
 #include <linux/console.h>
+#include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/dmapool.h>
 #define UARTSFIFO_TXOF         0x02
 #define UARTSFIFO_RXUF         0x01
 
+/* 32-bit global registers only for i.MX7ULP/i.MX8x
+ * Used to reset all internal logic and registers, except the Global Register.
+ */
+#define UART_GLOBAL            0x8
+
 /* 32-bit register definition */
 #define UARTBAUD               0x00
 #define UARTSTAT               0x04
 #define UARTWATER_TXWATER_OFF  0
 #define UARTWATER_RXWATER_OFF  16
 
+#define UART_GLOBAL_RST        0x2
+#define GLOBAL_RST_MIN_US      20
+#define GLOBAL_RST_MAX_US      40
+
 /* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
 #define DMA_RX_TIMEOUT         (10)
 
@@ -320,6 +330,11 @@ static inline bool is_layerscape_lpuart(struct lpuart_port *sport)
                sport->devtype == LS1028A_LPUART);
 }
 
+static inline bool is_imx7ulp_lpuart(struct lpuart_port *sport)
+{
+       return sport->devtype == IMX7ULP_LPUART;
+}
+
 static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport)
 {
        return sport->devtype == IMX8QXP_LPUART;
@@ -383,6 +398,33 @@ static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport)
 #define lpuart_enable_clks(x)  __lpuart_enable_clks(x, true)
 #define lpuart_disable_clks(x) __lpuart_enable_clks(x, false)
 
+static int lpuart_global_reset(struct lpuart_port *sport)
+{
+       struct uart_port *port = &sport->port;
+       void __iomem *global_addr;
+       int ret;
+
+       if (uart_console(port))
+               return 0;
+
+       ret = clk_prepare_enable(sport->ipg_clk);
+       if (ret) {
+               dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret);
+               return ret;
+       }
+
+       if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
+               global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF;
+               writel(UART_GLOBAL_RST, global_addr);
+               usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
+               writel(0, global_addr);
+               usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
+       }
+
+       clk_disable_unprepare(sport->ipg_clk);
+       return 0;
+}
+
 static void lpuart_stop_tx(struct uart_port *port)
 {
        unsigned char temp;
@@ -2699,6 +2741,10 @@ static int lpuart_probe(struct platform_device *pdev)
        if (ret)
                goto failed_attach_port;
 
+       ret = lpuart_global_reset(sport);
+       if (ret)
+               goto failed_reset;
+
        ret = uart_get_rs485_mode(&sport->port);
        if (ret)
                goto failed_get_rs485;
@@ -2715,6 +2761,8 @@ static int lpuart_probe(struct platform_device *pdev)
        return 0;
 
 failed_get_rs485:
+failed_reset:
+       uart_remove_one_port(&lpuart_reg, &sport->port);
 failed_attach_port:
 failed_irq_request:
        lpuart_disable_clks(sport);