summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-06-12 00:04:25 -0400
committerIan Moffett <ian@osmora.org>2025-06-12 00:04:25 -0400
commitf874dae43e8bb09cb15c652add7589899ed4799f (patch)
treeba3020eca7a38a7cc2271c35b7457b57a6e5c551
parent952caa186852ccf3fa82719d3e484918e89d17a8 (diff)
kernel: pci: Add support for PCI Express
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r--sys/dev/pci/pci.c77
-rw-r--r--sys/include/dev/acpi/tables.h30
-rw-r--r--sys/include/dev/pci/pci.h1
3 files changed, 105 insertions, 3 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 95fc5e2..9dfb90e 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -32,20 +32,31 @@
#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
@@ -129,6 +140,9 @@ pci_set_device_info(struct pci_device *dev)
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:
@@ -157,6 +171,53 @@ pci_set_device_info(struct pci_device *dev)
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;
+}
+
+static pcireg_t
+pcie_ecam_readl(struct pci_device *dev, uint32_t offset)
+{
+ vaddr_t address;
+
+ 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;
+}
+
/*
* Attempt to register a device.
*
@@ -283,8 +344,10 @@ pci_add_device(struct pci_device *dev)
pcireg_t
pci_readl(struct pci_device *dev, uint32_t offset)
{
- if (cam_hook.cam_readl == NULL) {
- return (pcireg_t)-1;
+ 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);
@@ -293,7 +356,10 @@ pci_readl(struct pci_device *dev, uint32_t offset)
void
pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val)
{
- if (cam_hook.cam_writel == NULL) {
+ bool have_ecam = cam_hook.ecam_writel != NULL;
+
+ if (dev->pci_express && have_ecam) {
+ cam_hook.ecam_writel(dev, offset, val);
return;
}
@@ -306,6 +372,11 @@ pci_init(void)
size_t ndev;
TAILQ_INIT(&device_list);
+ 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;
diff --git a/sys/include/dev/acpi/tables.h b/sys/include/dev/acpi/tables.h
index 5215c86..d190150 100644
--- a/sys/include/dev/acpi/tables.h
+++ b/sys/include/dev/acpi/tables.h
@@ -132,4 +132,34 @@ struct __packed acpi_hpet {
uint8_t page_protection;
};
+/*
+ * PCIe / ACPI MCFG base address description
+ * table.
+ *
+ * @base_pa: Enhanced configuration base [physical]
+ * @seg_grpno: PCI segment group number
+ * @bus_start: Host bridge bus start
+ * @bus_end: Host bridge bus end
+ */
+struct __packed acpi_mcfg_base {
+ uint64_t base_pa;
+ uint16_t seg_grpno;
+ uint8_t bus_start;
+ uint8_t bus_end;
+ uint32_t reserved;
+};
+
+/*
+ * PCIe / ACPI MCFG structure
+ *
+ * @hdr: ACPI header
+ * @reserved: Do not use
+ * @base: ECAM MMIO address list
+ */
+struct __packed acpi_mcfg {
+ struct acpi_header hdr;
+ uint32_t reserved[2];
+ struct acpi_mcfg_base base[1];
+};
+
#endif /* _ACPI_TABLES_H_ */
diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h
index a4de162..144b500 100644
--- a/sys/include/dev/pci/pci.h
+++ b/sys/include/dev/pci/pci.h
@@ -62,6 +62,7 @@ struct pci_device {
uint8_t pci_subclass;
uint8_t prog_if;
uint8_t hdr_type;
+ uint8_t pci_express : 1;
uint8_t pri_bus;
uint8_t sec_bus;