157 lines
4 KiB
C
157 lines
4 KiB
C
#include <stdint.h>
|
|
#include <sys.h>
|
|
|
|
#include "../panic.h"
|
|
#include "pci.h"
|
|
|
|
#define CONF_ADDR 0xCF8
|
|
#define CONF_DATA 0xCFC
|
|
|
|
#define TABLE_LEN 16
|
|
|
|
struct PciTableEntry {
|
|
struct PciDevice device;
|
|
uint16_t device_id;
|
|
uint16_t vendor_id;
|
|
uint8_t class;
|
|
uint8_t subclass;
|
|
uint8_t prog_if;
|
|
uint8_t revision;
|
|
};
|
|
|
|
static struct PciTableEntry pci_table[TABLE_LEN];
|
|
static size_t pci_table_next = 0;
|
|
|
|
uint32_t pci_rcfg_d(struct PciDevice dev, uint8_t offset) {
|
|
uint32_t addr = 0x80000000;
|
|
addr |= ((uint32_t)dev.bus) << 16;
|
|
addr |= ((uint32_t)dev.device) << 11;
|
|
addr |= ((uint32_t)dev.function) << 8;
|
|
addr |= offset & 0xFC;
|
|
|
|
outl(CONF_ADDR, addr);
|
|
uint32_t in = inl(CONF_DATA);
|
|
return in;
|
|
}
|
|
|
|
uint16_t pci_rcfg_w(struct PciDevice dev, uint8_t offset) {
|
|
uint32_t dword = pci_rcfg_d(dev, offset);
|
|
return (uint16_t)((dword >> ((offset & 2) * 8)) & 0xFFFF);
|
|
}
|
|
|
|
uint8_t pci_rcfg_b(struct PciDevice dev, uint8_t offset) {
|
|
uint32_t dword = pci_rcfg_d(dev, offset);
|
|
return (uint8_t)((dword >> ((offset & 3) * 8)) & 0xFF);
|
|
}
|
|
|
|
void pci_wcfg_d(struct PciDevice dev, uint8_t offset, uint32_t dword) {
|
|
uint32_t addr = 0x80000000;
|
|
addr |= ((uint32_t)dev.bus) << 16;
|
|
addr |= ((uint32_t)dev.device) << 11;
|
|
addr |= ((uint32_t)dev.function) << 8;
|
|
addr |= offset & 0xFC;
|
|
|
|
outl(CONF_ADDR, addr);
|
|
outl(CONF_DATA, dword);
|
|
}
|
|
|
|
void pci_wcfg_w(struct PciDevice dev, uint8_t offset, uint16_t word) {
|
|
size_t shift = (offset & 2) * 8;
|
|
uint32_t dword = pci_rcfg_d(dev, offset);
|
|
dword &= ~(0xFFFF << shift);
|
|
dword |= word << shift;
|
|
pci_wcfg_d(dev, offset, dword);
|
|
}
|
|
|
|
void pci_wcfg_b(struct PciDevice dev, uint8_t offset, uint8_t byte) {
|
|
size_t shift = (offset & 3) * 8;
|
|
uint32_t dword = pci_rcfg_d(dev, offset);
|
|
dword &= ~(0xFF << shift);
|
|
dword |= byte << shift;
|
|
pci_wcfg_d(dev, offset, dword);
|
|
}
|
|
|
|
//static void print_device(struct PciTableEntry *entry) {
|
|
// printf("pci bus @0e%X@0f dev @0e%X@0f func @0e%X@0f",
|
|
// entry->device.bus, entry->device.device, entry->device.function);
|
|
//
|
|
// printf("\tid @0c%X:%X@0f", entry->vendor_id, entry->device_id);
|
|
//
|
|
// printf("\tclass @0c%X:%X:%X@0f", entry->class, entry->subclass, entry->prog_if);
|
|
// printf("\trev @0c%X@0f\n", entry->revision);
|
|
//}
|
|
|
|
|
|
static struct PciTableEntry *load_device(struct PciDevice dev) {
|
|
if(pci_table_next >= TABLE_LEN) panic("Too many PCI devices: limit is %d", TABLE_LEN);
|
|
struct PciTableEntry *entry = &pci_table[pci_table_next++];
|
|
entry->device = dev;
|
|
uint32_t dword0 = pci_rcfg_d(dev, 0);
|
|
uint32_t dword2 = pci_rcfg_d(dev, 8);
|
|
|
|
entry->device_id = (dword0 >> 16) & 0xFFFF;
|
|
entry->vendor_id = dword0 & 0xFFFF;
|
|
|
|
entry->class = (dword2 >> 24) & 0xFF;
|
|
entry->subclass = (dword2 >> 16) & 0xFF;
|
|
entry->prog_if = (dword2 >> 8) & 0xFF;
|
|
entry->revision = dword2 & 0xFF;
|
|
|
|
//print_device(entry);
|
|
return entry;
|
|
}
|
|
|
|
void pci_init(void) {
|
|
pci_table_next = 0;
|
|
struct PciDevice pcidev;
|
|
for(int bus = 0; bus < 256; bus++) {
|
|
pcidev.bus = bus;
|
|
for(int dev = 0; dev < 32; dev++) {
|
|
pcidev.device = dev;
|
|
pcidev.function = 0;
|
|
|
|
uint16_t vendor = pci_rcfg_w(pcidev, 0);
|
|
if(vendor == 0xFFFF) continue;
|
|
|
|
load_device(pcidev);
|
|
|
|
uint8_t header_type = pci_rcfg_b(pcidev, 14);
|
|
|
|
if(!(header_type & 0x80)) continue;
|
|
for(int func = 1; func < 8; func++) {
|
|
pcidev.function = func;
|
|
|
|
uint16_t vendor = pci_rcfg_w(pcidev, 0);
|
|
if(vendor == 0xFFFF) continue;
|
|
|
|
load_device(pcidev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool pci_findby_class(struct PciDevice *dest, uint8_t class, uint8_t subclass, size_t *offset) {
|
|
size_t o = 0;
|
|
if(offset == NULL) offset = &o;
|
|
for(; *offset < pci_table_next; (*offset)++) {
|
|
struct PciTableEntry *entry = &pci_table[*offset];
|
|
if(entry->class == class && entry->subclass == subclass) {
|
|
*dest = entry->device;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool pci_findby_id(struct PciDevice *dest, uint16_t device, uint16_t vendor, size_t *offset) {
|
|
size_t o = 0;
|
|
if(offset == NULL) offset = &o;
|
|
for(; *offset < pci_table_next; (*offset)++) {
|
|
struct PciTableEntry *entry = &pci_table[*offset];
|
|
if(entry->device_id == device && entry->vendor_id == vendor) {
|
|
*dest = entry->device;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|