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