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:
David Karoly 2022-01-06 11:21:52 +01:00 committed by Alex von Gluck IV
parent 9a573968a2
commit 8fea4985b5

View File

@ -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(