LCOV - code coverage report
Current view: top level - drivers/pci/msi - irqdomain.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 92 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 12 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * PCI Message Signaled Interrupt (MSI) - irqdomain support
       4             :  */
       5             : #include <linux/acpi_iort.h>
       6             : #include <linux/irqdomain.h>
       7             : #include <linux/of_irq.h>
       8             : 
       9             : #include "msi.h"
      10             : 
      11           0 : int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
      12             : {
      13             :         struct irq_domain *domain;
      14             : 
      15           0 :         domain = dev_get_msi_domain(&dev->dev);
      16           0 :         if (domain && irq_domain_is_hierarchy(domain))
      17           0 :                 return msi_domain_alloc_irqs_descs_locked(domain, &dev->dev, nvec);
      18             : 
      19           0 :         return pci_msi_legacy_setup_msi_irqs(dev, nvec, type);
      20             : }
      21             : 
      22           0 : void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
      23             : {
      24             :         struct irq_domain *domain;
      25             : 
      26           0 :         domain = dev_get_msi_domain(&dev->dev);
      27           0 :         if (domain && irq_domain_is_hierarchy(domain))
      28           0 :                 msi_domain_free_irqs_descs_locked(domain, &dev->dev);
      29             :         else
      30           0 :                 pci_msi_legacy_teardown_msi_irqs(dev);
      31           0 :         msi_free_msi_descs(&dev->dev);
      32           0 : }
      33             : 
      34             : /**
      35             :  * pci_msi_domain_write_msg - Helper to write MSI message to PCI config space
      36             :  * @irq_data:   Pointer to interrupt data of the MSI interrupt
      37             :  * @msg:        Pointer to the message
      38             :  */
      39           0 : static void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg)
      40             : {
      41           0 :         struct msi_desc *desc = irq_data_get_msi_desc(irq_data);
      42             : 
      43             :         /*
      44             :          * For MSI-X desc->irq is always equal to irq_data->irq. For
      45             :          * MSI only the first interrupt of MULTI MSI passes the test.
      46             :          */
      47           0 :         if (desc->irq == irq_data->irq)
      48           0 :                 __pci_write_msi_msg(desc, msg);
      49           0 : }
      50             : 
      51             : /**
      52             :  * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source
      53             :  * @desc:       Pointer to the MSI descriptor
      54             :  *
      55             :  * The ID number is only used within the irqdomain.
      56             :  */
      57           0 : static irq_hw_number_t pci_msi_domain_calc_hwirq(struct msi_desc *desc)
      58             : {
      59           0 :         struct pci_dev *dev = msi_desc_to_pci_dev(desc);
      60             : 
      61           0 :         return (irq_hw_number_t)desc->msi_index |
      62           0 :                 pci_dev_id(dev) << 11 |
      63           0 :                 (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27;
      64             : }
      65             : 
      66             : static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc)
      67             : {
      68           0 :         return !desc->pci.msi_attrib.is_msix && desc->nvec_used > 1;
      69             : }
      70             : 
      71             : /**
      72             :  * pci_msi_domain_check_cap - Verify that @domain supports the capabilities
      73             :  *                            for @dev
      74             :  * @domain:     The interrupt domain to check
      75             :  * @info:       The domain info for verification
      76             :  * @dev:        The device to check
      77             :  *
      78             :  * Returns:
      79             :  *  0 if the functionality is supported
      80             :  *  1 if Multi MSI is requested, but the domain does not support it
      81             :  *  -ENOTSUPP otherwise
      82             :  */
      83           0 : static int pci_msi_domain_check_cap(struct irq_domain *domain,
      84             :                                     struct msi_domain_info *info,
      85             :                                     struct device *dev)
      86             : {
      87           0 :         struct msi_desc *desc = msi_first_desc(dev, MSI_DESC_ALL);
      88             : 
      89             :         /* Special handling to support __pci_enable_msi_range() */
      90           0 :         if (pci_msi_desc_is_multi_msi(desc) &&
      91           0 :             !(info->flags & MSI_FLAG_MULTI_PCI_MSI))
      92             :                 return 1;
      93             : 
      94           0 :         if (desc->pci.msi_attrib.is_msix) {
      95           0 :                 if (!(info->flags & MSI_FLAG_PCI_MSIX))
      96             :                         return -ENOTSUPP;
      97             : 
      98           0 :                 if (info->flags & MSI_FLAG_MSIX_CONTIGUOUS) {
      99           0 :                         unsigned int idx = 0;
     100             : 
     101             :                         /* Check for gaps in the entry indices */
     102           0 :                         msi_for_each_desc(desc, dev, MSI_DESC_ALL) {
     103           0 :                                 if (desc->msi_index != idx++)
     104             :                                         return -ENOTSUPP;
     105             :                         }
     106             :                 }
     107             :         }
     108             :         return 0;
     109             : }
     110             : 
     111           0 : static void pci_msi_domain_set_desc(msi_alloc_info_t *arg,
     112             :                                     struct msi_desc *desc)
     113             : {
     114           0 :         arg->desc = desc;
     115           0 :         arg->hwirq = pci_msi_domain_calc_hwirq(desc);
     116           0 : }
     117             : 
     118             : static struct msi_domain_ops pci_msi_domain_ops_default = {
     119             :         .set_desc       = pci_msi_domain_set_desc,
     120             :         .msi_check      = pci_msi_domain_check_cap,
     121             : };
     122             : 
     123             : static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info)
     124             : {
     125           0 :         struct msi_domain_ops *ops = info->ops;
     126             : 
     127           0 :         if (ops == NULL) {
     128           0 :                 info->ops = &pci_msi_domain_ops_default;
     129             :         } else {
     130           0 :                 if (ops->set_desc == NULL)
     131           0 :                         ops->set_desc = pci_msi_domain_set_desc;
     132           0 :                 if (ops->msi_check == NULL)
     133           0 :                         ops->msi_check = pci_msi_domain_check_cap;
     134             :         }
     135             : }
     136             : 
     137           0 : static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
     138             : {
     139           0 :         struct irq_chip *chip = info->chip;
     140             : 
     141           0 :         BUG_ON(!chip);
     142           0 :         if (!chip->irq_write_msi_msg)
     143           0 :                 chip->irq_write_msi_msg = pci_msi_domain_write_msg;
     144           0 :         if (!chip->irq_mask)
     145           0 :                 chip->irq_mask = pci_msi_mask_irq;
     146           0 :         if (!chip->irq_unmask)
     147           0 :                 chip->irq_unmask = pci_msi_unmask_irq;
     148           0 : }
     149             : 
     150             : /**
     151             :  * pci_msi_create_irq_domain - Create a MSI interrupt domain
     152             :  * @fwnode:     Optional fwnode of the interrupt controller
     153             :  * @info:       MSI domain info
     154             :  * @parent:     Parent irq domain
     155             :  *
     156             :  * Updates the domain and chip ops and creates a MSI interrupt domain.
     157             :  *
     158             :  * Returns:
     159             :  * A domain pointer or NULL in case of failure.
     160             :  */
     161           0 : struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
     162             :                                              struct msi_domain_info *info,
     163             :                                              struct irq_domain *parent)
     164             : {
     165             :         struct irq_domain *domain;
     166             : 
     167           0 :         if (WARN_ON(info->flags & MSI_FLAG_LEVEL_CAPABLE))
     168           0 :                 info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
     169             : 
     170           0 :         if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
     171           0 :                 pci_msi_domain_update_dom_ops(info);
     172           0 :         if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
     173           0 :                 pci_msi_domain_update_chip_ops(info);
     174             : 
     175           0 :         info->flags |= MSI_FLAG_ACTIVATE_EARLY | MSI_FLAG_DEV_SYSFS;
     176             :         if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE))
     177             :                 info->flags |= MSI_FLAG_MUST_REACTIVATE;
     178             : 
     179             :         /* PCI-MSI is oneshot-safe */
     180           0 :         info->chip->flags |= IRQCHIP_ONESHOT_SAFE;
     181             : 
     182           0 :         domain = msi_create_irq_domain(fwnode, info, parent);
     183           0 :         if (!domain)
     184             :                 return NULL;
     185             : 
     186           0 :         irq_domain_update_bus_token(domain, DOMAIN_BUS_PCI_MSI);
     187           0 :         return domain;
     188             : }
     189             : EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain);
     190             : 
     191             : /*
     192             :  * Users of the generic MSI infrastructure expect a device to have a single ID,
     193             :  * so with DMA aliases we have to pick the least-worst compromise. Devices with
     194             :  * DMA phantom functions tend to still emit MSIs from the real function number,
     195             :  * so we ignore those and only consider topological aliases where either the
     196             :  * alias device or RID appears on a different bus number. We also make the
     197             :  * reasonable assumption that bridges are walked in an upstream direction (so
     198             :  * the last one seen wins), and the much braver assumption that the most likely
     199             :  * case is that of PCI->PCIe so we should always use the alias RID. This echoes
     200             :  * the logic from intel_irq_remapping's set_msi_sid(), which presumably works
     201             :  * well enough in practice; in the face of the horrible PCIe<->PCI-X conditions
     202             :  * for taking ownership all we can really do is close our eyes and hope...
     203             :  */
     204           0 : static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data)
     205             : {
     206           0 :         u32 *pa = data;
     207           0 :         u8 bus = PCI_BUS_NUM(*pa);
     208             : 
     209           0 :         if (pdev->bus->number != bus || PCI_BUS_NUM(alias) != bus)
     210           0 :                 *pa = alias;
     211             : 
     212           0 :         return 0;
     213             : }
     214             : 
     215             : /**
     216             :  * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID)
     217             :  * @domain:     The interrupt domain
     218             :  * @pdev:       The PCI device.
     219             :  *
     220             :  * The RID for a device is formed from the alias, with a firmware
     221             :  * supplied mapping applied
     222             :  *
     223             :  * Returns: The RID.
     224             :  */
     225           0 : u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
     226             : {
     227             :         struct device_node *of_node;
     228           0 :         u32 rid = pci_dev_id(pdev);
     229             : 
     230           0 :         pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
     231             : 
     232           0 :         of_node = irq_domain_get_of_node(domain);
     233           0 :         rid = of_node ? of_msi_map_id(&pdev->dev, of_node, rid) :
     234           0 :                         iort_msi_map_id(&pdev->dev, rid);
     235             : 
     236           0 :         return rid;
     237             : }
     238             : 
     239             : /**
     240             :  * pci_msi_get_device_domain - Get the MSI domain for a given PCI device
     241             :  * @pdev:       The PCI device
     242             :  *
     243             :  * Use the firmware data to find a device-specific MSI domain
     244             :  * (i.e. not one that is set as a default).
     245             :  *
     246             :  * Returns: The corresponding MSI domain or NULL if none has been found.
     247             :  */
     248           0 : struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
     249             : {
     250             :         struct irq_domain *dom;
     251           0 :         u32 rid = pci_dev_id(pdev);
     252             : 
     253           0 :         pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
     254           0 :         dom = of_msi_map_get_device_domain(&pdev->dev, rid, DOMAIN_BUS_PCI_MSI);
     255             :         if (!dom)
     256           0 :                 dom = iort_get_device_domain(&pdev->dev, rid,
     257             :                                              DOMAIN_BUS_PCI_MSI);
     258           0 :         return dom;
     259             : }
     260             : 
     261             : /**
     262             :  * pci_dev_has_special_msi_domain - Check whether the device is handled by
     263             :  *                                  a non-standard PCI-MSI domain
     264             :  * @pdev:       The PCI device to check.
     265             :  *
     266             :  * Returns: True if the device irqdomain or the bus irqdomain is
     267             :  * non-standard PCI/MSI.
     268             :  */
     269           0 : bool pci_dev_has_special_msi_domain(struct pci_dev *pdev)
     270             : {
     271           0 :         struct irq_domain *dom = dev_get_msi_domain(&pdev->dev);
     272             : 
     273           0 :         if (!dom)
     274           0 :                 dom = dev_get_msi_domain(&pdev->bus->dev);
     275             : 
     276           0 :         if (!dom)
     277             :                 return true;
     278             : 
     279           0 :         return dom->bus_token != DOMAIN_BUS_PCI_MSI;
     280             : }

Generated by: LCOV version 1.14