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 ;)
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014, Ithamar R. Adema <[email protected]>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DRIVERS_BUS_FDT_H
|
||||||
|
#define _DRIVERS_BUS_FDT_H
|
||||||
|
|
||||||
|
#include <bus_manager.h>
|
||||||
|
|
||||||
|
#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
|
||||||
@@ -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 acpi ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel bus_managers agp_gart ;
|
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 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 firewire ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel bus_managers ata ;
|
SubInclude HAIKU_TOP src add-ons kernel bus_managers ata ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel bus_managers ide ;
|
SubInclude HAIKU_TOP src add-ons kernel bus_managers ide ;
|
||||||
|
|||||||
@@ -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 ] ;
|
||||||
|
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014, Ithamar R. Adema <[email protected]>
|
||||||
|
* All rights reserved. Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/bus/FDT.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <util/kernel_cpp.h>
|
||||||
|
|
||||||
|
#include <ctype.h> // isprint
|
||||||
|
#include <stdio.h> // snprintf
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <fdt.h>
|
||||||
|
#include <libfdt.h>
|
||||||
|
#include <libfdt_env.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user