From 778611c7e6a61b8ba072212756ce53eda826360a Mon Sep 17 00:00:00 2001 From: "Ithamar R. Adema" Date: Fri, 31 Oct 2014 11:14:05 +0100 Subject: [PATCH] fdt: humble beginnings of Flattened Device Tree This isn't really a bus_manager yet, but just minimal support so we can get rid of hardcoded ARM SoC support from the core kernel code. Needs lots of work, like proper handling of #address-cells and the like. Also, generic attribute handling, device_manager integration, and I could go on for hours ;) --- headers/os/drivers/bus/FDT.h | 45 +++++ src/add-ons/kernel/bus_managers/Jamfile | 1 + src/add-ons/kernel/bus_managers/fdt/Jamfile | 22 ++ src/add-ons/kernel/bus_managers/fdt/fdt.cpp | 210 ++++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 headers/os/drivers/bus/FDT.h create mode 100644 src/add-ons/kernel/bus_managers/fdt/Jamfile create mode 100644 src/add-ons/kernel/bus_managers/fdt/fdt.cpp diff --git a/headers/os/drivers/bus/FDT.h b/headers/os/drivers/bus/FDT.h new file mode 100644 index 0000000000..e391a653d3 --- /dev/null +++ b/headers/os/drivers/bus/FDT.h @@ -0,0 +1,45 @@ +/* + * Copyright 2014, Ithamar R. Adema + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef _DRIVERS_BUS_FDT_H +#define _DRIVERS_BUS_FDT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int fdt_device_node; + +struct fdt_device_info { + const char *compatible; + status_t (*init)(struct fdt_module_info *fdt, fdt_device_node node, void *cookie); +}; + +struct fdt_module_info { + bus_manager_info binfo; + + // basic call for triggering callbacks for supported devices + // scans the whole FDT tree once and calls the info.init function + // when a matching device is found. + status_t (*setup_devices)(struct fdt_device_info *info, int count, void *cookie); + + // map physical "reg" range "index" of node "node", and return the virtual address in '*_address' + // and return the area ID or error if not able to. + area_id (*map_reg_range)(fdt_device_node node, int index, void **_address); + + // return entry "index" out of "interrupts" property for node "node", or a negative error code on failure. + int (*get_interrupt)(fdt_device_node node, int index); +}; + +#define B_FDT_MODULE_NAME "bus_managers/fdt/v1" + + +#ifdef __cplusplus +} +#endif + +#endif // _DRIVERS_BUS_FDT_H diff --git a/src/add-ons/kernel/bus_managers/Jamfile b/src/add-ons/kernel/bus_managers/Jamfile index 777b8801dd..51739376c8 100644 --- a/src/add-ons/kernel/bus_managers/Jamfile +++ b/src/add-ons/kernel/bus_managers/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers ; SubInclude HAIKU_TOP src add-ons kernel bus_managers acpi ; SubInclude HAIKU_TOP src add-ons kernel bus_managers agp_gart ; SubInclude HAIKU_TOP src add-ons kernel bus_managers config_manager ; +SubInclude HAIKU_TOP src add-ons kernel bus_managers fdt ; SubInclude HAIKU_TOP src add-ons kernel bus_managers firewire ; SubInclude HAIKU_TOP src add-ons kernel bus_managers ata ; SubInclude HAIKU_TOP src add-ons kernel bus_managers ide ; diff --git a/src/add-ons/kernel/bus_managers/fdt/Jamfile b/src/add-ons/kernel/bus_managers/fdt/Jamfile new file mode 100644 index 0000000000..6c18bddb73 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/fdt/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src add-ons kernel bus_managers fdt ; + +UsePrivateKernelHeaders ; +UseLibraryHeaders [ FDirName libfdt ] ; + +local libFDTSources = + fdt.c + fdt_ro.c + fdt_rw.c + fdt_strerror.c + fdt_sw.c + fdt_wip.c +; + +KernelAddon fdt : + fdt.cpp +# $(libFDTSources) + ; + +SEARCH on [ FGristFiles $(libFDTSources) ] + = [ FDirName $(HAIKU_TOP) src libs libfdt ] ; + diff --git a/src/add-ons/kernel/bus_managers/fdt/fdt.cpp b/src/add-ons/kernel/bus_managers/fdt/fdt.cpp new file mode 100644 index 0000000000..5a4fc2a9da --- /dev/null +++ b/src/add-ons/kernel/bus_managers/fdt/fdt.cpp @@ -0,0 +1,210 @@ +/* + * Copyright 2014, Ithamar R. Adema + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include +#include +#include + +#include // isprint +#include // snprintf + +extern "C" { +#include +#include +#include +}; + +extern void *gFDT; + +static status_t fdt_setup_devices(struct fdt_device_info *info, int count, void *cookie); + +static const char *sTabTab = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; +#define DS "%.*s" +#define DA depth - 1, sTabTab + +static void +fdt_dump_value(const char *data, int32 len, int depth) +{ + char str[128]; + char astr[32]; + char *p; + int l; + int i; + + for (i = 0; i < len; ) { + p = str; + l = sizeof(str); + for (; i < len && (p == str || (i % 16 != 0)); i++) { + snprintf(p, l - 1, "%02x ", data[i]); + l -= strlen(p); + p += strlen(p); + astr[i % 16] = isprint(data[i]) ? data[i] : '.'; + astr[i % 16] = isprint(data[i]) ? data[i] : '.'; + astr[(i % 16) + 1] = '\0'; + } + dprintf(DS" %-48.48s %s\n", DA, str, astr); + } +} + +static int +fdt_debug_tree(int argc, char **argv) +{ + bool dump_props = false, dump_values = false; + + if (gFDT == NULL) { + dprintf("No fdt tree\n"); + return 0; + } + + dprintf("fdt tree:\n"); + + int node = -1; + int depth = 0; + while ((node = fdt_next_node(gFDT, node, &depth)) >= 0) { + dprintf(DS"node at %d: '%s'\n", DA, node, + fdt_get_name(gFDT, node, NULL)); + + if (dump_props) { + int prop, len; + const struct fdt_property *property; + prop = fdt_first_property_offset(gFDT, node); + while (prop >= 0) { + property = fdt_get_property_by_offset(gFDT, prop, &len); + if (property == NULL) { + dprintf("getting prop at %d: %s\n", prop, fdt_strerror(len)); + break; + } + dprintf(DS" prop at %d: '%s', len %d\n", DA, prop, + fdt_string(gFDT, fdt32_to_cpu(property->nameoff)), + fdt32_to_cpu(property->len)); + + if (dump_values) + fdt_dump_value(property->data, fdt32_to_cpu(property->len), depth); + + prop = fdt_next_property_offset(gFDT, prop); + } + } + } + + return 0; +} + + +static int32 +bus_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + if (gFDT == NULL || fdt_totalsize(gFDT) <= 0) + return B_BAD_DATA; + + add_debugger_command("fdt", &fdt_debug_tree, "Show Flattened Device Tree"); + break; + + case B_MODULE_UNINIT: + // Nothing to free, gFDT allocation is managed by kernel + break; + + default: + return EINVAL; + } + + return B_OK; +} + +static int +fdt_get_interrupt(fdt_device_node node, int index) +{ + const struct fdt_property *prop; + int lenp; + + prop = fdt_get_property(gFDT, node, "interrupts", &lenp); + if (prop == NULL) + return B_NAME_NOT_FOUND; + + int numRanges = lenp / sizeof(uint32); + if (index > numRanges) + return B_BAD_INDEX; + + return fdt32_to_cpu(((uint32*)prop->data)[index]); +} + +static area_id +fdt_map_reg_range(fdt_device_node node, int index, void **_address) +{ + char name[B_OS_NAME_LENGTH] = "ingo_asked_me_to_name_this"; + const struct fdt_property *prop; + int lenp; + + prop = fdt_get_property(gFDT, node, "reg", &lenp); + if (prop == NULL) + return B_NAME_NOT_FOUND; + + int numRanges = lenp / (sizeof(uint32) * 2); + if (index > numRanges) + return B_BAD_INDEX; + + //snprintf(name, sizeof(name), "%s_reg_%d", fdt_get_name(gFDT, node, NULL), index); + + uint32* regs = (uint32*)prop->data; + phys_addr_t rangeStart = (phys_addr_t)fdt32_to_cpu(regs[index*2]); + uint32 rangeSize = fdt32_to_cpu(regs[index*2+1]); + dprintf("fdt_map_reg_range: found reg range %p/%lu\n", (void*)rangeStart, rangeSize); + + return map_physical_memory(name, rangeStart, rangeSize, + 0, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)_address); +} + +static struct fdt_module_info sModuleInfo = { + // First the bus_manager_info: + { + { + "bus_managers/fdt/v1", + B_KEEP_LOADED, // Keep loaded, even if no driver requires it + bus_std_ops + }, + NULL // the rescan function + }, + + fdt_setup_devices, + fdt_map_reg_range, + fdt_get_interrupt, +}; + + +module_info *modules[] = { + (module_info *)&sModuleInfo, + NULL +}; + +static status_t +fdt_setup_devices(struct fdt_device_info *info, int count, void *cookie) +{ + int numDevs = 0; + + if (gFDT == NULL) + return B_NOT_INITIALIZED; + + int node = -1; + int depth = 0; + while ((node = fdt_next_node(gFDT, node, &depth)) >= 0) { + for (int i=0; i < count; i++) { + if (fdt_node_check_compatible(gFDT, node, info[i].compatible) == 0) { + status_t result = info[i].init(&sModuleInfo, node, cookie); + if (result != B_OK) { + // TODO handle return value from init somehow? + dprintf("fdt: device '%s' failed to initialize!\n", + fdt_get_name(gFDT, node, NULL)); + } else { + ++numDevs; + } + } + } + } + + return (numDevs <= 0) ? B_ENTRY_NOT_FOUND : B_OK; +} + +