1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f130420eSMarc Zyngier /*
3f130420eSMarc Zyngier * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
4f130420eSMarc Zyngier * Author: Marc Zyngier <marc.zyngier@arm.com>
5f130420eSMarc Zyngier */
6f130420eSMarc Zyngier
7723344ddSTomasz Nowicki #include <linux/acpi_iort.h>
8bbd8810dSKrzysztof Wilczynski #include <linux/pci.h>
9f130420eSMarc Zyngier #include <linux/msi.h>
10f130420eSMarc Zyngier #include <linux/of.h>
11f130420eSMarc Zyngier #include <linux/of_irq.h>
12f130420eSMarc Zyngier #include <linux/of_pci.h>
13f130420eSMarc Zyngier
its_mask_msi_irq(struct irq_data * d)14f130420eSMarc Zyngier static void its_mask_msi_irq(struct irq_data *d)
15f130420eSMarc Zyngier {
16f130420eSMarc Zyngier pci_msi_mask_irq(d);
17f130420eSMarc Zyngier irq_chip_mask_parent(d);
18f130420eSMarc Zyngier }
19f130420eSMarc Zyngier
its_unmask_msi_irq(struct irq_data * d)20f130420eSMarc Zyngier static void its_unmask_msi_irq(struct irq_data *d)
21f130420eSMarc Zyngier {
22f130420eSMarc Zyngier pci_msi_unmask_irq(d);
23f130420eSMarc Zyngier irq_chip_unmask_parent(d);
24f130420eSMarc Zyngier }
25f130420eSMarc Zyngier
26f130420eSMarc Zyngier static struct irq_chip its_msi_irq_chip = {
27f130420eSMarc Zyngier .name = "ITS-MSI",
28f130420eSMarc Zyngier .irq_unmask = its_unmask_msi_irq,
29f130420eSMarc Zyngier .irq_mask = its_mask_msi_irq,
30f130420eSMarc Zyngier .irq_eoi = irq_chip_eoi_parent,
31f130420eSMarc Zyngier };
32f130420eSMarc Zyngier
its_pci_msi_vec_count(struct pci_dev * pdev,void * data)333403b025SRobin Murphy static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
34f130420eSMarc Zyngier {
353403b025SRobin Murphy int msi, msix, *count = data;
36f130420eSMarc Zyngier
37f130420eSMarc Zyngier msi = max(pci_msi_vec_count(pdev), 0);
38f130420eSMarc Zyngier msix = max(pci_msix_vec_count(pdev), 0);
393403b025SRobin Murphy *count += max(msi, msix);
40f130420eSMarc Zyngier
413403b025SRobin Murphy return 0;
42f130420eSMarc Zyngier }
43f130420eSMarc Zyngier
its_get_pci_alias(struct pci_dev * pdev,u16 alias,void * data)44f130420eSMarc Zyngier static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
45f130420eSMarc Zyngier {
463403b025SRobin Murphy struct pci_dev **alias_dev = data;
47f130420eSMarc Zyngier
483403b025SRobin Murphy *alias_dev = pdev;
49f130420eSMarc Zyngier
50f130420eSMarc Zyngier return 0;
51f130420eSMarc Zyngier }
52f130420eSMarc Zyngier
its_pci_msi_prepare(struct irq_domain * domain,struct device * dev,int nvec,msi_alloc_info_t * info)53f130420eSMarc Zyngier static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
54f130420eSMarc Zyngier int nvec, msi_alloc_info_t *info)
55f130420eSMarc Zyngier {
563403b025SRobin Murphy struct pci_dev *pdev, *alias_dev;
5754456db9SMarc Zyngier struct msi_domain_info *msi_info;
5830800b3aSMarc Zyngier int alias_count = 0, minnvec = 1;
59f130420eSMarc Zyngier
60f130420eSMarc Zyngier if (!dev_is_pci(dev))
61f130420eSMarc Zyngier return -EINVAL;
62f130420eSMarc Zyngier
6354456db9SMarc Zyngier msi_info = msi_get_domain_info(domain->parent);
6454456db9SMarc Zyngier
65f130420eSMarc Zyngier pdev = to_pci_dev(dev);
663403b025SRobin Murphy /*
673403b025SRobin Murphy * If pdev is downstream of any aliasing bridges, take an upper
683403b025SRobin Murphy * bound of how many other vectors could map to the same DevID.
69*34dd263fSMarc Zyngier * Also tell the ITS that the signalling will come from a proxy
70*34dd263fSMarc Zyngier * device, and that special allocation rules apply.
713403b025SRobin Murphy */
723403b025SRobin Murphy pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev);
73*34dd263fSMarc Zyngier if (alias_dev != pdev) {
74*34dd263fSMarc Zyngier if (alias_dev->subordinate)
75*34dd263fSMarc Zyngier pci_walk_bus(alias_dev->subordinate,
76*34dd263fSMarc Zyngier its_pci_msi_vec_count, &alias_count);
77*34dd263fSMarc Zyngier info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
78*34dd263fSMarc Zyngier }
79f130420eSMarc Zyngier
8054456db9SMarc Zyngier /* ITS specific DeviceID, as the core ITS ignores dev. */
81ccf91e68SDavid Daney info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
8254456db9SMarc Zyngier
8330800b3aSMarc Zyngier /*
8430800b3aSMarc Zyngier * Always allocate a power of 2, and special case device 0 for
8530800b3aSMarc Zyngier * broken systems where the DevID is not wired (and all devices
8630800b3aSMarc Zyngier * appear as DevID 0). For that reason, we generously allocate a
8730800b3aSMarc Zyngier * minimum of 32 MSIs for DevID 0. If you want more because all
8830800b3aSMarc Zyngier * your devices are aliasing to DevID 0, consider fixing your HW.
8930800b3aSMarc Zyngier */
90147c8f37SMarc Zyngier nvec = max(nvec, alias_count);
9130800b3aSMarc Zyngier if (!info->scratchpad[0].ul)
9230800b3aSMarc Zyngier minnvec = 32;
9330800b3aSMarc Zyngier nvec = max_t(int, minnvec, roundup_pow_of_two(nvec));
94147c8f37SMarc Zyngier return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
95f130420eSMarc Zyngier }
96f130420eSMarc Zyngier
97f130420eSMarc Zyngier static struct msi_domain_ops its_pci_msi_ops = {
98f130420eSMarc Zyngier .msi_prepare = its_pci_msi_prepare,
99f130420eSMarc Zyngier };
100f130420eSMarc Zyngier
101f130420eSMarc Zyngier static struct msi_domain_info its_pci_msi_domain_info = {
102f130420eSMarc Zyngier .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
103f130420eSMarc Zyngier MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
104f130420eSMarc Zyngier .ops = &its_pci_msi_ops,
105f130420eSMarc Zyngier .chip = &its_msi_irq_chip,
106f130420eSMarc Zyngier };
107f130420eSMarc Zyngier
10854456db9SMarc Zyngier static struct of_device_id its_device_id[] = {
10954456db9SMarc Zyngier { .compatible = "arm,gic-v3-its", },
11054456db9SMarc Zyngier {},
11154456db9SMarc Zyngier };
11254456db9SMarc Zyngier
its_pci_msi_init_one(struct fwnode_handle * handle,const char * name)113db744aaaSTomasz Nowicki static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
114db744aaaSTomasz Nowicki const char *name)
115db744aaaSTomasz Nowicki {
116db744aaaSTomasz Nowicki struct irq_domain *parent;
117db744aaaSTomasz Nowicki
118db744aaaSTomasz Nowicki parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
119db744aaaSTomasz Nowicki if (!parent || !msi_get_domain_info(parent)) {
120db744aaaSTomasz Nowicki pr_err("%s: Unable to locate ITS domain\n", name);
121db744aaaSTomasz Nowicki return -ENXIO;
122db744aaaSTomasz Nowicki }
123db744aaaSTomasz Nowicki
124db744aaaSTomasz Nowicki if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
125db744aaaSTomasz Nowicki parent)) {
126db744aaaSTomasz Nowicki pr_err("%s: Unable to create PCI domain\n", name);
127db744aaaSTomasz Nowicki return -ENOMEM;
128db744aaaSTomasz Nowicki }
129db744aaaSTomasz Nowicki
130db744aaaSTomasz Nowicki return 0;
131db744aaaSTomasz Nowicki }
132db744aaaSTomasz Nowicki
its_pci_of_msi_init(void)133db744aaaSTomasz Nowicki static int __init its_pci_of_msi_init(void)
134f130420eSMarc Zyngier {
13554456db9SMarc Zyngier struct device_node *np;
13654456db9SMarc Zyngier
13754456db9SMarc Zyngier for (np = of_find_matching_node(NULL, its_device_id); np;
13854456db9SMarc Zyngier np = of_find_matching_node(np, its_device_id)) {
13995a25625SStephen Boyd if (!of_device_is_available(np))
14095a25625SStephen Boyd continue;
14154456db9SMarc Zyngier if (!of_property_read_bool(np, "msi-controller"))
14254456db9SMarc Zyngier continue;
14354456db9SMarc Zyngier
144db744aaaSTomasz Nowicki if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name))
14554456db9SMarc Zyngier continue;
14654456db9SMarc Zyngier
147e81f54c6SRob Herring pr_info("PCI/MSI: %pOF domain created\n", np);
14854456db9SMarc Zyngier }
14954456db9SMarc Zyngier
15054456db9SMarc Zyngier return 0;
15154456db9SMarc Zyngier }
152db744aaaSTomasz Nowicki
153723344ddSTomasz Nowicki #ifdef CONFIG_ACPI
154723344ddSTomasz Nowicki
155723344ddSTomasz Nowicki static int __init
its_pci_msi_parse_madt(union acpi_subtable_headers * header,const unsigned long end)15660574d1eSKeith Busch its_pci_msi_parse_madt(union acpi_subtable_headers *header,
157723344ddSTomasz Nowicki const unsigned long end)
158723344ddSTomasz Nowicki {
159723344ddSTomasz Nowicki struct acpi_madt_generic_translator *its_entry;
160723344ddSTomasz Nowicki struct fwnode_handle *dom_handle;
161723344ddSTomasz Nowicki const char *node_name;
162723344ddSTomasz Nowicki int err = -ENXIO;
163723344ddSTomasz Nowicki
164723344ddSTomasz Nowicki its_entry = (struct acpi_madt_generic_translator *)header;
165723344ddSTomasz Nowicki node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
166723344ddSTomasz Nowicki (long)its_entry->base_address);
167723344ddSTomasz Nowicki dom_handle = iort_find_domain_token(its_entry->translation_id);
168723344ddSTomasz Nowicki if (!dom_handle) {
169723344ddSTomasz Nowicki pr_err("%s: Unable to locate ITS domain handle\n", node_name);
170723344ddSTomasz Nowicki goto out;
171723344ddSTomasz Nowicki }
172723344ddSTomasz Nowicki
173723344ddSTomasz Nowicki err = its_pci_msi_init_one(dom_handle, node_name);
174723344ddSTomasz Nowicki if (!err)
175723344ddSTomasz Nowicki pr_info("PCI/MSI: %s domain created\n", node_name);
176723344ddSTomasz Nowicki
177723344ddSTomasz Nowicki out:
178723344ddSTomasz Nowicki kfree(node_name);
179723344ddSTomasz Nowicki return err;
180723344ddSTomasz Nowicki }
181723344ddSTomasz Nowicki
its_pci_acpi_msi_init(void)182723344ddSTomasz Nowicki static int __init its_pci_acpi_msi_init(void)
183723344ddSTomasz Nowicki {
184723344ddSTomasz Nowicki acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
185723344ddSTomasz Nowicki its_pci_msi_parse_madt, 0);
186723344ddSTomasz Nowicki return 0;
187723344ddSTomasz Nowicki }
188723344ddSTomasz Nowicki #else
its_pci_acpi_msi_init(void)189723344ddSTomasz Nowicki static int __init its_pci_acpi_msi_init(void)
190723344ddSTomasz Nowicki {
191723344ddSTomasz Nowicki return 0;
192723344ddSTomasz Nowicki }
193723344ddSTomasz Nowicki #endif
194723344ddSTomasz Nowicki
its_pci_msi_init(void)195db744aaaSTomasz Nowicki static int __init its_pci_msi_init(void)
196db744aaaSTomasz Nowicki {
197db744aaaSTomasz Nowicki its_pci_of_msi_init();
198723344ddSTomasz Nowicki its_pci_acpi_msi_init();
199db744aaaSTomasz Nowicki
200db744aaaSTomasz Nowicki return 0;
201db744aaaSTomasz Nowicki }
20254456db9SMarc Zyngier early_initcall(its_pci_msi_init);
203