summaryrefslogtreecommitdiff
path: root/sys/dev/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r--sys/dev/pci/pci.c192
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;
}