bus_managers/fdt: implement interrupt-cells handling
Change-Id: Id0ff78ce51fb64065c2a6398115e3a3dd12693da Reviewed-on: https://review.haiku-os.org/c/haiku/+/4658 Reviewed-by: Adrien Destugues <[email protected]> Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
committed by
Alex von Gluck IV
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
|
static bool
|
||||||
fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
|
fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
|
||||||
device_node** interruptController, uint64* interrupt)
|
device_node** interruptController, uint64* interrupt)
|
||||||
@@ -376,45 +412,45 @@ fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
|
|||||||
ASSERT(gDeviceManager->get_attr_uint32(
|
ASSERT(gDeviceManager->get_attr_uint32(
|
||||||
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
|
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
|
||||||
|
|
||||||
// TODO: handle other interrupt encodings
|
|
||||||
int propLen;
|
int propLen;
|
||||||
const void* prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
|
const void* prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
|
||||||
&propLen);
|
&propLen);
|
||||||
if (prop == NULL) {
|
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",
|
prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts",
|
||||||
&propLen);
|
&propLen);
|
||||||
if (prop == NULL)
|
if (prop == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((ord + 1)*4 > (uint32)propLen)
|
if ((ord + 1) * interruptCells * sizeof(uint32) > (uint32)propLen)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (interrupt != NULL)
|
uint32 offs;
|
||||||
*interrupt = fdt32_to_cpu(*(((uint32*)prop) + ord));
|
if (interruptCells == 3) {
|
||||||
|
offs = 3 * ord + 1;
|
||||||
if (interruptController != NULL) {
|
} else {
|
||||||
prop = fdt_getprop(gFDT, (int)fdtNode, "interrupt-parent",
|
offs = interruptCells * ord;
|
||||||
&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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use '#interrupt-cells' to identify field sizes
|
if ((ord + 1) * 8 > (uint32)propLen)
|
||||||
|
|
||||||
if ((ord + 1)*8 > (uint32)propLen)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (interruptController != NULL) {
|
if (interruptController != NULL) {
|
||||||
uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2*ord));
|
uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2 * ord));
|
||||||
|
|
||||||
fdt_bus* bus;
|
fdt_bus* bus;
|
||||||
ASSERT(gDeviceManager->get_driver(
|
ASSERT(gDeviceManager->get_driver(
|
||||||
|
|||||||
Reference in New Issue
Block a user