162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI MSI/MSI-X — Exported APIs for device drivers 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 * Copyright (C) 2022 Linutronix GmbH 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "msi.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/** 1762306a36Sopenharmony_ci * pci_enable_msi() - Enable MSI interrupt mode on device 1862306a36Sopenharmony_ci * @dev: the PCI device to operate on 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Legacy device driver API to enable MSI interrupts mode on device and 2162306a36Sopenharmony_ci * allocate a single interrupt vector. On success, the allocated vector 2262306a36Sopenharmony_ci * Linux IRQ will be saved at @dev->irq. The driver must invoke 2362306a36Sopenharmony_ci * pci_disable_msi() on cleanup. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API 2662306a36Sopenharmony_ci * pair should, in general, be used instead. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Return: 0 on success, errno otherwise 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ciint pci_enable_msi(struct pci_dev *dev) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int rc = __pci_enable_msi_range(dev, 1, 1, NULL); 3362306a36Sopenharmony_ci if (rc < 0) 3462306a36Sopenharmony_ci return rc; 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ciEXPORT_SYMBOL(pci_enable_msi); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * pci_disable_msi() - Disable MSI interrupt mode on device 4162306a36Sopenharmony_ci * @dev: the PCI device to operate on 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Legacy device driver API to disable MSI interrupt mode on device, 4462306a36Sopenharmony_ci * free earlier allocated interrupt vectors, and restore INTx emulation. 4562306a36Sopenharmony_ci * The PCI device Linux IRQ (@dev->irq) is restored to its default 4662306a36Sopenharmony_ci * pin-assertion IRQ. This is the cleanup pair of pci_enable_msi(). 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API 4962306a36Sopenharmony_ci * pair should, in general, be used instead. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_civoid pci_disable_msi(struct pci_dev *dev) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci if (!pci_msi_enabled() || !dev || !dev->msi_enabled) 5462306a36Sopenharmony_ci return; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci msi_lock_descs(&dev->dev); 5762306a36Sopenharmony_ci pci_msi_shutdown(dev); 5862306a36Sopenharmony_ci pci_free_msi_irqs(dev); 5962306a36Sopenharmony_ci msi_unlock_descs(&dev->dev); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ciEXPORT_SYMBOL(pci_disable_msi); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * pci_msix_vec_count() - Get number of MSI-X interrupt vectors on device 6562306a36Sopenharmony_ci * @dev: the PCI device to operate on 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * Return: number of MSI-X interrupt vectors available on this device 6862306a36Sopenharmony_ci * (i.e., the device's MSI-X capability structure "table size"), -EINVAL 6962306a36Sopenharmony_ci * if the device is not MSI-X capable, other errnos otherwise. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ciint pci_msix_vec_count(struct pci_dev *dev) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u16 control; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!dev->msix_cap) 7662306a36Sopenharmony_ci return -EINVAL; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); 7962306a36Sopenharmony_ci return msix_table_size(control); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ciEXPORT_SYMBOL(pci_msix_vec_count); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * pci_enable_msix_range() - Enable MSI-X interrupt mode on device 8562306a36Sopenharmony_ci * @dev: the PCI device to operate on 8662306a36Sopenharmony_ci * @entries: input/output parameter, array of MSI-X configuration entries 8762306a36Sopenharmony_ci * @minvec: minimum required number of MSI-X vectors 8862306a36Sopenharmony_ci * @maxvec: maximum desired number of MSI-X vectors 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Legacy device driver API to enable MSI-X interrupt mode on device and 9162306a36Sopenharmony_ci * configure its MSI-X capability structure as appropriate. The passed 9262306a36Sopenharmony_ci * @entries array must have each of its members "entry" field set to a 9362306a36Sopenharmony_ci * desired (valid) MSI-X vector number, where the range of valid MSI-X 9462306a36Sopenharmony_ci * vector numbers can be queried through pci_msix_vec_count(). If 9562306a36Sopenharmony_ci * successful, the driver must invoke pci_disable_msix() on cleanup. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API 9862306a36Sopenharmony_ci * pair should, in general, be used instead. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Return: number of MSI-X vectors allocated (which might be smaller 10162306a36Sopenharmony_ci * than @maxvecs), where Linux IRQ numbers for such allocated vectors 10262306a36Sopenharmony_ci * are saved back in the @entries array elements' "vector" field. Return 10362306a36Sopenharmony_ci * -ENOSPC if less than @minvecs interrupt vectors are available. 10462306a36Sopenharmony_ci * Return -EINVAL if one of the passed @entries members "entry" field 10562306a36Sopenharmony_ci * was invalid or a duplicate, or if plain MSI interrupts mode was 10662306a36Sopenharmony_ci * earlier enabled on device. Return other errnos otherwise. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ciint pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, 10962306a36Sopenharmony_ci int minvec, int maxvec) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci return __pci_enable_msix_range(dev, entries, minvec, maxvec, NULL, 0); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL(pci_enable_msix_range); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/** 11662306a36Sopenharmony_ci * pci_msix_can_alloc_dyn - Query whether dynamic allocation after enabling 11762306a36Sopenharmony_ci * MSI-X is supported 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * @dev: PCI device to operate on 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Return: True if supported, false otherwise 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cibool pci_msix_can_alloc_dyn(struct pci_dev *dev) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci if (!dev->msix_cap) 12662306a36Sopenharmony_ci return false; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return pci_msi_domain_supports(dev, MSI_FLAG_PCI_MSIX_ALLOC_DYN, DENY_LEGACY); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_msix_can_alloc_dyn); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/** 13362306a36Sopenharmony_ci * pci_msix_alloc_irq_at - Allocate an MSI-X interrupt after enabling MSI-X 13462306a36Sopenharmony_ci * at a given MSI-X vector index or any free vector index 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * @dev: PCI device to operate on 13762306a36Sopenharmony_ci * @index: Index to allocate. If @index == MSI_ANY_INDEX this allocates 13862306a36Sopenharmony_ci * the next free index in the MSI-X table 13962306a36Sopenharmony_ci * @affdesc: Optional pointer to an affinity descriptor structure. NULL otherwise 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Return: A struct msi_map 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * On success msi_map::index contains the allocated index (>= 0) and 14462306a36Sopenharmony_ci * msi_map::virq contains the allocated Linux interrupt number (> 0). 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * On fail msi_map::index contains the error code and msi_map::virq 14762306a36Sopenharmony_ci * is set to 0. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistruct msi_map pci_msix_alloc_irq_at(struct pci_dev *dev, unsigned int index, 15062306a36Sopenharmony_ci const struct irq_affinity_desc *affdesc) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct msi_map map = { .index = -ENOTSUPP }; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!dev->msix_enabled) 15562306a36Sopenharmony_ci return map; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!pci_msix_can_alloc_dyn(dev)) 15862306a36Sopenharmony_ci return map; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return msi_domain_alloc_irq_at(&dev->dev, MSI_DEFAULT_DOMAIN, index, affdesc, NULL); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_msix_alloc_irq_at); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/** 16562306a36Sopenharmony_ci * pci_msix_free_irq - Free an interrupt on a PCI/MSIX interrupt domain 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * @dev: The PCI device to operate on 16862306a36Sopenharmony_ci * @map: A struct msi_map describing the interrupt to free 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * Undo an interrupt vector allocation. Does not disable MSI-X. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_civoid pci_msix_free_irq(struct pci_dev *dev, struct msi_map map) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci if (WARN_ON_ONCE(map.index < 0 || map.virq <= 0)) 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci if (WARN_ON_ONCE(!pci_msix_can_alloc_dyn(dev))) 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci msi_domain_free_irqs_range(&dev->dev, MSI_DEFAULT_DOMAIN, map.index, map.index); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_msix_free_irq); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/** 18362306a36Sopenharmony_ci * pci_disable_msix() - Disable MSI-X interrupt mode on device 18462306a36Sopenharmony_ci * @dev: the PCI device to operate on 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Legacy device driver API to disable MSI-X interrupt mode on device, 18762306a36Sopenharmony_ci * free earlier-allocated interrupt vectors, and restore INTx. 18862306a36Sopenharmony_ci * The PCI device Linux IRQ (@dev->irq) is restored to its default pin 18962306a36Sopenharmony_ci * assertion IRQ. This is the cleanup pair of pci_enable_msix_range(). 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API 19262306a36Sopenharmony_ci * pair should, in general, be used instead. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_civoid pci_disable_msix(struct pci_dev *dev) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci if (!pci_msi_enabled() || !dev || !dev->msix_enabled) 19762306a36Sopenharmony_ci return; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci msi_lock_descs(&dev->dev); 20062306a36Sopenharmony_ci pci_msix_shutdown(dev); 20162306a36Sopenharmony_ci pci_free_msi_irqs(dev); 20262306a36Sopenharmony_ci msi_unlock_descs(&dev->dev); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ciEXPORT_SYMBOL(pci_disable_msix); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/** 20762306a36Sopenharmony_ci * pci_alloc_irq_vectors() - Allocate multiple device interrupt vectors 20862306a36Sopenharmony_ci * @dev: the PCI device to operate on 20962306a36Sopenharmony_ci * @min_vecs: minimum required number of vectors (must be >= 1) 21062306a36Sopenharmony_ci * @max_vecs: maximum desired number of vectors 21162306a36Sopenharmony_ci * @flags: One or more of: 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * * %PCI_IRQ_MSIX Allow trying MSI-X vector allocations 21462306a36Sopenharmony_ci * * %PCI_IRQ_MSI Allow trying MSI vector allocations 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * * %PCI_IRQ_LEGACY Allow trying legacy INTx interrupts, if 21762306a36Sopenharmony_ci * and only if @min_vecs == 1 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * * %PCI_IRQ_AFFINITY Auto-manage IRQs affinity by spreading 22062306a36Sopenharmony_ci * the vectors around available CPUs 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * Allocate up to @max_vecs interrupt vectors on device. MSI-X irq 22362306a36Sopenharmony_ci * vector allocation has a higher precedence over plain MSI, which has a 22462306a36Sopenharmony_ci * higher precedence over legacy INTx emulation. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * Upon a successful allocation, the caller should use pci_irq_vector() 22762306a36Sopenharmony_ci * to get the Linux IRQ number to be passed to request_threaded_irq(). 22862306a36Sopenharmony_ci * The driver must call pci_free_irq_vectors() on cleanup. 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * Return: number of allocated vectors (which might be smaller than 23162306a36Sopenharmony_ci * @max_vecs), -ENOSPC if less than @min_vecs interrupt vectors are 23262306a36Sopenharmony_ci * available, other errnos otherwise. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ciint pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, 23562306a36Sopenharmony_ci unsigned int max_vecs, unsigned int flags) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return pci_alloc_irq_vectors_affinity(dev, min_vecs, max_vecs, 23862306a36Sopenharmony_ci flags, NULL); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ciEXPORT_SYMBOL(pci_alloc_irq_vectors); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/** 24362306a36Sopenharmony_ci * pci_alloc_irq_vectors_affinity() - Allocate multiple device interrupt 24462306a36Sopenharmony_ci * vectors with affinity requirements 24562306a36Sopenharmony_ci * @dev: the PCI device to operate on 24662306a36Sopenharmony_ci * @min_vecs: minimum required number of vectors (must be >= 1) 24762306a36Sopenharmony_ci * @max_vecs: maximum desired number of vectors 24862306a36Sopenharmony_ci * @flags: allocation flags, as in pci_alloc_irq_vectors() 24962306a36Sopenharmony_ci * @affd: affinity requirements (can be %NULL). 25062306a36Sopenharmony_ci * 25162306a36Sopenharmony_ci * Same as pci_alloc_irq_vectors(), but with the extra @affd parameter. 25262306a36Sopenharmony_ci * Check that function docs, and &struct irq_affinity, for more details. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ciint pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, 25562306a36Sopenharmony_ci unsigned int max_vecs, unsigned int flags, 25662306a36Sopenharmony_ci struct irq_affinity *affd) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct irq_affinity msi_default_affd = {0}; 25962306a36Sopenharmony_ci int nvecs = -ENOSPC; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (flags & PCI_IRQ_AFFINITY) { 26262306a36Sopenharmony_ci if (!affd) 26362306a36Sopenharmony_ci affd = &msi_default_affd; 26462306a36Sopenharmony_ci } else { 26562306a36Sopenharmony_ci if (WARN_ON(affd)) 26662306a36Sopenharmony_ci affd = NULL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (flags & PCI_IRQ_MSIX) { 27062306a36Sopenharmony_ci nvecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs, 27162306a36Sopenharmony_ci affd, flags); 27262306a36Sopenharmony_ci if (nvecs > 0) 27362306a36Sopenharmony_ci return nvecs; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (flags & PCI_IRQ_MSI) { 27762306a36Sopenharmony_ci nvecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, affd); 27862306a36Sopenharmony_ci if (nvecs > 0) 27962306a36Sopenharmony_ci return nvecs; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* use legacy IRQ if allowed */ 28362306a36Sopenharmony_ci if (flags & PCI_IRQ_LEGACY) { 28462306a36Sopenharmony_ci if (min_vecs == 1 && dev->irq) { 28562306a36Sopenharmony_ci /* 28662306a36Sopenharmony_ci * Invoke the affinity spreading logic to ensure that 28762306a36Sopenharmony_ci * the device driver can adjust queue configuration 28862306a36Sopenharmony_ci * for the single interrupt case. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci if (affd) 29162306a36Sopenharmony_ci irq_create_affinity_masks(1, affd); 29262306a36Sopenharmony_ci pci_intx(dev, 1); 29362306a36Sopenharmony_ci return 1; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return nvecs; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ciEXPORT_SYMBOL(pci_alloc_irq_vectors_affinity); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * pci_irq_vector() - Get Linux IRQ number of a device interrupt vector 30362306a36Sopenharmony_ci * @dev: the PCI device to operate on 30462306a36Sopenharmony_ci * @nr: device-relative interrupt vector index (0-based); has different 30562306a36Sopenharmony_ci * meanings, depending on interrupt mode: 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * * MSI-X the index in the MSI-X vector table 30862306a36Sopenharmony_ci * * MSI the index of the enabled MSI vectors 30962306a36Sopenharmony_ci * * INTx must be 0 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Return: the Linux IRQ number, or -EINVAL if @nr is out of range 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ciint pci_irq_vector(struct pci_dev *dev, unsigned int nr) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci unsigned int irq; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!dev->msi_enabled && !dev->msix_enabled) 31862306a36Sopenharmony_ci return !nr ? dev->irq : -EINVAL; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci irq = msi_get_virq(&dev->dev, nr); 32162306a36Sopenharmony_ci return irq ? irq : -EINVAL; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL(pci_irq_vector); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/** 32662306a36Sopenharmony_ci * pci_irq_get_affinity() - Get a device interrupt vector affinity 32762306a36Sopenharmony_ci * @dev: the PCI device to operate on 32862306a36Sopenharmony_ci * @nr: device-relative interrupt vector index (0-based); has different 32962306a36Sopenharmony_ci * meanings, depending on interrupt mode: 33062306a36Sopenharmony_ci * 33162306a36Sopenharmony_ci * * MSI-X the index in the MSI-X vector table 33262306a36Sopenharmony_ci * * MSI the index of the enabled MSI vectors 33362306a36Sopenharmony_ci * * INTx must be 0 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Return: MSI/MSI-X vector affinity, NULL if @nr is out of range or if 33662306a36Sopenharmony_ci * the MSI(-X) vector was allocated without explicit affinity 33762306a36Sopenharmony_ci * requirements (e.g., by pci_enable_msi(), pci_enable_msix_range(), or 33862306a36Sopenharmony_ci * pci_alloc_irq_vectors() without the %PCI_IRQ_AFFINITY flag). Return a 33962306a36Sopenharmony_ci * generic set of CPU IDs representing all possible CPUs available 34062306a36Sopenharmony_ci * during system boot if the device is in legacy INTx mode. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ciconst struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci int idx, irq = pci_irq_vector(dev, nr); 34562306a36Sopenharmony_ci struct msi_desc *desc; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (WARN_ON_ONCE(irq <= 0)) 34862306a36Sopenharmony_ci return NULL; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci desc = irq_get_msi_desc(irq); 35162306a36Sopenharmony_ci /* Non-MSI does not have the information handy */ 35262306a36Sopenharmony_ci if (!desc) 35362306a36Sopenharmony_ci return cpu_possible_mask; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* MSI[X] interrupts can be allocated without affinity descriptor */ 35662306a36Sopenharmony_ci if (!desc->affinity) 35762306a36Sopenharmony_ci return NULL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * MSI has a mask array in the descriptor. 36162306a36Sopenharmony_ci * MSI-X has a single mask. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci idx = dev->msi_enabled ? nr : 0; 36462306a36Sopenharmony_ci return &desc->affinity[idx].mask; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ciEXPORT_SYMBOL(pci_irq_get_affinity); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/** 36962306a36Sopenharmony_ci * pci_ims_alloc_irq - Allocate an interrupt on a PCI/IMS interrupt domain 37062306a36Sopenharmony_ci * @dev: The PCI device to operate on 37162306a36Sopenharmony_ci * @icookie: Pointer to an IMS implementation specific cookie for this 37262306a36Sopenharmony_ci * IMS instance (PASID, queue ID, pointer...). 37362306a36Sopenharmony_ci * The cookie content is copied into the MSI descriptor for the 37462306a36Sopenharmony_ci * interrupt chip callbacks or domain specific setup functions. 37562306a36Sopenharmony_ci * @affdesc: Optional pointer to an interrupt affinity descriptor 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * There is no index for IMS allocations as IMS is an implementation 37862306a36Sopenharmony_ci * specific storage and does not have any direct associations between 37962306a36Sopenharmony_ci * index, which might be a pure software construct, and device 38062306a36Sopenharmony_ci * functionality. This association is established by the driver either via 38162306a36Sopenharmony_ci * the index - if there is a hardware table - or in case of purely software 38262306a36Sopenharmony_ci * managed IMS implementation the association happens via the 38362306a36Sopenharmony_ci * irq_write_msi_msg() callback of the implementation specific interrupt 38462306a36Sopenharmony_ci * chip, which utilizes the provided @icookie to store the MSI message in 38562306a36Sopenharmony_ci * the appropriate place. 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Return: A struct msi_map 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * On success msi_map::index contains the allocated index (>= 0) and 39062306a36Sopenharmony_ci * msi_map::virq the allocated Linux interrupt number (> 0). 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * On fail msi_map::index contains the error code and msi_map::virq 39362306a36Sopenharmony_ci * is set to 0. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistruct msi_map pci_ims_alloc_irq(struct pci_dev *dev, union msi_instance_cookie *icookie, 39662306a36Sopenharmony_ci const struct irq_affinity_desc *affdesc) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci return msi_domain_alloc_irq_at(&dev->dev, MSI_SECONDARY_DOMAIN, MSI_ANY_INDEX, 39962306a36Sopenharmony_ci affdesc, icookie); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_ims_alloc_irq); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/** 40462306a36Sopenharmony_ci * pci_ims_free_irq - Allocate an interrupt on a PCI/IMS interrupt domain 40562306a36Sopenharmony_ci * which was allocated via pci_ims_alloc_irq() 40662306a36Sopenharmony_ci * @dev: The PCI device to operate on 40762306a36Sopenharmony_ci * @map: A struct msi_map describing the interrupt to free as 40862306a36Sopenharmony_ci * returned from pci_ims_alloc_irq() 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_civoid pci_ims_free_irq(struct pci_dev *dev, struct msi_map map) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci if (WARN_ON_ONCE(map.index < 0 || map.virq <= 0)) 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci msi_domain_free_irqs_range(&dev->dev, MSI_SECONDARY_DOMAIN, map.index, map.index); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_ims_free_irq); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/** 41962306a36Sopenharmony_ci * pci_free_irq_vectors() - Free previously allocated IRQs for a device 42062306a36Sopenharmony_ci * @dev: the PCI device to operate on 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Undo the interrupt vector allocations and possible device MSI/MSI-X 42362306a36Sopenharmony_ci * enablement earlier done through pci_alloc_irq_vectors_affinity() or 42462306a36Sopenharmony_ci * pci_alloc_irq_vectors(). 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_civoid pci_free_irq_vectors(struct pci_dev *dev) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci pci_disable_msix(dev); 42962306a36Sopenharmony_ci pci_disable_msi(dev); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ciEXPORT_SYMBOL(pci_free_irq_vectors); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/** 43462306a36Sopenharmony_ci * pci_restore_msi_state() - Restore cached MSI(-X) state on device 43562306a36Sopenharmony_ci * @dev: the PCI device to operate on 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Write the Linux-cached MSI(-X) state back on device. This is 43862306a36Sopenharmony_ci * typically useful upon system resume, or after an error-recovery PCI 43962306a36Sopenharmony_ci * adapter reset. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_civoid pci_restore_msi_state(struct pci_dev *dev) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci __pci_restore_msi_state(dev); 44462306a36Sopenharmony_ci __pci_restore_msix_state(dev); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_restore_msi_state); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/** 44962306a36Sopenharmony_ci * pci_msi_enabled() - Are MSI(-X) interrupts enabled system-wide? 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * Return: true if MSI has not been globally disabled through ACPI FADT, 45262306a36Sopenharmony_ci * PCI bridge quirks, or the "pci=nomsi" kernel command-line option. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ciint pci_msi_enabled(void) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci return pci_msi_enable; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ciEXPORT_SYMBOL(pci_msi_enabled); 459