summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-10-13 12:03:54 -0400
committerIan Moffett <ian@osmora.org>2025-10-13 12:03:54 -0400
commit155a36650ba5e207269d312c9a3d97b351fa5026 (patch)
tree51c49e9a2148e1ad3fcb7425447196907ffaa2fb /src
parentda018f309810ff6a232244c5d4bcc60644316029 (diff)
kern: pci: Parse PCI capability list per device
This allows us to see what kind of capabilities the device has (e.g., MSI/MSI-X) Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'src')
-rw-r--r--src/sys/include/io/pci/pci.h6
-rw-r--r--src/sys/io/pci/pci.c66
2 files changed, 71 insertions, 1 deletions
diff --git a/src/sys/include/io/pci/pci.h b/src/sys/include/io/pci/pci.h
index e6d0a24..27d3787 100644
--- a/src/sys/include/io/pci/pci.h
+++ b/src/sys/include/io/pci/pci.h
@@ -82,6 +82,9 @@ typedef uint32_t pcival_t;
* @vendor: Vendor ID
* @device: Device ID
* @bar: Base address registers
+ * @caplist: Set if a capability list is implemented
+ * @msix: Device implements MSI-X
+ * @msi: Device implements MSI
*/
struct pci_device {
uint16_t bus;
@@ -93,6 +96,9 @@ struct pci_device {
uint16_t vendor;
uint16_t device;
uint32_t bar[6];
+ uint8_t caplist : 1;
+ uint8_t msix : 1;
+ uint8_t msi : 1;
TAILQ_ENTRY(pci_device) link;
};
diff --git a/src/sys/io/pci/pci.c b/src/sys/io/pci/pci.c
index 2258d52..cc14f78 100644
--- a/src/sys/io/pci/pci.c
+++ b/src/sys/io/pci/pci.c
@@ -148,6 +148,54 @@ pci_csi_match(struct pci_device *csa, struct pci_device *csb)
}
/*
+ * Use by pci_read_caplist() to compare a value against
+ * known capabilities
+ */
+static void
+pci_check_cap(struct pci_device *dev, uint32_t cap)
+{
+ uint8_t id;
+
+ if (dev == NULL) {
+ return;
+ }
+
+ id = cap & 0xFF;
+ switch (id) {
+ case 0x11: /* MSI-X */
+ dev->msix = 1;
+ break;
+ case 0x05:
+ dev->msi = 1;
+ break;
+ }
+}
+
+/*
+ * Parse a capability list of a PCI device
+ *
+ * @dev: Device of capability list to parse
+ */
+static int
+pci_read_caplist(struct pci_device *dev)
+{
+ uint32_t cap;
+ uint8_t cap_ptr;
+
+ if (dev == NULL) {
+ return -EINVAL;
+ }
+
+ cap_ptr = pci_readl(dev, PCIREG_CAPPTR) & 0xFF;
+ do {
+ cap = pci_readl(dev, cap_ptr);
+ pci_check_cap(dev, cap);
+ cap_ptr = (cap >> 8) & 0xFF;
+ } while (cap_ptr != 0);
+ return 0;
+}
+
+/*
* Attempt to register a PCI device and bail
* if it doesn't exist on the bus.
*/
@@ -156,11 +204,12 @@ pci_register_dev(struct pci_device *dev)
{
struct pci_device *devp;
pcireg_t vend_dev;
- uint32_t classrev;
+ uint32_t classrev, cmdstatus;
uint8_t class, subclass;
uint8_t prog_if;
uint16_t device_id;
uint16_t vendor_id;
+ uint16_t status;
if (dev == NULL) {
return;
@@ -177,6 +226,10 @@ pci_register_dev(struct pci_device *dev)
subclass = PCIREG_SUBCLASS(classrev);
prog_if = PCIREG_PROGIF(classrev);
+ /* Get the status */
+ cmdstatus = pci_readl(dev, PCIREG_CMDSTATUS);
+ status = PCIREG_STATUS(cmdstatus);
+
/* Does this device exist? */
if (vendor_id == 0xFFFF) {
return;
@@ -187,6 +240,17 @@ pci_register_dev(struct pci_device *dev)
dev->class = class;
dev->subclass = subclass;
dev->prog_if = prog_if;
+ dev->caplist = (status >> 4) & 1;
+ dev->msix = 0;
+ dev->msi = 0;
+
+ /*
+ * Determine the device capabilities by parsing the
+ * capability list if supported.
+ */
+ if (dev->caplist) {
+ pci_read_caplist(dev);
+ }
/* Set up base address registers */
dev->bar[0] = pci_readl(dev, PCIREG_BAR0);