Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tty: serial: fsl_lpuart: do software reset for imx7ulp and imx8qxp

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>

authored by

Fugang Duan and committed by
Greg Kroah-Hartman
bd5305dc 48422152

+48
+48
drivers/tty/serial/fsl_lpuart.c
··· 7 7 8 8 #include <linux/clk.h> 9 9 #include <linux/console.h> 10 + #include <linux/delay.h> 10 11 #include <linux/dma-mapping.h> 11 12 #include <linux/dmaengine.h> 12 13 #include <linux/dmapool.h> ··· 109 108 #define UARTSFIFO_RXOF 0x04 110 109 #define UARTSFIFO_TXOF 0x02 111 110 #define UARTSFIFO_RXUF 0x01 111 + 112 + /* 32-bit global registers only for i.MX7ULP/i.MX8x 113 + * Used to reset all internal logic and registers, except the Global Register. 114 + */ 115 + #define UART_GLOBAL 0x8 112 116 113 117 /* 32-bit register definition */ 114 118 #define UARTBAUD 0x00 ··· 225 219 #define UARTWATER_TXWATER_OFF 0 226 220 #define UARTWATER_RXWATER_OFF 16 227 221 222 + #define UART_GLOBAL_RST 0x2 223 + #define GLOBAL_RST_MIN_US 20 224 + #define GLOBAL_RST_MAX_US 40 225 + 228 226 /* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */ 229 227 #define DMA_RX_TIMEOUT (10) 230 228 ··· 330 320 sport->devtype == LS1028A_LPUART); 331 321 } 332 322 323 + static inline bool is_imx7ulp_lpuart(struct lpuart_port *sport) 324 + { 325 + return sport->devtype == IMX7ULP_LPUART; 326 + } 327 + 333 328 static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport) 334 329 { 335 330 return sport->devtype == IMX8QXP_LPUART; ··· 397 382 398 383 #define lpuart_enable_clks(x) __lpuart_enable_clks(x, true) 399 384 #define lpuart_disable_clks(x) __lpuart_enable_clks(x, false) 385 + 386 + static int lpuart_global_reset(struct lpuart_port *sport) 387 + { 388 + struct uart_port *port = &sport->port; 389 + void __iomem *global_addr; 390 + int ret; 391 + 392 + if (uart_console(port)) 393 + return 0; 394 + 395 + ret = clk_prepare_enable(sport->ipg_clk); 396 + if (ret) { 397 + dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret); 398 + return ret; 399 + } 400 + 401 + if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { 402 + global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF; 403 + writel(UART_GLOBAL_RST, global_addr); 404 + usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US); 405 + writel(0, global_addr); 406 + usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US); 407 + } 408 + 409 + clk_disable_unprepare(sport->ipg_clk); 410 + return 0; 411 + } 400 412 401 413 static void lpuart_stop_tx(struct uart_port *port) 402 414 { ··· 2741 2699 if (ret) 2742 2700 goto failed_attach_port; 2743 2701 2702 + ret = lpuart_global_reset(sport); 2703 + if (ret) 2704 + goto failed_reset; 2705 + 2744 2706 ret = uart_get_rs485_mode(&sport->port); 2745 2707 if (ret) 2746 2708 goto failed_get_rs485; ··· 2761 2715 return 0; 2762 2716 2763 2717 failed_get_rs485: 2718 + failed_reset: 2719 + uart_remove_one_port(&lpuart_reg, &sport->port); 2764 2720 failed_attach_port: 2765 2721 failed_irq_request: 2766 2722 lpuart_disable_clks(sport);