From 226dd60a574abefa437bfc54dbe11988d9b46812 Mon Sep 17 00:00:00 2001 From: David Karoly Date: Wed, 17 Aug 2022 21:42:27 +0200 Subject: [PATCH] arm64: initialize PCI interrupts from FDT see: https://www.devicetree.org/open-firmware/practice/imap/imap0_9d.pdf Change-Id: I4158b022fd4404e3126f92ee844743e39a9b6646 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5560 Tested-by: Commit checker robot Reviewed-by: David Karoly --- headers/os/drivers/bus/FDT.h | 4 + .../kernel/bus_managers/fdt/fdt_module.cpp | 152 ++++++++++++++++++ .../bus_managers/pci/arch/arm64/Jamfile | 1 + .../pci/arch/generic/pci_controller.cpp | 108 +++++++++++-- 4 files changed, 248 insertions(+), 17 deletions(-) diff --git a/headers/os/drivers/bus/FDT.h b/headers/os/drivers/bus/FDT.h index ddd0512ea2..b7501a0504 100644 --- a/headers/os/drivers/bus/FDT.h +++ b/headers/os/drivers/bus/FDT.h @@ -11,6 +11,7 @@ struct fdt_bus; struct fdt_device; +struct fdt_interrupt_map; typedef struct fdt_bus_module_info { driver_module_info info; @@ -25,6 +26,9 @@ typedef struct fdt_device_module_info { bool (*get_reg)(struct fdt_device* dev, uint32 ord, uint64* regs, uint64* len); bool (*get_interrupt)(struct fdt_device* dev, uint32 ord, device_node** interruptController, uint64* interrupt); + struct fdt_interrupt_map* (*get_interrupt_map)(struct fdt_device* dev); + void (*print_interrupt_map)(struct fdt_interrupt_map* interruptMap); + uint32 (*lookup_interrupt_map)(struct fdt_interrupt_map* interruptMap, uint32 childAddr, uint32 childIrq); } fdt_device_module_info; diff --git a/src/add-ons/kernel/bus_managers/fdt/fdt_module.cpp b/src/add-ons/kernel/bus_managers/fdt/fdt_module.cpp index 7ef6ac8e6d..0cf7fa39c1 100644 --- a/src/add-ons/kernel/bus_managers/fdt/fdt_module.cpp +++ b/src/add-ons/kernel/bus_managers/fdt/fdt_module.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -64,6 +65,22 @@ struct fdt_device { }; +struct fdt_interrupt_map_entry { + uint32_t childAddr; + uint32_t childIrq; + uint32_t parentIrqCtrl; + uint32_t parentIrq; +}; + + +struct fdt_interrupt_map { + uint32_t childAddrMask; + uint32_t childIrqMask; + + Vector fInterruptMap; +}; + + static status_t fdt_register_node(fdt_bus* bus, int node, device_node* parentDev, device_node*& curDev) @@ -485,6 +502,138 @@ fdt_device_get_interrupt(fdt_device* dev, uint32 index, } +static struct fdt_interrupt_map * +fdt_device_get_interrupt_map(struct fdt_device* dev) +{ + int fdtNode; + ASSERT(gDeviceManager->get_attr_uint32( + dev->node, "fdt/node", (uint32*)&fdtNode, false) >= B_OK); + + ObjectDeleter interrupt_map(new struct fdt_interrupt_map()); + + int intMapMaskLen; + const void* intMapMask = fdt_getprop(gFDT, fdtNode, "interrupt-map-mask", + &intMapMaskLen); + + if (intMapMask == NULL || intMapMaskLen != 4 * 4) { + dprintf(" interrupt-map-mask property not found or invalid\n"); + return NULL; + } + + interrupt_map->childAddrMask = B_BENDIAN_TO_HOST_INT32(*((uint32*)intMapMask + 0)); + interrupt_map->childIrqMask = B_BENDIAN_TO_HOST_INT32(*((uint32*)intMapMask + 3)); + + int intMapLen; + const void* intMapAddr = fdt_getprop(gFDT, fdtNode, "interrupt-map", &intMapLen); + if (intMapAddr == NULL) { + dprintf(" interrupt-map property not found\n"); + return NULL; + } + + int addressCells = 3; + int interruptCells = 1; + int phandleCells = 1; + + const void *property; + + property = fdt_getprop(gFDT, fdtNode, "#address-cells", NULL); + if (property != NULL) + addressCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); + + property = fdt_getprop(gFDT, fdtNode, "#interrupt-cells", NULL); + if (property != NULL) + interruptCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); + + uint32_t *it = (uint32_t*)intMapAddr; + while ((uint8_t*)it - (uint8_t*)intMapAddr < intMapLen) { + struct fdt_interrupt_map_entry irqEntry; + + irqEntry.childAddr = B_BENDIAN_TO_HOST_INT32(*it); + it += addressCells; + + irqEntry.childIrq = B_BENDIAN_TO_HOST_INT32(*it); + it += interruptCells; + + irqEntry.parentIrqCtrl = B_BENDIAN_TO_HOST_INT32(*it); + it += phandleCells; + + int parentAddressCells = 0; + int parentInterruptCells = 1; + + int interruptParent = fdt_node_offset_by_phandle(gFDT, irqEntry.parentIrqCtrl); + if (interruptParent >= 0) { + property = fdt_getprop(gFDT, interruptParent, "#address-cells", NULL); + if (property != NULL) + parentAddressCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); + + property = fdt_getprop(gFDT, interruptParent, "#interrupt-cells", NULL); + if (property != NULL) + parentInterruptCells = B_BENDIAN_TO_HOST_INT32(*(uint32*)property); + } + + it += parentAddressCells; + + if ((parentInterruptCells == 1) || (parentInterruptCells == 2)) { + irqEntry.parentIrq = B_BENDIAN_TO_HOST_INT32(*it); + } else if (parentInterruptCells == 3) { + uint32 interruptType = fdt32_to_cpu(it[GIC_INTERRUPT_CELL_TYPE]); + uint32 interruptNumber = fdt32_to_cpu(it[GIC_INTERRUPT_CELL_ID]); + + if (interruptType == GIC_INTERRUPT_TYPE_SPI) + irqEntry.parentIrq = interruptNumber + GIC_INTERRUPT_BASE_SPI; + else if (interruptType == GIC_INTERRUPT_TYPE_PPI) + irqEntry.parentIrq = interruptNumber + GIC_INTERRUPT_BASE_PPI; + else + irqEntry.parentIrq = interruptNumber; + } + it += parentInterruptCells; + + interrupt_map->fInterruptMap.PushBack(irqEntry); + } + + return interrupt_map.Detach(); +} + + +static void +fdt_device_print_interrupt_map(struct fdt_interrupt_map* interruptMap) +{ + if (interruptMap == NULL) + return; + + dprintf("interrupt_map_mask: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", + interruptMap->childAddrMask, interruptMap->childIrqMask); + dprintf("interrupt_map:\n"); + + for (Vector::Iterator it = interruptMap->fInterruptMap.Begin(); + it != interruptMap->fInterruptMap.End(); + it++) { + + dprintf("childAddr=0x%08" PRIx32 ", childIrq=%" PRIu32 ", parentIrqCtrl=%" PRIu32 ", parentIrq=%" PRIu32 "\n", + it->childAddr, it->childIrq, it->parentIrqCtrl, it->parentIrq); + } +} + + +static uint32 +fdt_device_lookup_interrupt_map(struct fdt_interrupt_map* interruptMap, uint32 childAddr, uint32 childIrq) +{ + if (interruptMap == NULL) + return 0xffffffff; + + childAddr &= interruptMap->childAddrMask; + childIrq &= interruptMap->childIrqMask; + + for (Vector::Iterator it = interruptMap->fInterruptMap.Begin(); + it != interruptMap->fInterruptMap.End(); it++) { + if ((it->childAddr == childAddr) && (it->childIrq == childIrq)) + return it->parentIrq; + } + + return 0xffffffff; +} + + //#pragma mark - fdt_bus_module_info gBusModule = { @@ -527,6 +676,9 @@ fdt_device_module_info gDeviceModule = { fdt_device_get_prop, fdt_device_get_reg, fdt_device_get_interrupt, + fdt_device_get_interrupt_map, + fdt_device_print_interrupt_map, + fdt_device_lookup_interrupt_map, }; diff --git a/src/add-ons/kernel/bus_managers/pci/arch/arm64/Jamfile b/src/add-ons/kernel/bus_managers/pci/arch/arm64/Jamfile index 517bfe54e9..5eb1163e5b 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/arm64/Jamfile +++ b/src/add-ons/kernel/bus_managers/pci/arch/arm64/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers pci arch $(TARGET_ARCH) ; SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ; UsePrivateKernelHeaders ; +UsePrivateHeaders [ FDirName kernel util ] ; UsePrivateHeaders kernel [ FDirName kernel arch $(TARGET_ARCH) ] [ FDirName kernel boot platform $(HAIKU_BOOT_PLATFORM) ] ; diff --git a/src/add-ons/kernel/bus_managers/pci/arch/generic/pci_controller.cpp b/src/add-ons/kernel/bus_managers/pci/arch/generic/pci_controller.cpp index 943a7ff8ed..f82fad0486 100644 --- a/src/add-ons/kernel/bus_managers/pci/arch/generic/pci_controller.cpp +++ b/src/add-ons/kernel/bus_managers/pci/arch/generic/pci_controller.cpp @@ -13,6 +13,7 @@ #include #include "pci_private.h" +#include "pci.h" #include // module #include @@ -275,35 +276,108 @@ pci_controller_init(void) } +static void +pci_controller_finalize_interrupts(fdt_device_module_info* fdtModule, struct fdt_interrupt_map* interruptMap, + int bus, int device, int function) +{ + uint32 childAddr = ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x07) << 8); + uint32 interruptPin = pci_read_config(bus, device, function, PCI_interrupt_pin, 1); + + if (interruptPin == 0xffffffff) { + dprintf("Error: Unable to read interrupt pin!\n"); + return; + } + + uint32 irq = fdtModule->lookup_interrupt_map(interruptMap, childAddr, interruptPin); + if (irq == 0xffffffff) { + dprintf("no interrupt mapping for childAddr: (%d:%d:%d), childIrq: %d)\n", + bus, device, function, interruptPin); + } else { + dprintf("configure interrupt (%d,%d,%d) --> %d\n", + bus, device, function, irq); + pci_update_interrupt_line(bus, device, function, irq); + } +} + + status_t pci_controller_finalize(void) { - status_t res; + DeviceNodePutter<&gDeviceManager> + parent(gDeviceManager->get_parent_node(gPCIRootNode)); - acpi_module_info *acpiModule; - res = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); - if (res != B_OK) + if (parent.Get() == NULL) return B_ERROR; - IRQRoutingTable table; - res = prepare_irq_routing(acpiModule, table, &is_interrupt_available); - if (res != B_OK) { - dprintf("PCI: irq routing preparation failed\n"); + const char* bus; + if (gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false) < B_OK) return B_ERROR; + + if (strcmp(bus, "fdt") == 0) { + dprintf("finalize PCI controller from FDT\n"); + + status_t res; + fdt_device_module_info* parentModule; + fdt_device* parentDev; + + res = gDeviceManager->get_driver(parent.Get(), + (driver_module_info**)&parentModule, (void**)&parentDev); + if (res != B_OK) { + dprintf("can't get parent node driver\n"); + return B_ERROR; + } + + struct fdt_interrupt_map* interruptMap = parentModule->get_interrupt_map(parentDev); + parentModule->print_interrupt_map(interruptMap); + + for (int bus = 0; bus < 8; bus++) { + for (int device = 0; device < 32; device++) { + uint32 vendorID = pci_read_config(bus, device, 0, PCI_vendor_id, 2); + if ((vendorID != 0xffffffff) && (vendorID != 0xffff)) { + uint32 headerType = pci_read_config(bus, device, 0, PCI_header_type, 1); + if ((headerType & 0x80) != 0) { + for (int function = 0; function < 8; function++) { + pci_controller_finalize_interrupts(parentModule, interruptMap, bus, device, function); + } + } else { + pci_controller_finalize_interrupts(parentModule, interruptMap, bus, device, 0); + } + } + } + } + + return B_OK; } - for (Vector::Iterator it = table.Begin(); it != table.End(); it++) - reserve_io_interrupt_vectors(1, it->irq, INTERRUPT_TYPE_IRQ); + if (strcmp(bus, "acpi") == 0) { + dprintf("finalize PCI controller from ACPI\n"); - res = enable_irq_routing(acpiModule, table); - if (res != B_OK) { - dprintf("PCI: irq routing failed\n"); - return B_ERROR; + status_t res; + + acpi_module_info *acpiModule; + res = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule); + if (res != B_OK) + return B_ERROR; + + IRQRoutingTable table; + res = prepare_irq_routing(acpiModule, table, &is_interrupt_available); + if (res != B_OK) { + dprintf("PCI: irq routing preparation failed\n"); + return B_ERROR; + } + + res = enable_irq_routing(acpiModule, table); + if (res != B_OK) { + dprintf("PCI: irq routing failed\n"); + return B_ERROR; + } + + print_irq_routing_table(table); + + return B_OK; } - print_irq_routing_table(table); - - return B_OK; + return B_ERROR; }