patch-2.2.18 linux/arch/m68k/mac/oss.c
Next file: linux/arch/m68k/mac/psc.c
Previous file: linux/arch/m68k/mac/mackeyb.c
Back to the patch index
Back to the overall index
- Lines: 315
- Date:
Fri Oct 13 23:36:54 2000
- Orig file:
v2.2.17/arch/m68k/mac/oss.c
- Orig date:
Thu Jan 1 01:00:00 1970
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/arch/m68k/mac/oss.c linux/arch/m68k/mac/oss.c
@@ -0,0 +1,314 @@
+/*
+ * OSS handling
+ * Written by Joshua M. Thompson (funaho@jurai.org)
+ *
+ *
+ * This chip is used in the IIfx in place of VIA #2. It acts like a fancy
+ * VIA chip with prorammable interrupt levels.
+ *
+ * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some
+ * recent insights into OSS operational details.
+ * 990610 (jmt) - Now taking fulll advantage of the OSS. Interrupts are mapped
+ * to mostly match the A/UX interrupt scheme supported on the
+ * VIA side. Also added support for enabling the ISM irq again
+ * since we now have a functional IOP manager.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/bootinfo.h>
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+int oss_present;
+volatile struct mac_oss *oss;
+
+void oss_irq(int, void *, struct pt_regs *);
+void oss_nubus_irq(int, void *, struct pt_regs *);
+
+extern void via1_irq(int, void *, struct pt_regs *);
+extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern int console_loglevel;
+
+/*
+ * Initialize the OSS
+ *
+ * The OSS "detection" code is actually in via_init() which is always called
+ * before us. Thus we can count on oss_present being valid on entry.
+ */
+
+__initfunc(void oss_init(void))
+{
+ int i;
+
+ if (!oss_present) return;
+
+ oss = (struct mac_oss *) OSS_BASE;
+
+ /* Disable all interrupts. Unlike a VIA it looks like we */
+ /* 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;
+ }
+ /* 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.
+ */
+
+__initfunc(void oss_register_interrupts(void))
+{
+ sys_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
+ "OSS SCSI Dispatch", (void *) oss);
+ sys_request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK,
+ "SCC Dispatch", mac_SCC_handler);
+ sys_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
+ "Nubus Dispatch", (void *) oss);
+ sys_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
+ "OSS Sound Dispatch", (void *) oss);
+ sys_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
+ "VIA1 Dispatch", (void *) via1);
+}
+
+/*
+ * Initialize OSS for Nubus access
+ */
+
+__initfunc(void oss_nubus_init(void))
+{
+}
+
+/*
+ * Turn off the power via the ROM control register
+ *
+ * FIXME: not sure how this is supposed to work exactly...
+ */
+
+void oss_poweroff(void)
+{
+ oss->rom_ctrl = OSS_POWEROFF;
+
+ /* We should never make it this far... */
+
+ printk ("It is now safe to switch off your machine.\n");
+ while(1);
+}
+
+/*
+ * Handle miscellaneous OSS interrupts. Right now that's just sound
+ * and SCSI; everything else is routed to its own autovector IRQ.
+ */
+
+void oss_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int events;
+
+ events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI);
+ if (!events) return;
+
+#ifdef DEBUG_IRQS
+ if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) {
+ printk("oss_irq: irq %d 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) {
+ oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
+ oss->irq_pending &= ~OSS_IP_SCSI;
+ mac_do_irq_list(IRQ_MAC_SCSI, regs);
+ oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
+ } else {
+ /* FIXME: error check here? */
+ }
+}
+
+/*
+ * Nubus IRQ handler, OSS style
+ *
+ * Unlike the VIA/RBV this is on its own autovector interupt level.
+ */
+
+void oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int events, irq_bit, i;
+
+ events = oss->irq_pending & OSS_IP_NUBUS;
+ if (!events) return;
+
+#ifdef DEBUG_NUBUS_INT
+ if (console_loglevel > 7) {
+ printk("oss_nubus_irq: events = 0x%04X\n", events);
+ }
+#endif
+ /* There are only six slots on the OSS, not seven */
+
+ for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) {
+ if (events & irq_bit) {
+ oss->irq_level[i] = OSS_IRQLEV_DISABLED;
+ oss->irq_pending &= ~irq_bit;
+ mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs);
+ oss->irq_level[i] = OSS_IRQLEV_NUBUS;
+ }
+ }
+}
+
+/*
+ * Enable an OSS interrupt
+ *
+ * It looks messy but it's rather straightforward. The switch() statement
+ * just maps the machspec interrupt numbers to the right OSS interrupt
+ * source (if the OSS handles that interrupt) and then sets the interrupt
+ * level for that source to nonzero, thus enabling the interrupt.
+ */
+
+void oss_irq_enable(int irq) {
+#ifdef DEBUG_IRQUSE
+ printk("oss_irq_enable(%d)\n", irq);
+#endif
+ switch(irq) {
+ case IRQ_SCC:
+ case IRQ_SCCA:
+ case IRQ_SCCB:
+ oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
+ break;
+ case IRQ_MAC_ADB:
+ oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
+ break;
+ case IRQ_MAC_SCSI:
+ oss->irq_level[OSS_SCSI] = OSS_IRQLEV_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;
+ oss->irq_level[irq] = OSS_IRQLEV_NUBUS;
+ break;
+#ifdef DEBUG_IRQUSE
+ default:
+ printk("%s unknown irq %d\n",__FUNCTION__, irq);
+ break;
+#endif
+ }
+}
+
+/*
+ * Disable an OSS interrupt
+ *
+ * Same as above except we set the source's interrupt level to zero,
+ * to disable the interrupt.
+ */
+
+void oss_irq_disable(int irq) {
+#ifdef DEBUG_IRQUSE
+ printk("oss_irq_disable(%d)\n", irq);
+#endif
+ switch(irq) {
+ case IRQ_SCC:
+ case IRQ_SCCA:
+ case IRQ_SCCB:
+ 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", __FUNCTION__, 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_SCC:
+ case IRQ_SCCA:
+ case IRQ_SCCB:
+ oss->irq_pending &= ~OSS_IP_IOPSCC;
+ break;
+ case IRQ_MAC_ADB:
+ oss->irq_pending &= ~OSS_IP_IOPISM;
+ break;
+ case IRQ_MAC_SCSI:
+ 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;
+ oss->irq_pending &= ~(1 << irq);
+ break;
+ }
+}
+
+/*
+ * Check to see if a specific OSS interrupt is pending
+ */
+
+int oss_irq_pending(int irq)
+{
+ switch(irq) {
+ case IRQ_SCC:
+ case IRQ_SCCA:
+ case IRQ_SCCB:
+ 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;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)