diff options
Diffstat (limited to 'src/arch/amd64/drivers/pci.c')
-rw-r--r-- | src/arch/amd64/drivers/pci.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/src/arch/amd64/drivers/pci.c b/src/arch/amd64/drivers/pci.c new file mode 100644 index 0000000..410eb7e --- /dev/null +++ b/src/arch/amd64/drivers/pci.c @@ -0,0 +1,166 @@ +#include <stdint.h> +#include <pci.h> +#include <panic.h> +#include <lib.h> + +#include "../bindings.h" + +#define CONF_ADDR 0xCF8 +#define CONF_DATA 0xCFC + +#define TABLE_LEN 16 + +struct pci_table_entry { + struct pci_device device; + uint16_t device_id; + uint16_t vendor_id; + uint8_t class; + uint8_t subclass; + uint8_t prog_if; + uint8_t revision; +}; + +static struct pci_table_entry pci_table[TABLE_LEN]; +static size_t pci_table_next = 0; + +uint32_t pci_rcfg_d(struct pci_device 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 pci_device 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 pci_device 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 pci_device 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 pci_device 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 pci_device 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 pci_table_entry *entry) { + kprintf( + "BUS: %#-4x DEV: %#-4x FUNC: %#-4x ID: %04x:%04x CLASS: %02x:%02x:%02x REV: %#02x\n", + entry->device.bus, + entry->device.device, + entry->device.function, + entry->vendor_id, + entry->device_id, + entry->class, + entry->subclass, + entry->prog_if, + entry->revision + ); +} + +static struct pci_table_entry *load_device(struct pci_device dev) { + if(pci_table_next >= TABLE_LEN) panic("Too many PCI devices: limit is %d", TABLE_LEN); + struct pci_table_entry *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; + + return entry; +} + +void pci_init(void) { + pci_table_next = 0; + struct pci_device 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); + } + } + } + kprintf("PCI DEVICES\n"); + for (size_t i = 0; i < pci_table_next; i++) { + print_device(&pci_table[i]); + } + kprintf("\n"); +} + +bool pci_findby_class(struct pci_device *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 pci_table_entry *entry = &pci_table[*offset]; + if(entry->class == class && entry->subclass == subclass) { + *dest = entry->device; + return true; + } + } + return false; +} + +bool pci_findby_id(struct pci_device *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 pci_table_entry *entry = &pci_table[*offset]; + if(entry->device_id == device && entry->vendor_id == vendor) { + *dest = entry->device; + return true; + } + } + return false; +} |