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