xref: /linux/drivers/irqchip/irq-gic-its-msi-parent.c (revision b4ead12d95002b9c65e3c646cf73e0a91c608024)
1*b4ead12dSLorenzo Pieralisi // SPDX-License-Identifier: GPL-2.0-only
2*b4ead12dSLorenzo Pieralisi // Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
3*b4ead12dSLorenzo Pieralisi // Author: Marc Zyngier <marc.zyngier@arm.com>
4*b4ead12dSLorenzo Pieralisi // Copyright (C) 2022 Linutronix GmbH
5*b4ead12dSLorenzo Pieralisi // Copyright (C) 2022 Intel
6*b4ead12dSLorenzo Pieralisi 
7*b4ead12dSLorenzo Pieralisi #include <linux/acpi_iort.h>
8*b4ead12dSLorenzo Pieralisi #include <linux/pci.h>
9*b4ead12dSLorenzo Pieralisi 
10*b4ead12dSLorenzo Pieralisi #include "irq-gic-its-msi-parent.h"
11*b4ead12dSLorenzo Pieralisi #include <linux/irqchip/irq-msi-lib.h>
12*b4ead12dSLorenzo Pieralisi 
13*b4ead12dSLorenzo Pieralisi #define ITS_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS |	\
14*b4ead12dSLorenzo Pieralisi 				 MSI_FLAG_USE_DEF_CHIP_OPS |	\
15*b4ead12dSLorenzo Pieralisi 				 MSI_FLAG_PCI_MSI_MASK_PARENT)
16*b4ead12dSLorenzo Pieralisi 
17*b4ead12dSLorenzo Pieralisi #define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK |	\
18*b4ead12dSLorenzo Pieralisi 				 MSI_FLAG_PCI_MSIX      |	\
19*b4ead12dSLorenzo Pieralisi 				 MSI_FLAG_MULTI_PCI_MSI)
20*b4ead12dSLorenzo Pieralisi 
21*b4ead12dSLorenzo Pieralisi #ifdef CONFIG_PCI_MSI
22*b4ead12dSLorenzo Pieralisi static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
23*b4ead12dSLorenzo Pieralisi {
24*b4ead12dSLorenzo Pieralisi 	int msi, msix, *count = data;
25*b4ead12dSLorenzo Pieralisi 
26*b4ead12dSLorenzo Pieralisi 	msi = max(pci_msi_vec_count(pdev), 0);
27*b4ead12dSLorenzo Pieralisi 	msix = max(pci_msix_vec_count(pdev), 0);
28*b4ead12dSLorenzo Pieralisi 	*count += max(msi, msix);
29*b4ead12dSLorenzo Pieralisi 
30*b4ead12dSLorenzo Pieralisi 	return 0;
31*b4ead12dSLorenzo Pieralisi }
32*b4ead12dSLorenzo Pieralisi 
33*b4ead12dSLorenzo Pieralisi static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
34*b4ead12dSLorenzo Pieralisi {
35*b4ead12dSLorenzo Pieralisi 	struct pci_dev **alias_dev = data;
36*b4ead12dSLorenzo Pieralisi 
37*b4ead12dSLorenzo Pieralisi 	*alias_dev = pdev;
38*b4ead12dSLorenzo Pieralisi 
39*b4ead12dSLorenzo Pieralisi 	return 0;
40*b4ead12dSLorenzo Pieralisi }
41*b4ead12dSLorenzo Pieralisi 
42*b4ead12dSLorenzo Pieralisi static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
43*b4ead12dSLorenzo Pieralisi 			       int nvec, msi_alloc_info_t *info)
44*b4ead12dSLorenzo Pieralisi {
45*b4ead12dSLorenzo Pieralisi 	struct pci_dev *pdev, *alias_dev;
46*b4ead12dSLorenzo Pieralisi 	struct msi_domain_info *msi_info;
47*b4ead12dSLorenzo Pieralisi 	int alias_count = 0, minnvec = 1;
48*b4ead12dSLorenzo Pieralisi 
49*b4ead12dSLorenzo Pieralisi 	if (!dev_is_pci(dev))
50*b4ead12dSLorenzo Pieralisi 		return -EINVAL;
51*b4ead12dSLorenzo Pieralisi 
52*b4ead12dSLorenzo Pieralisi 	pdev = to_pci_dev(dev);
53*b4ead12dSLorenzo Pieralisi 	/*
54*b4ead12dSLorenzo Pieralisi 	 * If pdev is downstream of any aliasing bridges, take an upper
55*b4ead12dSLorenzo Pieralisi 	 * bound of how many other vectors could map to the same DevID.
56*b4ead12dSLorenzo Pieralisi 	 * Also tell the ITS that the signalling will come from a proxy
57*b4ead12dSLorenzo Pieralisi 	 * device, and that special allocation rules apply.
58*b4ead12dSLorenzo Pieralisi 	 */
59*b4ead12dSLorenzo Pieralisi 	pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev);
60*b4ead12dSLorenzo Pieralisi 	if (alias_dev != pdev) {
61*b4ead12dSLorenzo Pieralisi 		if (alias_dev->subordinate)
62*b4ead12dSLorenzo Pieralisi 			pci_walk_bus(alias_dev->subordinate,
63*b4ead12dSLorenzo Pieralisi 				     its_pci_msi_vec_count, &alias_count);
64*b4ead12dSLorenzo Pieralisi 		info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE;
65*b4ead12dSLorenzo Pieralisi 	}
66*b4ead12dSLorenzo Pieralisi 
67*b4ead12dSLorenzo Pieralisi 	/* ITS specific DeviceID, as the core ITS ignores dev. */
68*b4ead12dSLorenzo Pieralisi 	info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev);
69*b4ead12dSLorenzo Pieralisi 
70*b4ead12dSLorenzo Pieralisi 	/*
71*b4ead12dSLorenzo Pieralisi 	 * Always allocate a power of 2, and special case device 0 for
72*b4ead12dSLorenzo Pieralisi 	 * broken systems where the DevID is not wired (and all devices
73*b4ead12dSLorenzo Pieralisi 	 * appear as DevID 0). For that reason, we generously allocate a
74*b4ead12dSLorenzo Pieralisi 	 * minimum of 32 MSIs for DevID 0. If you want more because all
75*b4ead12dSLorenzo Pieralisi 	 * your devices are aliasing to DevID 0, consider fixing your HW.
76*b4ead12dSLorenzo Pieralisi 	 */
77*b4ead12dSLorenzo Pieralisi 	nvec = max(nvec, alias_count);
78*b4ead12dSLorenzo Pieralisi 	if (!info->scratchpad[0].ul)
79*b4ead12dSLorenzo Pieralisi 		minnvec = 32;
80*b4ead12dSLorenzo Pieralisi 	nvec = max_t(int, minnvec, roundup_pow_of_two(nvec));
81*b4ead12dSLorenzo Pieralisi 
82*b4ead12dSLorenzo Pieralisi 	msi_info = msi_get_domain_info(domain->parent);
83*b4ead12dSLorenzo Pieralisi 	return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
84*b4ead12dSLorenzo Pieralisi }
85*b4ead12dSLorenzo Pieralisi #else /* CONFIG_PCI_MSI */
86*b4ead12dSLorenzo Pieralisi #define its_pci_msi_prepare	NULL
87*b4ead12dSLorenzo Pieralisi #endif /* !CONFIG_PCI_MSI */
88*b4ead12dSLorenzo Pieralisi 
89*b4ead12dSLorenzo Pieralisi static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
90*b4ead12dSLorenzo Pieralisi 				  u32 *dev_id)
91*b4ead12dSLorenzo Pieralisi {
92*b4ead12dSLorenzo Pieralisi 	int ret, index = 0;
93*b4ead12dSLorenzo Pieralisi 
94*b4ead12dSLorenzo Pieralisi 	/* Suck the DeviceID out of the msi-parent property */
95*b4ead12dSLorenzo Pieralisi 	do {
96*b4ead12dSLorenzo Pieralisi 		struct of_phandle_args args;
97*b4ead12dSLorenzo Pieralisi 
98*b4ead12dSLorenzo Pieralisi 		ret = of_parse_phandle_with_args(dev->of_node,
99*b4ead12dSLorenzo Pieralisi 						 "msi-parent", "#msi-cells",
100*b4ead12dSLorenzo Pieralisi 						 index, &args);
101*b4ead12dSLorenzo Pieralisi 		if (args.np == irq_domain_get_of_node(domain)) {
102*b4ead12dSLorenzo Pieralisi 			if (WARN_ON(args.args_count != 1))
103*b4ead12dSLorenzo Pieralisi 				return -EINVAL;
104*b4ead12dSLorenzo Pieralisi 			*dev_id = args.args[0];
105*b4ead12dSLorenzo Pieralisi 			break;
106*b4ead12dSLorenzo Pieralisi 		}
107*b4ead12dSLorenzo Pieralisi 		index++;
108*b4ead12dSLorenzo Pieralisi 	} while (!ret);
109*b4ead12dSLorenzo Pieralisi 
110*b4ead12dSLorenzo Pieralisi 	if (ret) {
111*b4ead12dSLorenzo Pieralisi 		struct device_node *np = NULL;
112*b4ead12dSLorenzo Pieralisi 
113*b4ead12dSLorenzo Pieralisi 		ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
114*b4ead12dSLorenzo Pieralisi 		if (np)
115*b4ead12dSLorenzo Pieralisi 			of_node_put(np);
116*b4ead12dSLorenzo Pieralisi 	}
117*b4ead12dSLorenzo Pieralisi 
118*b4ead12dSLorenzo Pieralisi 	return ret;
119*b4ead12dSLorenzo Pieralisi }
120*b4ead12dSLorenzo Pieralisi 
121*b4ead12dSLorenzo Pieralisi int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
122*b4ead12dSLorenzo Pieralisi {
123*b4ead12dSLorenzo Pieralisi 	return -1;
124*b4ead12dSLorenzo Pieralisi }
125*b4ead12dSLorenzo Pieralisi 
126*b4ead12dSLorenzo Pieralisi static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
127*b4ead12dSLorenzo Pieralisi 			    int nvec, msi_alloc_info_t *info)
128*b4ead12dSLorenzo Pieralisi {
129*b4ead12dSLorenzo Pieralisi 	struct msi_domain_info *msi_info;
130*b4ead12dSLorenzo Pieralisi 	u32 dev_id;
131*b4ead12dSLorenzo Pieralisi 	int ret;
132*b4ead12dSLorenzo Pieralisi 
133*b4ead12dSLorenzo Pieralisi 	if (dev->of_node)
134*b4ead12dSLorenzo Pieralisi 		ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id);
135*b4ead12dSLorenzo Pieralisi 	else
136*b4ead12dSLorenzo Pieralisi 		ret = iort_pmsi_get_dev_id(dev, &dev_id);
137*b4ead12dSLorenzo Pieralisi 	if (ret)
138*b4ead12dSLorenzo Pieralisi 		return ret;
139*b4ead12dSLorenzo Pieralisi 
140*b4ead12dSLorenzo Pieralisi 	/* ITS specific DeviceID, as the core ITS ignores dev. */
141*b4ead12dSLorenzo Pieralisi 	info->scratchpad[0].ul = dev_id;
142*b4ead12dSLorenzo Pieralisi 
143*b4ead12dSLorenzo Pieralisi 	/* Allocate at least 32 MSIs, and always as a power of 2 */
144*b4ead12dSLorenzo Pieralisi 	nvec = max_t(int, 32, roundup_pow_of_two(nvec));
145*b4ead12dSLorenzo Pieralisi 
146*b4ead12dSLorenzo Pieralisi 	msi_info = msi_get_domain_info(domain->parent);
147*b4ead12dSLorenzo Pieralisi 	return msi_info->ops->msi_prepare(domain->parent,
148*b4ead12dSLorenzo Pieralisi 					  dev, nvec, info);
149*b4ead12dSLorenzo Pieralisi }
150*b4ead12dSLorenzo Pieralisi 
151*b4ead12dSLorenzo Pieralisi static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
152*b4ead12dSLorenzo Pieralisi {
153*b4ead12dSLorenzo Pieralisi 	struct msi_domain_info *msi_info;
154*b4ead12dSLorenzo Pieralisi 
155*b4ead12dSLorenzo Pieralisi 	msi_info = msi_get_domain_info(domain->parent);
156*b4ead12dSLorenzo Pieralisi 	msi_info->ops->msi_teardown(domain->parent, info);
157*b4ead12dSLorenzo Pieralisi }
158*b4ead12dSLorenzo Pieralisi 
159*b4ead12dSLorenzo Pieralisi static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
160*b4ead12dSLorenzo Pieralisi 				  struct irq_domain *real_parent, struct msi_domain_info *info)
161*b4ead12dSLorenzo Pieralisi {
162*b4ead12dSLorenzo Pieralisi 	if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
163*b4ead12dSLorenzo Pieralisi 		return false;
164*b4ead12dSLorenzo Pieralisi 
165*b4ead12dSLorenzo Pieralisi 	switch(info->bus_token) {
166*b4ead12dSLorenzo Pieralisi 	case DOMAIN_BUS_PCI_DEVICE_MSI:
167*b4ead12dSLorenzo Pieralisi 	case DOMAIN_BUS_PCI_DEVICE_MSIX:
168*b4ead12dSLorenzo Pieralisi 		/*
169*b4ead12dSLorenzo Pieralisi 		 * FIXME: This probably should be done after a (not yet
170*b4ead12dSLorenzo Pieralisi 		 * existing) post domain creation callback once to make
171*b4ead12dSLorenzo Pieralisi 		 * support for dynamic post-enable MSI-X allocations
172*b4ead12dSLorenzo Pieralisi 		 * work without having to reevaluate the domain size
173*b4ead12dSLorenzo Pieralisi 		 * over and over. It is known already at allocation
174*b4ead12dSLorenzo Pieralisi 		 * time via info->hwsize.
175*b4ead12dSLorenzo Pieralisi 		 *
176*b4ead12dSLorenzo Pieralisi 		 * That should work perfectly fine for MSI/MSI-X but needs
177*b4ead12dSLorenzo Pieralisi 		 * some thoughts for purely software managed MSI domains
178*b4ead12dSLorenzo Pieralisi 		 * where the index space is only limited artificially via
179*b4ead12dSLorenzo Pieralisi 		 * %MSI_MAX_INDEX.
180*b4ead12dSLorenzo Pieralisi 		 */
181*b4ead12dSLorenzo Pieralisi 		info->ops->msi_prepare = its_pci_msi_prepare;
182*b4ead12dSLorenzo Pieralisi 		info->ops->msi_teardown = its_msi_teardown;
183*b4ead12dSLorenzo Pieralisi 		break;
184*b4ead12dSLorenzo Pieralisi 	case DOMAIN_BUS_DEVICE_MSI:
185*b4ead12dSLorenzo Pieralisi 	case DOMAIN_BUS_WIRED_TO_MSI:
186*b4ead12dSLorenzo Pieralisi 		/*
187*b4ead12dSLorenzo Pieralisi 		 * FIXME: See the above PCI prepare comment. The domain
188*b4ead12dSLorenzo Pieralisi 		 * size is also known at domain creation time.
189*b4ead12dSLorenzo Pieralisi 		 */
190*b4ead12dSLorenzo Pieralisi 		info->ops->msi_prepare = its_pmsi_prepare;
191*b4ead12dSLorenzo Pieralisi 		info->ops->msi_teardown = its_msi_teardown;
192*b4ead12dSLorenzo Pieralisi 		break;
193*b4ead12dSLorenzo Pieralisi 	default:
194*b4ead12dSLorenzo Pieralisi 		/* Confused. How did the lib return true? */
195*b4ead12dSLorenzo Pieralisi 		WARN_ON_ONCE(1);
196*b4ead12dSLorenzo Pieralisi 		return false;
197*b4ead12dSLorenzo Pieralisi 	}
198*b4ead12dSLorenzo Pieralisi 
199*b4ead12dSLorenzo Pieralisi 	return true;
200*b4ead12dSLorenzo Pieralisi }
201*b4ead12dSLorenzo Pieralisi 
202*b4ead12dSLorenzo Pieralisi const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
203*b4ead12dSLorenzo Pieralisi 	.supported_flags	= ITS_MSI_FLAGS_SUPPORTED,
204*b4ead12dSLorenzo Pieralisi 	.required_flags		= ITS_MSI_FLAGS_REQUIRED,
205*b4ead12dSLorenzo Pieralisi 	.chip_flags		= MSI_CHIP_FLAG_SET_EOI,
206*b4ead12dSLorenzo Pieralisi 	.bus_select_token	= DOMAIN_BUS_NEXUS,
207*b4ead12dSLorenzo Pieralisi 	.bus_select_mask	= MATCH_PCI_MSI | MATCH_PLATFORM_MSI,
208*b4ead12dSLorenzo Pieralisi 	.prefix			= "ITS-",
209*b4ead12dSLorenzo Pieralisi 	.init_dev_msi_info	= its_init_dev_msi_info,
210*b4ead12dSLorenzo Pieralisi };
211