11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * OSS handling 31da177e4SLinus Torvalds * Written by Joshua M. Thompson (funaho@jurai.org) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This chip is used in the IIfx in place of VIA #2. It acts like a fancy 71da177e4SLinus Torvalds * VIA chip with prorammable interrupt levels. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some 101da177e4SLinus Torvalds * recent insights into OSS operational details. 110c79cf6aSSimon Arlott * 990610 (jmt) - Now taking full advantage of the OSS. Interrupts are mapped 121da177e4SLinus Torvalds * to mostly match the A/UX interrupt scheme supported on the 131da177e4SLinus Torvalds * VIA side. Also added support for enabling the ISM irq again 141da177e4SLinus Torvalds * since we now have a functional IOP manager. 151da177e4SLinus Torvalds */ 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include <linux/types.h> 181da177e4SLinus Torvalds #include <linux/kernel.h> 191da177e4SLinus Torvalds #include <linux/mm.h> 201da177e4SLinus Torvalds #include <linux/delay.h> 211da177e4SLinus Torvalds #include <linux/init.h> 22ddc7fd25SGeert Uytterhoeven #ifdef CONFIG_GENERIC_HARDIRQS 23ddc7fd25SGeert Uytterhoeven #include <linux/irq.h> 24ddc7fd25SGeert Uytterhoeven #endif 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds #include <asm/bootinfo.h> 271da177e4SLinus Torvalds #include <asm/macintosh.h> 281da177e4SLinus Torvalds #include <asm/macints.h> 291da177e4SLinus Torvalds #include <asm/mac_via.h> 301da177e4SLinus Torvalds #include <asm/mac_oss.h> 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds int oss_present; 331da177e4SLinus Torvalds volatile struct mac_oss *oss; 341da177e4SLinus Torvalds 35*9145db56SGeert Uytterhoeven #ifdef CONFIG_GENERIC_HARDIRQS 36*9145db56SGeert Uytterhoeven extern void via1_irq(unsigned int irq, struct irq_desc *desc); 37*9145db56SGeert Uytterhoeven #else 382850bc27SAl Viro extern irqreturn_t via1_irq(int, void *); 39*9145db56SGeert Uytterhoeven #endif 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds /* 421da177e4SLinus Torvalds * Initialize the OSS 431da177e4SLinus Torvalds * 441da177e4SLinus Torvalds * The OSS "detection" code is actually in via_init() which is always called 451da177e4SLinus Torvalds * before us. Thus we can count on oss_present being valid on entry. 461da177e4SLinus Torvalds */ 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds void __init oss_init(void) 491da177e4SLinus Torvalds { 501da177e4SLinus Torvalds int i; 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds if (!oss_present) return; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds oss = (struct mac_oss *) OSS_BASE; 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds /* Disable all interrupts. Unlike a VIA it looks like we */ 571da177e4SLinus Torvalds /* do this by setting the source's interrupt level to zero. */ 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds for (i = 0; i <= OSS_NUM_SOURCES; i++) { 601da177e4SLinus Torvalds oss->irq_level[i] = OSS_IRQLEV_DISABLED; 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds /* If we disable VIA1 here, we never really handle it... */ 631da177e4SLinus Torvalds oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds /* 671da177e4SLinus Torvalds * Initialize OSS for Nubus access 681da177e4SLinus Torvalds */ 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds void __init oss_nubus_init(void) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /* 751da177e4SLinus Torvalds * Handle miscellaneous OSS interrupts. Right now that's just sound 761da177e4SLinus Torvalds * and SCSI; everything else is routed to its own autovector IRQ. 771da177e4SLinus Torvalds */ 781da177e4SLinus Torvalds 79*9145db56SGeert Uytterhoeven #ifdef CONFIG_GENERIC_HARDIRQS 80*9145db56SGeert Uytterhoeven static void oss_irq(unsigned int irq, struct irq_desc *desc) 81*9145db56SGeert Uytterhoeven { 82*9145db56SGeert Uytterhoeven int events; 83*9145db56SGeert Uytterhoeven 84*9145db56SGeert Uytterhoeven events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI); 85*9145db56SGeert Uytterhoeven if (!events) 86*9145db56SGeert Uytterhoeven return; 87*9145db56SGeert Uytterhoeven 88*9145db56SGeert Uytterhoeven #ifdef DEBUG_IRQS 89*9145db56SGeert Uytterhoeven if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { 90*9145db56SGeert Uytterhoeven printk("oss_irq: irq %u events = 0x%04X\n", irq, 91*9145db56SGeert Uytterhoeven (int) oss->irq_pending); 92*9145db56SGeert Uytterhoeven } 93*9145db56SGeert Uytterhoeven #endif 94*9145db56SGeert Uytterhoeven /* FIXME: how do you clear a pending IRQ? */ 95*9145db56SGeert Uytterhoeven 96*9145db56SGeert Uytterhoeven if (events & OSS_IP_SOUND) { 97*9145db56SGeert Uytterhoeven oss->irq_pending &= ~OSS_IP_SOUND; 98*9145db56SGeert Uytterhoeven /* FIXME: call sound handler */ 99*9145db56SGeert Uytterhoeven } else if (events & OSS_IP_SCSI) { 100*9145db56SGeert Uytterhoeven oss->irq_pending &= ~OSS_IP_SCSI; 101*9145db56SGeert Uytterhoeven generic_handle_irq(IRQ_MAC_SCSI); 102*9145db56SGeert Uytterhoeven } else { 103*9145db56SGeert Uytterhoeven /* FIXME: error check here? */ 104*9145db56SGeert Uytterhoeven } 105*9145db56SGeert Uytterhoeven } 106*9145db56SGeert Uytterhoeven #else 1078dfbdf4aSAdrian Bunk static irqreturn_t oss_irq(int irq, void *dev_id) 1081da177e4SLinus Torvalds { 1091da177e4SLinus Torvalds int events; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI); 1121da177e4SLinus Torvalds if (!events) 1131da177e4SLinus Torvalds return IRQ_NONE; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds #ifdef DEBUG_IRQS 1161da177e4SLinus Torvalds if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { 1171da177e4SLinus Torvalds printk("oss_irq: irq %d events = 0x%04X\n", irq, 1181da177e4SLinus Torvalds (int) oss->irq_pending); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds #endif 1211da177e4SLinus Torvalds /* FIXME: how do you clear a pending IRQ? */ 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds if (events & OSS_IP_SOUND) { 1241da177e4SLinus Torvalds oss->irq_pending &= ~OSS_IP_SOUND; 125647b804cSFinn Thain /* FIXME: call sound handler */ 1261da177e4SLinus Torvalds } else if (events & OSS_IP_SCSI) { 1271da177e4SLinus Torvalds oss->irq_pending &= ~OSS_IP_SCSI; 1281425df87SGeert Uytterhoeven generic_handle_irq(IRQ_MAC_SCSI); 1291da177e4SLinus Torvalds } else { 1301da177e4SLinus Torvalds /* FIXME: error check here? */ 1311da177e4SLinus Torvalds } 1321da177e4SLinus Torvalds return IRQ_HANDLED; 1331da177e4SLinus Torvalds } 134*9145db56SGeert Uytterhoeven #endif 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds /* 1371da177e4SLinus Torvalds * Nubus IRQ handler, OSS style 1381da177e4SLinus Torvalds * 1391da177e4SLinus Torvalds * Unlike the VIA/RBV this is on its own autovector interrupt level. 1401da177e4SLinus Torvalds */ 1411da177e4SLinus Torvalds 142*9145db56SGeert Uytterhoeven #ifdef CONFIG_GENERIC_HARDIRQS 143*9145db56SGeert Uytterhoeven static void oss_nubus_irq(unsigned int irq, struct irq_desc *desc) 144*9145db56SGeert Uytterhoeven { 145*9145db56SGeert Uytterhoeven int events, irq_bit, i; 146*9145db56SGeert Uytterhoeven 147*9145db56SGeert Uytterhoeven events = oss->irq_pending & OSS_IP_NUBUS; 148*9145db56SGeert Uytterhoeven if (!events) 149*9145db56SGeert Uytterhoeven return; 150*9145db56SGeert Uytterhoeven 151*9145db56SGeert Uytterhoeven #ifdef DEBUG_NUBUS_INT 152*9145db56SGeert Uytterhoeven if (console_loglevel > 7) { 153*9145db56SGeert Uytterhoeven printk("oss_nubus_irq: events = 0x%04X\n", events); 154*9145db56SGeert Uytterhoeven } 155*9145db56SGeert Uytterhoeven #endif 156*9145db56SGeert Uytterhoeven /* There are only six slots on the OSS, not seven */ 157*9145db56SGeert Uytterhoeven 158*9145db56SGeert Uytterhoeven i = 6; 159*9145db56SGeert Uytterhoeven irq_bit = 0x40; 160*9145db56SGeert Uytterhoeven do { 161*9145db56SGeert Uytterhoeven --i; 162*9145db56SGeert Uytterhoeven irq_bit >>= 1; 163*9145db56SGeert Uytterhoeven if (events & irq_bit) { 164*9145db56SGeert Uytterhoeven oss->irq_pending &= ~irq_bit; 165*9145db56SGeert Uytterhoeven generic_handle_irq(NUBUS_SOURCE_BASE + i); 166*9145db56SGeert Uytterhoeven } 167*9145db56SGeert Uytterhoeven } while(events & (irq_bit - 1)); 168*9145db56SGeert Uytterhoeven } 169*9145db56SGeert Uytterhoeven #else 1708dfbdf4aSAdrian Bunk static irqreturn_t oss_nubus_irq(int irq, void *dev_id) 1711da177e4SLinus Torvalds { 1721da177e4SLinus Torvalds int events, irq_bit, i; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds events = oss->irq_pending & OSS_IP_NUBUS; 1751da177e4SLinus Torvalds if (!events) 1761da177e4SLinus Torvalds return IRQ_NONE; 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds #ifdef DEBUG_NUBUS_INT 1791da177e4SLinus Torvalds if (console_loglevel > 7) { 1801da177e4SLinus Torvalds printk("oss_nubus_irq: events = 0x%04X\n", events); 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds #endif 1831da177e4SLinus Torvalds /* There are only six slots on the OSS, not seven */ 1841da177e4SLinus Torvalds 18567dfb153SFinn Thain i = 6; 18667dfb153SFinn Thain irq_bit = 0x40; 18767dfb153SFinn Thain do { 18867dfb153SFinn Thain --i; 18967dfb153SFinn Thain irq_bit >>= 1; 1901da177e4SLinus Torvalds if (events & irq_bit) { 1911da177e4SLinus Torvalds oss->irq_pending &= ~irq_bit; 1921425df87SGeert Uytterhoeven generic_handle_irq(NUBUS_SOURCE_BASE + i); 1931da177e4SLinus Torvalds } 19467dfb153SFinn Thain } while(events & (irq_bit - 1)); 1951da177e4SLinus Torvalds return IRQ_HANDLED; 1961da177e4SLinus Torvalds } 197*9145db56SGeert Uytterhoeven #endif 198*9145db56SGeert Uytterhoeven 199*9145db56SGeert Uytterhoeven /* 200*9145db56SGeert Uytterhoeven * Register the OSS and NuBus interrupt dispatchers. 201*9145db56SGeert Uytterhoeven */ 202*9145db56SGeert Uytterhoeven 203*9145db56SGeert Uytterhoeven void __init oss_register_interrupts(void) 204*9145db56SGeert Uytterhoeven { 205*9145db56SGeert Uytterhoeven #ifdef CONFIG_GENERIC_HARDIRQS 206*9145db56SGeert Uytterhoeven irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_irq); 207*9145db56SGeert Uytterhoeven irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq); 208*9145db56SGeert Uytterhoeven irq_set_chained_handler(OSS_IRQLEV_SOUND, oss_irq); 209*9145db56SGeert Uytterhoeven irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq); 210*9145db56SGeert Uytterhoeven #else /* !CONFIG_GENERIC_HARDIRQS */ 211*9145db56SGeert Uytterhoeven if (request_irq(OSS_IRQLEV_SCSI, oss_irq, 0, "scsi", (void *)oss)) 212*9145db56SGeert Uytterhoeven pr_err("Couldn't register %s interrupt\n", "scsi"); 213*9145db56SGeert Uytterhoeven if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, 0, "nubus", 214*9145db56SGeert Uytterhoeven (void *)oss)) 215*9145db56SGeert Uytterhoeven pr_err("Couldn't register %s interrupt\n", "nubus"); 216*9145db56SGeert Uytterhoeven if (request_irq(OSS_IRQLEV_SOUND, oss_irq, 0, "sound", (void *)oss)) 217*9145db56SGeert Uytterhoeven pr_err("Couldn't register %s interrupt\n", "sound"); 218*9145db56SGeert Uytterhoeven if (request_irq(OSS_IRQLEV_VIA1, via1_irq, 0, "via1", (void *)via1)) 219*9145db56SGeert Uytterhoeven pr_err("Couldn't register %s interrupt\n", "via1"); 220*9145db56SGeert Uytterhoeven #endif /* !CONFIG_GENERIC_HARDIRQS */ 221*9145db56SGeert Uytterhoeven } 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds /* 2241da177e4SLinus Torvalds * Enable an OSS interrupt 2251da177e4SLinus Torvalds * 2261da177e4SLinus Torvalds * It looks messy but it's rather straightforward. The switch() statement 2271da177e4SLinus Torvalds * just maps the machspec interrupt numbers to the right OSS interrupt 2281da177e4SLinus Torvalds * source (if the OSS handles that interrupt) and then sets the interrupt 2291da177e4SLinus Torvalds * level for that source to nonzero, thus enabling the interrupt. 2301da177e4SLinus Torvalds */ 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds void oss_irq_enable(int irq) { 2331da177e4SLinus Torvalds #ifdef DEBUG_IRQUSE 2341da177e4SLinus Torvalds printk("oss_irq_enable(%d)\n", irq); 2351da177e4SLinus Torvalds #endif 2361da177e4SLinus Torvalds switch(irq) { 23780614e5aSFinn Thain case IRQ_MAC_SCC: 2381da177e4SLinus Torvalds oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; 2391da177e4SLinus Torvalds break; 2401da177e4SLinus Torvalds case IRQ_MAC_ADB: 2411da177e4SLinus Torvalds oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; 2421da177e4SLinus Torvalds break; 2431da177e4SLinus Torvalds case IRQ_MAC_SCSI: 2441da177e4SLinus Torvalds oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; 2451da177e4SLinus Torvalds break; 2461da177e4SLinus Torvalds case IRQ_NUBUS_9: 2471da177e4SLinus Torvalds case IRQ_NUBUS_A: 2481da177e4SLinus Torvalds case IRQ_NUBUS_B: 2491da177e4SLinus Torvalds case IRQ_NUBUS_C: 2501da177e4SLinus Torvalds case IRQ_NUBUS_D: 2511da177e4SLinus Torvalds case IRQ_NUBUS_E: 2521da177e4SLinus Torvalds irq -= NUBUS_SOURCE_BASE; 2531da177e4SLinus Torvalds oss->irq_level[irq] = OSS_IRQLEV_NUBUS; 2541da177e4SLinus Torvalds break; 2551da177e4SLinus Torvalds #ifdef DEBUG_IRQUSE 2561da177e4SLinus Torvalds default: 257f85e7cdcSHarvey Harrison printk("%s unknown irq %d\n", __func__, irq); 2581da177e4SLinus Torvalds break; 2591da177e4SLinus Torvalds #endif 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* 2641da177e4SLinus Torvalds * Disable an OSS interrupt 2651da177e4SLinus Torvalds * 2661da177e4SLinus Torvalds * Same as above except we set the source's interrupt level to zero, 2671da177e4SLinus Torvalds * to disable the interrupt. 2681da177e4SLinus Torvalds */ 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds void oss_irq_disable(int irq) { 2711da177e4SLinus Torvalds #ifdef DEBUG_IRQUSE 2721da177e4SLinus Torvalds printk("oss_irq_disable(%d)\n", irq); 2731da177e4SLinus Torvalds #endif 2741da177e4SLinus Torvalds switch(irq) { 27580614e5aSFinn Thain case IRQ_MAC_SCC: 2761da177e4SLinus Torvalds oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; 2771da177e4SLinus Torvalds break; 2781da177e4SLinus Torvalds case IRQ_MAC_ADB: 2791da177e4SLinus Torvalds oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; 2801da177e4SLinus Torvalds break; 2811da177e4SLinus Torvalds case IRQ_MAC_SCSI: 2821da177e4SLinus Torvalds oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; 2831da177e4SLinus Torvalds break; 2841da177e4SLinus Torvalds case IRQ_NUBUS_9: 2851da177e4SLinus Torvalds case IRQ_NUBUS_A: 2861da177e4SLinus Torvalds case IRQ_NUBUS_B: 2871da177e4SLinus Torvalds case IRQ_NUBUS_C: 2881da177e4SLinus Torvalds case IRQ_NUBUS_D: 2891da177e4SLinus Torvalds case IRQ_NUBUS_E: 2901da177e4SLinus Torvalds irq -= NUBUS_SOURCE_BASE; 2911da177e4SLinus Torvalds oss->irq_level[irq] = OSS_IRQLEV_DISABLED; 2921da177e4SLinus Torvalds break; 2931da177e4SLinus Torvalds #ifdef DEBUG_IRQUSE 2941da177e4SLinus Torvalds default: 295f85e7cdcSHarvey Harrison printk("%s unknown irq %d\n", __func__, irq); 2961da177e4SLinus Torvalds break; 2971da177e4SLinus Torvalds #endif 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds } 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds /* 3021da177e4SLinus Torvalds * Clear an OSS interrupt 3031da177e4SLinus Torvalds * 3041da177e4SLinus Torvalds * Not sure if this works or not but it's the only method I could 3051da177e4SLinus Torvalds * think of based on the contents of the mac_oss structure. 3061da177e4SLinus Torvalds */ 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds void oss_irq_clear(int irq) { 3091da177e4SLinus Torvalds /* FIXME: how to do this on OSS? */ 3101da177e4SLinus Torvalds switch(irq) { 31180614e5aSFinn Thain case IRQ_MAC_SCC: 3121da177e4SLinus Torvalds oss->irq_pending &= ~OSS_IP_IOPSCC; 3131da177e4SLinus Torvalds break; 3141da177e4SLinus Torvalds case IRQ_MAC_ADB: 3151da177e4SLinus Torvalds oss->irq_pending &= ~OSS_IP_IOPISM; 3161da177e4SLinus Torvalds break; 3171da177e4SLinus Torvalds case IRQ_MAC_SCSI: 3181da177e4SLinus Torvalds oss->irq_pending &= ~OSS_IP_SCSI; 3191da177e4SLinus Torvalds break; 3201da177e4SLinus Torvalds case IRQ_NUBUS_9: 3211da177e4SLinus Torvalds case IRQ_NUBUS_A: 3221da177e4SLinus Torvalds case IRQ_NUBUS_B: 3231da177e4SLinus Torvalds case IRQ_NUBUS_C: 3241da177e4SLinus Torvalds case IRQ_NUBUS_D: 3251da177e4SLinus Torvalds case IRQ_NUBUS_E: 3261da177e4SLinus Torvalds irq -= NUBUS_SOURCE_BASE; 3271da177e4SLinus Torvalds oss->irq_pending &= ~(1 << irq); 3281da177e4SLinus Torvalds break; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds /* 3331da177e4SLinus Torvalds * Check to see if a specific OSS interrupt is pending 3341da177e4SLinus Torvalds */ 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds int oss_irq_pending(int irq) 3371da177e4SLinus Torvalds { 3381da177e4SLinus Torvalds switch(irq) { 33980614e5aSFinn Thain case IRQ_MAC_SCC: 3401da177e4SLinus Torvalds return oss->irq_pending & OSS_IP_IOPSCC; 3411da177e4SLinus Torvalds break; 3421da177e4SLinus Torvalds case IRQ_MAC_ADB: 3431da177e4SLinus Torvalds return oss->irq_pending & OSS_IP_IOPISM; 3441da177e4SLinus Torvalds break; 3451da177e4SLinus Torvalds case IRQ_MAC_SCSI: 3461da177e4SLinus Torvalds return oss->irq_pending & OSS_IP_SCSI; 3471da177e4SLinus Torvalds break; 3481da177e4SLinus Torvalds case IRQ_NUBUS_9: 3491da177e4SLinus Torvalds case IRQ_NUBUS_A: 3501da177e4SLinus Torvalds case IRQ_NUBUS_B: 3511da177e4SLinus Torvalds case IRQ_NUBUS_C: 3521da177e4SLinus Torvalds case IRQ_NUBUS_D: 3531da177e4SLinus Torvalds case IRQ_NUBUS_E: 3541da177e4SLinus Torvalds irq -= NUBUS_SOURCE_BASE; 3551da177e4SLinus Torvalds return oss->irq_pending & (1 << irq); 3561da177e4SLinus Torvalds break; 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds return 0; 3591da177e4SLinus Torvalds } 360