2 * Copyright (C) 2017 Marvell
4 * Hanna Hawa <hannah@marvell.com>
5 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
7 * This file is licensed under the terms of the GNU General Public
8 * License version 2. This program is licensed "as is" without any
9 * warranty of any kind, whether express or implied.
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
14 #include <linux/irqchip.h>
15 #include <linux/irqdomain.h>
16 #include <linux/jump_label.h>
17 #include <linux/kernel.h>
18 #include <linux/msi.h>
19 #include <linux/of_irq.h>
20 #include <linux/of_platform.h>
21 #include <linux/platform_device.h>
23 #include "irq-msi-lib.h"
25 #include <dt-bindings/interrupt-controller/mvebu-icu.h>
28 #define ICU_SETSPI_NSR_AL 0x10
29 #define ICU_SETSPI_NSR_AH 0x14
30 #define ICU_CLRSPI_NSR_AL 0x18
31 #define ICU_CLRSPI_NSR_AH 0x1c
32 #define ICU_SET_SEI_AL 0x50
33 #define ICU_SET_SEI_AH 0x54
34 #define ICU_CLR_SEI_AL 0x58
35 #define ICU_CLR_SEI_AH 0x5C
36 #define ICU_INT_CFG(x) (0x100 + 4 * (x))
37 #define ICU_INT_ENABLE BIT(24)
38 #define ICU_IS_EDGE BIT(28)
39 #define ICU_GROUP_SHIFT 29
42 #define ICU_MAX_IRQS 207
43 #define ICU_SATA0_ICU_ID 109
44 #define ICU_SATA1_ICU_ID 107
46 struct mvebu_icu_subset_data
{
47 unsigned int icu_group
;
48 unsigned int offset_set_ah
;
49 unsigned int offset_set_al
;
50 unsigned int offset_clr_ah
;
51 unsigned int offset_clr_al
;
59 struct mvebu_icu_msi_data
{
60 struct mvebu_icu
*icu
;
62 const struct mvebu_icu_subset_data
*subset_data
;
65 static DEFINE_STATIC_KEY_FALSE(legacy_bindings
);
67 static int mvebu_icu_translate(struct irq_domain
*d
, struct irq_fwspec
*fwspec
,
68 unsigned long *hwirq
, unsigned int *type
)
70 unsigned int param_count
= static_branch_unlikely(&legacy_bindings
) ? 3 : 2;
71 struct mvebu_icu_msi_data
*msi_data
= d
->host_data
;
72 struct mvebu_icu
*icu
= msi_data
->icu
;
74 /* Check the count of the parameters in dt */
75 if (WARN_ON(fwspec
->param_count
!= param_count
)) {
76 dev_err(icu
->dev
, "wrong ICU parameter count %d\n",
81 if (static_branch_unlikely(&legacy_bindings
)) {
82 *hwirq
= fwspec
->param
[1];
83 *type
= fwspec
->param
[2] & IRQ_TYPE_SENSE_MASK
;
84 if (fwspec
->param
[0] != ICU_GRP_NSR
) {
85 dev_err(icu
->dev
, "wrong ICU group type %x\n",
90 *hwirq
= fwspec
->param
[0];
91 *type
= fwspec
->param
[1] & IRQ_TYPE_SENSE_MASK
;
94 * The ICU receives level interrupts. While the NSR are also
95 * level interrupts, SEI are edge interrupts. Force the type
96 * here in this case. Please note that this makes the interrupt
97 * handling unreliable.
99 if (msi_data
->subset_data
->icu_group
== ICU_GRP_SEI
)
100 *type
= IRQ_TYPE_EDGE_RISING
;
103 if (*hwirq
>= ICU_MAX_IRQS
) {
104 dev_err(icu
->dev
, "invalid interrupt number %ld\n", *hwirq
);
111 static void mvebu_icu_init(struct mvebu_icu
*icu
,
112 struct mvebu_icu_msi_data
*msi_data
,
115 const struct mvebu_icu_subset_data
*subset
= msi_data
->subset_data
;
117 if (atomic_cmpxchg(&msi_data
->initialized
, false, true))
120 /* Set 'SET' ICU SPI message address in AP */
121 writel_relaxed(msg
[0].address_hi
, icu
->base
+ subset
->offset_set_ah
);
122 writel_relaxed(msg
[0].address_lo
, icu
->base
+ subset
->offset_set_al
);
124 if (subset
->icu_group
!= ICU_GRP_NSR
)
127 /* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */
128 writel_relaxed(msg
[1].address_hi
, icu
->base
+ subset
->offset_clr_ah
);
129 writel_relaxed(msg
[1].address_lo
, icu
->base
+ subset
->offset_clr_al
);
132 static int mvebu_icu_msi_init(struct irq_domain
*domain
, struct msi_domain_info
*info
,
133 unsigned int virq
, irq_hw_number_t hwirq
, msi_alloc_info_t
*arg
)
135 irq_domain_set_hwirq_and_chip(domain
, virq
, hwirq
, info
->chip
, info
->chip_data
);
136 return irq_set_irqchip_state(virq
, IRQCHIP_STATE_PENDING
, false);
139 static void mvebu_icu_set_desc(msi_alloc_info_t
*arg
, struct msi_desc
*desc
)
142 arg
->hwirq
= (u32
)desc
->data
.icookie
.value
;
145 static void mvebu_icu_write_msi_msg(struct irq_data
*d
, struct msi_msg
*msg
)
147 struct mvebu_icu_msi_data
*msi_data
= d
->chip_data
;
148 unsigned int icu_group
= msi_data
->subset_data
->icu_group
;
149 struct msi_desc
*desc
= irq_data_get_msi_desc(d
);
150 struct mvebu_icu
*icu
= msi_data
->icu
;
154 if (msg
->address_lo
|| msg
->address_hi
) {
155 /* One off initialization per domain */
156 mvebu_icu_init(icu
, msi_data
, msg
);
157 /* Configure the ICU with irq number & type */
158 icu_int
= msg
->data
| ICU_INT_ENABLE
;
159 type
= (unsigned int)(desc
->data
.icookie
.value
>> 32);
160 if (type
& IRQ_TYPE_EDGE_RISING
)
161 icu_int
|= ICU_IS_EDGE
;
162 icu_int
|= icu_group
<< ICU_GROUP_SHIFT
;
164 /* De-configure the ICU */
168 writel_relaxed(icu_int
, icu
->base
+ ICU_INT_CFG(d
->hwirq
));
171 * The SATA unit has 2 ports, and a dedicated ICU entry per
172 * port. The ahci sata driver supports only one irq interrupt
173 * per SATA unit. To solve this conflict, we configure the 2
174 * SATA wired interrupts in the south bridge into 1 GIC
175 * interrupt in the north bridge. Even if only a single port
176 * is enabled, if sata node is enabled, both interrupts are
177 * configured (regardless of which port is actually in use).
179 if (d
->hwirq
== ICU_SATA0_ICU_ID
|| d
->hwirq
== ICU_SATA1_ICU_ID
) {
180 writel_relaxed(icu_int
, icu
->base
+ ICU_INT_CFG(ICU_SATA0_ICU_ID
));
181 writel_relaxed(icu_int
, icu
->base
+ ICU_INT_CFG(ICU_SATA1_ICU_ID
));
185 static const struct msi_domain_template mvebu_icu_nsr_msi_template
= {
188 .irq_mask
= irq_chip_mask_parent
,
189 .irq_unmask
= irq_chip_unmask_parent
,
190 .irq_eoi
= irq_chip_eoi_parent
,
191 .irq_set_type
= irq_chip_set_type_parent
,
192 .irq_write_msi_msg
= mvebu_icu_write_msi_msg
,
193 .flags
= IRQCHIP_SUPPORTS_LEVEL_MSI
,
197 .msi_translate
= mvebu_icu_translate
,
198 .msi_init
= mvebu_icu_msi_init
,
199 .set_desc
= mvebu_icu_set_desc
,
203 .bus_token
= DOMAIN_BUS_WIRED_TO_MSI
,
204 .flags
= MSI_FLAG_LEVEL_CAPABLE
|
205 MSI_FLAG_USE_DEV_FWNODE
,
209 static const struct msi_domain_template mvebu_icu_sei_msi_template
= {
212 .irq_mask
= irq_chip_mask_parent
,
213 .irq_unmask
= irq_chip_unmask_parent
,
214 .irq_ack
= irq_chip_ack_parent
,
215 .irq_set_type
= irq_chip_set_type_parent
,
216 .irq_write_msi_msg
= mvebu_icu_write_msi_msg
,
217 .flags
= IRQCHIP_SUPPORTS_LEVEL_MSI
,
221 .msi_translate
= mvebu_icu_translate
,
222 .msi_init
= mvebu_icu_msi_init
,
223 .set_desc
= mvebu_icu_set_desc
,
227 .bus_token
= DOMAIN_BUS_WIRED_TO_MSI
,
228 .flags
= MSI_FLAG_LEVEL_CAPABLE
|
229 MSI_FLAG_USE_DEV_FWNODE
,
233 static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data
= {
234 .icu_group
= ICU_GRP_NSR
,
235 .offset_set_ah
= ICU_SETSPI_NSR_AH
,
236 .offset_set_al
= ICU_SETSPI_NSR_AL
,
237 .offset_clr_ah
= ICU_CLRSPI_NSR_AH
,
238 .offset_clr_al
= ICU_CLRSPI_NSR_AL
,
241 static const struct mvebu_icu_subset_data mvebu_icu_sei_subset_data
= {
242 .icu_group
= ICU_GRP_SEI
,
243 .offset_set_ah
= ICU_SET_SEI_AH
,
244 .offset_set_al
= ICU_SET_SEI_AL
,
247 static const struct of_device_id mvebu_icu_subset_of_match
[] = {
249 .compatible
= "marvell,cp110-icu-nsr",
250 .data
= &mvebu_icu_nsr_subset_data
,
253 .compatible
= "marvell,cp110-icu-sei",
254 .data
= &mvebu_icu_sei_subset_data
,
259 static int mvebu_icu_subset_probe(struct platform_device
*pdev
)
261 const struct msi_domain_template
*tmpl
;
262 struct mvebu_icu_msi_data
*msi_data
;
263 struct device
*dev
= &pdev
->dev
;
266 msi_data
= devm_kzalloc(dev
, sizeof(*msi_data
), GFP_KERNEL
);
270 if (static_branch_unlikely(&legacy_bindings
)) {
271 msi_data
->icu
= dev_get_drvdata(dev
);
272 msi_data
->subset_data
= &mvebu_icu_nsr_subset_data
;
274 msi_data
->icu
= dev_get_drvdata(dev
->parent
);
275 msi_data
->subset_data
= of_device_get_match_data(dev
);
278 dev
->msi
.domain
= of_msi_get_domain(dev
, dev
->of_node
, DOMAIN_BUS_PLATFORM_MSI
);
279 if (!dev
->msi
.domain
)
280 return -EPROBE_DEFER
;
282 if (!irq_domain_get_of_node(dev
->msi
.domain
))
285 sei
= msi_data
->subset_data
->icu_group
== ICU_GRP_SEI
;
286 tmpl
= sei
? &mvebu_icu_sei_msi_template
: &mvebu_icu_nsr_msi_template
;
288 if (!msi_create_device_irq_domain(dev
, MSI_DEFAULT_DOMAIN
, tmpl
,
289 ICU_MAX_IRQS
, NULL
, msi_data
)) {
290 dev_err(dev
, "Failed to create ICU MSI domain\n");
297 static struct platform_driver mvebu_icu_subset_driver
= {
298 .probe
= mvebu_icu_subset_probe
,
300 .name
= "mvebu-icu-subset",
301 .of_match_table
= mvebu_icu_subset_of_match
,
304 builtin_platform_driver(mvebu_icu_subset_driver
);
306 static int mvebu_icu_probe(struct platform_device
*pdev
)
308 struct mvebu_icu
*icu
;
311 icu
= devm_kzalloc(&pdev
->dev
, sizeof(struct mvebu_icu
),
316 icu
->dev
= &pdev
->dev
;
318 icu
->base
= devm_platform_ioremap_resource(pdev
, 0);
319 if (IS_ERR(icu
->base
))
320 return PTR_ERR(icu
->base
);
323 * Legacy bindings: ICU is one node with one MSI parent: force manually
324 * the probe of the NSR interrupts side.
325 * New bindings: ICU node has children, one per interrupt controller
326 * having its own MSI parent: call platform_populate().
327 * All ICU instances should use the same bindings.
329 if (!of_get_child_count(pdev
->dev
.of_node
))
330 static_branch_enable(&legacy_bindings
);
333 * Clean all ICU interrupts of type NSR and SEI, required to
334 * avoid unpredictable SPI assignments done by firmware.
336 for (i
= 0 ; i
< ICU_MAX_IRQS
; i
++) {
337 u32 icu_int
, icu_grp
;
339 icu_int
= readl_relaxed(icu
->base
+ ICU_INT_CFG(i
));
340 icu_grp
= icu_int
>> ICU_GROUP_SHIFT
;
342 if (icu_grp
== ICU_GRP_NSR
||
343 (icu_grp
== ICU_GRP_SEI
&&
344 !static_branch_unlikely(&legacy_bindings
)))
345 writel_relaxed(0x0, icu
->base
+ ICU_INT_CFG(i
));
348 platform_set_drvdata(pdev
, icu
);
350 if (static_branch_unlikely(&legacy_bindings
))
351 return mvebu_icu_subset_probe(pdev
);
353 return devm_of_platform_populate(&pdev
->dev
);
356 static const struct of_device_id mvebu_icu_of_match
[] = {
357 { .compatible
= "marvell,cp110-icu", },
361 static struct platform_driver mvebu_icu_driver
= {
362 .probe
= mvebu_icu_probe
,
365 .of_match_table
= mvebu_icu_of_match
,
368 builtin_platform_driver(mvebu_icu_driver
);