xref: /openbmc/linux/drivers/irqchip/irq-gic-v3-its-pci-msi.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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