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