LCOV - code coverage report
Current view: top level - drivers/pci/msi - api.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 84 0.0 %
Date: 2023-03-27 20:00:47 Functions: 0 17 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * PCI MSI/MSI-X — Exported APIs for device drivers
       4             :  *
       5             :  * Copyright (C) 2003-2004 Intel
       6             :  * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
       7             :  * Copyright (C) 2016 Christoph Hellwig.
       8             :  * Copyright (C) 2022 Linutronix GmbH
       9             :  */
      10             : 
      11             : #include <linux/export.h>
      12             : #include <linux/irq.h>
      13             : 
      14             : #include "msi.h"
      15             : 
      16             : /**
      17             :  * pci_enable_msi() - Enable MSI interrupt mode on device
      18             :  * @dev: the PCI device to operate on
      19             :  *
      20             :  * Legacy device driver API to enable MSI interrupts mode on device and
      21             :  * allocate a single interrupt vector. On success, the allocated vector
      22             :  * Linux IRQ will be saved at @dev->irq. The driver must invoke
      23             :  * pci_disable_msi() on cleanup.
      24             :  *
      25             :  * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API
      26             :  * pair should, in general, be used instead.
      27             :  *
      28             :  * Return: 0 on success, errno otherwise
      29             :  */
      30           0 : int pci_enable_msi(struct pci_dev *dev)
      31             : {
      32           0 :         int rc = __pci_enable_msi_range(dev, 1, 1, NULL);
      33           0 :         if (rc < 0)
      34             :                 return rc;
      35           0 :         return 0;
      36             : }
      37             : EXPORT_SYMBOL(pci_enable_msi);
      38             : 
      39             : /**
      40             :  * pci_disable_msi() - Disable MSI interrupt mode on device
      41             :  * @dev: the PCI device to operate on
      42             :  *
      43             :  * Legacy device driver API to disable MSI interrupt mode on device,
      44             :  * free earlier allocated interrupt vectors, and restore INTx emulation.
      45             :  * The PCI device Linux IRQ (@dev->irq) is restored to its default
      46             :  * pin-assertion IRQ. This is the cleanup pair of pci_enable_msi().
      47             :  *
      48             :  * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API
      49             :  * pair should, in general, be used instead.
      50             :  */
      51           0 : void pci_disable_msi(struct pci_dev *dev)
      52             : {
      53           0 :         if (!pci_msi_enabled() || !dev || !dev->msi_enabled)
      54             :                 return;
      55             : 
      56           0 :         msi_lock_descs(&dev->dev);
      57           0 :         pci_msi_shutdown(dev);
      58           0 :         pci_free_msi_irqs(dev);
      59           0 :         msi_unlock_descs(&dev->dev);
      60             : }
      61             : EXPORT_SYMBOL(pci_disable_msi);
      62             : 
      63             : /**
      64             :  * pci_msix_vec_count() - Get number of MSI-X interrupt vectors on device
      65             :  * @dev: the PCI device to operate on
      66             :  *
      67             :  * Return: number of MSI-X interrupt vectors available on this device
      68             :  * (i.e., the device's MSI-X capability structure "table size"), -EINVAL
      69             :  * if the device is not MSI-X capable, other errnos otherwise.
      70             :  */
      71           0 : int pci_msix_vec_count(struct pci_dev *dev)
      72             : {
      73             :         u16 control;
      74             : 
      75           0 :         if (!dev->msix_cap)
      76             :                 return -EINVAL;
      77             : 
      78           0 :         pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
      79           0 :         return msix_table_size(control);
      80             : }
      81             : EXPORT_SYMBOL(pci_msix_vec_count);
      82             : 
      83             : /**
      84             :  * pci_enable_msix_range() - Enable MSI-X interrupt mode on device
      85             :  * @dev:     the PCI device to operate on
      86             :  * @entries: input/output parameter, array of MSI-X configuration entries
      87             :  * @minvec:  minimum required number of MSI-X vectors
      88             :  * @maxvec:  maximum desired number of MSI-X vectors
      89             :  *
      90             :  * Legacy device driver API to enable MSI-X interrupt mode on device and
      91             :  * configure its MSI-X capability structure as appropriate.  The passed
      92             :  * @entries array must have each of its members "entry" field set to a
      93             :  * desired (valid) MSI-X vector number, where the range of valid MSI-X
      94             :  * vector numbers can be queried through pci_msix_vec_count().  If
      95             :  * successful, the driver must invoke pci_disable_msix() on cleanup.
      96             :  *
      97             :  * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API
      98             :  * pair should, in general, be used instead.
      99             :  *
     100             :  * Return: number of MSI-X vectors allocated (which might be smaller
     101             :  * than @maxvecs), where Linux IRQ numbers for such allocated vectors
     102             :  * are saved back in the @entries array elements' "vector" field. Return
     103             :  * -ENOSPC if less than @minvecs interrupt vectors are available.
     104             :  * Return -EINVAL if one of the passed @entries members "entry" field
     105             :  * was invalid or a duplicate, or if plain MSI interrupts mode was
     106             :  * earlier enabled on device. Return other errnos otherwise.
     107             :  */
     108           0 : int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
     109             :                           int minvec, int maxvec)
     110             : {
     111           0 :         return __pci_enable_msix_range(dev, entries, minvec, maxvec, NULL, 0);
     112             : }
     113             : EXPORT_SYMBOL(pci_enable_msix_range);
     114             : 
     115             : /**
     116             :  * pci_msix_can_alloc_dyn - Query whether dynamic allocation after enabling
     117             :  *                          MSI-X is supported
     118             :  *
     119             :  * @dev:        PCI device to operate on
     120             :  *
     121             :  * Return: True if supported, false otherwise
     122             :  */
     123           0 : bool pci_msix_can_alloc_dyn(struct pci_dev *dev)
     124             : {
     125           0 :         if (!dev->msix_cap)
     126             :                 return false;
     127             : 
     128           0 :         return pci_msi_domain_supports(dev, MSI_FLAG_PCI_MSIX_ALLOC_DYN, DENY_LEGACY);
     129             : }
     130             : EXPORT_SYMBOL_GPL(pci_msix_can_alloc_dyn);
     131             : 
     132             : /**
     133             :  * pci_msix_alloc_irq_at - Allocate an MSI-X interrupt after enabling MSI-X
     134             :  *                         at a given MSI-X vector index or any free vector index
     135             :  *
     136             :  * @dev:        PCI device to operate on
     137             :  * @index:      Index to allocate. If @index == MSI_ANY_INDEX this allocates
     138             :  *              the next free index in the MSI-X table
     139             :  * @affdesc:    Optional pointer to an affinity descriptor structure. NULL otherwise
     140             :  *
     141             :  * Return: A struct msi_map
     142             :  *
     143             :  *      On success msi_map::index contains the allocated index (>= 0) and
     144             :  *      msi_map::virq contains the allocated Linux interrupt number (> 0).
     145             :  *
     146             :  *      On fail msi_map::index contains the error code and msi_map::virq
     147             :  *      is set to 0.
     148             :  */
     149           0 : struct msi_map pci_msix_alloc_irq_at(struct pci_dev *dev, unsigned int index,
     150             :                                      const struct irq_affinity_desc *affdesc)
     151             : {
     152           0 :         struct msi_map map = { .index = -ENOTSUPP };
     153             : 
     154           0 :         if (!dev->msix_enabled)
     155           0 :                 return map;
     156             : 
     157           0 :         if (!pci_msix_can_alloc_dyn(dev))
     158           0 :                 return map;
     159             : 
     160           0 :         return msi_domain_alloc_irq_at(&dev->dev, MSI_DEFAULT_DOMAIN, index, affdesc, NULL);
     161             : }
     162             : EXPORT_SYMBOL_GPL(pci_msix_alloc_irq_at);
     163             : 
     164             : /**
     165             :  * pci_msix_free_irq - Free an interrupt on a PCI/MSIX interrupt domain
     166             :  *
     167             :  * @dev:        The PCI device to operate on
     168             :  * @map:        A struct msi_map describing the interrupt to free
     169             :  *
     170             :  * Undo an interrupt vector allocation. Does not disable MSI-X.
     171             :  */
     172           0 : void pci_msix_free_irq(struct pci_dev *dev, struct msi_map map)
     173             : {
     174           0 :         if (WARN_ON_ONCE(map.index < 0 || map.virq <= 0))
     175             :                 return;
     176           0 :         if (WARN_ON_ONCE(!pci_msix_can_alloc_dyn(dev)))
     177             :                 return;
     178           0 :         msi_domain_free_irqs_range(&dev->dev, MSI_DEFAULT_DOMAIN, map.index, map.index);
     179             : }
     180             : EXPORT_SYMBOL_GPL(pci_msix_free_irq);
     181             : 
     182             : /**
     183             :  * pci_disable_msix() - Disable MSI-X interrupt mode on device
     184             :  * @dev: the PCI device to operate on
     185             :  *
     186             :  * Legacy device driver API to disable MSI-X interrupt mode on device,
     187             :  * free earlier-allocated interrupt vectors, and restore INTx.
     188             :  * The PCI device Linux IRQ (@dev->irq) is restored to its default pin
     189             :  * assertion IRQ. This is the cleanup pair of pci_enable_msix_range().
     190             :  *
     191             :  * NOTE: The newer pci_alloc_irq_vectors() / pci_free_irq_vectors() API
     192             :  * pair should, in general, be used instead.
     193             :  */
     194           0 : void pci_disable_msix(struct pci_dev *dev)
     195             : {
     196           0 :         if (!pci_msi_enabled() || !dev || !dev->msix_enabled)
     197             :                 return;
     198             : 
     199           0 :         msi_lock_descs(&dev->dev);
     200           0 :         pci_msix_shutdown(dev);
     201           0 :         pci_free_msi_irqs(dev);
     202           0 :         msi_unlock_descs(&dev->dev);
     203             : }
     204             : EXPORT_SYMBOL(pci_disable_msix);
     205             : 
     206             : /**
     207             :  * pci_alloc_irq_vectors() - Allocate multiple device interrupt vectors
     208             :  * @dev:      the PCI device to operate on
     209             :  * @min_vecs: minimum required number of vectors (must be >= 1)
     210             :  * @max_vecs: maximum desired number of vectors
     211             :  * @flags:    One or more of:
     212             :  *
     213             :  *            * %PCI_IRQ_MSIX      Allow trying MSI-X vector allocations
     214             :  *            * %PCI_IRQ_MSI       Allow trying MSI vector allocations
     215             :  *
     216             :  *            * %PCI_IRQ_LEGACY    Allow trying legacy INTx interrupts, if
     217             :  *              and only if @min_vecs == 1
     218             :  *
     219             :  *            * %PCI_IRQ_AFFINITY  Auto-manage IRQs affinity by spreading
     220             :  *              the vectors around available CPUs
     221             :  *
     222             :  * Allocate up to @max_vecs interrupt vectors on device. MSI-X irq
     223             :  * vector allocation has a higher precedence over plain MSI, which has a
     224             :  * higher precedence over legacy INTx emulation.
     225             :  *
     226             :  * Upon a successful allocation, the caller should use pci_irq_vector()
     227             :  * to get the Linux IRQ number to be passed to request_threaded_irq().
     228             :  * The driver must call pci_free_irq_vectors() on cleanup.
     229             :  *
     230             :  * Return: number of allocated vectors (which might be smaller than
     231             :  * @max_vecs), -ENOSPC if less than @min_vecs interrupt vectors are
     232             :  * available, other errnos otherwise.
     233             :  */
     234           0 : int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
     235             :                           unsigned int max_vecs, unsigned int flags)
     236             : {
     237           0 :         return pci_alloc_irq_vectors_affinity(dev, min_vecs, max_vecs,
     238             :                                               flags, NULL);
     239             : }
     240             : EXPORT_SYMBOL(pci_alloc_irq_vectors);
     241             : 
     242             : /**
     243             :  * pci_alloc_irq_vectors_affinity() - Allocate multiple device interrupt
     244             :  *                                    vectors with affinity requirements
     245             :  * @dev:      the PCI device to operate on
     246             :  * @min_vecs: minimum required number of vectors (must be >= 1)
     247             :  * @max_vecs: maximum desired number of vectors
     248             :  * @flags:    allocation flags, as in pci_alloc_irq_vectors()
     249             :  * @affd:     affinity requirements (can be %NULL).
     250             :  *
     251             :  * Same as pci_alloc_irq_vectors(), but with the extra @affd parameter.
     252             :  * Check that function docs, and &struct irq_affinity, for more details.
     253             :  */
     254           0 : int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
     255             :                                    unsigned int max_vecs, unsigned int flags,
     256             :                                    struct irq_affinity *affd)
     257             : {
     258           0 :         struct irq_affinity msi_default_affd = {0};
     259           0 :         int nvecs = -ENOSPC;
     260             : 
     261           0 :         if (flags & PCI_IRQ_AFFINITY) {
     262           0 :                 if (!affd)
     263           0 :                         affd = &msi_default_affd;
     264             :         } else {
     265           0 :                 if (WARN_ON(affd))
     266           0 :                         affd = NULL;
     267             :         }
     268             : 
     269           0 :         if (flags & PCI_IRQ_MSIX) {
     270           0 :                 nvecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs,
     271             :                                                 affd, flags);
     272           0 :                 if (nvecs > 0)
     273             :                         return nvecs;
     274             :         }
     275             : 
     276           0 :         if (flags & PCI_IRQ_MSI) {
     277           0 :                 nvecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, affd);
     278           0 :                 if (nvecs > 0)
     279             :                         return nvecs;
     280             :         }
     281             : 
     282             :         /* use legacy IRQ if allowed */
     283           0 :         if (flags & PCI_IRQ_LEGACY) {
     284           0 :                 if (min_vecs == 1 && dev->irq) {
     285             :                         /*
     286             :                          * Invoke the affinity spreading logic to ensure that
     287             :                          * the device driver can adjust queue configuration
     288             :                          * for the single interrupt case.
     289             :                          */
     290             :                         if (affd)
     291             :                                 irq_create_affinity_masks(1, affd);
     292           0 :                         pci_intx(dev, 1);
     293           0 :                         return 1;
     294             :                 }
     295             :         }
     296             : 
     297             :         return nvecs;
     298             : }
     299             : EXPORT_SYMBOL(pci_alloc_irq_vectors_affinity);
     300             : 
     301             : /**
     302             :  * pci_irq_vector() - Get Linux IRQ number of a device interrupt vector
     303             :  * @dev: the PCI device to operate on
     304             :  * @nr:  device-relative interrupt vector index (0-based); has different
     305             :  *       meanings, depending on interrupt mode:
     306             :  *
     307             :  *         * MSI-X     the index in the MSI-X vector table
     308             :  *         * MSI       the index of the enabled MSI vectors
     309             :  *         * INTx      must be 0
     310             :  *
     311             :  * Return: the Linux IRQ number, or -EINVAL if @nr is out of range
     312             :  */
     313           0 : int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
     314             : {
     315             :         unsigned int irq;
     316             : 
     317           0 :         if (!dev->msi_enabled && !dev->msix_enabled)
     318           0 :                 return !nr ? dev->irq : -EINVAL;
     319             : 
     320           0 :         irq = msi_get_virq(&dev->dev, nr);
     321           0 :         return irq ? irq : -EINVAL;
     322             : }
     323             : EXPORT_SYMBOL(pci_irq_vector);
     324             : 
     325             : /**
     326             :  * pci_irq_get_affinity() - Get a device interrupt vector affinity
     327             :  * @dev: the PCI device to operate on
     328             :  * @nr:  device-relative interrupt vector index (0-based); has different
     329             :  *       meanings, depending on interrupt mode:
     330             :  *
     331             :  *         * MSI-X     the index in the MSI-X vector table
     332             :  *         * MSI       the index of the enabled MSI vectors
     333             :  *         * INTx      must be 0
     334             :  *
     335             :  * Return: MSI/MSI-X vector affinity, NULL if @nr is out of range or if
     336             :  * the MSI(-X) vector was allocated without explicit affinity
     337             :  * requirements (e.g., by pci_enable_msi(), pci_enable_msix_range(), or
     338             :  * pci_alloc_irq_vectors() without the %PCI_IRQ_AFFINITY flag). Return a
     339             :  * generic set of CPU IDs representing all possible CPUs available
     340             :  * during system boot if the device is in legacy INTx mode.
     341             :  */
     342           0 : const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr)
     343             : {
     344           0 :         int idx, irq = pci_irq_vector(dev, nr);
     345             :         struct msi_desc *desc;
     346             : 
     347           0 :         if (WARN_ON_ONCE(irq <= 0))
     348             :                 return NULL;
     349             : 
     350           0 :         desc = irq_get_msi_desc(irq);
     351             :         /* Non-MSI does not have the information handy */
     352           0 :         if (!desc)
     353             :                 return cpu_possible_mask;
     354             : 
     355             :         /* MSI[X] interrupts can be allocated without affinity descriptor */
     356           0 :         if (!desc->affinity)
     357             :                 return NULL;
     358             : 
     359             :         /*
     360             :          * MSI has a mask array in the descriptor.
     361             :          * MSI-X has a single mask.
     362             :          */
     363           0 :         idx = dev->msi_enabled ? nr : 0;
     364           0 :         return &desc->affinity[idx].mask;
     365             : }
     366             : EXPORT_SYMBOL(pci_irq_get_affinity);
     367             : 
     368             : /**
     369             :  * pci_ims_alloc_irq - Allocate an interrupt on a PCI/IMS interrupt domain
     370             :  * @dev:        The PCI device to operate on
     371             :  * @icookie:    Pointer to an IMS implementation specific cookie for this
     372             :  *              IMS instance (PASID, queue ID, pointer...).
     373             :  *              The cookie content is copied into the MSI descriptor for the
     374             :  *              interrupt chip callbacks or domain specific setup functions.
     375             :  * @affdesc:    Optional pointer to an interrupt affinity descriptor
     376             :  *
     377             :  * There is no index for IMS allocations as IMS is an implementation
     378             :  * specific storage and does not have any direct associations between
     379             :  * index, which might be a pure software construct, and device
     380             :  * functionality. This association is established by the driver either via
     381             :  * the index - if there is a hardware table - or in case of purely software
     382             :  * managed IMS implementation the association happens via the
     383             :  * irq_write_msi_msg() callback of the implementation specific interrupt
     384             :  * chip, which utilizes the provided @icookie to store the MSI message in
     385             :  * the appropriate place.
     386             :  *
     387             :  * Return: A struct msi_map
     388             :  *
     389             :  *      On success msi_map::index contains the allocated index (>= 0) and
     390             :  *      msi_map::virq the allocated Linux interrupt number (> 0).
     391             :  *
     392             :  *      On fail msi_map::index contains the error code and msi_map::virq
     393             :  *      is set to 0.
     394             :  */
     395           0 : struct msi_map pci_ims_alloc_irq(struct pci_dev *dev, union msi_instance_cookie *icookie,
     396             :                                  const struct irq_affinity_desc *affdesc)
     397             : {
     398           0 :         return msi_domain_alloc_irq_at(&dev->dev, MSI_SECONDARY_DOMAIN, MSI_ANY_INDEX,
     399             :                                        affdesc, icookie);
     400             : }
     401             : EXPORT_SYMBOL_GPL(pci_ims_alloc_irq);
     402             : 
     403             : /**
     404             :  * pci_ims_free_irq - Allocate an interrupt on a PCI/IMS interrupt domain
     405             :  *                    which was allocated via pci_ims_alloc_irq()
     406             :  * @dev:        The PCI device to operate on
     407             :  * @map:        A struct msi_map describing the interrupt to free as
     408             :  *              returned from pci_ims_alloc_irq()
     409             :  */
     410           0 : void pci_ims_free_irq(struct pci_dev *dev, struct msi_map map)
     411             : {
     412           0 :         if (WARN_ON_ONCE(map.index < 0 || map.virq <= 0))
     413             :                 return;
     414           0 :         msi_domain_free_irqs_range(&dev->dev, MSI_SECONDARY_DOMAIN, map.index, map.index);
     415             : }
     416             : EXPORT_SYMBOL_GPL(pci_ims_free_irq);
     417             : 
     418             : /**
     419             :  * pci_free_irq_vectors() - Free previously allocated IRQs for a device
     420             :  * @dev: the PCI device to operate on
     421             :  *
     422             :  * Undo the interrupt vector allocations and possible device MSI/MSI-X
     423             :  * enablement earlier done through pci_alloc_irq_vectors_affinity() or
     424             :  * pci_alloc_irq_vectors().
     425             :  */
     426           0 : void pci_free_irq_vectors(struct pci_dev *dev)
     427             : {
     428           0 :         pci_disable_msix(dev);
     429           0 :         pci_disable_msi(dev);
     430           0 : }
     431             : EXPORT_SYMBOL(pci_free_irq_vectors);
     432             : 
     433             : /**
     434             :  * pci_restore_msi_state() - Restore cached MSI(-X) state on device
     435             :  * @dev: the PCI device to operate on
     436             :  *
     437             :  * Write the Linux-cached MSI(-X) state back on device. This is
     438             :  * typically useful upon system resume, or after an error-recovery PCI
     439             :  * adapter reset.
     440             :  */
     441           0 : void pci_restore_msi_state(struct pci_dev *dev)
     442             : {
     443           0 :         __pci_restore_msi_state(dev);
     444           0 :         __pci_restore_msix_state(dev);
     445           0 : }
     446             : EXPORT_SYMBOL_GPL(pci_restore_msi_state);
     447             : 
     448             : /**
     449             :  * pci_msi_enabled() - Are MSI(-X) interrupts enabled system-wide?
     450             :  *
     451             :  * Return: true if MSI has not been globally disabled through ACPI FADT,
     452             :  * PCI bridge quirks, or the "pci=nomsi" kernel command-line option.
     453             :  */
     454           0 : int pci_msi_enabled(void)
     455             : {
     456           0 :         return pci_msi_enable;
     457             : }
     458             : EXPORT_SYMBOL(pci_msi_enabled);

Generated by: LCOV version 1.14