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