diff options
Diffstat (limited to 'arch/m68k/mac/oss.c')
-rw-r--r-- | arch/m68k/mac/oss.c | 199 |
1 files changed, 67 insertions, 132 deletions
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c index a9c0f5a..6c4c882 100644 --- a/arch/m68k/mac/oss.c +++ b/arch/m68k/mac/oss.c @@ -1,5 +1,5 @@ /* - * OSS handling + * Operating System Services (OSS) chip handling * Written by Joshua M. Thompson (funaho@jurai.org) * * @@ -19,6 +19,7 @@ #include <linux/mm.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/irq.h> #include <asm/bootinfo.h> #include <asm/macintosh.h> @@ -29,11 +30,6 @@ int oss_present; volatile struct mac_oss *oss; -static irqreturn_t oss_irq(int, void *); -static irqreturn_t oss_nubus_irq(int, void *); - -extern irqreturn_t via1_irq(int, void *); - /* * Initialize the OSS * @@ -53,30 +49,8 @@ void __init oss_init(void) /* do this by setting the source's interrupt level to zero. */ for (i = 0; i <= OSS_NUM_SOURCES; i++) { - oss->irq_level[i] = OSS_IRQLEV_DISABLED; + oss->irq_level[i] = 0; } - /* If we disable VIA1 here, we never really handle it... */ - oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; -} - -/* - * Register the OSS and NuBus interrupt dispatchers. - */ - -void __init oss_register_interrupts(void) -{ - if (request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, - "scsi", (void *) oss)) - pr_err("Couldn't register %s interrupt\n", "scsi"); - if (request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, - "nubus", (void *) oss)) - pr_err("Couldn't register %s interrupt\n", "nubus"); - if (request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, - "sound", (void *) oss)) - pr_err("Couldn't register %s interrupt\n", "sound"); - if (request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, - "via1", (void *) via1)) - pr_err("Couldn't register %s interrupt\n", "via1"); } /* @@ -88,36 +62,35 @@ void __init oss_nubus_init(void) } /* - * Handle miscellaneous OSS interrupts. Right now that's just sound - * and SCSI; everything else is routed to its own autovector IRQ. + * Handle miscellaneous OSS interrupts. */ -static irqreturn_t oss_irq(int irq, void *dev_id) +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 IRQ_NONE; + int events = oss->irq_pending & + (OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM); #ifdef DEBUG_IRQS if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { - printk("oss_irq: irq %d events = 0x%04X\n", irq, + 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) { + if (events & OSS_IP_IOPSCC) { + oss->irq_pending &= ~OSS_IP_IOPSCC; + generic_handle_irq(IRQ_MAC_SCC); + } + + if (events & OSS_IP_SCSI) { oss->irq_pending &= ~OSS_IP_SCSI; - m68k_handle_int(IRQ_MAC_SCSI); - } else { - /* FIXME: error check here? */ + generic_handle_irq(IRQ_MAC_SCSI); + } + + if (events & OSS_IP_IOPISM) { + oss->irq_pending &= ~OSS_IP_IOPISM; + generic_handle_irq(IRQ_MAC_ADB); } - return IRQ_HANDLED; } /* @@ -126,13 +99,13 @@ static irqreturn_t oss_irq(int irq, void *dev_id) * Unlike the VIA/RBV this is on its own autovector interrupt level. */ -static irqreturn_t oss_nubus_irq(int irq, void *dev_id) +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 IRQ_NONE; + return; #ifdef DEBUG_NUBUS_INT if (console_loglevel > 7) { @@ -148,10 +121,36 @@ static irqreturn_t oss_nubus_irq(int irq, void *dev_id) irq_bit >>= 1; if (events & irq_bit) { oss->irq_pending &= ~irq_bit; - m68k_handle_int(NUBUS_SOURCE_BASE + i); + generic_handle_irq(NUBUS_SOURCE_BASE + i); } } while(events & (irq_bit - 1)); - return IRQ_HANDLED; +} + +/* + * Register the OSS and NuBus interrupt dispatchers. + * + * This IRQ mapping is laid out with two things in mind: first, we try to keep + * things on their own levels to avoid having to do double-dispatches. Second, + * the levels match as closely as possible the alternate IRQ mapping mode (aka + * "A/UX mode") available on some VIA machines. + */ + +#define OSS_IRQLEV_IOPISM IRQ_AUTO_1 +#define OSS_IRQLEV_SCSI IRQ_AUTO_2 +#define OSS_IRQLEV_NUBUS IRQ_AUTO_3 +#define OSS_IRQLEV_IOPSCC IRQ_AUTO_4 +#define OSS_IRQLEV_VIA1 IRQ_AUTO_6 + +void __init oss_register_interrupts(void) +{ + irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_irq); + 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_IOPSCC, oss_irq); + irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq); + + /* OSS_VIA1 gets enabled here because it has no machspec interrupt. */ + oss->irq_level[OSS_VIA1] = IRQ_AUTO_6; } /* @@ -170,13 +169,13 @@ void oss_irq_enable(int irq) { switch(irq) { case IRQ_MAC_SCC: oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; - break; + return; case IRQ_MAC_ADB: oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; - break; + return; case IRQ_MAC_SCSI: oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; - break; + return; case IRQ_NUBUS_9: case IRQ_NUBUS_A: case IRQ_NUBUS_B: @@ -185,13 +184,11 @@ void oss_irq_enable(int irq) { case IRQ_NUBUS_E: irq -= NUBUS_SOURCE_BASE; oss->irq_level[irq] = OSS_IRQLEV_NUBUS; - break; -#ifdef DEBUG_IRQUSE - default: - printk("%s unknown irq %d\n", __func__, irq); - break; -#endif + return; } + + if (IRQ_SRC(irq) == 1) + via_irq_enable(irq); } /* @@ -207,50 +204,14 @@ void oss_irq_disable(int irq) { #endif switch(irq) { case IRQ_MAC_SCC: - oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; - break; - case IRQ_MAC_ADB: - oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; - break; - case IRQ_MAC_SCSI: - oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; - break; - case IRQ_NUBUS_9: - case IRQ_NUBUS_A: - case IRQ_NUBUS_B: - case IRQ_NUBUS_C: - case IRQ_NUBUS_D: - case IRQ_NUBUS_E: - irq -= NUBUS_SOURCE_BASE; - oss->irq_level[irq] = OSS_IRQLEV_DISABLED; - break; -#ifdef DEBUG_IRQUSE - default: - printk("%s unknown irq %d\n", __func__, irq); - break; -#endif - } -} - -/* - * Clear an OSS interrupt - * - * Not sure if this works or not but it's the only method I could - * think of based on the contents of the mac_oss structure. - */ - -void oss_irq_clear(int irq) { - /* FIXME: how to do this on OSS? */ - switch(irq) { - case IRQ_MAC_SCC: - oss->irq_pending &= ~OSS_IP_IOPSCC; - break; + oss->irq_level[OSS_IOPSCC] = 0; + return; case IRQ_MAC_ADB: - oss->irq_pending &= ~OSS_IP_IOPISM; - break; + oss->irq_level[OSS_IOPISM] = 0; + return; case IRQ_MAC_SCSI: - oss->irq_pending &= ~OSS_IP_SCSI; - break; + oss->irq_level[OSS_SCSI] = 0; + return; case IRQ_NUBUS_9: case IRQ_NUBUS_A: case IRQ_NUBUS_B: @@ -258,36 +219,10 @@ void oss_irq_clear(int irq) { case IRQ_NUBUS_D: case IRQ_NUBUS_E: irq -= NUBUS_SOURCE_BASE; - oss->irq_pending &= ~(1 << irq); - break; + oss->irq_level[irq] = 0; + return; } -} -/* - * Check to see if a specific OSS interrupt is pending - */ - -int oss_irq_pending(int irq) -{ - switch(irq) { - case IRQ_MAC_SCC: - return oss->irq_pending & OSS_IP_IOPSCC; - break; - case IRQ_MAC_ADB: - return oss->irq_pending & OSS_IP_IOPISM; - break; - case IRQ_MAC_SCSI: - return oss->irq_pending & OSS_IP_SCSI; - break; - case IRQ_NUBUS_9: - case IRQ_NUBUS_A: - case IRQ_NUBUS_B: - case IRQ_NUBUS_C: - case IRQ_NUBUS_D: - case IRQ_NUBUS_E: - irq -= NUBUS_SOURCE_BASE; - return oss->irq_pending & (1 << irq); - break; - } - return 0; + if (IRQ_SRC(irq) == 1) + via_irq_disable(irq); } |