busses/pci/designware: add

Change-Id: I43dfc8a4b4fabe7b892e98aa192db95ba1b9378f
Reviewed-on: https://review.haiku-os.org/c/haiku/+/6225
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
X512 2022-12-16 06:54:38 +09:00 committed by waddlesplash
parent 912bdb2aca
commit f33c8f9ce2
7 changed files with 939 additions and 2 deletions

View File

@ -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 : <pci>ecam@riscv64,arm,arm64 <pci>x86@x86,x86_64 ;
AddFilesToPackage add-ons kernel busses pci :
<pci>designware@riscv64 <pci>ecam@riscv64,arm,arm64 <pci>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 : <usb>uhci <usb>ohci <usb>ehci
@ -198,7 +199,7 @@ AddBootModuleSymlinksToPackage
nvme_disk
openpic@ppc
packagefs pci
<pci>ecam@riscv64,arm,arm64 <pci>x86@x86,x86_64
<pci>designware@riscv64 <pci>ecam@riscv64,arm,arm64 <pci>x86@x86,x86_64
fdt@riscv64,arm,arm64
scsi scsi_cd scsi_disk scsi_periph silicon_image_3112 highpoint_ide_pci
sdhci_pci

View File

@ -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 ;

View File

@ -0,0 +1,500 @@
/*
* Copyright 2022, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include "DWPCIController.h"
#include <bus/FDT.h>
#include <AutoDeleterDrivers.h>
#include <util/AutoLock.h>
#include <string.h>
#include <new>
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<DWPCIController> 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");
}
}
}

View File

@ -0,0 +1,249 @@
/*
* Copyright 2022, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#ifndef _PCICONTROLLERDW_H_
#define _PCICONTROLLERDW_H_
#include <bus/PCI.h>
#include <AutoDeleterOS.h>
#include <lock.h>
#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<InterruptMap> fInterruptMap;
AreaDeleter fDbiArea;
addr_t fDbiPhysBase {};
addr_t fDbiBase {};
size_t fDbiSize {};
MsiInterruptCtrlDW fIrqCtrl;
};
extern device_manager_info* gDeviceManager;
#endif // _PCICONTROLLERDW_H_

View File

@ -0,0 +1,11 @@
SubDir HAIKU_TOP src add-ons kernel busses pci designware ;
SubDirC++Flags -fno-rtti ;
UsePrivateKernelHeaders ;
KernelAddon <pci>designware :
DWPCIController.cpp
MsiInterruptCtrlDW.cpp
kernel_interface.cpp
;

View File

@ -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<MSIInterface*>(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<MsiInterruptCtrlDW*>(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;
}

View File

@ -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<DWPCIController*>(driverCookie)->UninitDriver();
},
},
.read_pci_config = [](void* cookie,
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32* value) {
return static_cast<DWPCIController*>(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<DWPCIController*>(cookie)
->WriteConfig(bus, device, function, offset, size, value);
},
.get_max_bus_devices = [](void* cookie, int32* count) {
return static_cast<DWPCIController*>(cookie)->GetMaxBusDevices(*count);
},
.read_pci_irq = [](void* cookie,
uint8 bus, uint8 device, uint8 function,
uint8 pin, uint8 *irq) {
return static_cast<DWPCIController*>(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<DWPCIController*>(cookie)->WriteIrq(bus, device, function, pin, irq);
},
.get_range = [](void *cookie, uint32 index, pci_resource_range* range) {
return static_cast<DWPCIController*>(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
};