/* * Macintosh interrupts * * General design: * In contrary to the Amiga and Atari platforms, the Mac hardware seems to * exclusively use the autovector interrupts (the 'generic level0-level7' * interrupts with exception vectors 0x19-0x1f). The following interrupt levels * are used: * 1 - VIA1 * - slot 0: one second interrupt * - slot 1: VBlank * - slot 2: ADB data ready (SR full) * - slot 3: ADB data (CB2) * - slot 4: ADB clock (CB1) * - slot 5: timer 2 * - slot 6: timer 1 * - slot 7: status of IRQ; signals 'any enabled int.' * * 2 - VIA2, RBV or OSS * - slot 0: SCSI DRQ * - slot 1: NUBUS IRQ * - slot 3: SCSI IRQ * * 4 - SCC * - subdivided into Channel B and Channel A interrupts * * 6 - Off switch (??) * * 7 - Debug output * * AV Macs only, handled by PSC: * * 3 - MACE ethernet IRQ (DMA complete on level 4) * * 5 - DSP ?? * * Using the autovector irq numbers for Linux/m68k hardware interrupts without * the IRQ_MACHSPEC bit set would interfere with the general m68k interrupt * handling in kernel versions 2.0.x, so the following strategy is used: * * - mac_init_IRQ installs the low-level entry points for the via1 and via2 * exception vectors and the corresponding handlers (C functions); these * entry points just add the machspec bit and call the handlers proper. * (in principle, the C functions can be installed as the exception vectors * directly, as they are hardcoded anyway; that's the current method). * * - via[12]_irq determine what interrupt sources have triggered the interrupt, * and call the corresponding device interrupt handlers. * (currently, via1_irq and via2_irq just call via_irq, passing the via base * address. RBV interrupts are handled by (you guessed it) rbv_irq). * Some interrupt functions want to have the interrupt number passed, so * via_irq and rbv_irq need to generate the 'fake' numbers from scratch. * * - for the request/free/enable/disable business, interrupt sources are * numbered internally (suggestion: keep irq 0-7 unused :-). One bit in the * irq number specifies the via# to use, i.e. via1 interrupts are 8-16, * via2 interrupts 17-32, rbv interrupts ... * The device interrupt table and the irq_enable bitmap is maintained by * the machspec interrupt code; all device drivers should only use these * functions ! * * - For future porting to version 2.1 (and removing of the machspec bit) it * should be sufficient to use the same numbers (everything > 7 is assumed * to be machspec, according to Jes!). * * TODO: * - integrate Nubus interrupts in request/free_irq * * - */ #include #include #include #include #include #include /* for intr_count */ #include #include #include #include #include #include #include "via6522.h" #include /* * Interrupt handler and parameter types */ struct irqhandler { void (*handler)(int, void *, struct pt_regs *); void *dev_id; }; struct irqparam { unsigned long flags; const char *devname; }; struct irqflags { unsigned int disabled; unsigned int pending; }; /* * Array with irq's and their parameter data. */ static struct irqhandler via1_handler[8]; static struct irqhandler via2_handler[8]; static struct irqhandler rbv_handler[8]; static struct irqhandler psc3_handler[8]; static struct irqhandler scc_handler[8]; static struct irqhandler psc5_handler[8]; static struct irqhandler psc6_handler[8]; static struct irqhandler nubus_handler[8]; static struct irqhandler *handler_table[8]; /* * This array hold the rest of parameters of int handlers: type * (slow,fast,prio) and the name of the handler. These values are only * accessed from C */ static struct irqparam via1_param[8]; static struct irqparam via2_param[8]; static struct irqparam rbv_param[8]; static struct irqparam psc3_param[8]; static struct irqparam scc_param[8]; static struct irqparam psc5_param[8]; static struct irqparam psc6_param[8]; static struct irqparam nubus_param[8]; static struct irqparam *param_table[8]; /* * This array holds the 'disabled' and 'pending' software flags maintained * by mac_{enable,disable}_irq and the generic via_irq function. */ static struct irqflags irq_flags[8]; /* * This array holds the pointers to the various VIA or other interrupt * controllers, indexed by interrupt level */ static volatile unsigned char *via_table[8]; /* * Arrays with irq statistics */ static unsigned long via1_irqs[8]; static unsigned long via2_irqs[8]; static unsigned long rbv_irqs[8]; static unsigned long psc3_irqs[8]; static unsigned long scc_irqs[8]; static unsigned long psc5_irqs[8]; static unsigned long psc6_irqs[8]; static unsigned long nubus_irqs[8]; static unsigned long *mac_irqs[8]; /* * Some special nutcases ... */ static unsigned long mac_ide_irqs = 0; static unsigned long nubus_stuck_events = 0; /* * VIA/RBV/OSS/PSC register base pointers */ volatile unsigned char *via2_regp=(volatile unsigned char *)VIA2_BAS; volatile unsigned char *rbv_regp=(volatile unsigned char *)VIA2_BAS_IIci; volatile unsigned char *oss_regp=(volatile unsigned char *)OSS_BAS; volatile unsigned char *psc_regp=(volatile unsigned char *)PSC_BAS; /* * Flags to control via2 / rbv behaviour */ static int via2_is_rbv = 0; static int via2_is_oss = 0; static int rbv_clear = 0; /* fake VIA2 to OSS bit mapping */ static int oss_map[8] = {2, 7, 0, 1, 3, 4, 5}; void oss_irq(int irq, void *dev_id, struct pt_regs *regs); static void oss_do_nubus(int irq, void *dev_id, struct pt_regs *regs); /* PSC ints */ void psc_irq(int irq, void *dev_id, struct pt_regs *regs); /* * PSC hooks */ extern void psc_init(void); /* * console_loglevel determines NMI handler function */ extern int console_loglevel; /* * ADB test hooks */ extern int in_keybinit; void adb_queue_poll(void); /* Defined in entry.S; only increments 'num_spurious' */ asmlinkage void bad_interrupt(void); void nubus_wtf(int slot, void *via, struct pt_regs *regs); void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *regs); void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs); static void via_do_nubus(int slot, void *via, struct pt_regs *regs); /* #define DEBUG_MACINTS */ #define DEBUG_SPURIOUS #define DEBUG_NUBUS_SPURIOUS #define DEBUG_NUBUS_INT /* #define DEBUG_VIA */ #define DEBUG_VIA_NUBUS void mac_init_IRQ(void) { int i; #ifdef DEBUG_MACINTS printk("Mac interrupt stuff initializing ...\n"); #endif via2_regp = (unsigned char *)VIA2_BAS; rbv_regp = (unsigned char *)VIA2_BAS_IIci; /* initialize the hardwired (primary, autovector) IRQs */ /* level 1 IRQ: VIA1, always present */ sys_request_irq(1, via1_irq, IRQ_FLG_LOCK, "via1", via1_irq); /* via2 or rbv?? */ if (macintosh_config->via_type == MAC_VIA_IIci) { /* * A word of caution: the definitions here only affect interrupt * handling, see via6522.c for yet another file to change * base addresses and RBV flags */ /* yes, this is messy - the IIfx deserves a class of his own */ if (macintosh_config->ident == MAC_MODEL_IIFX) { /* no real VIA2, the OSS seems _very_ different */ via2_is_oss = 1; /* IIfx has OSS, at a different base address than RBV */ rbv_regp = (unsigned char *) OSS_BAS; sys_request_irq(2, oss_irq, IRQ_FLG_LOCK, "oss", oss_irq); } else { /* VIA2 is part of the RBV: different base, other offsets */ via2_is_rbv = 1; /* LC III weirdness: IFR seems to behave like VIA2 */ /* FIXME: maybe also for LC II ?? */ if (macintosh_config->ident == MAC_MODEL_LCIII) { rbv_clear = 0x0; } else { rbv_clear = 0x80; } /* level 2 IRQ: RBV/OSS; we only care about RBV for now */ sys_request_irq(2, rbv_irq, IRQ_FLG_LOCK, "rbv", rbv_irq); } } else /* level 2 IRQ: VIA2 */ sys_request_irq(2, via2_irq, IRQ_FLG_LOCK, "via2", via2_irq); /* * level 4 IRQ: SCC - use 'master' interrupt routine that calls the * registered channel-specific interrupts in turn. * Currently, one interrupt per channel is used, solely * to pass the correct async_info as parameter! */ sys_request_irq(4, mac_debug_handler, IRQ_FLG_STD, "INT4", mac_debug_handler); /* level 6 */ sys_request_irq(6, mac_bang, IRQ_FLG_LOCK, "offswitch", mac_bang); /* level 7 (or NMI) : debug stuff */ sys_request_irq(7, mac_nmi_handler, IRQ_FLG_STD, "NMI", mac_nmi_handler); /* initialize the handler tables for VIAs */ for (i = 0; i < 8; i++) { via1_handler[i].handler = mac_default_handler; via1_handler[i].dev_id = NULL; via1_param[i].flags = IRQ_FLG_STD; via1_param[i].devname = NULL; via2_handler[i].handler = mac_default_handler; via2_handler[i].dev_id = NULL; via2_param[i].flags = IRQ_FLG_STD; via2_param[i].devname = NULL; rbv_handler[i].handler = mac_default_handler; rbv_handler[i].dev_id = NULL; rbv_param[i].flags = IRQ_FLG_STD; rbv_param[i].devname = NULL; scc_handler[i].handler = mac_default_handler; scc_handler[i].dev_id = NULL; scc_param[i].flags = IRQ_FLG_STD; scc_param[i].devname = NULL; /* NUBUS interrupts routed through VIA2 slot 2 - special */ nubus_handler[i].handler = nubus_wtf; nubus_handler[i].dev_id = NULL; nubus_param[i].flags = IRQ_FLG_STD; nubus_param[i].devname = NULL; } /* initialize the handler tables (level 1 -> via_handler[0] !!!) */ via_table[0] = via1_regp; handler_table[0] = &via1_handler[0]; param_table[0] = &via1_param[0]; mac_irqs[0] = &via1_irqs[0]; if (via2_is_rbv || via2_is_oss) { via_table[1] = rbv_regp; handler_table[1] = &rbv_handler[0]; param_table[1] = &rbv_param[0]; mac_irqs[1] = &rbv_irqs[0]; } else { via_table[1] = via2_regp; handler_table[1] = &via2_handler[0]; param_table[1] = &via2_param[0]; mac_irqs[1] = &via2_irqs[0]; } via_table[2] = NULL; via_table[3] = NULL; handler_table[2] = &rbv_handler[0]; handler_table[3] = &scc_handler[0]; handler_table[4] = NULL; handler_table[5] = NULL; handler_table[6] = NULL; handler_table[7] = &nubus_handler[0]; param_table[2] = &rbv_param[0]; param_table[3] = &scc_param[0]; param_table[7] = &nubus_param[0]; mac_irqs[2] = &rbv_irqs[0]; mac_irqs[3] = &scc_irqs[0]; mac_irqs[7] = &nubus_irqs[0]; /* * Nubus Macs: turn off the Nubus dispatch interrupt for now */ mac_turnoff_irq(IRQ_MAC_NUBUS); /* * AV Macs: shutup the PSC ints */ if (macintosh_config->ident == MAC_MODEL_C660 || macintosh_config->ident == MAC_MODEL_Q840) { psc_init(); handler_table[2] = &psc3_handler[0]; /* handler_table[3] = &psc4_handler[0]; */ handler_table[4] = &psc5_handler[0]; handler_table[5] = &psc6_handler[0]; param_table[2] = &psc3_param[0]; /* param_table[3] = &psc4_param[0]; */ param_table[4] = &psc5_param[0]; param_table[5] = &psc6_param[0]; mac_irqs[2] = &psc3_irqs[0]; /* mac_irqs[3] = &psc4_irqs[0]; */ mac_irqs[4] = &psc5_irqs[0]; mac_irqs[5] = &psc6_irqs[0]; sys_request_irq(3, psc_irq, IRQ_FLG_STD, "PSC3", psc_irq); sys_request_irq(4, psc_irq, IRQ_FLG_STD, "PSC4", psc_irq); sys_request_irq(5, psc_irq, IRQ_FLG_STD, "PSC5", psc_irq); sys_request_irq(6, psc_irq, IRQ_FLG_STD, "PSC6", psc_irq); } #ifdef DEBUG_MACINTS printk("Mac interrupt init done!\n"); #endif } /* * We have no machine specific interrupts on a macintoy * Yet, we need to register/unregister interrupts ... :-) * Currently unimplemented: Test for valid irq number, chained irqs, * Nubus interrupts (use nubus_request_irq!). */ int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id) { int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); struct irqhandler *via_handler; struct irqparam *via_param; volatile unsigned char *via; #ifdef DEBUG_MACINTS printk ("%s: IRQ %d on VIA%d[%d] requested from %s\n", __FUNCTION__, irq, srcidx+1, irqidx, devname); #endif if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) { printk ("%s: Bad irq type %ld requested from %s\n", __FUNCTION__, flags, devname); return -EINVAL; } /* figure out what VIA is handling this irq */ if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) { /* non-via irqs unimplemented */ printk ("%s: Bad irq source %d on VIA %d requested from %s\n", __FUNCTION__, irq, srcidx, devname); return -EINVAL; } /* figure out if SCC pseudo-irq (redundant ??) */ if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) { /* set specific SCC handler */ scc_handler[irqidx].handler = handler; scc_handler[irqidx].dev_id = dev_id; scc_param[irqidx].flags = flags; scc_param[irqidx].devname = devname; /* and done! */ return 0; } /* * code below: only for VIA irqs currently * add similar hack for Nubus pseudo-irq here - hide nubus_request_irq */ via = (volatile unsigned char *) via_table[srcidx]; if (!via) return -EINVAL; via_handler = handler_table[srcidx]; via_param = param_table[srcidx]; /* check for conflicts or possible replacement */ /* set the handler - no chained irqs yet !! */ via_handler[irqidx].handler = handler; via_handler[irqidx].dev_id = dev_id; via_param[irqidx].flags = flags; via_param[irqidx].devname = devname; /* and turn it on ... careful, that's VIA only ... */ if (srcidx == SRC_VIA2 && via2_is_rbv) via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); else if (srcidx == SRC_VIA2 && via2_is_oss) via_write(oss_regp, oss_map[irqidx]+8, 2); else via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); if (irq == IRQ_IDX(IRQ_MAC_SCSI)) { /* * Set vPCR for SCSI interrupts. (what about RBV here?) * 980429 MS: RBV is ok, OSS seems to be different */ if (!via2_is_oss) if (macintosh_config->scsi_type == MAC_SCSI_OLD) { /* CB2 (IRQ) indep. interrupt input, positive edge */ /* CA2 (DRQ) indep. interrupt input, positive edge */ via_write(via, vPCR, 0x66); } else { /* CB2 (IRQ) indep. interrupt input, negative edge */ /* CA2 (DRQ) indep. interrupt input, negative edge */ via_write(via, vPCR, 0x22); } #if 0 else /* CB2 (IRQ) indep. interrupt input, negative edge */ /* CA2 (DRQ) indep. interrupt input, negative edge */ via_write(via, vPCR, 0x22); #endif } return 0; } void mac_free_irq (unsigned int irq, void *dev_id) { unsigned long flags; int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); struct irqhandler *via_handler; struct irqparam *via_param; volatile unsigned char *via; #ifdef DEBUG_MACINTS printk ("%s: IRQ %d on VIA%d[%d] freed\n", __FUNCTION__, irq, srcidx+1, irqidx); #endif /* figure out what VIA is handling this irq */ if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) { /* non-via irqs unimplemented */ return; } save_flags(flags); cli(); /* figure out if SCC pseudo-irq */ if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) { /* clear specific SCC handler */ scc_handler[irqidx].handler = mac_default_handler; scc_handler[irqidx].dev_id = NULL; scc_param[irqidx].flags = IRQ_FLG_STD; scc_param[irqidx].devname = NULL; /* and done! */ restore_flags(flags); return; } via = (volatile unsigned char *) via_table[srcidx]; via_handler = handler_table[srcidx]; via_param = param_table[srcidx]; if ( !via || (via_handler[irqidx].dev_id != dev_id) ) { restore_flags(flags); goto not_found; } /* clear the handler - no chained irqs yet !! */ via_handler[irqidx].handler = mac_default_handler; via_handler[irqidx].dev_id = NULL; via_param[irqidx].flags = IRQ_FLG_STD; via_param[irqidx].devname = NULL; /* and turn it off */ if (srcidx == SRC_VIA2 && via2_is_rbv) via_write(via, rIER, (via_read(via, rIER)&(1<>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); irq_flags[srcidx].disabled &= ~(1<>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); irq_flags[srcidx].disabled |= (1<>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); volatile unsigned char *via; via = (volatile unsigned char *) via_table[srcidx]; if (!via) return; if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); else if (srcidx == SRC_VIA2 && via2_is_oss) /* OSS */ via_write(oss_regp, oss_map[irqidx]+8, 2); else if (srcidx > SRC_VIA2) /* hope AVs have VIA2 */ via_write(via, (0x104 + 0x10*srcidx), via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx))); else /* VIA1+2 */ via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); } void mac_turnoff_irq( unsigned int irq ) { int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); volatile unsigned char *via; via = (volatile unsigned char *) via_table[srcidx]; if (!via) return; if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ via_write(via, rIER, (via_read(via, rIER)&(1< SRC_VIA2) via_write(via, (0x104 + 0x10*srcidx), via_read(via, (0x104 + 0x10*srcidx))|(1<<(irqidx))); else /* VIA1+2 */ via_write(via, vIER, (via_read(via, vIER)&(1<>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); irq_flags[srcidx].pending &= ~(1<>3) - 1; int irqidx = (irq & IRQ_IDX_MASK); pending = irq_flags[srcidx].pending & (1< SRC_VIA2) pending |= via_read(via, (0x100 + 0x10*srcidx))&(1<>3) - 1; irqidx = (i & IRQ_IDX_MASK); /* * Not present: skip */ if (mac_irqs[srcidx] == NULL) continue; /* * never used by VIAs, unused by others so far, counts * the magic 'nothing pending' cases ... */ if (irqidx == 7 && mac_irqs[srcidx][irqidx]) { len += sprintf(buf+len, "Level %01d: %10lu (spurious) \n", srcidx, mac_irqs[srcidx][irqidx]); continue; } /* * Nothing registered for this IPL: skip */ if (handler_table[srcidx] == NULL) continue; /* * No handler installed: skip */ if (handler_table[srcidx][irqidx].handler == mac_default_handler || handler_table[srcidx][irqidx].handler == nubus_wtf) continue; if (i < VIA2_SOURCE_BASE) len += sprintf(buf+len, "via1 %01d: %10lu ", irqidx, mac_irqs[srcidx][irqidx]); else if (i < RBV_SOURCE_BASE) len += sprintf(buf+len, "via2 %01d: %10lu ", irqidx, mac_irqs[srcidx][irqidx]); else if (i < MAC_SCC_SOURCE_BASE) len += sprintf(buf+len, "rbv %01d: %10lu ", irqidx, mac_irqs[srcidx][irqidx]); else if (i < NUBUS_SOURCE_BASE) len += sprintf(buf+len, "scc %01d: %10lu ", irqidx, mac_irqs[srcidx][irqidx]); else /* Nubus */ len += sprintf(buf+len, "nubus %01d: %10lu ", irqidx, mac_irqs[srcidx][irqidx]); len += sprintf(buf+len, "%s\n", param_table[srcidx][irqidx].devname); } if (num_spurious) len += sprintf(buf+len, "spurio.: %10u\n", num_spurious); /* * XXX Fixme: Nubus sources are never logged above ... */ len += sprintf(buf+len, "Nubus interrupts:\n"); for (i = 0; i < 7; i++) { if (nubus_handler[i].handler == nubus_wtf) continue; len += sprintf(buf+len, "nubus %01X: %10lu ", i+9, nubus_irqs[i]); len += sprintf(buf+len, "%s\n", nubus_param[i].devname); } len += sprintf(buf+len, "nubus spurious ints: %10lu\n", nubus_irqs[7]); len += sprintf(buf+len, "nubus stuck events : %10lu\n", nubus_stuck_events); #ifdef CONFIG_BLK_DEV_IDE len += sprintf(buf+len, "nubus/IDE interrupt: %10lu\n", mac_ide_irqs); #endif return len; } void via_scsi_clear(void) { volatile unsigned char deep_magic; if (via2_is_rbv) { via_write(rbv_regp, rIFR, (1<<3)|(1<<0)|0x80); deep_magic = via_read(rbv_regp, rBufB); } else if (via2_is_oss) { /* nothing */ /* via_write(oss_regp, 9, 0) */; } else deep_magic = via_read(via2_regp, vBufB); mac_enable_irq( IRQ_IDX(IRQ_MAC_SCSI) ); } void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG_SPURIOUS if (console_loglevel > 6) printk("Unexpected IRQ %d on device %p\n", irq, dev_id); #endif } static int num_debug[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs) { if (num_debug[irq] < 10) { printk("DEBUG: Unexpected IRQ %d\n", irq); num_debug[irq]++; } } void scsi_mac_debug(void); void scsi_mac_polled(void); static int in_nmi = 0; static volatile int nmi_hold = 0; void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) { int i; /* * generate debug output on NMI switch if 'debug' kernel option given * (only works with Penguin!) */ in_nmi++; #if 0 scsi_mac_debug(); printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); #endif for (i=0; i<100; i++) udelay(1000); if (in_nmi == 1) { nmi_hold = 1; printk("... pausing, press NMI to resume ..."); } else { printk(" ok!\n"); nmi_hold = 0; } barrier(); while (nmi_hold == 1) udelay(1000); #if 0 scsi_mac_polled(); #endif if ( console_loglevel >= 8 ) { #if 0 show_state(); printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", fp->d0, fp->d1, fp->d2, fp->d3); printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", fp->d4, fp->d5, fp->a0, fp->a1); if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) printk("Corrupted stack page\n"); printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, current->kernel_stack_page); if (intr_count == 1) dump_stack((struct frame *)fp); #else /* printk("NMI "); */ #endif } in_nmi--; } /* * Unexpected via interrupt */ void via_wtf(int slot, void *via, struct pt_regs *regs) { #ifdef DEBUG_SPURIOUS if (console_loglevel > 6) printk("Unexpected nubus event %d on via %p\n",slot,via); #endif } /* * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's * via6522.c :-), disable/pending masks added. * The int *viaidx etc. is just to keep the prototype happy ... */ static void via_irq(unsigned char *via, int *viaidx, struct pt_regs *regs) { unsigned char events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F; int i; int ct = 0; struct irqhandler *via_handler = handler_table[*viaidx]; struct irqparam *via_param = param_table[*viaidx]; unsigned long *via_irqs = mac_irqs[*viaidx]; /* to be changed, possibly: for each non'masked', enabled IRQ, read * flag bit, ack and call handler ... * Currently: all pending irqs ack'ed en bloc. * If ack for masked IRQ required: keep 'pending' info separate. */ /* shouldn't we disable interrupts here ?? */ /* * Shouldnt happen */ if(events==0) { #ifdef DEBUG_VIA /* should go away; mostly missing timer ticks and ADB events */ printk("via%d_irq: nothing pending, flags %x mask %x!\n", *viaidx + 1, via_read(via, vIFR), via_read(via,vIER)); #endif via_irqs[7]++; return; } #ifdef DEBUG_VIA /* * limited verbosity for VIA interrupts */ #if 0 if ( (*viaidx == 0 && events != 1<<6) /* timer int */ || (*viaidx == 1 && events != 1<<3) ) /* SCSI IRQ */ #else if ( *viaidx == 0 && (events & 1<<2) ) #endif printk("via_irq: irq %d events %x !\n", (*viaidx)+1, events); #endif do { /* * Clear the pending flag */ via_write(via, vIFR, events); /* * Now see what bits are raised */ for(i=0;i<7;i++) { /* determine machspec. irq no. */ int irq = ((*viaidx)+1)* 8 + i; /* call corresponding handlers */ if (events&(1< mark pending */ irq_flags[*viaidx].pending |= (1< call handler */ (via_handler[i].handler)(irq, via, regs); } } /* and call handlers for pending irqs - first ?? */ if ( (irq_flags[*viaidx].pending & (1<8) { #ifdef DEBUG_VIA printk("via%d: stuck events %x\n", (*viaidx)+1, events); #endif break; } } while(events); #if 0 scsi_mac_polled(); #endif } /* * Caution: the following stuff is called from process_int as _autovector_ * system interrupts. So irq is always in the range 0-7 :-( and the selection * of the appropriate VIA is up to the irq handler here based on the autovec * irq number. There's no information whatsoever about which source on the VIA * triggered the int - and that's what the machspec irq no's are about. * Broken design :-(((( */ /* * System interrupts */ void via1_irq(int irq, void *dev_id, struct pt_regs *regs) { int srcidx = IRQ_IDX(irq) - 1; via_irq((unsigned char *)via1_regp, &srcidx, regs); } /* * Nubus / SCSI interrupts, VIA style (could be wrapped into via1_irq or * via_irq directly by selecting the regp based on the irq!) */ void via2_irq(int irq, void *dev_id, struct pt_regs *regs) { int srcidx = IRQ_IDX(irq) - 1; via_irq((unsigned char *)via2_regp, &srcidx, regs); } /* * Nubus / SCSI interrupts; RBV style * The RBV is different. RBV appears to stand for randomly broken * VIA (or even real broken VIA). */ void rbv_irq(int irq, void *dev_id, struct pt_regs *regs) { int srcidx = IRQ_IDX(irq) - 1; /* MUST be 1 !! */ volatile unsigned char *via = rbv_regp; unsigned char events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F; int i; int ct = 0; struct irqhandler *via_handler = handler_table[srcidx]; struct irqparam *via_param = param_table[srcidx]; /* shouldn't we disable interrupts here ?? */ /* * Shouldnt happen */ if(events==0) { #ifdef DEBUG_VIA printk("rbv_irq: nothing pending, flags %x mask %x!\n", via_read(via, rIFR), via_read(via,rIER)); #endif rbv_irqs[7]++; return; } #ifdef DEBUG_VIA /* * limited verbosity for RBV interrupts (add more if needed) */ if ( srcidx == 1 && events != 1<<3 ) /* SCSI IRQ */ printk("rbv_irq: irq %d (%d) events %x !\n", irq, srcidx+1, events); #endif /* to be changed, possibly: for each non'masked', enabled IRQ, read * flag bit, ack and call handler ... * Currently: all pending irqs ack'ed en bloc. * If ack for masked IRQ required: keep 'pending' info separate. */ do { /* * Clear the pending flag */ via_write(via, rIFR, events | rbv_clear); /* * Now see what bits are raised */ for(i=0;i<7;i++) { /* determine machspec. irq no. */ int irq = (srcidx+1)* 8 + i; /* call corresponding handlers */ if (events&(1< mark pending */ irq_flags[srcidx].pending |= (1< call handler */ (via_handler[i].handler)(irq, via, regs); } } /* and call handlers for pending irqs - first ?? */ if ( (irq_flags[srcidx].pending & (1<8) { printk("rbv: stuck events %x\n",events); for(i=0;i<7;i++) { if(events&(1< mark pending */ irq_flags[srcidx].pending |= (1< call handler */ (via_handler[i].handler)(irq, via, regs); } } /* and call handlers for pending irqs - first ?? */ if ( (irq_flags[srcidx].pending & (1<8) { printk("oss: stuck events %x\n",events); for(i=0;i<7;i++) { if(events&(1< 6) printk("Unexpected interrupt on nubus slot %d\n",slot); #endif } /* * SCC master interrupt handler; sole purpose: pass the registered * async struct to the SCC handler proper. */ void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs) { int i; /* 1+2: compatibility with PSC ! */ for (i = 1; i < 3; i++) /* currently only these two used */ if (scc_handler[i].handler != mac_default_handler) { (scc_handler[i].handler)(i, scc_handler[i].dev_id, regs); scc_irqs[i]++; } } /* * PSC interrupt handler */ void psc_irq(int irq, void *dev_id, struct pt_regs *regs) { int srcidx = IRQ_IDX(irq) - 1; volatile unsigned char *via = psc_regp; unsigned int pIFR = 0x100 + 0x10*srcidx; unsigned int pIER = 0x104 + 0x10*srcidx; unsigned char events=(via_read(via, pIFR)&via_read(via,pIER))&0xF; int i; int ct = 0; struct irqhandler *via_handler = handler_table[srcidx]; struct irqparam *via_param = param_table[srcidx]; /* shouldn't we disable interrupts here ?? */ /* * Shouldnt happen */ if(events==0) { #ifdef DEBUG_VIA printk("psc_irq: nothing pending, flags %x mask %x!\n", via_read(via, pIFR), via_read(via,pIER)); #endif mac_irqs[srcidx][7]++; return; } #ifdef DEBUG_VIA /* * limited verbosity for PSC interrupts (add more if needed) */ if ( srcidx == 1 && events != 1<<3 && events != 1<<1 ) /* SCSI IRQ */ printk("psc_irq: irq %d srcidx+1 %d events %x !\n", irq, srcidx+1, events); #endif /* to be changed, possibly: for each non'masked', enabled IRQ, read * flag bit, ack and call handler ... * Currently: all pending irqs ack'ed en bloc. * If ack for masked IRQ required: keep 'pending' info separate. */ do { /* * Clear the pending flag */ /* via_write(via, pIFR, events); */ /* * Now see what bits are raised */ for(i=0;i<7;i++) { /* determine machspec. irq no. */ int irq = (srcidx+1)* 8 + i; /* call corresponding handlers */ if (events&(1< mark pending */ irq_flags[srcidx].pending |= (1< call handler */ (via_handler[i].handler)(irq, via, regs); } } /* and call handlers for pending irqs - first ?? */ if ( (irq_flags[srcidx].pending & (1<8) { printk("psc: stuck events %x\n",events); for(i=0;i<7;i++) { if(events&(1< 5) printk("nubus_irq: nothing pending, map %x mask %x active %x\n", allints, nubus_active, map); #endif nubus_irqs[7]++; } /* clear it */ if (allints) if (via2_is_rbv) via_write(rbv_regp, rIFR, 0x02); else via_write(via2_regp, vIFR, 0x02); break; } #ifdef DEBUG_VIA_NUBUS if (console_loglevel > 6) printk("nubus_irq: map %x mask %x active %x\n", allints, nubus_active, map); #endif #ifdef CONFIG_BLK_DEV_MAC_IDE if (mac_ide_intr_hook && ide_pending) { mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); mac_ide_irqs++; } #endif if(ct++>2) { if (console_loglevel > 5) printk("nubus stuck events - %x/%x/%x ide %x\n", allints, nubus_active, map, ide_pending); nubus_stuck_events++; return; } for(i=0;i<7;i++) { if(map&(1<2) { #if 0 printk("nubus stuck events - %d/%d\n", map, nubus_active); #endif return; } for(i=0;i<7;i++) { if(map&(1<