diff options
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r-- | sys/dev/pci/pci.c | 192 |
1 files changed, 176 insertions, 16 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index a7b8bac..9dfb90e 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,14 +31,33 @@ #include <sys/queue.h> #include <sys/syslog.h> #include <sys/errno.h> +#include <sys/spinlock.h> +#include <sys/mmio.h> #include <dev/pci/pci.h> #include <dev/pci/pciregs.h> +#include <dev/acpi/acpi.h> +#include <dev/acpi/tables.h> +#include <machine/pci/pci.h> #include <vm/dynalloc.h> +#include <vm/vm.h> #include <lib/assert.h> #define pr_trace(fmt, ...) kprintf("pci: " fmt, ##__VA_ARGS__) static TAILQ_HEAD(, pci_device) device_list; +static struct spinlock devlist_lock = {0}; +static struct acpi_mcfg *mcfg; + +struct cam_hook { + /* PCI CAM */ + pcireg_t(*cam_readl)(struct pci_device *dev, uint32_t off); + void(*cam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val); + + /* PCIe ECAM */ + pcireg_t(*ecam_readl)(struct pci_device *dev, uint32_t off); + void(*ecam_writel)(struct pci_device *dev, uint32_t off, pcireg_t val); + void *ecam_base[1]; +} cam_hook = { NULL }; static bool pci_dev_exists(uint8_t bus, uint8_t slot, uint8_t func) @@ -109,7 +128,7 @@ pci_get_cap(struct pci_device *dev, uint8_t id) static void pci_set_device_info(struct pci_device *dev) { - uint32_t classrev; + uint32_t classrev, buses; int capoff; dev->vendor_id = pci_readl(dev, PCIREG_VENDOR_ID) & 0xFFFF; @@ -119,17 +138,84 @@ pci_set_device_info(struct pci_device *dev) dev->pci_class = PCIREG_CLASS(classrev); dev->pci_subclass = PCIREG_SUBCLASS(classrev); dev->prog_if = PCIREG_PROGIF(classrev); + dev->hdr_type = (uint8_t)pci_readl(dev, PCIREG_HDRTYPE); + + /* This is a PCIe device if it has CAP ID of 0x10 */ + dev->pci_express = pci_get_cap(dev, 0x10) != 0; + + /* Set type-specific data */ + switch (dev->hdr_type & ~BIT(7)) { + case PCI_HDRTYPE_NORMAL: + dev->bar[0] = pci_readl(dev, PCIREG_BAR0); + dev->bar[1] = pci_readl(dev, PCIREG_BAR1); + dev->bar[2] = pci_readl(dev, PCIREG_BAR2); + dev->bar[3] = pci_readl(dev, PCIREG_BAR3); + dev->bar[4] = pci_readl(dev, PCIREG_BAR4); + dev->bar[5] = pci_readl(dev, PCIREG_BAR5); + + dev->irq_line = pci_readl(dev, PCIREG_IRQLINE) & 0xFF; + capoff = pci_get_cap(dev, PCI_CAP_MSIX); + dev->msix_capoff = (capoff < 0) ? 0 : capoff; + break; + case PCI_HDRTYPE_BRIDGE: + buses = pci_readl(dev, PCIREG_BUSES); + dev->pri_bus = PCIREG_PRIBUS(buses); + dev->sec_bus = PCIREG_SECBUS(buses); + dev->sub_bus = PCIREG_SUBBUS(buses); + break; + default: + break; + } +} + +static void +pci_scan_bus(uint8_t bus); + +static inline vaddr_t +pcie_ecam_addr(struct pci_device *dev) +{ + vaddr_t base = (vaddr_t)cam_hook.ecam_base[0]; + + base += dev->bus << 20 | + dev->slot << 15 | + dev->func << 12; + return base; +} - dev->bar[0] = pci_readl(dev, PCIREG_BAR0); - dev->bar[1] = pci_readl(dev, PCIREG_BAR1); - dev->bar[2] = pci_readl(dev, PCIREG_BAR2); - dev->bar[3] = pci_readl(dev, PCIREG_BAR3); - dev->bar[4] = pci_readl(dev, PCIREG_BAR4); - dev->bar[5] = pci_readl(dev, PCIREG_BAR5); +static pcireg_t +pcie_ecam_readl(struct pci_device *dev, uint32_t offset) +{ + vaddr_t address; - dev->irq_line = pci_readl(dev, PCIREG_IRQLINE) & 0xFF; - capoff = pci_get_cap(dev, PCI_CAP_MSIX); - dev->msix_capoff = (capoff < 0) ? 0 : capoff; + address = pcie_ecam_addr(dev); + address += (offset & ~3); + return mmio_read32((void *)address); +} + +static void +pcie_ecam_writel(struct pci_device *dev, uint32_t offset, pcireg_t val) +{ + vaddr_t address; + + address = pcie_ecam_addr(dev); + address += (offset & ~3); + mmio_write32((void *)address, val); +} + +static int +pcie_init(struct acpi_mcfg_base *base) +{ + void *iobase; + + pr_trace("[group %02d] @ bus [%02d - %02d]\n", base->seg_grpno, + base->bus_start, base->bus_end); + pr_trace("ecam @ %p\n", base->base_pa); + + iobase = PHYS_TO_VIRT(base->base_pa); + cam_hook.ecam_base[0] = iobase; + cam_hook.ecam_writel = pcie_ecam_writel; + cam_hook.ecam_readl = pcie_ecam_readl; + return 0; } /* @@ -159,14 +245,45 @@ pci_register_device(uint8_t bus, uint8_t slot, uint8_t func) dev->func = func; pci_set_device_info(dev); + + /* Check if this is a valid bridge */ + if ( + (dev->hdr_type & ~BIT(7)) == PCI_HDRTYPE_BRIDGE && + dev->sec_bus > dev->bus && + dev->sub_bus >= dev->sec_bus + ) { + /* Scan all subordinate buses */ + for (uint8_t bus = dev->sec_bus; bus <= dev->sub_bus; ++bus) { + pci_scan_bus(bus); + } + } + TAILQ_INSERT_TAIL(&device_list, dev, link); } static void pci_scan_bus(uint8_t bus) { - for (int slot = 0; slot < 32; ++slot) { - for (int func = 0; func < 8; ++func) { + struct pci_device dev; + + dev.bus = bus; + dev.func = 0; + for (uint8_t slot = 0; slot < 32; ++slot) { + dev.slot = slot; + + /* Skip nonexistent device */ + if ((uint16_t)pci_readl(&dev, PCIREG_VENDOR_ID) == 0xFFFF) { + continue; + } + + /* Register single-function device */ + if (!(pci_readl(&dev, PCIREG_HDRTYPE) & BIT(7))) { + pci_register_device(bus, slot, 0); + continue; + } + + /* Register all functions */ + for (uint8_t func = 0; func < 8; ++func) { pci_register_device(bus, slot, func); } } @@ -214,15 +331,58 @@ pci_get_device(struct pci_lookup lookup, uint16_t lookup_type) return NULL; } + +void +pci_add_device(struct pci_device *dev) +{ + spinlock_acquire(&devlist_lock); + TAILQ_INSERT_TAIL(&device_list, dev, link); + spinlock_release(&devlist_lock); +} + + +pcireg_t +pci_readl(struct pci_device *dev, uint32_t offset) +{ + bool have_ecam = cam_hook.ecam_readl != NULL; + + if (dev->pci_express && have_ecam) { + return cam_hook.ecam_readl(dev, offset); + } + + return cam_hook.cam_readl(dev, offset); +} + +void +pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val) +{ + bool have_ecam = cam_hook.ecam_writel != NULL; + + if (dev->pci_express && have_ecam) { + cam_hook.ecam_writel(dev, offset, val); + return; + } + + cam_hook.cam_writel(dev, offset, val); +} + int pci_init(void) { + size_t ndev; TAILQ_INIT(&device_list); - pr_trace("Scanning each bus...\n"); - for (uint16_t i = 0; i < 256; ++i) { - pci_scan_bus(i); + mcfg = acpi_query("MCFG"); + if (mcfg != NULL) { + pcie_init(&mcfg->base[0]); } + cam_hook.cam_readl = md_pci_readl; + cam_hook.cam_writel = md_pci_writel; + + /* Recursively scan bus 0 */ + pci_scan_bus(0); + ndev = TAILQ_NELEM(&device_list); + pr_trace("detected %d devices at pci*\n", ndev); return 0; } |