From 4e2f95b789d8f3b505f52c42b634401dd5f718a2 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 16 May 2018 14:56:22 +0200 Subject: device/pci: Add MSI-X helper functions Basic PCI MSI-X table helper functions. Imported from GNU/Linux kernel PCI subsystem. To be used on Cavium to configure MSI-X tables. Change-Id: I94413712e7986efd17e6b11ba59f6eb390384c8c Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/26329 Reviewed-by: Philipp Deppenwiese Tested-by: build bot (Jenkins) --- src/device/pci_device.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ src/include/device/pci.h | 18 ++++++++++++ src/include/device/pci_def.h | 12 ++++++++ 3 files changed, 98 insertions(+) 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 @@ -337,6 +337,74 @@ static void pci_get_rom_resource(struct device *dev, unsigned long index) compact_resources(dev); } +/** + * 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. * 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 */ -- cgit v1.2.3