exynos_uart.c revision 1.6 1 /* $NetBSD: exynos_uart.c,v 1.6 2021/09/13 23:31:23 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2013-2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry and Jared McNeill.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "locators.h"
33
34 #include <sys/cdefs.h>
35
36 __KERNEL_RCSID(1, "$NetBSD: exynos_uart.c,v 1.6 2021/09/13 23:31:23 jmcneill Exp $");
37
38 #define cn_trap() \
39 do { \
40 console_debugger(); \
41 cn_trapped = 1; \
42 } while (/* CONSTCOND */ 0)
43
44 #include <sys/param.h>
45 #include <sys/bus.h>
46 #include <sys/device.h>
47 #include <sys/conf.h>
48 #include <sys/intr.h>
49 #include <sys/systm.h>
50 #include <sys/time.h>
51 #include <sys/termios.h>
52 #include <sys/kauth.h>
53 #include <sys/lwp.h>
54 #include <sys/tty.h>
55
56 #include <dev/cons.h>
57
58 #include <dev/fdt/fdtvar.h>
59
60 #include <arm/samsung/sscom_reg.h>
61
62 static int exynos_uart_match(device_t, cfdata_t, void *);
63 static void exynos_uart_attach(device_t, device_t, void *);
64
65 static int exynos_uart_intr(void *);
66
67 static int exynos_uart_cngetc(dev_t);
68 static void exynos_uart_cnputc(dev_t, int);
69 static void exynos_uart_cnpollc(dev_t, int);
70
71 static void exynos_uart_start(struct tty *);
72 static int exynos_uart_param(struct tty *, struct termios *);
73
74 extern struct cfdriver exuart_cd;
75
76 enum exynos_uart_type {
77 EXYNOS_UART_SAMSUNG,
78 EXYNOS_UART_APPLE,
79 };
80
81 struct exynos_uart_config {
82 enum exynos_uart_type type;
83 uint32_t rxfull;
84 uint32_t txfull;
85 uint32_t rxcount;
86 };
87
88 struct exynos_uart_softc {
89 device_t sc_dev;
90 bus_space_tag_t sc_bst;
91 bus_space_handle_t sc_bsh;
92 kmutex_t sc_lock;
93 u_int sc_freq;
94 void *sc_ih;
95
96 bool sc_console;
97 struct tty *sc_tty;
98
99 int sc_ospeed;
100 tcflag_t sc_cflag;
101
102 const struct exynos_uart_config *sc_conf;
103
104 u_char sc_buf[1024];
105 };
106
107 #define RD4(sc, reg) \
108 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
109 #define WR4(sc, reg, val) \
110 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
111
112 static bus_addr_t exynos_uart_consaddr;
113
114 static struct exynos_uart_softc exynos_uart_cnsc;
115
116 static struct cnm_state exynos_uart_cnm_state;
117
118 struct consdev exynos_uart_consdev = {
119 .cn_getc = exynos_uart_cngetc,
120 .cn_putc = exynos_uart_cnputc,
121 .cn_pollc = exynos_uart_cnpollc,
122 .cn_dev = NODEV,
123 .cn_pri = CN_NORMAL,
124 };
125
126 static dev_type_open(exynos_uart_open);
127 static dev_type_open(exynos_uart_close);
128 static dev_type_read(exynos_uart_read);
129 static dev_type_write(exynos_uart_write);
130 static dev_type_ioctl(exynos_uart_ioctl);
131 static dev_type_tty(exynos_uart_tty);
132 static dev_type_poll(exynos_uart_poll);
133 static dev_type_stop(exynos_uart_stop);
134
135 const struct cdevsw exuart_cdevsw = {
136 .d_open = exynos_uart_open,
137 .d_close = exynos_uart_close,
138 .d_read = exynos_uart_read,
139 .d_write = exynos_uart_write,
140 .d_ioctl = exynos_uart_ioctl,
141 .d_stop = exynos_uart_stop,
142 .d_tty = exynos_uart_tty,
143 .d_poll = exynos_uart_poll,
144 .d_mmap = nommap,
145 .d_kqfilter = ttykqfilter,
146 .d_discard = nodiscard,
147 .d_flag = D_TTY
148 };
149
150 static int exynos_uart_cmajor = -1;
151
152 static const struct exynos_uart_config exynos_uart_samsung = {
153 .type = EXYNOS_UART_SAMSUNG,
154 .rxfull = UFSTAT_RXFULL,
155 .txfull = UFSTAT_TXFULL,
156 .rxcount = UFSTAT_RXCOUNT,
157 };
158
159 static const struct exynos_uart_config exynos_uart_apple = {
160 .type = EXYNOS_UART_APPLE,
161 .rxfull = UFSTAT_S5L_RXFULL,
162 .txfull = UFSTAT_S5L_TXFULL,
163 .rxcount = UFSTAT_S5L_RXCOUNT,
164 };
165
166 static const struct device_compatible_entry compat_data[] = {
167 { .compat = "samsung,exynos4210-uart", .data = &exynos_uart_samsung },
168 { .compat = "apple,s5l-uart", .data = &exynos_uart_apple },
169 DEVICE_COMPAT_EOL
170 };
171
172 CFATTACH_DECL_NEW(exynos_uart, sizeof(struct exynos_uart_softc),
173 exynos_uart_match, exynos_uart_attach, NULL, NULL);
174
175 static int
176 exynos_uart_match(device_t parent, cfdata_t cf, void *aux)
177 {
178 struct fdt_attach_args * const faa = aux;
179
180 return of_compatible_match(faa->faa_phandle, compat_data);
181 }
182
183 static void
184 exynos_uart_attach(device_t parent, device_t self, void *aux)
185 {
186 struct exynos_uart_softc * const sc = device_private(self);
187 struct fdt_attach_args * const faa = aux;
188 const int phandle = faa->faa_phandle;
189 char intrstr[128];
190 struct clk *clk_uart, *clk_uart_baud0;
191 struct tty *tp;
192 int major, minor;
193 bus_addr_t addr;
194 bus_size_t size;
195 uint32_t ucon;
196
197 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
198 aprint_error(": couldn't get registers\n");
199 return;
200 }
201 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
202 aprint_error(": failed to decode interrupt\n");
203 return;
204 }
205 clk_uart = fdtbus_clock_get(phandle, "uart");
206 if (clk_uart == NULL || clk_enable(clk_uart) != 0) {
207 aprint_error(": failed to enable uart clock\n");
208 return;
209 }
210 clk_uart_baud0 = fdtbus_clock_get(phandle, "clk_uart_baud0");
211 if (clk_uart_baud0 == NULL || clk_enable(clk_uart_baud0) != 0) {
212 aprint_error(": failed to enable clk_uart_baud0 clock\n");
213 return;
214 }
215
216 const bool is_console = exynos_uart_consaddr == addr;
217
218 sc->sc_dev = self;
219 sc->sc_bst = faa->faa_bst;
220 sc->sc_conf = of_compatible_lookup(phandle, compat_data)->data;
221 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
222 sc->sc_console = is_console;
223 if (is_console) {
224 sc->sc_bsh = exynos_uart_cnsc.sc_bsh;
225 } else {
226 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
227 aprint_error(": failed to map registers\n");
228 return;
229 }
230 }
231 sc->sc_freq = clk_get_rate(clk_uart_baud0);
232
233 sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SERIAL,
234 0, exynos_uart_intr, sc, device_xname(self));
235 if (sc->sc_ih == NULL) {
236 aprint_error(": failed to establish interrupt on %s\n",
237 intrstr);
238 return;
239 }
240
241 if (exynos_uart_cmajor == -1) {
242 /* allocate a major number */
243 int bmajor = -1, cmajor = -1;
244 int error = devsw_attach("exuart", NULL, &bmajor,
245 &exuart_cdevsw, &cmajor);
246 if (error) {
247 aprint_error(": couldn't allocate major number\n");
248 return;
249 }
250 exynos_uart_cmajor = cmajor;
251 }
252
253 major = cdevsw_lookup_major(&exuart_cdevsw);
254 minor = device_unit(self);
255
256 tp = sc->sc_tty = tty_alloc();
257 tp->t_oproc = exynos_uart_start;
258 tp->t_param = exynos_uart_param;
259 tp->t_dev = makedev(major, minor);
260 tp->t_sc = sc;
261 tty_attach(tp);
262
263 aprint_naive("\n");
264 if (is_console) {
265 cn_tab->cn_dev = tp->t_dev;
266 aprint_normal(": console");
267 }
268 aprint_normal("\n");
269
270 if (is_console)
271 delay(10000);
272
273 /* Initialize device */
274 WR4(sc, SSCOM_UFCON,
275 __SHIFTIN(2, UFCON_TXTRIGGER) |
276 __SHIFTIN(1, UFCON_RXTRIGGER) |
277 UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
278 UFCON_FIFO_ENABLE);
279
280 /* Configure PIO mode with RX timeout interrupts */
281 ucon = UCON_TOINT | UCON_ERRINT |
282 UCON_TXMODE_INT | UCON_RXMODE_INT;
283 WR4(sc, SSCOM_UCON, ucon);
284
285 switch (sc->sc_conf->type) {
286 case EXYNOS_UART_SAMSUNG:
287 WR4(sc, SSCOM_UCON, ucon | __SHIFTIN(3, UCON_RXTO));
288 /* Disable interrupts */
289 WR4(sc, SSCOM_UINTM, ~0u);
290 break;
291 case EXYNOS_UART_APPLE:
292 break;
293 }
294
295 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
296 }
297
298 static int
299 exynos_uart_cngetc(dev_t dev)
300 {
301 struct exynos_uart_softc * const sc = &exynos_uart_cnsc;
302 uint32_t ufstat;
303 int s, c;
304
305 s = splserial();
306
307 ufstat = RD4(sc, SSCOM_UFSTAT);
308 if (__SHIFTOUT(ufstat, sc->sc_conf->rxcount) == 0) {
309 splx(s);
310 return -1;
311 }
312
313 c = RD4(sc, SSCOM_URXH);
314 #if defined(DDB)
315 extern int db_active;
316 if (!db_active)
317 #endif
318 {
319 int cn_trapped __unused = 0;
320 cn_check_magic(dev, c & 0xff, exynos_uart_cnm_state);
321 }
322
323 splx(s);
324
325 return c & 0xff;
326 }
327
328 static void
329 exynos_uart_cnputc(dev_t dev, int c)
330 {
331 struct exynos_uart_softc * const sc = &exynos_uart_cnsc;
332 int s;
333
334 s = splserial();
335 while ((RD4(sc, SSCOM_UFSTAT) & sc->sc_conf->txfull) != 0)
336 ;
337
338 WR4(sc, SSCOM_UTXH, c & 0xff);
339
340 splx(s);
341 }
342
343
344 static void
345 exynos_uart_cnpollc(dev_t dev, int on)
346 {
347 }
348
349 static void
350 exynos_uart_cnattach(bus_space_tag_t bst, bus_space_handle_t bsh,
351 int ospeed, tcflag_t cflag, const struct exynos_uart_config *conf)
352 {
353 struct exynos_uart_softc *sc = &exynos_uart_cnsc;
354
355 cn_tab = &exynos_uart_consdev;
356 cn_init_magic(&exynos_uart_cnm_state);
357 cn_set_magic("\047\001");
358
359 sc->sc_bst = bst;
360 sc->sc_bsh = bsh;
361 sc->sc_ospeed = ospeed;
362 sc->sc_cflag = cflag;
363 sc->sc_conf = conf;
364 }
365
366 static int
367 exynos_uart_open(dev_t dev, int flag, int mode, lwp_t *l)
368 {
369 struct exynos_uart_softc *sc =
370 device_lookup_private(&exuart_cd, minor(dev));
371 struct tty *tp = sc->sc_tty;
372 uint32_t ucon;
373
374 if (kauth_authorize_device_tty(l->l_cred,
375 KAUTH_DEVICE_TTY_OPEN, tp) != 0) {
376 return EBUSY;
377 }
378
379 mutex_enter(&sc->sc_lock);
380
381 if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
382 tp->t_dev = dev;
383 ttychars(tp);
384 tp->t_iflag = TTYDEF_IFLAG;
385 tp->t_oflag = TTYDEF_OFLAG;
386 tp->t_lflag = TTYDEF_LFLAG;
387 if (sc->sc_console) {
388 tp->t_ispeed = tp->t_ospeed = exynos_uart_cnsc.sc_ospeed;
389 tp->t_cflag = exynos_uart_cnsc.sc_cflag;
390 } else {
391 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
392 tp->t_cflag = TTYDEF_CFLAG;
393 }
394 ttsetwater(tp);
395 }
396 tp->t_state |= TS_CARR_ON;
397
398 /* Enable RX and error interrupts */
399 switch (sc->sc_conf->type) {
400 case EXYNOS_UART_SAMSUNG:
401 WR4(sc, SSCOM_UINTM, ~0u & ~(UINT_RXD|UINT_ERROR));
402 break;
403 case EXYNOS_UART_APPLE:
404 ucon = RD4(sc, SSCOM_UCON);
405 ucon |= UCON_S5L_RXTHRESH | UCON_S5L_RX_TIMEOUT;
406 WR4(sc, SSCOM_UCON, ucon);
407 break;
408 }
409
410 mutex_exit(&sc->sc_lock);
411
412 return tp->t_linesw->l_open(dev, tp);
413 }
414
415 static int
416 exynos_uart_close(dev_t dev, int flag, int mode, lwp_t *l)
417 {
418 struct exynos_uart_softc *sc =
419 device_lookup_private(&exuart_cd, minor(dev));
420 struct tty *tp = sc->sc_tty;
421 uint32_t ucon;
422
423 mutex_enter(&sc->sc_lock);
424
425 tp->t_linesw->l_close(tp, flag);
426 ttyclose(tp);
427
428 /* Disable interrupts */
429 switch (sc->sc_conf->type) {
430 case EXYNOS_UART_SAMSUNG:
431 WR4(sc, SSCOM_UINTM, ~0u);
432 break;
433 case EXYNOS_UART_APPLE:
434 ucon = RD4(sc, SSCOM_UCON);
435 ucon &= ~(UCON_S5L_RXTHRESH | UCON_S5L_RX_TIMEOUT);
436 WR4(sc, SSCOM_UCON, ucon);
437 break;
438 }
439
440 mutex_exit(&sc->sc_lock);
441
442 return 0;
443 }
444
445 static int
446 exynos_uart_read(dev_t dev, struct uio *uio, int flag)
447 {
448 struct exynos_uart_softc *sc =
449 device_lookup_private(&exuart_cd, minor(dev));
450 struct tty *tp = sc->sc_tty;
451
452 return tp->t_linesw->l_read(tp, uio, flag);
453 }
454
455 static int
456 exynos_uart_write(dev_t dev, struct uio *uio, int flag)
457 {
458 struct exynos_uart_softc *sc =
459 device_lookup_private(&exuart_cd, minor(dev));
460 struct tty *tp = sc->sc_tty;
461
462 return tp->t_linesw->l_write(tp, uio, flag);
463 }
464
465 static int
466 exynos_uart_poll(dev_t dev, int events, lwp_t *l)
467 {
468 struct exynos_uart_softc *sc =
469 device_lookup_private(&exuart_cd, minor(dev));
470 struct tty *tp = sc->sc_tty;
471
472 return tp->t_linesw->l_poll(tp, events, l);
473 }
474
475 static int
476 exynos_uart_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
477 {
478 struct exynos_uart_softc *sc =
479 device_lookup_private(&exuart_cd, minor(dev));
480 struct tty *tp = sc->sc_tty;
481 int error;
482
483 error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
484 if (error != EPASSTHROUGH)
485 return error;
486
487 return ttioctl(tp, cmd, data, flag, l);
488 }
489
490 static struct tty *
491 exynos_uart_tty(dev_t dev)
492 {
493 struct exynos_uart_softc *sc =
494 device_lookup_private(&exuart_cd, minor(dev));
495
496 return sc->sc_tty;
497 }
498
499 static void
500 exynos_uart_stop(struct tty *tp, int flag)
501 {
502 }
503
504 static void
505 exynos_uart_start(struct tty *tp)
506 {
507 struct exynos_uart_softc *sc = tp->t_sc;
508 u_char *p = sc->sc_buf;
509 int s, brem;
510
511 s = spltty();
512
513 if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) {
514 splx(s);
515 return;
516 }
517 tp->t_state |= TS_BUSY;
518
519 for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf));
520 brem > 0;
521 brem--, p++) {
522 while ((RD4(sc, SSCOM_UFSTAT) & sc->sc_conf->txfull) != 0)
523 ;
524
525 WR4(sc, SSCOM_UTXH, *p);
526 }
527
528 tp->t_state &= ~TS_BUSY;
529 if (ttypull(tp)) {
530 tp->t_state |= TS_TIMEOUT;
531 callout_schedule(&tp->t_rstrt_ch, 1);
532 }
533 splx(s);
534 }
535
536 static int
537 exynos_uart_param(struct tty *tp, struct termios *t)
538 {
539 struct exynos_uart_softc *sc = tp->t_sc;
540
541 mutex_enter(&sc->sc_lock);
542
543 if (tp->t_cflag != t->c_cflag) {
544 uint32_t ulcon = 0;
545 switch (ISSET(t->c_cflag, CSIZE)) {
546 case CS5:
547 ulcon |= ULCON_LENGTH_5;
548 break;
549 case CS6:
550 ulcon |= ULCON_LENGTH_6;
551 break;
552 case CS7:
553 ulcon |= ULCON_LENGTH_7;
554 break;
555 case CS8:
556 ulcon |= ULCON_LENGTH_8;
557 break;
558 }
559 switch (ISSET(t->c_cflag, PARENB|PARODD)) {
560 case PARENB|PARODD:
561 ulcon |= ULCON_PARITY_ODD;
562 break;
563 case PARENB:
564 ulcon |= ULCON_PARITY_EVEN;
565 break;
566 default:
567 ulcon |= ULCON_PARITY_NONE;
568 break;
569 }
570 if (ISSET(t->c_cflag, CSTOPB))
571 ulcon |= ULCON_STOP;
572 WR4(sc, SSCOM_ULCON, ulcon);
573 }
574
575 if (tp->t_ospeed != t->c_ospeed) {
576 const uint32_t ubrdiv = (sc->sc_freq / 16) / t->c_ospeed - 1;
577 WR4(sc, SSCOM_UBRDIV, ubrdiv);
578 }
579
580 tp->t_ispeed = t->c_ispeed;
581 tp->t_ospeed = t->c_ospeed;
582 tp->t_cflag = t->c_cflag;
583
584 mutex_exit(&sc->sc_lock);
585
586 return 0;
587 }
588
589 static int
590 exynos_uart_intr(void *priv)
591 {
592 struct exynos_uart_softc *sc = priv;
593 struct tty *tp = sc->sc_tty;
594 uint32_t ack, uerstat, ufstat, c;
595
596 mutex_enter(&sc->sc_lock);
597
598 if (sc->sc_conf->type == EXYNOS_UART_APPLE) {
599 ack = RD4(sc, SSCOM_UTRSTAT);
600 } else {
601 ack = RD4(sc, SSCOM_UINTP);
602 }
603
604 for (;;) {
605 int cn_trapped = 0;
606
607 uerstat = RD4(sc, SSCOM_UERSTAT);
608 if (uerstat & UERSTAT_BREAK) {
609 cn_check_magic(tp->t_dev, CNC_BREAK,
610 exynos_uart_cnm_state);
611 if (cn_trapped)
612 continue;
613 }
614
615 ufstat = RD4(sc, SSCOM_UFSTAT);
616 if (__SHIFTOUT(ufstat, sc->sc_conf->rxcount) == 0) {
617 break;
618 }
619
620 c = RD4(sc, SSCOM_URXH);
621 cn_check_magic(tp->t_dev, c & 0xff, exynos_uart_cnm_state);
622 if (cn_trapped)
623 continue;
624 tp->t_linesw->l_rint(c & 0xff, tp);
625 }
626
627 if (sc->sc_conf->type == EXYNOS_UART_APPLE) {
628 WR4(sc, SSCOM_UTRSTAT, ack);
629 } else {
630 WR4(sc, SSCOM_UINTP, ack);
631 }
632
633 mutex_exit(&sc->sc_lock);
634
635 return 1;
636 }
637
638 /*
639 * Console support
640 */
641
642 static int
643 exynos_uart_console_match(int phandle)
644 {
645 return of_compatible_match(phandle, compat_data);
646 }
647
648 static void
649 exynos_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
650 {
651 const int phandle = faa->faa_phandle;
652 bus_space_tag_t bst = faa->faa_bst;
653 bus_space_handle_t bsh;
654 bus_addr_t addr;
655 bus_size_t size;
656 tcflag_t flags;
657 int speed;
658 const struct exynos_uart_config *conf;
659
660 speed = fdtbus_get_stdout_speed();
661 if (speed < 0)
662 speed = 115200; /* default */
663 flags = fdtbus_get_stdout_flags();
664 conf = of_compatible_lookup(phandle, compat_data)->data;
665
666 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0)
667 panic("exynos_uart: couldn't get registers");
668 if (bus_space_map(bst, addr, size, 0, &bsh) != 0)
669 panic("exynos_uart: couldn't map registers");
670
671 exynos_uart_consaddr = addr;
672
673 exynos_uart_cnattach(bst, bsh, speed, flags, conf);
674 }
675
676 static const struct fdt_console exynos_uart_console = {
677 .match = exynos_uart_console_match,
678 .consinit = exynos_uart_console_consinit,
679 };
680
681 FDT_CONSOLE(exynos_uart, &exynos_uart_console);
682