m68k/atari: EtherNAT - add interrupt chip definition for CPLD interrupts
Michael Schmitz [Sat, 6 Apr 2013 00:26:43 +0000 (13:26 +1300)]
Add a dedicated interrupt chip definition for the EtherNAT CPLD interrupts.
SMC91C111 and ISP1160 chips have separate interrupts that can be enabled
and disabled in a CPLD register at offset 0x23 from the card base.

Note the CPLD interrupt control register is mapped on demand, whenever any
interrupt enable/disable action is requested. The EtherNAT USB driver still
needs interrupts disabled around reset and start actions.
In particular, we cannot entirely rely on the irq_startup being called
first.

The smc91x and isp116x-hcd drivers will use this feature.

Signed-off-by: Michael Schmitz <schmitz@debian.org>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>

arch/m68k/atari/ataints.c

index f699c82..20cde4e 100644 (file)
@@ -49,6 +49,7 @@
 #include <asm/atari_stdma.h>
 #include <asm/irq.h>
 #include <asm/entry.h>
+#include <asm/io.h>
 
 
 /*
@@ -177,6 +178,80 @@ static struct irq_chip atari_mfptimer_chip = {
        .irq_disable    = atari_mfptimer_disable,
 };
 
+
+/*
+ * EtherNAT CPLD interrupt handling
+ * CPLD interrupt register is at phys. 0x80000023
+ * Need this mapped in at interrupt startup time
+ * Possibly need this mapped on demand anyway -
+ * EtherNAT USB driver needs to disable IRQ before
+ * startup!
+ */
+
+static unsigned char *enat_cpld;
+
+static unsigned int atari_ethernat_startup(struct irq_data *data)
+{
+       int enat_num = 140 - data->irq + 1;
+
+       m68k_irq_startup(data);
+       /*
+       * map CPLD interrupt register
+       */
+       if (!enat_cpld)
+               enat_cpld = (unsigned char *)ioremap((ATARI_ETHERNAT_PHYS_ADDR+0x23), 0x2);
+       /*
+        * do _not_ enable the USB chip interrupt here - causes interrupt storm
+        * and triggers dead interrupt watchdog
+        * Need to reset the USB chip to a sane state in early startup before
+        * removing this hack
+        */
+       if (enat_num == 1)
+               *enat_cpld |= 1 << enat_num;
+
+       return 0;
+}
+
+static void atari_ethernat_enable(struct irq_data *data)
+{
+       int enat_num = 140 - data->irq + 1;
+       /*
+       * map CPLD interrupt register
+       */
+       if (!enat_cpld)
+               enat_cpld = (unsigned char *)ioremap((ATARI_ETHERNAT_PHYS_ADDR+0x23), 0x2);
+       *enat_cpld |= 1 << enat_num;
+}
+
+static void atari_ethernat_disable(struct irq_data *data)
+{
+       int enat_num = 140 - data->irq + 1;
+       /*
+       * map CPLD interrupt register
+       */
+       if (!enat_cpld)
+               enat_cpld = (unsigned char *)ioremap((ATARI_ETHERNAT_PHYS_ADDR+0x23), 0x2);
+       *enat_cpld &= ~(1 << enat_num);
+}
+
+static void atari_ethernat_shutdown(struct irq_data *data)
+{
+       int enat_num = 140 - data->irq + 1;
+       if (enat_cpld) {
+               *enat_cpld &= ~(1 << enat_num);
+               iounmap(enat_cpld);
+               enat_cpld = NULL;
+       }
+}
+
+static struct irq_chip atari_ethernat_chip = {
+       .name           = "ethernat",
+       .irq_startup    = atari_ethernat_startup,
+       .irq_shutdown   = atari_ethernat_shutdown,
+       .irq_enable     = atari_ethernat_enable,
+       .irq_disable    = atari_ethernat_disable,
+};
+
 /*
  * void atari_init_IRQ (void)
  *
@@ -268,6 +343,13 @@ void __init atari_init_IRQ(void)
        if (request_irq(IRQ_MFP_TIMD, mfptimer_handler, IRQF_SHARED,
                        stmfp_base.name, &stmfp_base))
                pr_err("Couldn't register %s interrupt\n", stmfp_base.name);
+
+       /*
+        * EtherNAT ethernet / USB interrupt handlers
+        */
+
+       m68k_setup_irq_controller(&atari_ethernat_chip, handle_simple_irq,
+                                 139, 2);
 }