blob: 3f9c4d14eab06ce16e787149c0b0cd83a66c0bab [file] [log] [blame]
Alison Wang427eba72013-05-27 22:55:45 +00001/*
2 * Copyright 2013 Freescale Semiconductor, Inc.
3 *
Wolfgang Denk1a459662013-07-08 09:37:19 +02004 * SPDX-License-Identifier: GPL-2.0+
Alison Wang427eba72013-05-27 22:55:45 +00005 */
6
7#include <common.h>
Bin Mengfdbae092016-01-13 19:39:04 -08008#include <dm.h>
Alison Wang427eba72013-05-27 22:55:45 +00009#include <watchdog.h>
10#include <asm/io.h>
11#include <serial.h>
12#include <linux/compiler.h>
13#include <asm/arch/imx-regs.h>
14#include <asm/arch/clock.h>
15
Bin Meng47f1bfc2016-01-13 19:39:01 -080016#define US1_TDRE (1 << 7)
17#define US1_RDRF (1 << 5)
18#define US1_OR (1 << 3)
19#define UC2_TE (1 << 3)
20#define UC2_RE (1 << 2)
21#define CFIFO_TXFLUSH (1 << 7)
22#define CFIFO_RXFLUSH (1 << 6)
23#define SFIFO_RXOF (1 << 2)
24#define SFIFO_RXUF (1 << 0)
Alison Wang427eba72013-05-27 22:55:45 +000025
Jingchang Lu6209e142014-09-05 13:52:47 +080026#define STAT_LBKDIF (1 << 31)
27#define STAT_RXEDGIF (1 << 30)
28#define STAT_TDRE (1 << 23)
29#define STAT_RDRF (1 << 21)
30#define STAT_IDLE (1 << 20)
31#define STAT_OR (1 << 19)
32#define STAT_NF (1 << 18)
33#define STAT_FE (1 << 17)
34#define STAT_PF (1 << 16)
35#define STAT_MA1F (1 << 15)
36#define STAT_MA2F (1 << 14)
37#define STAT_FLAGS (STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
Bin Meng47f1bfc2016-01-13 19:39:01 -080038 STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
Jingchang Lu6209e142014-09-05 13:52:47 +080039
40#define CTRL_TE (1 << 19)
41#define CTRL_RE (1 << 18)
42
43#define FIFO_TXFE 0x80
44#define FIFO_RXFE 0x40
45
46#define WATER_TXWATER_OFF 1
47#define WATER_RXWATER_OFF 16
48
Alison Wang427eba72013-05-27 22:55:45 +000049DECLARE_GLOBAL_DATA_PTR;
50
51struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE;
52
Bin Mengfdbae092016-01-13 19:39:04 -080053struct lpuart_serial_platdata {
54 struct lpuart_fsl *reg;
55};
56
Jingchang Lu6209e142014-09-05 13:52:47 +080057#ifndef CONFIG_LPUART_32B_REG
Bin Meng6ca13b12016-01-13 19:39:03 -080058static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate)
Alison Wang427eba72013-05-27 22:55:45 +000059{
60 u32 clk = mxc_get_clock(MXC_UART_CLK);
61 u16 sbr;
62
Bin Meng6ca13b12016-01-13 19:39:03 -080063 sbr = (u16)(clk / (16 * baudrate));
Alison Wang427eba72013-05-27 22:55:45 +000064
Bin Meng47f1bfc2016-01-13 19:39:01 -080065 /* place adjustment later - n/32 BRFA */
Alison Wang427eba72013-05-27 22:55:45 +000066 __raw_writeb(sbr >> 8, &base->ubdh);
67 __raw_writeb(sbr & 0xff, &base->ubdl);
68}
69
Bin Meng6ca13b12016-01-13 19:39:03 -080070static int _lpuart_serial_getc(struct lpuart_fsl *base)
Alison Wang427eba72013-05-27 22:55:45 +000071{
Stefan Agnera3db78d2014-08-19 17:54:27 +020072 while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
Alison Wang427eba72013-05-27 22:55:45 +000073 WATCHDOG_RESET();
74
Stefan Agnera3db78d2014-08-19 17:54:27 +020075 barrier();
Alison Wang427eba72013-05-27 22:55:45 +000076
77 return __raw_readb(&base->ud);
78}
79
Bin Meng6ca13b12016-01-13 19:39:03 -080080static void _lpuart_serial_putc(struct lpuart_fsl *base, const char c)
Alison Wang427eba72013-05-27 22:55:45 +000081{
82 if (c == '\n')
Bin Meng6ca13b12016-01-13 19:39:03 -080083 _lpuart_serial_putc(base, '\r');
Alison Wang427eba72013-05-27 22:55:45 +000084
85 while (!(__raw_readb(&base->us1) & US1_TDRE))
86 WATCHDOG_RESET();
87
88 __raw_writeb(c, &base->ud);
89}
90
Bin Meng47f1bfc2016-01-13 19:39:01 -080091/* Test whether a character is in the RX buffer */
Bin Meng6ca13b12016-01-13 19:39:03 -080092static int _lpuart_serial_tstc(struct lpuart_fsl *base)
Alison Wang427eba72013-05-27 22:55:45 +000093{
94 if (__raw_readb(&base->urcfifo) == 0)
95 return 0;
96
97 return 1;
98}
99
100/*
101 * Initialise the serial port with the given baudrate. The settings
102 * are always 8 data bits, no parity, 1 stop bit, no start bits.
103 */
Bin Meng6ca13b12016-01-13 19:39:03 -0800104static int _lpuart_serial_init(struct lpuart_fsl *base)
Alison Wang427eba72013-05-27 22:55:45 +0000105{
106 u8 ctrl;
107
108 ctrl = __raw_readb(&base->uc2);
109 ctrl &= ~UC2_RE;
110 ctrl &= ~UC2_TE;
111 __raw_writeb(ctrl, &base->uc2);
112
113 __raw_writeb(0, &base->umodem);
114 __raw_writeb(0, &base->uc1);
115
Stefan Agner89e69fd2014-08-19 17:54:28 +0200116 /* Disable FIFO and flush buffer */
117 __raw_writeb(0x0, &base->upfifo);
118 __raw_writeb(0x0, &base->utwfifo);
119 __raw_writeb(0x1, &base->urwfifo);
120 __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
121
Alison Wang427eba72013-05-27 22:55:45 +0000122 /* provide data bits, parity, stop bit, etc */
Bin Meng6ca13b12016-01-13 19:39:03 -0800123 _lpuart_serial_setbrg(base, gd->baudrate);
Alison Wang427eba72013-05-27 22:55:45 +0000124
125 __raw_writeb(UC2_RE | UC2_TE, &base->uc2);
126
127 return 0;
128}
129
Bin Mengfdbae092016-01-13 19:39:04 -0800130#ifndef CONFIG_DM_SERIAL
Bin Meng6ca13b12016-01-13 19:39:03 -0800131static void lpuart_serial_setbrg(void)
132{
133 _lpuart_serial_setbrg(base, gd->baudrate);
134}
135
136static int lpuart_serial_getc(void)
137{
138 return _lpuart_serial_getc(base);
139}
140
141static void lpuart_serial_putc(const char c)
142{
143 _lpuart_serial_putc(base, c);
144}
145
146static int lpuart_serial_tstc(void)
147{
148 return _lpuart_serial_tstc(base);
149}
150
151static int lpuart_serial_init(void)
152{
153 return _lpuart_serial_init(base);
154}
155
Alison Wang427eba72013-05-27 22:55:45 +0000156static struct serial_device lpuart_serial_drv = {
157 .name = "lpuart_serial",
158 .start = lpuart_serial_init,
159 .stop = NULL,
160 .setbrg = lpuart_serial_setbrg,
161 .putc = lpuart_serial_putc,
162 .puts = default_serial_puts,
163 .getc = lpuart_serial_getc,
164 .tstc = lpuart_serial_tstc,
165};
Bin Mengfdbae092016-01-13 19:39:04 -0800166#else /* CONFIG_DM_SERIAL */
167static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
168{
169 struct lpuart_serial_platdata *plat = dev->platdata;
170 struct lpuart_fsl *reg = plat->reg;
171
172 _lpuart_serial_setbrg(reg, baudrate);
173
174 return 0;
175}
176
177static int lpuart_serial_getc(struct udevice *dev)
178{
179 struct lpuart_serial_platdata *plat = dev->platdata;
180 struct lpuart_fsl *reg = plat->reg;
181
182 return _lpuart_serial_getc(reg);
183}
184
185static int lpuart_serial_putc(struct udevice *dev, const char c)
186{
187 struct lpuart_serial_platdata *plat = dev->platdata;
188 struct lpuart_fsl *reg = plat->reg;
189
190 _lpuart_serial_putc(reg, c);
191
192 return 0;
193}
194
195static int lpuart_serial_pending(struct udevice *dev, bool input)
196{
197 struct lpuart_serial_platdata *plat = dev->platdata;
198 struct lpuart_fsl *reg = plat->reg;
199
200 if (input)
201 return _lpuart_serial_tstc(reg);
202 else
203 return __raw_readb(&reg->us1) & US1_TDRE ? 0 : 1;
204}
205
206static int lpuart_serial_probe(struct udevice *dev)
207{
208 struct lpuart_serial_platdata *plat = dev->platdata;
209 struct lpuart_fsl *reg = plat->reg;
210
211 return _lpuart_serial_init(reg);
212}
213#endif /* CONFIG_DM_SERIAL */
Jingchang Lu6209e142014-09-05 13:52:47 +0800214#else
Bin Meng6ca13b12016-01-13 19:39:03 -0800215static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate)
Jingchang Lu6209e142014-09-05 13:52:47 +0800216{
217 u32 clk = CONFIG_SYS_CLK_FREQ;
218 u32 sbr;
219
Bin Meng6ca13b12016-01-13 19:39:03 -0800220 sbr = (clk / (16 * baudrate));
Jingchang Lu6209e142014-09-05 13:52:47 +0800221
Bin Meng47f1bfc2016-01-13 19:39:01 -0800222 /* place adjustment later - n/32 BRFA */
Jingchang Lu6209e142014-09-05 13:52:47 +0800223 out_be32(&base->baud, sbr);
224}
225
Bin Meng6ca13b12016-01-13 19:39:03 -0800226static int _lpuart32_serial_getc(struct lpuart_fsl *base)
Jingchang Lu6209e142014-09-05 13:52:47 +0800227{
228 u32 stat;
229
230 while (((stat = in_be32(&base->stat)) & STAT_RDRF) == 0) {
231 out_be32(&base->stat, STAT_FLAGS);
232 WATCHDOG_RESET();
233 }
234
235 return in_be32(&base->data) & 0x3ff;
236}
237
Bin Meng6ca13b12016-01-13 19:39:03 -0800238static void _lpuart32_serial_putc(struct lpuart_fsl *base, const char c)
Jingchang Lu6209e142014-09-05 13:52:47 +0800239{
240 if (c == '\n')
Bin Meng6ca13b12016-01-13 19:39:03 -0800241 _lpuart32_serial_putc(base, '\r');
Jingchang Lu6209e142014-09-05 13:52:47 +0800242
243 while (!(in_be32(&base->stat) & STAT_TDRE))
244 WATCHDOG_RESET();
245
246 out_be32(&base->data, c);
247}
248
Bin Meng47f1bfc2016-01-13 19:39:01 -0800249/* Test whether a character is in the RX buffer */
Bin Meng6ca13b12016-01-13 19:39:03 -0800250static int _lpuart32_serial_tstc(struct lpuart_fsl *base)
Jingchang Lu6209e142014-09-05 13:52:47 +0800251{
252 if ((in_be32(&base->water) >> 24) == 0)
253 return 0;
254
255 return 1;
256}
257
258/*
259 * Initialise the serial port with the given baudrate. The settings
260 * are always 8 data bits, no parity, 1 stop bit, no start bits.
261 */
Bin Meng6ca13b12016-01-13 19:39:03 -0800262static int _lpuart32_serial_init(struct lpuart_fsl *base)
Jingchang Lu6209e142014-09-05 13:52:47 +0800263{
264 u8 ctrl;
265
266 ctrl = in_be32(&base->ctrl);
267 ctrl &= ~CTRL_RE;
268 ctrl &= ~CTRL_TE;
269 out_be32(&base->ctrl, ctrl);
270
271 out_be32(&base->modir, 0);
272 out_be32(&base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
273
274 out_be32(&base->match, 0);
Jingchang Lu6209e142014-09-05 13:52:47 +0800275
Bin Meng47f1bfc2016-01-13 19:39:01 -0800276 /* provide data bits, parity, stop bit, etc */
Bin Meng6ca13b12016-01-13 19:39:03 -0800277 _lpuart32_serial_setbrg(base, gd->baudrate);
Jingchang Lu6209e142014-09-05 13:52:47 +0800278
279 out_be32(&base->ctrl, CTRL_RE | CTRL_TE);
280
281 return 0;
282}
283
Bin Mengfdbae092016-01-13 19:39:04 -0800284#ifndef CONFIG_DM_SERIAL
Bin Meng6ca13b12016-01-13 19:39:03 -0800285static void lpuart32_serial_setbrg(void)
286{
287 _lpuart32_serial_setbrg(base, gd->baudrate);
288}
289
290static int lpuart32_serial_getc(void)
291{
292 return _lpuart32_serial_getc(base);
293}
294
295static void lpuart32_serial_putc(const char c)
296{
297 _lpuart32_serial_putc(base, c);
298}
299
300static int lpuart32_serial_tstc(void)
301{
302 return _lpuart32_serial_tstc(base);
303}
304
305static int lpuart32_serial_init(void)
306{
307 return _lpuart32_serial_init(base);
308}
309
Jingchang Lu6209e142014-09-05 13:52:47 +0800310static struct serial_device lpuart32_serial_drv = {
311 .name = "lpuart32_serial",
312 .start = lpuart32_serial_init,
313 .stop = NULL,
314 .setbrg = lpuart32_serial_setbrg,
315 .putc = lpuart32_serial_putc,
316 .puts = default_serial_puts,
317 .getc = lpuart32_serial_getc,
318 .tstc = lpuart32_serial_tstc,
319};
Bin Mengfdbae092016-01-13 19:39:04 -0800320#else /* CONFIG_DM_SERIAL */
321static int lpuart32_serial_setbrg(struct udevice *dev, int baudrate)
322{
323 struct lpuart_serial_platdata *plat = dev->platdata;
324 struct lpuart_fsl *reg = plat->reg;
325
326 _lpuart32_serial_setbrg(reg, baudrate);
327
328 return 0;
329}
330
331static int lpuart32_serial_getc(struct udevice *dev)
332{
333 struct lpuart_serial_platdata *plat = dev->platdata;
334 struct lpuart_fsl *reg = plat->reg;
335
336 return _lpuart32_serial_getc(reg);
337}
338
339static int lpuart32_serial_putc(struct udevice *dev, const char c)
340{
341 struct lpuart_serial_platdata *plat = dev->platdata;
342 struct lpuart_fsl *reg = plat->reg;
343
344 _lpuart32_serial_putc(reg, c);
345
346 return 0;
347}
348
349static int lpuart32_serial_pending(struct udevice *dev, bool input)
350{
351 struct lpuart_serial_platdata *plat = dev->platdata;
352 struct lpuart_fsl *reg = plat->reg;
353
354 if (input)
355 return _lpuart32_serial_tstc(reg);
356 else
357 return in_be32(&reg->stat) & STAT_TDRE ? 0 : 1;
358}
359
360static int lpuart32_serial_probe(struct udevice *dev)
361{
362 struct lpuart_serial_platdata *plat = dev->platdata;
363 struct lpuart_fsl *reg = plat->reg;
364
365 return _lpuart32_serial_init(reg);
366}
367#endif /* CONFIG_DM_SERIAL */
Jingchang Lu6209e142014-09-05 13:52:47 +0800368#endif
Alison Wang427eba72013-05-27 22:55:45 +0000369
Bin Mengfdbae092016-01-13 19:39:04 -0800370#ifndef CONFIG_DM_SERIAL
Alison Wang427eba72013-05-27 22:55:45 +0000371void lpuart_serial_initialize(void)
372{
Jingchang Lu6209e142014-09-05 13:52:47 +0800373#ifdef CONFIG_LPUART_32B_REG
374 serial_register(&lpuart32_serial_drv);
375#else
Alison Wang427eba72013-05-27 22:55:45 +0000376 serial_register(&lpuart_serial_drv);
Jingchang Lu6209e142014-09-05 13:52:47 +0800377#endif
Alison Wang427eba72013-05-27 22:55:45 +0000378}
379
380__weak struct serial_device *default_serial_console(void)
381{
Jingchang Lu6209e142014-09-05 13:52:47 +0800382#ifdef CONFIG_LPUART_32B_REG
383 return &lpuart32_serial_drv;
384#else
Alison Wang427eba72013-05-27 22:55:45 +0000385 return &lpuart_serial_drv;
Jingchang Lu6209e142014-09-05 13:52:47 +0800386#endif
Alison Wang427eba72013-05-27 22:55:45 +0000387}
Bin Mengfdbae092016-01-13 19:39:04 -0800388#else /* CONFIG_DM_SERIAL */
389static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
390{
391 struct lpuart_serial_platdata *plat = dev->platdata;
392 fdt_addr_t addr;
393
394 addr = dev_get_addr(dev);
395 if (addr == FDT_ADDR_T_NONE)
396 return -EINVAL;
397
398 plat->reg = (struct lpuart_fsl *)addr;
399
400 return 0;
401}
402
403#ifndef CONFIG_LPUART_32B_REG
404static const struct dm_serial_ops lpuart_serial_ops = {
405 .putc = lpuart_serial_putc,
406 .pending = lpuart_serial_pending,
407 .getc = lpuart_serial_getc,
408 .setbrg = lpuart_serial_setbrg,
409};
410
411static const struct udevice_id lpuart_serial_ids[] = {
412 { .compatible = "fsl,vf610-lpuart" },
413 { }
414};
415
416U_BOOT_DRIVER(serial_lpuart) = {
417 .name = "serial_lpuart",
418 .id = UCLASS_SERIAL,
419 .of_match = lpuart_serial_ids,
420 .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
421 .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
422 .probe = lpuart_serial_probe,
423 .ops = &lpuart_serial_ops,
424 .flags = DM_FLAG_PRE_RELOC,
425};
426#else /* CONFIG_LPUART_32B_REG */
427static const struct dm_serial_ops lpuart32_serial_ops = {
428 .putc = lpuart32_serial_putc,
429 .pending = lpuart32_serial_pending,
430 .getc = lpuart32_serial_getc,
431 .setbrg = lpuart32_serial_setbrg,
432};
433
434static const struct udevice_id lpuart32_serial_ids[] = {
435 { .compatible = "fsl,ls1021a-lpuart" },
436 { }
437};
438
439U_BOOT_DRIVER(serial_lpuart32) = {
440 .name = "serial_lpuart32",
441 .id = UCLASS_SERIAL,
442 .of_match = lpuart32_serial_ids,
443 .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
444 .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
445 .probe = lpuart32_serial_probe,
446 .ops = &lpuart32_serial_ops,
447 .flags = DM_FLAG_PRE_RELOC,
448};
449#endif /* CONFIG_LPUART_32B_REG */
450#endif /* CONFIG_DM_SERIAL */