m68k/mac: Optimize interrupts using chain handlers

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c
index ed95270..cc784c2 100644
--- a/arch/m68k/mac/oss.c
+++ b/arch/m68k/mac/oss.c
@@ -32,10 +32,11 @@
 int oss_present;
 volatile struct mac_oss *oss;
 
-static irqreturn_t oss_irq(int, void *);
-static irqreturn_t oss_nubus_irq(int, void *);
-
+#ifdef CONFIG_GENERIC_HARDIRQS
+extern void via1_irq(unsigned int irq, struct irq_desc *desc);
+#else
 extern irqreturn_t via1_irq(int, void *);
+#endif
 
 /*
  * Initialize the OSS
@@ -63,23 +64,6 @@
 }
 
 /*
- * Register the OSS and NuBus interrupt dispatchers.
- */
-
-void __init oss_register_interrupts(void)
-{
-	if (request_irq(OSS_IRQLEV_SCSI, oss_irq, 0, "scsi", (void *)oss))
-		pr_err("Couldn't register %s interrupt\n", "scsi");
-	if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, 0, "nubus",
-			(void *)oss))
-		pr_err("Couldn't register %s interrupt\n", "nubus");
-	if (request_irq(OSS_IRQLEV_SOUND, oss_irq, 0, "sound", (void *)oss))
-		pr_err("Couldn't register %s interrupt\n", "sound");
-	if (request_irq(OSS_IRQLEV_VIA1, via1_irq, 0, "via1", (void *)via1))
-		pr_err("Couldn't register %s interrupt\n", "via1");
-}
-
-/*
  * Initialize OSS for Nubus access
  */
 
@@ -92,6 +76,34 @@
  * and SCSI; everything else is routed to its own autovector IRQ.
  */
 
+#ifdef CONFIG_GENERIC_HARDIRQS
+static void oss_irq(unsigned int irq, struct irq_desc *desc)
+{
+	int events;
+
+	events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI);
+	if (!events)
+		return;
+
+#ifdef DEBUG_IRQS
+	if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
+		printk("oss_irq: irq %u events = 0x%04X\n", irq,
+			(int) oss->irq_pending);
+	}
+#endif
+	/* FIXME: how do you clear a pending IRQ?    */
+
+	if (events & OSS_IP_SOUND) {
+		oss->irq_pending &= ~OSS_IP_SOUND;
+		/* FIXME: call sound handler */
+	} else if (events & OSS_IP_SCSI) {
+		oss->irq_pending &= ~OSS_IP_SCSI;
+		generic_handle_irq(IRQ_MAC_SCSI);
+	} else {
+		/* FIXME: error check here? */
+	}
+}
+#else
 static irqreturn_t oss_irq(int irq, void *dev_id)
 {
 	int events;
@@ -119,6 +131,7 @@
 	}
 	return IRQ_HANDLED;
 }
+#endif
 
 /*
  * Nubus IRQ handler, OSS style
@@ -126,6 +139,34 @@
  * Unlike the VIA/RBV this is on its own autovector interrupt level.
  */
 
+#ifdef CONFIG_GENERIC_HARDIRQS
+static void oss_nubus_irq(unsigned int irq, struct irq_desc *desc)
+{
+	int events, irq_bit, i;
+
+	events = oss->irq_pending & OSS_IP_NUBUS;
+	if (!events)
+		return;
+
+#ifdef DEBUG_NUBUS_INT
+	if (console_loglevel > 7) {
+		printk("oss_nubus_irq: events = 0x%04X\n", events);
+	}
+#endif
+	/* There are only six slots on the OSS, not seven */
+
+	i = 6;
+	irq_bit = 0x40;
+	do {
+		--i;
+		irq_bit >>= 1;
+		if (events & irq_bit) {
+			oss->irq_pending &= ~irq_bit;
+			generic_handle_irq(NUBUS_SOURCE_BASE + i);
+		}
+	} while(events & (irq_bit - 1));
+}
+#else
 static irqreturn_t oss_nubus_irq(int irq, void *dev_id)
 {
 	int events, irq_bit, i;
@@ -153,6 +194,31 @@
 	} while(events & (irq_bit - 1));
 	return IRQ_HANDLED;
 }
+#endif
+
+/*
+ * Register the OSS and NuBus interrupt dispatchers.
+ */
+
+void __init oss_register_interrupts(void)
+{
+#ifdef CONFIG_GENERIC_HARDIRQS
+	irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_irq);
+	irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq);
+	irq_set_chained_handler(OSS_IRQLEV_SOUND, oss_irq);
+	irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq);
+#else /* !CONFIG_GENERIC_HARDIRQS */
+	if (request_irq(OSS_IRQLEV_SCSI, oss_irq, 0, "scsi", (void *)oss))
+		pr_err("Couldn't register %s interrupt\n", "scsi");
+	if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, 0, "nubus",
+			(void *)oss))
+		pr_err("Couldn't register %s interrupt\n", "nubus");
+	if (request_irq(OSS_IRQLEV_SOUND, oss_irq, 0, "sound", (void *)oss))
+		pr_err("Couldn't register %s interrupt\n", "sound");
+	if (request_irq(OSS_IRQLEV_VIA1, via1_irq, 0, "via1", (void *)via1))
+		pr_err("Couldn't register %s interrupt\n", "via1");
+#endif /* !CONFIG_GENERIC_HARDIRQS */
+}
 
 /*
  * Enable an OSS interrupt