1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21e6db000SMarc Zyngier /*
31e6db000SMarc Zyngier  * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
41e6db000SMarc Zyngier  * Author: Marc Zyngier <marc.zyngier@arm.com>
51e6db000SMarc Zyngier  */
61e6db000SMarc Zyngier 
7f785f7d2SHanjun Guo #include <linux/acpi_iort.h>
81e6db000SMarc Zyngier #include <linux/device.h>
91e6db000SMarc Zyngier #include <linux/msi.h>
101e6db000SMarc Zyngier #include <linux/of.h>
111e6db000SMarc Zyngier #include <linux/of_irq.h>
121e6db000SMarc Zyngier 
131e6db000SMarc Zyngier static struct irq_chip its_pmsi_irq_chip = {
141e6db000SMarc Zyngier 	.name			= "ITS-pMSI",
151e6db000SMarc Zyngier };
161e6db000SMarc Zyngier 
of_pmsi_get_dev_id(struct irq_domain * domain,struct device * dev,u32 * dev_id)179ab460c2SHanjun Guo static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
189ab460c2SHanjun Guo 				  u32 *dev_id)
191e6db000SMarc Zyngier {
20deac7fc1SMarc Zyngier 	int ret, index = 0;
211e6db000SMarc Zyngier 
221e6db000SMarc Zyngier 	/* Suck the DeviceID out of the msi-parent property */
23deac7fc1SMarc Zyngier 	do {
24deac7fc1SMarc Zyngier 		struct of_phandle_args args;
25deac7fc1SMarc Zyngier 
26deac7fc1SMarc Zyngier 		ret = of_parse_phandle_with_args(dev->of_node,
27deac7fc1SMarc Zyngier 						 "msi-parent", "#msi-cells",
28deac7fc1SMarc Zyngier 						 index, &args);
29deac7fc1SMarc Zyngier 		if (args.np == irq_domain_get_of_node(domain)) {
30deac7fc1SMarc Zyngier 			if (WARN_ON(args.args_count != 1))
31deac7fc1SMarc Zyngier 				return -EINVAL;
329ab460c2SHanjun Guo 			*dev_id = args.args[0];
33deac7fc1SMarc Zyngier 			break;
34deac7fc1SMarc Zyngier 		}
35a0088737SLorenzo Pieralisi 		index++;
36deac7fc1SMarc Zyngier 	} while (!ret);
37deac7fc1SMarc Zyngier 
389ab460c2SHanjun Guo 	return ret;
399ab460c2SHanjun Guo }
409ab460c2SHanjun Guo 
iort_pmsi_get_dev_id(struct device * dev,u32 * dev_id)41c2c8661fSMarc Zyngier int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
42c2c8661fSMarc Zyngier {
43c2c8661fSMarc Zyngier 	return -1;
44c2c8661fSMarc Zyngier }
45c2c8661fSMarc Zyngier 
its_pmsi_prepare(struct irq_domain * domain,struct device * dev,int nvec,msi_alloc_info_t * info)469ab460c2SHanjun Guo static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
479ab460c2SHanjun Guo 			    int nvec, msi_alloc_info_t *info)
489ab460c2SHanjun Guo {
499ab460c2SHanjun Guo 	struct msi_domain_info *msi_info;
509ab460c2SHanjun Guo 	u32 dev_id;
519ab460c2SHanjun Guo 	int ret;
529ab460c2SHanjun Guo 
539ab460c2SHanjun Guo 	msi_info = msi_get_domain_info(domain->parent);
549ab460c2SHanjun Guo 
55c2c8661fSMarc Zyngier 	if (dev->of_node)
569ab460c2SHanjun Guo 		ret = of_pmsi_get_dev_id(domain, dev, &dev_id);
57c2c8661fSMarc Zyngier 	else
58c2c8661fSMarc Zyngier 		ret = iort_pmsi_get_dev_id(dev, &dev_id);
591e6db000SMarc Zyngier 	if (ret)
601e6db000SMarc Zyngier 		return ret;
611e6db000SMarc Zyngier 
621e6db000SMarc Zyngier 	/* ITS specific DeviceID, as the core ITS ignores dev. */
631e6db000SMarc Zyngier 	info->scratchpad[0].ul = dev_id;
641e6db000SMarc 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));
671e6db000SMarc Zyngier 	return msi_info->ops->msi_prepare(domain->parent,
681e6db000SMarc Zyngier 					  dev, nvec, info);
691e6db000SMarc Zyngier }
701e6db000SMarc Zyngier 
711e6db000SMarc Zyngier static struct msi_domain_ops its_pmsi_ops = {
721e6db000SMarc Zyngier 	.msi_prepare	= its_pmsi_prepare,
731e6db000SMarc Zyngier };
741e6db000SMarc Zyngier 
751e6db000SMarc Zyngier static struct msi_domain_info its_pmsi_domain_info = {
761e6db000SMarc Zyngier 	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
771e6db000SMarc Zyngier 	.ops	= &its_pmsi_ops,
781e6db000SMarc Zyngier 	.chip	= &its_pmsi_irq_chip,
791e6db000SMarc Zyngier };
801e6db000SMarc Zyngier 
815b867061SArvind Yadav static const struct of_device_id its_device_id[] = {
821e6db000SMarc Zyngier 	{	.compatible	= "arm,gic-v3-its",	},
831e6db000SMarc Zyngier 	{},
841e6db000SMarc Zyngier };
851e6db000SMarc Zyngier 
its_pmsi_init_one(struct fwnode_handle * fwnode,const char * name)8642677db9SHanjun Guo static int __init its_pmsi_init_one(struct fwnode_handle *fwnode,
8742677db9SHanjun Guo 				const char *name)
8842677db9SHanjun Guo {
8942677db9SHanjun Guo 	struct irq_domain *parent;
9042677db9SHanjun Guo 
9142677db9SHanjun Guo 	parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS);
9242677db9SHanjun Guo 	if (!parent || !msi_get_domain_info(parent)) {
9342677db9SHanjun Guo 		pr_err("%s: unable to locate ITS domain\n", name);
9442677db9SHanjun Guo 		return -ENXIO;
9542677db9SHanjun Guo 	}
9642677db9SHanjun Guo 
9742677db9SHanjun Guo 	if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info,
9842677db9SHanjun Guo 					    parent)) {
9942677db9SHanjun Guo 		pr_err("%s: unable to create platform domain\n", name);
10042677db9SHanjun Guo 		return -ENXIO;
10142677db9SHanjun Guo 	}
10242677db9SHanjun Guo 
10342677db9SHanjun Guo 	pr_info("Platform MSI: %s domain created\n", name);
10442677db9SHanjun Guo 	return 0;
10542677db9SHanjun Guo }
10642677db9SHanjun Guo 
107f785f7d2SHanjun Guo #ifdef CONFIG_ACPI
108f785f7d2SHanjun Guo static int __init
its_pmsi_parse_madt(union acpi_subtable_headers * header,const unsigned long end)10960574d1eSKeith Busch its_pmsi_parse_madt(union acpi_subtable_headers *header,
110f785f7d2SHanjun Guo 			const unsigned long end)
111f785f7d2SHanjun Guo {
112f785f7d2SHanjun Guo 	struct acpi_madt_generic_translator *its_entry;
113f785f7d2SHanjun Guo 	struct fwnode_handle *domain_handle;
114f785f7d2SHanjun Guo 	const char *node_name;
115f785f7d2SHanjun Guo 	int err = -ENXIO;
116f785f7d2SHanjun Guo 
117f785f7d2SHanjun Guo 	its_entry = (struct acpi_madt_generic_translator *)header;
118f785f7d2SHanjun Guo 	node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
119f785f7d2SHanjun Guo 			      (long)its_entry->base_address);
120f785f7d2SHanjun Guo 	domain_handle = iort_find_domain_token(its_entry->translation_id);
121f785f7d2SHanjun Guo 	if (!domain_handle) {
122f785f7d2SHanjun Guo 		pr_err("%s: Unable to locate ITS domain handle\n", node_name);
123f785f7d2SHanjun Guo 		goto out;
124f785f7d2SHanjun Guo 	}
125f785f7d2SHanjun Guo 
126f785f7d2SHanjun Guo 	err = its_pmsi_init_one(domain_handle, node_name);
127f785f7d2SHanjun Guo 
128f785f7d2SHanjun Guo out:
129f785f7d2SHanjun Guo 	kfree(node_name);
130f785f7d2SHanjun Guo 	return err;
131f785f7d2SHanjun Guo }
132f785f7d2SHanjun Guo 
its_pmsi_acpi_init(void)133f785f7d2SHanjun Guo static void __init its_pmsi_acpi_init(void)
134f785f7d2SHanjun Guo {
135f785f7d2SHanjun Guo 	acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
136f785f7d2SHanjun Guo 			      its_pmsi_parse_madt, 0);
137f785f7d2SHanjun Guo }
138f785f7d2SHanjun Guo #else
its_pmsi_acpi_init(void)139f785f7d2SHanjun Guo static inline void its_pmsi_acpi_init(void) { }
140f785f7d2SHanjun Guo #endif
141f785f7d2SHanjun Guo 
its_pmsi_of_init(void)14242677db9SHanjun Guo static void __init its_pmsi_of_init(void)
1431e6db000SMarc Zyngier {
1441e6db000SMarc Zyngier 	struct device_node *np;
1451e6db000SMarc Zyngier 
1461e6db000SMarc Zyngier 	for (np = of_find_matching_node(NULL, its_device_id); np;
1471e6db000SMarc Zyngier 	     np = of_find_matching_node(np, its_device_id)) {
14895a25625SStephen Boyd 		if (!of_device_is_available(np))
14995a25625SStephen Boyd 			continue;
1501e6db000SMarc Zyngier 		if (!of_property_read_bool(np, "msi-controller"))
1511e6db000SMarc Zyngier 			continue;
1521e6db000SMarc Zyngier 
15342677db9SHanjun Guo 		its_pmsi_init_one(of_node_to_fwnode(np), np->full_name);
15442677db9SHanjun Guo 	}
1551e6db000SMarc Zyngier }
1561e6db000SMarc Zyngier 
its_pmsi_init(void)15742677db9SHanjun Guo static int __init its_pmsi_init(void)
15842677db9SHanjun Guo {
15942677db9SHanjun Guo 	its_pmsi_of_init();
160f785f7d2SHanjun Guo 	its_pmsi_acpi_init();
1611e6db000SMarc Zyngier 	return 0;
1621e6db000SMarc Zyngier }
1631e6db000SMarc Zyngier early_initcall(its_pmsi_init);
164