patch-1.3.70 linux/arch/mips/kernel/irq.c

Next file: linux/arch/ppc/kernel/irq.c
Previous file: linux/arch/i386/kernel/time.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.69/linux/arch/mips/kernel/irq.c linux/arch/mips/kernel/irq.c
@@ -32,6 +32,7 @@
 #include <linux/types.h>
 #include <linux/interrupt.h>
 #include <linux/timex.h>
+#include <linux/malloc.h>
 #include <linux/random.h>
 
 #include <asm/bitops.h>
@@ -42,6 +43,8 @@
 #include <asm/mipsregs.h>
 #include <asm/system.h>
 
+#define TIMER_IRQ 0                     /* Keep this in sync with time.c */
+
 unsigned char cache_21 = 0xff;
 unsigned char cache_A1 = 0xff;
 
@@ -98,36 +101,34 @@
 /*
  * Initial irq handlers.
  */
-struct irqaction {
-	void (*handler)(int, struct pt_regs *);
-	unsigned long flags;
-	unsigned long mask;
-	const char *name;
-};
-
-static struct irqaction irq_action[16] = {
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
-	{ NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
+static struct irqaction timer_irq = { NULL, 0, 0, NULL, NULL, NULL};
+static struct irqaction cascade_irq = { NULL, 0, 0, NULL, NULL, NULL};
+static struct irqaction math_irq = { NULL, 0, 0, NULL, NULL, NULL};
+
+static struct irqaction *irq_action[16] = {
+	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
+	  NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
 };
 
 int get_irq_list(char *buf)
 {
 	int i, len = 0;
-	struct irqaction * action = irq_action;
+	struct irqaction * action;
 
-	for (i = 0 ; i < 16 ; i++, action++) {
-		if (!action->handler)
-			continue;
-		len += sprintf(buf+len, "%3d: %8d %c %s\n",
+	for (i = 0 ; i < 16 ; i++) {
+	        action = *(i + irq_action);
+		if (!action) 
+		        continue;
+		len += sprintf(buf+len, "%2d: %8d %c %s",
 			i, kstat.interrupts[i],
 			(action->flags & SA_INTERRUPT) ? '+' : ' ',
 			action->name);
+		for (action=action->next; action; action = action->next) {
+			len += sprintf(buf+len, ",%s %s",
+				(action->flags & SA_INTERRUPT) ? " +" : "",
+				action->name);
+		}
+		len += sprintf(buf+len, "\n");
 	}
 	return len;
 }
@@ -141,12 +142,15 @@
  */
 asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 {
-	struct irqaction * action = irq + irq_action;
+	struct irqaction * action = *(irq + irq_action);
 
 	kstat.interrupts[irq]++;
 	if (action->flags & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
-	action->handler(irq, regs);
+	while (action) {
+	    action->handler(irq, action->dev_id, regs);
+	    action = action->next;
+	}
 }
 
 /*
@@ -156,38 +160,74 @@
  */
 asmlinkage void do_fast_IRQ(int irq)
 {
-	struct irqaction * action = irq + irq_action;
+	struct irqaction * action = *(irq + irq_action);
 
 	kstat.interrupts[irq]++;
 	if (action->flags & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
-	action->handler(irq, NULL);
+	while (action) {
+	    action->handler(irq, action->dev_id, NULL);
+	    action = action->next;
+	}
 }
 
 #define SA_PROBE SA_ONESHOT
 
-int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
-	unsigned long irqflags, const char * devname)
+int request_irq(unsigned int irq, 
+		void (*handler)(int, void *, struct pt_regs *),
+		unsigned long irqflags, 
+		const char * devname,
+		void *dev_id)
 {
-	struct irqaction * action;
+	struct irqaction * action, *tmp = NULL;
 	unsigned long flags;
 
 	if (irq > 15)
 		return -EINVAL;
-	action = irq + irq_action;
-	if (action->handler)
-		return -EBUSY;
 	if (!handler)
-		return -EINVAL;
+	    return -EINVAL;
+	action = *(irq + irq_action);
+	if (action) {
+	    if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+		for (tmp = action; tmp->next; tmp = tmp->next);
+	    } else {
+		return -EBUSY;
+	    }
+	    if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+	      printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+	      return -EBUSY;
+	    }   
+	}
 	if (irqflags & SA_SAMPLE_RANDOM)
 		rand_initialize_irq(irq);
 	save_flags(flags);
 	cli();
+	if (irq == 2)
+	    action = &cascade_irq;
+	else if (irq == 13)
+	  action = &math_irq;
+	else if (irq == TIMER_IRQ)
+	  action = &timer_irq;
+	else
+	  action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+
+	if (!action) { 
+	    restore_flags(flags);
+	    return -ENOMEM;
+	}
+
 	action->handler = handler;
 	action->flags = irqflags;
 	action->mask = 0;
 	action->name = devname;
-	if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	if (tmp) {
+	    tmp->next = action;
+	} else {
+	    *(irq + irq_action) = action;
+	    if (!(action->flags & SA_PROBE)) {/* SA_ONESHOT used by probing */
 		/*
 		 * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS???
 		 */
@@ -195,23 +235,25 @@
 			set_int_vector(irq,fast_interrupt);
 		else
 			set_int_vector(irq,interrupt);
-	}
-	if (irq < 8) {
+	    }
+	    if (irq < 8) {
 		cache_21 &= ~(1<<irq);
 		outb(cache_21,0x21);
-	} else {
+	    } else {
 		cache_21 &= ~(1<<2);
 		cache_A1 &= ~(1<<(irq-8));
 		outb(cache_21,0x21);
 		outb(cache_A1,0xA1);
+	    }
 	}
 	restore_flags(flags);
 	return 0;
 }
 
-void free_irq(unsigned int irq)
+void free_irq(unsigned int irq, void *dev_id)
 {
-	struct irqaction * action = irq + irq_action;
+	struct irqaction * action = *(irq + irq_action);
+	struct irqaction * tmp = NULL;
 	unsigned long flags;
 
 	if (irq > 15) {
@@ -222,24 +264,46 @@
 		printk("Trying to free free IRQ%d\n",irq);
 		return;
 	}
+	if (dev_id) {
+	    for (; action; action = action->next) {
+	        if (action->dev_id == dev_id) break;
+		tmp = action;
+	    }
+	    if (!action) {
+		printk("Trying to free free shared IRQ%d\n",irq);
+		return;
+	    }
+	} else if (action->flags & SA_SHIRQ) {
+	    printk("Trying to free shared IRQ%d with NULL device ID\n", irq);
+	    return;
+	}
 	save_flags(flags);
 	cli();
-	if (irq < 8) {
+	if (action && tmp) {
+	    tmp->next = action->next;
+	} else {
+	    *(irq + irq_action) = action->next;
+	}
+
+	if ((irq == 2) || (irq == 13) | (irq == TIMER_IRQ))
+	  memset(action, 0, sizeof(struct irqaction));
+	else 
+	  kfree_s(action, sizeof(struct irqaction));
+	
+	if (!(*(irq + irq_action))) {
+	    if (irq < 8) {
 		cache_21 |= 1 << irq;
 		outb(cache_21,0x21);
-	} else {
+	    } else {
 		cache_A1 |= 1 << (irq-8);
 		outb(cache_A1,0xA1);
+	    }
+	    set_int_vector(irq,bad_interrupt);
 	}
-	set_int_vector(irq,bad_interrupt);
-	action->handler = NULL;
-	action->flags = 0;
-	action->mask = 0;
-	action->name = NULL;
 	restore_flags(flags);
 }
 
-static void no_action(int cpl, struct pt_regs * regs) { }
+static void no_action(int cpl, void *dev_id, struct pt_regs * regs) { }
 
 unsigned long probe_irq_on (void)
 {
@@ -248,7 +312,7 @@
 
 	/* first, snaffle up any unassigned irqs */
 	for (i = 15; i > 0; i--) {
-		if (!request_irq(i, no_action, SA_PROBE, "probe")) {
+		if (!request_irq(i, no_action, SA_PROBE, "probe", NULL)) {
 			enable_irq(i);
 			irqs |= (1 << i);
 		}
@@ -262,7 +326,7 @@
 	for (i = 15; i > 0; i--) {
 		if (irqs & (1 << i) & irqmask) {
 			irqs ^= (1 << i);
-			free_irq(i);
+			free_irq(i, NULL);
 		}
 	}
 #ifdef DEBUG
@@ -278,7 +342,7 @@
 	irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
 	for (i = 15; i > 0; i--) {
 		if (irqs & (1 << i)) {
-			free_irq(i);
+			free_irq(i, NULL);
 		}
 	}
 #ifdef DEBUG
@@ -317,7 +381,7 @@
 			outb_p(LATCH & 0xff , 0x40);	/* LSB */
 			outb(LATCH >> 8 , 0x40);	/* MSB */
 
-			if (request_irq(2, no_action, SA_INTERRUPT, "cascade"))
+			if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL))
 				printk("Unable to get IRQ2 for cascade\n");
 			break;
 		default:

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this