xref: /kernel/linux/linux-6.6/drivers/pci/msi/api.c (revision 62306a36)
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