m68k: Mac nubus IRQ fixes (plan E)
[linux-2.6.git] / arch / m68k / mac / via.c
index 0c1cc45c570d0d4923939dfaa9030ef1e3f9dbcb..83a3bb0c032521abba6cfdd4026aa10705d7bdbc 100644 (file)
@@ -64,7 +64,19 @@ static int gIER,gIFR,gBufA,gBufB;
 #define MAC_CLOCK_LOW          (MAC_CLOCK_TICK&0xFF)
 #define MAC_CLOCK_HIGH         (MAC_CLOCK_TICK>>8)
 
-static int  nubus_active;
+/* To disable a NuBus slot on Quadras we make the slot IRQ lines outputs, set
+ * high. On RBV we just use the slot interrupt enable register. On Macs with
+ * genuine VIA chips we must use nubus_disabled to keep track of disabled slot
+ * interrupts. When any slot IRQ is disabled we mask the (edge triggered) CA1
+ * or "SLOTS" interrupt. When no slot is disabled, we unmask the CA1 interrupt.
+ * So, on genuine VIAs, having more than one NuBus IRQ can mean trouble,
+ * because closing one of those drivers can mask all of the NuBus interrupts.
+ * Also, since we can't mask the unregistered slot IRQs on genuine VIAs, it's
+ * possible to get interrupts from cards that MacOS or the ROM has configured
+ * but we have not. FWIW, "Designing Cards and Drivers for Macintosh II and
+ * Macintosh SE", page 9-8, says, a slot IRQ with no driver would crash MacOS.
+ */
+static u8 nubus_disabled;
 
 void via_debug_dump(void);
 irqreturn_t via1_irq(int, void *);
@@ -383,9 +395,6 @@ int via_get_cache_disable(void)
 
 void __init via_nubus_init(void)
 {
-       /* don't set nubus_active = 0 here, it kills the Baboon */
-       /* interrupt that we've already registered.             */
-
        /* unlock nubus transactions */
 
        if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
@@ -399,28 +408,35 @@ void __init via_nubus_init(void)
                via2[gBufB] |= 0x02;
        }
 
-       /* disable nubus slot interrupts. */
-       if (rbv_present) {
+       /* Disable all the slot interrupts (where possible). */
+
+       switch (macintosh_config->via_type) {
+       case MAC_VIA_II:
+               /* Just make the port A lines inputs. */
+               switch(macintosh_config->ident) {
+               case MAC_MODEL_II:
+               case MAC_MODEL_IIX:
+               case MAC_MODEL_IICX:
+               case MAC_MODEL_SE30:
+                       /* The top two bits are RAM size outputs. */
+                       via2[vDirA] &= 0xC0;
+                       break;
+               default:
+                       via2[vDirA] &= 0x80;
+               }
+               break;
+       case MAC_VIA_IIci:
+               /* RBV. Disable all the slot interrupts. SIER works like IER. */
                via2[rSIER] = 0x7F;
-               via2[rSIER] = nubus_active | 0x80;
-       } else {
-               /* These are ADB bits on PMU */
+               break;
+       case MAC_VIA_QUADRA:
+               /* Disable the inactive slot interrupts by making those lines outputs. */
                if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
-                  (macintosh_config->adb_type != MAC_ADB_PB2)) {
-                       switch(macintosh_config->ident)
-                       {
-                               case MAC_MODEL_II:
-                               case MAC_MODEL_IIX:
-                               case MAC_MODEL_IICX:
-                               case MAC_MODEL_SE30:
-                                       via2[vBufA] |= 0x3F;
-                                       via2[vDirA] = ~nubus_active | 0xc0;
-                                       break;
-                               default:
-                                       via2[vBufA] = 0xFF;
-                                       via2[vDirA] = ~nubus_active;
-                       }
+                   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+                       via2[vBufA] |= 0x7F;
+                       via2[vDirA] |= 0x7F;
                }
+               break;
        }
 }
 
@@ -489,7 +505,8 @@ irqreturn_t via2_irq(int irq, void *dev_id)
                        via2[gIER] = irq_bit;
                        via2[gIFR] = irq_bit | rbv_clear;
                        m68k_handle_int(irq_num);
-                       via2[gIER] = irq_bit | 0x80;
+                       if (irq_num != IRQ_MAC_NUBUS || nubus_disabled == 0)
+                               via2[gIER] = irq_bit | 0x80;
                }
                ++irq_num;
                irq_bit <<= 1;
@@ -511,7 +528,7 @@ irqreturn_t via_nubus_irq(int irq, void *dev_id)
        if (rbv_present)
                events &= via2[rSIER];
        else
-               events &= nubus_active;
+               events &= ~via2[vDirA];
        if (!events)
                return IRQ_NONE;
 
@@ -533,7 +550,7 @@ irqreturn_t via_nubus_irq(int irq, void *dev_id)
                if (rbv_present)
                        events &= via2[rSIER];
                else
-                       events &= nubus_active;
+                       events &= ~via2[vDirA];
        } while (events);
        return IRQ_HANDLED;
 }
@@ -541,38 +558,38 @@ irqreturn_t via_nubus_irq(int irq, void *dev_id)
 void via_irq_enable(int irq) {
        int irq_src     = IRQ_SRC(irq);
        int irq_idx     = IRQ_IDX(irq);
-       int irq_bit     = 1 << irq_idx;
 
 #ifdef DEBUG_IRQUSE
        printk(KERN_DEBUG "via_irq_enable(%d)\n", irq);
 #endif
 
        if (irq_src == 1) {
-               via1[vIER] = irq_bit | 0x80;
+               via1[vIER] = IER_SET_BIT(irq_idx);
        } else if (irq_src == 2) {
-               via2[gIER] = irq_bit | 0x80;
+               if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)
+                       via2[gIER] = IER_SET_BIT(irq_idx);
        } else if (irq_src == 7) {
-               nubus_active |= irq_bit;
-               if (rbv_present) {
-                       /* enable the slot interrupt. SIER works like IER. */
+               switch (macintosh_config->via_type) {
+               case MAC_VIA_II:
+                       nubus_disabled &= ~(1 << irq_idx);
+                       /* Enable the CA1 interrupt when no slot is disabled. */
+                       if (!nubus_disabled)
+                               via2[gIER] = IER_SET_BIT(1);
+                       break;
+               case MAC_VIA_IIci:
+                       /* On RBV, enable the slot interrupt.
+                        * SIER works like IER.
+                        */
                        via2[rSIER] = IER_SET_BIT(irq_idx);
-               } else {
-                       /* Make sure the bit is an input, to enable the irq */
-                       /* But not on PowerBooks, that's ADB... */
+                       break;
+               case MAC_VIA_QUADRA:
+                       /* Make the port A line an input to enable the slot irq.
+                        * But not on PowerBooks, that's ADB.
+                        */
                        if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
-                          (macintosh_config->adb_type != MAC_ADB_PB2)) {
-                               switch(macintosh_config->ident)
-                               {
-                                       case MAC_MODEL_II:
-                                       case MAC_MODEL_IIX:
-                                       case MAC_MODEL_IICX:
-                                       case MAC_MODEL_SE30:
-                                               via2[vDirA] &= (~irq_bit | 0xc0);
-                                               break;
-                                       default:
-                                               via2[vDirA] &= ~irq_bit;
-                               }
-                       }
+                           (macintosh_config->adb_type != MAC_ADB_PB2))
+                               via2[vDirA] &= ~(1 << irq_idx);
+                       break;
                }
        }
 }
@@ -580,29 +597,31 @@ void via_irq_enable(int irq) {
 void via_irq_disable(int irq) {
        int irq_src     = IRQ_SRC(irq);
        int irq_idx     = IRQ_IDX(irq);
-       int irq_bit     = 1 << irq_idx;
 
 #ifdef DEBUG_IRQUSE
        printk(KERN_DEBUG "via_irq_disable(%d)\n", irq);
 #endif
 
        if (irq_src == 1) {
-               via1[vIER] = irq_bit & 0x7F;
+               via1[vIER] = IER_CLR_BIT(irq_idx);
        } else if (irq_src == 2) {
-               via2[gIER] = irq_bit & 0x7F;
+               via2[gIER] = IER_CLR_BIT(irq_idx);
        } else if (irq_src == 7) {
-               if (rbv_present) {
-                       /* disable the slot interrupt.  SIER works like IER. */
+               switch (macintosh_config->via_type) {
+               case MAC_VIA_II:
+                       nubus_disabled |= 1 << irq_idx;
+                       if (nubus_disabled)
+                               via2[gIER] = IER_CLR_BIT(1);
+                       break;
+               case MAC_VIA_IIci:
                        via2[rSIER] = IER_CLR_BIT(irq_idx);
-               } else {
-                       /* disable the nubus irq by changing dir to output */
-                       /* except on PMU */
+                       break;
+               case MAC_VIA_QUADRA:
                        if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
-                          (macintosh_config->adb_type != MAC_ADB_PB2)) {
-                               via2[vDirA] |= irq_bit;
-                       }
+                           (macintosh_config->adb_type != MAC_ADB_PB2))
+                               via2[vDirA] |= 1 << irq_idx;
+                       break;
                }
-               nubus_active &= ~irq_bit;
        }
 }
 
@@ -638,7 +657,7 @@ int via_irq_pending(int irq)
        } else if (irq_src == 2) {
                return via2[gIFR] & irq_bit;
        } else if (irq_src == 7) {
-               /* FIXME: this can't work while a slot irq is disabled! */
+               /* Always 0 for MAC_VIA_QUADRA if the slot irq is disabled. */
                return ~via2[gBufA] & irq_bit;
        }
        return 0;