patch-2.3.7 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.3.6/linux/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c
@@ -23,7 +23,6 @@
 #include <linux/sched.h>
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
-#include <linux/timex.h>
 #include <linux/malloc.h>
 #include <linux/random.h>
 #include <linux/smp.h>
@@ -32,7 +31,6 @@
 
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/pgtable.h>
 #include <asm/system.h>
 
 #ifndef SMP
@@ -46,10 +44,22 @@
 #define cliIF()
 #endif
 
+/*
+ * Maximum IRQ count.  Currently, this is arbitary.
+ * However, it should not be set too low to prevent
+ * false triggering.  Conversely, if it is set too
+ * high, then you could miss a stuck IRQ.
+ *
+ * Maybe we ought to set a timer and re-enable the
+ * IRQ at a later time?
+ */
+#define MAX_IRQ_CNT	100000
+
 unsigned int local_bh_count[NR_CPUS];
 unsigned int local_irq_count[NR_CPUS];
 spinlock_t irq_controller_lock;
 
+int setup_arm_irq(int, struct irqaction *);
 extern int get_fiq_list(char *);
 extern void init_FIQ(void);
 
@@ -60,17 +70,29 @@
 	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;
+	unsigned int	 noautoenable : 1;	/* don't automatically enable IRQ */
+	unsigned int	 unused   :25;
 	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];
+	/*
+	 * IRQ lock detection
+	 */
+	unsigned int	 lck_cnt;
+	unsigned int	 lck_pc;
+	unsigned int	 lck_jif;
 };
 
 static struct irqdesc irq_desc[NR_IRQS];
 
 /*
+ * Get architecture specific interrupt handlers
+ * and interrupt initialisation.
+ */
+#include <asm/arch/irq.h>
+
+/*
  * Dummy mask/unmask handler
  */
 static void dummy_mask_unmask_irq(unsigned int irq)
@@ -94,10 +116,12 @@
 
 	spin_lock_irqsave(&irq_controller_lock, flags);
 	cliIF();
-	irq_desc[irq].enabled = 1;
 	irq_desc[irq].probing = 0;
 	irq_desc[irq].triggered = 0;
-	irq_desc[irq].unmask(irq);
+	if (!irq_desc[irq].noautoenable) {
+		irq_desc[irq].enabled = 1;
+		irq_desc[irq].unmask(irq);
+	}
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
@@ -119,21 +143,52 @@
 		*p++ = '\n';
 	}
 
-#ifdef CONFIG_ACORN
+#ifdef CONFIG_ARCH_ACORN
 	p += get_fiq_list(p);
 #endif
 	return p - buf;
 }
 
 /*
+ * IRQ lock detection.
+ *
+ * Hopefully, this should get us out of a few locked situations.
+ * However, it may take a while for this to happen, since we need
+ * a large number if IRQs to appear in the same jiffie with the
+ * same instruction pointer (or within 2 instructions).
+ */
+static void check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs)
+{
+	unsigned long instr_ptr = instruction_pointer(regs);
+
+	if (desc->lck_jif == jiffies &&
+	    desc->lck_pc >= instr_ptr && desc->lck_pc < instr_ptr + 8) {
+		desc->lck_cnt += 1;
+
+		if (desc->lck_cnt > MAX_IRQ_CNT) {
+			printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq);
+			disable_irq(irq);
+		}
+	} else {
+		desc->lck_cnt = 0;
+		desc->lck_pc  = instruction_pointer(regs);
+		desc->lck_jif = jiffies;
+	}
+}
+
+/*
  * do_IRQ handles all normal device IRQ's
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct irqdesc * desc = irq_desc + irq;
+	struct irqdesc * desc;
 	struct irqaction * action;
 	int status, cpu;
 
+	irq = fixup_irq(irq);
+
+	desc = irq_desc + irq;
+
 	spin_lock(&irq_controller_lock);
 	desc->mask_ack(irq);
 	spin_unlock(&irq_controller_lock);
@@ -174,6 +229,12 @@
 		}
 	}
 
+	/*
+	 * Debug measure - hopefully we can continue if an
+	 * IRQ lockup problem occurs...
+	 */
+	check_irq_lock(desc, irq, regs);
+
 	irq_exit(cpu, irq);
 
 	/*
@@ -181,15 +242,10 @@
 	 * a return code from the irq handler to tell us
 	 * whether the handler wants us to do software bottom
 	 * half handling or not..
-	 *
-	 * ** IMPORTANT NOTE: do_bottom_half() ENABLES IRQS!!! **
-	 * **  WE MUST DISABLE THEM AGAIN, ELSE IDE DISKS GO   **
-	 * **                       AWOL                       **
 	 */
 	if (1) {
 		if (bh_active & bh_mask)
 			do_bottom_half();
-		__cli();
 	}
 }
 
@@ -227,11 +283,27 @@
 	struct irqaction *old, **p;
 	unsigned long flags;
 
-	if (new->flags & SA_SAMPLE_RANDOM)
+	/*
+	 * Some drivers like serial.c use request_irq() heavily,
+	 * so we have to be careful not to interfere with a
+	 * running system.
+	 */
+	if (new->flags & SA_SAMPLE_RANDOM) {
+		/*
+		 * This function might sleep, we want to call it first,
+		 * outside of the atomic block.
+		 * Yes, this might clear the entropy pool if the wrong
+		 * driver is attempted to be loaded, without actually
+		 * installing a new handler, but is this really a problem,
+		 * only the sysadmin is able to do this.
+		 */
 	        rand_initialize_irq(irq);
+	}
 
+	/*
+	 * The following block of code has to be executed atomically
+	 */
 	spin_lock_irqsave(&irq_controller_lock, flags);
-
 	p = &irq_desc[irq].action;
 	if ((old = *p) != NULL) {
 		/* Can't share interrupts unless both agree to */
@@ -252,28 +324,24 @@
 
 	if (!shared) {
 		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);
+		if (!irq_desc[irq].noautoenable) {
+			irq_desc[irq].enabled = 1;
+			irq_desc[irq].unmask(irq);
+		}
 	}
 
 	spin_unlock_irqrestore(&irq_controller_lock, flags);
 	return 0;
 }
 
-/*
- * Using "struct sigaction" is slightly silly, but there
- * are historical reasons and it works well, so..
- */
 int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
 		 unsigned long irq_flags, const char * devname, void *dev_id)
 {
 	unsigned long retval;
 	struct irqaction *action;
 
-	if (!irq_desc[irq].valid)
-		return -EINVAL;
-	if (!handler)
+	if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler)
 		return -EINVAL;
 
 	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
@@ -299,28 +367,30 @@
 	struct irqaction * action, **p;
 	unsigned long flags;
 
-	if (!irq_desc[irq].valid) {
+	if (irq >= NR_IRQS || !irq_desc[irq].valid) {
 		printk(KERN_ERR "Trying to free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
 		__backtrace();
 #endif
 		return;
 	}
+
+	spin_lock_irqsave(&irq_controller_lock, flags);
 	for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
 		if (action->dev_id != dev_id)
 			continue;
 
 	    	/* Found it - now free it */
-		save_flags_cli (flags);
 		*p = action->next;
-		restore_flags (flags);
 		kfree(action);
-		return;
+		goto out;
 	}
 	printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
 	__backtrace();
 #endif
+out:
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
 }
 
 /* Start the interrupt probing.  Unlike other architectures,
@@ -346,7 +416,6 @@
 			continue;
 
 		irq_desc[i].probing = 1;
-		irq_desc[i].enabled = 1;
 		irq_desc[i].triggered = 0;
 		irq_desc[i].unmask(i);
 		irqs += 1;
@@ -364,7 +433,8 @@
 	 */
 	spin_lock_irq(&irq_controller_lock);
 	for (i = 0; i < NR_IRQS; i++) {
-		if (irq_desc[i].probing && irq_desc[i].triggered) {
+		if (irq_desc[i].probing &&
+		    irq_desc[i].triggered) {
 			irq_desc[i].probing = 0;
 			irqs -= 1;
 		}
@@ -383,7 +453,7 @@
 int probe_irq_off(unsigned long irqs)
 {
 	unsigned int i;
-	int irq_found = -1;
+	int irq_found = NO_IRQ;
 
 	/*
 	 * look at the interrupts, and find exactly one
@@ -393,7 +463,7 @@
 	for (i = 0; i < NR_IRQS; i++) {
 		if (irq_desc[i].probing &&
 		    irq_desc[i].triggered) {
-			if (irq_found != -1) {
+			if (irq_found != NO_IRQ) {
 				irq_found = NO_IRQ;
 				goto out;
 			}
@@ -405,21 +475,19 @@
 		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;
 
 	for (irq = 0; irq < NR_IRQS; irq++) {
+		irq_desc[irq].probe_ok = 0;
+		irq_desc[irq].valid    = 0;
+		irq_desc[irq].noautoenable = 0;
 		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;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)