]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
3d3befa7 WD |
2 | /* |
3 | * (C) Copyright 2000 | |
4 | * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. | |
5 | * | |
6 | * (C) Copyright 2004 | |
7 | * ARM Ltd. | |
8 | * Philippe Robin, <philippe.robin@arm.com> | |
3d3befa7 WD |
9 | */ |
10 | ||
48d0192f | 11 | /* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ |
3d3befa7 | 12 | |
d678a59d | 13 | #include <common.h> |
401d1c4f | 14 | #include <asm/global_data.h> |
e3e2d662 AP |
15 | /* For get_bus_freq() */ |
16 | #include <clock_legacy.h> | |
8a9cd5ad | 17 | #include <dm.h> |
e3e2d662 | 18 | #include <clk.h> |
aed2fbef | 19 | #include <errno.h> |
8b616edb | 20 | #include <watchdog.h> |
249d5219 | 21 | #include <asm/io.h> |
39f61477 | 22 | #include <serial.h> |
6c9662df | 23 | #include <dm/device_compat.h> |
86256b79 | 24 | #include <dm/platform_data/serial_pl01x.h> |
39f61477 | 25 | #include <linux/compiler.h> |
aed2fbef | 26 | #include "serial_pl01x_internal.h" |
69751729 VM |
27 | |
28 | DECLARE_GLOBAL_DATA_PTR; | |
3d3befa7 | 29 | |
0478dac6 | 30 | #if !CONFIG_IS_ENABLED(DM_SERIAL) |
b861574b | 31 | static volatile unsigned char *const port[] = CFG_PL01x_PORTS; |
236f2ec4 MB |
32 | static enum pl01x_type pl01x_type __section(".data"); |
33 | static struct pl01x_regs *base_regs __section(".data"); | |
6705d81e | 34 | #define NUM_PORTS (sizeof(port)/sizeof(port[0])) |
3d3befa7 | 35 | |
8a9cd5ad | 36 | #endif |
3d3befa7 | 37 | |
aed2fbef | 38 | static int pl01x_putc(struct pl01x_regs *regs, char c) |
72d5e44c | 39 | { |
aed2fbef SG |
40 | /* Wait until there is space in the FIFO */ |
41 | if (readl(®s->fr) & UART_PL01x_FR_TXFF) | |
42 | return -EAGAIN; | |
42dfe7a1 | 43 | |
aed2fbef SG |
44 | /* Send the character */ |
45 | writel(c, ®s->dr); | |
42dfe7a1 | 46 | |
aed2fbef SG |
47 | return 0; |
48 | } | |
42dfe7a1 | 49 | |
aed2fbef SG |
50 | static int pl01x_getc(struct pl01x_regs *regs) |
51 | { | |
52 | unsigned int data; | |
42dfe7a1 | 53 | |
aed2fbef SG |
54 | /* Wait until there is data in the FIFO */ |
55 | if (readl(®s->fr) & UART_PL01x_FR_RXFE) | |
56 | return -EAGAIN; | |
42dfe7a1 | 57 | |
aed2fbef | 58 | data = readl(®s->dr); |
42dfe7a1 | 59 | |
aed2fbef SG |
60 | /* Check for an error flag */ |
61 | if (data & 0xFFFFFF00) { | |
62 | /* Clear the error */ | |
63 | writel(0xFFFFFFFF, ®s->ecr); | |
64 | return -1; | |
42dfe7a1 WD |
65 | } |
66 | ||
aed2fbef | 67 | return (int) data; |
3d3befa7 WD |
68 | } |
69 | ||
aed2fbef SG |
70 | static int pl01x_tstc(struct pl01x_regs *regs) |
71 | { | |
29caf930 | 72 | schedule(); |
aed2fbef SG |
73 | return !(readl(®s->fr) & UART_PL01x_FR_RXFE); |
74 | } | |
20c9226c | 75 | |
aed2fbef SG |
76 | static int pl01x_generic_serial_init(struct pl01x_regs *regs, |
77 | enum pl01x_type type) | |
20c9226c | 78 | { |
eb8a4fe0 VM |
79 | switch (type) { |
80 | case TYPE_PL010: | |
81 | /* disable everything */ | |
82 | writel(0, ®s->pl010_cr); | |
83 | break; | |
84 | case TYPE_PL011: | |
f7e517b4 VM |
85 | /* disable everything */ |
86 | writel(0, ®s->pl011_cr); | |
d2ca9fd2 VM |
87 | break; |
88 | default: | |
89 | return -EINVAL; | |
90 | } | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
d77447fd | 95 | static int pl011_set_line_control(struct pl01x_regs *regs) |
d2ca9fd2 VM |
96 | { |
97 | unsigned int lcr; | |
98 | /* | |
99 | * Internal update of baud rate register require line | |
100 | * control register write | |
101 | */ | |
102 | lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN; | |
d2ca9fd2 | 103 | writel(lcr, ®s->pl011_lcrh); |
20c9226c AE |
104 | return 0; |
105 | } | |
106 | ||
aed2fbef SG |
107 | static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type, |
108 | int clock, int baudrate) | |
3d3befa7 | 109 | { |
aed2fbef SG |
110 | switch (type) { |
111 | case TYPE_PL010: { | |
112 | unsigned int divisor; | |
113 | ||
d77447fd LW |
114 | /* disable everything */ |
115 | writel(0, ®s->pl010_cr); | |
116 | ||
aed2fbef SG |
117 | switch (baudrate) { |
118 | case 9600: | |
119 | divisor = UART_PL010_BAUD_9600; | |
120 | break; | |
121 | case 19200: | |
b2aa8894 | 122 | divisor = UART_PL010_BAUD_19200; |
aed2fbef SG |
123 | break; |
124 | case 38400: | |
125 | divisor = UART_PL010_BAUD_38400; | |
126 | break; | |
127 | case 57600: | |
128 | divisor = UART_PL010_BAUD_57600; | |
129 | break; | |
130 | case 115200: | |
131 | divisor = UART_PL010_BAUD_115200; | |
132 | break; | |
133 | default: | |
134 | divisor = UART_PL010_BAUD_38400; | |
135 | } | |
136 | ||
137 | writel((divisor & 0xf00) >> 8, ®s->pl010_lcrm); | |
138 | writel(divisor & 0xff, ®s->pl010_lcrl); | |
139 | ||
d77447fd LW |
140 | /* |
141 | * Set line control for the PL010 to be 8 bits, 1 stop bit, | |
142 | * no parity, fifo enabled | |
143 | */ | |
144 | writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, | |
145 | ®s->pl010_lcrh); | |
aed2fbef SG |
146 | /* Finally, enable the UART */ |
147 | writel(UART_PL010_CR_UARTEN, ®s->pl010_cr); | |
148 | break; | |
149 | } | |
150 | case TYPE_PL011: { | |
151 | unsigned int temp; | |
152 | unsigned int divider; | |
153 | unsigned int remainder; | |
154 | unsigned int fraction; | |
3d3befa7 | 155 | |
e3e2d662 AP |
156 | /* Without a valid clock rate we cannot set up the baudrate. */ |
157 | if (clock) { | |
158 | /* | |
159 | * Set baud rate | |
160 | * | |
161 | * IBRD = UART_CLK / (16 * BAUD_RATE) | |
162 | * FBRD = RND((64 * MOD(UART_CLK,(16 * BAUD_RATE))) | |
163 | * / (16 * BAUD_RATE)) | |
164 | */ | |
165 | temp = 16 * baudrate; | |
166 | divider = clock / temp; | |
167 | remainder = clock % temp; | |
168 | temp = (8 * remainder) / baudrate; | |
169 | fraction = (temp >> 1) + (temp & 1); | |
170 | ||
171 | writel(divider, ®s->pl011_ibrd); | |
172 | writel(fraction, ®s->pl011_fbrd); | |
173 | } | |
aed2fbef | 174 | |
d77447fd | 175 | pl011_set_line_control(regs); |
aed2fbef SG |
176 | /* Finally, enable the UART */ |
177 | writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | | |
178 | UART_PL011_CR_RXE | UART_PL011_CR_RTS, ®s->pl011_cr); | |
179 | break; | |
180 | } | |
181 | default: | |
182 | return -EINVAL; | |
183 | } | |
3d3befa7 | 184 | |
aed2fbef | 185 | return 0; |
3d3befa7 WD |
186 | } |
187 | ||
0478dac6 | 188 | #if !CONFIG_IS_ENABLED(DM_SERIAL) |
aed2fbef | 189 | static void pl01x_serial_init_baud(int baudrate) |
3d3befa7 | 190 | { |
aed2fbef SG |
191 | int clock = 0; |
192 | ||
bc08dc56 | 193 | #if defined(CONFIG_PL011_SERIAL) |
aed2fbef | 194 | pl01x_type = TYPE_PL011; |
f410d0ac | 195 | clock = CFG_PL011_CLOCK; |
aed2fbef SG |
196 | #endif |
197 | base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX]; | |
198 | ||
199 | pl01x_generic_serial_init(base_regs, pl01x_type); | |
a7deea69 | 200 | pl01x_generic_setbrg(base_regs, pl01x_type, clock, baudrate); |
3d3befa7 WD |
201 | } |
202 | ||
aed2fbef SG |
203 | /* |
204 | * Integrator AP has two UARTs, we use the first one, at 38400-8-N-1 | |
205 | * Integrator CP has two UARTs, use the first one, at 38400-8-N-1 | |
206 | * Versatile PB has four UARTs. | |
207 | */ | |
208 | int pl01x_serial_init(void) | |
3d3befa7 | 209 | { |
aed2fbef | 210 | pl01x_serial_init_baud(CONFIG_BAUDRATE); |
96baa4c3 | 211 | |
aed2fbef | 212 | return 0; |
3d3befa7 WD |
213 | } |
214 | ||
aed2fbef | 215 | static void pl01x_serial_putc(const char c) |
3d3befa7 | 216 | { |
aed2fbef SG |
217 | if (c == '\n') |
218 | while (pl01x_putc(base_regs, '\r') == -EAGAIN); | |
42dfe7a1 | 219 | |
aed2fbef | 220 | while (pl01x_putc(base_regs, c) == -EAGAIN); |
3d3befa7 WD |
221 | } |
222 | ||
aed2fbef | 223 | static int pl01x_serial_getc(void) |
3d3befa7 | 224 | { |
aed2fbef SG |
225 | while (1) { |
226 | int ch = pl01x_getc(base_regs); | |
42dfe7a1 | 227 | |
aed2fbef | 228 | if (ch == -EAGAIN) { |
29caf930 | 229 | schedule(); |
aed2fbef SG |
230 | continue; |
231 | } | |
42dfe7a1 | 232 | |
aed2fbef | 233 | return ch; |
42dfe7a1 | 234 | } |
3d3befa7 WD |
235 | } |
236 | ||
aed2fbef | 237 | static int pl01x_serial_tstc(void) |
3d3befa7 | 238 | { |
aed2fbef SG |
239 | return pl01x_tstc(base_regs); |
240 | } | |
72d5e44c | 241 | |
aed2fbef SG |
242 | static void pl01x_serial_setbrg(void) |
243 | { | |
244 | /* | |
245 | * Flush FIFO and wait for non-busy before changing baudrate to avoid | |
246 | * crap in console | |
247 | */ | |
248 | while (!(readl(&base_regs->fr) & UART_PL01x_FR_TXFE)) | |
29caf930 | 249 | schedule(); |
aed2fbef | 250 | while (readl(&base_regs->fr) & UART_PL01x_FR_BUSY) |
29caf930 | 251 | schedule(); |
aed2fbef | 252 | pl01x_serial_init_baud(gd->baudrate); |
3d3befa7 | 253 | } |
39f61477 | 254 | |
39f61477 MV |
255 | static struct serial_device pl01x_serial_drv = { |
256 | .name = "pl01x_serial", | |
257 | .start = pl01x_serial_init, | |
258 | .stop = NULL, | |
259 | .setbrg = pl01x_serial_setbrg, | |
260 | .putc = pl01x_serial_putc, | |
ec3fd689 | 261 | .puts = default_serial_puts, |
39f61477 MV |
262 | .getc = pl01x_serial_getc, |
263 | .tstc = pl01x_serial_tstc, | |
264 | }; | |
265 | ||
266 | void pl01x_serial_initialize(void) | |
267 | { | |
268 | serial_register(&pl01x_serial_drv); | |
269 | } | |
270 | ||
271 | __weak struct serial_device *default_serial_console(void) | |
272 | { | |
273 | return &pl01x_serial_drv; | |
274 | } | |
0478dac6 | 275 | #else |
c9bf43dd | 276 | int pl01x_serial_setbrg(struct udevice *dev, int baudrate) |
8a9cd5ad | 277 | { |
8a8d24bd | 278 | struct pl01x_serial_plat *plat = dev_get_plat(dev); |
8a9cd5ad SG |
279 | struct pl01x_priv *priv = dev_get_priv(dev); |
280 | ||
cd0fa5bf EA |
281 | if (!plat->skip_init) { |
282 | pl01x_generic_setbrg(priv->regs, priv->type, plat->clock, | |
283 | baudrate); | |
284 | } | |
8a9cd5ad SG |
285 | |
286 | return 0; | |
287 | } | |
288 | ||
6001985f | 289 | int pl01x_serial_probe(struct udevice *dev) |
8a9cd5ad | 290 | { |
8a8d24bd | 291 | struct pl01x_serial_plat *plat = dev_get_plat(dev); |
8a9cd5ad | 292 | struct pl01x_priv *priv = dev_get_priv(dev); |
91febe80 | 293 | int ret; |
8a9cd5ad | 294 | |
875752ad LM |
295 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
296 | struct dtd_serial_pl01x *dtplat = &plat->dtplat; | |
297 | ||
298 | priv->regs = (struct pl01x_regs *)dtplat->reg[0]; | |
299 | plat->type = dtplat->type; | |
300 | #else | |
8a9cd5ad | 301 | priv->regs = (struct pl01x_regs *)plat->base; |
875752ad | 302 | #endif |
8a9cd5ad | 303 | priv->type = plat->type; |
875752ad | 304 | |
91febe80 YX |
305 | if (!plat->skip_init) { |
306 | ret = pl01x_generic_serial_init(priv->regs, priv->type); | |
307 | if (ret) | |
308 | return ret; | |
309 | return pl01x_serial_setbrg(dev, gd->baudrate); | |
310 | } else { | |
cd0fa5bf | 311 | return 0; |
91febe80 | 312 | } |
8a9cd5ad SG |
313 | } |
314 | ||
c9bf43dd | 315 | int pl01x_serial_getc(struct udevice *dev) |
8a9cd5ad SG |
316 | { |
317 | struct pl01x_priv *priv = dev_get_priv(dev); | |
318 | ||
319 | return pl01x_getc(priv->regs); | |
320 | } | |
321 | ||
c9bf43dd | 322 | int pl01x_serial_putc(struct udevice *dev, const char ch) |
8a9cd5ad SG |
323 | { |
324 | struct pl01x_priv *priv = dev_get_priv(dev); | |
325 | ||
326 | return pl01x_putc(priv->regs, ch); | |
327 | } | |
328 | ||
c9bf43dd | 329 | int pl01x_serial_pending(struct udevice *dev, bool input) |
8a9cd5ad SG |
330 | { |
331 | struct pl01x_priv *priv = dev_get_priv(dev); | |
332 | unsigned int fr = readl(&priv->regs->fr); | |
333 | ||
334 | if (input) | |
335 | return pl01x_tstc(priv->regs); | |
336 | else | |
a2178919 | 337 | return fr & UART_PL01x_FR_TXFE ? 0 : 1; |
8a9cd5ad SG |
338 | } |
339 | ||
c9bf43dd | 340 | static const struct dm_serial_ops pl01x_serial_ops = { |
8a9cd5ad SG |
341 | .putc = pl01x_serial_putc, |
342 | .pending = pl01x_serial_pending, | |
343 | .getc = pl01x_serial_getc, | |
344 | .setbrg = pl01x_serial_setbrg, | |
345 | }; | |
346 | ||
fdef5e15 | 347 | #if CONFIG_IS_ENABLED(OF_REAL) |
69751729 VM |
348 | static const struct udevice_id pl01x_serial_id[] ={ |
349 | {.compatible = "arm,pl011", .data = TYPE_PL011}, | |
350 | {.compatible = "arm,pl010", .data = TYPE_PL010}, | |
351 | {} | |
352 | }; | |
353 | ||
f410d0ac TR |
354 | #ifndef CFG_PL011_CLOCK |
355 | #define CFG_PL011_CLOCK 0 | |
e3e2d662 AP |
356 | #endif |
357 | ||
d1998a9f | 358 | int pl01x_serial_of_to_plat(struct udevice *dev) |
69751729 | 359 | { |
8a8d24bd | 360 | struct pl01x_serial_plat *plat = dev_get_plat(dev); |
e3e2d662 | 361 | struct clk clk; |
69751729 | 362 | fdt_addr_t addr; |
e3e2d662 | 363 | int ret; |
69751729 | 364 | |
2548493a | 365 | addr = dev_read_addr(dev); |
69751729 VM |
366 | if (addr == FDT_ADDR_T_NONE) |
367 | return -EINVAL; | |
368 | ||
369 | plat->base = addr; | |
f410d0ac | 370 | plat->clock = dev_read_u32_default(dev, "clock", CFG_PL011_CLOCK); |
e3e2d662 AP |
371 | ret = clk_get_by_index(dev, 0, &clk); |
372 | if (!ret) { | |
6c9662df MS |
373 | ret = clk_enable(&clk); |
374 | if (ret && ret != -ENOSYS) { | |
375 | dev_err(dev, "failed to enable clock\n"); | |
376 | return ret; | |
377 | } | |
378 | ||
e3e2d662 | 379 | plat->clock = clk_get_rate(&clk); |
6c9662df MS |
380 | if (IS_ERR_VALUE(plat->clock)) { |
381 | dev_err(dev, "failed to get rate\n"); | |
382 | return plat->clock; | |
383 | } | |
384 | debug("%s: CLK %d\n", __func__, plat->clock); | |
e3e2d662 | 385 | } |
69751729 | 386 | plat->type = dev_get_driver_data(dev); |
b3111630 AG |
387 | plat->skip_init = dev_read_bool(dev, "skip-init"); |
388 | ||
69751729 VM |
389 | return 0; |
390 | } | |
391 | #endif | |
392 | ||
8a9cd5ad SG |
393 | U_BOOT_DRIVER(serial_pl01x) = { |
394 | .name = "serial_pl01x", | |
395 | .id = UCLASS_SERIAL, | |
875752ad | 396 | #if CONFIG_IS_ENABLED(OF_REAL) |
69751729 | 397 | .of_match = of_match_ptr(pl01x_serial_id), |
d1998a9f | 398 | .of_to_plat = of_match_ptr(pl01x_serial_of_to_plat), |
875752ad | 399 | #endif |
8a8d24bd | 400 | .plat_auto = sizeof(struct pl01x_serial_plat), |
8a9cd5ad SG |
401 | .probe = pl01x_serial_probe, |
402 | .ops = &pl01x_serial_ops, | |
403 | .flags = DM_FLAG_PRE_RELOC, | |
41575d8e | 404 | .priv_auto = sizeof(struct pl01x_priv), |
8a9cd5ad SG |
405 | }; |
406 | ||
875752ad LM |
407 | DM_DRIVER_ALIAS(serial_pl01x, arm_pl011) |
408 | DM_DRIVER_ALIAS(serial_pl01x, arm_pl010) | |
8a9cd5ad | 409 | #endif |
b81406db ST |
410 | |
411 | #if defined(CONFIG_DEBUG_UART_PL010) || defined(CONFIG_DEBUG_UART_PL011) | |
412 | ||
413 | #include <debug_uart.h> | |
414 | ||
415 | static void _debug_uart_init(void) | |
416 | { | |
417 | #ifndef CONFIG_DEBUG_UART_SKIP_INIT | |
b62450cf | 418 | struct pl01x_regs *regs = (struct pl01x_regs *)CONFIG_VAL(DEBUG_UART_BASE); |
91a04389 CB |
419 | enum pl01x_type type; |
420 | ||
421 | if (IS_ENABLED(CONFIG_DEBUG_UART_PL011)) | |
422 | type = TYPE_PL011; | |
423 | else | |
424 | type = TYPE_PL010; | |
b81406db ST |
425 | |
426 | pl01x_generic_serial_init(regs, type); | |
427 | pl01x_generic_setbrg(regs, type, | |
428 | CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); | |
429 | #endif | |
430 | } | |
431 | ||
432 | static inline void _debug_uart_putc(int ch) | |
433 | { | |
b62450cf | 434 | struct pl01x_regs *regs = (struct pl01x_regs *)CONFIG_VAL(DEBUG_UART_BASE); |
b81406db | 435 | |
19820150 CB |
436 | while (pl01x_putc(regs, ch) == -EAGAIN) |
437 | ; | |
b81406db ST |
438 | } |
439 | ||
440 | DEBUG_UART_FUNCS | |
441 | ||
442 | #endif |