bus_managers/fdt: rewrite to support device manager node tree

* Breaks previous fdt module clients.

Change-Id: I8bfdca40a77c041ddef51488e1995e5d43edb340
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3977
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Alex von Gluck IV <[email protected]>
This commit is contained in:
X512
2021-08-14 23:57:35 +00:00
committed by Alex von Gluck IV
parent 961c0ecef4
commit f850bba8d0
8 changed files with 494 additions and 792 deletions
+17 -31
View File
@@ -1,45 +1,31 @@
/* /*
* Copyright 2014, Ithamar R. Adema <[email protected]> * Copyright 2020-2021, Haiku, Inc. All rights reserved.
* All rights reserved. Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef _DRIVERS_BUS_FDT_H #ifndef _DRIVERS_BUS_FDT_H
#define _DRIVERS_BUS_FDT_H #define _DRIVERS_BUS_FDT_H
#include <bus_manager.h>
#ifdef __cplusplus #include <device_manager.h>
extern "C" {
#endif
typedef int fdt_device_node;
struct fdt_device_info { struct fdt_bus;
const char *compatible; struct fdt_device;
status_t (*init)(struct fdt_module_info *fdt, fdt_device_node node, void *cookie);
struct fdt_bus_module_info {
driver_module_info info;
device_node* (*node_by_phandle)(fdt_bus* bus, int phandle);
}; };
struct fdt_module_info { struct fdt_device_module_info{
bus_manager_info binfo; driver_module_info info;
device_node* (*get_bus)(fdt_device* dev);
// basic call for triggering callbacks for supported devices const char* (*get_name)(fdt_device* dev);
// scans the whole FDT tree once and calls the info.init function const void* (*get_prop)(fdt_device* dev, const char* name, int* len);
// when a matching device is found. bool (*get_reg)(fdt_device* dev, uint32 ord, uint64* regs, uint64* len);
status_t (*setup_devices)(struct fdt_device_info *info, int count, void *cookie); bool (*get_interrupt)(fdt_device* dev, uint32 ord,
device_node** interruptController, uint64* interrupt);
// 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 #endif // _DRIVERS_BUS_FDT_H
+2 -4
View File
@@ -19,17 +19,15 @@ local earlyFDTHelpers =
; ;
KernelAddon fdt : KernelAddon fdt :
fdt.cpp fdt_module.cpp
# $(libFDTSources) $(libFDTSources)
; ;
BootStaticLibrary boot_fdt : BootStaticLibrary boot_fdt :
$(earlyFDTHelpers)
$(libFDTSources) $(libFDTSources)
; ;
KernelStaticLibrary kernel_fdt : KernelStaticLibrary kernel_fdt :
$(earlyFDTHelpers)
$(libFDTSources) $(libFDTSources)
; ;
-210
View File
@@ -1,210 +0,0 @@
/*
* 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;
}
@@ -0,0 +1,475 @@
/*
* Copyright 2014, Ithamar R. Adema <[email protected]>
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2015-2021, Haiku, Inc. 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 <device_manager.h>
#include <AutoDeleter.h>
#include <AutoDeleterDrivers.h>
#include <HashMap.h>
#include <debug.h>
extern "C" {
#include <fdt.h>
#include <libfdt.h>
#include <libfdt_env.h>
};
//#define TRACE_FDT
#ifdef TRACE_FDT
#define TRACE(x...) dprintf(x)
#else
#define TRACE(x...)
#endif
extern void* gFDT;
device_manager_info* gDeviceManager;
extern fdt_bus_module_info gBusModule;
extern fdt_device_module_info gDeviceModule;
//#pragma mark -
struct fdt_bus {
device_node* node;
HashMap<HashKey32<int32>, device_node*> phandles;
};
struct fdt_device {
device_node* node;
device_node* bus;
};
static status_t
fdt_register_node(fdt_bus* bus, int node, device_node* parentDev,
device_node*& curDev)
{
TRACE("%s('%s', %p)\n", __func__, fdt_get_name(gFDT, node, NULL),
parentDev);
const void* prop; int propLen;
device_attr attrs[8];
device_attr* attr = attrs;
const char *name = fdt_get_name(gFDT, node, NULL);
*attr++ = (device_attr) { B_DEVICE_BUS, B_STRING_TYPE, {string: "fdt"}};
*attr++ = (device_attr) { B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
{ string: (strcmp(name, "") != 0) ? name : "Root" } };
*attr++ = (device_attr) { "fdt/node", B_UINT32_TYPE, {ui32: (uint32)node}};
*attr++ = (device_attr) { "fdt/name", B_STRING_TYPE, {string: name}};
prop = fdt_getprop(gFDT, node, "device_type", &propLen);
if (prop != NULL) {
*attr++ = (device_attr) { "fdt/device_type", B_STRING_TYPE,
{ string: (const char*)prop } };
prop = fdt_getprop(gFDT, node, "compatible", &propLen);
if (prop != NULL) {
*attr++ = (device_attr){ "fdt/compatible", B_STRING_TYPE,
{ string: (const char*)prop } };
}
*attr = {0};
status_t res = gDeviceManager->register_node(parentDev,
"bus_managers/fdt/driver_v1", attrs, NULL, &curDev);
if (res < B_OK)
return res;
prop = fdt_getprop(gFDT, node, "phandle", &propLen);
if (prop != NULL)
bus->phandles.Put(fdt32_to_cpu(*(uint32_t*)prop), curDev);
return B_OK;
}
static void
fdt_traverse(fdt_bus* bus, int &node, int &depth, device_node* parentDev)
{
int curDepth = depth;
#if 0
for (int i = 0; i < depth; i++) dprintf(" ");
dprintf("node('%s')\n", fdt_get_name(gFDT, node, NULL));
#endif
device_node* curDev;
fdt_register_node(bus, node, parentDev, curDev);
node = fdt_next_node(gFDT, node, &depth);
while (node >= 0 && depth == curDepth + 1) {
fdt_traverse(bus, node, depth, curDev);
}
}
//#pragma mark bus
static int32
fdt_bus_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
TRACE("fdt root init\n");
return B_OK;
case B_MODULE_UNINIT:
TRACE("fdt root uninit\n");
return B_OK;
}
return B_BAD_VALUE;
}
static float
fdt_bus_supports_device(device_node* parent)
{
TRACE("fdt_bus_supports_device\n");
// make sure parent is really device root
const char* bus;
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
return B_ERROR;
if (strcmp(bus, "root"))
return 0.0;
return 1.0;
}
static status_t
fdt_bus_register_device(device_node* parent)
{
TRACE("+fdt_bus_register_device\n");
struct ScopeExit {
ScopeExit() {TRACE("-fdt_bus_register_device\n");}
} scopeExit;
device_attr attrs[] = {
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "FDT"}},
{B_DEVICE_FLAGS, B_UINT32_TYPE, {ui32: B_KEEP_DRIVER_LOADED}},
{}
};
return gDeviceManager->register_node(
parent, "bus_managers/fdt/root/driver_v1", attrs, NULL, NULL);
}
static status_t
fdt_bus_init(device_node* node, void** cookie)
{
TRACE("fdt_bus_init\n");
ObjectDeleter<fdt_bus> bus(new(std::nothrow) fdt_bus());
if (!bus.IsSet())
return B_NO_MEMORY;
bus->node = node;
*cookie = bus.Detach();
return B_OK;
}
static void
fdt_bus_uninit(void* cookie)
{
TRACE("fdt_bus_uninit\n");
ObjectDeleter<fdt_bus> bus((fdt_bus*)cookie);
}
static status_t
fdt_bus_register_child_devices(void* cookie)
{
TRACE("fdt_bus_register_child_devices\n");
fdt_bus* bus = (fdt_bus*)cookie;
int node = -1, depth = -1;
node = fdt_next_node(gFDT, node, &depth);
fdt_traverse(bus, node, depth, bus->node);
return B_OK;
}
device_node*
fdt_bus_node_by_phandle(fdt_bus* bus, int phandle)
{
ASSERT(bus != NULL);
device_node** devNode;
if (!bus->phandles.Get(phandle, devNode))
return NULL;
return *devNode;
}
//#pragma mark device
static status_t
fdt_device_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
}
return B_BAD_VALUE;
}
static status_t
fdt_device_init_driver(device_node* node, void** cookie)
{
TRACE("fdt_device_init_driver()\n");
ObjectDeleter<fdt_device> dev(new(std::nothrow) fdt_device());
if (!dev.IsSet())
return B_NO_MEMORY;
dev->node = node;
// get bus from parent node
DeviceNodePutter<&gDeviceManager> parent(
gDeviceManager->get_parent_node(node));
driver_module_info* parentModule;
void* parentDev;
ASSERT(gDeviceManager->get_driver(
parent.Get(), &parentModule, &parentDev) >= B_OK);
if (parentModule == (driver_module_info*)&gDeviceModule)
dev->bus = ((fdt_device*)parentDev)->bus;
else if (parentModule == (driver_module_info*)&gBusModule)
dev->bus = parent.Get();
else
panic("bad parent node");
*cookie = dev.Detach();
return B_OK;
}
static void
fdt_device_uninit_driver(void* cookie)
{
TRACE("fdt_device_uninit_driver()\n");
ObjectDeleter<fdt_device> dev((fdt_device*)cookie);
}
static status_t
fdt_device_register_child_devices(void* cookie)
{
TRACE("fdt_device_register_child_devices()\n");
return B_OK;
}
static device_node*
fdt_device_get_bus(fdt_device* dev)
{
ASSERT(dev != NULL);
return dev->bus;
}
static const char*
fdt_device_get_name(fdt_device* dev)
{
ASSERT(dev != NULL);
uint32 fdtNode;
ASSERT(gDeviceManager->get_attr_uint32(
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
return fdt_get_name(gFDT, (int)fdtNode, NULL);
}
static const void*
fdt_device_get_prop(fdt_device* dev, const char* name, int* len)
{
ASSERT(dev != NULL);
uint32 fdtNode;
ASSERT(gDeviceManager->get_attr_uint32(
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
return fdt_getprop(gFDT, (int)fdtNode, name, len);
}
static bool
fdt_device_get_reg(fdt_device* dev, uint32 ord, uint64* regs, uint64* len)
{
ASSERT(dev != NULL);
uint32 fdtNode;
ASSERT(gDeviceManager->get_attr_uint32(
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
int propLen;
const void* prop = fdt_getprop(gFDT, (int)fdtNode, "reg", &propLen);
if (prop == NULL)
return false;
// TODO: use '#address-cells', '#size-cells' in parent node to identify
// field sizes
if ((ord + 1)*16 > (uint32)propLen)
return false;
if (regs != NULL)
*regs = fdt64_to_cpu(*(((uint64*)prop) + 2*ord));
if (len != NULL)
*len = fdt64_to_cpu(*(((uint64*)prop) + 2*ord + 1));
return true;
}
static bool
fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
device_node** interruptController, uint64* interrupt)
{
ASSERT(dev != NULL);
uint32 fdtNode;
ASSERT(gDeviceManager->get_attr_uint32(
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
// TODO: handle other interrupt encodings
int propLen;
const void* prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
&propLen);
if (prop == NULL) {
prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts",
&propLen);
if (prop == NULL)
return false;
if ((ord + 1)*4 > (uint32)propLen)
return false;
if (interrupt != NULL)
*interrupt = fdt32_to_cpu(*(((uint32*)prop) + ord));
if (interruptController != NULL) {
prop = fdt_getprop(gFDT, (int)fdtNode, "interrupt-parent",
&propLen);
if (prop != NULL && propLen == 4) {
uint32 phandle = fdt32_to_cpu(*(uint32*)prop);
fdt_bus* bus;
ASSERT(gDeviceManager->get_driver(
dev->bus, NULL, (void**)&bus) >= B_OK);
*interruptController = fdt_bus_node_by_phandle(bus, phandle);
}
}
return true;
}
// TODO: use '#interrupt-cells' to identify field sizes
if ((ord + 1)*8 > (uint32)propLen)
return false;
if (interruptController != NULL) {
uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2*ord));
fdt_bus* bus;
ASSERT(gDeviceManager->get_driver(
dev->bus, NULL, (void**)&bus) >= B_OK);
*interruptController = fdt_bus_node_by_phandle(bus, phandle);
}
if (interrupt != NULL)
*interrupt = fdt32_to_cpu(*(((uint32*)prop) + 2*ord + 1));
return true;
}
//#pragma mark -
fdt_bus_module_info gBusModule = {
{
{
"bus_managers/fdt/root/driver_v1",
0,
fdt_bus_std_ops
},
fdt_bus_supports_device,
fdt_bus_register_device,
fdt_bus_init,
fdt_bus_uninit,
fdt_bus_register_child_devices,
NULL, // rescan devices
NULL, // device removed
},
fdt_bus_node_by_phandle,
};
fdt_device_module_info gDeviceModule = {
{
{
"bus_managers/fdt/driver_v1",
0,
fdt_device_std_ops
},
NULL, // supports device
NULL, // register device (our parent registered us)
fdt_device_init_driver,
fdt_device_uninit_driver,
fdt_device_register_child_devices,
NULL, // rescan devices
NULL, // device removed
},
fdt_device_get_bus,
fdt_device_get_name,
fdt_device_get_prop,
fdt_device_get_reg,
fdt_device_get_interrupt,
};
module_info* modules[] = {
(module_info*)&gBusModule,
(module_info*)&gDeviceModule,
NULL
};
module_dependency module_dependencies[] = {
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
{ NULL }
};
@@ -1,194 +0,0 @@
/*
* Copyright 2012, François Revol, [email protected].
* Distributed under the terms of the MIT License.
*
* Authors:
* François Revol, [email protected]
* Alexander von Gluck IV, [email protected]
*/
#include "fdt_serial.h"
#include <KernelExport.h>
#include <ByteOrder.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/cdefs.h>
#include <arch/generic/debug_uart_8250.h>
#if defined(__arm__)
#include <arch/arm/arch_uart_pl011.h>
#endif
extern "C" {
#include <fdt.h>
#include <libfdt.h>
#include <libfdt_env.h>
};
#include "fdt_support.h"
//#define TRACE_SERIAL
#ifdef TRACE_SERIAL
# define TRACE(x...) dprintf("INIT: " x)
#else
# define TRACE(x...) ;
#endif
// If we dprintf before the UART is initalized there will be no output
static DebugUART*
debug_uart_from_node(const void *fdt, int node)
{
int len;
const void *prop;
phys_addr_t regs;
int32 clock = 0;
int32 speed = 0;
DebugUART *uart = NULL;
if (node < 0 || fdt == NULL)
return NULL;
// determine the MMIO address
regs = fdt_get_device_reg(fdt, node, false);
if (regs == 0) {
TRACE("%s: FDT UART regs not found!\n", __func__);
return NULL;
}
TRACE("serial: checking '%s', node %d @ %" B_PRIxPHYSADDR "\n",
name, node, regs);
// get the UART clock rate
prop = fdt_getprop(fdt, node, "clock-frequency", &len);
if (prop && len == 4) {
clock = fdt32_to_cpu(*(uint32_t *)prop);
TRACE("serial: clock %ld\n", clock);
}
// get current speed (XXX: not yet passed over)
prop = fdt_getprop(fdt, node, "current-speed", &len);
if (prop && len == 4) {
speed = fdt32_to_cpu(*(uint32_t *)prop);
TRACE("serial: speed %ld\n", speed);
}
// 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, "snps,dw-apb-uart") == 0) {
TRACE("serial: Found 8250 serial UART!\n");
uart = arch_get_uart_8250(regs, clock);
#if defined(__arm__)
} else if (fdt_node_check_compatible(fdt, node, "ti,omap3-uart") == 0
|| fdt_node_check_compatible(fdt, node, "ti,omap4-uart") == 0
|| fdt_node_check_compatible(fdt, node, "ti,omap5-uart") == 0
|| fdt_node_check_compatible(fdt, node, "ti,am3352-uart") == 0
|| fdt_node_check_compatible(fdt, node, "ti,am4372-uart") == 0
|| fdt_node_check_compatible(fdt, node, "ti,dra742-uart") == 0) {
// TODO: ti,am* and ti,dr* have some special quirks.
TRACE("serial: Found omap 8250 serial UART!\n");
uart = arch_get_uart_8250_omap(regs, clock);
} else if (fdt_node_check_compatible(fdt, node, "arm,pl011") == 0
|| fdt_node_check_compatible(fdt, node, "arm,primecell") == 0) {
TRACE("serial: Found pl011 serial UART!\n");
uart = arch_get_uart_pl011(regs, clock);
#endif
}
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;
}
@@ -1,19 +0,0 @@
/*
* Copyright 2012-2015, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors
* Alexander von Gluck IV, [email protected]
*/
#ifndef __FDT_SERIAL_H
#define __FDT_SERIAL_H
#include <KernelExport.h>
#include <arch/generic/debug_uart.h>
DebugUART * debug_uart_from_fdt(const void *fdt);
#endif /*__FDT_SERIAL_H*/
@@ -1,310 +0,0 @@
/*
* Copyright 2012-2015 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* François Revol, [email protected]
* Alexander von Gluck IV, [email protected]
*/
#include "fdt_support.h"
#include <KernelExport.h>
#include <ByteOrder.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/cdefs.h>
extern "C" {
#include <fdt.h>
#include <libfdt.h>
#include <libfdt_env.h>
};
#define TRACE_FDT
#ifdef TRACE_FDT
# define TRACE(x...) dprintf(x)
#else
# define TRACE(x...) ;
#endif
//#define FDT_DUMP_NODES
//#define FDT_DUMP_PROPS
//#define FDT_DUMP_PROP_VALUES
#ifdef FDT_DUMP_NODES
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
#endif
#ifdef FDT_DUMP_PROP_VALUES
static void dump_hex(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);
}
}
#endif
void dump_fdt(const void *fdt)
{
int err;
dprintf("FDT @ %p:\n", fdt);
if (!fdt)
return;
err = fdt_check_header(fdt);
if (err) {
dprintf("fdt error: %s\n", fdt_strerror(err));
return;
}
dprintf("fdt_totalsize: %d\n", fdt_totalsize(fdt));
dprintf("fdt_off_dt_struct: %d\n", fdt_off_dt_struct(fdt));
dprintf("fdt_off_dt_strings: %d\n", fdt_off_dt_strings(fdt));
dprintf("fdt_off_mem_rsvmap: %d\n", fdt_off_mem_rsvmap(fdt));
dprintf("fdt_version: %d\n", fdt_version(fdt));
dprintf("fdt_last_comp_version: %d\n", fdt_last_comp_version(fdt));
dprintf("fdt_boot_cpuid_phys: %d\n", fdt_boot_cpuid_phys(fdt));
dprintf("fdt_size_dt_strings: %d\n", fdt_size_dt_strings(fdt));
dprintf("fdt_size_dt_struct: %d\n", fdt_size_dt_struct(fdt));
#ifdef FDT_DUMP_NODES
dprintf("fdt tree:\n");
int node = -1;
int depth = 0;
while ((node = fdt_next_node(fdt, node, &depth)) >= 0) {
dprintf(DS"node at %d: '%s'\n", DA, node,
fdt_get_name(fdt, node, NULL));
#ifdef FDT_DUMP_PROPS
int prop, len;
const struct fdt_property *property;
prop = fdt_first_property_offset(fdt, node);
while (prop >= 0) {
property = fdt_get_property_by_offset(fdt, 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(fdt, fdt32_to_cpu(property->nameoff)),
fdt32_to_cpu(property->len));
#ifdef FDT_DUMP_PROP_VALUES
dump_hex(property->data, fdt32_to_cpu(property->len), depth);
#endif
prop = fdt_next_property_offset(fdt, prop);
}
#endif
}
#endif
}
static uint64
fdt_get_range_offset(const void* fdt, int32 node)
{
// Obtain the offset of the device by searching
// for the first ranges start in parents.
// It could be possible that there are multiple
// offset ranges in several parents + children.
// Lets hope that no system designer is that insane.
int depth = fdt_node_depth(fdt, node);
int32 examineNode = node;
uint64 pathOffset = 0x0;
while (depth > 0) {
int len;
const void* prop;
prop = fdt_getprop(fdt, examineNode, "ranges", &len);
if (prop) {
int32 regAddressCells = 1;
int32 regSizeCells = 1;
fdt_get_cell_count(fdt, examineNode, regAddressCells, regSizeCells);
const uint32 *p = (const uint32 *)prop;
// All we are interested in is the start offset
if (regAddressCells == 2)
pathOffset = fdt64_to_cpu(*(uint64_t *)p);
else
pathOffset = fdt32_to_cpu(*(uint32_t *)p);
break;
}
int32 parentNode = fdt_parent_offset(fdt, examineNode);
depth = fdt_node_depth(fdt, parentNode);
examineNode = parentNode;
}
TRACE("%s: range offset: 0x%" B_PRIx64 "\n", __func__, pathOffset);
return pathOffset;
}
status_t
fdt_get_cell_count(const void* fdt, int node,
int32 &addressCells, int32 &sizeCells)
{
// It would be nice if libfdt provided this.
// Memory base addresses are provided in 32 or 64 bit flavors
// #address-cells and #size-cells matches the number of 32-bit 'cells'
// representing the length of the base address and size fields
// TODO: assert !fdt || !pathOffset?
int len;
if (node < 0) {
TRACE("%s: Invalid FDT node id provided!\n", __func__);
return B_ERROR;
}
const void *prop;
prop = fdt_getprop(fdt, node, "#address-cells", &len);
if (prop && len == sizeof(uint32))
addressCells = fdt32_to_cpu(*(uint32_t *)prop);
prop = fdt_getprop(fdt, node, "#size-cells", &len);
if (prop && len == sizeof(uint32))
sizeCells = fdt32_to_cpu(*(uint32_t *)prop);
// NOTE : Cells over 2 is possible in theory...
if (addressCells > 2 || sizeCells > 2) {
panic("%s: Unsupported FDT cell count detected.\n"
"Address Cells: %" B_PRId32 "; Size Cells: %" B_PRId32
" (CPU > 64bit?).\n", __func__, addressCells, sizeCells);
return B_ERROR;
}
return B_OK;
}
phys_addr_t
fdt_get_device_reg(const void* fdt, int node, bool physical)
{
const void *prop = NULL;
int len;
uint64 baseDevice = 0x0;
int32 regAddressCells = 1;
int32 regSizeCells = 1;
fdt_get_cell_count(fdt, node, regAddressCells, regSizeCells);
// TODO: check for virtual-reg, and don't -= fdt_get_range_offset?
// XXX: not sure #address-cells & #size-cells actually apply to virtual-reg
if (!physical) {
prop = fdt_getprop(fdt, node, "virtual-reg", &len);
if (prop != NULL) {
baseDevice = fdt32_to_cpu(*(uint32_t *)prop);
return baseDevice;
}
}
prop = fdt_getprop(fdt, node, "reg", &len);
if (!prop) {
dprintf("%s: reg property not found on node in FDT!\n", __func__);
return 0;
}
const uint32 *p = (const uint32 *)prop;
// soc base address cells
if (regAddressCells == 2)
baseDevice = fdt64_to_cpu(*(uint64_t *)p);
else
baseDevice = fdt32_to_cpu(*(uint32_t *)p);
//p += regAddressCells;
// subtract the range offset (X) on the parent node (ranges = X Y Z)
baseDevice -= fdt_get_range_offset(fdt, node);
// find the start of the parent (X) and add to base (regs = X Y)
int parentNode = fdt_parent_offset(fdt, node);
if (!parentNode)
return baseDevice;
fdt_get_cell_count(fdt, parentNode, regAddressCells, regSizeCells);
prop = fdt_getprop(fdt, parentNode, "reg", &len);
if (!prop)
return baseDevice;
p = (const uint32 *)prop;
uint64 parentReg = 0x0;
// soc base address cells
if (regAddressCells == 2)
parentReg = fdt64_to_cpu(*(uint64_t *)p);
else
parentReg = fdt32_to_cpu(*(uint32_t *)p);
// add parent reg base to property
baseDevice += parentReg;
return baseDevice;
}
phys_addr_t
fdt_get_device_reg_byname(const void* fdt, const char* name)
{
// Find device in FDT
int node = fdt_path_offset(fdt, name);
if (node < 0) {
dprintf("%s: %s not found in FDT!\n", __func__, name);
return 0;
}
addr_t deviceReg = fdt_get_device_reg(fdt, node);
if (deviceReg > 0) {
//TRACE("%s: %s found @ 0x%" B_PRIx64 " , size: 0x%" B_PRIx64 "\n",
// __func__, name, deviceReg, size);
TRACE("%s: %s found @ 0x%" B_PRIxADDR "\n", __func__, name, deviceReg);
} else {
dprintf("%s: No valid reg entry on FDT device %s!\n",
__func__, name);
return 0;
}
return deviceReg;
}
phys_addr_t
fdt_get_device_reg_byalias(const void* fdt, const char* alias)
{
const char* name = fdt_get_alias(fdt, alias);
if (name == NULL) {
dprintf("%s: No alias found for %s!\n", __func__, alias);
return 0;
}
phys_addr_t deviceReg = fdt_get_device_reg_byname(fdt, name);
return deviceReg;
}
@@ -1,24 +0,0 @@
/*
* Copyright 2012-2015, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors
* Alexander von Gluck IV, [email protected]
*/
#ifndef __FDT_SUPPORT_H
#define __FDT_SUPPORT_H
#include <KernelExport.h>
void dump_fdt(const void *fdt);
status_t fdt_get_cell_count(const void* fdt, int node,
int32 &addressCells, int32 &sizeCells);
phys_addr_t fdt_get_device_reg(const void* fdt, int node, bool physical=true);
phys_addr_t fdt_get_device_reg_byname(const void* fdt, const char* name);
phys_addr_t fdt_get_device_reg_byalias(const void* fdt, const char* alias);
#endif /*__FDT_SUPPORT_H*/