Commit 77b5f703 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Michael Ellerman

powerpc/powernv/opal: Use standard interrupts property when available

For (bad) historical reasons, OPAL used to create a non-standard pair
of properties "opal-interrupts" and "opal-interrupts-names" for
representing the list of interrupts it wants Linux to request on its
behalf.

Among other issues, the opal-interrupts doesn't have a way to carry
the type of interrupts, and they were assumed to be all level
sensitive.

This is wrong on some recent systems where some of them are edge
sensitive causing warnings in the XIVE code and possible misbehaviours
if they need to be retriggered (typically the NPU2 TCE error
interrupts).

This makes Linux switch to using the standard "interrupts" and
"interrupt-names" properties instead when they are available, using
standard of_irq helpers, which can carry all the desired type
information.

Newer versions of OPAL will generate those properties in addition to
the legacy ones.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
[mpe: Fixup prefix logic to check strlen(r->name). Reinstate setting
 of start = 0 in opal_event_shutdown() to avoid double free warnings]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent d6690b1a
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of_irq.h>
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/opal.h> #include <asm/opal.h>
...@@ -38,8 +39,8 @@ struct opal_event_irqchip { ...@@ -38,8 +39,8 @@ struct opal_event_irqchip {
}; };
static struct opal_event_irqchip opal_event_irqchip; static struct opal_event_irqchip opal_event_irqchip;
static u64 last_outstanding_events; static u64 last_outstanding_events;
static unsigned int opal_irq_count; static int opal_irq_count;
static unsigned int *opal_irqs; static struct resource *opal_irqs;
void opal_handle_events(void) void opal_handle_events(void)
{ {
...@@ -165,24 +166,23 @@ void opal_event_shutdown(void) ...@@ -165,24 +166,23 @@ void opal_event_shutdown(void)
/* First free interrupts, which will also mask them */ /* First free interrupts, which will also mask them */
for (i = 0; i < opal_irq_count; i++) { for (i = 0; i < opal_irq_count; i++) {
if (!opal_irqs[i]) if (!opal_irqs || !opal_irqs[i].start)
continue; continue;
if (in_interrupt() || irqs_disabled()) if (in_interrupt() || irqs_disabled())
disable_irq_nosync(opal_irqs[i]); disable_irq_nosync(opal_irqs[i].start);
else else
free_irq(opal_irqs[i], NULL); free_irq(opal_irqs[i].start, NULL);
opal_irqs[i] = 0; opal_irqs[i].start = 0;
} }
} }
int __init opal_event_init(void) int __init opal_event_init(void)
{ {
struct device_node *dn, *opal_node; struct device_node *dn, *opal_node;
const char **names; bool old_style = false;
u32 *irqs; int i, rc = 0;
int i, rc;
opal_node = of_find_node_by_path("/ibm,opal"); opal_node = of_find_node_by_path("/ibm,opal");
if (!opal_node) { if (!opal_node) {
...@@ -207,67 +207,91 @@ int __init opal_event_init(void) ...@@ -207,67 +207,91 @@ int __init opal_event_init(void)
goto out; goto out;
} }
/* Get opal-interrupts property and names if present */ /* Look for new-style (standard) "interrupts" property */
rc = of_property_count_u32_elems(opal_node, "opal-interrupts"); opal_irq_count = of_irq_count(opal_node);
if (rc < 0)
goto out;
opal_irq_count = rc; /* Absent ? Look for the old one */
pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count); if (opal_irq_count < 1) {
/* Get opal-interrupts property and names if present */
rc = of_property_count_u32_elems(opal_node, "opal-interrupts");
if (rc > 0)
opal_irq_count = rc;
old_style = true;
}
irqs = kcalloc(opal_irq_count, sizeof(*irqs), GFP_KERNEL); /* No interrupts ? Bail out */
names = kcalloc(opal_irq_count, sizeof(*names), GFP_KERNEL); if (!opal_irq_count)
opal_irqs = kcalloc(opal_irq_count, sizeof(*opal_irqs), GFP_KERNEL); goto out;
if (WARN_ON(!irqs || !names || !opal_irqs)) pr_debug("OPAL: Found %d interrupts reserved for OPAL using %s scheme\n",
goto out_free; opal_irq_count, old_style ? "old" : "new");
rc = of_property_read_u32_array(opal_node, "opal-interrupts", /* Allocate an IRQ resources array */
irqs, opal_irq_count); opal_irqs = kcalloc(opal_irq_count, sizeof(struct resource), GFP_KERNEL);
if (rc < 0) { if (WARN_ON(!opal_irqs)) {
pr_err("Error %d reading opal-interrupts array\n", rc); rc = -ENOMEM;
goto out_free; goto out;
} }
/* It's not an error for the names to be missing */ /* Build the resources array */
of_property_read_string_array(opal_node, "opal-interrupts-names", if (old_style) {
names, opal_irq_count); /* Old style "opal-interrupts" property */
for (i = 0; i < opal_irq_count; i++) {
struct resource *r = &opal_irqs[i];
const char *name = NULL;
u32 hw_irq;
int virq;
rc = of_property_read_u32_index(opal_node, "opal-interrupts",
i, &hw_irq);
if (WARN_ON(rc < 0)) {
opal_irq_count = i;
break;
}
of_property_read_string_index(opal_node, "opal-interrupts-names",
i, &name);
virq = irq_create_mapping(NULL, hw_irq);
if (!virq) {
pr_warn("Failed to map OPAL irq 0x%x\n", hw_irq);
continue;
}
r->start = r->end = virq;
r->flags = IORESOURCE_IRQ | IRQ_TYPE_LEVEL_LOW;
r->name = name;
}
} else {
/* new style standard "interrupts" property */
rc = of_irq_to_resource_table(opal_node, opal_irqs, opal_irq_count);
if (WARN_ON(rc < 0)) {
opal_irq_count = 0;
kfree(opal_irqs);
goto out;
}
if (WARN_ON(rc < opal_irq_count))
opal_irq_count = rc;
}
/* Install interrupt handlers */ /* Install interrupt handlers */
for (i = 0; i < opal_irq_count; i++) { for (i = 0; i < opal_irq_count; i++) {
unsigned int virq; struct resource *r = &opal_irqs[i];
char *name; const char *name;
/* Get hardware and virtual IRQ */
virq = irq_create_mapping(NULL, irqs[i]);
if (!virq) {
pr_warn("Failed to map irq 0x%x\n", irqs[i]);
continue;
}
if (names[i] && strlen(names[i])) /* Prefix name */
name = kasprintf(GFP_KERNEL, "opal-%s", names[i]); if (r->name && strlen(r->name))
name = kasprintf(GFP_KERNEL, "opal-%s", r->name);
else else
name = kasprintf(GFP_KERNEL, "opal"); name = kasprintf(GFP_KERNEL, "opal");
/* Install interrupt handler */ /* Install interrupt handler */
rc = request_irq(virq, opal_interrupt, IRQF_TRIGGER_LOW, rc = request_irq(r->start, opal_interrupt, r->flags & IRQD_TRIGGER_MASK,
name, NULL); name, NULL);
if (rc) { if (rc) {
irq_dispose_mapping(virq); pr_warn("Error %d requesting OPAL irq %d\n", rc, (int)r->start);
pr_warn("Error %d requesting irq %d (0x%x)\n",
rc, virq, irqs[i]);
continue; continue;
} }
/* Cache IRQ */
opal_irqs[i] = virq;
} }
rc = 0;
out_free: out:
kfree(irqs);
kfree(names);
out:
of_node_put(opal_node); of_node_put(opal_node);
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment