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