1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
5 *
6 * Authors:
7 * Anup Patel <anup.patel@wdc.com>
8 */
9
10 #include <libfdt.h>
11 #include <sbi/sbi_scratch.h>
12 #include <sbi_utils/fdt/fdt_helper.h>
13 #include <sbi_utils/serial/fdt_serial.h>
14
15 extern struct fdt_serial fdt_serial_uart8250;
16 extern struct fdt_serial fdt_serial_sifive;
17 extern struct fdt_serial fdt_serial_htif;
18 extern struct fdt_serial fdt_serial_shakti;
19
20 static struct fdt_serial *serial_drivers[] = {
21 &fdt_serial_uart8250,
22 &fdt_serial_sifive,
23 &fdt_serial_htif,
24 &fdt_serial_shakti,
25 };
26
dummy_putc(char ch)27 static void dummy_putc(char ch)
28 {
29 }
30
dummy_getc(void)31 static int dummy_getc(void)
32 {
33 return -1;
34 }
35
36 static struct fdt_serial dummy = {
37 .match_table = NULL,
38 .init = NULL,
39 .putc = dummy_putc,
40 .getc = dummy_getc,
41 };
42
43 static struct fdt_serial *current_driver = &dummy;
44
fdt_serial_putc(char ch)45 void fdt_serial_putc(char ch)
46 {
47 current_driver->putc(ch);
48 }
49
fdt_serial_getc(void)50 int fdt_serial_getc(void)
51 {
52 return current_driver->getc();
53 }
54
fdt_serial_init(void)55 int fdt_serial_init(void)
56 {
57 const void *prop;
58 struct fdt_serial *drv;
59 const struct fdt_match *match;
60 int pos, noff = -1, len, coff, rc;
61 void *fdt = sbi_scratch_thishart_arg1_ptr();
62
63 /* Find offset of node pointed by stdout-path */
64 coff = fdt_path_offset(fdt, "/chosen");
65 if (-1 < coff) {
66 prop = fdt_getprop(fdt, coff, "stdout-path", &len);
67 if (prop && len)
68 noff = fdt_path_offset(fdt, prop);
69 }
70
71 /* First check DT node pointed by stdout-path */
72 for (pos = 0; pos < array_size(serial_drivers) && -1 < noff; pos++) {
73 drv = serial_drivers[pos];
74
75 match = fdt_match_node(fdt, noff, drv->match_table);
76 if (!match)
77 continue;
78
79 if (drv->init) {
80 rc = drv->init(fdt, noff, match);
81 if (rc)
82 return rc;
83 }
84 current_driver = drv;
85 break;
86 }
87
88 /* Check if we found desired driver */
89 if (current_driver != &dummy)
90 goto done;
91
92 /* Lastly check all DT nodes */
93 for (pos = 0; pos < array_size(serial_drivers); pos++) {
94 drv = serial_drivers[pos];
95
96 noff = fdt_find_match(fdt, -1, drv->match_table, &match);
97 if (noff < 0)
98 continue;
99
100 if (drv->init) {
101 rc = drv->init(fdt, noff, match);
102 if (rc)
103 return rc;
104 }
105 current_driver = drv;
106 break;
107 }
108
109 done:
110 return 0;
111 }
112