Commit 1ffdc2aa authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: Fix RAS irq handlers

On pSeries systems, the firmware tells us a list of interrupt numbers
that we should enable in order to detect various error conditions.
When we get one of these interrupts we are supposed to call the
firmware, which will work out and tell us what the error was and
possibly also fix it.

We were not correctly parsing the property values that tell us which
interrupts need to be handled in this fashion.  This patch fixes it.
It exports prom_n_intr_cells from prom.c since that is needed to do
the parsing properly.
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b12db24d
...@@ -1881,8 +1881,7 @@ intr_parent(struct device_node *p) ...@@ -1881,8 +1881,7 @@ intr_parent(struct device_node *p)
* Find out the size of each entry of the interrupts property * Find out the size of each entry of the interrupts property
* for a node. * for a node.
*/ */
static int __devinit int __devinit prom_n_intr_cells(struct device_node *np)
prom_n_intr_cells(struct device_node *np)
{ {
struct device_node *p; struct device_node *p;
unsigned int *icp; unsigned int *icp;
......
...@@ -52,6 +52,16 @@ ...@@ -52,6 +52,16 @@
#include <asm/rtas.h> #include <asm/rtas.h>
#include <asm/ppcdebug.h> #include <asm/ppcdebug.h>
static unsigned char log_buf[RTAS_ERROR_LOG_MAX];
static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
static int ras_get_sensor_state_token;
static int ras_check_exception_token;
#define EPOW_SENSOR_TOKEN 9
#define EPOW_SENSOR_INDEX 0
#define RAS_VECTOR_OFFSET 0x500
static irqreturn_t ras_epow_interrupt(int irq, void *dev_id, static irqreturn_t ras_epow_interrupt(int irq, void *dev_id,
struct pt_regs * regs); struct pt_regs * regs);
static irqreturn_t ras_error_interrupt(int irq, void *dev_id, static irqreturn_t ras_error_interrupt(int irq, void *dev_id,
...@@ -59,59 +69,70 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id, ...@@ -59,59 +69,70 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id,
/* #define DEBUG */ /* #define DEBUG */
/* static void request_ras_irqs(struct device_node *np, char *propname,
* Initialize handlers for the set of interrupts caused by hardware errors irqreturn_t (*handler)(int, void *, struct pt_regs *),
* and power system events. const char *name)
*/
static int __init init_ras_IRQ(void)
{ {
struct device_node *np;
unsigned int *ireg, len, i; unsigned int *ireg, len, i;
int virq; int virq, n_intr;
ireg = (unsigned int *)get_property(np, propname, &len);
if (ireg == NULL)
return;
n_intr = prom_n_intr_cells(np);
len /= n_intr * sizeof(*ireg);
if ((np = of_find_node_by_path("/event-sources/internal-errors")) && for (i = 0; i < len; i++) {
(ireg = (unsigned int *)get_property(np, "open-pic-interrupt", virq = virt_irq_create_mapping(*ireg);
&len))) {
for (i=0; i<(len / sizeof(*ireg)); i++) {
virq = virt_irq_create_mapping(*(ireg));
if (virq == NO_IRQ) { if (virq == NO_IRQ) {
printk(KERN_ERR "Unable to allocate interrupt " printk(KERN_ERR "Unable to allocate interrupt "
"number for %s\n", np->full_name); "number for %s\n", np->full_name);
break; return;
} }
request_irq(irq_offset_up(virq), if (request_irq(irq_offset_up(virq), handler, 0, name, NULL)) {
ras_error_interrupt, 0, printk(KERN_ERR "Unable to request interrupt %d for "
"RAS_ERROR", NULL); "%s\n", irq_offset_up(virq), np->full_name);
ireg++; return;
} }
ireg += n_intr;
} }
of_node_put(np); }
if ((np = of_find_node_by_path("/event-sources/epow-events")) && /*
(ireg = (unsigned int *)get_property(np, "open-pic-interrupt", * Initialize handlers for the set of interrupts caused by hardware errors
&len))) { * and power system events.
for (i=0; i<(len / sizeof(*ireg)); i++) { */
virq = virt_irq_create_mapping(*(ireg)); static int __init init_ras_IRQ(void)
if (virq == NO_IRQ) { {
printk(KERN_ERR "Unable to allocate interrupt " struct device_node *np;
" number for %s\n", np->full_name);
break; ras_get_sensor_state_token = rtas_token("get-sensor-state");
} ras_check_exception_token = rtas_token("check-exception");
request_irq(irq_offset_up(virq),
ras_epow_interrupt, 0, /* Internal Errors */
"RAS_EPOW", NULL); np = of_find_node_by_path("/event-sources/internal-errors");
ireg++; if (np != NULL) {
} request_ras_irqs(np, "open-pic-interrupt", ras_error_interrupt,
"RAS_ERROR");
request_ras_irqs(np, "interrupts", ras_error_interrupt,
"RAS_ERROR");
of_node_put(np);
} }
/* EPOW Events */
np = of_find_node_by_path("/event-sources/epow-events");
if (np != NULL) {
request_ras_irqs(np, "open-pic-interrupt", ras_epow_interrupt,
"RAS_EPOW");
request_ras_irqs(np, "interrupts", ras_epow_interrupt,
"RAS_EPOW");
of_node_put(np); of_node_put(np);
}
return 1; return 1;
} }
__initcall(init_ras_IRQ); __initcall(init_ras_IRQ);
static struct rtas_error_log log_buf;
static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
/* /*
* Handle power subsystem events (EPOW). * Handle power subsystem events (EPOW).
* *
...@@ -122,30 +143,35 @@ static spinlock_t log_lock = SPIN_LOCK_UNLOCKED; ...@@ -122,30 +143,35 @@ static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
static irqreturn_t static irqreturn_t
ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs) ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{ {
struct rtas_error_log log_entry;
unsigned int size = sizeof(log_entry);
int status = 0xdeadbeef; int status = 0xdeadbeef;
int state = 0;
int critical;
spin_lock(&log_lock); status = rtas_call(ras_get_sensor_state_token, 2, 2, &state,
EPOW_SENSOR_TOKEN, EPOW_SENSOR_INDEX);
status = rtas_call(rtas_token("check-exception"), 6, 1, NULL, if (state > 3)
0x500, irq, critical = 1; /* Time Critical */
RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS, else
1, /* Time Critical */ critical = 0;
__pa(&log_buf), size);
log_entry = log_buf; spin_lock(&log_lock);
spin_unlock(&log_lock); status = rtas_call(ras_check_exception_token, 6, 1, NULL,
RAS_VECTOR_OFFSET,
virt_irq_to_real(irq_offset_down(irq)),
RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS,
critical, __pa(&log_buf), RTAS_ERROR_LOG_MAX);
udbg_printf("EPOW <0x%lx 0x%x>\n", udbg_printf("EPOW <0x%lx 0x%x 0x%x>\n",
*((unsigned long *)&log_entry), status); *((unsigned long *)&log_buf), status, state);
printk(KERN_WARNING printk(KERN_WARNING "EPOW <0x%lx 0x%x 0x%x>\n",
"EPOW <0x%lx 0x%x>\n",*((unsigned long *)&log_entry), status); *((unsigned long *)&log_buf), status, state);
/* format and print the extended information */ /* format and print the extended information */
log_error((char *)&log_entry, ERR_TYPE_RTAS_LOG, 0); log_error(log_buf, ERR_TYPE_RTAS_LOG, 0);
spin_unlock(&log_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -160,37 +186,33 @@ ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -160,37 +186,33 @@ ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs)
static irqreturn_t static irqreturn_t
ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs) ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{ {
struct rtas_error_log log_entry; struct rtas_error_log *rtas_elog;
unsigned int size = sizeof(log_entry);
int status = 0xdeadbeef; int status = 0xdeadbeef;
int fatal; int fatal;
spin_lock(&log_lock); spin_lock(&log_lock);
status = rtas_call(rtas_token("check-exception"), 6, 1, NULL, status = rtas_call(ras_check_exception_token, 6, 1, NULL,
0x500, irq, RAS_VECTOR_OFFSET,
RTAS_INTERNAL_ERROR, virt_irq_to_real(irq_offset_down(irq)),
1, /* Time Critical */ RTAS_INTERNAL_ERROR, 1 /*Time Critical */,
__pa(&log_buf), size); __pa(&log_buf), RTAS_ERROR_LOG_MAX);
log_entry = log_buf;
spin_unlock(&log_lock); rtas_elog = (struct rtas_error_log *)log_buf;
if ((status == 0) && (log_entry.severity >= SEVERITY_ERROR_SYNC)) if ((status == 0) && (rtas_elog->severity >= SEVERITY_ERROR_SYNC))
fatal = 1; fatal = 1;
else else
fatal = 0; fatal = 0;
/* format and print the extended information */ /* format and print the extended information */
log_error((char *)&log_entry, ERR_TYPE_RTAS_LOG, fatal); log_error(log_buf, ERR_TYPE_RTAS_LOG, fatal);
if (fatal) { if (fatal) {
udbg_printf("HW Error <0x%lx 0x%x>\n", udbg_printf("Fatal HW Error <0x%lx 0x%x>\n",
*((unsigned long *)&log_entry), status); *((unsigned long *)&log_buf), status);
printk(KERN_EMERG printk(KERN_EMERG "Error: Fatal hardware error <0x%lx 0x%x>\n",
"Error: Fatal hardware error <0x%lx 0x%x>\n", *((unsigned long *)&log_buf), status);
*((unsigned long *)&log_entry), status);
#ifndef DEBUG #ifndef DEBUG
/* Don't actually power off when debugging so we can test /* Don't actually power off when debugging so we can test
...@@ -201,10 +223,12 @@ ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -201,10 +223,12 @@ ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs)
#endif #endif
} else { } else {
udbg_printf("Recoverable HW Error <0x%lx 0x%x>\n", udbg_printf("Recoverable HW Error <0x%lx 0x%x>\n",
*((unsigned long *)&log_entry), status); *((unsigned long *)&log_buf), status);
printk(KERN_WARNING printk(KERN_WARNING
"Warning: Recoverable hardware error <0x%lx 0x%x>\n", "Warning: Recoverable hardware error <0x%lx 0x%x>\n",
*((unsigned long *)&log_entry), status); *((unsigned long *)&log_buf), status);
} }
spin_unlock(&log_lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -269,6 +269,7 @@ extern unsigned char *get_property(struct device_node *node, const char *name, ...@@ -269,6 +269,7 @@ extern unsigned char *get_property(struct device_node *node, const char *name,
extern void print_properties(struct device_node *node); extern void print_properties(struct device_node *node);
extern int prom_n_addr_cells(struct device_node* np); extern int prom_n_addr_cells(struct device_node* np);
extern int prom_n_size_cells(struct device_node* np); extern int prom_n_size_cells(struct device_node* np);
extern int prom_n_intr_cells(struct device_node* np);
extern void prom_get_irq_senses(unsigned char *senses, int off, int max); extern void prom_get_irq_senses(unsigned char *senses, int off, int max);
extern void prom_add_property(struct device_node* np, struct property* prop); extern void prom_add_property(struct device_node* np, struct property* prop);
......
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