* Copyright 2022, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include "ECAMPCIController.h"
#include <AutoDeleterDrivers.h>
#include <string.h>
status_t
ECAMPCIControllerFDT::ReadResourceInfo()
{
DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(fNode));
fdt_device_module_info *fdtModule;
fdt_device* fdtDev;
CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(),
(driver_module_info**)&fdtModule, (void**)&fdtDev));
const void* prop;
int propLen;
prop = fdtModule->get_prop(fdtDev, "bus-range", &propLen);
if (prop != NULL && propLen == 8) {
uint32 busBeg = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 0));
uint32 busEnd = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 1));
dprintf(" bus-range: %" B_PRIu32 " - %" B_PRIu32 "\n", busBeg, busEnd);
}
prop = fdtModule->get_prop(fdtDev, "ranges", &propLen);
if (prop == NULL) {
dprintf(" \"ranges\" property not found");
return B_ERROR;
}
dprintf(" ranges:\n");
for (uint32_t *it = (uint32_t*)prop; (uint8_t*)it - (uint8_t*)prop < propLen; it += 7) {
dprintf(" ");
uint32_t type = B_BENDIAN_TO_HOST_INT32(*(it + 0));
uint64_t childAdr = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 1));
uint64_t parentAdr = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 3));
uint64_t len = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 5));
pci_resource_range range = {};
range.host_address = parentAdr;
range.pci_address = childAdr;
range.size = len;
if ((type & fdtPciRangePrefechable) != 0)
range.address_type |= PCI_address_prefetchable;
switch (type & fdtPciRangeTypeMask) {
case fdtPciRangeIoPort:
range.type = B_IO_PORT;
fResourceRanges.Add(range);
break;
case fdtPciRangeMmio32Bit:
range.type = B_IO_MEMORY;
range.address_type |= PCI_address_type_32;
fResourceRanges.Add(range);
break;
case fdtPciRangeMmio64Bit:
range.type = B_IO_MEMORY;
range.address_type |= PCI_address_type_64;
fResourceRanges.Add(range);
break;
}
switch (type & fdtPciRangeTypeMask) {
case fdtPciRangeConfig: dprintf("CONFIG"); break;
case fdtPciRangeIoPort: dprintf("IOPORT"); break;
case fdtPciRangeMmio32Bit: dprintf("MMIO32"); break;
case fdtPciRangeMmio64Bit: dprintf("MMIO64"); break;
}
dprintf(" (0x%08" B_PRIx32 "): ", type);
dprintf("child: %08" B_PRIx64, childAdr);
dprintf(", parent: %08" B_PRIx64, parentAdr);
dprintf(", len: %" B_PRIx64 "\n", len);
}
uint64 regs = 0;
if (!fdtModule->get_reg(fdtDev, 0, ®s, &fRegsLen))
return B_ERROR;
fRegsArea.SetTo(map_physical_memory("PCI Config MMIO", regs, fRegsLen, B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fRegs));
CHECK_RET(fRegsArea.Get());
return B_OK;
}
status_t
ECAMPCIControllerFDT::Finalize()
{
dprintf("finalize PCI controller from FDT\n");
DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(fNode));
fdt_device_module_info* parentModule;
fdt_device* parentDev;
CHECK_RET(gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&parentModule,
(void**)&parentDev));
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 = gPCI->read_pci_config(bus, device, 0, PCI_vendor_id, 2);
if ((vendorID != 0xffffffff) && (vendorID != 0xffff)) {
uint32 headerType = gPCI->read_pci_config(bus, device, 0, PCI_header_type, 1);
if ((headerType & 0x80) != 0) {
for (int function = 0; function < 8; function++) {
FinalizeInterrupts(parentModule, interruptMap, bus, device, function);
}
} else {
FinalizeInterrupts(parentModule, interruptMap, bus, device, 0);
}
}
}
}
return B_OK;
}
void
ECAMPCIControllerFDT::FinalizeInterrupts(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 = gPCI->read_pci_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);
gPCI->update_interrupt_line(bus, device, function, irq);
}
}