@@ -507,11 +507,32 @@ EXPORT_SYMBOL_GPL(of_irq_to_resource_table);
struct of_intc_desc {
struct list_head list;
+ struct list_head parents;
of_irq_init_cb_t irq_init_cb;
struct device_node *dev;
struct device_node *interrupt_parent;
};
+static bool of_irq_init_check_parents(struct of_intc_desc *desc,
+ struct device_node *parent)
+{
+ struct of_intc_desc *search, *temp_desc;
+
+ if (list_empty(&desc->parents))
+ return desc->interrupt_parent == parent;
+
+ list_for_each_entry_safe(search, temp_desc, &desc->parents, list) {
+ if (search->interrupt_parent == parent) {
+ pr_debug("%s: %pOF removing %pOF\n",
+ __func__, desc->dev, search->interrupt_parent);
+ list_del(&search->list);
+ kfree(search);
+ }
+ }
+
+ return list_empty(&desc->parents);
+}
+
/**
* of_irq_init - Scan and init matching interrupt controllers in DT
* @matches: 0 terminated array of nodes to match and init function to call
@@ -548,6 +569,7 @@ void __init of_irq_init(const struct of_device_id *matches)
goto err;
}
+ INIT_LIST_HEAD(&desc->parents);
desc->irq_init_cb = match->data;
desc->dev = of_node_get(np);
/*
@@ -563,6 +585,40 @@ void __init of_irq_init(const struct of_device_id *matches)
desc->interrupt_parent = NULL;
}
list_add_tail(&desc->list, &intc_desc_list);
+
+ /*
+ * If the irq controller has an interrupts-extended
+ * property then go through it to find out if there
+ * are any parents in there to consider.
+ */
+ if (!desc->interrupt_parent &&
+ of_find_property(np, "interrupts-extended", NULL)) {
+ struct of_phandle_args irq;
+ int nr_irqs = of_irq_count(np);
+ int index, res;
+
+ pr_debug("%s: %pOF interrupts-extended, %d irqs\n",
+ __func__, np, nr_irqs);
+
+ for (index = 0; index < nr_irqs; index++) {
+ res = of_parse_phandle_with_args(np, "interrupts-extended",
+ "#interrupt-cells", index, &irq);
+ if (res) {
+ goto err;
+ }
+
+ pr_debug("%s: %pOF parent %pOF\n", __func__, np, irq.np);
+ if (irq.np == np)
+ continue;
+
+ temp_desc = kzalloc(sizeof(*temp_desc), GFP_KERNEL);
+ if (!temp_desc)
+ goto err;
+
+ temp_desc->interrupt_parent = irq.np;
+ list_add_tail(&temp_desc->list, &desc->parents);
+ }
+ }
}
/*
@@ -579,14 +635,14 @@ void __init of_irq_init(const struct of_device_id *matches)
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
int ret;
- if (desc->interrupt_parent != parent)
+ if (!of_irq_init_check_parents(desc, parent))
continue;
list_del(&desc->list);
of_node_set_flag(desc->dev, OF_POPULATED);
- pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
+ pr_debug("of_irq_init: init %pOF (%px), parent %px\n",
desc->dev,
desc->dev, desc->interrupt_parent);
ret = desc->irq_init_cb(desc->dev,
When the irq controler code works out the heirarchy for initialialisation it only looks at interrupt-parent properties, but controllers such as the RISC-V PLIC use a extended-interrupt property and therefore do not get properly considered during initialisation. This means that if anything changes in the driver initialisation order then the PLIC can get called before the CLINT nodes, and thus interrupts do not get configured properly and the init continues without noticing the error until drivers fail due to having no interrupts delivered. Add code to the of_irq_init that checks for the extended-interrupt property and adds these parent nodes so that they can be considered during the calculations of whether an irq controller node can be initialised. Signed-off-by: Ben Dooks <ben.dooks@sifive.com> --- drivers/of/irq.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-)