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:
parent
912bdb2aca
commit
f33c8f9ce2
@ -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
|
||||
|
@ -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 ;
|
||||
|
500
src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp
Normal file
500
src/add-ons/kernel/busses/pci/designware/DWPCIController.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
249
src/add-ons/kernel/busses/pci/designware/DWPCIController.h
Normal file
249
src/add-ons/kernel/busses/pci/designware/DWPCIController.h
Normal 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_
|
11
src/add-ons/kernel/busses/pci/designware/Jamfile
Normal file
11
src/add-ons/kernel/busses/pci/designware/Jamfile
Normal 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
|
||||
;
|
109
src/add-ons/kernel/busses/pci/designware/MsiInterruptCtrlDW.cpp
Normal file
109
src/add-ons/kernel/busses/pci/designware/MsiInterruptCtrlDW.cpp
Normal 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;
|
||||
}
|
@ -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
|
||||
};
|
Loading…
Reference in New Issue
Block a user