patch-2.1.121 linux/arch/arm/kernel/irq.c
Next file: linux/arch/arm/kernel/leds-ebsa110.c
Previous file: linux/arch/arm/kernel/ioport.c
Back to the patch index
Back to the overall index
- Lines: 474
- Date:
Sun Sep 6 10:44:47 1998
- Orig file:
v2.1.120/linux/arch/arm/kernel/irq.c
- Orig date:
Tue Jul 21 00:15:30 1998
diff -u --recursive --new-file v2.1.120/linux/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c
@@ -3,7 +3,6 @@
*
* Copyright (C) 1992 Linus Torvalds
* Modifications for ARM processor Copyright (C) 1995-1998 Russell King.
- * FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
*
* This file contains the code used by various IRQ handling routines:
* asking for different IRQ's should be done through these routines
@@ -31,20 +30,10 @@
#include <linux/smp_lock.h>
#include <linux/init.h>
-#include <asm/fiq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/system.h>
-#include <asm/arch/irq.h>
-
-unsigned int local_bh_count[NR_CPUS];
-unsigned int local_irq_count[NR_CPUS];
-spinlock_t irq_controller_lock;
-static struct fiq_handler *current_fiq;
-static unsigned long no_fiq_insn;
-
-#define FIQ_VECTOR ((unsigned long *)0x1c)
#ifndef SMP
#define irq_enter(cpu, irq) (++local_irq_count[cpu])
@@ -53,48 +42,65 @@
#error SMP not supported
#endif
-#ifdef CONFIG_ARCH_ACORN
-/* Bitmask indicating valid interrupt numbers
- * (to be moved to include/asm-arm/arch-*)
- */
-unsigned long validirqs[NR_IRQS / 32] = {
- 0x003ffe7f, 0x000001ff, 0x000000ff, 0x00000000
+#ifndef cliIF
+#define cliIF()
+#endif
+
+unsigned int local_bh_count[NR_CPUS];
+unsigned int local_irq_count[NR_CPUS];
+spinlock_t irq_controller_lock;
+
+extern int get_fiq_list(char *);
+extern void init_FIQ(void);
+
+struct irqdesc {
+ unsigned int nomask : 1; /* IRQ does not mask in IRQ */
+ unsigned int enabled : 1; /* IRQ is currently enabled */
+ unsigned int triggered: 1; /* IRQ has occurred */
+ unsigned int probing : 1; /* IRQ in use for a probe */
+ unsigned int probe_ok : 1; /* IRQ can be used for probe */
+ unsigned int valid : 1; /* IRQ claimable */
+ unsigned int unused :26;
+ void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */
+ void (*mask)(unsigned int irq); /* Mask IRQ */
+ void (*unmask)(unsigned int irq); /* Unmask IRQ */
+ struct irqaction *action;
+ unsigned int unused2[3];
};
-#define valid_irq(x) ((x) < NR_IRQS && validirqs[(x) >> 5] & (1 << ((x) & 31)))
-#else
+static struct irqdesc irq_desc[NR_IRQS];
-#define valid_irq(x) ((x) < NR_IRQS)
-#endif
+/*
+ * Dummy mask/unmask handler
+ */
+static void dummy_mask_unmask_irq(unsigned int irq)
+{
+}
-void disable_irq(unsigned int irq_nr)
+void disable_irq(unsigned int irq)
{
unsigned long flags;
spin_lock_irqsave(&irq_controller_lock, flags);
-#ifdef cliIF
- save_flags(flags);
cliIF();
-#endif
- mask_irq(irq_nr);
+ irq_desc[irq].enabled = 0;
+ irq_desc[irq].mask(irq);
spin_unlock_irqrestore(&irq_controller_lock, flags);
}
-void enable_irq(unsigned int irq_nr)
+void enable_irq(unsigned int irq)
{
unsigned long flags;
spin_lock_irqsave(&irq_controller_lock, flags);
-#ifdef cliIF
- save_flags (flags);
cliIF();
-#endif
- unmask_irq(irq_nr);
+ irq_desc[irq].enabled = 1;
+ irq_desc[irq].probing = 0;
+ irq_desc[irq].triggered = 0;
+ irq_desc[irq].unmask(irq);
spin_unlock_irqrestore(&irq_controller_lock, flags);
}
-struct irqaction *irq_action[NR_IRQS];
-
int get_irq_list(char *buf)
{
int i;
@@ -102,7 +108,7 @@
char *p = buf;
for (i = 0 ; i < NR_IRQS ; i++) {
- action = irq_action[i];
+ action = irq_desc[i].action;
if (!action)
continue;
p += sprintf(p, "%3d: %10u %s",
@@ -112,8 +118,8 @@
}
*p++ = '\n';
}
- p += sprintf(p, "FIQ: %s\n",
- current_fiq?current_fiq->name:"unused");
+
+ p += get_fiq_list(p);
return p - buf;
}
@@ -122,26 +128,30 @@
*/
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
+ struct irqdesc * desc = irq_desc + irq;
struct irqaction * action;
int status, cpu;
-#if defined(HAS_IOMD) || defined(HAS_IOC)
- if (irq != IRQ_EXPANSIONCARD)
-#endif
- {
- spin_lock(&irq_controller_lock);
- mask_and_ack_irq(irq);
- spin_unlock(&irq_controller_lock);
- }
+ spin_lock(&irq_controller_lock);
+ desc->mask_ack(irq);
+ spin_unlock(&irq_controller_lock);
cpu = smp_processor_id();
irq_enter(cpu, irq);
kstat.irqs[cpu][irq]++;
+ desc->triggered = 1;
/* Return with this interrupt masked if no action */
status = 0;
- action = *(irq + irq_action);
+ action = desc->action;
+
if (action) {
+ if (desc->nomask) {
+ spin_lock(&irq_controller_lock);
+ desc->unmask(irq);
+ spin_unlock(&irq_controller_lock);
+ }
+
if (!(action->flags & SA_INTERRUPT))
__sti();
@@ -150,33 +160,20 @@
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
+
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
- switch (irq) {
-#if defined(HAS_IOMD) || defined(HAS_IOC)
- case IRQ_KEYBOARDTX:
- case IRQ_EXPANSIONCARD:
- break;
-#endif
-#ifdef HAS_IOMD
- case IRQ_DMA0:
- case IRQ_DMA1:
- case IRQ_DMA2:
- case IRQ_DMA3:
- break;
-#endif
-
- default:
+ if (!desc->nomask && desc->enabled) {
spin_lock(&irq_controller_lock);
- unmask_irq(irq);
+ desc->unmask(irq);
spin_unlock(&irq_controller_lock);
- break;
}
}
irq_exit(cpu, irq);
+
/*
* This should be conditional: we should really get
* a return code from the irq handler to tell us
@@ -197,9 +194,18 @@
#if defined(CONFIG_ARCH_ACORN)
void do_ecard_IRQ(int irq, struct pt_regs *regs)
{
+ struct irqdesc * desc;
struct irqaction * action;
+ int cpu;
+
+ desc = irq_desc + irq;
+
+ cpu = smp_processor_id();
+ kstat.irqs[cpu][irq]++;
+ desc->triggered = 1;
+
+ action = desc->action;
- action = *(irq + irq_action);
if (action) {
do {
action->handler(irq, action->dev_id, regs);
@@ -207,7 +213,7 @@
} while (action);
} else {
spin_lock(&irq_controller_lock);
- mask_irq (irq);
+ desc->mask(irq);
spin_unlock(&irq_controller_lock);
}
}
@@ -219,11 +225,18 @@
struct irqaction *old, **p;
unsigned long flags;
- p = irq_action + irq;
+ if (new->flags & SA_SAMPLE_RANDOM)
+ rand_initialize_irq(irq);
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
+
+ p = &irq_desc[irq].action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
- if (!(old->flags & new->flags & SA_SHIRQ))
+ if (!(old->flags & new->flags & SA_SHIRQ)) {
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
return -EBUSY;
+ }
/* add new interrupt at end of irq queue */
do {
@@ -233,18 +246,16 @@
shared = 1;
}
- if (new->flags & SA_SAMPLE_RANDOM)
- rand_initialize_irq(irq);
-
- save_flags_cli(flags);
*p = new;
if (!shared) {
- spin_lock(&irq_controller_lock);
- unmask_irq(irq);
- spin_unlock(&irq_controller_lock);
+ irq_desc[irq].nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
+ irq_desc[irq].enabled = 1;
+ irq_desc[irq].probing = 0;
+ irq_desc[irq].unmask(irq);
}
- restore_flags(flags);
+
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
return 0;
}
@@ -257,8 +268,8 @@
{
unsigned long retval;
struct irqaction *action;
-
- if (!valid_irq(irq))
+
+ if (!irq_desc[irq].valid)
return -EINVAL;
if (!handler)
return -EINVAL;
@@ -286,14 +297,14 @@
struct irqaction * action, **p;
unsigned long flags;
- if (!valid_irq(irq)) {
+ if (!irq_desc[irq].valid) {
printk(KERN_ERR "Trying to free IRQ%d\n",irq);
#ifdef CONFIG_DEBUG_ERRORS
__backtrace();
#endif
return;
}
- for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+ for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
@@ -310,75 +321,109 @@
#endif
}
-unsigned long probe_irq_on (void)
+/* Start the interrupt probing. Unlike other architectures,
+ * we don't return a mask of interrupts from probe_irq_on,
+ * but return the number of interrupts enabled for the probe.
+ * The interrupts which have been enabled for probing is
+ * instead recorded in the irq_desc structure.
+ */
+unsigned long probe_irq_on(void)
{
unsigned int i, irqs = 0;
unsigned long delay;
- /* first snaffle up any unassigned irqs */
- for (i = 15; i > 0; i--) {
- if (!irq_action[i] && valid_irq(i)) {
- enable_irq(i);
- irqs |= 1 << i;
- }
+ /*
+ * first snaffle up any unassigned but
+ * probe-able interrupts
+ */
+ spin_lock_irq(&irq_controller_lock);
+ for (i = 0; i < NR_IRQS; i++) {
+ if (!irq_desc[i].valid ||
+ !irq_desc[i].probe_ok ||
+ irq_desc[i].action)
+ continue;
+
+ irq_desc[i].probing = 1;
+ irq_desc[i].enabled = 1;
+ irq_desc[i].triggered = 0;
+ irq_desc[i].unmask(i);
+ irqs += 1;
}
+ spin_unlock_irq(&irq_controller_lock);
- /* wait for spurious interrupts to mask themselves out again */
+ /*
+ * wait for spurious interrupts to mask themselves out again
+ */
for (delay = jiffies + HZ/10; delay > jiffies; )
/* min 100ms delay */;
+ /*
+ * now filter out any obviously spurious interrupts
+ */
+ spin_lock_irq(&irq_controller_lock);
+ for (i = 0; i < NR_IRQS; i++) {
+ if (irq_desc[i].probing && irq_desc[i].triggered) {
+ irq_desc[i].probing = 0;
+ irqs -= 1;
+ }
+ }
+ spin_unlock_irq(&irq_controller_lock);
+
/* now filter out any obviously spurious interrupts */
- return irqs & get_enabled_irqs();
+ return irqs;
}
-int probe_irq_off (unsigned long irqs)
+/*
+ * Possible return values:
+ * >= 0 - interrupt number
+ * -1 - no interrupt/many interrupts
+ */
+int probe_irq_off(unsigned long irqs)
{
unsigned int i;
+ int irq_found = -1;
- irqs &= ~get_enabled_irqs();
- if (!irqs)
- return 0;
- i = ffz (~irqs);
- if (irqs != (irqs & (1 << i)))
- i = -i;
- return i;
-}
-
-int claim_fiq(struct fiq_handler *f)
-{
- if (current_fiq) {
- if (current_fiq->callback == NULL || (*current_fiq->callback)())
- return -EBUSY;
- }
- current_fiq = f;
- return 0;
-}
-
-void release_fiq(struct fiq_handler *f)
-{
- if (current_fiq != f) {
- printk(KERN_ERR "%s tried to release FIQ when not owner!\n",
- f->name);
-#ifdef CONFIG_DEBUG_ERRORS
- __backtrace();
-#endif
- return;
+ /*
+ * look at the interrupts, and find exactly one
+ * that we were probing has been triggered
+ */
+ spin_lock_irq(&irq_controller_lock);
+ for (i = 0; i < NR_IRQS; i++) {
+ if (irq_desc[i].probing &&
+ irq_desc[i].triggered) {
+ if (irq_found != -1) {
+ irq_found = NO_IRQ;
+ goto out;
+ }
+ irq_found = i;
+ }
}
- current_fiq = NULL;
- *FIQ_VECTOR = no_fiq_insn;
- __flush_entry_to_ram(FIQ_VECTOR);
+ if (irq_found == -1)
+ irq_found = NO_IRQ;
+out:
+ spin_unlock_irq(&irq_controller_lock);
+ return irq_found;
}
+/*
+ * Get architecture specific interrupt handlers
+ * and interrupt initialisation.
+ */
+#include <asm/arch/irq.h>
+
__initfunc(void init_IRQ(void))
{
extern void init_dma(void);
+ int irq;
- irq_init_irq();
-
- current_fiq = NULL;
- no_fiq_insn = *FIQ_VECTOR;
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
+ irq_desc[irq].mask = dummy_mask_unmask_irq;
+ irq_desc[irq].unmask = dummy_mask_unmask_irq;
+ }
+ irq_init_irq();
+ init_FIQ();
init_dma();
}
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov