1 // SPDX-License-Identifier: GPL-2.0
3 * Freescale Management Complex (MC) bus driver MSI support
5 * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
6 * Author: German Rivera <German.Rivera@freescale.com>
10 #include <linux/acpi.h>
11 #include <linux/acpi_iort.h>
12 #include <linux/irq.h>
13 #include <linux/msi.h>
15 #include <linux/of_irq.h>
16 #include <linux/fsl/mc.h>
18 static struct irq_chip its_msi_irq_chip
= {
20 .irq_mask
= irq_chip_mask_parent
,
21 .irq_unmask
= irq_chip_unmask_parent
,
22 .irq_eoi
= irq_chip_eoi_parent
,
23 .irq_set_affinity
= msi_domain_set_affinity
26 static u32
fsl_mc_msi_domain_get_msi_id(struct irq_domain
*domain
,
27 struct fsl_mc_device
*mc_dev
)
29 struct device_node
*of_node
;
32 of_node
= irq_domain_get_of_node(domain
);
33 out_id
= of_node
? of_msi_map_id(&mc_dev
->dev
, of_node
, mc_dev
->icid
) :
34 iort_msi_map_id(&mc_dev
->dev
, mc_dev
->icid
);
39 static int its_fsl_mc_msi_prepare(struct irq_domain
*msi_domain
,
41 int nvec
, msi_alloc_info_t
*info
)
43 struct fsl_mc_device
*mc_bus_dev
;
44 struct msi_domain_info
*msi_info
;
46 if (!dev_is_fsl_mc(dev
))
49 mc_bus_dev
= to_fsl_mc_device(dev
);
50 if (!(mc_bus_dev
->flags
& FSL_MC_IS_DPRC
))
54 * Set the device Id to be passed to the GIC-ITS:
56 * NOTE: This device id corresponds to the IOMMU stream ID
57 * associated with the DPRC object (ICID).
59 info
->scratchpad
[0].ul
= fsl_mc_msi_domain_get_msi_id(msi_domain
,
61 msi_info
= msi_get_domain_info(msi_domain
->parent
);
63 /* Allocate at least 32 MSIs, and always as a power of 2 */
64 nvec
= max_t(int, 32, roundup_pow_of_two(nvec
));
65 return msi_info
->ops
->msi_prepare(msi_domain
->parent
, dev
, nvec
, info
);
68 static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init
= {
69 .msi_prepare
= its_fsl_mc_msi_prepare
,
72 static struct msi_domain_info its_fsl_mc_msi_domain_info
= {
73 .flags
= (MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS
),
74 .ops
= &its_fsl_mc_msi_ops
,
75 .chip
= &its_msi_irq_chip
,
78 static const struct of_device_id its_device_id
[] = {
79 { .compatible
= "arm,gic-v3-its", },
83 static void __init
its_fsl_mc_msi_init_one(struct fwnode_handle
*handle
,
86 struct irq_domain
*parent
;
87 struct irq_domain
*mc_msi_domain
;
89 parent
= irq_find_matching_fwnode(handle
, DOMAIN_BUS_NEXUS
);
90 if (!parent
|| !msi_get_domain_info(parent
)) {
91 pr_err("%s: unable to locate ITS domain\n", name
);
95 mc_msi_domain
= fsl_mc_msi_create_irq_domain(handle
,
96 &its_fsl_mc_msi_domain_info
,
99 pr_err("%s: unable to create fsl-mc domain\n", name
);
103 pr_info("fsl-mc MSI: %s domain created\n", name
);
108 its_fsl_mc_msi_parse_madt(union acpi_subtable_headers
*header
,
109 const unsigned long end
)
111 struct acpi_madt_generic_translator
*its_entry
;
112 struct fwnode_handle
*dom_handle
;
113 const char *node_name
;
116 its_entry
= (struct acpi_madt_generic_translator
*)header
;
117 node_name
= kasprintf(GFP_KERNEL
, "ITS@0x%lx",
118 (long)its_entry
->base_address
);
120 dom_handle
= iort_find_domain_token(its_entry
->translation_id
);
122 pr_err("%s: Unable to locate ITS domain handle\n", node_name
);
127 its_fsl_mc_msi_init_one(dom_handle
, node_name
);
135 static void __init
its_fsl_mc_acpi_msi_init(void)
137 acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR
,
138 its_fsl_mc_msi_parse_madt
, 0);
141 static inline void its_fsl_mc_acpi_msi_init(void) { }
144 static void __init
its_fsl_mc_of_msi_init(void)
146 struct device_node
*np
;
148 for (np
= of_find_matching_node(NULL
, its_device_id
); np
;
149 np
= of_find_matching_node(np
, its_device_id
)) {
150 if (!of_device_is_available(np
))
152 if (!of_property_read_bool(np
, "msi-controller"))
155 its_fsl_mc_msi_init_one(of_node_to_fwnode(np
),
160 static int __init
its_fsl_mc_msi_init(void)
162 its_fsl_mc_of_msi_init();
163 its_fsl_mc_acpi_msi_init();
168 early_initcall(its_fsl_mc_msi_init
);