bus_managers/fdt: implement interrupt-cells handling
Change-Id: Id0ff78ce51fb64065c2a6398115e3a3dd12693da Reviewed-on: https://review.haiku-os.org/c/haiku/+/4658 Reviewed-by: Adrien Destugues <pulkomandy@gmail.com> Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
parent
9a573968a2
commit
8fea4985b5
@ -366,6 +366,42 @@ fdt_device_get_reg(fdt_device* dev, uint32 ord, uint64* regs, uint64* len)
|
||||
}
|
||||
|
||||
|
||||
static uint32
|
||||
fdt_get_interrupt_parent(fdt_device* dev, int node)
|
||||
{
|
||||
while (node >= 0) {
|
||||
uint32* prop;
|
||||
int propLen;
|
||||
prop = (uint32*)fdt_getprop(gFDT, node, "interrupt-parent", &propLen);
|
||||
if (prop != NULL && propLen == 4) {
|
||||
return fdt32_to_cpu(*prop);
|
||||
}
|
||||
|
||||
node = fdt_parent_offset(gFDT, node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static uint32
|
||||
fdt_get_interrupt_cells(uint32 interrupt_parent_phandle)
|
||||
{
|
||||
if (interrupt_parent_phandle > 0) {
|
||||
int node = fdt_node_offset_by_phandle(gFDT, interrupt_parent_phandle);
|
||||
if (node >= 0) {
|
||||
uint32* prop;
|
||||
int propLen;
|
||||
prop = (uint32*)fdt_getprop(gFDT, node, "#interrupt-cells", &propLen);
|
||||
if (prop != NULL && propLen == 4) {
|
||||
return fdt32_to_cpu(*prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
|
||||
device_node** interruptController, uint64* interrupt)
|
||||
@ -376,45 +412,45 @@ fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
|
||||
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) {
|
||||
uint32 interruptParent = fdt_get_interrupt_parent(dev, fdtNode);
|
||||
uint32 interruptCells = fdt_get_interrupt_cells(interruptParent);
|
||||
|
||||
prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts",
|
||||
&propLen);
|
||||
if (prop == NULL)
|
||||
return false;
|
||||
|
||||
if ((ord + 1)*4 > (uint32)propLen)
|
||||
if ((ord + 1) * interruptCells * sizeof(uint32) > (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);
|
||||
}
|
||||
uint32 offs;
|
||||
if (interruptCells == 3) {
|
||||
offs = 3 * ord + 1;
|
||||
} else {
|
||||
offs = interruptCells * ord;
|
||||
}
|
||||
|
||||
if (interrupt != NULL)
|
||||
*interrupt = fdt32_to_cpu(*(((uint32*)prop) + offs));
|
||||
|
||||
if (interruptController != NULL && interruptParent != 0) {
|
||||
fdt_bus* bus;
|
||||
ASSERT(gDeviceManager->get_driver(dev->bus, NULL, (void**)&bus) >= B_OK);
|
||||
*interruptController = fdt_bus_node_by_phandle(bus, interruptParent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: use '#interrupt-cells' to identify field sizes
|
||||
|
||||
if ((ord + 1)*8 > (uint32)propLen)
|
||||
if ((ord + 1) * 8 > (uint32)propLen)
|
||||
return false;
|
||||
|
||||
if (interruptController != NULL) {
|
||||
uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2*ord));
|
||||
uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2 * ord));
|
||||
|
||||
fdt_bus* bus;
|
||||
ASSERT(gDeviceManager->get_driver(
|
||||
|
Loading…
x
Reference in New Issue
Block a user