]>
Commit | Line | Data |
---|---|---|
427eba70 AW |
1 | /* |
2 | * Copyright 2013 Freescale Semiconductor, Inc. | |
3 | * | |
1a459660 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
427eba70 AW |
5 | */ |
6 | ||
7 | #include <common.h> | |
fdbae099 | 8 | #include <dm.h> |
c40d612b | 9 | #include <fsl_lpuart.h> |
427eba70 AW |
10 | #include <watchdog.h> |
11 | #include <asm/io.h> | |
12 | #include <serial.h> | |
13 | #include <linux/compiler.h> | |
14 | #include <asm/arch/imx-regs.h> | |
15 | #include <asm/arch/clock.h> | |
16 | ||
47f1bfca BM |
17 | #define US1_TDRE (1 << 7) |
18 | #define US1_RDRF (1 << 5) | |
19 | #define US1_OR (1 << 3) | |
20 | #define UC2_TE (1 << 3) | |
21 | #define UC2_RE (1 << 2) | |
22 | #define CFIFO_TXFLUSH (1 << 7) | |
23 | #define CFIFO_RXFLUSH (1 << 6) | |
24 | #define SFIFO_RXOF (1 << 2) | |
25 | #define SFIFO_RXUF (1 << 0) | |
427eba70 | 26 | |
6209e14c JL |
27 | #define STAT_LBKDIF (1 << 31) |
28 | #define STAT_RXEDGIF (1 << 30) | |
29 | #define STAT_TDRE (1 << 23) | |
30 | #define STAT_RDRF (1 << 21) | |
31 | #define STAT_IDLE (1 << 20) | |
32 | #define STAT_OR (1 << 19) | |
33 | #define STAT_NF (1 << 18) | |
34 | #define STAT_FE (1 << 17) | |
35 | #define STAT_PF (1 << 16) | |
36 | #define STAT_MA1F (1 << 15) | |
37 | #define STAT_MA2F (1 << 14) | |
38 | #define STAT_FLAGS (STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \ | |
47f1bfca | 39 | STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F) |
6209e14c JL |
40 | |
41 | #define CTRL_TE (1 << 19) | |
42 | #define CTRL_RE (1 << 18) | |
43 | ||
44 | #define FIFO_TXFE 0x80 | |
45 | #define FIFO_RXFE 0x40 | |
46 | ||
47 | #define WATER_TXWATER_OFF 1 | |
48 | #define WATER_RXWATER_OFF 16 | |
49 | ||
427eba70 AW |
50 | DECLARE_GLOBAL_DATA_PTR; |
51 | ||
c40d612b PF |
52 | #define LPUART_FLAG_REGMAP_32BIT_REG BIT(0) |
53 | #define LPUART_FLAG_REGMAP_ENDIAN_BIG BIT(1) | |
54 | ||
7edf5c45 PF |
55 | enum lpuart_devtype { |
56 | DEV_VF610 = 1, | |
57 | DEV_LS1021A, | |
58 | DEV_MX7ULP | |
59 | }; | |
60 | ||
fdbae099 | 61 | struct lpuart_serial_platdata { |
c40d612b | 62 | void *reg; |
7edf5c45 | 63 | enum lpuart_devtype devtype; |
c40d612b | 64 | ulong flags; |
fdbae099 BM |
65 | }; |
66 | ||
c40d612b PF |
67 | static void lpuart_read32(u32 flags, u32 *addr, u32 *val) |
68 | { | |
69 | if (flags & LPUART_FLAG_REGMAP_32BIT_REG) { | |
70 | if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG) | |
71 | *(u32 *)val = in_be32(addr); | |
72 | else | |
73 | *(u32 *)val = in_le32(addr); | |
74 | } | |
75 | } | |
76 | ||
77 | static void lpuart_write32(u32 flags, u32 *addr, u32 val) | |
78 | { | |
79 | if (flags & LPUART_FLAG_REGMAP_32BIT_REG) { | |
80 | if (flags & LPUART_FLAG_REGMAP_ENDIAN_BIG) | |
81 | out_be32(addr, val); | |
82 | else | |
83 | out_le32(addr, val); | |
84 | } | |
85 | } | |
86 | ||
87 | ||
88 | #ifndef CONFIG_SYS_CLK_FREQ | |
89 | #define CONFIG_SYS_CLK_FREQ 0 | |
90 | #endif | |
91 | ||
92 | u32 __weak get_lpuart_clk(void) | |
427eba70 | 93 | { |
c40d612b PF |
94 | return CONFIG_SYS_CLK_FREQ; |
95 | } | |
96 | ||
97 | static bool is_lpuart32(struct udevice *dev) | |
98 | { | |
99 | struct lpuart_serial_platdata *plat = dev->platdata; | |
100 | ||
101 | return plat->flags & LPUART_FLAG_REGMAP_32BIT_REG; | |
102 | } | |
103 | ||
104 | static void _lpuart_serial_setbrg(struct lpuart_serial_platdata *plat, | |
105 | int baudrate) | |
106 | { | |
107 | struct lpuart_fsl *base = plat->reg; | |
108 | u32 clk = get_lpuart_clk(); | |
427eba70 AW |
109 | u16 sbr; |
110 | ||
6ca13b12 | 111 | sbr = (u16)(clk / (16 * baudrate)); |
427eba70 | 112 | |
47f1bfca | 113 | /* place adjustment later - n/32 BRFA */ |
427eba70 AW |
114 | __raw_writeb(sbr >> 8, &base->ubdh); |
115 | __raw_writeb(sbr & 0xff, &base->ubdl); | |
116 | } | |
117 | ||
c40d612b | 118 | static int _lpuart_serial_getc(struct lpuart_serial_platdata *plat) |
427eba70 | 119 | { |
c40d612b | 120 | struct lpuart_fsl *base = plat->reg; |
a3db78d8 | 121 | while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR))) |
427eba70 AW |
122 | WATCHDOG_RESET(); |
123 | ||
a3db78d8 | 124 | barrier(); |
427eba70 AW |
125 | |
126 | return __raw_readb(&base->ud); | |
127 | } | |
128 | ||
c40d612b PF |
129 | static void _lpuart_serial_putc(struct lpuart_serial_platdata *plat, |
130 | const char c) | |
427eba70 | 131 | { |
c40d612b PF |
132 | struct lpuart_fsl *base = plat->reg; |
133 | ||
427eba70 AW |
134 | while (!(__raw_readb(&base->us1) & US1_TDRE)) |
135 | WATCHDOG_RESET(); | |
136 | ||
137 | __raw_writeb(c, &base->ud); | |
138 | } | |
139 | ||
47f1bfca | 140 | /* Test whether a character is in the RX buffer */ |
c40d612b | 141 | static int _lpuart_serial_tstc(struct lpuart_serial_platdata *plat) |
427eba70 | 142 | { |
c40d612b PF |
143 | struct lpuart_fsl *base = plat->reg; |
144 | ||
427eba70 AW |
145 | if (__raw_readb(&base->urcfifo) == 0) |
146 | return 0; | |
147 | ||
148 | return 1; | |
149 | } | |
150 | ||
151 | /* | |
152 | * Initialise the serial port with the given baudrate. The settings | |
153 | * are always 8 data bits, no parity, 1 stop bit, no start bits. | |
154 | */ | |
c40d612b | 155 | static int _lpuart_serial_init(struct lpuart_serial_platdata *plat) |
427eba70 | 156 | { |
c40d612b | 157 | struct lpuart_fsl *base = (struct lpuart_fsl *)plat->reg; |
427eba70 AW |
158 | u8 ctrl; |
159 | ||
160 | ctrl = __raw_readb(&base->uc2); | |
161 | ctrl &= ~UC2_RE; | |
162 | ctrl &= ~UC2_TE; | |
163 | __raw_writeb(ctrl, &base->uc2); | |
164 | ||
165 | __raw_writeb(0, &base->umodem); | |
166 | __raw_writeb(0, &base->uc1); | |
167 | ||
89e69fd4 SA |
168 | /* Disable FIFO and flush buffer */ |
169 | __raw_writeb(0x0, &base->upfifo); | |
170 | __raw_writeb(0x0, &base->utwfifo); | |
171 | __raw_writeb(0x1, &base->urwfifo); | |
172 | __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo); | |
173 | ||
427eba70 | 174 | /* provide data bits, parity, stop bit, etc */ |
c40d612b | 175 | _lpuart_serial_setbrg(plat, gd->baudrate); |
427eba70 AW |
176 | |
177 | __raw_writeb(UC2_RE | UC2_TE, &base->uc2); | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
7edf5c45 PF |
182 | static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat, |
183 | int baudrate) | |
184 | { | |
185 | struct lpuart_fsl_reg32 *base = plat->reg; | |
186 | u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp; | |
187 | u32 clk = get_lpuart_clk(); | |
188 | ||
189 | baud_diff = baudrate; | |
190 | osr = 0; | |
191 | sbr = 0; | |
192 | ||
193 | for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) { | |
194 | tmp_sbr = (clk / (baudrate * tmp_osr)); | |
195 | ||
196 | if (tmp_sbr == 0) | |
197 | tmp_sbr = 1; | |
198 | ||
199 | /*calculate difference in actual buad w/ current values */ | |
200 | tmp_diff = (clk / (tmp_osr * tmp_sbr)); | |
201 | tmp_diff = tmp_diff - baudrate; | |
202 | ||
203 | /* select best values between sbr and sbr+1 */ | |
204 | if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) { | |
205 | tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1))); | |
206 | tmp_sbr++; | |
207 | } | |
208 | ||
209 | if (tmp_diff <= baud_diff) { | |
210 | baud_diff = tmp_diff; | |
211 | osr = tmp_osr; | |
212 | sbr = tmp_sbr; | |
213 | } | |
214 | } | |
215 | ||
216 | /* | |
217 | * TODO: handle buadrate outside acceptable rate | |
218 | * if (baudDiff > ((config->baudRate_Bps / 100) * 3)) | |
219 | * { | |
220 | * Unacceptable baud rate difference of more than 3% | |
221 | * return kStatus_LPUART_BaudrateNotSupport; | |
222 | * } | |
223 | */ | |
224 | tmp = in_le32(&base->baud); | |
225 | ||
226 | if ((osr > 3) && (osr < 8)) | |
227 | tmp |= LPUART_BAUD_BOTHEDGE_MASK; | |
228 | ||
229 | tmp &= ~LPUART_BAUD_OSR_MASK; | |
230 | tmp |= LPUART_BAUD_OSR(osr-1); | |
231 | ||
232 | tmp &= ~LPUART_BAUD_SBR_MASK; | |
233 | tmp |= LPUART_BAUD_SBR(sbr); | |
234 | ||
235 | /* explicitly disable 10 bit mode & set 1 stop bit */ | |
236 | tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK); | |
237 | ||
238 | out_le32(&base->baud, tmp); | |
239 | } | |
240 | ||
c40d612b PF |
241 | static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat, |
242 | int baudrate) | |
6209e14c | 243 | { |
c40d612b | 244 | struct lpuart_fsl_reg32 *base = plat->reg; |
9b46213b | 245 | u32 clk = get_lpuart_clk(); |
6209e14c JL |
246 | u32 sbr; |
247 | ||
6ca13b12 | 248 | sbr = (clk / (16 * baudrate)); |
6209e14c | 249 | |
47f1bfca | 250 | /* place adjustment later - n/32 BRFA */ |
c40d612b | 251 | lpuart_write32(plat->flags, &base->baud, sbr); |
6209e14c JL |
252 | } |
253 | ||
c40d612b | 254 | static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat) |
6209e14c | 255 | { |
c40d612b | 256 | struct lpuart_fsl_reg32 *base = plat->reg; |
7edf5c45 | 257 | u32 stat, val; |
6209e14c | 258 | |
c40d612b PF |
259 | lpuart_read32(plat->flags, &base->stat, &stat); |
260 | while ((stat & STAT_RDRF) == 0) { | |
261 | lpuart_write32(plat->flags, &base->stat, STAT_FLAGS); | |
6209e14c | 262 | WATCHDOG_RESET(); |
c40d612b | 263 | lpuart_read32(plat->flags, &base->stat, &stat); |
6209e14c JL |
264 | } |
265 | ||
7edf5c45 | 266 | lpuart_read32(plat->flags, &base->data, &val); |
c40d612b | 267 | |
7edf5c45 PF |
268 | if (plat->devtype & DEV_MX7ULP) { |
269 | lpuart_read32(plat->flags, &base->stat, &stat); | |
270 | if (stat & STAT_OR) | |
271 | lpuart_write32(plat->flags, &base->stat, STAT_OR); | |
272 | } | |
273 | ||
274 | return val & 0x3ff; | |
6209e14c JL |
275 | } |
276 | ||
c40d612b PF |
277 | static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat, |
278 | const char c) | |
6209e14c | 279 | { |
c40d612b PF |
280 | struct lpuart_fsl_reg32 *base = plat->reg; |
281 | u32 stat; | |
282 | ||
7edf5c45 PF |
283 | if (plat->devtype & DEV_MX7ULP) { |
284 | if (c == '\n') | |
285 | serial_putc('\r'); | |
286 | } | |
287 | ||
c40d612b PF |
288 | while (true) { |
289 | lpuart_read32(plat->flags, &base->stat, &stat); | |
290 | ||
291 | if ((stat & STAT_TDRE)) | |
292 | break; | |
293 | ||
6209e14c | 294 | WATCHDOG_RESET(); |
c40d612b | 295 | } |
6209e14c | 296 | |
c40d612b | 297 | lpuart_write32(plat->flags, &base->data, c); |
6209e14c JL |
298 | } |
299 | ||
47f1bfca | 300 | /* Test whether a character is in the RX buffer */ |
c40d612b | 301 | static int _lpuart32_serial_tstc(struct lpuart_serial_platdata *plat) |
6209e14c | 302 | { |
c40d612b PF |
303 | struct lpuart_fsl_reg32 *base = plat->reg; |
304 | u32 water; | |
305 | ||
306 | lpuart_read32(plat->flags, &base->water, &water); | |
307 | ||
308 | if ((water >> 24) == 0) | |
6209e14c JL |
309 | return 0; |
310 | ||
311 | return 1; | |
312 | } | |
313 | ||
314 | /* | |
315 | * Initialise the serial port with the given baudrate. The settings | |
316 | * are always 8 data bits, no parity, 1 stop bit, no start bits. | |
317 | */ | |
c40d612b | 318 | static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat) |
6209e14c | 319 | { |
c40d612b PF |
320 | struct lpuart_fsl_reg32 *base = (struct lpuart_fsl_reg32 *)plat->reg; |
321 | u32 ctrl; | |
6209e14c | 322 | |
c40d612b | 323 | lpuart_read32(plat->flags, &base->ctrl, &ctrl); |
6209e14c JL |
324 | ctrl &= ~CTRL_RE; |
325 | ctrl &= ~CTRL_TE; | |
c40d612b | 326 | lpuart_write32(plat->flags, &base->ctrl, ctrl); |
6209e14c | 327 | |
c40d612b PF |
328 | lpuart_write32(plat->flags, &base->modir, 0); |
329 | lpuart_write32(plat->flags, &base->fifo, ~(FIFO_TXFE | FIFO_RXFE)); | |
6209e14c | 330 | |
c40d612b | 331 | lpuart_write32(plat->flags, &base->match, 0); |
6209e14c | 332 | |
7edf5c45 PF |
333 | if (plat->devtype & DEV_MX7ULP) { |
334 | _lpuart32_serial_setbrg_7ulp(plat, gd->baudrate); | |
335 | } else { | |
336 | /* provide data bits, parity, stop bit, etc */ | |
337 | _lpuart32_serial_setbrg(plat, gd->baudrate); | |
338 | } | |
6209e14c | 339 | |
c40d612b | 340 | lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE); |
6209e14c JL |
341 | |
342 | return 0; | |
343 | } | |
344 | ||
c40d612b | 345 | static int lpuart_serial_setbrg(struct udevice *dev, int baudrate) |
fdbae099 BM |
346 | { |
347 | struct lpuart_serial_platdata *plat = dev->platdata; | |
fdbae099 | 348 | |
7edf5c45 PF |
349 | if (is_lpuart32(dev)) { |
350 | if (plat->devtype & DEV_MX7ULP) | |
351 | _lpuart32_serial_setbrg_7ulp(plat, baudrate); | |
352 | else | |
353 | _lpuart32_serial_setbrg(plat, baudrate); | |
354 | } else { | |
c40d612b | 355 | _lpuart_serial_setbrg(plat, baudrate); |
7edf5c45 | 356 | } |
fdbae099 BM |
357 | |
358 | return 0; | |
359 | } | |
360 | ||
c40d612b | 361 | static int lpuart_serial_getc(struct udevice *dev) |
fdbae099 BM |
362 | { |
363 | struct lpuart_serial_platdata *plat = dev->platdata; | |
fdbae099 | 364 | |
c40d612b PF |
365 | if (is_lpuart32(dev)) |
366 | return _lpuart32_serial_getc(plat); | |
367 | ||
368 | return _lpuart_serial_getc(plat); | |
fdbae099 BM |
369 | } |
370 | ||
c40d612b | 371 | static int lpuart_serial_putc(struct udevice *dev, const char c) |
fdbae099 BM |
372 | { |
373 | struct lpuart_serial_platdata *plat = dev->platdata; | |
fdbae099 | 374 | |
c40d612b PF |
375 | if (is_lpuart32(dev)) |
376 | _lpuart32_serial_putc(plat, c); | |
377 | else | |
378 | _lpuart_serial_putc(plat, c); | |
fdbae099 BM |
379 | |
380 | return 0; | |
381 | } | |
382 | ||
c40d612b | 383 | static int lpuart_serial_pending(struct udevice *dev, bool input) |
fdbae099 BM |
384 | { |
385 | struct lpuart_serial_platdata *plat = dev->platdata; | |
386 | struct lpuart_fsl *reg = plat->reg; | |
c40d612b PF |
387 | struct lpuart_fsl_reg32 *reg32 = plat->reg; |
388 | u32 stat; | |
389 | ||
390 | if (is_lpuart32(dev)) { | |
391 | if (input) { | |
392 | return _lpuart32_serial_tstc(plat); | |
393 | } else { | |
394 | lpuart_read32(plat->flags, ®32->stat, &stat); | |
395 | return stat & STAT_TDRE ? 0 : 1; | |
396 | } | |
397 | } | |
fdbae099 BM |
398 | |
399 | if (input) | |
c40d612b | 400 | return _lpuart_serial_tstc(plat); |
fdbae099 | 401 | else |
c40d612b | 402 | return __raw_readb(®->us1) & US1_TDRE ? 0 : 1; |
fdbae099 BM |
403 | } |
404 | ||
c40d612b | 405 | static int lpuart_serial_probe(struct udevice *dev) |
fdbae099 BM |
406 | { |
407 | struct lpuart_serial_platdata *plat = dev->platdata; | |
fdbae099 | 408 | |
c40d612b PF |
409 | if (is_lpuart32(dev)) |
410 | return _lpuart32_serial_init(plat); | |
411 | else | |
412 | return _lpuart_serial_init(plat); | |
fdbae099 | 413 | } |
427eba70 | 414 | |
fdbae099 BM |
415 | static int lpuart_serial_ofdata_to_platdata(struct udevice *dev) |
416 | { | |
417 | struct lpuart_serial_platdata *plat = dev->platdata; | |
7edf5c45 | 418 | const void *blob = gd->fdt_blob; |
da409ccc | 419 | int node = dev_of_offset(dev); |
fdbae099 BM |
420 | fdt_addr_t addr; |
421 | ||
a821c4af | 422 | addr = devfdt_get_addr(dev); |
fdbae099 BM |
423 | if (addr == FDT_ADDR_T_NONE) |
424 | return -EINVAL; | |
425 | ||
c40d612b PF |
426 | plat->reg = (void *)addr; |
427 | plat->flags = dev_get_driver_data(dev); | |
fdbae099 | 428 | |
7edf5c45 PF |
429 | if (!fdt_node_check_compatible(blob, node, "fsl,ls1021a-lpuart")) |
430 | plat->devtype = DEV_LS1021A; | |
431 | else if (!fdt_node_check_compatible(blob, node, "fsl,imx7ulp-lpuart")) | |
432 | plat->devtype = DEV_MX7ULP; | |
433 | else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart")) | |
434 | plat->devtype = DEV_VF610; | |
435 | ||
fdbae099 BM |
436 | return 0; |
437 | } | |
438 | ||
fdbae099 BM |
439 | static const struct dm_serial_ops lpuart_serial_ops = { |
440 | .putc = lpuart_serial_putc, | |
441 | .pending = lpuart_serial_pending, | |
442 | .getc = lpuart_serial_getc, | |
443 | .setbrg = lpuart_serial_setbrg, | |
444 | }; | |
445 | ||
446 | static const struct udevice_id lpuart_serial_ids[] = { | |
c40d612b PF |
447 | { .compatible = "fsl,ls1021a-lpuart", .data = |
448 | LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG }, | |
7edf5c45 PF |
449 | { .compatible = "fsl,imx7ulp-lpuart", |
450 | .data = LPUART_FLAG_REGMAP_32BIT_REG }, | |
c40d612b | 451 | { .compatible = "fsl,vf610-lpuart"}, |
fdbae099 BM |
452 | { } |
453 | }; | |
454 | ||
455 | U_BOOT_DRIVER(serial_lpuart) = { | |
456 | .name = "serial_lpuart", | |
457 | .id = UCLASS_SERIAL, | |
458 | .of_match = lpuart_serial_ids, | |
459 | .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata, | |
460 | .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata), | |
461 | .probe = lpuart_serial_probe, | |
462 | .ops = &lpuart_serial_ops, | |
463 | .flags = DM_FLAG_PRE_RELOC, | |
464 | }; |