162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale Management Complex (MC) bus driver MSI support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * Author: German Rivera <German.Rivera@freescale.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/acpi.h> 1162306a36Sopenharmony_ci#include <linux/acpi_iort.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/msi.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_irq.h> 1662306a36Sopenharmony_ci#include <linux/fsl/mc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic struct irq_chip its_msi_irq_chip = { 1962306a36Sopenharmony_ci .name = "ITS-fMSI", 2062306a36Sopenharmony_ci .irq_mask = irq_chip_mask_parent, 2162306a36Sopenharmony_ci .irq_unmask = irq_chip_unmask_parent, 2262306a36Sopenharmony_ci .irq_eoi = irq_chip_eoi_parent, 2362306a36Sopenharmony_ci .irq_set_affinity = msi_domain_set_affinity 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic u32 fsl_mc_msi_domain_get_msi_id(struct irq_domain *domain, 2762306a36Sopenharmony_ci struct fsl_mc_device *mc_dev) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct device_node *of_node; 3062306a36Sopenharmony_ci u32 out_id; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci of_node = irq_domain_get_of_node(domain); 3362306a36Sopenharmony_ci out_id = of_node ? of_msi_map_id(&mc_dev->dev, of_node, mc_dev->icid) : 3462306a36Sopenharmony_ci iort_msi_map_id(&mc_dev->dev, mc_dev->icid); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return out_id; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain, 4062306a36Sopenharmony_ci struct device *dev, 4162306a36Sopenharmony_ci int nvec, msi_alloc_info_t *info) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev; 4462306a36Sopenharmony_ci struct msi_domain_info *msi_info; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (!dev_is_fsl_mc(dev)) 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci mc_bus_dev = to_fsl_mc_device(dev); 5062306a36Sopenharmony_ci if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC)) 5162306a36Sopenharmony_ci return -EINVAL; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * Set the device Id to be passed to the GIC-ITS: 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * NOTE: This device id corresponds to the IOMMU stream ID 5762306a36Sopenharmony_ci * associated with the DPRC object (ICID). 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci info->scratchpad[0].ul = fsl_mc_msi_domain_get_msi_id(msi_domain, 6062306a36Sopenharmony_ci mc_bus_dev); 6162306a36Sopenharmony_ci msi_info = msi_get_domain_info(msi_domain->parent); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Allocate at least 32 MSIs, and always as a power of 2 */ 6462306a36Sopenharmony_ci nvec = max_t(int, 32, roundup_pow_of_two(nvec)); 6562306a36Sopenharmony_ci return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = { 6962306a36Sopenharmony_ci .msi_prepare = its_fsl_mc_msi_prepare, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic struct msi_domain_info its_fsl_mc_msi_domain_info = { 7362306a36Sopenharmony_ci .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), 7462306a36Sopenharmony_ci .ops = &its_fsl_mc_msi_ops, 7562306a36Sopenharmony_ci .chip = &its_msi_irq_chip, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct of_device_id its_device_id[] = { 7962306a36Sopenharmony_ci { .compatible = "arm,gic-v3-its", }, 8062306a36Sopenharmony_ci {}, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void __init its_fsl_mc_msi_init_one(struct fwnode_handle *handle, 8462306a36Sopenharmony_ci const char *name) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct irq_domain *parent; 8762306a36Sopenharmony_ci struct irq_domain *mc_msi_domain; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS); 9062306a36Sopenharmony_ci if (!parent || !msi_get_domain_info(parent)) { 9162306a36Sopenharmony_ci pr_err("%s: unable to locate ITS domain\n", name); 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci mc_msi_domain = fsl_mc_msi_create_irq_domain(handle, 9662306a36Sopenharmony_ci &its_fsl_mc_msi_domain_info, 9762306a36Sopenharmony_ci parent); 9862306a36Sopenharmony_ci if (!mc_msi_domain) { 9962306a36Sopenharmony_ci pr_err("%s: unable to create fsl-mc domain\n", name); 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pr_info("fsl-mc MSI: %s domain created\n", name); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#ifdef CONFIG_ACPI 10762306a36Sopenharmony_cistatic int __init 10862306a36Sopenharmony_ciits_fsl_mc_msi_parse_madt(union acpi_subtable_headers *header, 10962306a36Sopenharmony_ci const unsigned long end) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct acpi_madt_generic_translator *its_entry; 11262306a36Sopenharmony_ci struct fwnode_handle *dom_handle; 11362306a36Sopenharmony_ci const char *node_name; 11462306a36Sopenharmony_ci int err = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci its_entry = (struct acpi_madt_generic_translator *)header; 11762306a36Sopenharmony_ci node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", 11862306a36Sopenharmony_ci (long)its_entry->base_address); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci dom_handle = iort_find_domain_token(its_entry->translation_id); 12162306a36Sopenharmony_ci if (!dom_handle) { 12262306a36Sopenharmony_ci pr_err("%s: Unable to locate ITS domain handle\n", node_name); 12362306a36Sopenharmony_ci err = -ENXIO; 12462306a36Sopenharmony_ci goto out; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci its_fsl_mc_msi_init_one(dom_handle, node_name); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciout: 13062306a36Sopenharmony_ci kfree(node_name); 13162306a36Sopenharmony_ci return err; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void __init its_fsl_mc_acpi_msi_init(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, 13862306a36Sopenharmony_ci its_fsl_mc_msi_parse_madt, 0); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci#else 14162306a36Sopenharmony_cistatic inline void its_fsl_mc_acpi_msi_init(void) { } 14262306a36Sopenharmony_ci#endif 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void __init its_fsl_mc_of_msi_init(void) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct device_node *np; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (np = of_find_matching_node(NULL, its_device_id); np; 14962306a36Sopenharmony_ci np = of_find_matching_node(np, its_device_id)) { 15062306a36Sopenharmony_ci if (!of_device_is_available(np)) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci if (!of_property_read_bool(np, "msi-controller")) 15362306a36Sopenharmony_ci continue; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci its_fsl_mc_msi_init_one(of_node_to_fwnode(np), 15662306a36Sopenharmony_ci np->full_name); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int __init its_fsl_mc_msi_init(void) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci its_fsl_mc_of_msi_init(); 16362306a36Sopenharmony_ci its_fsl_mc_acpi_msi_init(); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciearly_initcall(its_fsl_mc_msi_init); 169