From f33c8f9ce2396b75ec0b0d3d3fc80589440b9d8d Mon Sep 17 00:00:00 2001 From: X512 Date: Fri, 16 Dec 2022 06:54:38 +0900 Subject: [PATCH] busses/pci/designware: add Change-Id: I43dfc8a4b4fabe7b892e98aa192db95ba1b9378f Reviewed-on: https://review.haiku-os.org/c/haiku/+/6225 Reviewed-by: waddlesplash --- build/jam/packages/Haiku | 5 +- src/add-ons/kernel/busses/pci/Jamfile | 1 + .../busses/pci/designware/DWPCIController.cpp | 500 ++++++++++++++++++ .../busses/pci/designware/DWPCIController.h | 249 +++++++++ .../kernel/busses/pci/designware/Jamfile | 11 + .../pci/designware/MsiInterruptCtrlDW.cpp | 109 ++++ .../pci/designware/kernel_interface.cpp | 66 +++ 7 files changed, 939 insertions(+), 2 deletions(-) create mode 100644 src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp create mode 100644 src/add-ons/kernel/busses/pci/designware/DWPCIController.h create mode 100644 src/add-ons/kernel/busses/pci/designware/Jamfile create mode 100644 src/add-ons/kernel/busses/pci/designware/MsiInterruptCtrlDW.cpp create mode 100644 src/add-ons/kernel/busses/pci/designware/kernel_interface.cpp diff --git a/build/jam/packages/Haiku b/build/jam/packages/Haiku index 275d69974e..aabd7218d7 100644 --- a/build/jam/packages/Haiku +++ b/build/jam/packages/Haiku @@ -30,7 +30,8 @@ AddFilesToPackage add-ons kernel busses ata AddFilesToPackage add-ons kernel busses i2c : pch_i2c@x86,x86_64 ; AddFilesToPackage add-ons kernel busses mmc : sdhci_pci ; -AddFilesToPackage add-ons kernel busses pci : ecam@riscv64,arm,arm64 x86@x86,x86_64 ; +AddFilesToPackage add-ons kernel busses pci : + designware@riscv64 ecam@riscv64,arm,arm64 x86@x86,x86_64 ; AddFilesToPackage add-ons kernel busses random : ccp_rng@x86,x86_64 virtio_rng ; AddFilesToPackage add-ons kernel busses scsi : ahci virtio_scsi ; AddFilesToPackage add-ons kernel busses usb : uhci ohci ehci @@ -198,7 +199,7 @@ AddBootModuleSymlinksToPackage nvme_disk openpic@ppc packagefs pci - ecam@riscv64,arm,arm64 x86@x86,x86_64 + designware@riscv64 ecam@riscv64,arm,arm64 x86@x86,x86_64 fdt@riscv64,arm,arm64 scsi scsi_cd scsi_disk scsi_periph silicon_image_3112 highpoint_ide_pci sdhci_pci diff --git a/src/add-ons/kernel/busses/pci/Jamfile b/src/add-ons/kernel/busses/pci/Jamfile index 30eb04763c..9a500dab3b 100644 --- a/src/add-ons/kernel/busses/pci/Jamfile +++ b/src/add-ons/kernel/busses/pci/Jamfile @@ -1,4 +1,5 @@ SubDir HAIKU_TOP src add-ons kernel busses pci ; +SubInclude HAIKU_TOP src add-ons kernel busses pci designware ; SubInclude HAIKU_TOP src add-ons kernel busses pci ecam ; SubInclude HAIKU_TOP src add-ons kernel busses pci x86 ; diff --git a/src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp b/src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp new file mode 100644 index 0000000000..6df23447ed --- /dev/null +++ b/src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp @@ -0,0 +1,500 @@ +/* + * Copyright 2022, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ + + +#include "DWPCIController.h" +#include + +#include +#include + +#include +#include + + +static uint32 +ReadReg8(addr_t adr) +{ + uint32 ofs = adr % 4; + adr = adr / 4 * 4; + union { + uint32 in; + uint8 out[4]; + } val{.in = *(vuint32*)adr}; + return val.out[ofs]; +} + + +static uint32 +ReadReg16(addr_t adr) +{ + uint32 ofs = adr / 2 % 2; + adr = adr / 4 * 4; + union { + uint32 in; + uint16 out[2]; + } val{.in = *(vuint32*)adr}; + return val.out[ofs]; +} + + +static void +WriteReg8(addr_t adr, uint32 value) +{ + uint32 ofs = adr % 4; + adr = adr / 4 * 4; + union { + uint32 in; + uint8 out[4]; + } val{.in = *(vuint32*)adr}; + val.out[ofs] = (uint8)value; + *(vuint32*)adr = val.in; +} + + +static void +WriteReg16(addr_t adr, uint32 value) +{ + uint32 ofs = adr / 2 % 2; + adr = adr / 4 * 4; + union { + uint32 in; + uint16 out[2]; + } val{.in = *(vuint32*)adr}; + val.out[ofs] = (uint16)value; + *(vuint32*)adr = val.in; +} + + +//#pragma mark - driver + + +float +DWPCIController::SupportsDevice(device_node* parent) +{ + const char* bus; + status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false); + if (status < B_OK) + return -1.0f; + + if (strcmp(bus, "fdt") != 0) + return 0.0f; + + const char* compatible; + status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false); + if (status < B_OK) + return -1.0f; + + // Support only a variant used in HiFive Unmatched board. + // TODO: Support more Synapsis Designware IP core based PCIe host controllers. + if (strcmp(compatible, "sifive,fu740-pcie") != 0) + return 0.0f; + + return 1.0f; +} + + +status_t +DWPCIController::RegisterDevice(device_node* parent) +{ + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Designware PCI Host Controller"} }, + { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} }, + {} + }; + + return gDeviceManager->register_node(parent, DESIGNWARE_PCI_DRIVER_MODULE_NAME, attrs, NULL, + NULL); +} + + +status_t +DWPCIController::InitDriver(device_node* node, DWPCIController*& outDriver) +{ + ObjectDeleter driver(new(std::nothrow) DWPCIController()); + if (!driver.IsSet()) + return B_NO_MEMORY; + + CHECK_RET(driver->InitDriverInt(node)); + outDriver = driver.Detach(); + return B_OK; +} + + +status_t +DWPCIController::ReadResourceInfo() +{ + DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(fNode)); + + const char* bus; + CHECK_RET(gDeviceManager->get_attr_string(fdtNode.Get(), B_DEVICE_BUS, &bus, false)); + if (strcmp(bus, "fdt") != 0) + return B_ERROR; + + 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, "interrupt-map-mask", &propLen); + if (prop == NULL || propLen != 4 * 4) { + dprintf(" \"interrupt-map-mask\" property not found or invalid"); + return B_ERROR; + } + fInterruptMapMask.childAdr = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 0)); + fInterruptMapMask.childIrq = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 3)); + + prop = fdtModule->get_prop(fdtDev, "interrupt-map", &propLen); + fInterruptMapLen = (uint32)propLen / (6 * 4); + fInterruptMap.SetTo(new(std::nothrow) InterruptMap[fInterruptMapLen]); + if (!fInterruptMap.IsSet()) + return B_NO_MEMORY; + + for (uint32_t *it = (uint32_t*)prop; (uint8_t*)it - (uint8_t*)prop < propLen; it += 6) { + size_t i = (it - (uint32_t*)prop) / 6; + + fInterruptMap[i].childAdr = B_BENDIAN_TO_HOST_INT32(*(it + 0)); + fInterruptMap[i].childIrq = B_BENDIAN_TO_HOST_INT32(*(it + 3)); + fInterruptMap[i].parentIrqCtrl = B_BENDIAN_TO_HOST_INT32(*(it + 4)); + fInterruptMap[i].parentIrq = B_BENDIAN_TO_HOST_INT32(*(it + 5)); + } + + dprintf(" interrupt-map:\n"); + for (size_t i = 0; i < fInterruptMapLen; i++) { + dprintf(" "); + // child unit address + PciAddress pciAddress{.val = fInterruptMap[i].childAdr}; + dprintf("bus: %" B_PRIu32, pciAddress.bus); + dprintf(", dev: %" B_PRIu32, pciAddress.device); + dprintf(", fn: %" B_PRIu32, pciAddress.function); + + dprintf(", childIrq: %" B_PRIu32, fInterruptMap[i].childIrq); + dprintf(", parentIrq: (%" B_PRIu32, fInterruptMap[i].parentIrqCtrl); + dprintf(", %" B_PRIu32, fInterruptMap[i].parentIrq); + dprintf(")\n"); + if (i % 4 == 3 && (i + 1 < fInterruptMapLen)) + dprintf("\n"); + } + + 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)); + + uint32 outType = kPciRangeInvalid; + switch (type & fdtPciRangeTypeMask) { + case fdtPciRangeIoPort: + outType = kPciRangeIoPort; + break; + case fdtPciRangeMmio32Bit: + outType = kPciRangeMmio; + break; + case fdtPciRangeMmio64Bit: + outType = kPciRangeMmio + kPciRangeMmio64Bit; + break; + } + if (outType >= kPciRangeMmio && outType < kPciRangeMmioEnd + && (fdtPciRangePrefechable & type) != 0) + outType += kPciRangeMmioPrefetch; + + if (outType != kPciRangeInvalid) { + fResourceRanges[outType].type = outType; + fResourceRanges[outType].host_addr = parentAdr; + fResourceRanges[outType].pci_addr = childAdr; + fResourceRanges[outType].size = len; + } + + 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); + } + return B_OK; +} + + +status_t +DWPCIController::InitDriverInt(device_node* node) +{ + fNode = node; + dprintf("+DWPCIController::InitDriver()\n"); + + CHECK_RET(ReadResourceInfo()); + + DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(node)); + + fdt_device_module_info *fdtModule; + fdt_device* fdtDev; + CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(), + (driver_module_info**)&fdtModule, (void**)&fdtDev)); + + if (!fdtModule->get_reg(fdtDev, 0, &fDbiPhysBase, &fDbiSize)) + return B_ERROR; + dprintf(" DBI: %08" B_PRIx64 ", %08" B_PRIx64 "\n", fDbiPhysBase, fDbiSize); + + if (!fdtModule->get_reg(fdtDev, 1, &fConfigPhysBase, &fConfigSize)) + return B_ERROR; + dprintf(" config: %08" B_PRIx64 ", %08" B_PRIx64 "\n", fConfigPhysBase, fConfigSize); + + uint64 msiIrq; + if (!fdtModule->get_interrupt(fdtDev, 0, NULL, &msiIrq)) + return B_ERROR; + + fDbiArea.SetTo(map_physical_memory("PCI DBI MMIO", fDbiPhysBase, fDbiSize, B_ANY_KERNEL_ADDRESS, + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fDbiBase)); + CHECK_RET(fDbiArea.Get()); + + fConfigArea.SetTo(map_physical_memory("PCI Config MMIO", fConfigPhysBase, fConfigSize, + B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fConfigBase)); + CHECK_RET(fConfigArea.Get()); + + CHECK_RET(fIrqCtrl.Init(GetDbuRegs(), msiIrq)); + + AtuDump(); + + dprintf("-DWPCIController::InitDriver()\n"); + return B_OK; +} + + +void +DWPCIController::UninitDriver() +{ + delete this; +} + + +addr_t +DWPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset) +{ + uint32 atuType; + if (bus == 0) { + if (device != 0 || function != 0) + return 0; + return fDbiBase + offset; + } else if (bus == 1) + atuType = kPciAtuTypeCfg0; + else + atuType = kPciAtuTypeCfg1; + + uint64 address = (uint64)(PciAddress { + .function = function, + .device = device, + .bus = bus + }.val) << 8; + + status_t res = AtuMap(1, kPciAtuOutbound, atuType, fConfigPhysBase, address, fConfigSize); + if (res < B_OK) + return 0; + + return fConfigBase + offset; +} + + +//#pragma mark - PCI controller + + +status_t +DWPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32& value) +{ + InterruptsSpinLocker lock(fLock); + + addr_t address = ConfigAddress(bus, device, function, offset); + if (address == 0) + return B_ERROR; + + switch (size) { + case 1: value = ReadReg8(address); break; + case 2: value = ReadReg16(address); break; + case 4: value = *(vuint32*)address; break; + default: + return B_ERROR; + } + + return B_OK; +} + + +status_t +DWPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32 value) +{ + InterruptsSpinLocker lock(fLock); + + addr_t address = ConfigAddress(bus, device, function, offset); + if (address == 0) + return B_ERROR; + + switch (size) { + case 1: WriteReg8(address, value); break; + case 2: WriteReg16(address, value); break; + case 4: *(vuint32*)address = value; break; + default: + return B_ERROR; + } + + return B_OK; +} + + +status_t +DWPCIController::GetMaxBusDevices(int32& count) +{ + count = 32; + return B_OK; +} + + +status_t +DWPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function, + uint8 pin, uint8& irq) +{ + return B_UNSUPPORTED; +} + + +status_t +DWPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function, + uint8 pin, uint8 irq) +{ + return B_UNSUPPORTED; +} + + +status_t +DWPCIController::GetRange(uint32 index, pci_resource_range* range) +{ + if (index >= kPciRangeEnd) + return B_BAD_INDEX; + + *range = fResourceRanges[index]; + return B_OK; +} + + +//#pragma mark - DWPCIController + + +status_t +DWPCIController::AtuMap(uint32 index, uint32 direction, uint32 type, uint64 parentAdr, + uint64 childAdr, uint32 size) +{ + /* + dprintf("AtuMap(%" B_PRIu32 ", %" B_PRIu32 ", %#" B_PRIx64 ", %#" B_PRIx64 ", " + "%#" B_PRIx32 ")\n", index, type, parentAdr, childAdr, size); + */ + volatile PciAtuRegs* atu = (PciAtuRegs*)(fDbiBase + kPciAtuOffset + + (2 * index + direction) * sizeof(PciAtuRegs)); + + atu->baseLo = (uint32)parentAdr; + atu->baseHi = (uint32)(parentAdr >> 32); + atu->limit = (uint32)(parentAdr + size - 1); + atu->targetLo = (uint32)childAdr; + atu->targetHi = (uint32)(childAdr >> 32); + atu->ctrl1 = type; + atu->ctrl2 = kPciAtuEnable; + + for (;;) { + if ((atu->ctrl2 & kPciAtuEnable) != 0) + break; + } + + return B_OK; +} + + +void +DWPCIController::AtuDump() +{ + dprintf("ATU:\n"); + for (uint32 direction = 0; direction < 2; direction++) { + switch (direction) { + case kPciAtuOutbound: + dprintf(" outbound:\n"); + break; + case kPciAtuInbound: + dprintf(" inbound:\n"); + break; + } + + for (uint32 index = 0; index < 8; index++) { + volatile PciAtuRegs* atu = (PciAtuRegs*)(fDbiBase + + kPciAtuOffset + (2 * index + direction) * sizeof(PciAtuRegs)); + + dprintf(" %" B_PRIu32 ": ", index); + dprintf("base: %#08" B_PRIx64, atu->baseLo + ((uint64)atu->baseHi << 32)); + dprintf(", limit: %#08" B_PRIx32, atu->limit); + dprintf(", target: %#08" B_PRIx64, atu->targetLo + + ((uint64)atu->targetHi << 32)); + dprintf(", ctrl1: "); + uint32 ctrl1 = atu->ctrl1; + switch (ctrl1) { + case kPciAtuTypeMem: + dprintf("mem"); + break; + case kPciAtuTypeIo: + dprintf("io"); + break; + case kPciAtuTypeCfg0: + dprintf("cfg0"); + break; + case kPciAtuTypeCfg1: + dprintf("cfg1"); + break; + default: + dprintf("? (%#" B_PRIx32 ")", ctrl1); + } + dprintf(", ctrl2: {"); + uint32 ctrl2 = atu->ctrl2; + bool first = true; + for (uint32 i = 0; i < 32; i++) { + if (((1 << i) & ctrl2) != 0) { + if (first) + first = false; + else + dprintf(", "); + switch (i) { + case 30: + dprintf("barModeEnable"); + break; + case 31: + dprintf("enable"); + break; + default: + dprintf("? (%" B_PRIu32 ")", i); + break; + } + } + } + dprintf("}\n"); + } + } +} diff --git a/src/add-ons/kernel/busses/pci/designware/DWPCIController.h b/src/add-ons/kernel/busses/pci/designware/DWPCIController.h new file mode 100644 index 0000000000..dcbbd6742a --- /dev/null +++ b/src/add-ons/kernel/busses/pci/designware/DWPCIController.h @@ -0,0 +1,249 @@ +/* + * Copyright 2022, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ + + +#ifndef _PCICONTROLLERDW_H_ +#define _PCICONTROLLERDW_H_ + +#include + +#include +#include + + +#define CHECK_RET(err) {status_t _err = (err); if (_err < B_OK) return _err;} + +#define DESIGNWARE_PCI_DRIVER_MODULE_NAME "busses/pci/designware/driver_v1" + + +enum { + fdtPciRangeConfig = 0x00000000, + fdtPciRangeIoPort = 0x01000000, + fdtPciRangeMmio32Bit = 0x02000000, + fdtPciRangeMmio64Bit = 0x03000000, + fdtPciRangeTypeMask = 0x03000000, + fdtPciRangeAliased = 0x20000000, + fdtPciRangePrefechable = 0x40000000, + fdtPciRangeRelocatable = 0x80000000, +}; + + +enum PciBarKind { + kRegIo, + kRegMmio32, + kRegMmio64, + kRegMmio1MB, + kRegUnknown, +}; + + +union PciAddress { + struct { + uint32 offset: 8; + uint32 function: 3; + uint32 device: 5; + uint32 bus: 8; + uint32 unused: 8; + }; + uint32 val; +}; + +union PciAddressEcam { + struct { + uint32 offset: 12; + uint32 function: 3; + uint32 device: 5; + uint32 bus: 8; + uint32 unused: 4; + }; + uint32 val; +}; + +struct RegisterRange { + phys_addr_t parentBase; + phys_addr_t childBase; + uint64 size; +}; + +struct InterruptMapMask { + uint32_t childAdr; + uint32_t childIrq; +}; + +struct InterruptMap { + uint32_t childAdr; + uint32_t childIrq; + uint32_t parentIrqCtrl; + uint32_t parentIrq; +}; + + +enum { + kPciAtuOffset = 0x300000, +}; + +enum { + kPciAtuOutbound = 0, + kPciAtuInbound = 1, +}; + +enum { + // ctrl1 + kPciAtuTypeMem = 0, + kPciAtuTypeIo = 2, + kPciAtuTypeCfg0 = 4, + kPciAtuTypeCfg1 = 5, + // ctrl2 + kPciAtuBarModeEnable = 1 << 30, + kPciAtuEnable = 1 << 31, +}; + +struct PciAtuRegs { + uint32 ctrl1; + uint32 ctrl2; + uint32 baseLo; + uint32 baseHi; + uint32 limit; + uint32 targetLo; + uint32 targetHi; + uint32 unused[57]; +}; + +struct PciDbiRegs { + uint8 unknown0[0x700]; + + uint32 unknown1[3]; + uint32 portAfr; + uint32 linkControl; + uint32 unknown2[5]; + uint32 portDebug0; + uint32 portDebug1; + uint32 unknown3[55]; + uint32 linkWidthSpeedControl; + uint32 unknown4[4]; + uint32 msiAddrLo; + uint32 msiAddrHi; + struct { + uint32 enable; + uint32 mask; + uint32 status; + } msiIntr[8]; + uint32 unknown5[13]; + uint32 miscControl1Off; + uint32 miscPortMultiLaneCtrl; + uint32 unknown6[15]; + + uint32 atuViewport; + uint32 atuCr1; + uint32 atuCr2; + uint32 atuBaseLo; + uint32 atuBaseHi; + uint32 atuLimit; + uint32 atuTargetLo; + uint32 atuTargetHi; + uint32 unknown7; + uint32 atuLimitHi; + uint32 unknown8[8]; + + uint32 msixDoorbell; + uint32 unknown9[117]; + + uint32 plChkRegControlStatus; + uint32 unknown10; + uint32 plChkRegErrAddr; + uint32 unknown11[309]; +}; + + +class MsiInterruptCtrlDW: public MSIInterface { +public: + virtual ~MsiInterruptCtrlDW() = default; + + status_t Init(PciDbiRegs volatile* dbiRegs, int32 msiIrq); + + status_t AllocateVectors(uint8 count, uint8& startVector, uint64& address, + uint16& data) final; + void FreeVectors(uint8 count, uint8 startVector) final; + + +private: + static int32 InterruptReceived(void* arg); + inline int32 InterruptReceivedInt(); + +private: + PciDbiRegs volatile* fDbiRegs {}; + + uint32 fAllocatedMsiIrqs[1]; + phys_addr_t fMsiPhysAddr {}; + long fMsiStartIrq {}; + uint64 fMsiData {}; +}; + + +class DWPCIController { +public: + static float SupportsDevice(device_node* parent); + static status_t RegisterDevice(device_node* parent); + static status_t InitDriver(device_node* node, DWPCIController*& outDriver); + void UninitDriver(); + + status_t ReadConfig( + uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32 &value); + + status_t WriteConfig( + uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32 value); + + status_t GetMaxBusDevices(int32& count); + + status_t ReadIrq( + uint8 bus, uint8 device, uint8 function, + uint8 pin, uint8& irq); + + status_t WriteIrq( + uint8 bus, uint8 device, uint8 function, + uint8 pin, uint8 irq); + + status_t GetRange(uint32 index, pci_resource_range* range); + +private: + status_t ReadResourceInfo(); + inline status_t InitDriverInt(device_node* node); + + inline addr_t ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset); + + PciDbiRegs volatile* GetDbuRegs() {return (PciDbiRegs volatile*)fDbiBase;} + status_t AtuMap(uint32 index, uint32 direction, uint32 type, + uint64 parentAdr, uint64 childAdr, uint32 size); + void AtuDump(); + +private: + spinlock fLock = B_SPINLOCK_INITIALIZER; + + device_node* fNode {}; + + AreaDeleter fConfigArea; + addr_t fConfigPhysBase {}; + addr_t fConfigBase {}; + size_t fConfigSize {}; + + pci_resource_range fResourceRanges[kPciRangeEnd] {}; + InterruptMapMask fInterruptMapMask {}; + uint32 fInterruptMapLen {}; + ArrayDeleter fInterruptMap; + + AreaDeleter fDbiArea; + addr_t fDbiPhysBase {}; + addr_t fDbiBase {}; + size_t fDbiSize {}; + + MsiInterruptCtrlDW fIrqCtrl; +}; + + +extern device_manager_info* gDeviceManager; + +#endif // _PCICONTROLLERDW_H_ diff --git a/src/add-ons/kernel/busses/pci/designware/Jamfile b/src/add-ons/kernel/busses/pci/designware/Jamfile new file mode 100644 index 0000000000..50604c88f0 --- /dev/null +++ b/src/add-ons/kernel/busses/pci/designware/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src add-ons kernel busses pci designware ; + +SubDirC++Flags -fno-rtti ; + +UsePrivateKernelHeaders ; + +KernelAddon designware : + DWPCIController.cpp + MsiInterruptCtrlDW.cpp + kernel_interface.cpp +; diff --git a/src/add-ons/kernel/busses/pci/designware/MsiInterruptCtrlDW.cpp b/src/add-ons/kernel/busses/pci/designware/MsiInterruptCtrlDW.cpp new file mode 100644 index 0000000000..719d8a012b --- /dev/null +++ b/src/add-ons/kernel/busses/pci/designware/MsiInterruptCtrlDW.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2022, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ + + +#include "DWPCIController.h" + + +status_t +MsiInterruptCtrlDW::Init(PciDbiRegs volatile* dbiRegs, int32 msiIrq) +{ + dprintf("MsiInterruptCtrlDW::Init()\n"); + dprintf(" msiIrq: %" B_PRId32 "\n", msiIrq); + + fDbiRegs = dbiRegs; + + physical_entry pe; + status_t result = get_memory_map(&fMsiData, sizeof(fMsiData), &pe, 1); + if (result != B_OK) { + dprintf(" unable to get MSI Memory map!\n"); + return result; + } + + fMsiPhysAddr = pe.address; + dprintf(" fMsiPhysAddr: %#" B_PRIxADDR "\n", fMsiPhysAddr); + fDbiRegs->msiAddrLo = (uint32)fMsiPhysAddr; + fDbiRegs->msiAddrHi = (uint32)(fMsiPhysAddr >> 32); + + fDbiRegs->msiIntr[0].enable = 0xffffffff; + fDbiRegs->msiIntr[0].mask = 0xffffffff; + + result = install_io_interrupt_handler(msiIrq, InterruptReceived, this, 0); + if (result != B_OK) { + dprintf(" unable to attach MSI irq handler!\n"); + return result; + } + result = allocate_io_interrupt_vectors(32, &fMsiStartIrq, INTERRUPT_TYPE_IRQ); + + if (result != B_OK) { + dprintf(" unable to attach MSI irq handler!\n"); + return result; + } + + msi_set_interface(static_cast(this)); + + dprintf(" fMsiStartIrq: %ld\n", fMsiStartIrq); + + return B_OK; +} + + +status_t +MsiInterruptCtrlDW::AllocateVectors(uint8 count, uint8& startVector, uint64& address, uint16& data) +{ + if (count != 1) + return B_ERROR; + + for (int i = 0; i < 32; i++) { + if (((1 << i) & fAllocatedMsiIrqs[0]) == 0) { + fAllocatedMsiIrqs[0] |= (1 << i); + fDbiRegs->msiIntr[0].mask &= ~(1 << i); + + startVector = fMsiStartIrq + i; + address = fMsiPhysAddr; + data = i; + return B_OK; + } + } + return B_ERROR; +} + + +void +MsiInterruptCtrlDW::FreeVectors(uint8 count, uint8 startVector) +{ + int32 irq = (int32)startVector - fMsiStartIrq; + while (count > 0) { + if (irq >= 0 && irq < 32 && ((1 << irq) & fAllocatedMsiIrqs[0]) != 0) { + fDbiRegs->msiIntr[0].mask |= (1 << (uint32)irq); + fAllocatedMsiIrqs[0] &= ~(1 << (uint32)irq); + } + irq++; + count--; + } +} + + +int32 +MsiInterruptCtrlDW::InterruptReceived(void* arg) +{ + return static_cast(arg)->InterruptReceivedInt(); +} + + +int32 +MsiInterruptCtrlDW::InterruptReceivedInt() +{ +// dprintf("MsiInterruptCtrlDW::InterruptReceivedInt()\n"); + uint32 status = fDbiRegs->msiIntr[0].status; + for (int i = 0; i < 32; i++) { + if (((1 << i) & status) != 0) { +// dprintf("MSI IRQ: %d (%ld)\n", i, fStartMsiIrq + i); + int_io_interrupt_handler(fMsiStartIrq + i, false); + fDbiRegs->msiIntr[0].status = (1 << i); + } + } + return B_HANDLED_INTERRUPT; +} diff --git a/src/add-ons/kernel/busses/pci/designware/kernel_interface.cpp b/src/add-ons/kernel/busses/pci/designware/kernel_interface.cpp new file mode 100644 index 0000000000..4f428b90a4 --- /dev/null +++ b/src/add-ons/kernel/busses/pci/designware/kernel_interface.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2022, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ + + +#include "DWPCIController.h" + + +device_manager_info* gDeviceManager; + + +pci_controller_module_info gPciControllerDriver = { + .info = { + .info = { + .name = DESIGNWARE_PCI_DRIVER_MODULE_NAME, + }, + .supports_device = DWPCIController::SupportsDevice, + .register_device = DWPCIController::RegisterDevice, + .init_driver = [](device_node* node, void** driverCookie) { + return DWPCIController::InitDriver(node, *(DWPCIController**)driverCookie); + }, + .uninit_driver = [](void* driverCookie) { + return static_cast(driverCookie)->UninitDriver(); + }, + }, + .read_pci_config = [](void* cookie, + uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32* value) { + return static_cast(cookie) + ->ReadConfig(bus, device, function, offset, size, *value); + }, + .write_pci_config = [](void* cookie, + uint8 bus, uint8 device, uint8 function, + uint16 offset, uint8 size, uint32 value) { + return static_cast(cookie) + ->WriteConfig(bus, device, function, offset, size, value); + }, + .get_max_bus_devices = [](void* cookie, int32* count) { + return static_cast(cookie)->GetMaxBusDevices(*count); + }, + .read_pci_irq = [](void* cookie, + uint8 bus, uint8 device, uint8 function, + uint8 pin, uint8 *irq) { + return static_cast(cookie)->ReadIrq(bus, device, function, pin, *irq); + }, + .write_pci_irq = [](void* cookie, + uint8 bus, uint8 device, uint8 function, + uint8 pin, uint8 irq) { + return static_cast(cookie)->WriteIrq(bus, device, function, pin, irq); + }, + .get_range = [](void *cookie, uint32 index, pci_resource_range* range) { + return static_cast(cookie)->GetRange(index, range); + } +}; + + +_EXPORT module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager }, + {} +}; + +_EXPORT module_info *modules[] = { + (module_info *)&gPciControllerDriver, + NULL +};