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