@@ -7,6 +7,74 @@
#include "pci.h"
#include "asm/pci.h"
+typedef void (*pci_cap_handler)(struct pci_dev *dev, int cap_offset);
+
+static void pci_cap_msi_handler(struct pci_dev *dev, int cap_offset)
+{
+ printf("Detected MSI for device 0x%x offset 0x%x\n",
+ dev->bdf, cap_offset);
+ dev->msi_offset = cap_offset;
+}
+
+static pci_cap_handler cap_handlers[PCI_CAP_ID_MAX + 1] = {
+ [PCI_CAP_ID_MSI] = pci_cap_msi_handler,
+};
+
+void pci_cap_walk(struct pci_dev *dev)
+{
+ uint8_t cap_offset;
+ uint8_t cap_id;
+ int count = 0;
+
+ cap_offset = pci_config_readb(dev->bdf, PCI_CAPABILITY_LIST);
+ while (cap_offset) {
+ cap_id = pci_config_readb(dev->bdf, cap_offset);
+ printf("PCI detected cap 0x%x\n", cap_id);
+ assert(cap_id < PCI_CAP_ID_MAX + 1);
+ if (cap_handlers[cap_id])
+ cap_handlers[cap_id](dev, cap_offset);
+ cap_offset = pci_config_readb(dev->bdf, cap_offset + 1);
+ /* Avoid dead loop during cap walk */
+ assert(++count <= 255);
+ }
+}
+
+bool pci_setup_msi(struct pci_dev *dev, uint64_t msi_addr, uint32_t msi_data)
+{
+ uint16_t msi_control;
+ uint16_t offset;
+ pcidevaddr_t addr;
+
+ assert(dev);
+
+ if (!dev->msi_offset) {
+ printf("MSI: dev 0x%x does not support MSI.\n", dev->bdf);
+ return false;
+ }
+
+ addr = dev->bdf;
+ offset = dev->msi_offset;
+ msi_control = pci_config_readw(addr, offset + PCI_MSI_FLAGS);
+ pci_config_writel(addr, offset + PCI_MSI_ADDRESS_LO,
+ msi_addr & 0xffffffff);
+
+ if (msi_control & PCI_MSI_FLAGS_64BIT) {
+ pci_config_writel(addr, offset + PCI_MSI_ADDRESS_HI,
+ (uint32_t)(msi_addr >> 32));
+ pci_config_writel(addr, offset + PCI_MSI_DATA_64, msi_data);
+ printf("MSI: dev 0x%x init 64bit address: ", addr);
+ } else {
+ pci_config_writel(addr, offset + PCI_MSI_DATA_32, msi_data);
+ printf("MSI: dev 0x%x init 32bit address: ", addr);
+ }
+ printf("addr=0x%lx, data=0x%x\n", msi_addr, msi_data);
+
+ msi_control |= PCI_MSI_FLAGS_ENABLE;
+ pci_config_writew(addr, offset + PCI_MSI_FLAGS, msi_control);
+
+ return true;
+}
+
void pci_cmd_set_clr(struct pci_dev *dev, uint16_t set, uint16_t clr)
{
uint16_t val = pci_config_readw(dev->bdf, PCI_COMMAND);
@@ -260,4 +328,5 @@ void pci_enable_defaults(struct pci_dev *dev)
pci_scan_bars(dev);
/* Enable device DMA operations */
pci_cmd_set_clr(dev, PCI_COMMAND_MASTER, 0);
+ pci_cap_walk(dev);
}
@@ -23,13 +23,17 @@ enum {
struct pci_dev {
uint16_t bdf;
+ uint16_t msi_offset;
phys_addr_t resource[PCI_BAR_NUM];
};
extern void pci_dev_init(struct pci_dev *dev, pcidevaddr_t bdf);
extern void pci_scan_bars(struct pci_dev *dev);
extern void pci_cmd_set_clr(struct pci_dev *dev, uint16_t set, uint16_t clr);
+extern void pci_cap_walk(struct pci_dev *dev);
extern void pci_enable_defaults(struct pci_dev *dev);
+extern bool pci_setup_msi(struct pci_dev *dev, uint64_t msi_addr,
+ uint32_t msi_data);
typedef phys_addr_t iova_t;