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

Next file: linux/drivers/block/Config.in
Previous file: linux/arch/ppc/kernel/irq.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.69/linux/arch/sparc/kernel/irq.c linux/arch/sparc/kernel/irq.c
@@ -16,6 +16,7 @@
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
+#include <linux/malloc.h>
 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
@@ -143,62 +144,79 @@
 /*
  * 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, "%2d: %8d %c %s\n",
-			       i, kstat.interrupts[i],
-			       (action->flags & SA_INTERRUPT) ? '+' : ' ',
-			       action->name);
+	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;
 }
 
 void
-free_irq(unsigned int irq)
+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 > 14) {  /* 14 irq levels on the sparc */
                 printk("Trying to free bogus IRQ %d\n", irq);
                 return;
         }
-        if (!action->handler) {
-                printk("Trying to free free IRQ%d\n", irq);
-                return;
-        }
+	if (!action->handler) {
+		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();
-        disable_irq(irq);
-        action->handler = NULL;
-        action->flags = 0;
-        action->mask = 0;
-        action->name = NULL;
+	if (action && tmp) {
+	    tmp->next = action->next;
+	} else {
+	    *(irq + irq_action) = action->next;
+	}
+	kfree_s(action, sizeof(struct irqaction));
+
+	if (!(*(irq + irq_action))) {
+	    disable_irq(irq);
+	}
+
         restore_flags(flags);
 }
 
@@ -206,15 +224,16 @@
 unexpected_irq(int irq, struct pt_regs * regs)
 {
         int i;
+	struct irqaction * action = *(irq + irq_action);
 
         printk("IO device interrupt, irq = %d\n", irq);
         printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc, 
 		    regs->npc, regs->u_regs[14]);
         printk("Expecting: ");
         for (i = 0; i < 16; i++)
-                if (irq_action[i].handler)
-                        prom_printf("[%s:%d:0x%x] ", irq_action[i].name, (int) i,
-			       (unsigned int) irq_action[i].handler);
+                if (action->handler)
+                        prom_printf("[%s:%d:0x%x] ", action->name, (int) i,
+			       (unsigned int) action->handler);
         printk("AIEEE\n");
 	panic("bogus interrupt received");
 }
@@ -222,13 +241,16 @@
 void
 handler_irq(int irq, struct pt_regs * regs)
 {
-	struct irqaction * action = irq_action + irq;
+	struct irqaction * action = *(irq + irq_action);
 
 	kstat.interrupts[irq]++;
-	if (!action->handler)
-		unexpected_irq(irq, regs);
-	else
-		action->handler(irq, regs);
+	while (action) {
+	    if (!action->handler)
+	        unexpected_irq(irq, action->dev_id, regs);
+	    else
+		action->handler(irq, action->dev_id, regs);
+	    action = action->next;
+	}
 }
 
 /*
@@ -241,10 +263,13 @@
 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]++;
-	action->handler(irq, regs);
+	while (action) {
+	    action->handler(irq, action->dev_id, regs);
+	    action = action->next;
+	}
 	return;
 }
 
@@ -263,21 +288,38 @@
 
 int
 request_fast_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
-		 unsigned long irqflags, const char *devname)
+		 unsigned long irqflags, const char *devname, void *dev_id)
+)
 {
-	struct irqaction *action;
+	struct irqaction * action, *tmp = NULL;
 	unsigned long flags;
 
 	if(irq > 14)
 		return -EINVAL;
-	action = irq + irq_action;
-	if(action->handler)
+	if (!handler)
+	    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(!handler)
-		return -EINVAL;
+	    }
+	    if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+	      printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+	      return -EBUSY;
+	    }   
+	}
 
 	save_flags(flags); cli();
 
+	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+
+	if (!action) { 
+	    restore_flags(flags);
+	    return -ENOMEM;
+	}
+
 	/* Dork with trap table if we get this far. */
 	sparc_ttable[SP_TRAP_IRQ1+(irq-1)].inst_one =
 		SPARC_BRANCH((unsigned long) handler,
@@ -290,6 +332,13 @@
 	action->flags = irqflags;
 	action->mask = 0;
 	action->name = devname;
+	action->dev_id = dev_id;
+
+	if (tmp) {
+	    tmp->next = action;
+	} else {
+	    *(irq + irq_action) = action;
+	}
 
 	restore_flags(flags);
 	return 0;
@@ -299,25 +348,48 @@
 		
 int
 request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
-	    unsigned long irqflags, const char * devname)
+	    unsigned long irqflags, const char * devname, void *dev_id)
 {
-	struct irqaction *action;
+	struct irqaction * action, *tmp = NULL;
 	unsigned long flags;
 
 	if(irq > 14)
 		return -EINVAL;
 
-	action = irq + irq_action;
-	if(action->handler)
+	if (!handler)
+	    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(!handler)
-		return -EINVAL;
-
+	    }
+	    if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
+	      printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
+	      return -EBUSY;
+	    }   
+	}
 	save_flags(flags); cli();
+	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;
+	action->next = NULL;
+	action->dev_id = dev_id;
+
+	if (tmp) {
+	    tmp->next = action;
+	} else {
+	    *(irq + irq_action) = action;
+	}
 	enable_irq(irq);
 	if(irq == 10)
 		probe_clock();

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