summaryrefslogtreecommitdiffstats
path: root/arch/alpha/kernel/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/kernel/irq.c')
-rw-r--r--arch/alpha/kernel/irq.c1076
1 files changed, 315 insertions, 761 deletions
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
index 613a633ba..bc8ca101a 100644
--- a/arch/alpha/kernel/irq.c
+++ b/arch/alpha/kernel/irq.c
@@ -17,304 +17,85 @@
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
+#include <linux/interrupt.h>
#include <linux/malloc.h>
#include <linux/random.h>
#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/proc_fs.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/dma.h>
#include <asm/bitops.h>
-#include <asm/machvec.h>
-
-#include "proto.h"
-#include "irq_impl.h"
-
-#define vulp volatile unsigned long *
-#define vuip volatile unsigned int *
-
-/* Only uniprocessor needs this IRQ/BH locking depth, on SMP it lives
- in the per-cpu structure for cache reasons. */
-#ifndef CONFIG_SMP
-int __local_irq_count;
-int __local_bh_count;
-unsigned long __irq_attempt[NR_IRQS];
-#endif
-
-#ifdef CONFIG_ALPHA_GENERIC
-#define ACTUAL_NR_IRQS alpha_mv.nr_irqs
-#else
-#define ACTUAL_NR_IRQS NR_IRQS
-#endif
-
-/* Hack minimum IPL during interupt processing for broken hardware. */
-
-#ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK
-int __min_ipl;
-#endif
+#include <asm/uaccess.h>
/*
- * Performance counter hook. A module can override this to
- * do something useful.
+ * Controller mappings for all interrupt sources:
*/
+irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
+ [0 ... NR_IRQS-1] = { 0, &no_irq_type, NULL, 0, SPIN_LOCK_UNLOCKED}
+};
-static void
-dummy_perf(unsigned long vector, struct pt_regs *regs)
-{
- printk(KERN_CRIT "Performance counter interrupt!\n");
-}
-
-void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf;
-
-/*
- * Dispatch device interrupts.
- */
-
-/*
- * Handle ISA interrupt via the PICs.
- */
-
-#if defined(CONFIG_ALPHA_GENERIC)
-# define IACK_SC alpha_mv.iack_sc
-#elif defined(CONFIG_ALPHA_APECS)
-# define IACK_SC APECS_IACK_SC
-#elif defined(CONFIG_ALPHA_LCA)
-# define IACK_SC LCA_IACK_SC
-#elif defined(CONFIG_ALPHA_CIA)
-# define IACK_SC CIA_IACK_SC
-#elif defined(CONFIG_ALPHA_PYXIS)
-# define IACK_SC PYXIS_IACK_SC
-#elif defined(CONFIG_ALPHA_TSUNAMI)
-# define IACK_SC TSUNAMI_IACK_SC
-#elif defined(CONFIG_ALPHA_POLARIS)
-# define IACK_SC POLARIS_IACK_SC
-#elif defined(CONFIG_ALPHA_IRONGATE)
-# define IACK_SC IRONGATE_IACK_SC
-#endif
-
-#if defined(IACK_SC)
-void
-isa_device_interrupt(unsigned long vector, struct pt_regs *regs)
-{
- /*
- * Generate a PCI interrupt acknowledge cycle. The PIC will
- * respond with the interrupt vector of the highest priority
- * interrupt that is pending. The PALcode sets up the
- * interrupts vectors such that irq level L generates vector L.
- */
- int j = *(vuip) IACK_SC;
- j &= 0xff;
- if (j == 7) {
- if (!(inb(0x20) & 0x80)) {
- /* It's only a passive release... */
- return;
- }
- }
- handle_irq(j, regs);
-}
-#endif
-#if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC)
-void
-isa_no_iack_sc_device_interrupt(unsigned long vector, struct pt_regs *regs)
-{
- unsigned long pic;
-
- /*
- * It seems to me that the probability of two or more *device*
- * interrupts occurring at almost exactly the same time is
- * pretty low. So why pay the price of checking for
- * additional interrupts here if the common case can be
- * handled so much easier?
- */
- /*
- * The first read of gives you *all* interrupting lines.
- * Therefore, read the mask register and and out those lines
- * not enabled. Note that some documentation has 21 and a1
- * write only. This is not true.
- */
- pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */
- pic &= 0xFFFB; /* mask out cascade & hibits */
+static void register_irq_proc(unsigned int irq);
- while (pic) {
- int j = ffz(~pic);
- pic &= pic - 1;
- handle_irq(j, regs);
- }
-}
-#endif
+unsigned long irq_err_count;
/*
- * Handle interrupts from the SRM, assuming no additional weirdness.
+ * Special irq handlers.
*/
-static inline void
-srm_enable_irq(unsigned int irq)
-{
- cserve_ena(irq - 16);
-}
-
-static void
-srm_disable_irq(unsigned int irq)
-{
- cserve_dis(irq - 16);
-}
-
-static unsigned int
-srm_startup_irq(unsigned int irq)
-{
- srm_enable_irq(irq);
- return 0;
-}
-
-static struct hw_interrupt_type srm_irq_type = {
- typename: "SRM",
- startup: srm_startup_irq,
- shutdown: srm_disable_irq,
- enable: srm_enable_irq,
- disable: srm_disable_irq,
- ack: srm_disable_irq,
- end: srm_enable_irq,
-};
-
-void
-srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
-{
- int irq = (vector - 0x800) >> 4;
- handle_irq(irq, regs);
-}
-
-void __init
-init_srm_irqs(long max, unsigned long ignore_mask)
-{
- long i;
-
- for (i = 16; i < max; ++i) {
- if (i < 64 && ((ignore_mask >> i) & 1))
- continue;
- irq_desc[i].status = IRQ_DISABLED;
- irq_desc[i].handler = &srm_irq_type;
- }
-}
+void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
/*
- * The not-handled irq handler.
+ * Generic no controller code
*/
-static void
-noirq_enable_disable(unsigned int irq)
-{
-}
-
-static unsigned int
-noirq_startup(unsigned int irq)
-{
- return 0;
-}
+static void no_irq_enable_disable(unsigned int irq) { }
+static unsigned int no_irq_startup(unsigned int irq) { return 0; }
static void
-noirq_ack(unsigned int irq)
+no_irq_ack(unsigned int irq)
{
- printk(KERN_CRIT "Unexpected IRQ %u\n", irq);
+ irq_err_count++;
+ printk(KERN_CRIT "Unexpected IRQ trap at vector %u\n", irq);
}
-static struct hw_interrupt_type no_irq_type = {
+struct hw_interrupt_type no_irq_type = {
typename: "none",
- startup: noirq_startup,
- shutdown: noirq_enable_disable,
- enable: noirq_enable_disable,
- disable: noirq_enable_disable,
- ack: noirq_ack,
- end: noirq_enable_disable,
-};
-
-/*
- * The special RTC interrupt type. The interrupt itself was
- * processed by PALcode, and comes in via entInt vector 1.
- */
-
-static struct hw_interrupt_type rtc_irq_type = {
- typename: "RTC",
- startup: noirq_startup,
- shutdown: noirq_enable_disable,
- enable: noirq_enable_disable,
- disable: noirq_enable_disable,
- ack: noirq_enable_disable,
- end: noirq_enable_disable,
-};
-
-void __init
-init_rtc_irq(void)
-{
- irq_desc[RTC_IRQ].status = IRQ_DISABLED;
- irq_desc[RTC_IRQ].handler = &rtc_irq_type;
-}
-
-/*
- * Special irq handlers.
- */
-
-void
-no_action(int cpl, void *dev_id, struct pt_regs *regs)
-{
-}
-
-/*
- * Common irq handlers.
- */
-
-struct irqaction isa_cascade_irqaction = {
- handler: no_action,
- name: "isa-cascade"
-};
-
-struct irqaction timer_cascade_irqaction = {
- handler: no_action,
- name: "timer-cascade"
-};
-
-struct irqaction halt_switch_irqaction = {
- handler: no_action,
- name: "halt-switch"
-};
-
-
-spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;
-irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
- [0 ... NR_IRQS-1] = { 0, &no_irq_type, }
+ startup: no_irq_startup,
+ shutdown: no_irq_enable_disable,
+ enable: no_irq_enable_disable,
+ disable: no_irq_enable_disable,
+ ack: no_irq_ack,
+ end: no_irq_enable_disable,
};
int
handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
struct irqaction *action)
{
- int status, cpu = smp_processor_id();
- int old_ipl, ipl;
+ int status;
+ int cpu = smp_processor_id();
kstat.irqs[cpu][irq]++;
irq_enter(cpu, irq);
status = 1; /* Force the "do bottom halves" bit */
- old_ipl = ipl = getipl();
do {
- int new_ipl = IPL_MIN;
- if (action->flags & SA_INTERRUPT)
- new_ipl = IPL_MAX;
- if (new_ipl != ipl) {
- setipl(new_ipl);
- ipl = new_ipl;
- }
+ if (!(action->flags & SA_INTERRUPT))
+ __sti();
+ else
+ __cli();
status |= action->flags;
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
- if (ipl != old_ipl)
- setipl(old_ipl);
-
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
+ __cli();
+
irq_exit(cpu, irq);
return status;
@@ -326,17 +107,18 @@ handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
* hardware disable after having gotten the irq
* controller lock.
*/
-void
+void inline
disable_irq_nosync(unsigned int irq)
{
+ irq_desc_t *desc = irq_desc + irq;
unsigned long flags;
- spin_lock_irqsave(&irq_controller_lock, flags);
- if (!irq_desc[irq].depth++) {
- irq_desc[irq].status |= IRQ_DISABLED | IRQ_MASKED;
- irq_desc[irq].handler->disable(irq);
+ spin_lock_irqsave(&desc->lock, flags);
+ if (!desc->depth++) {
+ desc->status |= IRQ_DISABLED;
+ desc->handler->disable(irq);
}
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ spin_unlock_irqrestore(&desc->lock, flags);
}
/*
@@ -358,32 +140,29 @@ disable_irq(unsigned int irq)
void
enable_irq(unsigned int irq)
{
+ irq_desc_t *desc = irq_desc + irq;
unsigned long flags;
- spin_lock_irqsave(&irq_controller_lock, flags);
- switch (irq_desc[irq].depth) {
- case 1:
- {
- unsigned int status = irq_desc[irq].status;
-
- status &= ~(IRQ_DISABLED | IRQ_MASKED);
+ spin_lock_irqsave(&desc->lock, flags);
+ switch (desc->depth) {
+ case 1: {
+ unsigned int status = desc->status & ~IRQ_DISABLED;
+ desc->status = status;
if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
- status |= IRQ_REPLAY;
- /* ??? We can't re-send on (most?) alpha hw.
- hw_resend_irq(irq_desc[irq].handler,irq); */
+ desc->status = status | IRQ_REPLAY;
+ hw_resend_irq(desc->handler,irq);
}
- irq_desc[irq].status = status;
- irq_desc[irq].handler->enable(irq);
+ desc->handler->enable(irq);
/* fall-through */
- }
+ }
default:
- irq_desc[irq].depth--;
+ desc->depth--;
break;
case 0:
printk(KERN_ERR "enable_irq() unbalanced from %p\n",
__builtin_return_address(0));
}
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ spin_unlock_irqrestore(&desc->lock, flags);
}
int
@@ -392,6 +171,7 @@ setup_irq(unsigned int irq, struct irqaction * new)
int shared = 0;
struct irqaction *old, **p;
unsigned long flags;
+ irq_desc_t *desc = irq_desc + irq;
/*
* Some drivers like serial.c use request_irq() heavily,
@@ -413,12 +193,12 @@ setup_irq(unsigned int irq, struct irqaction * new)
/*
* The following block of code has to be executed atomically
*/
- spin_lock_irqsave(&irq_controller_lock,flags);
- p = &irq_desc[irq].action;
+ spin_lock_irqsave(&desc->lock,flags);
+ p = &desc->action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
- spin_unlock_irqrestore(&irq_controller_lock,flags);
+ spin_unlock_irqrestore(&desc->lock,flags);
return -EBUSY;
}
@@ -433,14 +213,181 @@ setup_irq(unsigned int irq, struct irqaction * new)
*p = new;
if (!shared) {
- irq_desc[irq].depth = 0;
- irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_MASKED);
- irq_desc[irq].handler->startup(irq);
+ desc->depth = 0;
+ desc->status &= ~IRQ_DISABLED;
+ desc->handler->startup(irq);
}
- spin_unlock_irqrestore(&irq_controller_lock,flags);
+ spin_unlock_irqrestore(&desc->lock,flags);
+
+ register_irq_proc(irq);
return 0;
}
+static struct proc_dir_entry * root_irq_dir;
+static struct proc_dir_entry * irq_dir [NR_IRQS];
+static struct proc_dir_entry * smp_affinity_entry [NR_IRQS];
+
+static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
+
+#define HEX_DIGITS 16
+
+static int
+irq_affinity_read_proc (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ if (count < HEX_DIGITS+1)
+ return -EINVAL;
+ return sprintf (page, "%016lx\n", irq_affinity[(long)data]);
+}
+
+static unsigned int
+parse_hex_value (const char *buffer,
+ unsigned long count, unsigned long *ret)
+{
+ unsigned char hexnum [HEX_DIGITS];
+ unsigned long value;
+ int i;
+
+ if (!count)
+ return -EINVAL;
+ if (count > HEX_DIGITS)
+ count = HEX_DIGITS;
+ if (copy_from_user(hexnum, buffer, count))
+ return -EFAULT;
+
+ /*
+ * Parse the first 8 characters as a hex string, any non-hex char
+ * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
+ */
+ value = 0;
+
+ for (i = 0; i < count; i++) {
+ unsigned int c = hexnum[i];
+
+ switch (c) {
+ case '0' ... '9': c -= '0'; break;
+ case 'a' ... 'f': c -= 'a'-10; break;
+ case 'A' ... 'F': c -= 'A'-10; break;
+ default:
+ goto out;
+ }
+ value = (value << 4) | c;
+ }
+out:
+ *ret = value;
+ return 0;
+}
+
+static int
+irq_affinity_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ int irq = (long) data, full_count = count, err;
+ unsigned long new_value;
+
+ if (!irq_desc[irq].handler->set_affinity)
+ return -EIO;
+
+ err = parse_hex_value(buffer, count, &new_value);
+
+#if CONFIG_SMP
+ /*
+ * Do not allow disabling IRQs completely - it's a too easy
+ * way to make the system unusable accidentally :-) At least
+ * one online CPU still has to be targeted.
+ */
+ if (!(new_value & cpu_present_mask))
+ return -EINVAL;
+#endif
+
+ irq_affinity[irq] = new_value;
+ irq_desc[irq].handler->set_affinity(irq, new_value);
+
+ return full_count;
+}
+
+static int
+prof_cpu_mask_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned long *mask = (unsigned long *) data;
+ if (count < HEX_DIGITS+1)
+ return -EINVAL;
+ return sprintf (page, "%08lx\n", *mask);
+}
+
+static int
+prof_cpu_mask_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ unsigned long *mask = (unsigned long *) data, full_count = count, err;
+ unsigned long new_value;
+
+ err = parse_hex_value(buffer, count, &new_value);
+ if (err)
+ return err;
+
+ *mask = new_value;
+ return full_count;
+}
+
+#define MAX_NAMELEN 10
+
+static void
+register_irq_proc (unsigned int irq)
+{
+ struct proc_dir_entry *entry;
+ char name [MAX_NAMELEN];
+
+ if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type))
+ return;
+
+ memset(name, 0, MAX_NAMELEN);
+ sprintf(name, "%d", irq);
+
+ /* create /proc/irq/1234 */
+ irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+
+ /* create /proc/irq/1234/smp_affinity */
+ entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]);
+
+ entry->nlink = 1;
+ entry->data = (void *)(long)irq;
+ entry->read_proc = irq_affinity_read_proc;
+ entry->write_proc = irq_affinity_write_proc;
+
+ smp_affinity_entry[irq] = entry;
+}
+
+unsigned long prof_cpu_mask = ~0UL;
+
+void
+init_irq_proc (void)
+{
+ struct proc_dir_entry *entry;
+ int i;
+
+ /* create /proc/irq */
+ root_irq_dir = proc_mkdir("irq", 0);
+
+ /* create /proc/irq/prof_cpu_mask */
+ entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir);
+
+ entry->nlink = 1;
+ entry->data = (void *)&prof_cpu_mask;
+ entry->read_proc = prof_cpu_mask_read_proc;
+ entry->write_proc = prof_cpu_mask_write_proc;
+
+ /*
+ * Create entries for all existing IRQs.
+ */
+ for (i = 0; i < NR_IRQS; i++) {
+ if (irq_desc[i].handler == &no_irq_type)
+ continue;
+ register_irq_proc(i);
+ }
+}
+
int
request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char * devname, void *dev_id)
@@ -488,6 +435,7 @@ request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
void
free_irq(unsigned int irq, void *dev_id)
{
+ irq_desc_t *desc;
struct irqaction **p;
unsigned long flags;
@@ -496,8 +444,9 @@ free_irq(unsigned int irq, void *dev_id)
return;
}
- spin_lock_irqsave(&irq_controller_lock,flags);
- p = &irq_desc[irq].action;
+ desc = irq_desc + irq;
+ spin_lock_irqsave(&desc->lock,flags);
+ p = &desc->action;
for (;;) {
struct irqaction * action = *p;
if (action) {
@@ -508,26 +457,29 @@ free_irq(unsigned int irq, void *dev_id)
/* Found - now remove it from the list of entries. */
*pp = action->next;
- if (!irq_desc[irq].action) {
- irq_desc[irq].status |= IRQ_DISABLED|IRQ_MASKED;
- irq_desc[irq].handler->shutdown(irq);
+ if (!desc->action) {
+ desc->status |= IRQ_DISABLED;
+ desc->handler->shutdown(irq);
}
- spin_unlock_irqrestore(&irq_controller_lock,flags);
+ spin_unlock_irqrestore(&desc->lock,flags);
+#ifdef CONFIG_SMP
/* Wait to make sure it's not being used on
another CPU. */
- while (irq_desc[irq].status & IRQ_INPROGRESS)
+ while (desc->status & IRQ_INPROGRESS)
barrier();
+#endif
kfree(action);
return;
}
printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
- spin_unlock_irqrestore(&irq_controller_lock,flags);
+ spin_unlock_irqrestore(&desc->lock,flags);
return;
}
}
-int get_irq_list(char *buf)
+int
+get_irq_list(char *buf)
{
int i, j;
struct irqaction * action;
@@ -576,246 +528,10 @@ int get_irq_list(char *buf)
cpu_data[cpu_logical_map(j)].smp_local_irq_count);
p += sprintf(p, "\n");
#endif
+ p += sprintf(p, "ERR: %10lu\n", irq_err_count);
return p - buf;
}
-#ifdef CONFIG_SMP
-/* Who has global_irq_lock. */
-int global_irq_holder = NO_PROC_ID;
-
-/* This protects IRQ's. */
-spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
-
-/* Global IRQ locking depth. */
-atomic_t global_irq_count = ATOMIC_INIT(0);
-
-static void *previous_irqholder = NULL;
-
-#define MAXCOUNT 100000000
-
-static void show(char * str, void *where);
-
-static inline void
-wait_on_irq(int cpu, void *where)
-{
- int count = MAXCOUNT;
-
- for (;;) {
-
- /*
- * Wait until all interrupts are gone. Wait
- * for bottom half handlers unless we're
- * already executing in one..
- */
- if (!atomic_read(&global_irq_count)) {
- if (local_bh_count(cpu)
- || !spin_is_locked(&global_bh_lock))
- break;
- }
-
- /* Duh, we have to loop. Release the lock to avoid deadlocks */
- spin_unlock(&global_irq_lock);
-
- for (;;) {
- if (!--count) {
- show("wait_on_irq", where);
- count = MAXCOUNT;
- }
- __sti();
- udelay(1); /* make sure to run pending irqs */
- __cli();
-
- if (atomic_read(&global_irq_count))
- continue;
- if (spin_is_locked(&global_irq_lock))
- continue;
- if (!local_bh_count(cpu)
- && spin_is_locked(&global_bh_lock))
- continue;
- if (spin_trylock(&global_irq_lock))
- break;
- }
- }
-}
-
-static inline void
-get_irqlock(int cpu, void* where)
-{
- if (!spin_trylock(&global_irq_lock)) {
- /* Do we already hold the lock? */
- if (cpu == global_irq_holder)
- return;
- /* Uhhuh.. Somebody else got it. Wait. */
- spin_lock(&global_irq_lock);
- }
-
- /*
- * Ok, we got the lock bit.
- * But that's actually just the easy part.. Now
- * we need to make sure that nobody else is running
- * in an interrupt context.
- */
- wait_on_irq(cpu, where);
-
- /*
- * Finally.
- */
-#if DEBUG_SPINLOCK
- global_irq_lock.task = current;
- global_irq_lock.previous = where;
-#endif
- global_irq_holder = cpu;
- previous_irqholder = where;
-}
-
-void
-__global_cli(void)
-{
- int cpu = smp_processor_id();
- void *where = __builtin_return_address(0);
-
- /*
- * Maximize ipl. If ipl was previously 0 and if this thread
- * is not in an irq, then take global_irq_lock.
- */
- if (swpipl(IPL_MAX) == IPL_MIN && !local_irq_count(cpu))
- get_irqlock(cpu, where);
-}
-
-void
-__global_sti(void)
-{
- int cpu = smp_processor_id();
-
- if (!local_irq_count(cpu))
- release_irqlock(cpu);
- __sti();
-}
-
-/*
- * SMP flags value to restore to:
- * 0 - global cli
- * 1 - global sti
- * 2 - local cli
- * 3 - local sti
- */
-unsigned long
-__global_save_flags(void)
-{
- int retval;
- int local_enabled;
- unsigned long flags;
- int cpu = smp_processor_id();
-
- __save_flags(flags);
- local_enabled = (!(flags & 7));
- /* default to local */
- retval = 2 + local_enabled;
-
- /* Check for global flags if we're not in an interrupt. */
- if (!local_irq_count(cpu)) {
- if (local_enabled)
- retval = 1;
- if (global_irq_holder == cpu)
- retval = 0;
- }
- return retval;
-}
-
-void
-__global_restore_flags(unsigned long flags)
-{
- switch (flags) {
- case 0:
- __global_cli();
- break;
- case 1:
- __global_sti();
- break;
- case 2:
- __cli();
- break;
- case 3:
- __sti();
- break;
- default:
- printk(KERN_ERR "global_restore_flags: %08lx (%p)\n",
- flags, __builtin_return_address(0));
- }
-}
-
-static void
-show(char * str, void *where)
-{
-#if 0
- int i;
- unsigned long *stack;
-#endif
- int cpu = smp_processor_id();
-
- printk("\n%s, CPU %d: %p\n", str, cpu, where);
- printk("irq: %d [%d %d]\n",
- atomic_read(&global_irq_count),
- cpu_data[0].irq_count,
- cpu_data[1].irq_count);
-
- printk("bh: %d [%d %d]\n",
- spin_is_locked(&global_bh_lock) ? 1 : 0,
- cpu_data[0].bh_count,
- cpu_data[1].bh_count);
-#if 0
- stack = (unsigned long *) &str;
- for (i = 40; i ; i--) {
- unsigned long x = *++stack;
- if (x > (unsigned long) &init_task_union &&
- x < (unsigned long) &vsprintf) {
- printk("<[%08lx]> ", x);
- }
- }
-#endif
-}
-
-/*
- * From its use, I infer that synchronize_irq() stalls a thread until
- * the effects of a command to an external device are known to have
- * taken hold. Typically, the command is to stop sending interrupts.
- * The strategy here is wait until there is at most one processor
- * (this one) in an irq. The memory barrier serializes the write to
- * the device and the subsequent accesses of global_irq_count.
- * --jmartin
- */
-#define DEBUG_SYNCHRONIZE_IRQ 0
-
-void
-synchronize_irq(void)
-{
-#if 0
- /* Joe's version. */
- int cpu = smp_processor_id();
- int local_count;
- int global_count;
- int countdown = 1<<24;
- void *where = __builtin_return_address(0);
-
- mb();
- do {
- local_count = local_irq_count(cpu);
- global_count = atomic_read(&global_irq_count);
- if (DEBUG_SYNCHRONIZE_IRQ && (--countdown == 0)) {
- printk("%d:%d/%d\n", cpu, local_count, global_count);
- show("synchronize_irq", where);
- break;
- }
- } while (global_count != local_count);
-#else
- /* Jay's version. */
- if (atomic_read(&global_irq_count)) {
- cli();
- sti();
- }
-#endif
-}
-#endif /* CONFIG_SMP */
/*
* do_IRQ handles all normal device IRQ's (the special
@@ -836,39 +552,26 @@ handle_irq(int irq, struct pt_regs * regs)
* handled by some other CPU. (or is disabled)
*/
int cpu = smp_processor_id();
- irq_desc_t *desc;
+ irq_desc_t *desc = irq_desc + irq;
struct irqaction * action;
unsigned int status;
if ((unsigned) irq > ACTUAL_NR_IRQS) {
+ irq_err_count++;
printk(KERN_CRIT "device_interrupt: illegal interrupt %d\n",
irq);
return;
}
irq_attempt(cpu, irq)++;
- desc = irq_desc + irq;
- spin_lock_irq(&irq_controller_lock); /* mask also the RTC */
+ spin_lock_irq(&desc->lock); /* mask also the higher prio events */
desc->handler->ack(irq);
- status = desc->status;
-
-#ifndef CONFIG_SMP
- /* Look for broken irq masking. */
- if (status & IRQ_MASKED) {
- static unsigned long last_printed;
- if (time_after(jiffies, last_printed+HZ)) {
- printk(KERN_CRIT "Mask didn't work for irq %d!\n", irq);
- last_printed = jiffies;
- }
- }
-#endif
-
/*
* REPLAY is when Linux resends an IRQ that was dropped earlier.
* WAITING is used by probe to mark irqs that are being tested.
*/
- status &= ~(IRQ_REPLAY | IRQ_WAITING);
- status |= IRQ_PENDING | IRQ_MASKED; /* we _want_ to handle it */
+ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
+ status |= IRQ_PENDING; /* we _want_ to handle it */
/*
* If the IRQ is disabled for whatever reason, we cannot
@@ -881,7 +584,6 @@ handle_irq(int irq, struct pt_regs * regs)
status |= IRQ_INPROGRESS; /* we are handling it */
}
desc->status = status;
- spin_unlock(&irq_controller_lock);
/*
* If there is no IRQ handler or it was disabled, exit early.
@@ -890,7 +592,7 @@ handle_irq(int irq, struct pt_regs * regs)
* will take care of it.
*/
if (!action)
- return;
+ goto out;
/*
* Edge triggered interrupts need to remember pending events.
@@ -902,22 +604,23 @@ handle_irq(int irq, struct pt_regs * regs)
* SMP environment.
*/
for (;;) {
+ spin_unlock(&desc->lock);
handle_IRQ_event(irq, regs, action);
- spin_lock(&irq_controller_lock);
+ spin_lock(&desc->lock);
if (!(desc->status & IRQ_PENDING)
|| (desc->status & IRQ_LEVEL))
break;
desc->status &= ~IRQ_PENDING;
- spin_unlock(&irq_controller_lock);
- }
- status = desc->status & ~IRQ_INPROGRESS;
- if (!(status & IRQ_DISABLED)) {
- status &= ~IRQ_MASKED;
- desc->handler->end(irq);
}
- desc->status = status;
- spin_unlock(&irq_controller_lock);
+ desc->status &= ~IRQ_INPROGRESS;
+out:
+ /*
+ * The ->end() handler has to deal with interrupts which got
+ * disabled while the handler was running.
+ */
+ desc->handler->end(irq);
+ spin_unlock(&desc->lock);
}
/*
@@ -932,17 +635,20 @@ unsigned long
probe_irq_on(void)
{
int i;
+ irq_desc_t *desc;
unsigned long delay;
unsigned long val;
/* Something may have generated an irq long ago and we want to
flush such a longstanding irq before considering it as spurious. */
- spin_lock_irq(&irq_controller_lock);
- for (i = NR_IRQS-1; i >= 0; i--)
+ for (i = NR_IRQS-1; i >= 0; i--) {
+ desc = irq_desc + i;
+
+ spin_lock_irq(&desc->lock);
if (!irq_desc[i].action)
- if(irq_desc[i].handler->startup(i))
- irq_desc[i].status |= IRQ_PENDING;
- spin_unlock_irq(&irq_controller_lock);
+ irq_desc[i].handler->startup(i);
+ spin_unlock_irq(&desc->lock);
+ }
/* Wait for longstanding interrupts to trigger. */
for (delay = jiffies + HZ/50; time_after(delay, jiffies); )
@@ -951,15 +657,17 @@ probe_irq_on(void)
/* enable any unassigned irqs (we must startup again here because
if a longstanding irq happened in the previous stage, it may have
masked itself) first, enable any unassigned irqs. */
- spin_lock_irq(&irq_controller_lock);
for (i = NR_IRQS-1; i >= 0; i--) {
- if (!irq_desc[i].action) {
- irq_desc[i].status |= IRQ_AUTODETECT | IRQ_WAITING;
- if(irq_desc[i].handler->startup(i))
- irq_desc[i].status |= IRQ_PENDING;
+ desc = irq_desc + i;
+
+ spin_lock_irq(&desc->lock);
+ if (!desc->action) {
+ desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
+ if (desc->handler->startup(i))
+ desc->status |= IRQ_PENDING;
}
+ spin_unlock_irq(&desc->lock);
}
- spin_unlock_irq(&irq_controller_lock);
/*
* Wait for spurious interrupts to trigger
@@ -971,24 +679,24 @@ probe_irq_on(void)
* Now filter out any obviously spurious interrupts
*/
val = 0;
- spin_lock_irq(&irq_controller_lock);
for (i=0; i<NR_IRQS; i++) {
- unsigned int status = irq_desc[i].status;
-
- if (!(status & IRQ_AUTODETECT))
- continue;
-
- /* It triggered already - consider it spurious. */
- if (!(status & IRQ_WAITING)) {
- irq_desc[i].status = status & ~IRQ_AUTODETECT;
- irq_desc[i].handler->shutdown(i);
- continue;
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
+
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
+
+ if (status & IRQ_AUTODETECT) {
+ /* It triggered already - consider it spurious. */
+ if (!(status & IRQ_WAITING)) {
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ } else
+ if (i < 32)
+ val |= 1 << i;
}
-
- if (i < 64)
- val |= 1 << i;
+ spin_unlock_irq(&desc->lock);
}
- spin_unlock_irq(&irq_controller_lock);
return val;
}
@@ -997,26 +705,29 @@ probe_irq_on(void)
* Return a mask of triggered interrupts (this
* can handle only legacy ISA interrupts).
*/
-unsigned int probe_irq_mask(unsigned long val)
+unsigned int
+probe_irq_mask(unsigned long val)
{
int i;
unsigned int mask;
mask = 0;
- spin_lock_irq(&irq_controller_lock);
for (i = 0; i < 16; i++) {
- unsigned int status = irq_desc[i].status;
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
- if (!(status & IRQ_AUTODETECT))
- continue;
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
- if (!(status & IRQ_WAITING))
- mask |= 1 << i;
+ if (status & IRQ_AUTODETECT) {
+ if (!(status & IRQ_WAITING))
+ mask |= 1 << i;
- irq_desc[i].status = status & ~IRQ_AUTODETECT;
- irq_desc[i].handler->shutdown(i);
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ }
+ spin_unlock_irq(&desc->lock);
}
- spin_unlock_irq(&irq_controller_lock);
return mask & val;
}
@@ -1034,183 +745,26 @@ probe_irq_off(unsigned long val)
nr_irqs = 0;
irq_found = 0;
- spin_lock_irq(&irq_controller_lock);
for (i=0; i<NR_IRQS; i++) {
- unsigned int status = irq_desc[i].status;
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
- if (!(status & IRQ_AUTODETECT))
- continue;
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
- if (!(status & IRQ_WAITING)) {
- if (!nr_irqs)
- irq_found = i;
- nr_irqs++;
+ if (status & IRQ_AUTODETECT) {
+ if (!(status & IRQ_WAITING)) {
+ if (!nr_irqs)
+ irq_found = i;
+ nr_irqs++;
+ }
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
}
- irq_desc[i].status = status & ~IRQ_AUTODETECT;
- irq_desc[i].handler->shutdown(i);
+ spin_unlock_irq(&desc->lock);
}
- spin_unlock_irq(&irq_controller_lock);
if (nr_irqs > 1)
irq_found = -irq_found;
return irq_found;
}
-
-
-/*
- * The main interrupt entry point.
- */
-
-asmlinkage void
-do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
- unsigned long a3, unsigned long a4, unsigned long a5,
- struct pt_regs regs)
-{
- switch (type) {
- case 0:
-#ifdef CONFIG_SMP
- handle_ipi(&regs);
- return;
-#else
- printk(KERN_CRIT "Interprocessor interrupt? "
- "You must be kidding!\n");
-#endif
- break;
- case 1:
-#ifdef CONFIG_SMP
- cpu_data[smp_processor_id()].smp_local_irq_count++;
- smp_percpu_timer_interrupt(&regs);
- if (smp_processor_id() == boot_cpuid)
-#endif
- handle_irq(RTC_IRQ, &regs);
- return;
- case 2:
- alpha_mv.machine_check(vector, la_ptr, &regs);
- return;
- case 3:
- alpha_mv.device_interrupt(vector, &regs);
- return;
- case 4:
- perf_irq(vector, &regs);
- return;
- default:
- printk(KERN_CRIT "Hardware intr %ld %lx? Huh?\n",
- type, vector);
- }
- printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps);
-}
-
-void __init
-common_init_isa_dma(void)
-{
- outb(0, DMA1_RESET_REG);
- outb(0, DMA2_RESET_REG);
- outb(0, DMA1_CLR_MASK_REG);
- outb(0, DMA2_CLR_MASK_REG);
-}
-
-void __init
-init_IRQ(void)
-{
- wrent(entInt, 0);
- alpha_mv.init_irq();
-}
-
-
-/*
- */
-#define MCHK_K_TPERR 0x0080
-#define MCHK_K_TCPERR 0x0082
-#define MCHK_K_HERR 0x0084
-#define MCHK_K_ECC_C 0x0086
-#define MCHK_K_ECC_NC 0x0088
-#define MCHK_K_OS_BUGCHECK 0x008A
-#define MCHK_K_PAL_BUGCHECK 0x0090
-
-#ifndef CONFIG_SMP
-struct mcheck_info __mcheck_info;
-#endif
-
-void
-process_mcheck_info(unsigned long vector, unsigned long la_ptr,
- struct pt_regs *regs, const char *machine,
- int expected)
-{
- struct el_common *mchk_header;
- const char *reason;
-
- /*
- * See if the machine check is due to a badaddr() and if so,
- * ignore it.
- */
-
-#if DEBUG_MCHECK > 0
- printk(KERN_CRIT "%s machine check %s\n", machine,
- expected ? "expected." : "NOT expected!!!");
-#endif
-
- if (expected) {
- int cpu = smp_processor_id();
- mcheck_expected(cpu) = 0;
- mcheck_taken(cpu) = 1;
- return;
- }
-
- mchk_header = (struct el_common *)la_ptr;
-
- printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%lx\n",
- machine, vector, regs->pc, mchk_header->code);
-
- switch ((unsigned int) mchk_header->code) {
- /* Machine check reasons. Defined according to PALcode sources. */
- case 0x80: reason = "tag parity error"; break;
- case 0x82: reason = "tag control parity error"; break;
- case 0x84: reason = "generic hard error"; break;
- case 0x86: reason = "correctable ECC error"; break;
- case 0x88: reason = "uncorrectable ECC error"; break;
- case 0x8A: reason = "OS-specific PAL bugcheck"; break;
- case 0x90: reason = "callsys in kernel mode"; break;
- case 0x96: reason = "i-cache read retryable error"; break;
- case 0x98: reason = "processor detected hard error"; break;
-
- /* System specific (these are for Alcor, at least): */
- case 0x202: reason = "system detected hard error"; break;
- case 0x203: reason = "system detected uncorrectable ECC error"; break;
- case 0x204: reason = "SIO SERR occurred on PCI bus"; break;
- case 0x205: reason = "parity error detected by CIA"; break;
- case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break;
- case 0x207: reason = "non-existent memory error"; break;
- case 0x208: reason = "MCHK_K_DCSR"; break;
- case 0x209: reason = "PCI SERR detected"; break;
- case 0x20b: reason = "PCI data parity error detected"; break;
- case 0x20d: reason = "PCI address parity error detected"; break;
- case 0x20f: reason = "PCI master abort error"; break;
- case 0x211: reason = "PCI target abort error"; break;
- case 0x213: reason = "scatter/gather PTE invalid error"; break;
- case 0x215: reason = "flash ROM write error"; break;
- case 0x217: reason = "IOA timeout detected"; break;
- case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
- case 0x21b: reason = "EISA fail-safe timer timeout"; break;
- case 0x21d: reason = "EISA bus time-out"; break;
- case 0x21f: reason = "EISA software generated NMI"; break;
- case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
- default: reason = "unknown"; break;
- }
-
- printk(KERN_CRIT "machine check type: %s%s\n",
- reason, mchk_header->retry ? " (retryable)" : "");
-
- dik_show_regs(regs, NULL);
-
-#if DEBUG_MCHECK > 1
- {
- /* Dump the logout area to give all info. */
- unsigned long *ptr = (unsigned long *)la_ptr;
- long i;
- for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
- printk(KERN_CRIT " +%8lx %016lx %016lx\n",
- i*sizeof(long), ptr[i], ptr[i+1]);
- }
- }
-#endif
-}