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 <[email protected]>
Reviewed-by: David Karoly <[email protected]>
This commit is contained in:
David Karoly
2022-08-24 09:41:35 +00:00
parent 7b233926e2
commit 226dd60a57
4 changed files with 248 additions and 17 deletions
+4
View File
@@ -11,6 +11,7 @@
struct fdt_bus; struct fdt_bus;
struct fdt_device; struct fdt_device;
struct fdt_interrupt_map;
typedef struct fdt_bus_module_info { typedef struct fdt_bus_module_info {
driver_module_info 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_reg)(struct fdt_device* dev, uint32 ord, uint64* regs, uint64* len);
bool (*get_interrupt)(struct fdt_device* dev, uint32 ord, bool (*get_interrupt)(struct fdt_device* dev, uint32 ord,
device_node** interruptController, uint64* interrupt); 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; } fdt_device_module_info;
@@ -10,6 +10,7 @@
#include <drivers/bus/FDT.h> #include <drivers/bus/FDT.h>
#include <KernelExport.h> #include <KernelExport.h>
#include <util/kernel_cpp.h> #include <util/kernel_cpp.h>
#include <util/Vector.h>
#include <device_manager.h> #include <device_manager.h>
#include <AutoDeleter.h> #include <AutoDeleter.h>
@@ -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<fdt_interrupt_map_entry> fInterruptMap;
};
static status_t static status_t
fdt_register_node(fdt_bus* bus, int node, device_node* parentDev, fdt_register_node(fdt_bus* bus, int node, device_node* parentDev,
device_node*& curDev) 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<struct fdt_interrupt_map> 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<struct fdt_interrupt_map_entry>::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<struct fdt_interrupt_map_entry>::Iterator it = interruptMap->fInterruptMap.Begin();
it != interruptMap->fInterruptMap.End(); it++) {
if ((it->childAddr == childAddr) && (it->childIrq == childIrq))
return it->parentIrq;
}
return 0xffffffff;
}
//#pragma mark - //#pragma mark -
fdt_bus_module_info gBusModule = { fdt_bus_module_info gBusModule = {
@@ -527,6 +676,9 @@ fdt_device_module_info gDeviceModule = {
fdt_device_get_prop, fdt_device_get_prop,
fdt_device_get_reg, fdt_device_get_reg,
fdt_device_get_interrupt, fdt_device_get_interrupt,
fdt_device_get_interrupt_map,
fdt_device_print_interrupt_map,
fdt_device_lookup_interrupt_map,
}; };
@@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers pci arch $(TARGET_ARCH) ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ; SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ;
UsePrivateKernelHeaders ; UsePrivateKernelHeaders ;
UsePrivateHeaders [ FDirName kernel util ] ;
UsePrivateHeaders kernel [ FDirName kernel arch $(TARGET_ARCH) ] UsePrivateHeaders kernel [ FDirName kernel arch $(TARGET_ARCH) ]
[ FDirName kernel boot platform $(HAIKU_BOOT_PLATFORM) ] ; [ FDirName kernel boot platform $(HAIKU_BOOT_PLATFORM) ] ;
@@ -13,6 +13,7 @@
#include <AutoDeleterDrivers.h> #include <AutoDeleterDrivers.h>
#include "pci_private.h" #include "pci_private.h"
#include "pci.h"
#include <ACPI.h> // module #include <ACPI.h> // module
#include <acpi.h> #include <acpi.h>
@@ -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 status_t
pci_controller_finalize(void) pci_controller_finalize(void)
{ {
status_t res; DeviceNodePutter<&gDeviceManager>
parent(gDeviceManager->get_parent_node(gPCIRootNode));
acpi_module_info *acpiModule; if (parent.Get() == NULL)
res = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
if (res != B_OK)
return B_ERROR; return B_ERROR;
IRQRoutingTable table; const char* bus;
res = prepare_irq_routing(acpiModule, table, &is_interrupt_available); if (gDeviceManager->get_attr_string(parent.Get(), B_DEVICE_BUS, &bus, false) < B_OK)
if (res != B_OK) {
dprintf("PCI: irq routing preparation failed\n");
return B_ERROR; 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<irq_routing_entry>::Iterator it = table.Begin(); it != table.End(); it++) if (strcmp(bus, "acpi") == 0) {
reserve_io_interrupt_vectors(1, it->irq, INTERRUPT_TYPE_IRQ); dprintf("finalize PCI controller from ACPI\n");
res = enable_irq_routing(acpiModule, table); status_t res;
if (res != B_OK) {
dprintf("PCI: irq routing failed\n"); acpi_module_info *acpiModule;
return B_ERROR; 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_ERROR;
return B_OK;
} }