From 9d010ea47db677131e385b5e7855d38fd0c8103f Mon Sep 17 00:00:00 2001 From: Alexander von Gluck IV Date: Fri, 20 Sep 2019 09:32:59 -0500 Subject: [PATCH] fdt/serial: Make uart search more agressive. Examine /chosen for serial devices Change-Id: Icc1673b331d9afb3a7b34c91e7b1f20c3dee964a Reviewed-on: https://review.haiku-os.org/c/haiku/+/1871 Reviewed-by: Adrien Destugues Reviewed-by: Alex von Gluck IV --- .../kernel/bus_managers/fdt/fdt_serial.cpp | 129 +++++++++++++----- 1 file changed, 95 insertions(+), 34 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/fdt/fdt_serial.cpp b/src/add-ons/kernel/bus_managers/fdt/fdt_serial.cpp index 25d06fdf31..a2ba64832d 100644 --- a/src/add-ons/kernel/bus_managers/fdt/fdt_serial.cpp +++ b/src/add-ons/kernel/bus_managers/fdt/fdt_serial.cpp @@ -40,43 +40,18 @@ extern "C" { // If we dprintf before the UART is initalized there will be no output - -DebugUART* -debug_uart_from_fdt(const void *fdt) +static DebugUART* +debug_uart_from_node(const void *fdt, int node) { - const char *name; - int node; int len; + const void *prop; phys_addr_t regs; int32 clock = 0; int32 speed = 0; - const void *prop; DebugUART *uart = NULL; - if (fdt == NULL) { - TRACE("%s: No FDT found!\n", __func__); + if (node < 0 || fdt == NULL) return NULL; - } - - name = fdt_get_alias(fdt, "serial"); - if (name == NULL) - name = fdt_get_alias(fdt, "serial0"); - if (name == NULL) - name = fdt_get_alias(fdt, "serial1"); - if (name == NULL) - name = fdt_get_alias(fdt, "uart0"); - // TODO: else use /chosen linux,stdout-path - if (name == NULL) { - TRACE("%s: No known FDT UART alias found!\n", __func__); - return NULL; - } - - node = fdt_path_offset(fdt, name); - - if (node < 0) { - TRACE("%s: FDT node not found!\n", __func__); - return NULL; - } // determine the MMIO address regs = fdt_get_device_reg(fdt, node, false); @@ -86,7 +61,7 @@ debug_uart_from_fdt(const void *fdt) return NULL; } - TRACE("serial: using '%s', node %d @ %" B_PRIxPHYSADDR "\n", + TRACE("serial: checking '%s', node %d @ %" B_PRIxPHYSADDR "\n", name, node, regs); // get the UART clock rate @@ -104,9 +79,9 @@ debug_uart_from_fdt(const void *fdt) } // fdt_node_check_compatible returns 0 on match. - if (fdt_node_check_compatible(fdt, node, "ns16550a") == 0 - || fdt_node_check_compatible(fdt, node, "ns16550") == 0) { + || fdt_node_check_compatible(fdt, node, "ns16550") == 0 + || fdt_node_check_compatible(fdt, node, "snps,dw-apb-uart") == 0) { TRACE("serial: Found 8250 serial UART!\n"); uart = arch_get_uart_8250(regs, clock); #if defined(__arm__) @@ -124,10 +99,96 @@ debug_uart_from_fdt(const void *fdt) TRACE("serial: Found pl011 serial UART!\n"); uart = arch_get_uart_pl011(regs, clock); #endif - } else { - // TODO: handle more UART types + } + return uart; +} + + +DebugUART* +debug_uart_from_fdt(const void *fdt) +{ + int chosen_node; + int node; + int len; + const char *name; + const void *prop; + DebugUART *uart = NULL; + + if (fdt == NULL) { + TRACE("%s: No FDT found!\n", __func__); return NULL; } + chosen_node = fdt_path_offset(fdt, "/chosen"); + if (chosen_node >= 0) { + prop = fdt_getprop(fdt, chosen_node, "stdout-path", &len); + if (prop && len > 0) { + node = fdt_path_offset(fdt, (const char*)prop); + uart = debug_uart_from_node(fdt, node); + } + if (uart == NULL) { + prop = fdt_getprop(fdt, chosen_node, "linux,stdout-path", &len); + if (prop && len > 0) { + node = fdt_path_offset(fdt, (const char*)prop); + uart = debug_uart_from_node(fdt, node); + } + } + + if (uart == NULL) { + // From what i've seen, stdout is generally an alias. + // we could check for "/..." in the prop, but not sure + // it's needed. If we *did* check for a prop starting + // with / we could make all three of these "the same" + prop = fdt_getprop(fdt, chosen_node, "stdout", &len); + if (prop && len > 0) { + name = fdt_get_alias(fdt, (const char*)prop); + if (name != NULL) { + node = fdt_path_offset(fdt, name); + uart = debug_uart_from_node(fdt, node); + } + } + } + + // Whoo-hoo! Bail. + if (uart != NULL) + return uart; + } + + // If we didn't find a /chosen serial device, lets search for some common aliases + char aliases[][8] = { + "serial", + "serial0", + "uart", + "uart0", + "serial1", + "serial2", + "serial3", + "uart1", + "uart2", + "uart3" + }; + + // For each known common serial alias, check it out and see if we have the + // needed driver for it. uart0 seems most common. + for (int index = 0; index < sizeof(aliases[0]) / sizeof(aliases); index++) { + name = fdt_get_alias(fdt, aliases[index]); + if (name == NULL) + continue; + + node = fdt_path_offset(fdt, name); + if (node < 0) { + TRACE("%s: FDT node not found!\n", __func__); + continue; + } + uart = debug_uart_from_node(fdt, node); + + // We found a valid serial device. bail. + if (uart != NULL) + break; + } + + // It would be nice if we had *some* communication mechanism here if uart is still + // NULL to warn the user that we couldn't find a serial port. + return uart; }