xref: /linux/drivers/irqchip/irq-mvebu-icu.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1e0de91a9SThomas Petazzoni /*
2e0de91a9SThomas Petazzoni  * Copyright (C) 2017 Marvell
3e0de91a9SThomas Petazzoni  *
4e0de91a9SThomas Petazzoni  * Hanna Hawa <hannah@marvell.com>
5e0de91a9SThomas Petazzoni  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
6e0de91a9SThomas Petazzoni  *
7e0de91a9SThomas Petazzoni  * This file is licensed under the terms of the GNU General Public
8e0de91a9SThomas Petazzoni  * License version 2. This program is licensed "as is" without any
9e0de91a9SThomas Petazzoni  * warranty of any kind, whether express or implied.
10e0de91a9SThomas Petazzoni  */
11e0de91a9SThomas Petazzoni 
12e0de91a9SThomas Petazzoni #include <linux/interrupt.h>
13e0de91a9SThomas Petazzoni #include <linux/irq.h>
14e0de91a9SThomas Petazzoni #include <linux/irqchip.h>
15e0de91a9SThomas Petazzoni #include <linux/irqdomain.h>
164f4c867cSMiquel Raynal #include <linux/jump_label.h>
17e0de91a9SThomas Petazzoni #include <linux/kernel.h>
18e0de91a9SThomas Petazzoni #include <linux/msi.h>
19e0de91a9SThomas Petazzoni #include <linux/of_irq.h>
20e0de91a9SThomas Petazzoni #include <linux/of_platform.h>
21e0de91a9SThomas Petazzoni #include <linux/platform_device.h>
22e0de91a9SThomas Petazzoni 
23e0de91a9SThomas Petazzoni #include <dt-bindings/interrupt-controller/mvebu-icu.h>
24e0de91a9SThomas Petazzoni 
25e0de91a9SThomas Petazzoni /* ICU registers */
26e0de91a9SThomas Petazzoni #define ICU_SETSPI_NSR_AL	0x10
27e0de91a9SThomas Petazzoni #define ICU_SETSPI_NSR_AH	0x14
28e0de91a9SThomas Petazzoni #define ICU_CLRSPI_NSR_AL	0x18
29e0de91a9SThomas Petazzoni #define ICU_CLRSPI_NSR_AH	0x1c
30175c98aaSMiquel Raynal #define ICU_SET_SEI_AL		0x50
31175c98aaSMiquel Raynal #define ICU_SET_SEI_AH		0x54
32175c98aaSMiquel Raynal #define ICU_CLR_SEI_AL		0x58
33175c98aaSMiquel Raynal #define ICU_CLR_SEI_AH		0x5C
34e0de91a9SThomas Petazzoni #define ICU_INT_CFG(x)          (0x100 + 4 * (x))
35e0de91a9SThomas Petazzoni #define   ICU_INT_ENABLE	BIT(24)
36e0de91a9SThomas Petazzoni #define   ICU_IS_EDGE		BIT(28)
37e0de91a9SThomas Petazzoni #define   ICU_GROUP_SHIFT	29
38e0de91a9SThomas Petazzoni 
39e0de91a9SThomas Petazzoni /* ICU definitions */
40e0de91a9SThomas Petazzoni #define ICU_MAX_IRQS		207
41e0de91a9SThomas Petazzoni #define ICU_SATA0_ICU_ID	109
42e0de91a9SThomas Petazzoni #define ICU_SATA1_ICU_ID	107
43e0de91a9SThomas Petazzoni 
44175c98aaSMiquel Raynal struct mvebu_icu_subset_data {
45175c98aaSMiquel Raynal 	unsigned int icu_group;
46175c98aaSMiquel Raynal 	unsigned int offset_set_ah;
47175c98aaSMiquel Raynal 	unsigned int offset_set_al;
48175c98aaSMiquel Raynal 	unsigned int offset_clr_ah;
49175c98aaSMiquel Raynal 	unsigned int offset_clr_al;
50175c98aaSMiquel Raynal };
51175c98aaSMiquel Raynal 
52e0de91a9SThomas Petazzoni struct mvebu_icu {
53e0de91a9SThomas Petazzoni 	void __iomem *base;
54e0de91a9SThomas Petazzoni 	struct device *dev;
55175c98aaSMiquel Raynal };
56175c98aaSMiquel Raynal 
57175c98aaSMiquel Raynal struct mvebu_icu_msi_data {
58175c98aaSMiquel Raynal 	struct mvebu_icu *icu;
5925eaaabbSMarc Zyngier 	atomic_t initialized;
60175c98aaSMiquel Raynal 	const struct mvebu_icu_subset_data *subset_data;
61e0de91a9SThomas Petazzoni };
62e0de91a9SThomas Petazzoni 
63e0de91a9SThomas Petazzoni struct mvebu_icu_irq_data {
64e0de91a9SThomas Petazzoni 	struct mvebu_icu *icu;
65e0de91a9SThomas Petazzoni 	unsigned int icu_group;
66e0de91a9SThomas Petazzoni 	unsigned int type;
67e0de91a9SThomas Petazzoni };
68e0de91a9SThomas Petazzoni 
699fed9ccbSJason Yan static DEFINE_STATIC_KEY_FALSE(legacy_bindings);
704f4c867cSMiquel Raynal 
71175c98aaSMiquel Raynal static void mvebu_icu_init(struct mvebu_icu *icu,
72175c98aaSMiquel Raynal 			   struct mvebu_icu_msi_data *msi_data,
73175c98aaSMiquel Raynal 			   struct msi_msg *msg)
7425eaaabbSMarc Zyngier {
75175c98aaSMiquel Raynal 	const struct mvebu_icu_subset_data *subset = msi_data->subset_data;
76175c98aaSMiquel Raynal 
77175c98aaSMiquel Raynal 	if (atomic_cmpxchg(&msi_data->initialized, false, true))
7825eaaabbSMarc Zyngier 		return;
7925eaaabbSMarc Zyngier 
80175c98aaSMiquel Raynal 	/* Set 'SET' ICU SPI message address in AP */
81175c98aaSMiquel Raynal 	writel_relaxed(msg[0].address_hi, icu->base + subset->offset_set_ah);
82175c98aaSMiquel Raynal 	writel_relaxed(msg[0].address_lo, icu->base + subset->offset_set_al);
83175c98aaSMiquel Raynal 
84175c98aaSMiquel Raynal 	if (subset->icu_group != ICU_GRP_NSR)
85175c98aaSMiquel Raynal 		return;
86175c98aaSMiquel Raynal 
87175c98aaSMiquel Raynal 	/* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */
88175c98aaSMiquel Raynal 	writel_relaxed(msg[1].address_hi, icu->base + subset->offset_clr_ah);
89175c98aaSMiquel Raynal 	writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al);
9025eaaabbSMarc Zyngier }
9125eaaabbSMarc Zyngier 
92e0de91a9SThomas Petazzoni static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg)
93e0de91a9SThomas Petazzoni {
94e0de91a9SThomas Petazzoni 	struct irq_data *d = irq_get_irq_data(desc->irq);
95175c98aaSMiquel Raynal 	struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d->domain);
96e0de91a9SThomas Petazzoni 	struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
97e0de91a9SThomas Petazzoni 	struct mvebu_icu *icu = icu_irqd->icu;
98e0de91a9SThomas Petazzoni 	unsigned int icu_int;
99e0de91a9SThomas Petazzoni 
100e0de91a9SThomas Petazzoni 	if (msg->address_lo || msg->address_hi) {
101175c98aaSMiquel Raynal 		/* One off initialization per domain */
102175c98aaSMiquel Raynal 		mvebu_icu_init(icu, msi_data, msg);
103e0de91a9SThomas Petazzoni 		/* Configure the ICU with irq number & type */
104e0de91a9SThomas Petazzoni 		icu_int = msg->data | ICU_INT_ENABLE;
105e0de91a9SThomas Petazzoni 		if (icu_irqd->type & IRQ_TYPE_EDGE_RISING)
106e0de91a9SThomas Petazzoni 			icu_int |= ICU_IS_EDGE;
107e0de91a9SThomas Petazzoni 		icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT;
108e0de91a9SThomas Petazzoni 	} else {
109e0de91a9SThomas Petazzoni 		/* De-configure the ICU */
110e0de91a9SThomas Petazzoni 		icu_int = 0;
111e0de91a9SThomas Petazzoni 	}
112e0de91a9SThomas Petazzoni 
113e0de91a9SThomas Petazzoni 	writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq));
114e0de91a9SThomas Petazzoni 
115e0de91a9SThomas Petazzoni 	/*
116e0de91a9SThomas Petazzoni 	 * The SATA unit has 2 ports, and a dedicated ICU entry per
117e0de91a9SThomas Petazzoni 	 * port. The ahci sata driver supports only one irq interrupt
118e0de91a9SThomas Petazzoni 	 * per SATA unit. To solve this conflict, we configure the 2
119e0de91a9SThomas Petazzoni 	 * SATA wired interrupts in the south bridge into 1 GIC
120e0de91a9SThomas Petazzoni 	 * interrupt in the north bridge. Even if only a single port
121e0de91a9SThomas Petazzoni 	 * is enabled, if sata node is enabled, both interrupts are
122e0de91a9SThomas Petazzoni 	 * configured (regardless of which port is actually in use).
123e0de91a9SThomas Petazzoni 	 */
124e0de91a9SThomas Petazzoni 	if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) {
125e0de91a9SThomas Petazzoni 		writel_relaxed(icu_int,
126e0de91a9SThomas Petazzoni 			       icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID));
127e0de91a9SThomas Petazzoni 		writel_relaxed(icu_int,
128e0de91a9SThomas Petazzoni 			       icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID));
129e0de91a9SThomas Petazzoni 	}
130e0de91a9SThomas Petazzoni }
131e0de91a9SThomas Petazzoni 
132175c98aaSMiquel Raynal static struct irq_chip mvebu_icu_nsr_chip = {
133175c98aaSMiquel Raynal 	.name			= "ICU-NSR",
134175c98aaSMiquel Raynal 	.irq_mask		= irq_chip_mask_parent,
135175c98aaSMiquel Raynal 	.irq_unmask		= irq_chip_unmask_parent,
136175c98aaSMiquel Raynal 	.irq_eoi		= irq_chip_eoi_parent,
137175c98aaSMiquel Raynal 	.irq_set_type		= irq_chip_set_type_parent,
138175c98aaSMiquel Raynal 	.irq_set_affinity	= irq_chip_set_affinity_parent,
139175c98aaSMiquel Raynal };
140175c98aaSMiquel Raynal 
141175c98aaSMiquel Raynal static struct irq_chip mvebu_icu_sei_chip = {
142175c98aaSMiquel Raynal 	.name			= "ICU-SEI",
143175c98aaSMiquel Raynal 	.irq_ack		= irq_chip_ack_parent,
144175c98aaSMiquel Raynal 	.irq_mask		= irq_chip_mask_parent,
145175c98aaSMiquel Raynal 	.irq_unmask		= irq_chip_unmask_parent,
146175c98aaSMiquel Raynal 	.irq_set_type		= irq_chip_set_type_parent,
147175c98aaSMiquel Raynal 	.irq_set_affinity	= irq_chip_set_affinity_parent,
148175c98aaSMiquel Raynal };
149175c98aaSMiquel Raynal 
150e0de91a9SThomas Petazzoni static int
151e0de91a9SThomas Petazzoni mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
152e0de91a9SThomas Petazzoni 			       unsigned long *hwirq, unsigned int *type)
153e0de91a9SThomas Petazzoni {
154175c98aaSMiquel Raynal 	struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d);
1552b4dab69SMiquel Raynal 	struct mvebu_icu *icu = platform_msi_get_host_data(d);
1564f4c867cSMiquel Raynal 	unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2;
157e0de91a9SThomas Petazzoni 
158e0de91a9SThomas Petazzoni 	/* Check the count of the parameters in dt */
1594f4c867cSMiquel Raynal 	if (WARN_ON(fwspec->param_count != param_count)) {
160e0de91a9SThomas Petazzoni 		dev_err(icu->dev, "wrong ICU parameter count %d\n",
161e0de91a9SThomas Petazzoni 			fwspec->param_count);
162e0de91a9SThomas Petazzoni 		return -EINVAL;
163e0de91a9SThomas Petazzoni 	}
164e0de91a9SThomas Petazzoni 
1654f4c867cSMiquel Raynal 	if (static_branch_unlikely(&legacy_bindings)) {
1664f4c867cSMiquel Raynal 		*hwirq = fwspec->param[1];
1674f4c867cSMiquel Raynal 		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
1684f4c867cSMiquel Raynal 		if (fwspec->param[0] != ICU_GRP_NSR) {
1694f4c867cSMiquel Raynal 			dev_err(icu->dev, "wrong ICU group type %x\n",
1704f4c867cSMiquel Raynal 				fwspec->param[0]);
171e0de91a9SThomas Petazzoni 			return -EINVAL;
172e0de91a9SThomas Petazzoni 		}
1734f4c867cSMiquel Raynal 	} else {
1744f4c867cSMiquel Raynal 		*hwirq = fwspec->param[0];
1754f4c867cSMiquel Raynal 		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
176175c98aaSMiquel Raynal 
177175c98aaSMiquel Raynal 		/*
178175c98aaSMiquel Raynal 		 * The ICU receives level interrupts. While the NSR are also
179175c98aaSMiquel Raynal 		 * level interrupts, SEI are edge interrupts. Force the type
180175c98aaSMiquel Raynal 		 * here in this case. Please note that this makes the interrupt
181175c98aaSMiquel Raynal 		 * handling unreliable.
182175c98aaSMiquel Raynal 		 */
183175c98aaSMiquel Raynal 		if (msi_data->subset_data->icu_group == ICU_GRP_SEI)
184175c98aaSMiquel Raynal 			*type = IRQ_TYPE_EDGE_RISING;
1854f4c867cSMiquel Raynal 	}
186e0de91a9SThomas Petazzoni 
187e0de91a9SThomas Petazzoni 	if (*hwirq >= ICU_MAX_IRQS) {
188e0de91a9SThomas Petazzoni 		dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq);
189e0de91a9SThomas Petazzoni 		return -EINVAL;
190e0de91a9SThomas Petazzoni 	}
191e0de91a9SThomas Petazzoni 
192e0de91a9SThomas Petazzoni 	return 0;
193e0de91a9SThomas Petazzoni }
194e0de91a9SThomas Petazzoni 
195e0de91a9SThomas Petazzoni static int
196e0de91a9SThomas Petazzoni mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
197e0de91a9SThomas Petazzoni 			   unsigned int nr_irqs, void *args)
198e0de91a9SThomas Petazzoni {
199e0de91a9SThomas Petazzoni 	int err;
200e0de91a9SThomas Petazzoni 	unsigned long hwirq;
201e0de91a9SThomas Petazzoni 	struct irq_fwspec *fwspec = args;
202175c98aaSMiquel Raynal 	struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(domain);
203175c98aaSMiquel Raynal 	struct mvebu_icu *icu = msi_data->icu;
204e0de91a9SThomas Petazzoni 	struct mvebu_icu_irq_data *icu_irqd;
205175c98aaSMiquel Raynal 	struct irq_chip *chip = &mvebu_icu_nsr_chip;
206e0de91a9SThomas Petazzoni 
207e0de91a9SThomas Petazzoni 	icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL);
208e0de91a9SThomas Petazzoni 	if (!icu_irqd)
209e0de91a9SThomas Petazzoni 		return -ENOMEM;
210e0de91a9SThomas Petazzoni 
211e0de91a9SThomas Petazzoni 	err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq,
212e0de91a9SThomas Petazzoni 					     &icu_irqd->type);
213e0de91a9SThomas Petazzoni 	if (err) {
214e0de91a9SThomas Petazzoni 		dev_err(icu->dev, "failed to translate ICU parameters\n");
215e0de91a9SThomas Petazzoni 		goto free_irqd;
216e0de91a9SThomas Petazzoni 	}
217e0de91a9SThomas Petazzoni 
2184f4c867cSMiquel Raynal 	if (static_branch_unlikely(&legacy_bindings))
219e0de91a9SThomas Petazzoni 		icu_irqd->icu_group = fwspec->param[0];
2204f4c867cSMiquel Raynal 	else
221175c98aaSMiquel Raynal 		icu_irqd->icu_group = msi_data->subset_data->icu_group;
222e0de91a9SThomas Petazzoni 	icu_irqd->icu = icu;
223e0de91a9SThomas Petazzoni 
2249835cec6SThomas Gleixner 	err = platform_msi_device_domain_alloc(domain, virq, nr_irqs);
225e0de91a9SThomas Petazzoni 	if (err) {
226e0de91a9SThomas Petazzoni 		dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n");
227e0de91a9SThomas Petazzoni 		goto free_irqd;
228e0de91a9SThomas Petazzoni 	}
229e0de91a9SThomas Petazzoni 
230e0de91a9SThomas Petazzoni 	/* Make sure there is no interrupt left pending by the firmware */
231e0de91a9SThomas Petazzoni 	err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false);
232e0de91a9SThomas Petazzoni 	if (err)
233e0de91a9SThomas Petazzoni 		goto free_msi;
234e0de91a9SThomas Petazzoni 
235175c98aaSMiquel Raynal 	if (icu_irqd->icu_group == ICU_GRP_SEI)
236175c98aaSMiquel Raynal 		chip = &mvebu_icu_sei_chip;
237175c98aaSMiquel Raynal 
238e0de91a9SThomas Petazzoni 	err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
239175c98aaSMiquel Raynal 					    chip, icu_irqd);
240e0de91a9SThomas Petazzoni 	if (err) {
241e0de91a9SThomas Petazzoni 		dev_err(icu->dev, "failed to set the data to IRQ domain\n");
242e0de91a9SThomas Petazzoni 		goto free_msi;
243e0de91a9SThomas Petazzoni 	}
244e0de91a9SThomas Petazzoni 
245e0de91a9SThomas Petazzoni 	return 0;
246e0de91a9SThomas Petazzoni 
247e0de91a9SThomas Petazzoni free_msi:
2489835cec6SThomas Gleixner 	platform_msi_device_domain_free(domain, virq, nr_irqs);
249e0de91a9SThomas Petazzoni free_irqd:
250e0de91a9SThomas Petazzoni 	kfree(icu_irqd);
251e0de91a9SThomas Petazzoni 	return err;
252e0de91a9SThomas Petazzoni }
253e0de91a9SThomas Petazzoni 
254e0de91a9SThomas Petazzoni static void
255e0de91a9SThomas Petazzoni mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq,
256e0de91a9SThomas Petazzoni 			  unsigned int nr_irqs)
257e0de91a9SThomas Petazzoni {
258e0de91a9SThomas Petazzoni 	struct irq_data *d = irq_get_irq_data(virq);
259e0de91a9SThomas Petazzoni 	struct mvebu_icu_irq_data *icu_irqd = d->chip_data;
260e0de91a9SThomas Petazzoni 
261e0de91a9SThomas Petazzoni 	kfree(icu_irqd);
262e0de91a9SThomas Petazzoni 
2639835cec6SThomas Gleixner 	platform_msi_device_domain_free(domain, virq, nr_irqs);
264e0de91a9SThomas Petazzoni }
265e0de91a9SThomas Petazzoni 
266e0de91a9SThomas Petazzoni static const struct irq_domain_ops mvebu_icu_domain_ops = {
267e0de91a9SThomas Petazzoni 	.translate = mvebu_icu_irq_domain_translate,
268e0de91a9SThomas Petazzoni 	.alloc     = mvebu_icu_irq_domain_alloc,
269e0de91a9SThomas Petazzoni 	.free      = mvebu_icu_irq_domain_free,
270e0de91a9SThomas Petazzoni };
271e0de91a9SThomas Petazzoni 
272175c98aaSMiquel Raynal static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data = {
273175c98aaSMiquel Raynal 	.icu_group = ICU_GRP_NSR,
274175c98aaSMiquel Raynal 	.offset_set_ah = ICU_SETSPI_NSR_AH,
275175c98aaSMiquel Raynal 	.offset_set_al = ICU_SETSPI_NSR_AL,
276175c98aaSMiquel Raynal 	.offset_clr_ah = ICU_CLRSPI_NSR_AH,
277175c98aaSMiquel Raynal 	.offset_clr_al = ICU_CLRSPI_NSR_AL,
278175c98aaSMiquel Raynal };
279175c98aaSMiquel Raynal 
280175c98aaSMiquel Raynal static const struct mvebu_icu_subset_data mvebu_icu_sei_subset_data = {
281175c98aaSMiquel Raynal 	.icu_group = ICU_GRP_SEI,
282175c98aaSMiquel Raynal 	.offset_set_ah = ICU_SET_SEI_AH,
283175c98aaSMiquel Raynal 	.offset_set_al = ICU_SET_SEI_AL,
284175c98aaSMiquel Raynal };
285175c98aaSMiquel Raynal 
2864f4c867cSMiquel Raynal static const struct of_device_id mvebu_icu_subset_of_match[] = {
2874f4c867cSMiquel Raynal 	{
2884f4c867cSMiquel Raynal 		.compatible = "marvell,cp110-icu-nsr",
289175c98aaSMiquel Raynal 		.data = &mvebu_icu_nsr_subset_data,
290175c98aaSMiquel Raynal 	},
291175c98aaSMiquel Raynal 	{
292175c98aaSMiquel Raynal 		.compatible = "marvell,cp110-icu-sei",
293175c98aaSMiquel Raynal 		.data = &mvebu_icu_sei_subset_data,
2944f4c867cSMiquel Raynal 	},
2954f4c867cSMiquel Raynal 	{},
2964f4c867cSMiquel Raynal };
2974f4c867cSMiquel Raynal 
29800885a77SMiquel Raynal static int mvebu_icu_subset_probe(struct platform_device *pdev)
29900885a77SMiquel Raynal {
300175c98aaSMiquel Raynal 	struct mvebu_icu_msi_data *msi_data;
30100885a77SMiquel Raynal 	struct device_node *msi_parent_dn;
30200885a77SMiquel Raynal 	struct device *dev = &pdev->dev;
30300885a77SMiquel Raynal 	struct irq_domain *irq_domain;
30400885a77SMiquel Raynal 
305175c98aaSMiquel Raynal 	msi_data = devm_kzalloc(dev, sizeof(*msi_data), GFP_KERNEL);
306175c98aaSMiquel Raynal 	if (!msi_data)
307175c98aaSMiquel Raynal 		return -ENOMEM;
308175c98aaSMiquel Raynal 
309175c98aaSMiquel Raynal 	if (static_branch_unlikely(&legacy_bindings)) {
310175c98aaSMiquel Raynal 		msi_data->icu = dev_get_drvdata(dev);
311175c98aaSMiquel Raynal 		msi_data->subset_data = &mvebu_icu_nsr_subset_data;
312175c98aaSMiquel Raynal 	} else {
313175c98aaSMiquel Raynal 		msi_data->icu = dev_get_drvdata(dev->parent);
314175c98aaSMiquel Raynal 		msi_data->subset_data = of_device_get_match_data(dev);
315175c98aaSMiquel Raynal 	}
31600885a77SMiquel Raynal 
31734fff628SThomas Gleixner 	dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
31800885a77SMiquel Raynal 					    DOMAIN_BUS_PLATFORM_MSI);
31934fff628SThomas Gleixner 	if (!dev->msi.domain)
32000885a77SMiquel Raynal 		return -EPROBE_DEFER;
32100885a77SMiquel Raynal 
32234fff628SThomas Gleixner 	msi_parent_dn = irq_domain_get_of_node(dev->msi.domain);
32300885a77SMiquel Raynal 	if (!msi_parent_dn)
32400885a77SMiquel Raynal 		return -ENODEV;
32500885a77SMiquel Raynal 
32600885a77SMiquel Raynal 	irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS,
32700885a77SMiquel Raynal 							    mvebu_icu_write_msg,
32800885a77SMiquel Raynal 							    &mvebu_icu_domain_ops,
329175c98aaSMiquel Raynal 							    msi_data);
33000885a77SMiquel Raynal 	if (!irq_domain) {
33100885a77SMiquel Raynal 		dev_err(dev, "Failed to create ICU MSI domain\n");
33200885a77SMiquel Raynal 		return -ENOMEM;
33300885a77SMiquel Raynal 	}
33400885a77SMiquel Raynal 
33500885a77SMiquel Raynal 	return 0;
33600885a77SMiquel Raynal }
33700885a77SMiquel Raynal 
3384f4c867cSMiquel Raynal static struct platform_driver mvebu_icu_subset_driver = {
3394f4c867cSMiquel Raynal 	.probe  = mvebu_icu_subset_probe,
3404f4c867cSMiquel Raynal 	.driver = {
3414f4c867cSMiquel Raynal 		.name = "mvebu-icu-subset",
3424f4c867cSMiquel Raynal 		.of_match_table = mvebu_icu_subset_of_match,
3434f4c867cSMiquel Raynal 	},
3444f4c867cSMiquel Raynal };
3454f4c867cSMiquel Raynal builtin_platform_driver(mvebu_icu_subset_driver);
3464f4c867cSMiquel Raynal 
347e0de91a9SThomas Petazzoni static int mvebu_icu_probe(struct platform_device *pdev)
348e0de91a9SThomas Petazzoni {
349e0de91a9SThomas Petazzoni 	struct mvebu_icu *icu;
35025eaaabbSMarc Zyngier 	int i;
351e0de91a9SThomas Petazzoni 
352e0de91a9SThomas Petazzoni 	icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu),
353e0de91a9SThomas Petazzoni 			   GFP_KERNEL);
354e0de91a9SThomas Petazzoni 	if (!icu)
355e0de91a9SThomas Petazzoni 		return -ENOMEM;
356e0de91a9SThomas Petazzoni 
357e0de91a9SThomas Petazzoni 	icu->dev = &pdev->dev;
358e0de91a9SThomas Petazzoni 
3590c1479a6SCai Huoqing 	icu->base = devm_platform_ioremap_resource(pdev, 0);
360fbb80d5aSZhen Lei 	if (IS_ERR(icu->base))
361e0de91a9SThomas Petazzoni 		return PTR_ERR(icu->base);
362e0de91a9SThomas Petazzoni 
3634f4c867cSMiquel Raynal 	/*
3644f4c867cSMiquel Raynal 	 * Legacy bindings: ICU is one node with one MSI parent: force manually
3654f4c867cSMiquel Raynal 	 *                  the probe of the NSR interrupts side.
3664f4c867cSMiquel Raynal 	 * New bindings: ICU node has children, one per interrupt controller
3674f4c867cSMiquel Raynal 	 *               having its own MSI parent: call platform_populate().
3684f4c867cSMiquel Raynal 	 * All ICU instances should use the same bindings.
3694f4c867cSMiquel Raynal 	 */
3704f4c867cSMiquel Raynal 	if (!of_get_child_count(pdev->dev.of_node))
3714f4c867cSMiquel Raynal 		static_branch_enable(&legacy_bindings);
3724f4c867cSMiquel Raynal 
373e0de91a9SThomas Petazzoni 	/*
374175c98aaSMiquel Raynal 	 * Clean all ICU interrupts of type NSR and SEI, required to
375e0de91a9SThomas Petazzoni 	 * avoid unpredictable SPI assignments done by firmware.
376e0de91a9SThomas Petazzoni 	 */
377e0de91a9SThomas Petazzoni 	for (i = 0 ; i < ICU_MAX_IRQS ; i++) {
3789770c667SMiquel Raynal 		u32 icu_int, icu_grp;
3799770c667SMiquel Raynal 
3809770c667SMiquel Raynal 		icu_int = readl_relaxed(icu->base + ICU_INT_CFG(i));
3819770c667SMiquel Raynal 		icu_grp = icu_int >> ICU_GROUP_SHIFT;
3829770c667SMiquel Raynal 
3834f4c867cSMiquel Raynal 		if (icu_grp == ICU_GRP_NSR ||
3844f4c867cSMiquel Raynal 		    (icu_grp == ICU_GRP_SEI &&
3854f4c867cSMiquel Raynal 		     !static_branch_unlikely(&legacy_bindings)))
386e0de91a9SThomas Petazzoni 			writel_relaxed(0x0, icu->base + ICU_INT_CFG(i));
387e0de91a9SThomas Petazzoni 	}
388e0de91a9SThomas Petazzoni 
38900885a77SMiquel Raynal 	platform_set_drvdata(pdev, icu);
390e0de91a9SThomas Petazzoni 
3914f4c867cSMiquel Raynal 	if (static_branch_unlikely(&legacy_bindings))
39200885a77SMiquel Raynal 		return mvebu_icu_subset_probe(pdev);
3934f4c867cSMiquel Raynal 	else
3944f4c867cSMiquel Raynal 		return devm_of_platform_populate(&pdev->dev);
395e0de91a9SThomas Petazzoni }
396e0de91a9SThomas Petazzoni 
397e0de91a9SThomas Petazzoni static const struct of_device_id mvebu_icu_of_match[] = {
398e0de91a9SThomas Petazzoni 	{ .compatible = "marvell,cp110-icu", },
399e0de91a9SThomas Petazzoni 	{},
400e0de91a9SThomas Petazzoni };
401e0de91a9SThomas Petazzoni 
402e0de91a9SThomas Petazzoni static struct platform_driver mvebu_icu_driver = {
403e0de91a9SThomas Petazzoni 	.probe  = mvebu_icu_probe,
404e0de91a9SThomas Petazzoni 	.driver = {
405e0de91a9SThomas Petazzoni 		.name = "mvebu-icu",
406e0de91a9SThomas Petazzoni 		.of_match_table = mvebu_icu_of_match,
407e0de91a9SThomas Petazzoni 	},
408e0de91a9SThomas Petazzoni };
409e0de91a9SThomas Petazzoni builtin_platform_driver(mvebu_icu_driver);
410