Commit 6c6c6d774aa9d982f8599492baaf12b2544c3e16
1 parent
c90b6544df
Exists in
smarc_8mm_imx_4.14.98_2.0.0_ga
and in
4 other branches
MLK-21445 serial: fsl_lpuart: do HW reset for communication port
Do HW reset for communication port 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.MX8QM platform, which is not expected action, so reset the HW is required. Currently, only i.MX7ULP and i.MX8QM LPUART controllers include global register that support HW reset. Tested-by: Robin Gong <yibin.gong@nxp.com> Tested-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Robby Cai <robby.cai@nxp.com> Signed-off-by: Fugang Duan <fugang.duan@nxp.com> (cherry picked from commit c2bc1f62ec28981462c9cb5ceac17134931ca19f)
Showing 1 changed file with 43 additions and 0 deletions Side-by-side Diff
drivers/tty/serial/fsl_lpuart.c
... | ... | @@ -131,6 +131,11 @@ |
131 | 131 | #define UARTFIFO 0x18 |
132 | 132 | #define UARTWATER 0x1c |
133 | 133 | |
134 | +/* 32-bit global registers only for i.MX7ulp/MX8x | |
135 | + * The driver only use the reset feature to reset HW. | |
136 | + */ | |
137 | +#define UART_GLOBAL 0x8 | |
138 | + | |
134 | 139 | #define UARTBAUD_MAEN1 0x80000000 |
135 | 140 | #define UARTBAUD_MAEN2 0x40000000 |
136 | 141 | #define UARTBAUD_M10 0x20000000 |
... | ... | @@ -236,6 +241,10 @@ |
236 | 241 | #define UARTWATER_TXWATER_OFF 0 |
237 | 242 | #define UARTWATER_RXWATER_OFF 16 |
238 | 243 | |
244 | +#define UART_GLOBAL_RST 0x2 | |
245 | +#define RST_HW_MIN_US 20 | |
246 | +#define RST_HW_MAX_US 40 | |
247 | + | |
239 | 248 | #define UARTFIFO_RXIDEN_RDRF 0x3 |
240 | 249 | #define UARTCTRL_IDLECFG 0x7 |
241 | 250 | #define FSL_UART_RX_DMA_BUFFER_SIZE 128 |
... | ... | @@ -250,6 +259,8 @@ |
250 | 259 | |
251 | 260 | struct lpuart_port { |
252 | 261 | struct uart_port port; |
262 | + void __iomem *regbase; | |
263 | + | |
253 | 264 | struct clk *ipg_clk; |
254 | 265 | struct clk *per_clk; |
255 | 266 | unsigned int txfifo_size; |
... | ... | @@ -346,6 +357,33 @@ |
346 | 357 | } |
347 | 358 | } |
348 | 359 | |
360 | +static int lpuart_hw_reset(struct lpuart_port *sport) | |
361 | +{ | |
362 | + struct uart_port *port = &sport->port; | |
363 | + struct device_node *np = sport->port.dev->of_node; | |
364 | + int ret; | |
365 | + | |
366 | + if (uart_console(port)) | |
367 | + return 0; | |
368 | + | |
369 | + ret = clk_prepare_enable(sport->ipg_clk); | |
370 | + if (ret) { | |
371 | + dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret); | |
372 | + return ret; | |
373 | + } | |
374 | + | |
375 | + if (np && (of_device_is_compatible(np, "fsl,imx7ulp-lpuart") || | |
376 | + of_device_is_compatible(np, "fsl,imx8qm-lpuart"))) { | |
377 | + writel(UART_GLOBAL_RST, sport->regbase + UART_GLOBAL); | |
378 | + usleep_range(RST_HW_MIN_US, RST_HW_MAX_US); | |
379 | + writel(0, sport->regbase + UART_GLOBAL); | |
380 | + usleep_range(RST_HW_MIN_US, RST_HW_MAX_US); | |
381 | + } | |
382 | + | |
383 | + clk_disable_unprepare(sport->ipg_clk); | |
384 | + return 0; | |
385 | +} | |
386 | + | |
349 | 387 | static void lpuart_stop_tx(struct uart_port *port) |
350 | 388 | { |
351 | 389 | unsigned char temp; |
... | ... | @@ -2420,6 +2458,7 @@ |
2420 | 2458 | if (IS_ERR(sport->port.membase)) |
2421 | 2459 | return PTR_ERR(sport->port.membase); |
2422 | 2460 | |
2461 | + sport->regbase = sport->port.membase; | |
2423 | 2462 | sport->port.membase += sdata->reg_off; |
2424 | 2463 | sport->port.mapbase = res->start + sdata->reg_off; |
2425 | 2464 | sport->port.dev = &pdev->dev; |
... | ... | @@ -2500,6 +2539,10 @@ |
2500 | 2539 | pm_runtime_enable(&pdev->dev); |
2501 | 2540 | |
2502 | 2541 | ret = uart_add_one_port(&lpuart_reg, &sport->port); |
2542 | + if (ret) | |
2543 | + goto failed_attach_port; | |
2544 | + | |
2545 | + ret = lpuart_hw_reset(sport); | |
2503 | 2546 | if (ret) |
2504 | 2547 | goto failed_attach_port; |
2505 | 2548 |