linux/drivers/irqchip/irq-gic-v3-its-pci-msi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
   3 * Author: Marc Zyngier <marc.zyngier@arm.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16 */
  17
  18#include <linux/msi.h>
  19#include <linux/of.h>
  20#include <linux/of_irq.h>
  21#include <linux/of_pci.h>
  22
  23static void its_mask_msi_irq(struct irq_data *d)
  24{
  25        pci_msi_mask_irq(d);
  26        irq_chip_mask_parent(d);
  27}
  28
  29static void its_unmask_msi_irq(struct irq_data *d)
  30{
  31        pci_msi_unmask_irq(d);
  32        irq_chip_unmask_parent(d);
  33}
  34
  35static struct irq_chip its_msi_irq_chip = {
  36        .name                   = "ITS-MSI",
  37        .irq_unmask             = its_unmask_msi_irq,
  38        .irq_mask               = its_mask_msi_irq,
  39        .irq_eoi                = irq_chip_eoi_parent,
  40        .irq_write_msi_msg      = pci_msi_domain_write_msg,
  41};
  42
  43struct its_pci_alias {
  44        struct pci_dev  *pdev;
  45        u32             count;
  46};
  47
  48static int its_pci_msi_vec_count(struct pci_dev *pdev)
  49{
  50        int msi, msix;
  51
  52        msi = max(pci_msi_vec_count(pdev), 0);
  53        msix = max(pci_msix_vec_count(pdev), 0);
  54
  55        return max(msi, msix);
  56}
  57
  58static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
  59{
  60        struct its_pci_alias *dev_alias = data;
  61
  62        if (pdev != dev_alias->pdev)
  63                dev_alias->count += its_pci_msi_vec_count(pdev);
  64
  65        return 0;
  66}
  67
  68static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
  69                               int nvec, msi_alloc_info_t *info)
  70{
  71        struct pci_dev *pdev;
  72        struct its_pci_alias dev_alias;
  73        struct msi_domain_info *msi_info;
  74
  75        if (!dev_is_pci(dev))
  76                return -EINVAL;
  77
  78        msi_info = msi_get_domain_info(domain->parent);
  79
  80        pdev = to_pci_dev(dev);
  81        dev_alias.pdev = pdev;
  82        dev_alias.count = nvec;
  83
  84        pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
  85
  86        /* ITS specific DeviceID, as the core ITS ignores dev. */
  87        info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
  88
  89        return msi_info->ops->msi_prepare(domain->parent,
  90                                          dev, dev_alias.count, info);
  91}
  92
  93static struct msi_domain_ops its_pci_msi_ops = {
  94        .msi_prepare    = its_pci_msi_prepare,
  95};
  96
  97static struct msi_domain_info its_pci_msi_domain_info = {
  98        .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
  99                   MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
 100        .ops    = &its_pci_msi_ops,
 101        .chip   = &its_msi_irq_chip,
 102};
 103
 104static struct of_device_id its_device_id[] = {
 105        {       .compatible     = "arm,gic-v3-its",     },
 106        {},
 107};
 108
 109static int __init its_pci_msi_init(void)
 110{
 111        struct device_node *np;
 112        struct irq_domain *parent;
 113
 114        for (np = of_find_matching_node(NULL, its_device_id); np;
 115             np = of_find_matching_node(np, its_device_id)) {
 116                if (!of_property_read_bool(np, "msi-controller"))
 117                        continue;
 118
 119                parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
 120                if (!parent || !msi_get_domain_info(parent)) {
 121                        pr_err("%s: unable to locate ITS domain\n",
 122                               np->full_name);
 123                        continue;
 124                }
 125
 126                if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
 127                                               &its_pci_msi_domain_info,
 128                                               parent)) {
 129                        pr_err("%s: unable to create PCI domain\n",
 130                               np->full_name);
 131                        continue;
 132                }
 133
 134                pr_info("PCI/MSI: %s domain created\n", np->full_name);
 135        }
 136
 137        return 0;
 138}
 139early_initcall(its_pci_msi_init);
 140