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