patch-2.3.20 linux/arch/ppc/amiga/amiints.c

Next file: linux/arch/ppc/amiga/bootinfo.c
Previous file: linux/arch/ppc/amiga/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.19/linux/arch/ppc/amiga/amiints.c linux/arch/ppc/amiga/amiints.c
@@ -1,7 +1,3 @@
-/* Rename a few functions. */
-#define amiga_request_irq request_irq
-#define amiga_free_irq free_irq
-
 /*
  * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code
  *
@@ -23,6 +19,20 @@
  *                           called again.
  *           The whole interrupt handling for CIAs is moved to cia.c
  *           /Roman Zippel
+ *
+ * 07/08/99: rewamp of the interrupt handling - we now have two types of
+ *           interrupts, normal and fast handlers, fast handlers being
+ *           marked with SA_INTERRUPT and runs with all other interrupts
+ *           disabled. Normal interrupts disable their own source but
+ *           run with all other interrupt sources enabled.
+ *           PORTS and EXTER interrupts are always shared even if the
+ *           drivers do not explicitly mark this when calling
+ *           request_irq which they really should do.
+ *           This is similar to the way interrupts are handled on all
+ *           other architectures and makes a ton of sense besides
+ *           having the advantage of making it easier to share
+ *           drivers.
+ *           /Jes
  */
 
 #include <linux/config.h>
@@ -90,7 +100,7 @@
 		} else {
 			ami_irq_list[i] = new_irq_node();
 			ami_irq_list[i]->handler = ami_badint;
-			ami_irq_list[i]->flags   = IRQ_FLG_STD;
+			ami_irq_list[i]->flags   = 0;
 			ami_irq_list[i]->dev_id  = NULL;
 			ami_irq_list[i]->devname = NULL;
 			ami_irq_list[i]->next    = NULL;
@@ -108,7 +118,7 @@
 	custom.intreq = 0x7fff;
 
 #ifdef CONFIG_APUS
-	/* Clear any inter-CPU interrupt requests. Circumvents bug in
+	/* Clear any inter-CPU interupt requests. Circumvents bug in
            Blizzard IPL emulation HW (or so it appears). */
 	APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK);
 
@@ -124,7 +134,7 @@
 	cia_init_IRQ(&ciab_base);
 }
 
-static inline void amiga_insert_irq(irq_node_t **list, irq_node_t *node)
+static inline int amiga_insert_irq(irq_node_t **list, irq_node_t *node)
 {
 	unsigned long flags;
 	irq_node_t *cur;
@@ -138,19 +148,18 @@
 
 	cur = *list;
 
-	if (node->flags & IRQ_FLG_FAST) {
-		node->flags &= ~IRQ_FLG_SLOW;
-		while (cur && cur->flags & IRQ_FLG_FAST) {
-			list = &cur->next;
-			cur = cur->next;
-		}
-	} else if (node->flags & IRQ_FLG_SLOW) {
-		while (cur) {
+	if (node->flags & SA_INTERRUPT) {
+		if (node->flags & SA_SHIRQ)
+			return -EBUSY;
+		/*
+		 * There should never be more than one
+		 */
+		while (cur && cur->flags & SA_INTERRUPT) {
 			list = &cur->next;
 			cur = cur->next;
 		}
 	} else {
-		while (cur && !(cur->flags & IRQ_FLG_SLOW)) {
+		while (cur) {
 			list = &cur->next;
 			cur = cur->next;
 		}
@@ -160,6 +169,7 @@
 	*list = node;
 
 	restore_flags(flags);
+	return 0;
 }
 
 static inline void amiga_delete_irq(irq_node_t **list, void *dev_id)
@@ -189,13 +199,16 @@
  *                     If the addition was successful, it returns 0.
  */
 
-int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+int amiga_request_irq(unsigned int irq,
+		      void (*handler)(int, void *, struct pt_regs *),
                       unsigned long flags, const char *devname, void *dev_id)
 {
 	irq_node_t *node;
+	int error = 0;
 
 	if (irq >= AMI_IRQS) {
-		printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname);
+		printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__,
+			irq, devname);
 		return -ENXIO;
 	}
 
@@ -211,6 +224,11 @@
 		return cia_request_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA,
 		                       handler, flags, devname, dev_id);
 
+	/*
+	 * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared,
+	 * we could add a check here for the SA_SHIRQ flag but all drivers
+	 * should be aware of sharing anyway.
+	 */
 	if (ami_servers[irq]) {
 		if (!(node = new_irq_node()))
 			return -ENOMEM;
@@ -219,20 +237,8 @@
 		node->dev_id  = dev_id;
 		node->devname = devname;
 		node->next    = NULL;
-		amiga_insert_irq(&ami_irq_list[irq], node);
+		error = amiga_insert_irq(&ami_irq_list[irq], node);
 	} else {
-		if (!(ami_irq_list[irq]->flags & IRQ_FLG_STD)) {
-			if (ami_irq_list[irq]->flags & IRQ_FLG_LOCK) {
-				printk("%s: IRQ %d from %s is not replaceable\n",
-				       __FUNCTION__, irq, ami_irq_list[irq]->devname);
-				return -EBUSY;
-			}
-			if (!(flags & IRQ_FLG_REPLACE)) {
-				printk("%s: %s can't replace IRQ %d from %s\n",
-				       __FUNCTION__, devname, irq, ami_irq_list[irq]->devname);
-				return -EBUSY;
-			}
-		}
 		ami_irq_list[irq]->handler = handler;
 		ami_irq_list[irq]->flags   = flags;
 		ami_irq_list[irq]->dev_id  = dev_id;
@@ -243,7 +249,7 @@
 	if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq])
 		custom.intena = IF_SETCLR | ami_intena_vals[irq];
 
-	return 0;
+	return error;
 }
 
 void amiga_free_irq(unsigned int irq, void *dev_id)
@@ -276,7 +282,7 @@
 			printk("%s: removing probably wrong IRQ %d from %s\n",
 			       __FUNCTION__, irq, ami_irq_list[irq]->devname);
 		ami_irq_list[irq]->handler = ami_badint;
-		ami_irq_list[irq]->flags   = IRQ_FLG_STD;
+		ami_irq_list[irq]->flags   = 0;
 		ami_irq_list[irq]->dev_id  = NULL;
 		ami_irq_list[irq]->devname = NULL;
 		custom.intena = ami_intena_vals[irq];
@@ -366,45 +372,58 @@
 void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server)
 {
 	irq_node_t *node, *slow_nodes;
-	unsigned short flags;
+	unsigned short flags, intena;
 
 	kstat.irqs[0][SYS_IRQS + irq]++;
 	if (server->count++)
 		server->reentrance = 1;
-	/* serve first fast and normal handlers */
-	for (node = ami_irq_list[irq];
-	     node && (!(node->flags & IRQ_FLG_SLOW));
-	     node = node->next)
-		node->handler(irq, node->dev_id, fp);
-	custom.intreq = ami_intena_vals[irq];
+
+	intena = ami_intena_vals[irq];
+	custom.intreq = intena;
+
+	/* serve first fast handlers - there can only be one of these */
+	node = ami_irq_list[irq];
+
+	/*
+	 * Timer interrupts show up like this
+	 */
 	if (!node) {
 		server->count--;
 		return;
 	}
-#ifdef CONFIG_APUS
-	APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT);
-	APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK);
-	APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET
-				  | (~(fp->mq) & IPLEMU_IPLMASK)));
-	APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT);
-#else
+
+	if (node && (node->flags & SA_INTERRUPT)) {
+		save_flags(flags);
+		cli();
+		node->handler(irq, node->dev_id, fp);
+		restore_flags(flags);
+
+		server->count--;
+		return;
+	}
+
+	/*
+	 * Disable the interrupt source in question and reenable all
+	 * other interrupts. No interrupt handler should ever touch
+	 * the intena flags directly!
+	 */
+	custom.intena = intena;
 	save_flags(flags);
-	restore_flags((flags & ~0x0700) | (fp->sr & 0x0700));
-#endif
-	/* if slow handlers exists, serve them now */
+	sti();
+
 	slow_nodes = node;
 	for (;;) {
 		for (; node; node = node->next)
 			node->handler(irq, node->dev_id, fp);
-		/* if reentrance occurred, serve slow handlers again */
-		custom.intena = ami_intena_vals[irq];
+
 		if (!server->reentrance) {
 			server->count--;
-			custom.intena = IF_SETCLR | ami_intena_vals[irq];
+			restore_flags(flags);
+			custom.intena = IF_SETCLR | intena;
 			return;
 		}
+
 		server->reentrance = 0;
-		custom.intena = IF_SETCLR | ami_intena_vals[irq];
 		node = slow_nodes;
 	}
 }
@@ -522,24 +541,13 @@
 	for (i = 0; i < AMI_STD_IRQS; i++) {
 		if (!(node = ami_irq_list[i]))
 			continue;
-		if (node->flags & IRQ_FLG_STD)
-			continue;
 		len += sprintf(buf+len, "ami  %2d: %10u ", i,
 		               kstat.irqs[0][SYS_IRQS + i]);
 		do {
-			if (ami_servers[i]) {
-				if (node->flags & IRQ_FLG_FAST)
-					len += sprintf(buf+len, "F ");
-				else if (node->flags & IRQ_FLG_SLOW)
-					len += sprintf(buf+len, "S ");
-				else
-					len += sprintf(buf+len, "  ");
-			} else {
-				if (node->flags & IRQ_FLG_LOCK)
-					len += sprintf(buf+len, "L ");
-				else
-					len += sprintf(buf+len, "  ");
-			}
+			if (node->flags & SA_INTERRUPT)
+				len += sprintf(buf+len, "F ");
+			else
+				len += sprintf(buf+len, "  ");
 			len += sprintf(buf+len, "%s\n", node->devname);
 			if ((node = node->next))
 				len += sprintf(buf+len, "                    ");

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