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