202 lines
7.5 KiB
C
202 lines
7.5 KiB
C
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <exec/hal.h>
|
|
#include <exec/debcon.h>
|
|
#include <exec/mem.h>
|
|
#include <exec/detect.h>
|
|
#include <platform/pcidetect.h>
|
|
|
|
static enum HalAddressSpace space;
|
|
static uintptr_t addr_port, data_port;
|
|
static const char *DETECT_STRING_TEMPLATE = "pci()vendor(0000)device(0000)subvendor(0000)subdevice(0000)class(00)subclass(00)progif(00)revision(00)bus(00)dev(00)fun(00)";
|
|
|
|
static uint32_t PCI_ReadConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
|
|
uint32_t address = (1U << 31) | ((uint32_t)bus << 16) | ((uint32_t)device << 11) | ((uint32_t)function << 8) | (offset & 0xFC);
|
|
HalWriteIO32(space, addr_port, address);
|
|
return HalReadIO32(space, data_port);
|
|
}
|
|
|
|
static void PCI_WriteConfig(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) {
|
|
uint32_t address = (1U << 31) | ((uint32_t)bus << 16) | ((uint32_t)device << 11) | ((uint32_t)function << 8) | (offset & 0xFC);
|
|
HalWriteIO32(space, addr_port, address);
|
|
HalWriteIO32(space, data_port, value);
|
|
}
|
|
|
|
static uint16_t GetVendorID(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint16_t)(PCI_ReadConfig(bus, device, function, 0x00) & 0xFFFF);
|
|
}
|
|
|
|
static uint16_t GetDeviceID(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint16_t)((PCI_ReadConfig(bus, device, function, 0x00) >> 16) & 0xFFFF);
|
|
}
|
|
|
|
static uint8_t GetHeaderType(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x0C) >> 16) & 0xFF);
|
|
}
|
|
|
|
static uint8_t GetRevisionID(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint8_t)(PCI_ReadConfig(bus, device, function, 0x08) & 0xFF);
|
|
}
|
|
|
|
static uint8_t GetProgIF(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x08) >> 8) & 0xFF);
|
|
}
|
|
|
|
static uint8_t GetSubclass(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x08) >> 16) & 0xFF);
|
|
}
|
|
|
|
static uint8_t GetClassCode(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint8_t)((PCI_ReadConfig(bus, device, function, 0x08) >> 16) & 0xFF);
|
|
}
|
|
|
|
static uint16_t GetSubsystemVendorID(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint16_t)(PCI_ReadConfig(bus, device, function, 0x2C) & 0xFFFF);
|
|
}
|
|
|
|
static uint16_t GetSubsystemID(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint16_t)((PCI_ReadConfig(bus, device, function, 0x2C) >> 16) & 0xFFFF);
|
|
}
|
|
|
|
static uint8_t GetInterruptLine(uint8_t bus, uint8_t device, uint8_t function) {
|
|
return (uint8_t)(PCI_ReadConfig(bus, device, function, 0x3C) & 0xFF);
|
|
}
|
|
|
|
static void EnumerateDevice(uint8_t bus, uint8_t device) {
|
|
for (uint8_t function = 0; function < 8; function++) {
|
|
uint16_t vendor_id = GetVendorID(bus, device, function);
|
|
|
|
/* check if function present */
|
|
if (vendor_id == 0xFFFF) {
|
|
if (function) continue;
|
|
else break;
|
|
}
|
|
|
|
/* get device info */
|
|
uint16_t device_id = GetDeviceID(bus, device, function);
|
|
uint8_t header_type = GetHeaderType(bus, device, function);
|
|
uint8_t revision_id = GetRevisionID(bus, device, function);
|
|
uint8_t prog_if = GetProgIF(bus, device, function);
|
|
uint8_t subclass = GetSubclass(bus, device, function);
|
|
uint8_t class_code = GetClassCode(bus, device, function);
|
|
uint16_t subvendor = GetSubsystemVendorID(bus, device, function);
|
|
uint16_t subdevice = GetSubsystemID(bus, device, function);
|
|
uint8_t interrupt_line = GetInterruptLine(bus, device, function);
|
|
|
|
/* build compat string */
|
|
char *compat = AllocMem(strlen(DETECT_STRING_TEMPLATE) + 1);
|
|
strncpy(compat, DETECT_STRING_TEMPLATE, strlen(DETECT_STRING_TEMPLATE));
|
|
compat[strlen(compat)] = 0;
|
|
BuildCompat(compat,
|
|
"vendor", vendor_id, 4,
|
|
"device", device_id, 4,
|
|
"class", class_code, 2,
|
|
"subvendor", subvendor, 4,
|
|
"subdevice", subdevice, 4,
|
|
"subclass", subclass, 2,
|
|
"progif", prog_if, 2,
|
|
"revision", revision_id, 2,
|
|
"bus", bus, 2,
|
|
"dev", device, 2,
|
|
"fun", function, 2,
|
|
NULL
|
|
);
|
|
bool has_irq = (interrupt_line && interrupt_line != 0xFF);
|
|
|
|
/* determine number of BARs */
|
|
int region = 0;
|
|
for (int i = 0; i < 6; i++) {
|
|
uint32_t bar = PCI_ReadConfig(bus, device, function, 0x10 + i * 4);
|
|
if (bar == 0) continue;
|
|
region++;
|
|
}
|
|
|
|
/* create detect structure */
|
|
struct Detect *detect = CreateDetect(compat, region, has_irq);
|
|
|
|
/* set IRQ if present */
|
|
if (has_irq) {
|
|
SetDetectIRQ(detect, 0, interrupt_line);
|
|
}
|
|
|
|
/* set regions (BARs) */
|
|
region = 0;
|
|
for (int i = 0; i < 6; i++) {
|
|
int bar_addr = 0x10 + i * 4;
|
|
uint32_t bar = PCI_ReadConfig(bus, device, function, bar_addr);
|
|
if (bar == 0) continue;
|
|
enum HalAddressSpace region_space = (bar & 1) ? AS_IO : AS_MEM;
|
|
uint64_t base;
|
|
size_t limit;
|
|
if (region_space == AS_IO) {
|
|
base = (uintptr_t)(bar & ~0x3);
|
|
|
|
/* determine size */
|
|
PCI_WriteConfig(bus, device, function, bar_addr, 0xFFFFFFFF);
|
|
uint32_t size_mask = PCI_ReadConfig(bus, device, function, bar_addr);
|
|
PCI_WriteConfig(bus, device, function, bar_addr, bar);
|
|
if (size_mask == 0) continue;
|
|
limit = (size_t)(~(size_mask & ~0x3) + 1);
|
|
} else {
|
|
if ((bar & 0x6) == 0x4) {
|
|
/* 64-bit address */
|
|
uint32_t bar_high = PCI_ReadConfig(bus, device, function, bar_addr + 4);
|
|
base = ((uint64_t)bar_high << 32) | (uint64_t)(bar & ~0xF);
|
|
i++; /* skip next BAR */
|
|
} else {
|
|
base = (uint64_t)(bar & ~0xF);
|
|
}
|
|
|
|
/* determine size */
|
|
PCI_WriteConfig(bus, device, function, bar_addr, 0xFFFFFFFF);
|
|
uint32_t size_mask = PCI_ReadConfig(bus, device, function, bar_addr);
|
|
PCI_WriteConfig(bus, device, function, bar_addr, bar);
|
|
if (size_mask == 0) continue;
|
|
limit = (size_t)(~(size_mask & ~0xF) + 1);
|
|
}
|
|
if (base < 0xFFFFFFFFULL) {
|
|
/* only support 32-bit addresses for now */
|
|
SetDetectRegion(detect, region++, region_space, base, limit);
|
|
}
|
|
}
|
|
AddDetect(detect);
|
|
|
|
if (!function && !(header_type & 0x80)) {
|
|
/* no more functions */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EnumerateBus(uint8_t bus) {
|
|
for (uint8_t device = 0; device < 32; device++) {
|
|
EnumerateDevice(bus, device);
|
|
}
|
|
}
|
|
|
|
static void EnumerateBusses(void) {
|
|
uint8_t function;
|
|
uint8_t header_type = GetHeaderType(0, 0, 0);
|
|
|
|
if ((header_type & 0x80) == 0) {
|
|
/* single function device */
|
|
EnumerateBus(0);
|
|
} else {
|
|
/* multi-function device */
|
|
for (function = 0; function < 8; function++) {
|
|
if (GetVendorID(0, 0, function) != 0xFFFF) {
|
|
EnumerateBus(function);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PCI_Detect(enum HalAddressSpace _space, uintptr_t _addr_port, uintptr_t _data_port) {
|
|
space = _space;
|
|
addr_port = _addr_port;
|
|
data_port = _data_port;
|
|
|
|
EnumerateBusses();
|
|
} |