summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-07-11 01:30:14 -0400
committerIan Moffett <ian@osmora.org>2024-07-11 01:30:14 -0400
commitbf9ad82ee961b839654e2322f083c13532f2c0c6 (patch)
tree09db2648a09e100f5027132d93e12c938a530cf9
parent1d258c4bfeaab845b250006ed8bf3df6776b5c11 (diff)
kernel/amd64: pci: Add support for PCI MSI-X
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r--sys/arch/amd64/pci/pci_machdep.c65
-rw-r--r--sys/dev/pci/pci.c43
-rw-r--r--sys/include/dev/pci/pci.h8
-rw-r--r--sys/include/dev/pci/pciregs.h5
4 files changed, 121 insertions, 0 deletions
diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c
index 50d0d57..ea34b25 100644
--- a/sys/arch/amd64/pci/pci_machdep.c
+++ b/sys/arch/amd64/pci/pci_machdep.c
@@ -30,10 +30,14 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
+#include <sys/mmio.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
#include <machine/pio.h>
#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/idt.h>
/* Base address masks for BARs */
#define PCI_BAR_MEMMASK ~7
@@ -121,3 +125,64 @@ pci_map_bar(struct pci_device *dev, uint8_t barno, void **vap)
bar = dev->bar[barno] & PCI_BAR_MEMMASK;
return bus_map(bar, size, 0, vap);
}
+
+/*
+ * Enable MSI-X for a device and allocate an
+ * interrupt vector.
+ *
+ * @dev: Device to enable MSI-X for.
+ * @intr: MSI-X interrupt descriptor.
+ */
+int
+pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr)
+{
+ volatile uint64_t *tbl;
+ struct cpu_info *ci;
+ uint32_t data, msg_ctl;
+ uint64_t msg_addr, tmp;
+ uint16_t tbl_off;
+ uint8_t bir;
+ uint8_t vector;
+
+ if (dev->msix_capoff == 0)
+ return -ENOTSUP;
+
+ /* Get the data from cap offset 0x04 */
+ data = pci_readl(dev, (dev->msix_capoff + 0x04));
+ bir = data & 3;
+ tbl_off = data & ~3;
+
+ ci = this_cpu();
+ msg_addr = (0xFEE00000 | (ci->apicid << 12));
+
+ /* Calculate the start of the message table */
+ tbl = (void *)((dev->bar[bir] & PCI_BAR_MEMMASK) + MMIO_OFFSET);
+ tbl = (void *)((char *)tbl + tbl_off);
+
+ /* Get the vector and setup handler */
+ vector = intr_alloc_vector(intr->name, IPL_BIO);
+ idt_set_desc(vector, IDT_INT_GATE, ISR(intr->handler), 0);
+
+ /*
+ * Setup the message data at bits 95:64 of the message
+ * table by ORing the interrupt vector to it. We also
+ * unmask the interrupt with bit 1 of the vector control.
+ */
+ tmp = mmio_read64(&tbl[1]);
+ tmp |= vector;
+ tmp &= ~BIT(32);
+
+ /* Write the message table */
+ mmio_write64(&tbl[0], msg_addr);
+ mmio_write64(&tbl[1], tmp);
+
+ /*
+ * Set bit 16 of message control to enable MSI-X.
+ * Message control lives at cap offset 0x00 in bits
+ * 31:16.
+ */
+ msg_ctl = pci_readl(dev, dev->msix_capoff);
+ msg_ctl |= BIT(31);
+ pci_writel(dev, dev->msix_capoff, msg_ctl);
+ return 0;
+}
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 587881a..65f315d 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -30,6 +30,7 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/syslog.h>
+#include <sys/errno.h>
#include <dev/pci/pci.h>
#include <dev/pci/pciregs.h>
#include <vm/dynalloc.h>
@@ -58,6 +59,47 @@ pci_dev_exists(uint8_t bus, uint8_t slot, uint8_t func)
}
/*
+ * Attempt to search for a capability within the device
+ * capability list if it supports one.
+ *
+ * @dev: Target device.
+ * @id: Requested capability ID.
+ *
+ * The offset is returned if found, otherwise 0.
+ * A value less than zero is returned on error.
+ */
+static int
+pci_get_cap(struct pci_device *dev, uint8_t id)
+{
+ uint16_t status;
+ uint32_t cap;
+ uint8_t curid;
+ uint8_t cap_ptr;
+
+ /* Does the device even support this? */
+ status = pci_readl(dev, PCIREG_CMDSTATUS) >> 16;
+ if (!ISSET(status, PCI_STATUS_CAPLIST)) {
+ return -ENOTSUP;
+ }
+
+ cap_ptr = pci_readl(dev, PCIREG_CAPPTR) & 0xFF;
+
+ /* Go through the capability list */
+ while (cap_ptr != 0) {
+ cap = pci_readl(dev, cap_ptr);
+ curid = cap & 0xFF;
+
+ if (curid == id) {
+ return cap_ptr;
+ }
+
+ cap_ptr = (cap >> 8) & 0xFF;
+ }
+
+ return 0;
+}
+
+/*
* Sets other device information (device id, vendor id, etc)
*
* @dev: Device descriptor to set up.
@@ -85,6 +127,7 @@ pci_set_device_info(struct pci_device *dev)
dev->bar[5] = pci_readl(dev, PCIREG_BAR5);
dev->irq_line = pci_readl(dev, PCIREG_IRQLINE) & 0xFF;
+ dev->msix_capoff = pci_get_cap(dev, PCI_CAP_MSIX);
}
/*
diff --git a/sys/include/dev/pci/pci.h b/sys/include/dev/pci/pci.h
index 3340e1a..497bfc7 100644
--- a/sys/include/dev/pci/pci.h
+++ b/sys/include/dev/pci/pci.h
@@ -54,6 +54,7 @@ struct pci_device {
uint8_t slot;
uint8_t func;
+ uint16_t msix_capoff;
uint16_t device_id;
uint16_t vendor_id;
uint8_t pci_class;
@@ -64,11 +65,18 @@ struct pci_device {
TAILQ_ENTRY(pci_device) link;
};
+struct msi_intr {
+ const char *name;
+ void(*handler)(void *);
+};
+
pcireg_t pci_readl(struct pci_device *dev, uint32_t offset);
struct pci_device *pci_get_device(struct pci_lookup lookup, uint16_t lookup_type);
int pci_map_bar(struct pci_device *dev, uint8_t barno, void **vap);
void pci_writel(struct pci_device *dev, uint32_t offset, pcireg_t val);
+
+int pci_enable_msix(struct pci_device *dev, const struct msi_intr *intr);
int pci_init(void);
#endif /* !_PCI_H_ */
diff --git a/sys/include/dev/pci/pciregs.h b/sys/include/dev/pci/pciregs.h
index c1b02e9..763fade 100644
--- a/sys/include/dev/pci/pciregs.h
+++ b/sys/include/dev/pci/pciregs.h
@@ -42,6 +42,7 @@
#define PCIREG_BAR4 0x20 /* 32 bits */
#define PCIREG_BAR5 0x24 /* 32 bits */
#define PCIREG_IRQLINE 0x3C /* 8 bits */
+#define PCIREG_CAPPTR 0x34 /* 8 bits */
#define PCIREG_CMDSTATUS 0x04 /* command (15:0), status (31:16) */
/* Macros to extract PCIREG_CLASSREV bits */
@@ -64,4 +65,8 @@
#define PCI_STATUS_CAPLIST BIT(4)
#define PCI_STATUS_66MHZ BIT(5)
+/* Capability IDs */
+#define PCI_CAP_MSI 0x05
+#define PCI_CAP_MSIX 0x11
+
#endif /* _PCI_PCIREGS_H_ */