xref: /haiku/src/system/boot/platform/u-boot/serial.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
1 /*
2  * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2012, Alexander von Gluck, kallisti5@unixzen.com
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include "serial.h"
11 
12 #include <debug_uart_8250.h>
13 #include <board_config.h>
14 #include <boot/platform.h>
15 #include <arch/cpu.h>
16 #include <boot/stage2.h>
17 #include <new>
18 #include <string.h>
19 
20 extern "C" {
21 #include <fdt.h>
22 #include <libfdt.h>
23 #include <libfdt_env.h>
24 };
25 
26 
27 DebugUART* gUART;
28 
29 static int32 sSerialEnabled = 0;
30 static char sBuffer[16384];
31 static uint32 sBufferPosition;
32 
33 
34 static void
35 serial_putc(char c)
36 {
37 	gUART->PutChar(c);
38 }
39 
40 
41 extern "C" void
42 serial_puts(const char* string, size_t size)
43 {
44 	if (sSerialEnabled <= 0)
45 		return;
46 
47 	if (sBufferPosition + size < sizeof(sBuffer)) {
48 		memcpy(sBuffer + sBufferPosition, string, size);
49 		sBufferPosition += size;
50 	}
51 
52 	while (size-- != 0) {
53 		char c = string[0];
54 
55 		if (c == '\n') {
56 			serial_putc('\r');
57 			serial_putc('\n');
58 		} else if (c != '\r')
59 			serial_putc(c);
60 
61 		string++;
62 	}
63 }
64 
65 
66 extern "C" void
67 serial_disable(void)
68 {
69 	sSerialEnabled = 0;
70 }
71 
72 
73 extern "C" void
74 serial_enable(void)
75 {
76 	/* should already be initialized by U-Boot */
77 	/*
78 	gUART->InitEarly();
79 	gUART->InitPort(9600);
80 	*/
81 	sSerialEnabled++;
82 }
83 
84 
85 extern "C" void
86 serial_cleanup(void)
87 {
88 	if (sSerialEnabled <= 0)
89 		return;
90 
91 	gKernelArgs.debug_output = kernel_args_malloc(sBufferPosition);
92 	if (gKernelArgs.debug_output != NULL) {
93 		memcpy(gKernelArgs.debug_output, sBuffer, sBufferPosition);
94 		gKernelArgs.debug_size = sBufferPosition;
95 	}
96 }
97 
98 
99 static void
100 serial_init_fdt(const void *fdt)
101 {
102 	const char *name;
103 	const char *type;
104 	int node;
105 	int len;
106 	phys_addr_t regs;
107 	int32 clock = 0;
108 	int32 speed = 0;
109 	const void *prop;
110 
111 	if (fdt == NULL)
112 		return;
113 
114 	name = fdt_get_alias(fdt, "serial");
115 	if (name == NULL)
116 		name = fdt_get_alias(fdt, "serial0");
117 	if (name == NULL)
118 		name = fdt_get_alias(fdt, "serial1");
119 	// TODO: else use /chosen linux,stdout-path
120 	if (name == NULL)
121 		return;
122 
123 	node = fdt_path_offset(fdt, name);
124 	//dprintf("serial: using '%s', node %d\n", name, node);
125 	if (node < 0)
126 		return;
127 
128 	type = (const char *)fdt_getprop(fdt, node, "device_type", &len);
129 	//dprintf("serial: type: '%s'\n", type);
130 	if (type == NULL || strcmp(type, "serial"))
131 		return;
132 
133 	// determine the MMIO address
134 	// TODO: ppc640 use 64bit addressing, but U-Boot seems to map it below 4G,
135 	// and the FDT is not very clear. libfdt is also getting 64bit addr support.
136 	// so FIXME someday.
137 	prop = fdt_getprop(fdt, node, "virtual-reg", &len);
138 	if (prop && len == 4) {
139 		regs = fdt32_to_cpu(*(uint32_t *)prop);
140 		//dprintf("serial: virtual-reg 0x%08llx\n", (int64)regs);
141 	} else {
142 		prop = fdt_getprop(fdt, node, "reg", &len);
143 		if (prop && len >= 4) {
144 			regs = fdt32_to_cpu(*(uint32_t *)prop);
145 			//dprintf("serial: reg 0x%08llx\n", (int64)regs);
146 		} else
147 			return;
148 	}
149 
150 	// get the UART clock rate
151 	prop = fdt_getprop(fdt, node, "clock-frequency", &len);
152 	if (prop && len == 4) {
153 		clock = fdt32_to_cpu(*(uint32_t *)prop);
154 		//dprintf("serial: clock %ld\n", clock);
155 	}
156 
157 	// get current speed (XXX: not yet passed over)
158 	prop = fdt_getprop(fdt, node, "current-speed", &len);
159 	if (prop && len == 4) {
160 		speed = fdt32_to_cpu(*(uint32_t *)prop);
161 		//dprintf("serial: speed %ld\n", speed);
162 	}
163 
164 	if (fdt_node_check_compatible(fdt, node, "ns16550a") == 1
165 		|| fdt_node_check_compatible(fdt, node, "ns16550") == 1) {
166 		gUART = arch_get_uart_8250(regs, clock);
167 		//dprintf("serial: using 8250\n");
168 		return;
169 	}
170 
171 }
172 
173 
174 extern "C" void
175 serial_init(const void *fdt)
176 {
177 	// first try with hints from the FDT
178 	serial_init_fdt(fdt);
179 
180 #ifdef BOARD_UART_DEBUG
181 	// fallback to hardcoded board UART
182 	if (gUART == NULL)
183 		gUART = arch_get_uart_8250(BOARD_UART_DEBUG, BOARD_UART_CLOCK);
184 #endif
185 
186 	if (gUART == NULL)
187 		return;
188 
189 	serial_enable();
190 }
191