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 18f785f7d2SHanjun Guo #include <linux/acpi_iort.h> 191e6db000SMarc Zyngier #include <linux/device.h> 201e6db000SMarc Zyngier #include <linux/msi.h> 211e6db000SMarc Zyngier #include <linux/of.h> 221e6db000SMarc Zyngier #include <linux/of_irq.h> 231e6db000SMarc Zyngier 241e6db000SMarc Zyngier static struct irq_chip its_pmsi_irq_chip = { 251e6db000SMarc Zyngier .name = "ITS-pMSI", 261e6db000SMarc Zyngier }; 271e6db000SMarc Zyngier 289ab460c2SHanjun Guo static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, 299ab460c2SHanjun Guo u32 *dev_id) 301e6db000SMarc Zyngier { 31deac7fc1SMarc Zyngier int ret, index = 0; 321e6db000SMarc Zyngier 331e6db000SMarc Zyngier /* Suck the DeviceID out of the msi-parent property */ 34deac7fc1SMarc Zyngier do { 35deac7fc1SMarc Zyngier struct of_phandle_args args; 36deac7fc1SMarc Zyngier 37deac7fc1SMarc Zyngier ret = of_parse_phandle_with_args(dev->of_node, 38deac7fc1SMarc Zyngier "msi-parent", "#msi-cells", 39deac7fc1SMarc Zyngier index, &args); 40deac7fc1SMarc Zyngier if (args.np == irq_domain_get_of_node(domain)) { 41deac7fc1SMarc Zyngier if (WARN_ON(args.args_count != 1)) 42deac7fc1SMarc Zyngier return -EINVAL; 439ab460c2SHanjun Guo *dev_id = args.args[0]; 44deac7fc1SMarc Zyngier break; 45deac7fc1SMarc Zyngier } 46deac7fc1SMarc Zyngier } while (!ret); 47deac7fc1SMarc Zyngier 489ab460c2SHanjun Guo return ret; 499ab460c2SHanjun Guo } 509ab460c2SHanjun Guo 51c2c8661fSMarc Zyngier int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) 52c2c8661fSMarc Zyngier { 53c2c8661fSMarc Zyngier return -1; 54c2c8661fSMarc Zyngier } 55c2c8661fSMarc Zyngier 569ab460c2SHanjun Guo static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, 579ab460c2SHanjun Guo int nvec, msi_alloc_info_t *info) 589ab460c2SHanjun Guo { 599ab460c2SHanjun Guo struct msi_domain_info *msi_info; 609ab460c2SHanjun Guo u32 dev_id; 619ab460c2SHanjun Guo int ret; 629ab460c2SHanjun Guo 639ab460c2SHanjun Guo msi_info = msi_get_domain_info(domain->parent); 649ab460c2SHanjun Guo 65c2c8661fSMarc Zyngier if (dev->of_node) 669ab460c2SHanjun Guo ret = of_pmsi_get_dev_id(domain, dev, &dev_id); 67c2c8661fSMarc Zyngier else 68c2c8661fSMarc Zyngier ret = iort_pmsi_get_dev_id(dev, &dev_id); 691e6db000SMarc Zyngier if (ret) 701e6db000SMarc Zyngier return ret; 711e6db000SMarc Zyngier 721e6db000SMarc Zyngier /* ITS specific DeviceID, as the core ITS ignores dev. */ 731e6db000SMarc Zyngier info->scratchpad[0].ul = dev_id; 741e6db000SMarc Zyngier 751e6db000SMarc Zyngier return msi_info->ops->msi_prepare(domain->parent, 761e6db000SMarc Zyngier dev, nvec, info); 771e6db000SMarc Zyngier } 781e6db000SMarc Zyngier 791e6db000SMarc Zyngier static struct msi_domain_ops its_pmsi_ops = { 801e6db000SMarc Zyngier .msi_prepare = its_pmsi_prepare, 811e6db000SMarc Zyngier }; 821e6db000SMarc Zyngier 831e6db000SMarc Zyngier static struct msi_domain_info its_pmsi_domain_info = { 841e6db000SMarc Zyngier .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), 851e6db000SMarc Zyngier .ops = &its_pmsi_ops, 861e6db000SMarc Zyngier .chip = &its_pmsi_irq_chip, 871e6db000SMarc Zyngier }; 881e6db000SMarc Zyngier 895b867061SArvind Yadav static const struct of_device_id its_device_id[] = { 901e6db000SMarc Zyngier { .compatible = "arm,gic-v3-its", }, 911e6db000SMarc Zyngier {}, 921e6db000SMarc Zyngier }; 931e6db000SMarc Zyngier 9442677db9SHanjun Guo static int __init its_pmsi_init_one(struct fwnode_handle *fwnode, 9542677db9SHanjun Guo const char *name) 9642677db9SHanjun Guo { 9742677db9SHanjun Guo struct irq_domain *parent; 9842677db9SHanjun Guo 9942677db9SHanjun Guo parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS); 10042677db9SHanjun Guo if (!parent || !msi_get_domain_info(parent)) { 10142677db9SHanjun Guo pr_err("%s: unable to locate ITS domain\n", name); 10242677db9SHanjun Guo return -ENXIO; 10342677db9SHanjun Guo } 10442677db9SHanjun Guo 10542677db9SHanjun Guo if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info, 10642677db9SHanjun Guo parent)) { 10742677db9SHanjun Guo pr_err("%s: unable to create platform domain\n", name); 10842677db9SHanjun Guo return -ENXIO; 10942677db9SHanjun Guo } 11042677db9SHanjun Guo 11142677db9SHanjun Guo pr_info("Platform MSI: %s domain created\n", name); 11242677db9SHanjun Guo return 0; 11342677db9SHanjun Guo } 11442677db9SHanjun Guo 115f785f7d2SHanjun Guo #ifdef CONFIG_ACPI 116f785f7d2SHanjun Guo static int __init 117f785f7d2SHanjun Guo its_pmsi_parse_madt(struct acpi_subtable_header *header, 118f785f7d2SHanjun Guo const unsigned long end) 119f785f7d2SHanjun Guo { 120f785f7d2SHanjun Guo struct acpi_madt_generic_translator *its_entry; 121f785f7d2SHanjun Guo struct fwnode_handle *domain_handle; 122f785f7d2SHanjun Guo const char *node_name; 123f785f7d2SHanjun Guo int err = -ENXIO; 124f785f7d2SHanjun Guo 125f785f7d2SHanjun Guo its_entry = (struct acpi_madt_generic_translator *)header; 126f785f7d2SHanjun Guo node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", 127f785f7d2SHanjun Guo (long)its_entry->base_address); 128f785f7d2SHanjun Guo domain_handle = iort_find_domain_token(its_entry->translation_id); 129f785f7d2SHanjun Guo if (!domain_handle) { 130f785f7d2SHanjun Guo pr_err("%s: Unable to locate ITS domain handle\n", node_name); 131f785f7d2SHanjun Guo goto out; 132f785f7d2SHanjun Guo } 133f785f7d2SHanjun Guo 134f785f7d2SHanjun Guo err = its_pmsi_init_one(domain_handle, node_name); 135f785f7d2SHanjun Guo 136f785f7d2SHanjun Guo out: 137f785f7d2SHanjun Guo kfree(node_name); 138f785f7d2SHanjun Guo return err; 139f785f7d2SHanjun Guo } 140f785f7d2SHanjun Guo 141f785f7d2SHanjun Guo static void __init its_pmsi_acpi_init(void) 142f785f7d2SHanjun Guo { 143f785f7d2SHanjun Guo acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, 144f785f7d2SHanjun Guo its_pmsi_parse_madt, 0); 145f785f7d2SHanjun Guo } 146f785f7d2SHanjun Guo #else 147f785f7d2SHanjun Guo static inline void its_pmsi_acpi_init(void) { } 148f785f7d2SHanjun Guo #endif 149f785f7d2SHanjun Guo 15042677db9SHanjun Guo static void __init its_pmsi_of_init(void) 1511e6db000SMarc Zyngier { 1521e6db000SMarc Zyngier struct device_node *np; 1531e6db000SMarc Zyngier 1541e6db000SMarc Zyngier for (np = of_find_matching_node(NULL, its_device_id); np; 1551e6db000SMarc Zyngier np = of_find_matching_node(np, its_device_id)) { 1561e6db000SMarc Zyngier if (!of_property_read_bool(np, "msi-controller")) 1571e6db000SMarc Zyngier continue; 1581e6db000SMarc Zyngier 15942677db9SHanjun Guo its_pmsi_init_one(of_node_to_fwnode(np), np->full_name); 16042677db9SHanjun Guo } 1611e6db000SMarc Zyngier } 1621e6db000SMarc Zyngier 16342677db9SHanjun Guo static int __init its_pmsi_init(void) 16442677db9SHanjun Guo { 16542677db9SHanjun Guo its_pmsi_of_init(); 166f785f7d2SHanjun Guo its_pmsi_acpi_init(); 1671e6db000SMarc Zyngier return 0; 1681e6db000SMarc Zyngier } 1691e6db000SMarc Zyngier early_initcall(its_pmsi_init); 170