162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI Message Signaled Interrupt (MSI) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003-2004 Intel 662306a36Sopenharmony_ci * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) 762306a36Sopenharmony_ci * Copyright (C) 2016 Christoph Hellwig. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/irq.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "../pci.h" 1462306a36Sopenharmony_ci#include "msi.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciint pci_msi_enable = 1; 1762306a36Sopenharmony_ciint pci_msi_ignore_mask; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * pci_msi_supported - check whether MSI may be enabled on a device 2162306a36Sopenharmony_ci * @dev: pointer to the pci_dev data structure of MSI device function 2262306a36Sopenharmony_ci * @nvec: how many MSIs have been requested? 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Look at global flags, the device itself, and its parent buses 2562306a36Sopenharmony_ci * to determine if MSI/-X are supported for the device. If MSI/-X is 2662306a36Sopenharmony_ci * supported return 1, else return 0. 2762306a36Sopenharmony_ci **/ 2862306a36Sopenharmony_cistatic int pci_msi_supported(struct pci_dev *dev, int nvec) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct pci_bus *bus; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* MSI must be globally enabled and supported by the device */ 3362306a36Sopenharmony_ci if (!pci_msi_enable) 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (!dev || dev->no_msi) 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * You can't ask to have 0 or less MSIs configured. 4162306a36Sopenharmony_ci * a) it's stupid .. 4262306a36Sopenharmony_ci * b) the list manipulation code assumes nvec >= 1. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci if (nvec < 1) 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * Any bridge which does NOT route MSI transactions from its 4962306a36Sopenharmony_ci * secondary bus to its primary bus must set NO_MSI flag on 5062306a36Sopenharmony_ci * the secondary pci_bus. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * The NO_MSI flag can either be set directly by: 5362306a36Sopenharmony_ci * - arch-specific PCI host bus controller drivers (deprecated) 5462306a36Sopenharmony_ci * - quirks for specific PCI bridges 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * or indirectly by platform-specific PCI host bridge drivers by 5762306a36Sopenharmony_ci * advertising the 'msi_domain' property, which results in 5862306a36Sopenharmony_ci * the NO_MSI flag when no MSI domain is found for this bridge 5962306a36Sopenharmony_ci * at probe time. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci for (bus = dev->bus; bus; bus = bus->parent) 6262306a36Sopenharmony_ci if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 1; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void pcim_msi_release(void *pcidev) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct pci_dev *dev = pcidev; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci dev->is_msi_managed = false; 7362306a36Sopenharmony_ci pci_free_irq_vectors(dev); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * Needs to be separate from pcim_release to prevent an ordering problem 7862306a36Sopenharmony_ci * vs. msi_device_data_release() in the MSI core code. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic int pcim_setup_msi_release(struct pci_dev *dev) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci int ret; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!pci_is_managed(dev) || dev->is_msi_managed) 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = devm_add_action(&dev->dev, pcim_msi_release, dev); 8862306a36Sopenharmony_ci if (!ret) 8962306a36Sopenharmony_ci dev->is_msi_managed = true; 9062306a36Sopenharmony_ci return ret; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * Ordering vs. devres: msi device data has to be installed first so that 9562306a36Sopenharmony_ci * pcim_msi_release() is invoked before it on device release. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic int pci_setup_msi_context(struct pci_dev *dev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci int ret = msi_setup_device_data(&dev->dev); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!ret) 10262306a36Sopenharmony_ci ret = pcim_setup_msi_release(dev); 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Helper functions for mask/unmask and MSI message handling 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_civoid pci_msi_update_mask(struct msi_desc *desc, u32 clear, u32 set) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci raw_spinlock_t *lock = &to_pci_dev(desc->dev)->msi_lock; 11362306a36Sopenharmony_ci unsigned long flags; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!desc->pci.msi_attrib.can_mask) 11662306a36Sopenharmony_ci return; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 11962306a36Sopenharmony_ci desc->pci.msi_mask &= ~clear; 12062306a36Sopenharmony_ci desc->pci.msi_mask |= set; 12162306a36Sopenharmony_ci pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->pci.mask_pos, 12262306a36Sopenharmony_ci desc->pci.msi_mask); 12362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/** 12762306a36Sopenharmony_ci * pci_msi_mask_irq - Generic IRQ chip callback to mask PCI/MSI interrupts 12862306a36Sopenharmony_ci * @data: pointer to irqdata associated to that interrupt 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_civoid pci_msi_mask_irq(struct irq_data *data) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct msi_desc *desc = irq_data_get_msi_desc(data); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci __pci_msi_mask_desc(desc, BIT(data->irq - desc->irq)); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_msi_mask_irq); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * pci_msi_unmask_irq - Generic IRQ chip callback to unmask PCI/MSI interrupts 14062306a36Sopenharmony_ci * @data: pointer to irqdata associated to that interrupt 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_civoid pci_msi_unmask_irq(struct irq_data *data) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct msi_desc *desc = irq_data_get_msi_desc(data); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci __pci_msi_unmask_desc(desc, BIT(data->irq - desc->irq)); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_msi_unmask_irq); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_civoid __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct pci_dev *dev = msi_desc_to_pci_dev(entry); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci BUG_ON(dev->current_state != PCI_D0); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (entry->pci.msi_attrib.is_msix) { 15762306a36Sopenharmony_ci void __iomem *base = pci_msix_desc_addr(entry); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (WARN_ON_ONCE(entry->pci.msi_attrib.is_virtual)) 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR); 16362306a36Sopenharmony_ci msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR); 16462306a36Sopenharmony_ci msg->data = readl(base + PCI_MSIX_ENTRY_DATA); 16562306a36Sopenharmony_ci } else { 16662306a36Sopenharmony_ci int pos = dev->msi_cap; 16762306a36Sopenharmony_ci u16 data; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, 17062306a36Sopenharmony_ci &msg->address_lo); 17162306a36Sopenharmony_ci if (entry->pci.msi_attrib.is_64) { 17262306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, 17362306a36Sopenharmony_ci &msg->address_hi); 17462306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_MSI_DATA_64, &data); 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci msg->address_hi = 0; 17762306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_MSI_DATA_32, &data); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci msg->data = data; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline void pci_write_msg_msi(struct pci_dev *dev, struct msi_desc *desc, 18462306a36Sopenharmony_ci struct msi_msg *msg) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int pos = dev->msi_cap; 18762306a36Sopenharmony_ci u16 msgctl; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); 19062306a36Sopenharmony_ci msgctl &= ~PCI_MSI_FLAGS_QSIZE; 19162306a36Sopenharmony_ci msgctl |= desc->pci.msi_attrib.multiple << 4; 19262306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, msg->address_lo); 19562306a36Sopenharmony_ci if (desc->pci.msi_attrib.is_64) { 19662306a36Sopenharmony_ci pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, msg->address_hi); 19762306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_MSI_DATA_64, msg->data); 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci pci_write_config_word(dev, pos + PCI_MSI_DATA_32, msg->data); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci /* Ensure that the writes are visible in the device */ 20262306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic inline void pci_write_msg_msix(struct msi_desc *desc, struct msi_msg *msg) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci void __iomem *base = pci_msix_desc_addr(desc); 20862306a36Sopenharmony_ci u32 ctrl = desc->pci.msix_ctrl; 20962306a36Sopenharmony_ci bool unmasked = !(ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (desc->pci.msi_attrib.is_virtual) 21262306a36Sopenharmony_ci return; 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * The specification mandates that the entry is masked 21562306a36Sopenharmony_ci * when the message is modified: 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * "If software changes the Address or Data value of an 21862306a36Sopenharmony_ci * entry while the entry is unmasked, the result is 21962306a36Sopenharmony_ci * undefined." 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci if (unmasked) 22262306a36Sopenharmony_ci pci_msix_write_vector_ctrl(desc, ctrl | PCI_MSIX_ENTRY_CTRL_MASKBIT); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR); 22562306a36Sopenharmony_ci writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR); 22662306a36Sopenharmony_ci writel(msg->data, base + PCI_MSIX_ENTRY_DATA); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (unmasked) 22962306a36Sopenharmony_ci pci_msix_write_vector_ctrl(desc, ctrl); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Ensure that the writes are visible in the device */ 23262306a36Sopenharmony_ci readl(base + PCI_MSIX_ENTRY_DATA); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_civoid __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct pci_dev *dev = msi_desc_to_pci_dev(entry); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (dev->current_state != PCI_D0 || pci_dev_is_disconnected(dev)) { 24062306a36Sopenharmony_ci /* Don't touch the hardware now */ 24162306a36Sopenharmony_ci } else if (entry->pci.msi_attrib.is_msix) { 24262306a36Sopenharmony_ci pci_write_msg_msix(entry, msg); 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci pci_write_msg_msi(dev, entry, msg); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci entry->msg = *msg; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (entry->write_msi_msg) 25062306a36Sopenharmony_ci entry->write_msi_msg(entry, entry->write_msi_msg_data); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_civoid pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct msi_desc *entry = irq_get_msi_desc(irq); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci __pci_write_msi_msg(entry, msg); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_write_msi_msg); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* PCI/MSI specific functionality */ 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void pci_intx_for_msi(struct pci_dev *dev, int enable) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG)) 26762306a36Sopenharmony_ci pci_intx(dev, enable); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void pci_msi_set_enable(struct pci_dev *dev, int enable) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci u16 control; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); 27562306a36Sopenharmony_ci control &= ~PCI_MSI_FLAGS_ENABLE; 27662306a36Sopenharmony_ci if (enable) 27762306a36Sopenharmony_ci control |= PCI_MSI_FLAGS_ENABLE; 27862306a36Sopenharmony_ci pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int msi_setup_msi_desc(struct pci_dev *dev, int nvec, 28262306a36Sopenharmony_ci struct irq_affinity_desc *masks) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct msi_desc desc; 28562306a36Sopenharmony_ci u16 control; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* MSI Entry Initialization */ 28862306a36Sopenharmony_ci memset(&desc, 0, sizeof(desc)); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); 29162306a36Sopenharmony_ci /* Lies, damned lies, and MSIs */ 29262306a36Sopenharmony_ci if (dev->dev_flags & PCI_DEV_FLAGS_HAS_MSI_MASKING) 29362306a36Sopenharmony_ci control |= PCI_MSI_FLAGS_MASKBIT; 29462306a36Sopenharmony_ci /* Respect XEN's mask disabling */ 29562306a36Sopenharmony_ci if (pci_msi_ignore_mask) 29662306a36Sopenharmony_ci control &= ~PCI_MSI_FLAGS_MASKBIT; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci desc.nvec_used = nvec; 29962306a36Sopenharmony_ci desc.pci.msi_attrib.is_64 = !!(control & PCI_MSI_FLAGS_64BIT); 30062306a36Sopenharmony_ci desc.pci.msi_attrib.can_mask = !!(control & PCI_MSI_FLAGS_MASKBIT); 30162306a36Sopenharmony_ci desc.pci.msi_attrib.default_irq = dev->irq; 30262306a36Sopenharmony_ci desc.pci.msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1; 30362306a36Sopenharmony_ci desc.pci.msi_attrib.multiple = ilog2(__roundup_pow_of_two(nvec)); 30462306a36Sopenharmony_ci desc.affinity = masks; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (control & PCI_MSI_FLAGS_64BIT) 30762306a36Sopenharmony_ci desc.pci.mask_pos = dev->msi_cap + PCI_MSI_MASK_64; 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci desc.pci.mask_pos = dev->msi_cap + PCI_MSI_MASK_32; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Save the initial mask status */ 31262306a36Sopenharmony_ci if (desc.pci.msi_attrib.can_mask) 31362306a36Sopenharmony_ci pci_read_config_dword(dev, desc.pci.mask_pos, &desc.pci.msi_mask); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return msi_insert_msi_desc(&dev->dev, &desc); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int msi_verify_entries(struct pci_dev *dev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct msi_desc *entry; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (!dev->no_64bit_msi) 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) { 32662306a36Sopenharmony_ci if (entry->msg.address_hi) { 32762306a36Sopenharmony_ci pci_err(dev, "arch assigned 64-bit MSI address %#x%08x but device only supports 32 bits\n", 32862306a36Sopenharmony_ci entry->msg.address_hi, entry->msg.address_lo); 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci return !entry ? 0 : -EIO; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/** 33662306a36Sopenharmony_ci * msi_capability_init - configure device's MSI capability structure 33762306a36Sopenharmony_ci * @dev: pointer to the pci_dev data structure of MSI device function 33862306a36Sopenharmony_ci * @nvec: number of interrupts to allocate 33962306a36Sopenharmony_ci * @affd: description of automatic IRQ affinity assignments (may be %NULL) 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * Setup the MSI capability structure of the device with the requested 34262306a36Sopenharmony_ci * number of interrupts. A return value of zero indicates the successful 34362306a36Sopenharmony_ci * setup of an entry with the new MSI IRQ. A negative return value indicates 34462306a36Sopenharmony_ci * an error, and a positive return value indicates the number of interrupts 34562306a36Sopenharmony_ci * which could have been allocated. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_cistatic int msi_capability_init(struct pci_dev *dev, int nvec, 34862306a36Sopenharmony_ci struct irq_affinity *affd) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct irq_affinity_desc *masks = NULL; 35162306a36Sopenharmony_ci struct msi_desc *entry; 35262306a36Sopenharmony_ci int ret; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Reject multi-MSI early on irq domain enabled architectures */ 35562306a36Sopenharmony_ci if (nvec > 1 && !pci_msi_domain_supports(dev, MSI_FLAG_MULTI_PCI_MSI, ALLOW_LEGACY)) 35662306a36Sopenharmony_ci return 1; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* 35962306a36Sopenharmony_ci * Disable MSI during setup in the hardware, but mark it enabled 36062306a36Sopenharmony_ci * so that setup code can evaluate it. 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci pci_msi_set_enable(dev, 0); 36362306a36Sopenharmony_ci dev->msi_enabled = 1; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (affd) 36662306a36Sopenharmony_ci masks = irq_create_affinity_masks(nvec, affd); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci msi_lock_descs(&dev->dev); 36962306a36Sopenharmony_ci ret = msi_setup_msi_desc(dev, nvec, masks); 37062306a36Sopenharmony_ci if (ret) 37162306a36Sopenharmony_ci goto fail; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* All MSIs are unmasked by default; mask them all */ 37462306a36Sopenharmony_ci entry = msi_first_desc(&dev->dev, MSI_DESC_ALL); 37562306a36Sopenharmony_ci pci_msi_mask(entry, msi_multi_mask(entry)); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Configure MSI capability structure */ 37862306a36Sopenharmony_ci ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); 37962306a36Sopenharmony_ci if (ret) 38062306a36Sopenharmony_ci goto err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = msi_verify_entries(dev); 38362306a36Sopenharmony_ci if (ret) 38462306a36Sopenharmony_ci goto err; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Set MSI enabled bits */ 38762306a36Sopenharmony_ci pci_intx_for_msi(dev, 0); 38862306a36Sopenharmony_ci pci_msi_set_enable(dev, 1); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci pcibios_free_irq(dev); 39162306a36Sopenharmony_ci dev->irq = entry->irq; 39262306a36Sopenharmony_ci goto unlock; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cierr: 39562306a36Sopenharmony_ci pci_msi_unmask(entry, msi_multi_mask(entry)); 39662306a36Sopenharmony_ci pci_free_msi_irqs(dev); 39762306a36Sopenharmony_cifail: 39862306a36Sopenharmony_ci dev->msi_enabled = 0; 39962306a36Sopenharmony_ciunlock: 40062306a36Sopenharmony_ci msi_unlock_descs(&dev->dev); 40162306a36Sopenharmony_ci kfree(masks); 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciint __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, 40662306a36Sopenharmony_ci struct irq_affinity *affd) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int nvec; 40962306a36Sopenharmony_ci int rc; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!pci_msi_supported(dev, minvec) || dev->current_state != PCI_D0) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Check whether driver already requested MSI-X IRQs */ 41562306a36Sopenharmony_ci if (dev->msix_enabled) { 41662306a36Sopenharmony_ci pci_info(dev, "can't enable MSI (MSI-X already enabled)\n"); 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (maxvec < minvec) 42162306a36Sopenharmony_ci return -ERANGE; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (WARN_ON_ONCE(dev->msi_enabled)) 42462306a36Sopenharmony_ci return -EINVAL; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci nvec = pci_msi_vec_count(dev); 42762306a36Sopenharmony_ci if (nvec < 0) 42862306a36Sopenharmony_ci return nvec; 42962306a36Sopenharmony_ci if (nvec < minvec) 43062306a36Sopenharmony_ci return -ENOSPC; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (nvec > maxvec) 43362306a36Sopenharmony_ci nvec = maxvec; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci rc = pci_setup_msi_context(dev); 43662306a36Sopenharmony_ci if (rc) 43762306a36Sopenharmony_ci return rc; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!pci_setup_msi_device_domain(dev)) 44062306a36Sopenharmony_ci return -ENODEV; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (;;) { 44362306a36Sopenharmony_ci if (affd) { 44462306a36Sopenharmony_ci nvec = irq_calc_affinity_vectors(minvec, nvec, affd); 44562306a36Sopenharmony_ci if (nvec < minvec) 44662306a36Sopenharmony_ci return -ENOSPC; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci rc = msi_capability_init(dev, nvec, affd); 45062306a36Sopenharmony_ci if (rc == 0) 45162306a36Sopenharmony_ci return nvec; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (rc < 0) 45462306a36Sopenharmony_ci return rc; 45562306a36Sopenharmony_ci if (rc < minvec) 45662306a36Sopenharmony_ci return -ENOSPC; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci nvec = rc; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci/** 46362306a36Sopenharmony_ci * pci_msi_vec_count - Return the number of MSI vectors a device can send 46462306a36Sopenharmony_ci * @dev: device to report about 46562306a36Sopenharmony_ci * 46662306a36Sopenharmony_ci * This function returns the number of MSI vectors a device requested via 46762306a36Sopenharmony_ci * Multiple Message Capable register. It returns a negative errno if the 46862306a36Sopenharmony_ci * device is not capable sending MSI interrupts. Otherwise, the call succeeds 46962306a36Sopenharmony_ci * and returns a power of two, up to a maximum of 2^5 (32), according to the 47062306a36Sopenharmony_ci * MSI specification. 47162306a36Sopenharmony_ci **/ 47262306a36Sopenharmony_ciint pci_msi_vec_count(struct pci_dev *dev) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci int ret; 47562306a36Sopenharmony_ci u16 msgctl; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!dev->msi_cap) 47862306a36Sopenharmony_ci return -EINVAL; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); 48162306a36Sopenharmony_ci ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return ret; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL(pci_msi_vec_count); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci/* 48862306a36Sopenharmony_ci * Architecture override returns true when the PCI MSI message should be 48962306a36Sopenharmony_ci * written by the generic restore function. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cibool __weak arch_restore_msi_irqs(struct pci_dev *dev) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci return true; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_civoid __pci_restore_msi_state(struct pci_dev *dev) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct msi_desc *entry; 49962306a36Sopenharmony_ci u16 control; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!dev->msi_enabled) 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci entry = irq_get_msi_desc(dev->irq); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci pci_intx_for_msi(dev, 0); 50762306a36Sopenharmony_ci pci_msi_set_enable(dev, 0); 50862306a36Sopenharmony_ci if (arch_restore_msi_irqs(dev)) 50962306a36Sopenharmony_ci __pci_write_msi_msg(entry, &entry->msg); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); 51262306a36Sopenharmony_ci pci_msi_update_mask(entry, 0, 0); 51362306a36Sopenharmony_ci control &= ~PCI_MSI_FLAGS_QSIZE; 51462306a36Sopenharmony_ci control |= (entry->pci.msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; 51562306a36Sopenharmony_ci pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_civoid pci_msi_shutdown(struct pci_dev *dev) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct msi_desc *desc; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (!pci_msi_enable || !dev || !dev->msi_enabled) 52362306a36Sopenharmony_ci return; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci pci_msi_set_enable(dev, 0); 52662306a36Sopenharmony_ci pci_intx_for_msi(dev, 1); 52762306a36Sopenharmony_ci dev->msi_enabled = 0; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Return the device with MSI unmasked as initial states */ 53062306a36Sopenharmony_ci desc = msi_first_desc(&dev->dev, MSI_DESC_ALL); 53162306a36Sopenharmony_ci if (!WARN_ON_ONCE(!desc)) 53262306a36Sopenharmony_ci pci_msi_unmask(desc, msi_multi_mask(desc)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Restore dev->irq to its default pin-assertion IRQ */ 53562306a36Sopenharmony_ci dev->irq = desc->pci.msi_attrib.default_irq; 53662306a36Sopenharmony_ci pcibios_alloc_irq(dev); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* PCI/MSI-X specific functionality */ 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci u16 ctrl; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl); 54662306a36Sopenharmony_ci ctrl &= ~clear; 54762306a36Sopenharmony_ci ctrl |= set; 54862306a36Sopenharmony_ci pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void __iomem *msix_map_region(struct pci_dev *dev, 55262306a36Sopenharmony_ci unsigned int nr_entries) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci resource_size_t phys_addr; 55562306a36Sopenharmony_ci u32 table_offset; 55662306a36Sopenharmony_ci unsigned long flags; 55762306a36Sopenharmony_ci u8 bir; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE, 56062306a36Sopenharmony_ci &table_offset); 56162306a36Sopenharmony_ci bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); 56262306a36Sopenharmony_ci flags = pci_resource_flags(dev, bir); 56362306a36Sopenharmony_ci if (!flags || (flags & IORESOURCE_UNSET)) 56462306a36Sopenharmony_ci return NULL; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci table_offset &= PCI_MSIX_TABLE_OFFSET; 56762306a36Sopenharmony_ci phys_addr = pci_resource_start(dev, bir) + table_offset; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return ioremap(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/** 57362306a36Sopenharmony_ci * msix_prepare_msi_desc - Prepare a half initialized MSI descriptor for operation 57462306a36Sopenharmony_ci * @dev: The PCI device for which the descriptor is prepared 57562306a36Sopenharmony_ci * @desc: The MSI descriptor for preparation 57662306a36Sopenharmony_ci * 57762306a36Sopenharmony_ci * This is separate from msix_setup_msi_descs() below to handle dynamic 57862306a36Sopenharmony_ci * allocations for MSI-X after initial enablement. 57962306a36Sopenharmony_ci * 58062306a36Sopenharmony_ci * Ideally the whole MSI-X setup would work that way, but there is no way to 58162306a36Sopenharmony_ci * support this for the legacy arch_setup_msi_irqs() mechanism and for the 58262306a36Sopenharmony_ci * fake irq domains like the x86 XEN one. Sigh... 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * The descriptor is zeroed and only @desc::msi_index and @desc::affinity 58562306a36Sopenharmony_ci * are set. When called from msix_setup_msi_descs() then the is_virtual 58662306a36Sopenharmony_ci * attribute is initialized as well. 58762306a36Sopenharmony_ci * 58862306a36Sopenharmony_ci * Fill in the rest. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_civoid msix_prepare_msi_desc(struct pci_dev *dev, struct msi_desc *desc) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci desc->nvec_used = 1; 59362306a36Sopenharmony_ci desc->pci.msi_attrib.is_msix = 1; 59462306a36Sopenharmony_ci desc->pci.msi_attrib.is_64 = 1; 59562306a36Sopenharmony_ci desc->pci.msi_attrib.default_irq = dev->irq; 59662306a36Sopenharmony_ci desc->pci.mask_base = dev->msix_base; 59762306a36Sopenharmony_ci desc->pci.msi_attrib.can_mask = !pci_msi_ignore_mask && 59862306a36Sopenharmony_ci !desc->pci.msi_attrib.is_virtual; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (desc->pci.msi_attrib.can_mask) { 60162306a36Sopenharmony_ci void __iomem *addr = pci_msix_desc_addr(desc); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci desc->pci.msix_ctrl = readl(addr + PCI_MSIX_ENTRY_VECTOR_CTRL); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int msix_setup_msi_descs(struct pci_dev *dev, struct msix_entry *entries, 60862306a36Sopenharmony_ci int nvec, struct irq_affinity_desc *masks) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci int ret = 0, i, vec_count = pci_msix_vec_count(dev); 61162306a36Sopenharmony_ci struct irq_affinity_desc *curmsk; 61262306a36Sopenharmony_ci struct msi_desc desc; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci memset(&desc, 0, sizeof(desc)); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci for (i = 0, curmsk = masks; i < nvec; i++, curmsk++) { 61762306a36Sopenharmony_ci desc.msi_index = entries ? entries[i].entry : i; 61862306a36Sopenharmony_ci desc.affinity = masks ? curmsk : NULL; 61962306a36Sopenharmony_ci desc.pci.msi_attrib.is_virtual = desc.msi_index >= vec_count; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci msix_prepare_msi_desc(dev, &desc); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ret = msi_insert_msi_desc(&dev->dev, &desc); 62462306a36Sopenharmony_ci if (ret) 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void msix_update_entries(struct pci_dev *dev, struct msix_entry *entries) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct msi_desc *desc; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (entries) { 63562306a36Sopenharmony_ci msi_for_each_desc(desc, &dev->dev, MSI_DESC_ALL) { 63662306a36Sopenharmony_ci entries->vector = desc->irq; 63762306a36Sopenharmony_ci entries++; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic void msix_mask_all(void __iomem *base, int tsize) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci u32 ctrl = PCI_MSIX_ENTRY_CTRL_MASKBIT; 64562306a36Sopenharmony_ci int i; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (pci_msi_ignore_mask) 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for (i = 0; i < tsize; i++, base += PCI_MSIX_ENTRY_SIZE) 65162306a36Sopenharmony_ci writel(ctrl, base + PCI_MSIX_ENTRY_VECTOR_CTRL); 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries, 65562306a36Sopenharmony_ci int nvec, struct irq_affinity *affd) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct irq_affinity_desc *masks = NULL; 65862306a36Sopenharmony_ci int ret; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (affd) 66162306a36Sopenharmony_ci masks = irq_create_affinity_masks(nvec, affd); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci msi_lock_descs(&dev->dev); 66462306a36Sopenharmony_ci ret = msix_setup_msi_descs(dev, entries, nvec, masks); 66562306a36Sopenharmony_ci if (ret) 66662306a36Sopenharmony_ci goto out_free; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); 66962306a36Sopenharmony_ci if (ret) 67062306a36Sopenharmony_ci goto out_free; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* Check if all MSI entries honor device restrictions */ 67362306a36Sopenharmony_ci ret = msi_verify_entries(dev); 67462306a36Sopenharmony_ci if (ret) 67562306a36Sopenharmony_ci goto out_free; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci msix_update_entries(dev, entries); 67862306a36Sopenharmony_ci goto out_unlock; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ciout_free: 68162306a36Sopenharmony_ci pci_free_msi_irqs(dev); 68262306a36Sopenharmony_ciout_unlock: 68362306a36Sopenharmony_ci msi_unlock_descs(&dev->dev); 68462306a36Sopenharmony_ci kfree(masks); 68562306a36Sopenharmony_ci return ret; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/** 68962306a36Sopenharmony_ci * msix_capability_init - configure device's MSI-X capability 69062306a36Sopenharmony_ci * @dev: pointer to the pci_dev data structure of MSI-X device function 69162306a36Sopenharmony_ci * @entries: pointer to an array of struct msix_entry entries 69262306a36Sopenharmony_ci * @nvec: number of @entries 69362306a36Sopenharmony_ci * @affd: Optional pointer to enable automatic affinity assignment 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * Setup the MSI-X capability structure of device function with a 69662306a36Sopenharmony_ci * single MSI-X IRQ. A return of zero indicates the successful setup of 69762306a36Sopenharmony_ci * requested MSI-X entries with allocated IRQs or non-zero for otherwise. 69862306a36Sopenharmony_ci **/ 69962306a36Sopenharmony_cistatic int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, 70062306a36Sopenharmony_ci int nvec, struct irq_affinity *affd) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci int ret, tsize; 70362306a36Sopenharmony_ci u16 control; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * Some devices require MSI-X to be enabled before the MSI-X 70762306a36Sopenharmony_ci * registers can be accessed. Mask all the vectors to prevent 70862306a36Sopenharmony_ci * interrupts coming in before they're fully set up. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci pci_msix_clear_and_set_ctrl(dev, 0, PCI_MSIX_FLAGS_MASKALL | 71162306a36Sopenharmony_ci PCI_MSIX_FLAGS_ENABLE); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Mark it enabled so setup functions can query it */ 71462306a36Sopenharmony_ci dev->msix_enabled = 1; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); 71762306a36Sopenharmony_ci /* Request & Map MSI-X table region */ 71862306a36Sopenharmony_ci tsize = msix_table_size(control); 71962306a36Sopenharmony_ci dev->msix_base = msix_map_region(dev, tsize); 72062306a36Sopenharmony_ci if (!dev->msix_base) { 72162306a36Sopenharmony_ci ret = -ENOMEM; 72262306a36Sopenharmony_ci goto out_disable; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci ret = msix_setup_interrupts(dev, entries, nvec, affd); 72662306a36Sopenharmony_ci if (ret) 72762306a36Sopenharmony_ci goto out_disable; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Disable INTX */ 73062306a36Sopenharmony_ci pci_intx_for_msi(dev, 0); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * Ensure that all table entries are masked to prevent 73462306a36Sopenharmony_ci * stale entries from firing in a crash kernel. 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * Done late to deal with a broken Marvell NVME device 73762306a36Sopenharmony_ci * which takes the MSI-X mask bits into account even 73862306a36Sopenharmony_ci * when MSI-X is disabled, which prevents MSI delivery. 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci msix_mask_all(dev->msix_base, tsize); 74162306a36Sopenharmony_ci pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci pcibios_free_irq(dev); 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ciout_disable: 74762306a36Sopenharmony_ci dev->msix_enabled = 0; 74862306a36Sopenharmony_ci pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE, 0); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return ret; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic bool pci_msix_validate_entries(struct pci_dev *dev, struct msix_entry *entries, int nvec) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci bool nogap; 75662306a36Sopenharmony_ci int i, j; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (!entries) 75962306a36Sopenharmony_ci return true; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci nogap = pci_msi_domain_supports(dev, MSI_FLAG_MSIX_CONTIGUOUS, DENY_LEGACY); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci for (i = 0; i < nvec; i++) { 76462306a36Sopenharmony_ci /* Check for duplicate entries */ 76562306a36Sopenharmony_ci for (j = i + 1; j < nvec; j++) { 76662306a36Sopenharmony_ci if (entries[i].entry == entries[j].entry) 76762306a36Sopenharmony_ci return false; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci /* Check for unsupported gaps */ 77062306a36Sopenharmony_ci if (nogap && entries[i].entry != i) 77162306a36Sopenharmony_ci return false; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci return true; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ciint __pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int minvec, 77762306a36Sopenharmony_ci int maxvec, struct irq_affinity *affd, int flags) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci int hwsize, rc, nvec = maxvec; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (maxvec < minvec) 78262306a36Sopenharmony_ci return -ERANGE; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (dev->msi_enabled) { 78562306a36Sopenharmony_ci pci_info(dev, "can't enable MSI-X (MSI already enabled)\n"); 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (WARN_ON_ONCE(dev->msix_enabled)) 79062306a36Sopenharmony_ci return -EINVAL; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* Check MSI-X early on irq domain enabled architectures */ 79362306a36Sopenharmony_ci if (!pci_msi_domain_supports(dev, MSI_FLAG_PCI_MSIX, ALLOW_LEGACY)) 79462306a36Sopenharmony_ci return -ENOTSUPP; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (!pci_msi_supported(dev, nvec) || dev->current_state != PCI_D0) 79762306a36Sopenharmony_ci return -EINVAL; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci hwsize = pci_msix_vec_count(dev); 80062306a36Sopenharmony_ci if (hwsize < 0) 80162306a36Sopenharmony_ci return hwsize; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (!pci_msix_validate_entries(dev, entries, nvec)) 80462306a36Sopenharmony_ci return -EINVAL; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (hwsize < nvec) { 80762306a36Sopenharmony_ci /* Keep the IRQ virtual hackery working */ 80862306a36Sopenharmony_ci if (flags & PCI_IRQ_VIRTUAL) 80962306a36Sopenharmony_ci hwsize = nvec; 81062306a36Sopenharmony_ci else 81162306a36Sopenharmony_ci nvec = hwsize; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (nvec < minvec) 81562306a36Sopenharmony_ci return -ENOSPC; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci rc = pci_setup_msi_context(dev); 81862306a36Sopenharmony_ci if (rc) 81962306a36Sopenharmony_ci return rc; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (!pci_setup_msix_device_domain(dev, hwsize)) 82262306a36Sopenharmony_ci return -ENODEV; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci for (;;) { 82562306a36Sopenharmony_ci if (affd) { 82662306a36Sopenharmony_ci nvec = irq_calc_affinity_vectors(minvec, nvec, affd); 82762306a36Sopenharmony_ci if (nvec < minvec) 82862306a36Sopenharmony_ci return -ENOSPC; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci rc = msix_capability_init(dev, entries, nvec, affd); 83262306a36Sopenharmony_ci if (rc == 0) 83362306a36Sopenharmony_ci return nvec; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (rc < 0) 83662306a36Sopenharmony_ci return rc; 83762306a36Sopenharmony_ci if (rc < minvec) 83862306a36Sopenharmony_ci return -ENOSPC; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci nvec = rc; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_civoid __pci_restore_msix_state(struct pci_dev *dev) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci struct msi_desc *entry; 84762306a36Sopenharmony_ci bool write_msg; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (!dev->msix_enabled) 85062306a36Sopenharmony_ci return; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* route the table */ 85362306a36Sopenharmony_ci pci_intx_for_msi(dev, 0); 85462306a36Sopenharmony_ci pci_msix_clear_and_set_ctrl(dev, 0, 85562306a36Sopenharmony_ci PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci write_msg = arch_restore_msi_irqs(dev); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci msi_lock_descs(&dev->dev); 86062306a36Sopenharmony_ci msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) { 86162306a36Sopenharmony_ci if (write_msg) 86262306a36Sopenharmony_ci __pci_write_msi_msg(entry, &entry->msg); 86362306a36Sopenharmony_ci pci_msix_write_vector_ctrl(entry, entry->pci.msix_ctrl); 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci msi_unlock_descs(&dev->dev); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_civoid pci_msix_shutdown(struct pci_dev *dev) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct msi_desc *desc; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (!pci_msi_enable || !dev || !dev->msix_enabled) 87562306a36Sopenharmony_ci return; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (pci_dev_is_disconnected(dev)) { 87862306a36Sopenharmony_ci dev->msix_enabled = 0; 87962306a36Sopenharmony_ci return; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* Return the device with MSI-X masked as initial states */ 88362306a36Sopenharmony_ci msi_for_each_desc(desc, &dev->dev, MSI_DESC_ALL) 88462306a36Sopenharmony_ci pci_msix_mask(desc); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0); 88762306a36Sopenharmony_ci pci_intx_for_msi(dev, 1); 88862306a36Sopenharmony_ci dev->msix_enabled = 0; 88962306a36Sopenharmony_ci pcibios_alloc_irq(dev); 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci/* Common interfaces */ 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_civoid pci_free_msi_irqs(struct pci_dev *dev) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci pci_msi_teardown_msi_irqs(dev); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (dev->msix_base) { 89962306a36Sopenharmony_ci iounmap(dev->msix_base); 90062306a36Sopenharmony_ci dev->msix_base = NULL; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/* Misc. infrastructure */ 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistruct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci return to_pci_dev(desc->dev); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ciEXPORT_SYMBOL(msi_desc_to_pci_dev); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_civoid pci_no_msi(void) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci pci_msi_enable = 0; 91562306a36Sopenharmony_ci} 916