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:
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user