diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 55097e068908..b1e7190ae483 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -109,6 +110,11 @@ #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 @@ -219,6 +225,10 @@ #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);