msi.c
来自「linux 内核源代码」· C语言 代码 · 共 732 行 · 第 1/2 页
C
732 行
* single MSI-X irq. A return of zero indicates the successful setup of * requested MSI-X entries with allocated irqs or non-zero for otherwise. **/static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec){ struct msi_desc *entry; int pos, i, j, nr_entries, ret; unsigned long phys_addr; u32 table_offset; u16 control; u8 bir; void __iomem *base; msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */ pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); /* Request & Map MSI-X table region */ pci_read_config_word(dev, msi_control_reg(pos), &control); nr_entries = multi_msix_capable(control); pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK); table_offset &= ~PCI_MSIX_FLAGS_BIRMASK; phys_addr = pci_resource_start (dev, bir) + table_offset; base = ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); if (base == NULL) return -ENOMEM; /* MSI-X Table Initialization */ for (i = 0; i < nvec; i++) { entry = alloc_msi_entry(); if (!entry) break; j = entries[i].entry; entry->msi_attrib.type = PCI_CAP_ID_MSIX; entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = j; entry->msi_attrib.maskbit = 1; entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; entry->msi_attrib.pos = pos; entry->dev = dev; entry->mask_base = base; list_add_tail(&entry->list, &dev->msi_list); } ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); if (ret) { int avail = 0; list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq != 0) { avail++; } } msi_free_irqs(dev); /* If we had some success report the number of irqs * we succeeded in setting up. */ if (avail == 0) avail = ret; return avail; } i = 0; list_for_each_entry(entry, &dev->msi_list, list) { entries[i].vector = entry->irq; set_irq_msi(entry->irq, entry); i++; } /* Set MSI-X enabled bits */ pci_intx_for_msi(dev, 0); msix_set_enable(dev, 1); dev->msix_enabled = 1; return 0;}/** * pci_msi_check_device - check whether MSI may be enabled on a device * @dev: pointer to the pci_dev data structure of MSI device function * @nvec: how many MSIs have been requested ? * @type: are we checking for MSI or MSI-X ? * * Look at global flags, the device itself, and its parent busses * to determine if MSI/-X are supported for the device. If MSI/-X is * supported return 0, else return an error code. **/static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type){ struct pci_bus *bus; int ret; /* MSI must be globally enabled and supported by the device */ if (!pci_msi_enable || !dev || dev->no_msi) return -EINVAL; /* * You can't ask to have 0 or less MSIs configured. * a) it's stupid .. * b) the list manipulation code assumes nvec >= 1. */ if (nvec < 1) return -ERANGE; /* Any bridge which does NOT route MSI transactions from it's * secondary bus to it's primary bus must set NO_MSI flag on * the secondary pci_bus. * We expect only arch-specific PCI host bus controller driver * or quirks for specific PCI bridges to be setting NO_MSI. */ for (bus = dev->bus; bus; bus = bus->parent) if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) return -EINVAL; ret = arch_msi_check_device(dev, nvec, type); if (ret) return ret; if (!pci_find_capability(dev, type)) return -EINVAL; return 0;}/** * pci_enable_msi - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * * Setup the MSI capability structure of device function with * a single MSI irq upon its software driver call to request for * MSI mode enabled on its hardware device function. A return of zero * indicates the successful setup of an entry zero with the new MSI * irq or non-zero for otherwise. **/int pci_enable_msi(struct pci_dev* dev){ int status; status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI); if (status) return status; WARN_ON(!!dev->msi_enabled); /* Check whether driver already requested for MSI-X irqs */ if (dev->msix_enabled) { printk(KERN_INFO "PCI: %s: Can't enable MSI. " "Device already has MSI-X enabled\n", pci_name(dev)); return -EINVAL; } status = msi_capability_init(dev); return status;}EXPORT_SYMBOL(pci_enable_msi);void pci_disable_msi(struct pci_dev* dev){ struct msi_desc *entry; int default_irq; if (!pci_msi_enable || !dev || !dev->msi_enabled) return; msi_set_enable(dev, 0); pci_intx_for_msi(dev, 1); dev->msi_enabled = 0; BUG_ON(list_empty(&dev->msi_list)); entry = list_entry(dev->msi_list.next, struct msi_desc, list); if (!entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) { return; } default_irq = entry->msi_attrib.default_irq; msi_free_irqs(dev); /* Restore dev->irq to its default pin-assertion irq */ dev->irq = default_irq;}EXPORT_SYMBOL(pci_disable_msi);static int msi_free_irqs(struct pci_dev* dev){ struct msi_desc *entry, *tmp; list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq) BUG_ON(irq_has_action(entry->irq)); } arch_teardown_msi_irqs(dev); list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { if (entry->msi_attrib.type == PCI_CAP_ID_MSIX) { writel(1, entry->mask_base + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); if (list_is_last(&entry->list, &dev->msi_list)) iounmap(entry->mask_base); } list_del(&entry->list); kfree(entry); } return 0;}/** * pci_enable_msix - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of MSI-X entries * @nvec: number of MSI-X irqs requested for allocation by device driver * * Setup the MSI-X capability structure of device function with the number * of requested irqs upon its software driver call to request for * MSI-X mode enabled on its hardware device function. A return of zero * indicates the successful configuration of MSI-X capability structure * with new allocated MSI-X irqs. A return of < 0 indicates a failure. * Or a return of > 0 indicates that driver request is exceeding the number * of irqs available. Driver should use the returned value to re-send * its request. **/int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec){ int status, pos, nr_entries; int i, j; u16 control; if (!entries) return -EINVAL; status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX); if (status) return status; pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); pci_read_config_word(dev, msi_control_reg(pos), &control); nr_entries = multi_msix_capable(control); if (nvec > nr_entries) return -EINVAL; /* Check for any invalid entries */ for (i = 0; i < nvec; i++) { if (entries[i].entry >= nr_entries) return -EINVAL; /* invalid entry */ for (j = i + 1; j < nvec; j++) { if (entries[i].entry == entries[j].entry) return -EINVAL; /* duplicate entry */ } } WARN_ON(!!dev->msix_enabled); /* Check whether driver already requested for MSI irq */ if (dev->msi_enabled) { printk(KERN_INFO "PCI: %s: Can't enable MSI-X. " "Device already has an MSI irq assigned\n", pci_name(dev)); return -EINVAL; } status = msix_capability_init(dev, entries, nvec); return status;}EXPORT_SYMBOL(pci_enable_msix);static void msix_free_all_irqs(struct pci_dev *dev){ msi_free_irqs(dev);}void pci_disable_msix(struct pci_dev* dev){ if (!pci_msi_enable || !dev || !dev->msix_enabled) return; msix_set_enable(dev, 0); pci_intx_for_msi(dev, 1); dev->msix_enabled = 0; msix_free_all_irqs(dev);}EXPORT_SYMBOL(pci_disable_msix);/** * msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state * @dev: pointer to the pci_dev data structure of MSI(X) device function * * Being called during hotplug remove, from which the device function * is hot-removed. All previous assigned MSI/MSI-X irqs, if * allocated for this device function, are reclaimed to unused state, * which may be used later on. **/void msi_remove_pci_irq_vectors(struct pci_dev* dev){ if (!pci_msi_enable || !dev) return; if (dev->msi_enabled) msi_free_irqs(dev); if (dev->msix_enabled) msix_free_all_irqs(dev);}void pci_no_msi(void){ pci_msi_enable = 0;}void pci_msi_init_pci_dev(struct pci_dev *dev){ INIT_LIST_HEAD(&dev->msi_list);}/* Arch hooks */int __attribute__ ((weak))arch_msi_check_device(struct pci_dev* dev, int nvec, int type){ return 0;}int __attribute__ ((weak))arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *entry){ return 0;}int __attribute__ ((weak))arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type){ struct msi_desc *entry; int ret; list_for_each_entry(entry, &dev->msi_list, list) { ret = arch_setup_msi_irq(dev, entry); if (ret) return ret; } return 0;}void __attribute__ ((weak)) arch_teardown_msi_irq(unsigned int irq){ return;}void __attribute__ ((weak))arch_teardown_msi_irqs(struct pci_dev *dev){ struct msi_desc *entry; list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq != 0) arch_teardown_msi_irq(entry->irq); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?