xref: /linux/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1203e2de3SBogdan Purcareata // SPDX-License-Identifier: GPL-2.0
23a288fd5SJ. German Rivera /*
33a288fd5SJ. German Rivera  * Freescale Management Complex (MC) bus driver MSI support
43a288fd5SJ. German Rivera  *
56466dac7SStuart Yoder  * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
63a288fd5SJ. German Rivera  * Author: German Rivera <German.Rivera@freescale.com>
73a288fd5SJ. German Rivera  *
83a288fd5SJ. German Rivera  */
93a288fd5SJ. German Rivera 
10*6305166cSMakarand Pawagi #include <linux/acpi.h>
11*6305166cSMakarand Pawagi #include <linux/acpi_iort.h>
123a288fd5SJ. German Rivera #include <linux/of_device.h>
133a288fd5SJ. German Rivera #include <linux/of_address.h>
143a288fd5SJ. German Rivera #include <linux/irq.h>
153a288fd5SJ. German Rivera #include <linux/msi.h>
163a288fd5SJ. German Rivera #include <linux/of.h>
173a288fd5SJ. German Rivera #include <linux/of_irq.h>
186bd067c4SBogdan Purcareata #include <linux/fsl/mc.h>
193a288fd5SJ. German Rivera 
203a288fd5SJ. German Rivera static struct irq_chip its_msi_irq_chip = {
21d64c28a3SStuart Yoder 	.name = "ITS-fMSI",
223a288fd5SJ. German Rivera 	.irq_mask = irq_chip_mask_parent,
233a288fd5SJ. German Rivera 	.irq_unmask = irq_chip_unmask_parent,
243a288fd5SJ. German Rivera 	.irq_eoi = irq_chip_eoi_parent,
253a288fd5SJ. German Rivera 	.irq_set_affinity = msi_domain_set_affinity
263a288fd5SJ. German Rivera };
273a288fd5SJ. German Rivera 
28998fb7baSDiana Craciun static u32 fsl_mc_msi_domain_get_msi_id(struct irq_domain *domain,
29998fb7baSDiana Craciun 					struct fsl_mc_device *mc_dev)
30998fb7baSDiana Craciun {
31998fb7baSDiana Craciun 	struct device_node *of_node;
32998fb7baSDiana Craciun 	u32 out_id;
33998fb7baSDiana Craciun 
34998fb7baSDiana Craciun 	of_node = irq_domain_get_of_node(domain);
35*6305166cSMakarand Pawagi 	out_id = of_node ? of_msi_map_id(&mc_dev->dev, of_node, mc_dev->icid) :
36*6305166cSMakarand Pawagi 			iort_msi_map_id(&mc_dev->dev, mc_dev->icid);
37998fb7baSDiana Craciun 
38998fb7baSDiana Craciun 	return out_id;
39998fb7baSDiana Craciun }
40998fb7baSDiana Craciun 
413a288fd5SJ. German Rivera static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,
423a288fd5SJ. German Rivera 				  struct device *dev,
433a288fd5SJ. German Rivera 				  int nvec, msi_alloc_info_t *info)
443a288fd5SJ. German Rivera {
453a288fd5SJ. German Rivera 	struct fsl_mc_device *mc_bus_dev;
463a288fd5SJ. German Rivera 	struct msi_domain_info *msi_info;
473a288fd5SJ. German Rivera 
48a385dd7bSLaurentiu Tudor 	if (!dev_is_fsl_mc(dev))
493a288fd5SJ. German Rivera 		return -EINVAL;
503a288fd5SJ. German Rivera 
513a288fd5SJ. German Rivera 	mc_bus_dev = to_fsl_mc_device(dev);
52a385dd7bSLaurentiu Tudor 	if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC))
533a288fd5SJ. German Rivera 		return -EINVAL;
543a288fd5SJ. German Rivera 
553a288fd5SJ. German Rivera 	/*
563a288fd5SJ. German Rivera 	 * Set the device Id to be passed to the GIC-ITS:
573a288fd5SJ. German Rivera 	 *
583a288fd5SJ. German Rivera 	 * NOTE: This device id corresponds to the IOMMU stream ID
593a288fd5SJ. German Rivera 	 * associated with the DPRC object (ICID).
603a288fd5SJ. German Rivera 	 */
61998fb7baSDiana Craciun 	info->scratchpad[0].ul = fsl_mc_msi_domain_get_msi_id(msi_domain,
62998fb7baSDiana Craciun 							      mc_bus_dev);
633a288fd5SJ. German Rivera 	msi_info = msi_get_domain_info(msi_domain->parent);
64147c8f37SMarc Zyngier 
65147c8f37SMarc Zyngier 	/* Allocate at least 32 MSIs, and always as a power of 2 */
66147c8f37SMarc Zyngier 	nvec = max_t(int, 32, roundup_pow_of_two(nvec));
673a288fd5SJ. German Rivera 	return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
683a288fd5SJ. German Rivera }
693a288fd5SJ. German Rivera 
7076b94eb1SJess Frazelle static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = {
713a288fd5SJ. German Rivera 	.msi_prepare = its_fsl_mc_msi_prepare,
723a288fd5SJ. German Rivera };
733a288fd5SJ. German Rivera 
743a288fd5SJ. German Rivera static struct msi_domain_info its_fsl_mc_msi_domain_info = {
753a288fd5SJ. German Rivera 	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
763a288fd5SJ. German Rivera 	.ops	= &its_fsl_mc_msi_ops,
773a288fd5SJ. German Rivera 	.chip	= &its_msi_irq_chip,
783a288fd5SJ. German Rivera };
793a288fd5SJ. German Rivera 
803a288fd5SJ. German Rivera static const struct of_device_id its_device_id[] = {
813a288fd5SJ. German Rivera 	{	.compatible	= "arm,gic-v3-its",	},
823a288fd5SJ. German Rivera 	{},
833a288fd5SJ. German Rivera };
843a288fd5SJ. German Rivera 
85*6305166cSMakarand Pawagi static void __init its_fsl_mc_msi_init_one(struct fwnode_handle *handle,
86*6305166cSMakarand Pawagi 					  const char *name)
873a288fd5SJ. German Rivera {
883a288fd5SJ. German Rivera 	struct irq_domain *parent;
893a288fd5SJ. German Rivera 	struct irq_domain *mc_msi_domain;
903a288fd5SJ. German Rivera 
91*6305166cSMakarand Pawagi 	parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
92*6305166cSMakarand Pawagi 	if (!parent || !msi_get_domain_info(parent)) {
93*6305166cSMakarand Pawagi 		pr_err("%s: unable to locate ITS domain\n", name);
94*6305166cSMakarand Pawagi 		return;
95*6305166cSMakarand Pawagi 	}
96*6305166cSMakarand Pawagi 
97*6305166cSMakarand Pawagi 	mc_msi_domain = fsl_mc_msi_create_irq_domain(handle,
98*6305166cSMakarand Pawagi 						&its_fsl_mc_msi_domain_info,
99*6305166cSMakarand Pawagi 						parent);
100*6305166cSMakarand Pawagi 	if (!mc_msi_domain) {
101*6305166cSMakarand Pawagi 		pr_err("%s: unable to create fsl-mc domain\n", name);
102*6305166cSMakarand Pawagi 		return;
103*6305166cSMakarand Pawagi 	}
104*6305166cSMakarand Pawagi 
105*6305166cSMakarand Pawagi 	pr_info("fsl-mc MSI: %s domain created\n", name);
106*6305166cSMakarand Pawagi }
107*6305166cSMakarand Pawagi 
108*6305166cSMakarand Pawagi #ifdef CONFIG_ACPI
109*6305166cSMakarand Pawagi static int __init
110*6305166cSMakarand Pawagi its_fsl_mc_msi_parse_madt(union acpi_subtable_headers *header,
111*6305166cSMakarand Pawagi 			  const unsigned long end)
112*6305166cSMakarand Pawagi {
113*6305166cSMakarand Pawagi 	struct acpi_madt_generic_translator *its_entry;
114*6305166cSMakarand Pawagi 	struct fwnode_handle *dom_handle;
115*6305166cSMakarand Pawagi 	const char *node_name;
116*6305166cSMakarand Pawagi 	int err = 0;
117*6305166cSMakarand Pawagi 
118*6305166cSMakarand Pawagi 	its_entry = (struct acpi_madt_generic_translator *)header;
119*6305166cSMakarand Pawagi 	node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
120*6305166cSMakarand Pawagi 			      (long)its_entry->base_address);
121*6305166cSMakarand Pawagi 
122*6305166cSMakarand Pawagi 	dom_handle = iort_find_domain_token(its_entry->translation_id);
123*6305166cSMakarand Pawagi 	if (!dom_handle) {
124*6305166cSMakarand Pawagi 		pr_err("%s: Unable to locate ITS domain handle\n", node_name);
125*6305166cSMakarand Pawagi 		err = -ENXIO;
126*6305166cSMakarand Pawagi 		goto out;
127*6305166cSMakarand Pawagi 	}
128*6305166cSMakarand Pawagi 
129*6305166cSMakarand Pawagi 	its_fsl_mc_msi_init_one(dom_handle, node_name);
130*6305166cSMakarand Pawagi 
131*6305166cSMakarand Pawagi out:
132*6305166cSMakarand Pawagi 	kfree(node_name);
133*6305166cSMakarand Pawagi 	return err;
134*6305166cSMakarand Pawagi }
135*6305166cSMakarand Pawagi 
136*6305166cSMakarand Pawagi 
137*6305166cSMakarand Pawagi static void __init its_fsl_mc_acpi_msi_init(void)
138*6305166cSMakarand Pawagi {
139*6305166cSMakarand Pawagi 	acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
140*6305166cSMakarand Pawagi 			      its_fsl_mc_msi_parse_madt, 0);
141*6305166cSMakarand Pawagi }
142*6305166cSMakarand Pawagi #else
143*6305166cSMakarand Pawagi static inline void its_fsl_mc_acpi_msi_init(void) { }
144*6305166cSMakarand Pawagi #endif
145*6305166cSMakarand Pawagi 
146*6305166cSMakarand Pawagi static void __init its_fsl_mc_of_msi_init(void)
147*6305166cSMakarand Pawagi {
148*6305166cSMakarand Pawagi 	struct device_node *np;
149*6305166cSMakarand Pawagi 
1503a288fd5SJ. German Rivera 	for (np = of_find_matching_node(NULL, its_device_id); np;
1513a288fd5SJ. German Rivera 	     np = of_find_matching_node(np, its_device_id)) {
15295a25625SStephen Boyd 		if (!of_device_is_available(np))
15395a25625SStephen Boyd 			continue;
1543a288fd5SJ. German Rivera 		if (!of_property_read_bool(np, "msi-controller"))
1553a288fd5SJ. German Rivera 			continue;
1563a288fd5SJ. German Rivera 
157*6305166cSMakarand Pawagi 		its_fsl_mc_msi_init_one(of_node_to_fwnode(np),
158*6305166cSMakarand Pawagi 					np->full_name);
159*6305166cSMakarand Pawagi 	}
1603a288fd5SJ. German Rivera }
1613a288fd5SJ. German Rivera 
162*6305166cSMakarand Pawagi static int __init its_fsl_mc_msi_init(void)
163*6305166cSMakarand Pawagi {
164*6305166cSMakarand Pawagi 	its_fsl_mc_of_msi_init();
165*6305166cSMakarand Pawagi 	its_fsl_mc_acpi_msi_init();
1663a288fd5SJ. German Rivera 
1673a288fd5SJ. German Rivera 	return 0;
1683a288fd5SJ. German Rivera }
1691d11d556SIoana Radulescu 
1701d11d556SIoana Radulescu early_initcall(its_fsl_mc_msi_init);
171