Skip to content

Commit 4f4c867

Browse files
miquelraynalMarc Zyngier
authored andcommitted
irqchip/irq-mvebu-icu: Support ICU subnodes
The ICU can handle several type of interrupt, each of them being handled differently on AP side. On CP side, the ICU should be able to make the distinction between each interrupt group by pointing to the right parent. This is done through the introduction of new bindings, presenting the ICU node as the parent of multiple ICU sub-nodes, each of them being an interrupt type with a different interrupt parent. ICU interrupt 'clients' now directly point to the right sub-node, avoiding the need for the extra ICU_GRP_* parameter. ICU subnodes are probed automatically with devm_platform_populate(). If the node as no child, the probe function for NSRs will still be called 'manually' in order to preserve backward compatibility with DT using the old binding. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent 00885a7 commit 4f4c867

File tree

1 file changed

+57
-16
lines changed

1 file changed

+57
-16
lines changed

drivers/irqchip/irq-mvebu-icu.c

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/irq.h>
1414
#include <linux/irqchip.h>
1515
#include <linux/irqdomain.h>
16+
#include <linux/jump_label.h>
1617
#include <linux/kernel.h>
1718
#include <linux/msi.h>
1819
#include <linux/of_irq.h>
@@ -49,6 +50,8 @@ struct mvebu_icu_irq_data {
4950
unsigned int type;
5051
};
5152

53+
DEFINE_STATIC_KEY_FALSE(legacy_bindings);
54+
5255
static void mvebu_icu_init(struct mvebu_icu *icu, struct msi_msg *msg)
5356
{
5457
if (atomic_cmpxchg(&icu->initialized, false, true))
@@ -105,32 +108,33 @@ mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
105108
unsigned long *hwirq, unsigned int *type)
106109
{
107110
struct mvebu_icu *icu = platform_msi_get_host_data(d);
108-
unsigned int icu_group;
111+
unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2;
109112

110113
/* Check the count of the parameters in dt */
111-
if (WARN_ON(fwspec->param_count < 3)) {
114+
if (WARN_ON(fwspec->param_count != param_count)) {
112115
dev_err(icu->dev, "wrong ICU parameter count %d\n",
113116
fwspec->param_count);
114117
return -EINVAL;
115118
}
116119

117-
/* Only ICU group type is handled */
118-
icu_group = fwspec->param[0];
119-
if (icu_group != ICU_GRP_NSR && icu_group != ICU_GRP_SR &&
120-
icu_group != ICU_GRP_SEI && icu_group != ICU_GRP_REI) {
121-
dev_err(icu->dev, "wrong ICU group type %x\n", icu_group);
122-
return -EINVAL;
120+
if (static_branch_unlikely(&legacy_bindings)) {
121+
*hwirq = fwspec->param[1];
122+
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
123+
if (fwspec->param[0] != ICU_GRP_NSR) {
124+
dev_err(icu->dev, "wrong ICU group type %x\n",
125+
fwspec->param[0]);
126+
return -EINVAL;
127+
}
128+
} else {
129+
*hwirq = fwspec->param[0];
130+
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
123131
}
124132

125-
*hwirq = fwspec->param[1];
126133
if (*hwirq >= ICU_MAX_IRQS) {
127134
dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq);
128135
return -EINVAL;
129136
}
130137

131-
/* Mask the type to prevent wrong DT configuration */
132-
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
133-
134138
return 0;
135139
}
136140

@@ -155,7 +159,10 @@ mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
155159
goto free_irqd;
156160
}
157161

158-
icu_irqd->icu_group = fwspec->param[0];
162+
if (static_branch_unlikely(&legacy_bindings))
163+
icu_irqd->icu_group = fwspec->param[0];
164+
else
165+
icu_irqd->icu_group = ICU_GRP_NSR;
159166
icu_irqd->icu = icu;
160167

161168
err = platform_msi_domain_alloc(domain, virq, nr_irqs);
@@ -203,14 +210,24 @@ static const struct irq_domain_ops mvebu_icu_domain_ops = {
203210
.free = mvebu_icu_irq_domain_free,
204211
};
205212

213+
static const struct of_device_id mvebu_icu_subset_of_match[] = {
214+
{
215+
.compatible = "marvell,cp110-icu-nsr",
216+
},
217+
{},
218+
};
219+
206220
static int mvebu_icu_subset_probe(struct platform_device *pdev)
207221
{
208222
struct device_node *msi_parent_dn;
209223
struct device *dev = &pdev->dev;
210224
struct irq_domain *irq_domain;
211225
struct mvebu_icu *icu;
212226

213-
icu = dev_get_drvdata(dev);
227+
if (static_branch_unlikely(&legacy_bindings))
228+
icu = dev_get_drvdata(dev);
229+
else
230+
icu = dev_get_drvdata(dev->parent);
214231

215232
dev->msi_domain = of_msi_get_domain(dev, dev->of_node,
216233
DOMAIN_BUS_PLATFORM_MSI);
@@ -233,6 +250,15 @@ static int mvebu_icu_subset_probe(struct platform_device *pdev)
233250
return 0;
234251
}
235252

253+
static struct platform_driver mvebu_icu_subset_driver = {
254+
.probe = mvebu_icu_subset_probe,
255+
.driver = {
256+
.name = "mvebu-icu-subset",
257+
.of_match_table = mvebu_icu_subset_of_match,
258+
},
259+
};
260+
builtin_platform_driver(mvebu_icu_subset_driver);
261+
236262
static int mvebu_icu_probe(struct platform_device *pdev)
237263
{
238264
struct mvebu_icu *icu;
@@ -259,6 +285,16 @@ static int mvebu_icu_probe(struct platform_device *pdev)
259285
if (!icu->irq_chip.name)
260286
return -ENOMEM;
261287

288+
/*
289+
* Legacy bindings: ICU is one node with one MSI parent: force manually
290+
* the probe of the NSR interrupts side.
291+
* New bindings: ICU node has children, one per interrupt controller
292+
* having its own MSI parent: call platform_populate().
293+
* All ICU instances should use the same bindings.
294+
*/
295+
if (!of_get_child_count(pdev->dev.of_node))
296+
static_branch_enable(&legacy_bindings);
297+
262298
icu->irq_chip.irq_mask = irq_chip_mask_parent;
263299
icu->irq_chip.irq_unmask = irq_chip_unmask_parent;
264300
icu->irq_chip.irq_eoi = irq_chip_eoi_parent;
@@ -277,13 +313,18 @@ static int mvebu_icu_probe(struct platform_device *pdev)
277313
icu_int = readl_relaxed(icu->base + ICU_INT_CFG(i));
278314
icu_grp = icu_int >> ICU_GROUP_SHIFT;
279315

280-
if (icu_grp == ICU_GRP_NSR)
316+
if (icu_grp == ICU_GRP_NSR ||
317+
(icu_grp == ICU_GRP_SEI &&
318+
!static_branch_unlikely(&legacy_bindings)))
281319
writel_relaxed(0x0, icu->base + ICU_INT_CFG(i));
282320
}
283321

284322
platform_set_drvdata(pdev, icu);
285323

286-
return mvebu_icu_subset_probe(pdev);
324+
if (static_branch_unlikely(&legacy_bindings))
325+
return mvebu_icu_subset_probe(pdev);
326+
else
327+
return devm_of_platform_populate(&pdev->dev);
287328
}
288329

289330
static const struct of_device_id mvebu_icu_of_match[] = {

0 commit comments

Comments
 (0)