11e6db000SMarc Zyngier /*
21e6db000SMarc Zyngier  * Copyright (C) 2013-2015 ARM Limited, All Rights Reserved.
31e6db000SMarc Zyngier  * Author: Marc Zyngier <marc.zyngier@arm.com>
41e6db000SMarc Zyngier  *
51e6db000SMarc Zyngier  * This program is free software; you can redistribute it and/or modify
61e6db000SMarc Zyngier  * it under the terms of the GNU General Public License version 2 as
71e6db000SMarc Zyngier  * published by the Free Software Foundation.
81e6db000SMarc Zyngier  *
91e6db000SMarc Zyngier  * This program is distributed in the hope that it will be useful,
101e6db000SMarc Zyngier  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111e6db000SMarc Zyngier  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121e6db000SMarc Zyngier  * GNU General Public License for more details.
131e6db000SMarc Zyngier  *
141e6db000SMarc Zyngier  * You should have received a copy of the GNU General Public License
151e6db000SMarc Zyngier  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
161e6db000SMarc Zyngier  */
171e6db000SMarc Zyngier 
181e6db000SMarc Zyngier #include <linux/device.h>
191e6db000SMarc Zyngier #include <linux/msi.h>
201e6db000SMarc Zyngier #include <linux/of.h>
211e6db000SMarc Zyngier #include <linux/of_irq.h>
221e6db000SMarc Zyngier 
231e6db000SMarc Zyngier static struct irq_chip its_pmsi_irq_chip = {
241e6db000SMarc Zyngier 	.name			= "ITS-pMSI",
251e6db000SMarc Zyngier };
261e6db000SMarc Zyngier 
279ab460c2SHanjun Guo static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
289ab460c2SHanjun Guo 				  u32 *dev_id)
291e6db000SMarc Zyngier {
30deac7fc1SMarc Zyngier 	int ret, index = 0;
311e6db000SMarc Zyngier 
321e6db000SMarc Zyngier 	/* Suck the DeviceID out of the msi-parent property */
33deac7fc1SMarc Zyngier 	do {
34deac7fc1SMarc Zyngier 		struct of_phandle_args args;
35deac7fc1SMarc Zyngier 
36deac7fc1SMarc Zyngier 		ret = of_parse_phandle_with_args(dev->of_node,
37deac7fc1SMarc Zyngier 						 "msi-parent", "#msi-cells",
38deac7fc1SMarc Zyngier 						 index, &args);
39deac7fc1SMarc Zyngier 		if (args.np == irq_domain_get_of_node(domain)) {
40deac7fc1SMarc Zyngier 			if (WARN_ON(args.args_count != 1))
41deac7fc1SMarc Zyngier 				return -EINVAL;
429ab460c2SHanjun Guo 			*dev_id = args.args[0];
43deac7fc1SMarc Zyngier 			break;
44deac7fc1SMarc Zyngier 		}
45deac7fc1SMarc Zyngier 	} while (!ret);
46deac7fc1SMarc Zyngier 
479ab460c2SHanjun Guo 	return ret;
489ab460c2SHanjun Guo }
499ab460c2SHanjun Guo 
509ab460c2SHanjun Guo static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
519ab460c2SHanjun Guo 			    int nvec, msi_alloc_info_t *info)
529ab460c2SHanjun Guo {
539ab460c2SHanjun Guo 	struct msi_domain_info *msi_info;
549ab460c2SHanjun Guo 	u32 dev_id;
559ab460c2SHanjun Guo 	int ret;
569ab460c2SHanjun Guo 
579ab460c2SHanjun Guo 	msi_info = msi_get_domain_info(domain->parent);
589ab460c2SHanjun Guo 
599ab460c2SHanjun Guo 	ret = of_pmsi_get_dev_id(domain, dev, &dev_id);
601e6db000SMarc Zyngier 	if (ret)
611e6db000SMarc Zyngier 		return ret;
621e6db000SMarc Zyngier 
631e6db000SMarc Zyngier 	/* ITS specific DeviceID, as the core ITS ignores dev. */
641e6db000SMarc Zyngier 	info->scratchpad[0].ul = dev_id;
651e6db000SMarc Zyngier 
661e6db000SMarc Zyngier 	return msi_info->ops->msi_prepare(domain->parent,
671e6db000SMarc Zyngier 					  dev, nvec, info);
681e6db000SMarc Zyngier }
691e6db000SMarc Zyngier 
701e6db000SMarc Zyngier static struct msi_domain_ops its_pmsi_ops = {
711e6db000SMarc Zyngier 	.msi_prepare	= its_pmsi_prepare,
721e6db000SMarc Zyngier };
731e6db000SMarc Zyngier 
741e6db000SMarc Zyngier static struct msi_domain_info its_pmsi_domain_info = {
751e6db000SMarc Zyngier 	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
761e6db000SMarc Zyngier 	.ops	= &its_pmsi_ops,
771e6db000SMarc Zyngier 	.chip	= &its_pmsi_irq_chip,
781e6db000SMarc Zyngier };
791e6db000SMarc Zyngier 
801e6db000SMarc Zyngier static struct of_device_id its_device_id[] = {
811e6db000SMarc Zyngier 	{	.compatible	= "arm,gic-v3-its",	},
821e6db000SMarc Zyngier 	{},
831e6db000SMarc Zyngier };
841e6db000SMarc Zyngier 
8542677db9SHanjun Guo static int __init its_pmsi_init_one(struct fwnode_handle *fwnode,
8642677db9SHanjun Guo 				const char *name)
8742677db9SHanjun Guo {
8842677db9SHanjun Guo 	struct irq_domain *parent;
8942677db9SHanjun Guo 
9042677db9SHanjun Guo 	parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS);
9142677db9SHanjun Guo 	if (!parent || !msi_get_domain_info(parent)) {
9242677db9SHanjun Guo 		pr_err("%s: unable to locate ITS domain\n", name);
9342677db9SHanjun Guo 		return -ENXIO;
9442677db9SHanjun Guo 	}
9542677db9SHanjun Guo 
9642677db9SHanjun Guo 	if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info,
9742677db9SHanjun Guo 					    parent)) {
9842677db9SHanjun Guo 		pr_err("%s: unable to create platform domain\n", name);
9942677db9SHanjun Guo 		return -ENXIO;
10042677db9SHanjun Guo 	}
10142677db9SHanjun Guo 
10242677db9SHanjun Guo 	pr_info("Platform MSI: %s domain created\n", name);
10342677db9SHanjun Guo 	return 0;
10442677db9SHanjun Guo }
10542677db9SHanjun Guo 
10642677db9SHanjun Guo static void __init its_pmsi_of_init(void)
1071e6db000SMarc Zyngier {
1081e6db000SMarc Zyngier 	struct device_node *np;
1091e6db000SMarc Zyngier 
1101e6db000SMarc Zyngier 	for (np = of_find_matching_node(NULL, its_device_id); np;
1111e6db000SMarc Zyngier 	     np = of_find_matching_node(np, its_device_id)) {
1121e6db000SMarc Zyngier 		if (!of_property_read_bool(np, "msi-controller"))
1131e6db000SMarc Zyngier 			continue;
1141e6db000SMarc Zyngier 
11542677db9SHanjun Guo 		its_pmsi_init_one(of_node_to_fwnode(np), np->full_name);
11642677db9SHanjun Guo 	}
1171e6db000SMarc Zyngier }
1181e6db000SMarc Zyngier 
11942677db9SHanjun Guo static int __init its_pmsi_init(void)
12042677db9SHanjun Guo {
12142677db9SHanjun Guo 	its_pmsi_of_init();
1221e6db000SMarc Zyngier 	return 0;
1231e6db000SMarc Zyngier }
1241e6db000SMarc Zyngier early_initcall(its_pmsi_init);
125