linux/drivers/irqchip/irq-gic-v3-its-pci-msi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
   4 * Author: Marc Zyngier <marc.zyngier@arm.com>
   5 */
   6
   7#include <linux/acpi_iort.h>
   8#include <linux/pci.h>
   9#include <linux/msi.h>
  10#include <linux/of.h>
  11#include <linux/of_irq.h>
  12#include <linux/of_pci.h>
  13
  14static void its_mask_msi_irq(struct irq_data *d)
  15{
  16        pci_msi_mask_irq(d);
  17        irq_chip_mask_parent(d);
  18}
  19
  20static void its_unmask_msi_irq(struct irq_data *d)
  21{
  22        pci_msi_unmask_irq(d);
  23        irq_chip_unmask_parent(d);
  24}
  25
  26static struct irq_chip its_msi_irq_chip = {
  27        .name                   = "ITS-MSI",
  28        .irq_unmask             = its_unmask_msi_irq,
  29        .irq_mask               = its_mask_msi_irq,
  30        .irq_eoi                = irq_chip_eoi_parent,
  31        .irq_write_msi_msg      = pci_msi_domain_write_msg,
  32};
  33
  34static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
  35{
  36        int msi, msix, *count = data;
  37
  38        msi = max(pci_msi_vec_count(pdev), 0);
  39        msix = max(pci_msix_vec_count(pdev), 0);
  40        *count += max(msi, msix);
  41
  42        return 0;
  43}
  44
  45static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
  46{
  47        struct pci_dev **alias_dev = data;
  48
  49        *alias_dev = pdev;
  50
  51        return 0;
  52}
  53
  54static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
  55                               int nvec, msi_alloc_info_t *info)
  56{
  57        struct pci_dev *pdev, *alias_dev;
  58        struct msi_domain_info *msi_info;
  59        int alias_count = 0, minnvec = 1;
  60
  61        if (!dev_is_pci(dev))
  62                return -EINVAL;
  63
  64        msi_info = msi_get_domain_info(domain->parent);
  65
  66        pdev = to_pci_dev(dev);
  67        /*
  68         * If pdev is downstream of any aliasing bridges, take an upper
  69         * bound of how many other vectors could map to the same DevID.
  70         * Also tell the ITS that the signalling will come from a proxy
  71         * device, and that special allocation rules apply.
  72         */
  73        pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev);
  74        if (alias_dev != pdev) {
  75                if (alias_dev->subordinate)
  76                        pci_walk_bus(alias_dev->subordinate,
  77                                     its_pci_msi_vec_count, &alias_count);
  78                info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
  79        }
  80
  81        /* ITS specific DeviceID, as the core ITS ignores dev. */
  82        info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
  83
  84        /*
  85         * Always allocate a power of 2, and special case device 0 for
  86         * broken systems where the DevID is not wired (and all devices
  87         * appear as DevID 0). For that reason, we generously allocate a
  88         * minimum of 32 MSIs for DevID 0. If you want more because all
  89         * your devices are aliasing to DevID 0, consider fixing your HW.
  90         */
  91        nvec = max(nvec, alias_count);
  92        if (!info->scratchpad[0].ul)
  93                minnvec = 32;
  94        nvec = max_t(int, minnvec, roundup_pow_of_two(nvec));
  95        return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
  96}
  97
  98static struct msi_domain_ops its_pci_msi_ops = {
  99        .msi_prepare    = its_pci_msi_prepare,
 100};
 101
 102static struct msi_domain_info its_pci_msi_domain_info = {
 103        .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
 104                   MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
 105        .ops    = &its_pci_msi_ops,
 106        .chip   = &its_msi_irq_chip,
 107};
 108
 109static struct of_device_id its_device_id[] = {
 110        {       .compatible     = "arm,gic-v3-its",     },
 111        {},
 112};
 113
 114static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
 115                                       const char *name)
 116{
 117        struct irq_domain *parent;
 118
 119        parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
 120        if (!parent || !msi_get_domain_info(parent)) {
 121                pr_err("%s: Unable to locate ITS domain\n", name);
 122                return -ENXIO;
 123        }
 124
 125        if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
 126                                       parent)) {
 127                pr_err("%s: Unable to create PCI domain\n", name);
 128                return -ENOMEM;
 129        }
 130
 131        return 0;
 132}
 133
 134static int __init its_pci_of_msi_init(void)
 135{
 136        struct device_node *np;
 137
 138        for (np = of_find_matching_node(NULL, its_device_id); np;
 139             np = of_find_matching_node(np, its_device_id)) {
 140                if (!of_device_is_available(np))
 141                        continue;
 142                if (!of_property_read_bool(np, "msi-controller"))
 143                        continue;
 144
 145                if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name))
 146                        continue;
 147
 148                pr_info("PCI/MSI: %pOF domain created\n", np);
 149        }
 150
 151        return 0;
 152}
 153
 154#ifdef CONFIG_ACPI
 155
 156static int __init
 157its_pci_msi_parse_madt(union acpi_subtable_headers *header,
 158                       const unsigned long end)
 159{
 160        struct acpi_madt_generic_translator *its_entry;
 161        struct fwnode_handle *dom_handle;
 162        const char *node_name;
 163        int err = -ENXIO;
 164
 165        its_entry = (struct acpi_madt_generic_translator *)header;
 166        node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
 167                              (long)its_entry->base_address);
 168        dom_handle = iort_find_domain_token(its_entry->translation_id);
 169        if (!dom_handle) {
 170                pr_err("%s: Unable to locate ITS domain handle\n", node_name);
 171                goto out;
 172        }
 173
 174        err = its_pci_msi_init_one(dom_handle, node_name);
 175        if (!err)
 176                pr_info("PCI/MSI: %s domain created\n", node_name);
 177
 178out:
 179        kfree(node_name);
 180        return err;
 181}
 182
 183static int __init its_pci_acpi_msi_init(void)
 184{
 185        acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
 186                              its_pci_msi_parse_madt, 0);
 187        return 0;
 188}
 189#else
 190static int __init its_pci_acpi_msi_init(void)
 191{
 192        return 0;
 193}
 194#endif
 195
 196static int __init its_pci_msi_init(void)
 197{
 198        its_pci_of_msi_init();
 199        its_pci_acpi_msi_init();
 200
 201        return 0;
 202}
 203early_initcall(its_pci_msi_init);
 204