patch-2.2.18 linux/arch/arm/kernel/irq.c
Next file: linux/arch/arm/kernel/isa.c
Previous file: linux/arch/arm/kernel/ioport.c
Back to the patch index
Back to the overall index
- Lines: 332
- Date:
Fri Sep 15 23:28:37 2000
- Orig file:
v2.2.17/arch/arm/kernel/irq.c
- Orig date:
Fri Apr 21 12:45:45 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/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>
-
-unsigned long __init init_IRQ(unsigned long memory)
+unsigned long __init init_IRQ(unsigned long memory)
{
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)