]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6985d496 SR |
2 | /* |
3 | * Copyright (C) 2016 Stefan Roese <[email protected]> | |
c53a30f0 | 4 | * Copyright (C) 2021 Pali Rohár <[email protected]> |
6985d496 SR |
5 | */ |
6 | ||
5f41bab8 | 7 | #include <clk.h> |
6985d496 SR |
8 | #include <dm.h> |
9 | #include <serial.h> | |
10 | #include <asm/io.h> | |
139d0813 | 11 | #include <asm/arch/cpu.h> |
5db5815e | 12 | #include <mach/soc.h> |
6985d496 | 13 | |
8a8d24bd | 14 | struct mvebu_plat { |
6985d496 | 15 | void __iomem *base; |
5f41bab8 T |
16 | ulong tbg_rate; |
17 | u8 tbg_idx; | |
6985d496 SR |
18 | }; |
19 | ||
20 | /* | |
21 | * Register offset | |
22 | */ | |
23 | #define UART_RX_REG 0x00 | |
24 | #define UART_TX_REG 0x04 | |
25 | #define UART_CTRL_REG 0x08 | |
26 | #define UART_STATUS_REG 0x0c | |
27 | #define UART_BAUD_REG 0x10 | |
28 | #define UART_POSSR_REG 0x14 | |
29 | ||
30 | #define UART_STATUS_RX_RDY 0x10 | |
aea2f721 | 31 | #define UART_STATUS_TX_EMPTY 0x40 |
6985d496 SR |
32 | #define UART_STATUS_TXFIFO_FULL 0x800 |
33 | ||
34 | #define UART_CTRL_RXFIFO_RESET 0x4000 | |
35 | #define UART_CTRL_TXFIFO_RESET 0x8000 | |
36 | ||
6985d496 SR |
37 | static int mvebu_serial_putc(struct udevice *dev, const char ch) |
38 | { | |
8a8d24bd | 39 | struct mvebu_plat *plat = dev_get_plat(dev); |
6985d496 SR |
40 | void __iomem *base = plat->base; |
41 | ||
1138bbe0 T |
42 | if (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL) |
43 | return -EAGAIN; | |
6985d496 SR |
44 | |
45 | writel(ch, base + UART_TX_REG); | |
46 | ||
47 | return 0; | |
48 | } | |
49 | ||
50 | static int mvebu_serial_getc(struct udevice *dev) | |
51 | { | |
8a8d24bd | 52 | struct mvebu_plat *plat = dev_get_plat(dev); |
6985d496 SR |
53 | void __iomem *base = plat->base; |
54 | ||
1138bbe0 T |
55 | if (!(readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY)) |
56 | return -EAGAIN; | |
6985d496 SR |
57 | |
58 | return readl(base + UART_RX_REG) & 0xff; | |
59 | } | |
60 | ||
61 | static int mvebu_serial_pending(struct udevice *dev, bool input) | |
62 | { | |
8a8d24bd | 63 | struct mvebu_plat *plat = dev_get_plat(dev); |
6985d496 SR |
64 | void __iomem *base = plat->base; |
65 | ||
aea2f721 T |
66 | if (input) { |
67 | if (readl(base + UART_STATUS_REG) & UART_STATUS_RX_RDY) | |
68 | return 1; | |
69 | } else { | |
70 | if (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY)) | |
71 | return 1; | |
72 | } | |
6985d496 SR |
73 | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static int mvebu_serial_setbrg(struct udevice *dev, int baudrate) | |
78 | { | |
8a8d24bd | 79 | struct mvebu_plat *plat = dev_get_plat(dev); |
6985d496 | 80 | void __iomem *base = plat->base; |
5f41bab8 T |
81 | u32 divider, d1, d2; |
82 | u32 oversampling; | |
6985d496 SR |
83 | |
84 | /* | |
85 | * Calculate divider | |
86 | * baudrate = clock / 16 / divider | |
87 | */ | |
5f41bab8 T |
88 | d1 = d2 = 1; |
89 | divider = DIV_ROUND_CLOSEST(plat->tbg_rate, baudrate * 16 * d1 * d2); | |
6985d496 SR |
90 | |
91 | /* | |
92 | * Set Programmable Oversampling Stack to 0, | |
93 | * UART defaults to 16x scheme | |
94 | */ | |
5f41bab8 T |
95 | oversampling = 0; |
96 | ||
97 | if (divider < 1) | |
98 | divider = 1; | |
99 | else if (divider > 1023) { | |
100 | /* | |
101 | * If divider is too high for selected baudrate then set | |
102 | * divider d1 to the maximal value 6. | |
103 | */ | |
104 | d1 = 6; | |
105 | divider = DIV_ROUND_CLOSEST(plat->tbg_rate, | |
106 | baudrate * 16 * d1 * d2); | |
107 | if (divider < 1) | |
108 | divider = 1; | |
109 | else if (divider > 1023) { | |
110 | /* | |
111 | * If divider is still too high then set also divider | |
112 | * d2 to the maximal value 6. | |
113 | */ | |
114 | d2 = 6; | |
115 | divider = DIV_ROUND_CLOSEST(plat->tbg_rate, | |
116 | baudrate * 16 * d1 * d2); | |
117 | if (divider < 1) | |
118 | divider = 1; | |
119 | else if (divider > 1023) { | |
120 | /* | |
121 | * And if divider is still to high then | |
122 | * use oversampling with maximal factor 63. | |
123 | */ | |
124 | oversampling = (63 << 0) | (63 << 8) | | |
125 | (63 << 16) | (63 << 24); | |
126 | divider = DIV_ROUND_CLOSEST(plat->tbg_rate, | |
127 | baudrate * 63 * d1 * d2); | |
128 | if (divider < 1) | |
129 | divider = 1; | |
130 | else if (divider > 1023) | |
131 | divider = 1023; | |
132 | } | |
133 | } | |
134 | } | |
135 | ||
136 | divider |= BIT(19); /* Do not use XTAL as a base clock */ | |
137 | divider |= d1 << 15; /* Set d1 divider */ | |
138 | divider |= d2 << 12; /* Set d2 divider */ | |
139 | divider |= plat->tbg_idx << 10; /* Use selected TBG as a base clock */ | |
140 | ||
141 | while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY)) | |
142 | ; | |
143 | writel(divider, base + UART_BAUD_REG); | |
144 | writel(oversampling, base + UART_POSSR_REG); | |
6985d496 SR |
145 | |
146 | return 0; | |
147 | } | |
148 | ||
149 | static int mvebu_serial_probe(struct udevice *dev) | |
150 | { | |
8a8d24bd | 151 | struct mvebu_plat *plat = dev_get_plat(dev); |
6985d496 | 152 | void __iomem *base = plat->base; |
5f41bab8 T |
153 | struct udevice *nb_clk; |
154 | ofnode nb_clk_node; | |
155 | int i, res; | |
156 | ||
157 | nb_clk_node = ofnode_by_compatible(ofnode_null(), | |
158 | "marvell,armada-3700-periph-clock-nb"); | |
159 | if (!ofnode_valid(nb_clk_node)) { | |
160 | printf("%s: NB periph clock node not available\n", __func__); | |
161 | return -ENODEV; | |
162 | } | |
163 | ||
164 | res = device_get_global_by_ofnode(nb_clk_node, &nb_clk); | |
165 | if (res) { | |
166 | printf("%s: Cannot get NB periph clock\n", __func__); | |
167 | return res; | |
168 | } | |
169 | ||
170 | /* | |
171 | * Choose the TBG clock with lowest frequency which allows to configure | |
172 | * UART also at lower baudrates. | |
173 | */ | |
174 | for (i = 0; i < 4; i++) { | |
175 | struct clk clk; | |
176 | ulong rate; | |
177 | ||
178 | res = clk_get_by_index_nodev(nb_clk_node, i, &clk); | |
179 | if (res) { | |
180 | printf("%s: Cannot get TBG clock %i: %i\n", __func__, | |
181 | i, res); | |
182 | return -ENODEV; | |
183 | } | |
184 | ||
185 | rate = clk_get_rate(&clk); | |
186 | if (!rate || IS_ERR_VALUE(rate)) { | |
187 | printf("%s: Cannot get rate for TBG clock %i\n", | |
188 | __func__, i); | |
189 | return -EINVAL; | |
190 | } | |
191 | ||
192 | if (!i || plat->tbg_rate > rate) { | |
193 | plat->tbg_rate = rate; | |
194 | plat->tbg_idx = i; | |
195 | } | |
196 | } | |
6985d496 SR |
197 | |
198 | /* reset FIFOs */ | |
199 | writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET, | |
200 | base + UART_CTRL_REG); | |
201 | ||
202 | /* No Parity, 1 Stop */ | |
203 | writel(0, base + UART_CTRL_REG); | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
8214728e T |
208 | static int mvebu_serial_remove(struct udevice *dev) |
209 | { | |
210 | struct mvebu_plat *plat = dev_get_plat(dev); | |
211 | void __iomem *base = plat->base; | |
212 | ulong new_parent_rate, parent_rate; | |
213 | u32 new_divider, divider; | |
214 | u32 new_oversampling; | |
215 | u32 oversampling; | |
216 | u32 d1, d2; | |
5db5815e | 217 | u32 nb_rst; |
8214728e T |
218 | |
219 | /* | |
220 | * Switch UART base clock back to XTAL because older Linux kernel | |
221 | * expects it. Otherwise it does not calculate UART divisor correctly | |
222 | * and therefore UART does not work in kernel. | |
223 | */ | |
224 | divider = readl(base + UART_BAUD_REG); | |
225 | if (!(divider & BIT(19))) /* UART already uses XTAL */ | |
226 | return 0; | |
227 | ||
228 | /* Read current divisors settings */ | |
229 | d1 = (divider >> 15) & 7; | |
230 | d2 = (divider >> 12) & 7; | |
231 | parent_rate = plat->tbg_rate; | |
232 | divider &= 1023; | |
233 | oversampling = readl(base + UART_POSSR_REG) & 63; | |
234 | if (!oversampling) | |
235 | oversampling = 16; | |
236 | ||
237 | /* Calculate new divisor against XTAL clock without changing baudrate */ | |
238 | new_oversampling = 0; | |
239 | new_parent_rate = get_ref_clk() * 1000000; | |
240 | new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * d2 * | |
241 | oversampling, parent_rate * 16); | |
242 | ||
243 | /* | |
244 | * UART does not work reliably when XTAL divisor is smaller than 4. | |
245 | * In this case we do not switch UART parent to XTAL. User either | |
246 | * configured unsupported settings or has newer kernel with patches | |
247 | * which allow usage of non-XTAL clock as a parent clock. | |
248 | */ | |
249 | if (new_divider < 4) | |
250 | return 0; | |
251 | ||
252 | /* | |
253 | * If new divisor is larger than maximal supported, try to switch | |
254 | * from default x16 scheme to oversampling with maximal factor 63. | |
255 | */ | |
256 | if (new_divider > 1023) { | |
257 | new_oversampling = 63; | |
258 | new_divider = DIV_ROUND_CLOSEST(new_parent_rate * divider * d1 * | |
259 | d2 * oversampling, | |
260 | parent_rate * new_oversampling); | |
261 | if (new_divider < 4 || new_divider > 1023) | |
262 | return 0; | |
263 | } | |
264 | ||
5db5815e | 265 | /* wait until TX empty */ |
8214728e T |
266 | while (!(readl(base + UART_STATUS_REG) & UART_STATUS_TX_EMPTY)) |
267 | ; | |
268 | ||
5db5815e T |
269 | /* external reset of UART via North Bridge Peripheral */ |
270 | nb_rst = readl(MVEBU_REGISTER(0x12400)); | |
271 | writel(nb_rst & ~BIT(3), MVEBU_REGISTER(0x12400)); | |
272 | writel(nb_rst | BIT(3), MVEBU_REGISTER(0x12400)); | |
273 | ||
274 | /* set baudrate and oversampling */ | |
8214728e T |
275 | writel(new_divider, base + UART_BAUD_REG); |
276 | writel(new_oversampling, base + UART_POSSR_REG); | |
277 | ||
5db5815e T |
278 | /* No Parity, 1 Stop */ |
279 | writel(0, base + UART_CTRL_REG); | |
280 | ||
8214728e T |
281 | return 0; |
282 | } | |
283 | ||
d1998a9f | 284 | static int mvebu_serial_of_to_plat(struct udevice *dev) |
6985d496 | 285 | { |
8a8d24bd | 286 | struct mvebu_plat *plat = dev_get_plat(dev); |
6985d496 | 287 | |
702e57e1 | 288 | plat->base = dev_read_addr_ptr(dev); |
6985d496 SR |
289 | |
290 | return 0; | |
291 | } | |
292 | ||
293 | static const struct dm_serial_ops mvebu_serial_ops = { | |
294 | .putc = mvebu_serial_putc, | |
295 | .pending = mvebu_serial_pending, | |
296 | .getc = mvebu_serial_getc, | |
297 | .setbrg = mvebu_serial_setbrg, | |
298 | }; | |
299 | ||
300 | static const struct udevice_id mvebu_serial_ids[] = { | |
301 | { .compatible = "marvell,armada-3700-uart" }, | |
302 | { } | |
303 | }; | |
304 | ||
305 | U_BOOT_DRIVER(serial_mvebu) = { | |
306 | .name = "serial_mvebu", | |
307 | .id = UCLASS_SERIAL, | |
308 | .of_match = mvebu_serial_ids, | |
d1998a9f | 309 | .of_to_plat = mvebu_serial_of_to_plat, |
8a8d24bd | 310 | .plat_auto = sizeof(struct mvebu_plat), |
6985d496 | 311 | .probe = mvebu_serial_probe, |
8214728e T |
312 | .remove = mvebu_serial_remove, |
313 | .flags = DM_FLAG_OS_PREPARE, | |
6985d496 | 314 | .ops = &mvebu_serial_ops, |
6985d496 SR |
315 | }; |
316 | ||
317 | #ifdef CONFIG_DEBUG_MVEBU_A3700_UART | |
318 | ||
319 | #include <debug_uart.h> | |
320 | ||
321 | static inline void _debug_uart_init(void) | |
322 | { | |
b62450cf | 323 | void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); |
5cd424d7 | 324 | u32 parent_rate, divider; |
6985d496 SR |
325 | |
326 | /* reset FIFOs */ | |
327 | writel(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET, | |
328 | base + UART_CTRL_REG); | |
329 | ||
330 | /* No Parity, 1 Stop */ | |
331 | writel(0, base + UART_CTRL_REG); | |
332 | ||
333 | /* | |
334 | * Calculate divider | |
335 | * baudrate = clock / 16 / divider | |
336 | */ | |
2cc4be28 T |
337 | parent_rate = (readl(MVEBU_REGISTER(0x13808)) & BIT(9)) ? |
338 | 40000000 : 25000000; | |
5cd424d7 | 339 | divider = DIV_ROUND_CLOSEST(parent_rate, CONFIG_BAUDRATE * 16); |
139d0813 | 340 | writel(divider, base + UART_BAUD_REG); |
6985d496 SR |
341 | |
342 | /* | |
343 | * Set Programmable Oversampling Stack to 0, | |
344 | * UART defaults to 16x scheme | |
345 | */ | |
346 | writel(0, base + UART_POSSR_REG); | |
347 | } | |
348 | ||
349 | static inline void _debug_uart_putc(int ch) | |
350 | { | |
b62450cf | 351 | void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); |
6985d496 SR |
352 | |
353 | while (readl(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL) | |
354 | ; | |
355 | ||
356 | writel(ch, base + UART_TX_REG); | |
357 | } | |
358 | ||
359 | DEBUG_UART_FUNCS | |
360 | #endif |