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