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:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -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*/
|
|
||||||
Reference in New Issue
Block a user