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

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