]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e6e912c4 KW |
2 | /* |
3 | * arch/arm/mach-lpc32xx/serial.c | |
4 | * | |
5 | * Author: Kevin Wells <kevin.wells@nxp.com> | |
6 | * | |
7 | * Copyright (C) 2010 NXP Semiconductors | |
e6e912c4 KW |
8 | */ |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/types.h> | |
12 | #include <linux/serial.h> | |
13 | #include <linux/serial_core.h> | |
14 | #include <linux/serial_reg.h> | |
15 | #include <linux/serial_8250.h> | |
16 | #include <linux/clk.h> | |
17 | #include <linux/io.h> | |
ecd2a576 | 18 | #include <linux/soc/nxp/lpc32xx-misc.h> |
e6e912c4 | 19 | |
d3532910 | 20 | #include "lpc32xx.h" |
e6e912c4 KW |
21 | #include "common.h" |
22 | ||
23 | #define LPC32XX_SUART_FIFO_SIZE 64 | |
24 | ||
e6e912c4 KW |
25 | struct uartinit { |
26 | char *uart_ck_name; | |
27 | u32 ck_mode_mask; | |
28 | void __iomem *pdiv_clk_reg; | |
2707208e | 29 | resource_size_t mapbase; |
e6e912c4 KW |
30 | }; |
31 | ||
32 | static struct uartinit uartinit_data[] __initdata = { | |
e6e912c4 KW |
33 | { |
34 | .uart_ck_name = "uart5_ck", | |
35 | .ck_mode_mask = | |
36 | LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 5), | |
37 | .pdiv_clk_reg = LPC32XX_CLKPWR_UART5_CLK_CTRL, | |
2707208e | 38 | .mapbase = LPC32XX_UART5_BASE, |
e6e912c4 | 39 | }, |
e6e912c4 KW |
40 | { |
41 | .uart_ck_name = "uart3_ck", | |
42 | .ck_mode_mask = | |
43 | LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 3), | |
44 | .pdiv_clk_reg = LPC32XX_CLKPWR_UART3_CLK_CTRL, | |
2707208e | 45 | .mapbase = LPC32XX_UART3_BASE, |
e6e912c4 | 46 | }, |
e6e912c4 KW |
47 | { |
48 | .uart_ck_name = "uart4_ck", | |
49 | .ck_mode_mask = | |
50 | LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 4), | |
51 | .pdiv_clk_reg = LPC32XX_CLKPWR_UART4_CLK_CTRL, | |
2707208e | 52 | .mapbase = LPC32XX_UART4_BASE, |
e6e912c4 | 53 | }, |
e6e912c4 KW |
54 | { |
55 | .uart_ck_name = "uart6_ck", | |
56 | .ck_mode_mask = | |
57 | LPC32XX_UART_CLKMODE_LOAD(LPC32XX_UART_CLKMODE_ON, 6), | |
58 | .pdiv_clk_reg = LPC32XX_CLKPWR_UART6_CLK_CTRL, | |
2707208e | 59 | .mapbase = LPC32XX_UART6_BASE, |
e6e912c4 | 60 | }, |
e6e912c4 KW |
61 | }; |
62 | ||
ffba29c9 AB |
63 | /* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */ |
64 | void lpc32xx_loopback_set(resource_size_t mapbase, int state) | |
65 | { | |
66 | int bit; | |
67 | u32 tmp; | |
68 | ||
69 | switch (mapbase) { | |
70 | case LPC32XX_HS_UART1_BASE: | |
71 | bit = 0; | |
72 | break; | |
73 | case LPC32XX_HS_UART2_BASE: | |
74 | bit = 1; | |
75 | break; | |
76 | case LPC32XX_HS_UART7_BASE: | |
77 | bit = 6; | |
78 | break; | |
79 | default: | |
80 | WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase); | |
81 | return; | |
82 | } | |
83 | ||
84 | tmp = readl(LPC32XX_UARTCTL_CLOOP); | |
85 | if (state) | |
86 | tmp |= (1 << bit); | |
87 | else | |
88 | tmp &= ~(1 << bit); | |
89 | writel(tmp, LPC32XX_UARTCTL_CLOOP); | |
90 | } | |
91 | EXPORT_SYMBOL_GPL(lpc32xx_loopback_set); | |
92 | ||
e6e912c4 KW |
93 | void __init lpc32xx_serial_init(void) |
94 | { | |
95 | u32 tmp, clkmodes = 0; | |
96 | struct clk *clk; | |
97 | unsigned int puart; | |
98 | int i, j; | |
99 | ||
e6e912c4 KW |
100 | for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) { |
101 | clk = clk_get(NULL, uartinit_data[i].uart_ck_name); | |
102 | if (!IS_ERR(clk)) { | |
103 | clk_enable(clk); | |
e6e912c4 KW |
104 | } |
105 | ||
e6e912c4 KW |
106 | /* Setup UART clock modes for all UARTs, disable autoclock */ |
107 | clkmodes |= uartinit_data[i].ck_mode_mask; | |
108 | ||
109 | /* pre-UART clock divider set to 1 */ | |
110 | __raw_writel(0x0101, uartinit_data[i].pdiv_clk_reg); | |
2707208e RS |
111 | |
112 | /* | |
113 | * Force a flush of the RX FIFOs to work around a | |
114 | * HW bug | |
115 | */ | |
116 | puart = uartinit_data[i].mapbase; | |
117 | __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart)); | |
118 | __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart)); | |
119 | j = LPC32XX_SUART_FIFO_SIZE; | |
120 | while (j--) | |
121 | tmp = __raw_readl( | |
122 | LPC32XX_UART_DLL_FIFO(puart)); | |
123 | __raw_writel(0, LPC32XX_UART_IIR_FCR(puart)); | |
e6e912c4 KW |
124 | } |
125 | ||
126 | /* This needs to be done after all UART clocks are setup */ | |
127 | __raw_writel(clkmodes, LPC32XX_UARTCTL_CLKMODE); | |
ff424aa4 | 128 | for (i = 0; i < ARRAY_SIZE(uartinit_data); i++) { |
e6e912c4 | 129 | /* Force a flush of the RX FIFOs to work around a HW bug */ |
c70426f1 | 130 | puart = uartinit_data[i].mapbase; |
e6e912c4 KW |
131 | __raw_writel(0xC1, LPC32XX_UART_IIR_FCR(puart)); |
132 | __raw_writel(0x00, LPC32XX_UART_DLL_FIFO(puart)); | |
133 | j = LPC32XX_SUART_FIFO_SIZE; | |
134 | while (j--) | |
135 | tmp = __raw_readl(LPC32XX_UART_DLL_FIFO(puart)); | |
136 | __raw_writel(0, LPC32XX_UART_IIR_FCR(puart)); | |
137 | } | |
138 | ||
5fe8f11c APS |
139 | /* Disable IrDA pulsing support on UART6 */ |
140 | tmp = __raw_readl(LPC32XX_UARTCTL_CTRL); | |
141 | tmp |= LPC32XX_UART_UART6_IRDAMOD_BYPASS; | |
142 | __raw_writel(tmp, LPC32XX_UARTCTL_CTRL); | |
143 | ||
e6e912c4 KW |
144 | /* Disable UART5->USB transparent mode or USB won't work */ |
145 | tmp = __raw_readl(LPC32XX_UARTCTL_CTRL); | |
146 | tmp &= ~LPC32XX_UART_U5_ROUTE_TO_USB; | |
147 | __raw_writel(tmp, LPC32XX_UARTCTL_CTRL); | |
e6e912c4 | 148 | } |