summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/device/pci_device.c68
-rw-r--r--src/include/device/pci.h18
-rw-r--r--src/include/device/pci_def.h12
3 files changed, 98 insertions, 0 deletions
diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index c18f529fbe..593838909c 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -338,6 +338,74 @@ static void pci_get_rom_resource(struct device *dev, unsigned long index)
}
/**
+ * Given a device, read the size of the MSI-X table.
+ *
+ * @param dev Pointer to the device structure.
+ * @return MSI-X table size or 0 if not MSI-X capable device
+ */
+size_t pci_msix_table_size(struct device *dev)
+{
+ const size_t pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+ if (!pos)
+ return 0;
+
+ const u16 control = pci_read_config16(dev, pos + PCI_MSIX_FLAGS);
+ return (control & PCI_MSIX_FLAGS_QSIZE) + 1;
+}
+
+/**
+ * Given a device, return the table offset and bar the MSI-X tables resides in.
+ *
+ * @param dev Pointer to the device structure.
+ * @param offset Returned value gives the offset in bytes inside the PCI BAR.
+ * @param idx The returned value is the index of the PCI_BASE_ADDRESS register
+ * the MSI-X table is located in.
+ * @return Zero on success
+ */
+int pci_msix_table_bar(struct device *dev, u32 *offset, u8 *idx)
+{
+ const size_t pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+ if (!pos || !offset || !idx)
+ return 1;
+
+ *offset = pci_read_config32(dev, pos + PCI_MSIX_TABLE);
+ *idx = (u8)(*offset & PCI_MSIX_PBA_BIR);
+ *offset &= PCI_MSIX_PBA_OFFSET;
+
+ return 0;
+}
+
+/**
+ * Given a device, return a msix_entry pointer or NULL if no table was found.
+ *
+ * @param dev Pointer to the device structure.
+ *
+ * @return NULL on error
+ */
+struct msix_entry *pci_msix_get_table(struct device *dev)
+{
+ struct resource *res;
+ u32 offset;
+ u8 idx;
+
+ if (pci_msix_table_bar(dev, &offset, &idx))
+ return NULL;
+
+ if (idx > 5)
+ return NULL;
+
+ res = probe_resource(dev, idx * 4 + PCI_BASE_ADDRESS_0);
+ if (!res || !res->base || offset >= res->size)
+ return NULL;
+
+ if ((res->flags & IORESOURCE_PCI64) &&
+ (uintptr_t)res->base != res->base)
+ return NULL;
+
+ return (struct msix_entry *)((uintptr_t)res->base + offset);
+}
+
+/**
* Read the base address registers for a given device.
*
* @param dev Pointer to the dev structure.
diff --git a/src/include/device/pci.h b/src/include/device/pci.h
index 3cc2c64e4f..f1ab91bbc7 100644
--- a/src/include/device/pci.h
+++ b/src/include/device/pci.h
@@ -56,6 +56,20 @@ struct pci_driver {
const unsigned short *devices;
};
+struct msix_entry {
+ union {
+ struct {
+ u32 lower_addr;
+ u32 upper_addr;
+ };
+ struct {
+ u64 addr;
+ };
+ };
+ u32 data;
+ u32 vec_control;
+};
+
#ifdef __SIMPLE_DEVICE__
#define __pci_driver __attribute__((unused))
#else
@@ -104,6 +118,10 @@ void pci_assign_irqs(unsigned int bus, unsigned int slot,
const char *get_pci_class_name(struct device *dev);
const char *get_pci_subclass_name(struct device *dev);
+size_t pci_msix_table_size(struct device *dev);
+int pci_msix_table_bar(struct device *dev, u32 *offset, u8 *idx);
+struct msix_entry *pci_msix_get_table(struct device *dev);
+
#define PCI_IO_BRIDGE_ALIGN 4096
#define PCI_MEM_BRIDGE_ALIGN (1024*1024)
diff --git a/src/include/device/pci_def.h b/src/include/device/pci_def.h
index 60d913204a..f9ce1a6382 100644
--- a/src/include/device/pci_def.h
+++ b/src/include/device/pci_def.h
@@ -292,6 +292,18 @@
#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
#define PCI_MSI_MASK_BIT 16 /* Mask bits register */
+/* MSI-X registers */
+#define PCI_MSIX_FLAGS 2
+#define PCI_MSIX_FLAGS_QSIZE 0x7FF /* table size */
+#define PCI_MSIX_FLAGS_MASKALL 0x4000 /* Mask all vectors for this function */
+#define PCI_MSIX_FLAGS_ENABLE 0x8000 /* MSI-X enable */
+#define PCI_MSIX_TABLE 4 /* Table offset */
+#define PCI_MSIX_PBA 8 /* Pending Bit Array offset */
+#define PCI_MSIX_PBA_BIR 0x7 /* BAR index */
+#define PCI_MSIX_PBA_OFFSET ~0x7 /* Offset into specified BAR */
+#define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */
+
+
/* CompactPCI Hotswap Register */
#define PCI_CHSWP_CSR 2 /* Control and Status Register */