Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/arch/m68k/mac/Makefile b/arch/m68k/mac/Makefile
new file mode 100644
index 0000000..995a09d9
--- /dev/null
+++ b/arch/m68k/mac/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Linux arch/m68k/mac source directory
+#
+
+obj-y		:= config.o bootparse.o macints.o iop.o via.o oss.o psc.o \
+			baboon.o macboing.o debug.o misc.o mac_ksyms.o
diff --git a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c
new file mode 100644
index 0000000..b19b7dd
--- /dev/null
+++ b/arch/m68k/mac/baboon.c
@@ -0,0 +1,126 @@
+/*
+ * Baboon Custom IC Management
+ *
+ * The Baboon custom IC controls the IDE, PCMCIA and media bay on the
+ * PowerBook 190. It multiplexes multiple interrupt sources onto the
+ * Nubus slot $C interrupt.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/traps.h>
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_baboon.h>
+
+/* #define DEBUG_BABOON */
+/* #define DEBUG_IRQS */
+
+int baboon_present,baboon_active;
+volatile struct baboon *baboon;
+
+irqreturn_t baboon_irq(int, void *, struct pt_regs *);
+
+#if 0
+extern int macide_ack_intr(struct ata_channel *);
+#endif
+
+/*
+ * Baboon initialization.
+ */
+
+void __init baboon_init(void)
+{
+	if (macintosh_config->ident != MAC_MODEL_PB190) {
+		baboon = NULL;
+		baboon_present = 0;
+		return;
+	}
+
+	baboon = (struct baboon *) BABOON_BASE;
+	baboon_present = 1;
+	baboon_active = 0;
+
+	printk("Baboon detected at %p\n", baboon);
+}
+
+/*
+ * Register the Baboon interrupt dispatcher on nubus slot $C.
+ */
+
+void __init baboon_register_interrupts(void)
+{
+	request_irq(IRQ_NUBUS_C, baboon_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+		    "baboon", (void *) baboon);
+}
+
+/*
+ * Baboon interrupt handler. This works a lot like a VIA.
+ */
+
+irqreturn_t baboon_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit,i;
+	unsigned char events;
+
+#ifdef DEBUG_IRQS
+	printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X active %02X\n",
+		(uint) baboon->mb_control, (uint) baboon->mb_ifr,
+		(uint) baboon->mb_status,  baboon_active);
+#endif
+
+	if (!(events = baboon->mb_ifr & 0x07))
+		return IRQ_NONE;
+
+	for (i = 0, irq_bit = 1 ; i < 3 ; i++, irq_bit <<= 1) {
+	        if (events & irq_bit/* & baboon_active*/) {
+			baboon_active &= ~irq_bit;
+			mac_do_irq_list(IRQ_BABOON_0 + i, regs);
+			baboon_active |= irq_bit;
+			baboon->mb_ifr &= ~irq_bit;
+		}
+	}
+#if 0
+	if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL);
+	/* for now we need to smash all interrupts */
+	baboon->mb_ifr &= ~events;
+#endif
+	return IRQ_HANDLED;
+}
+
+void baboon_irq_enable(int irq) {
+	int irq_idx	= IRQ_IDX(irq);
+
+#ifdef DEBUG_IRQUSE
+	printk("baboon_irq_enable(%d)\n", irq);
+#endif
+	baboon_active |= (1 << irq_idx);
+}
+
+void baboon_irq_disable(int irq) {
+	int irq_idx	= IRQ_IDX(irq);
+
+#ifdef DEBUG_IRQUSE
+	printk("baboon_irq_disable(%d)\n", irq);
+#endif
+	baboon_active &= ~(1 << irq_idx);
+}
+
+void baboon_irq_clear(int irq) {
+	int irq_idx	= IRQ_IDX(irq);
+
+	baboon->mb_ifr &= ~(1 << irq_idx);
+}
+
+int baboon_irq_pending(int irq)
+{
+	int irq_idx	= IRQ_IDX(irq);
+
+	return baboon->mb_ifr & (1 << irq_idx);
+}
diff --git a/arch/m68k/mac/bootparse.c b/arch/m68k/mac/bootparse.c
new file mode 100644
index 0000000..36d2236
--- /dev/null
+++ b/arch/m68k/mac/bootparse.c
@@ -0,0 +1,122 @@
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+
+/*
+ *	Booter vars
+ */
+
+int boothowto;
+int _boothowto;
+
+/*
+ *	Called early to parse the environment (passed to us from the booter)
+ *	into a bootinfo struct. Will die as soon as we have our own booter
+ */
+
+#define atol(x)	simple_strtoul(x,NULL,0)
+
+void parse_booter(char *env)
+{
+	char *name;
+	char *value;
+#if 0
+	while(0 && *env)
+#else
+	while(*env)
+#endif
+	{
+		name=env;
+		value=name;
+		while(*value!='='&&*value)
+			value++;
+		if(*value=='=')
+			*value++=0;
+		env=value;
+		while(*env)
+			env++;
+		env++;
+#if 0
+		if(strcmp(name,"VIDEO_ADDR")==0)
+			mac_mch.videoaddr=atol(value);
+		if(strcmp(name,"ROW_BYTES")==0)
+			mac_mch.videorow=atol(value);
+		if(strcmp(name,"SCREEN_DEPTH")==0)
+			mac_mch.videodepth=atol(value);
+		if(strcmp(name,"DIMENSIONS")==0)
+			mac_mch.dimensions=atol(value);
+#endif
+		if(strcmp(name,"BOOTTIME")==0)
+			mac_bi_data.boottime=atol(value);
+		if(strcmp(name,"GMTBIAS")==0)
+			mac_bi_data.gmtbias=atol(value);
+		if(strcmp(name,"BOOTERVER")==0)
+			mac_bi_data.bootver=atol(value);
+		if(strcmp(name,"MACOS_VIDEO")==0)
+			mac_bi_data.videological=atol(value);
+		if(strcmp(name,"MACOS_SCC")==0)
+			mac_bi_data.sccbase=atol(value);
+		if(strcmp(name,"MACHINEID")==0)
+			mac_bi_data.id=atol(value);
+		if(strcmp(name,"MEMSIZE")==0)
+			mac_bi_data.memsize=atol(value);
+		if(strcmp(name,"SERIAL_MODEM_FLAGS")==0)
+			mac_bi_data.serialmf=atol(value);
+		if(strcmp(name,"SERIAL_MODEM_HSKICLK")==0)
+			mac_bi_data.serialhsk=atol(value);
+		if(strcmp(name,"SERIAL_MODEM_GPICLK")==0)
+			mac_bi_data.serialgpi=atol(value);
+		if(strcmp(name,"SERIAL_PRINT_FLAGS")==0)
+			mac_bi_data.printmf=atol(value);
+		if(strcmp(name,"SERIAL_PRINT_HSKICLK")==0)
+			mac_bi_data.printhsk=atol(value);
+		if(strcmp(name,"SERIAL_PRINT_GPICLK")==0)
+			mac_bi_data.printgpi=atol(value);
+		if(strcmp(name,"PROCESSOR")==0)
+			mac_bi_data.cpuid=atol(value);
+		if(strcmp(name,"ROMBASE")==0)
+			mac_bi_data.rombase=atol(value);
+		if(strcmp(name,"TIMEDBRA")==0)
+			mac_bi_data.timedbra=atol(value);
+		if(strcmp(name,"ADBDELAY")==0)
+			mac_bi_data.adbdelay=atol(value);
+	}
+#if 0	/* XXX: TODO with m68k_mach_* */
+	/* Fill in the base stuff */
+	boot_info.machtype=MACH_MAC;
+	/* Read this from the macinfo we got ! */
+/*	boot_info.cputype=CPU_68020|FPUB_68881;*/
+/*	boot_info.memory[0].addr=0;*/
+/*	boot_info.memory[0].size=((mac_bi_data.id>>7)&31)<<20;*/
+	boot_info.num_memory=1;		/* On a MacII */
+	boot_info.ramdisk_size=0;	/* For now */
+	*boot_info.command_line=0;
+#endif
+ }
+
+
+void print_booter(char *env)
+{
+	char *name;
+	char *value;
+	while(*env)
+	{
+		name=env;
+		value=name;
+		while(*value!='='&&*value)
+			value++;
+		if(*value=='=')
+			*value++=0;
+		env=value;
+		while(*env)
+			env++;
+		env++;
+		printk("%s=%s\n", name,value);
+	}
+ }
+
+
diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c
new file mode 100644
index 0000000..cd19cbb
--- /dev/null
+++ b/arch/m68k/mac/config.c
@@ -0,0 +1,902 @@
+/*
+ *  linux/arch/m68k/mac/config.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * Miscellaneous linux stuff
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+/* keyb */
+#include <linux/random.h>
+#include <linux/delay.h>
+/* keyb */
+#include <linux/init.h>
+#include <linux/vt_kern.h>
+
+#define BOOTINFO_COMPAT_1_0
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/rtc.h>
+#include <asm/machdep.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+
+#include <asm/mac_iop.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+#include <asm/mac_psc.h>
+
+/* Mac bootinfo struct */
+
+struct mac_booter_data mac_bi_data;
+int mac_bisize = sizeof mac_bi_data;
+
+struct mac_hw_present mac_hw_present;
+
+/* New m68k bootinfo stuff and videobase */
+
+extern int m68k_num_memory;
+extern struct mem_info m68k_memory[NUM_MEMINFO];
+
+extern struct mem_info m68k_ramdisk;
+
+extern char m68k_command_line[CL_SIZE];
+
+void *mac_env;		/* Loaded by the boot asm */
+
+/* The phys. video addr. - might be bogus on some machines */
+unsigned long mac_orig_videoaddr;
+
+/* Mac specific timer functions */
+extern unsigned long mac_gettimeoffset (void);
+extern int mac_hwclk (int, struct rtc_time *);
+extern int mac_set_clock_mmss (unsigned long);
+extern int show_mac_interrupts(struct seq_file *, void *);
+extern void iop_preinit(void);
+extern void iop_init(void);
+extern void via_init(void);
+extern void via_init_clock(irqreturn_t (*func)(int, void *, struct pt_regs *));
+extern void via_flush_cache(void);
+extern void oss_init(void);
+extern void psc_init(void);
+extern void baboon_init(void);
+
+extern void mac_mksound(unsigned int, unsigned int);
+
+extern void nubus_sweep_video(void);
+
+/* Mac specific debug functions (in debug.c) */
+extern void mac_debug_init(void);
+extern void mac_debugging_long(int, long);
+
+static void mac_get_model(char *str);
+
+void mac_bang(int irq, void *vector, struct pt_regs *p)
+{
+	printk(KERN_INFO "Resetting ...\n");
+	mac_reset();
+}
+
+static void mac_sched_init(irqreturn_t (*vector)(int, void *, struct pt_regs *))
+{
+	via_init_clock(vector);
+}
+
+#if 0
+void mac_waitbut (void)
+{
+	;
+}
+#endif
+
+extern irqreturn_t mac_default_handler(int, void *, struct pt_regs *);
+
+irqreturn_t (*mac_handlers[8])(int, void *, struct pt_regs *)=
+{
+	mac_default_handler,
+	mac_default_handler,
+	mac_default_handler,
+	mac_default_handler,
+	mac_default_handler,
+	mac_default_handler,
+	mac_default_handler,
+	mac_default_handler
+};
+
+/*
+ * Parse a Macintosh-specific record in the bootinfo
+ */
+
+int __init mac_parse_bootinfo(const struct bi_record *record)
+{
+    int unknown = 0;
+    const u_long *data = record->data;
+
+    switch (record->tag) {
+	case BI_MAC_MODEL:
+	    mac_bi_data.id = *data;
+	    break;
+	case BI_MAC_VADDR:
+	    mac_bi_data.videoaddr = *data;
+	    break;
+	case BI_MAC_VDEPTH:
+	    mac_bi_data.videodepth = *data;
+	    break;
+	case BI_MAC_VROW:
+	    mac_bi_data.videorow = *data;
+	    break;
+	case BI_MAC_VDIM:
+	    mac_bi_data.dimensions = *data;
+	    break;
+	case BI_MAC_VLOGICAL:
+	    mac_bi_data.videological = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK);
+	    mac_orig_videoaddr = *data;
+	    break;
+	case BI_MAC_SCCBASE:
+	    mac_bi_data.sccbase = *data;
+	    break;
+	case BI_MAC_BTIME:
+	    mac_bi_data.boottime = *data;
+	    break;
+	case BI_MAC_GMTBIAS:
+	    mac_bi_data.gmtbias = *data;
+	    break;
+	case BI_MAC_MEMSIZE:
+	    mac_bi_data.memsize = *data;
+	    break;
+	case BI_MAC_CPUID:
+	    mac_bi_data.cpuid = *data;
+	    break;
+        case BI_MAC_ROMBASE:
+	    mac_bi_data.rombase = *data;
+	    break;
+	default:
+	    unknown = 1;
+    }
+    return(unknown);
+}
+
+/*
+ * Flip into 24bit mode for an instant - flushes the L2 cache card. We
+ * have to disable interrupts for this. Our IRQ handlers will crap
+ * themselves if they take an IRQ in 24bit mode!
+ */
+
+static void mac_cache_card_flush(int writeback)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	via_flush_cache();
+	local_irq_restore(flags);
+}
+
+void __init config_mac(void)
+{
+	if (!MACH_IS_MAC) {
+	  printk(KERN_ERR "ERROR: no Mac, but config_mac() called!! \n");
+	}
+
+	mach_sched_init      = mac_sched_init;
+	mach_init_IRQ        = mac_init_IRQ;
+	mach_request_irq     = mac_request_irq;
+	mach_free_irq        = mac_free_irq;
+	enable_irq           = mac_enable_irq;
+	disable_irq          = mac_disable_irq;
+	mach_get_model	 = mac_get_model;
+	mach_default_handler = &mac_handlers;
+	mach_get_irq_list    = show_mac_interrupts;
+	mach_gettimeoffset   = mac_gettimeoffset;
+#warning move to adb/via init
+#if 0
+	mach_hwclk           = mac_hwclk;
+#endif
+	mach_set_clock_mmss	 = mac_set_clock_mmss;
+	mach_reset           = mac_reset;
+	mach_halt            = mac_poweroff;
+	mach_power_off       = mac_poweroff;
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp	         = &dummy_con;
+#endif
+	mach_max_dma_address = 0xffffffff;
+#if 0
+	mach_debug_init	 = mac_debug_init;
+#endif
+#if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE)
+        mach_beep            = mac_mksound;
+#endif
+#ifdef CONFIG_HEARTBEAT
+#if 0
+	mach_heartbeat = mac_heartbeat;
+	mach_heartbeat_irq = IRQ_MAC_TIMER;
+#endif
+#endif
+
+	/*
+	 * Determine hardware present
+	 */
+
+	mac_identify();
+	mac_report_hardware();
+
+	/* AFAIK only the IIci takes a cache card.  The IIfx has onboard
+	   cache ... someone needs to figure out how to tell if it's on or
+	   not. */
+
+	if (macintosh_config->ident == MAC_MODEL_IICI
+	    || macintosh_config->ident == MAC_MODEL_IIFX) {
+		mach_l2_flush = mac_cache_card_flush;
+	}
+
+	/*
+	 * Check for machine specific fixups.
+	 */
+
+#ifdef OLD_NUBUS_CODE
+	 nubus_sweep_video();
+#endif
+}
+
+
+/*
+ *	Macintosh Table: hardcoded model configuration data.
+ *
+ *	Much of this was defined by Alan, based on who knows what docs.
+ *	I've added a lot more, and some of that was pure guesswork based
+ *	on hardware pages present on the Mac web site. Possibly wildly
+ *	inaccurate, so look here if a new Mac model won't run. Example: if
+ *	a Mac crashes immediately after the VIA1 registers have been dumped
+ *	to the screen, it probably died attempting to read DirB on a RBV.
+ *	Meaning it should have MAC_VIA_IIci here :-)
+ */
+
+struct mac_model *macintosh_config;
+EXPORT_SYMBOL(macintosh_config);
+
+static struct mac_model mac_data_table[]=
+{
+	/*
+	 *	We'll pretend to be a Macintosh II, that's pretty safe.
+	 */
+
+	{
+		.ident		= MAC_MODEL_II,
+		.name		= "Unknown",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_II,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Original MacII hardware
+	 *
+	 */
+
+	{
+		.ident		= MAC_MODEL_II,
+		.name		= "II",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_II,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_IIX,
+		.name		= "IIx",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_II,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_IICX,
+		.name		= "IIcx",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_II,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_SE30,
+		.name		= "SE/30",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_II,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Weirdified MacII hardware - all subtley different. Gee thanks
+	 *	Apple. All these boxes seem to have VIA2 in a different place to
+	 *	the MacII (+1A000 rather than +4000)
+	 * CSA: see http://developer.apple.com/technotes/hw/hw_09.html
+	 */
+
+	{
+		.ident		= MAC_MODEL_IICI,
+		.name		= "IIci",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_IIFX,
+		.name		= "IIfx",
+		.adb_type	= MAC_ADB_IOP,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_IOP,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_IISI,
+		.name		= "IIsi",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_IIVI,
+		.name		= "IIvi",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_IIVX,
+		.name		= "IIvx",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Classic models (guessing: similar to SE/30 ?? Nope, similar to LC ...)
+	 */
+
+	{
+		.ident		= MAC_MODEL_CLII,
+		.name		= "Classic II",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_CCL,
+		.name		= "Color Classic",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS},
+
+	/*
+	 *	Some Mac LC machines. Basically the same as the IIci, ADB like IIsi
+	 */
+
+	{
+		.ident		= MAC_MODEL_LC,
+		.name		= "LC",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_LCII,
+		.name		= "LC II",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_LCIII,
+		.name		= "LC III",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Quadra. Video is at 0xF9000000, via is like a MacII. We label it differently
+	 *	as some of the stuff connected to VIA2 seems different. Better SCSI chip and
+	 *	onboard ethernet using a NatSemi SONIC except the 660AV and 840AV which use an
+	 *	AMD 79C940 (MACE).
+	 *	The 700, 900 and 950 have some I/O chips in the wrong place to
+	 *	confuse us. The 840AV has a SCSI location of its own (same as
+	 *	the 660AV).
+	 */
+
+	{
+		.ident		= MAC_MODEL_Q605,
+		.name		= "Quadra 605",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q605_ACC,
+		.name		= "Quadra 605",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q610,
+		.name		= "Quadra 610",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q630,
+		.name		= "Quadra 630",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.ide_type	= MAC_IDE_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q650,
+		.name		= "Quadra 650",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	},
+	/*	The Q700 does have a NS Sonic */
+	{
+		.ident		= MAC_MODEL_Q700,
+		.name		= "Quadra 700",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA2,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q800,
+		.name		= "Quadra 800",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q840,
+		.name		= "Quadra 840AV",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA3,
+		.scc_type	= MAC_SCC_PSC,
+		.ether_type	= MAC_ETHER_MACE,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q900,
+		.name		= "Quadra 900",
+		.adb_type	= MAC_ADB_IOP,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA2,
+		.scc_type	= MAC_SCC_IOP,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_Q950,
+		.name		= "Quadra 950",
+		.adb_type	= MAC_ADB_IOP,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA2,
+		.scc_type	= MAC_SCC_IOP,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Performa - more LC type machines
+	 */
+
+	{
+		.ident		= MAC_MODEL_P460,
+		.name		=  "Performa 460",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_P475,
+		.name		=  "Performa 475",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_P475F,
+		.name		=  "Performa 475",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_P520,
+		.name		=  "Performa 520",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_P550,
+		.name		=  "Performa 550",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	},
+	/* These have the comm slot, and therefore the possibility of SONIC ethernet */
+	{
+		.ident		= MAC_MODEL_P575,
+		.name		= "Performa 575",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_II,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_P588,
+		.name		= "Performa 588",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.ide_type	= MAC_IDE_QUADRA,
+		.scc_type	= MAC_SCC_II,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_TV,
+		.name		= "TV",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_P600,
+		.name		= "Performa 600",
+		.adb_type	= MAC_ADB_IISI,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_II,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Centris - just guessing again; maybe like Quadra
+	 */
+
+	/* The C610 may or may not have SONIC.  We probe to make sure */
+	{
+		.ident		= MAC_MODEL_C610,
+		.name		= "Centris 610",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_C650,
+		.name		= "Centris 650",
+		.adb_type	= MAC_ADB_II,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_C660,
+		.name		= "Centris 660AV",
+		.adb_type	= MAC_ADB_CUDA,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_QUADRA3,
+		.scc_type	= MAC_SCC_PSC,
+		.ether_type	= MAC_ETHER_MACE,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 * The PowerBooks all the same "Combo" custom IC for SCSI and SCC
+	 * and a PMU (in two variations?) for ADB. Most of them use the
+	 * Quadra-style VIAs. A few models also have IDE from hell.
+	 */
+
+	{
+		.ident		= MAC_MODEL_PB140,
+		.name		= "PowerBook 140",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB145,
+		.name		= "PowerBook 145",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB150,
+		.name		= "PowerBook 150",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.ide_type	= MAC_IDE_PB,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB160,
+		.name		= "PowerBook 160",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB165,
+		.name		= "PowerBook 165",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB165C,
+		.name		= "PowerBook 165c",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB170,
+		.name		= "PowerBook 170",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB180,
+		.name		= "PowerBook 180",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB180C,
+		.name		= "PowerBook 180c",
+		.adb_type	= MAC_ADB_PB1,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB190,
+		.name		= "PowerBook 190",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.ide_type	= MAC_IDE_BABOON,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB520,
+		.name		= "PowerBook 520",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_QUADRA,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.ether_type	= MAC_ETHER_SONIC,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 * PowerBook Duos are pretty much like normal PowerBooks
+	 * All of these probably have onboard SONIC in the Dock which
+	 * means we'll have to probe for it eventually.
+	 *
+	 * Are these reallly MAC_VIA_IIci? The developer notes for the
+	 * Duos show pretty much the same custom parts as in most of
+	 * the other PowerBooks which would imply MAC_VIA_QUADRA.
+	 */
+
+	{
+		.ident		= MAC_MODEL_PB210,
+		.name		= "PowerBook Duo 210",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB230,
+		.name		= "PowerBook Duo 230",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB250,
+		.name		= "PowerBook Duo 250",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB270C,
+		.name		= "PowerBook Duo 270c",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB280,
+		.name		= "PowerBook Duo 280",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	}, {
+		.ident		= MAC_MODEL_PB280C,
+		.name		= "PowerBook Duo 280c",
+		.adb_type	= MAC_ADB_PB2,
+		.via_type	= MAC_VIA_IIci,
+		.scsi_type	= MAC_SCSI_OLD,
+		.scc_type	= MAC_SCC_QUADRA,
+		.nubus_type	= MAC_NUBUS
+	},
+
+	/*
+	 *	Other stuff ??
+	 */
+	{
+		.ident		= -1
+	}
+};
+
+void mac_identify(void)
+{
+	struct mac_model *m;
+
+	/* Penguin data useful? */
+	int model = mac_bi_data.id;
+	if (!model) {
+		/* no bootinfo model id -> NetBSD booter was used! */
+		/* XXX FIXME: breaks for model > 31 */
+		model=(mac_bi_data.cpuid>>2)&63;
+		printk (KERN_WARNING "No bootinfo model ID, using cpuid instead (hey, use Penguin!)\n");
+	}
+
+	macintosh_config = mac_data_table;
+	for (m = macintosh_config ; m->ident != -1 ; m++) {
+		if (m->ident == model) {
+			macintosh_config = m;
+			break;
+		}
+	}
+
+	/* We need to pre-init the IOPs, if any. Otherwise */
+	/* the serial console won't work if the user had   */
+	/* the serial ports set to "Faster" mode in MacOS. */
+
+	iop_preinit();
+	mac_debug_init();
+
+	printk (KERN_INFO "Detected Macintosh model: %d \n", model);
+
+	/*
+	 * Report booter data:
+	 */
+	printk (KERN_DEBUG " Penguin bootinfo data:\n");
+	printk (KERN_DEBUG " Video: addr 0x%lx row 0x%lx depth %lx dimensions %ld x %ld\n",
+		mac_bi_data.videoaddr, mac_bi_data.videorow,
+		mac_bi_data.videodepth, mac_bi_data.dimensions & 0xFFFF,
+		mac_bi_data.dimensions >> 16);
+	printk (KERN_DEBUG " Videological 0x%lx phys. 0x%lx, SCC at 0x%lx \n",
+		mac_bi_data.videological, mac_orig_videoaddr,
+		mac_bi_data.sccbase);
+	printk (KERN_DEBUG " Boottime: 0x%lx GMTBias: 0x%lx \n",
+		mac_bi_data.boottime, mac_bi_data.gmtbias);
+	printk (KERN_DEBUG " Machine ID: %ld CPUid: 0x%lx memory size: 0x%lx \n",
+		mac_bi_data.id, mac_bi_data.cpuid, mac_bi_data.memsize);
+#if 0
+	printk ("Ramdisk: addr 0x%lx size 0x%lx\n",
+		m68k_ramdisk.addr, m68k_ramdisk.size);
+#endif
+
+	/*
+	 * TODO: set the various fields in macintosh_config->hw_present here!
+	 */
+	switch (macintosh_config->scsi_type) {
+	case MAC_SCSI_OLD:
+	  MACHW_SET(MAC_SCSI_80);
+	  break;
+	case MAC_SCSI_QUADRA:
+	case MAC_SCSI_QUADRA2:
+	case MAC_SCSI_QUADRA3:
+	  MACHW_SET(MAC_SCSI_96);
+	  if ((macintosh_config->ident == MAC_MODEL_Q900) ||
+	      (macintosh_config->ident == MAC_MODEL_Q950))
+	    MACHW_SET(MAC_SCSI_96_2);
+	  break;
+	default:
+	  printk(KERN_WARNING "config.c: wtf: unknown scsi, using 53c80\n");
+	  MACHW_SET(MAC_SCSI_80);
+	  break;
+
+	}
+	iop_init();
+	via_init();
+	oss_init();
+	psc_init();
+	baboon_init();
+}
+
+void mac_report_hardware(void)
+{
+	printk(KERN_INFO "Apple Macintosh %s\n", macintosh_config->name);
+}
+
+static void mac_get_model(char *str)
+{
+	strcpy(str,"Macintosh ");
+	strcat(str, macintosh_config->name);
+}
diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c
new file mode 100644
index 0000000..cc62ed6
--- /dev/null
+++ b/arch/m68k/mac/debug.c
@@ -0,0 +1,398 @@
+/*
+ * linux/arch/m68k/mac/debug.c
+ *
+ * Shamelessly stolen (SCC code and general framework) from:
+ *
+ * linux/arch/m68k/atari/debug.c
+ *
+ * Atari debugging and serial console stuff
+ *
+ * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#define BOOTINFO_COMPAT_1_0
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/machw.h>
+#include <asm/macints.h>
+
+extern char m68k_debug_device[];
+
+extern struct compat_bootinfo compat_boot_info;
+
+extern unsigned long mac_videobase;
+extern unsigned long mac_videodepth;
+extern unsigned long mac_rowbytes;
+
+extern void mac_serial_print(const char *);
+
+#define DEBUG_HEADS
+#undef DEBUG_SCREEN
+#define DEBUG_SERIAL
+
+/*
+ * These two auxiliary debug functions should go away ASAP. Only usage:
+ * before the console output is up (after head.S come some other crucial
+ * setup routines :-) it permits writing 'data' to the screen as bit patterns
+ * (good luck reading those). Helped to figure that the bootinfo contained
+ * garbage data on the amount and size of memory chunks ...
+ *
+ * The 'pos' argument now simply means 'linefeed after print' ...
+ */
+
+#ifdef DEBUG_SCREEN
+static int peng=0, line=0;
+#endif
+
+void mac_debugging_short(int pos, short num)
+{
+#ifdef DEBUG_SCREEN
+	unsigned char *pengoffset;
+	unsigned char *pptr;
+	int i;
+#endif
+
+#ifdef DEBUG_SERIAL
+	printk("debug: %d !\n", num);
+#endif
+
+#ifdef DEBUG_SCREEN
+	if (!MACH_IS_MAC) {
+		/* printk("debug: %d !\n", num); */
+		return;
+	}
+
+	/* calculate current offset */
+	pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes)
+		    +80*peng;
+
+	pptr=pengoffset;
+
+	for(i=0;i<8*sizeof(short);i++) /* # of bits */
+	{
+		/*        value        mask for bit i, reverse order */
+		*pptr++ = (num & ( 1 << (8*sizeof(short)-i-1) ) ? 0xFF : 0x00);
+	}
+
+	peng++;
+
+	if (pos) {
+		line++;
+		peng = 0;
+	}
+#endif
+}
+
+void mac_debugging_long(int pos, long addr)
+{
+#ifdef DEBUG_SCREEN
+	unsigned char *pengoffset;
+	unsigned char *pptr;
+	int i;
+#endif
+
+#ifdef DEBUG_SERIAL
+	printk("debug: #%ld !\n", addr);
+#endif
+
+#ifdef DEBUG_SCREEN
+	if (!MACH_IS_MAC) {
+		/* printk("debug: #%ld !\n", addr); */
+		return;
+	}
+
+	pengoffset=(unsigned char *)(mac_videobase+(150+line*2)*mac_rowbytes)
+		    +80*peng;
+
+	pptr=pengoffset;
+
+	for(i=0;i<8*sizeof(long);i++) /* # of bits */
+	{
+		*pptr++ = (addr & ( 1 << (8*sizeof(long)-i-1) ) ? 0xFF : 0x00);
+	}
+
+	peng++;
+
+	if (pos) {
+		line++;
+		peng = 0;
+	}
+#endif
+}
+
+#ifdef DEBUG_SERIAL
+/*
+ * TODO: serial debug code
+ */
+
+struct mac_SCC
+ {
+  u_char cha_b_ctrl;
+  u_char char_dummy1;
+  u_char cha_a_ctrl;
+  u_char char_dummy2;
+  u_char cha_b_data;
+  u_char char_dummy3;
+  u_char cha_a_data;
+ };
+
+# define scc (*((volatile struct mac_SCC*)mac_bi_data.sccbase))
+
+/* Flag that serial port is already initialized and used */
+int mac_SCC_init_done;
+/* Can be set somewhere, if a SCC master reset has already be done and should
+ * not be repeated; used by kgdb */
+int mac_SCC_reset_done;
+
+static int scc_port = -1;
+
+static struct console mac_console_driver = {
+	.name =		"debug",
+	.flags =	CON_PRINTBUFFER,
+	.index =	-1,
+};
+
+/*
+ * Crude hack to get console output to the screen before the framebuffer
+ * is initialized (happens a lot later in 2.1!).
+ * We just use the console routines declared in head.S, this will interfere
+ * with regular framebuffer console output and should be used exclusively
+ * to debug kernel problems manifesting before framebuffer init (aka WSOD)
+ *
+ * To keep this hack from interfering with the regular console driver, either
+ * deregister this driver before/on framebuffer console init, or silence this
+ * function after the fbcon driver is running (will lose console messages!?).
+ * To debug real early bugs, need to write a 'mac_register_console_hack()'
+ * that is called from start_kernel() before setup_arch() and just registers
+ * this driver if Mac.
+ */
+
+void mac_debug_console_write (struct console *co, const char *str,
+			      unsigned int count)
+{
+	mac_serial_print(str);
+}
+
+
+
+/* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/
+
+#define uSEC 1
+
+static inline void mac_sccb_out (char c)
+{
+    int i;
+    do {
+	for( i = uSEC; i > 0; --i )
+		barrier();
+    } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */
+    for( i = uSEC; i > 0; --i )
+	barrier();
+    scc.cha_b_data = c;
+}
+
+static inline void mac_scca_out (char c)
+{
+    int i;
+    do {
+	for( i = uSEC; i > 0; --i )
+		barrier();
+    } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */
+    for( i = uSEC; i > 0; --i )
+	barrier();
+    scc.cha_a_data = c;
+}
+
+void mac_sccb_console_write (struct console *co, const char *str,
+			      unsigned int count)
+{
+    while (count--) {
+	if (*str == '\n')
+	    mac_sccb_out( '\r' );
+	mac_sccb_out( *str++ );
+    }
+}
+
+void mac_scca_console_write (struct console *co, const char *str,
+			      unsigned int count)
+{
+    while (count--) {
+	if (*str == '\n')
+	    mac_scca_out( '\r' );
+	mac_scca_out( *str++ );
+    }
+}
+
+
+/* The following two functions do a quick'n'dirty initialization of the MFP or
+ * SCC serial ports. They're used by the debugging interface, kgdb, and the
+ * serial console code. */
+#define SCCB_WRITE(reg,val)				\
+    do {						\
+	int i;						\
+	scc.cha_b_ctrl = (reg);				\
+	for( i = uSEC; i > 0; --i )			\
+		barrier();				\
+	scc.cha_b_ctrl = (val);				\
+	for( i = uSEC; i > 0; --i )			\
+		barrier();				\
+    } while(0)
+
+#define SCCA_WRITE(reg,val)				\
+    do {						\
+	int i;						\
+	scc.cha_a_ctrl = (reg);				\
+	for( i = uSEC; i > 0; --i )			\
+		barrier();				\
+	scc.cha_a_ctrl = (val);				\
+	for( i = uSEC; i > 0; --i )			\
+		barrier();				\
+    } while(0)
+
+/* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a
+ * delay of ~ 60us. */
+/* Mac: loops_per_jiffy min. 19000 ^= .5 us; MFPDELAY was 0.6 us*/
+#define LONG_DELAY()				\
+    do {					\
+	int i;					\
+	for( i = 60*uSEC; i > 0; --i )		\
+	    barrier();				\
+    } while(0)
+
+#ifndef CONFIG_SERIAL_CONSOLE
+static void __init mac_init_scc_port( int cflag, int port )
+#else
+void mac_init_scc_port( int cflag, int port )
+#endif
+{
+	extern int mac_SCC_reset_done;
+
+	/*
+	 * baud rates: 1200, 1800, 2400, 4800, 9600, 19.2k, 38.4k, 57.6k, 115.2k
+	 */
+
+	static int clksrc_table[9] =
+		/* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */
+		{ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 };
+	static int clkmode_table[9] =
+		/* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */
+		{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 };
+	static int div_table[9] =
+		/* reg12 (BRG low) */
+		{ 94, 62, 46, 22, 10, 4, 1, 0, 0 };
+
+    int baud = cflag & CBAUD;
+    int clksrc, clkmode, div, reg3, reg5;
+
+    if (cflag & CBAUDEX)
+	baud += B38400;
+    if (baud < B1200 || baud > B38400+2)
+	baud = B9600; /* use default 9600bps for non-implemented rates */
+    baud -= B1200; /* tables starts at 1200bps */
+
+    clksrc  = clksrc_table[baud];
+    clkmode = clkmode_table[baud];
+    div     = div_table[baud];
+
+    reg3 = (((cflag & CSIZE) == CS8) ? 0xc0 : 0x40);
+    reg5 = (((cflag & CSIZE) == CS8) ? 0x60 : 0x20) | 0x82 /* assert DTR/RTS */;
+
+    if (port == 1) {
+	    (void)scc.cha_b_ctrl;	/* reset reg pointer */
+	    SCCB_WRITE( 9, 0xc0 );	/* reset */
+	    LONG_DELAY();		/* extra delay after WR9 access */
+	    SCCB_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 |
+			  0x04 /* 1 stopbit */ |
+			  clkmode );
+	    SCCB_WRITE( 3, reg3 );
+	    SCCB_WRITE( 5, reg5 );
+	    SCCB_WRITE( 9, 0 );		/* no interrupts */
+	    LONG_DELAY();		/* extra delay after WR9 access */
+	    SCCB_WRITE( 10, 0 );	/* NRZ mode */
+	    SCCB_WRITE( 11, clksrc );	/* main clock source */
+	    SCCB_WRITE( 12, div );	/* BRG value */
+	    SCCB_WRITE( 13, 0 );		/* BRG high byte */
+	    SCCB_WRITE( 14, 1 );
+	    SCCB_WRITE( 3, reg3 | 1 );
+	    SCCB_WRITE( 5, reg5 | 8 );
+    } else if (port == 0) {
+	    (void)scc.cha_a_ctrl;	/* reset reg pointer */
+	    SCCA_WRITE( 9, 0xc0 );	/* reset */
+	    LONG_DELAY();		/* extra delay after WR9 access */
+	    SCCA_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 |
+			  0x04 /* 1 stopbit */ |
+			  clkmode );
+	    SCCA_WRITE( 3, reg3 );
+	    SCCA_WRITE( 5, reg5 );
+	    SCCA_WRITE( 9, 0 );		/* no interrupts */
+	    LONG_DELAY();		/* extra delay after WR9 access */
+	    SCCA_WRITE( 10, 0 );	/* NRZ mode */
+	    SCCA_WRITE( 11, clksrc );	/* main clock source */
+	    SCCA_WRITE( 12, div );	/* BRG value */
+	    SCCA_WRITE( 13, 0 );		/* BRG high byte */
+	    SCCA_WRITE( 14, 1 );
+	    SCCA_WRITE( 3, reg3 | 1 );
+	    SCCA_WRITE( 5, reg5 | 8 );
+    }
+
+    mac_SCC_reset_done = 1;
+    mac_SCC_init_done = 1;
+}
+#endif /* DEBUG_SERIAL */
+
+void mac_init_scca_port( int cflag )
+{
+	mac_init_scc_port(cflag, 0);
+}
+
+void mac_init_sccb_port( int cflag )
+{
+	mac_init_scc_port(cflag, 1);
+}
+
+void __init mac_debug_init(void)
+{
+#ifdef DEBUG_SERIAL
+    if (   !strcmp( m68k_debug_device, "ser"  )
+        || !strcmp( m68k_debug_device, "ser1" )) {
+	/* Mac modem port */
+	mac_init_scc_port( B9600|CS8, 0 );
+	mac_console_driver.write = mac_scca_console_write;
+	scc_port = 0;
+    }
+    else if (!strcmp( m68k_debug_device, "ser2" )) {
+	/* Mac printer port */
+	mac_init_scc_port( B9600|CS8, 1 );
+	mac_console_driver.write = mac_sccb_console_write;
+	scc_port = 1;
+    }
+#endif
+#ifdef DEBUG_HEADS
+    if (   !strcmp( m68k_debug_device, "scn"  )
+        || !strcmp( m68k_debug_device, "con" )) {
+	/* display, using head.S console routines */
+	mac_console_driver.write = mac_debug_console_write;
+    }
+#endif
+    if (mac_console_driver.write)
+	register_console(&mac_console_driver);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  tab-width: 8
+ * End:
+ */
diff --git a/arch/m68k/mac/iop.c b/arch/m68k/mac/iop.c
new file mode 100644
index 0000000..d889ba8
--- /dev/null
+++ b/arch/m68k/mac/iop.c
@@ -0,0 +1,714 @@
+/*
+ * I/O Processor (IOP) management
+ * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice and this list of conditions.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice and this list of conditions in the documentation and/or other
+ *    materials provided with the distribution.
+ */
+
+/*
+ * The IOP chips are used in the IIfx and some Quadras (900, 950) to manage
+ * serial and ADB. They are actually a 6502 processor and some glue logic.
+ *
+ * 990429 (jmt) - Initial implementation, just enough to knock the SCC IOP
+ *		  into compatible mode so nobody has to fiddle with the
+ *		  Serial Switch control panel anymore.
+ * 990603 (jmt) - Added code to grab the correct ISM IOP interrupt for OSS
+ *		  and non-OSS machines (at least I hope it's correct on a
+ *		  non-OSS machine -- someone with a Q900 or Q950 needs to
+ *		  check this.)
+ * 990605 (jmt) - Rearranged things a bit wrt IOP detection; iop_present is
+ *		  gone, IOP base addresses are now in an array and the
+ *		  globally-visible functions take an IOP number instead of an
+ *		  an actual base address.
+ * 990610 (jmt) - Finished the message passing framework and it seems to work.
+ *		  Sending _definitely_ works; my adb-bus.c mods can send
+ *		  messages and receive the MSG_COMPLETED status back from the
+ *		  IOP. The trick now is figuring out the message formats.
+ * 990611 (jmt) - More cleanups. Fixed problem where unclaimed messages on a
+ *		  receive channel were never properly acknowledged. Bracketed
+ *		  the remaining debug printk's with #ifdef's and disabled
+ *		  debugging. I can now type on the console.
+ * 990612 (jmt) - Copyright notice added. Reworked the way replies are handled.
+ *		  It turns out that replies are placed back in the send buffer
+ *		  for that channel; messages on the receive channels are always
+ *		  unsolicited messages from the IOP (and our replies to them
+ *		  should go back in the receive channel.) Also added tracking
+ *		  of device names to the listener functions ala the interrupt
+ *		  handlers.
+ * 990729 (jmt) - Added passing of pt_regs structure to IOP handlers. This is
+ *		  used by the new unified ADB driver.
+ *
+ * TODO:
+ *
+ * o Something should be periodically checking iop_alive() to make sure the
+ *   IOP hasn't died.
+ * o Some of the IOP manager routines need better error checking and
+ *   return codes. Nothing major, just prettying up.
+ */
+
+/*
+ * -----------------------
+ * IOP Message Passing 101
+ * -----------------------
+ *
+ * The host talks to the IOPs using a rather simple message-passing scheme via
+ * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each
+ * channel is conneced to a specific software driver on the IOP. For example
+ * on the SCC IOP there is one channel for each serial port. Each channel has
+ * an incoming and and outgoing message queue with a depth of one.
+ *
+ * A message is 32 bytes plus a state byte for the channel (MSG_IDLE, MSG_NEW,
+ * MSG_RCVD, MSG_COMPLETE). To send a message you copy the message into the
+ * buffer, set the state to MSG_NEW and signal the IOP by setting the IRQ flag
+ * in the IOP control to 1. The IOP will move the state to MSG_RCVD when it
+ * receives the message and then to MSG_COMPLETE when the message processing
+ * has completed. It is the host's responsibility at that point to read the
+ * reply back out of the send channel buffer and reset the channel state back
+ * to MSG_IDLE.
+ *
+ * To receive message from the IOP the same procedure is used except the roles
+ * are reversed. That is, the IOP puts message in the channel with a state of
+ * MSG_NEW, and the host receives the message and move its state to MSG_RCVD
+ * and then to MSG_COMPLETE when processing is completed and the reply (if any)
+ * has been placed back in the receive channel. The IOP will then reset the
+ * channel state to MSG_IDLE.
+ *
+ * Two sets of host interrupts are provided, INT0 and INT1. Both appear on one
+ * interrupt level; they are distinguished by a pair of bits in the IOP status
+ * register. The IOP will raise INT0 when one or more messages in the send
+ * channels have gone to the MSG_COMPLETE state and it will raise INT1 when one
+ * or more messages on the receive channels have gone to the MSG_NEW state.
+ *
+ * Since each channel handles only one message we have to implement a small
+ * interrupt-driven queue on our end. Messages to be sent are placed on the
+ * queue for sending and contain a pointer to an optional callback function.
+ * The handler for a message is called when the message state goes to
+ * MSG_COMPLETE.
+ *
+ * For receiving message we maintain a list of handler functions to call when
+ * a message is received on that IOP/channel combination. The handlers are
+ * called much like an interrupt handler and are passed a copy of the message
+ * from the IOP. The message state will be in MSG_RCVD while the handler runs;
+ * it is the handler's responsibility to call iop_complete_message() when
+ * finished; this function moves the message state to MSG_COMPLETE and signals
+ * the IOP. This two-step process is provided to allow the handler to defer
+ * message processing to a bottom-half handler if the processing will take
+ * a signifigant amount of time (handlers are called at interrupt time so they
+ * should execute quickly.)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_iop.h>
+#include <asm/mac_oss.h>
+
+/*#define DEBUG_IOP*/
+
+/* Set to nonezero if the IOPs are present. Set by iop_init() */
+
+int iop_scc_present,iop_ism_present;
+
+#ifdef CONFIG_PROC_FS
+static int iop_get_proc_info(char *, char **, off_t, int);
+#endif /* CONFIG_PROC_FS */
+
+/* structure for tracking channel listeners */
+
+struct listener {
+	const char *devname;
+	void (*handler)(struct iop_msg *, struct pt_regs *);
+};
+
+/*
+ * IOP structures for the two IOPs
+ *
+ * The SCC IOP controls both serial ports (A and B) as its two functions.
+ * The ISM IOP controls the SWIM (floppy drive) and ADB.
+ */
+
+static volatile struct mac_iop *iop_base[NUM_IOPS];
+
+/*
+ * IOP message queues
+ */
+
+static struct iop_msg iop_msg_pool[NUM_IOP_MSGS];
+static struct iop_msg *iop_send_queue[NUM_IOPS][NUM_IOP_CHAN];
+static struct listener iop_listeners[NUM_IOPS][NUM_IOP_CHAN];
+
+irqreturn_t iop_ism_irq(int, void *, struct pt_regs *);
+
+extern void oss_irq_enable(int);
+
+/*
+ * Private access functions
+ */
+
+static __inline__ void iop_loadaddr(volatile struct mac_iop *iop, __u16 addr)
+{
+	iop->ram_addr_lo = addr;
+	iop->ram_addr_hi = addr >> 8;
+}
+
+static __inline__ __u8 iop_readb(volatile struct mac_iop *iop, __u16 addr)
+{
+	iop->ram_addr_lo = addr;
+	iop->ram_addr_hi = addr >> 8;
+	return iop->ram_data;
+}
+
+static __inline__ void iop_writeb(volatile struct mac_iop *iop, __u16 addr, __u8 data)
+{
+	iop->ram_addr_lo = addr;
+	iop->ram_addr_hi = addr >> 8;
+	iop->ram_data = data;
+}
+
+static __inline__ void iop_stop(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl &= ~IOP_RUN;
+}
+
+static __inline__ void iop_start(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl = IOP_RUN | IOP_AUTOINC;
+}
+
+static __inline__ void iop_bypass(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl |= IOP_BYPASS;
+}
+
+static __inline__ void iop_interrupt(volatile struct mac_iop *iop)
+{
+	iop->status_ctrl |= IOP_IRQ;
+}
+
+static int iop_alive(volatile struct mac_iop *iop)
+{
+	int retval;
+
+	retval = (iop_readb(iop, IOP_ADDR_ALIVE) == 0xFF);
+	iop_writeb(iop, IOP_ADDR_ALIVE, 0);
+	return retval;
+}
+
+static struct iop_msg *iop_alloc_msg(void)
+{
+	int i;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	for (i = 0 ; i < NUM_IOP_MSGS ; i++) {
+		if (iop_msg_pool[i].status == IOP_MSGSTATUS_UNUSED) {
+			iop_msg_pool[i].status = IOP_MSGSTATUS_WAITING;
+			local_irq_restore(flags);
+			return &iop_msg_pool[i];
+		}
+	}
+
+	local_irq_restore(flags);
+	return NULL;
+}
+
+static void iop_free_msg(struct iop_msg *msg)
+{
+	msg->status = IOP_MSGSTATUS_UNUSED;
+}
+
+/*
+ * This is called by the startup code before anything else. Its purpose
+ * is to find and initialize the IOPs early in the boot sequence, so that
+ * the serial IOP can be placed into bypass mode _before_ we try to
+ * initialize the serial console.
+ */
+
+void __init iop_preinit(void)
+{
+	if (macintosh_config->scc_type == MAC_SCC_IOP) {
+		if (macintosh_config->ident == MAC_MODEL_IIFX) {
+			iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_IIFX;
+		} else {
+			iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_QUADRA;
+		}
+		iop_base[IOP_NUM_SCC]->status_ctrl = 0x87;
+		iop_scc_present = 1;
+	} else {
+		iop_base[IOP_NUM_SCC] = NULL;
+		iop_scc_present = 0;
+	}
+	if (macintosh_config->adb_type == MAC_ADB_IOP) {
+		if (macintosh_config->ident == MAC_MODEL_IIFX) {
+			iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_IIFX;
+		} else {
+			iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA;
+		}
+		iop_base[IOP_NUM_ISM]->status_ctrl = 0;
+		iop_ism_present = 1;
+	} else {
+		iop_base[IOP_NUM_ISM] = NULL;
+		iop_ism_present = 0;
+	}
+}
+
+/*
+ * Initialize the IOPs, if present.
+ */
+
+void __init iop_init(void)
+{
+	int i;
+
+	if (iop_scc_present) {
+		printk("IOP: detected SCC IOP at %p\n", iop_base[IOP_NUM_SCC]);
+	}
+	if (iop_ism_present) {
+		printk("IOP: detected ISM IOP at %p\n", iop_base[IOP_NUM_ISM]);
+		iop_start(iop_base[IOP_NUM_ISM]);
+		iop_alive(iop_base[IOP_NUM_ISM]); /* clears the alive flag */
+	}
+
+	/* Make the whole pool available and empty the queues */
+
+	for (i = 0 ; i < NUM_IOP_MSGS ; i++) {
+		iop_msg_pool[i].status = IOP_MSGSTATUS_UNUSED;
+	}
+
+	for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
+		iop_send_queue[IOP_NUM_SCC][i] = 0;
+		iop_send_queue[IOP_NUM_ISM][i] = 0;
+		iop_listeners[IOP_NUM_SCC][i].devname = NULL;
+		iop_listeners[IOP_NUM_SCC][i].handler = NULL;
+		iop_listeners[IOP_NUM_ISM][i].devname = NULL;
+		iop_listeners[IOP_NUM_ISM][i].handler = NULL;
+	}
+
+#if 0	/* Crashing in 2.4 now, not yet sure why.   --jmt */
+#ifdef CONFIG_PROC_FS
+	create_proc_info_entry("mac_iop", 0, &proc_root, iop_get_proc_info);
+#endif
+#endif
+}
+
+/*
+ * Register the interrupt handler for the IOPs.
+ * TODO: might be wrong for non-OSS machines. Anyone?
+ */
+
+void __init iop_register_interrupts(void)
+{
+	if (iop_ism_present) {
+		if (oss_present) {
+			cpu_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq,
+					IRQ_FLG_LOCK, "ISM IOP",
+					(void *) IOP_NUM_ISM);
+			oss_irq_enable(IRQ_MAC_ADB);
+		} else {
+			request_irq(IRQ_VIA2_0, iop_ism_irq,
+					IRQ_FLG_LOCK|IRQ_FLG_FAST, "ISM IOP",
+					(void *) IOP_NUM_ISM);
+		}
+		if (!iop_alive(iop_base[IOP_NUM_ISM])) {
+			printk("IOP: oh my god, they killed the ISM IOP!\n");
+		} else {
+			printk("IOP: the ISM IOP seems to be alive.\n");
+		}
+	}
+}
+
+/*
+ * Register or unregister a listener for a specific IOP and channel
+ *
+ * If the handler pointer is NULL the current listener (if any) is
+ * unregistered. Otherwise the new listener is registered provided
+ * there is no existing listener registered.
+ */
+
+int iop_listen(uint iop_num, uint chan,
+		void (*handler)(struct iop_msg *, struct pt_regs *),
+		const char *devname)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL;
+	if (chan >= NUM_IOP_CHAN) return -EINVAL;
+	if (iop_listeners[iop_num][chan].handler && handler) return -EINVAL;
+	iop_listeners[iop_num][chan].devname = devname;
+	iop_listeners[iop_num][chan].handler = handler;
+	return 0;
+}
+
+/*
+ * Complete reception of a message, which just means copying the reply
+ * into the buffer, setting the channel state to MSG_COMPLETE and
+ * notifying the IOP.
+ */
+
+void iop_complete_message(struct iop_msg *msg)
+{
+	int iop_num = msg->iop_num;
+	int chan = msg->channel;
+	int i,offset;
+
+#ifdef DEBUG_IOP
+	printk("iop_complete(%p): iop %d chan %d\n", msg, msg->iop_num, msg->channel);
+#endif
+
+	offset = IOP_ADDR_RECV_MSG + (msg->channel * IOP_MSG_LEN);
+
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		iop_writeb(iop_base[iop_num], offset, msg->reply[i]);
+	}
+
+	iop_writeb(iop_base[iop_num],
+		   IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE);
+	iop_interrupt(iop_base[msg->iop_num]);
+
+	iop_free_msg(msg);
+}
+
+/*
+ * Actually put a message into a send channel buffer
+ */
+
+static void iop_do_send(struct iop_msg *msg)
+{
+	volatile struct mac_iop *iop = iop_base[msg->iop_num];
+	int i,offset;
+
+	offset = IOP_ADDR_SEND_MSG + (msg->channel * IOP_MSG_LEN);
+
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		iop_writeb(iop, offset, msg->message[i]);
+	}
+
+	iop_writeb(iop, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW);
+
+	iop_interrupt(iop);
+}
+
+/*
+ * Handle sending a message on a channel that
+ * has gone into the IOP_MSG_COMPLETE state.
+ */
+
+static void iop_handle_send(uint iop_num, uint chan, struct pt_regs *regs)
+{
+	volatile struct mac_iop *iop = iop_base[iop_num];
+	struct iop_msg *msg,*msg2;
+	int i,offset;
+
+#ifdef DEBUG_IOP
+	printk("iop_handle_send: iop %d channel %d\n", iop_num, chan);
+#endif
+
+	iop_writeb(iop, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE);
+
+	if (!(msg = iop_send_queue[iop_num][chan])) return;
+
+	msg->status = IOP_MSGSTATUS_COMPLETE;
+	offset = IOP_ADDR_SEND_MSG + (chan * IOP_MSG_LEN);
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		msg->reply[i] = iop_readb(iop, offset);
+	}
+	if (msg->handler) (*msg->handler)(msg, regs);
+	msg2 = msg;
+	msg = msg->next;
+	iop_free_msg(msg2);
+
+	iop_send_queue[iop_num][chan] = msg;
+	if (msg) iop_do_send(msg);
+}
+
+/*
+ * Handle reception of a message on a channel that has
+ * gone into the IOP_MSG_NEW state.
+ */
+
+static void iop_handle_recv(uint iop_num, uint chan, struct pt_regs *regs)
+{
+	volatile struct mac_iop *iop = iop_base[iop_num];
+	int i,offset;
+	struct iop_msg *msg;
+
+#ifdef DEBUG_IOP
+	printk("iop_handle_recv: iop %d channel %d\n", iop_num, chan);
+#endif
+
+	msg = iop_alloc_msg();
+	msg->iop_num = iop_num;
+	msg->channel = chan;
+	msg->status = IOP_MSGSTATUS_UNSOL;
+	msg->handler = iop_listeners[iop_num][chan].handler;
+
+	offset = IOP_ADDR_RECV_MSG + (chan * IOP_MSG_LEN);
+
+	for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) {
+		msg->message[i] = iop_readb(iop, offset);
+	}
+
+	iop_writeb(iop, IOP_ADDR_RECV_STATE + chan, IOP_MSG_RCVD);
+
+	/* If there is a listener, call it now. Otherwise complete */
+	/* the message ourselves to avoid possible stalls.         */
+
+	if (msg->handler) {
+		(*msg->handler)(msg, regs);
+	} else {
+#ifdef DEBUG_IOP
+		printk("iop_handle_recv: unclaimed message on iop %d channel %d\n", iop_num, chan);
+		printk("iop_handle_recv:");
+		for (i = 0 ; i < IOP_MSG_LEN ; i++) {
+			printk(" %02X", (uint) msg->message[i]);
+		}
+		printk("\n");
+#endif
+		iop_complete_message(msg);
+	}
+}
+
+/*
+ * Send a message
+ *
+ * The message is placed at the end of the send queue. Afterwards if the
+ * channel is idle we force an immediate send of the next message in the
+ * queue.
+ */
+
+int iop_send_message(uint iop_num, uint chan, void *privdata,
+		      uint msg_len, __u8 *msg_data,
+		      void (*handler)(struct iop_msg *, struct pt_regs *))
+{
+	struct iop_msg *msg, *q;
+
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL;
+	if (chan >= NUM_IOP_CHAN) return -EINVAL;
+	if (msg_len > IOP_MSG_LEN) return -EINVAL;
+
+	msg = iop_alloc_msg();
+	if (!msg) return -ENOMEM;
+
+	msg->next = NULL;
+	msg->status = IOP_MSGSTATUS_WAITING;
+	msg->iop_num = iop_num;
+	msg->channel = chan;
+	msg->caller_priv = privdata;
+	memcpy(msg->message, msg_data, msg_len);
+	msg->handler = handler;
+
+	if (!(q = iop_send_queue[iop_num][chan])) {
+		iop_send_queue[iop_num][chan] = msg;
+	} else {
+		while (q->next) q = q->next;
+		q->next = msg;
+	}
+
+	if (iop_readb(iop_base[iop_num],
+	    IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE) {
+		iop_do_send(msg);
+	}
+
+	return 0;
+}
+
+/*
+ * Upload code to the shared RAM of an IOP.
+ */
+
+void iop_upload_code(uint iop_num, __u8 *code_start,
+		     uint code_len, __u16 shared_ram_start)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return;
+
+	iop_loadaddr(iop_base[iop_num], shared_ram_start);
+
+	while (code_len--) {
+		iop_base[iop_num]->ram_data = *code_start++;
+	}
+}
+
+/*
+ * Download code from the shared RAM of an IOP.
+ */
+
+void iop_download_code(uint iop_num, __u8 *code_start,
+		       uint code_len, __u16 shared_ram_start)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return;
+
+	iop_loadaddr(iop_base[iop_num], shared_ram_start);
+
+	while (code_len--) {
+		*code_start++ = iop_base[iop_num]->ram_data;
+	}
+}
+
+/*
+ * Compare the code in the shared RAM of an IOP with a copy in system memory
+ * and return 0 on match or the first nonmatching system memory address on
+ * failure.
+ */
+
+__u8 *iop_compare_code(uint iop_num, __u8 *code_start,
+		       uint code_len, __u16 shared_ram_start)
+{
+	if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return code_start;
+
+	iop_loadaddr(iop_base[iop_num], shared_ram_start);
+
+	while (code_len--) {
+		if (*code_start != iop_base[iop_num]->ram_data) {
+			return code_start;
+		}
+		code_start++;
+	}
+	return (__u8 *) 0;
+}
+
+/*
+ * Handle an ISM IOP interrupt
+ */
+
+irqreturn_t iop_ism_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	uint iop_num = (uint) dev_id;
+	volatile struct mac_iop *iop = iop_base[iop_num];
+	int i,state;
+
+#ifdef DEBUG_IOP
+	printk("iop_ism_irq: status = %02X\n", (uint) iop->status_ctrl);
+#endif
+
+	/* INT0 indicates a state change on an outgoing message channel */
+
+	if (iop->status_ctrl & IOP_INT0) {
+		iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
+#ifdef DEBUG_IOP
+		printk("iop_ism_irq: new status = %02X, send states",
+			(uint) iop->status_ctrl);
+#endif
+		for (i = 0 ; i < NUM_IOP_CHAN  ; i++) {
+			state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
+#ifdef DEBUG_IOP
+			printk(" %02X", state);
+#endif
+			if (state == IOP_MSG_COMPLETE) {
+				iop_handle_send(iop_num, i, regs);
+			}
+		}
+#ifdef DEBUG_IOP
+		printk("\n");
+#endif
+	}
+
+	if (iop->status_ctrl & IOP_INT1) {	/* INT1 for incoming msgs */
+		iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
+#ifdef DEBUG_IOP
+		printk("iop_ism_irq: new status = %02X, recv states",
+			(uint) iop->status_ctrl);
+#endif
+		for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
+			state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
+#ifdef DEBUG_IOP
+			printk(" %02X", state);
+#endif
+			if (state == IOP_MSG_NEW) {
+				iop_handle_recv(iop_num, i, regs);
+			}
+		}
+#ifdef DEBUG_IOP
+		printk("\n");
+#endif
+	}
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PROC_FS
+
+char *iop_chan_state(int state)
+{
+	switch(state) {
+		case IOP_MSG_IDLE	: return "idle      ";
+		case IOP_MSG_NEW	: return "new       ";
+		case IOP_MSG_RCVD	: return "received  ";
+		case IOP_MSG_COMPLETE	: return "completed ";
+		default			: return "unknown   ";
+	}
+}
+
+int iop_dump_one_iop(char *buf, int iop_num, char *iop_name)
+{
+	int i,len = 0;
+	volatile struct mac_iop *iop = iop_base[iop_num];
+
+	len += sprintf(buf+len, "%s IOP channel states:\n\n", iop_name);
+	len += sprintf(buf+len, "##  send_state  recv_state  device\n");
+	len += sprintf(buf+len, "------------------------------------------------\n");
+	for (i = 0 ; i < NUM_IOP_CHAN ; i++) {
+		len += sprintf(buf+len, "%2d  %10s  %10s  %s\n", i,
+			iop_chan_state(iop_readb(iop, IOP_ADDR_SEND_STATE+i)),
+			iop_chan_state(iop_readb(iop, IOP_ADDR_RECV_STATE+i)),
+			iop_listeners[iop_num][i].handler?
+				      iop_listeners[iop_num][i].devname : "");
+
+	}
+	len += sprintf(buf+len, "\n");
+	return len;
+}
+
+static int iop_get_proc_info(char *buf, char **start, off_t pos, int count)
+{
+	int len, cnt;
+
+	cnt = 0;
+	len =  sprintf(buf, "IOPs detected:\n\n");
+
+	if (iop_scc_present) {
+		len += sprintf(buf+len, "SCC IOP (%p): status %02X\n",
+				iop_base[IOP_NUM_SCC],
+				(uint) iop_base[IOP_NUM_SCC]->status_ctrl);
+	}
+	if (iop_ism_present) {
+		len += sprintf(buf+len, "ISM IOP (%p): status %02X\n\n",
+				iop_base[IOP_NUM_ISM],
+				(uint) iop_base[IOP_NUM_ISM]->status_ctrl);
+	}
+
+	if (iop_scc_present) {
+		len += iop_dump_one_iop(buf+len, IOP_NUM_SCC, "SCC");
+
+	}
+
+	if (iop_ism_present) {
+		len += iop_dump_one_iop(buf+len, IOP_NUM_ISM, "ISM");
+
+	}
+
+	if (len >= pos) {
+		if (!*start) {
+			*start = buf + pos;
+			cnt = len - pos;
+		} else {
+			cnt += len;
+		}
+	}
+	return (count > cnt) ? cnt : count;
+}
+
+#endif /* CONFIG_PROC_FS */
diff --git a/arch/m68k/mac/mac_ksyms.c b/arch/m68k/mac/mac_ksyms.c
new file mode 100644
index 0000000..6e37ceb
--- /dev/null
+++ b/arch/m68k/mac/mac_ksyms.c
@@ -0,0 +1,8 @@
+#include <linux/module.h>
+#include <asm/ptrace.h>
+#include <asm/traps.h>
+
+/* Says whether we're using A/UX interrupts or not */
+extern int via_alt_mapping;
+
+EXPORT_SYMBOL(via_alt_mapping);
diff --git a/arch/m68k/mac/mac_penguin.S b/arch/m68k/mac/mac_penguin.S
new file mode 100644
index 0000000..b3ce30b
--- /dev/null
+++ b/arch/m68k/mac/mac_penguin.S
@@ -0,0 +1,75 @@
+.byte \
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0xFF,0x0F,0xF0,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x00,0xFF,0xFF,0x0F,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x0F,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xF0,0xFF,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,\
+0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0xF0,0x00,0x00,\
+0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,\
+0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,\
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,\
+0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00
diff --git a/arch/m68k/mac/macboing.c b/arch/m68k/mac/macboing.c
new file mode 100644
index 0000000..44c5cd2
--- /dev/null
+++ b/arch/m68k/mac/macboing.c
@@ -0,0 +1,309 @@
+/*
+ *	Mac bong noise generator. Note - we ought to put a boingy noise
+ *	here 8)
+ *
+ *	----------------------------------------------------------------------
+ *	16.11.98:
+ *	rewrote some functions, added support for Enhanced ASC (Quadras)
+ *	after the NetBSD asc.c console bell patch by Colin Wood/Frederick Bruck
+ *	Juergen Mellinger (juergen.mellinger@t-online.de)
+ */
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+
+#include <asm/macintosh.h>
+#include <asm/mac_asc.h>
+
+static int mac_asc_inited;
+/*
+ * dumb triangular wave table
+ */
+static __u8 mac_asc_wave_tab[ 0x800 ];
+
+/*
+ * Alan's original sine table; needs interpolating to 0x800
+ * (hint: interpolate or hardwire [0 -> Pi/2[, it's symmetric)
+ */
+static const signed char sine_data[] = {
+	0,  39,  75,  103,  121,  127,  121,  103,  75,  39,
+	0, -39, -75, -103, -121, -127, -121, -103, -75, -39
+};
+
+/*
+ * where the ASC hides ...
+ */
+static volatile __u8* mac_asc_regs = ( void* )0x50F14000;
+
+/*
+ * sample rate; is this a good default value?
+ */
+static unsigned long mac_asc_samplespersec = 11050;
+static int mac_bell_duration;
+static unsigned long mac_bell_phase; /* 0..2*Pi -> 0..0x800 (wavetable size) */
+static unsigned long mac_bell_phasepersample;
+
+/*
+ * some function protos
+ */
+static void mac_init_asc( void );
+static void mac_nosound( unsigned long );
+static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int );
+static void mac_quadra_ring_bell( unsigned long );
+static void mac_av_start_bell( unsigned int, unsigned int, unsigned int );
+static void ( *mac_special_bell )( unsigned int, unsigned int, unsigned int );
+
+/*
+ * our timer to start/continue/stop the bell
+ */
+static struct timer_list mac_sound_timer =
+		TIMER_INITIALIZER(mac_nosound, 0, 0);
+
+/*
+ * Sort of initialize the sound chip (called from mac_mksound on the first
+ * beep).
+ */
+static void mac_init_asc( void )
+{
+	int i;
+
+	/*
+	 * do some machine specific initialization
+	 * BTW:
+	 * the NetBSD Quadra patch identifies the Enhanced Apple Sound Chip via
+	 *	mac_asc_regs[ 0x800 ] & 0xF0 != 0
+	 * this makes no sense here, because we have to set the default sample
+	 * rate anyway if we want correct frequencies
+	 */
+	switch ( macintosh_config->ident )
+	{
+		case MAC_MODEL_IIFX:
+			/*
+			 * The IIfx is always special ...
+			 */
+			mac_asc_regs = ( void* )0x50010000;
+			break;
+			/*
+			 * not sure about how correct this list is
+			 * machines with the EASC enhanced apple sound chip
+			 */
+		case MAC_MODEL_Q630:
+		case MAC_MODEL_P475:
+			mac_special_bell = mac_quadra_start_bell;
+			mac_asc_samplespersec = 22150;
+			break;
+		case MAC_MODEL_C660:
+		case MAC_MODEL_Q840:
+			/*
+			 * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O.
+			 * It appears to be similar to the "AWACS" custom ASIC in the Power Mac
+			 * [678]100.  Because Singer and AWACS may have a similar hardware
+			 * interface, this would imply that the code in drivers/sound/dmasound.c
+			 * for AWACS could be used as a basis for Singer support.  All we have to
+			 * do is figure out how to do DMA on the 660AV/840AV through the PSC and
+			 * figure out where the Singer hardware sits in memory. (I'd look in the
+			 * vicinity of the AWACS location in a Power Mac [678]100 first, or the
+			 * current location of the Apple Sound Chip--ASC--in other Macs.)  The
+			 * Power Mac [678]100 info can be found in MkLinux Mach kernel sources.
+			 *
+			 * Quoted from Apple's Tech Info Library, article number 16405:
+			 *   "Among desktop Macintosh computers, only the 660AV, 840AV, and Power
+			 *   Macintosh models have 16-bit audio input and output capability
+			 *   because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer
+			 *   codec circuitry in the AVs.  The Audio Waveform Amplifier and
+			 *   Converter (AWAC) chip in the Power Macintosh performs the same
+			 *   16-bit I/O functionality.  The PowerBook 500 series computers
+			 *   support 16-bit stereo output, but only mono input."
+			 *
+			 *   http://til.info.apple.com/techinfo.nsf/artnum/n16405
+			 *
+			 * --David Kilzer
+			 */
+			mac_special_bell = mac_av_start_bell;
+			break;
+		case MAC_MODEL_Q650:
+		case MAC_MODEL_Q700:
+		case MAC_MODEL_Q800:
+		case MAC_MODEL_Q900:
+		case MAC_MODEL_Q950:
+			/*
+			 * Currently not implemented!
+			 */
+			mac_special_bell = NULL;
+			break;
+		default:
+			/*
+			 * Every switch needs a default
+			 */
+			mac_special_bell = NULL;
+			break;
+	}
+
+	/*
+	 * init the wave table with a simple triangular wave
+	 * A sine wave would sure be nicer here ...
+	 */
+	for ( i = 0; i < 0x400; i++ )
+	{
+		mac_asc_wave_tab[ i ] = i / 4;
+		mac_asc_wave_tab[ i + 0x400 ] = 0xFF - i / 4;
+	}
+	mac_asc_inited = 1;
+}
+
+/*
+ * Called to make noise; current single entry to the boing driver.
+ * Does the job for simple ASC, calls other routines else.
+ * XXX Fixme:
+ * Should be split into asc_mksound, easc_mksound, av_mksound and
+ * function pointer set in mac_init_asc which would be called at
+ * init time.
+ * _This_ is rather ugly ...
+ */
+void mac_mksound( unsigned int freq, unsigned int length )
+{
+	__u32 cfreq = ( freq << 5 ) / 468;
+	__u32 flags;
+	int i;
+
+	if ( mac_special_bell == NULL )
+	{
+		/* Do nothing */
+		return;
+	}
+
+	if ( !mac_asc_inited )
+		mac_init_asc();
+
+	if ( mac_special_bell )
+	{
+		mac_special_bell( freq, length, 128 );
+		return;
+	}
+
+	if ( freq < 20 || freq > 20000 || length == 0 )
+	{
+		mac_nosound( 0 );
+		return;
+	}
+
+	local_irq_save(flags);
+
+	del_timer( &mac_sound_timer );
+
+	for ( i = 0; i < 0x800; i++ )
+		mac_asc_regs[ i ] = 0;
+	for ( i = 0; i < 0x800; i++ )
+		mac_asc_regs[ i ] = mac_asc_wave_tab[ i ];
+
+	for ( i = 0; i < 8; i++ )
+		*( __u32* )( ( __u32 )mac_asc_regs + ASC_CONTROL + 0x814 + 8 * i ) = cfreq;
+
+	mac_asc_regs[ 0x807 ] = 0;
+	mac_asc_regs[ ASC_VOLUME ] = 128;
+	mac_asc_regs[ 0x805 ] = 0;
+	mac_asc_regs[ 0x80F ] = 0;
+	mac_asc_regs[ ASC_MODE ] = ASC_MODE_SAMPLE;
+	mac_asc_regs[ ASC_ENABLE ] = ASC_ENABLE_SAMPLE;
+
+	mac_sound_timer.expires = jiffies + length;
+	add_timer( &mac_sound_timer );
+
+	local_irq_restore(flags);
+}
+
+/*
+ * regular ASC: stop whining ..
+ */
+static void mac_nosound( unsigned long ignored )
+{
+	mac_asc_regs[ ASC_ENABLE ] = 0;
+}
+
+/*
+ * EASC entry; init EASC, don't load wavetable, schedule 'start whining'.
+ */
+static void mac_quadra_start_bell( unsigned int freq, unsigned int length, unsigned int volume )
+{
+	__u32 flags;
+
+	/* if the bell is already ringing, ring longer */
+	if ( mac_bell_duration > 0 )
+	{
+		mac_bell_duration += length;
+		return;
+	}
+
+	mac_bell_duration = length;
+	mac_bell_phase = 0;
+	mac_bell_phasepersample = ( freq * sizeof( mac_asc_wave_tab ) ) / mac_asc_samplespersec;
+	/* this is reasonably big for small frequencies */
+
+	local_irq_save(flags);
+
+	/* set the volume */
+	mac_asc_regs[ 0x806 ] = volume;
+
+	/* set up the ASC registers */
+	if ( mac_asc_regs[ 0x801 ] != 1 )
+	{
+		/* select mono mode */
+		mac_asc_regs[ 0x807 ] = 0;
+		/* select sampled sound mode */
+		mac_asc_regs[ 0x802 ] = 0;
+		/* ??? */
+		mac_asc_regs[ 0x801 ] = 1;
+		mac_asc_regs[ 0x803 ] |= 0x80;
+		mac_asc_regs[ 0x803 ] &= 0x7F;
+	}
+
+	mac_sound_timer.function = mac_quadra_ring_bell;
+	mac_sound_timer.expires = jiffies + 1;
+	add_timer( &mac_sound_timer );
+
+	local_irq_restore(flags);
+}
+
+/*
+ * EASC 'start/continue whining'; I'm not sure why the above function didn't
+ * already load the wave table, or at least call this one...
+ * This piece keeps reloading the wave table until done.
+ */
+static void mac_quadra_ring_bell( unsigned long ignored )
+{
+	int	i, count = mac_asc_samplespersec / HZ;
+	__u32 flags;
+
+	/*
+	 * we neither want a sound buffer overflow nor underflow, so we need to match
+	 * the number of samples per timer interrupt as exactly as possible.
+	 * using the asc interrupt will give better results in the future
+	 * ...and the possibility to use a real sample (a boingy noise, maybe...)
+	 */
+
+	local_irq_save(flags);
+
+	del_timer( &mac_sound_timer );
+
+	if ( mac_bell_duration-- > 0 )
+	{
+		for ( i = 0; i < count; i++ )
+		{
+			mac_bell_phase += mac_bell_phasepersample;
+			mac_asc_regs[ 0 ] = mac_asc_wave_tab[ mac_bell_phase & ( sizeof( mac_asc_wave_tab ) - 1 ) ];
+		}
+		mac_sound_timer.expires = jiffies + 1;
+		add_timer( &mac_sound_timer );
+	}
+	else
+		mac_asc_regs[ 0x801 ] = 0;
+
+	local_irq_restore(flags);
+}
+
+/*
+ * AV code - please fill in.
+ */
+static void mac_av_start_bell( unsigned int freq, unsigned int length, unsigned int volume )
+{
+}
diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c
new file mode 100644
index 0000000..1809601
--- /dev/null
+++ b/arch/m68k/mac/macints.c
@@ -0,0 +1,760 @@
+/*
+ *	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 (CA2)
+ *		  - slot 1: VBlank (CA1)
+ *		  - 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 or RBV
+ *		  - slot 0: SCSI DRQ (CA2)
+ *		  - slot 1: NUBUS IRQ (CA1) need to read port A to find which
+ *		  - slot 2: /EXP IRQ (only on IIci)
+ *		  - slot 3: SCSI IRQ (CB2)
+ *		  - slot 4: ASC IRQ (CB1)
+ *		  - slot 5: timer 2 (not on IIci)
+ *		  - slot 6: timer 1 (not on IIci)
+ *		  - slot 7: status of IRQ; signals 'any enabled int.'
+ *
+ *	2	- OSS (IIfx only?)
+ *		  - slot 0: SCSI interrupt
+ *		  - slot 1: Sound interrupt
+ *
+ * Levels 3-6 vary by machine type. For VIA or RBV Macintoshes:
+ *
+ *	3	- unused (?)
+ *
+ *	4	- SCC (slot number determined by reading RR3 on the SSC itself)
+ *		  - slot 1: SCC channel A
+ *		  - slot 2: SCC channel B
+ *
+ *	5	- unused (?)
+ *		  [serial errors or special conditions seem to raise level 6
+ *		  interrupts on some models (LC4xx?)]
+ *
+ *	6	- off switch (?)
+ *
+ * For OSS Macintoshes (IIfx only at this point):
+ *
+ *	3	- Nubus interrupt
+ *		  - slot 0: Slot $9
+ *		  - slot 1: Slot $A
+ *		  - slot 2: Slot $B
+ *		  - slot 3: Slot $C
+ *		  - slot 4: Slot $D
+ *		  - slot 5: Slot $E
+ *
+ *	4	- SCC IOP
+ *		  - slot 1: SCC channel A
+ *		  - slot 2: SCC channel B
+ *
+ *	5	- ISM IOP (ADB?)
+ *
+ *	6	- unused
+ *
+ * For PSC Macintoshes (660AV, 840AV):
+ *
+ *	3	- PSC level 3
+ *		  - slot 0: MACE
+ *
+ *	4	- PSC level 4
+ *		  - slot 1: SCC channel A interrupt
+ *		  - slot 2: SCC channel B interrupt
+ *		  - slot 3: MACE DMA
+ *
+ *	5	- PSC level 5
+ *
+ *	6	- PSC level 6
+ *
+ * Finally we have good 'ole level 7, the non-maskable interrupt:
+ *
+ *	7	- NMI (programmer's switch on the back of some Macs)
+ *		  Also RAM parity error on models which support it (IIc, IIfx?)
+ *
+ * The current interrupt logic looks something like this:
+ *
+ * - We install dispatchers for the autovector interrupts (1-7). These
+ *   dispatchers are responsible for querying the hardware (the
+ *   VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using
+ *   this information a machspec interrupt number is generated by placing the
+ *   index of the interrupt hardware into the low three bits and the original
+ *   autovector interrupt number in the upper 5 bits. The handlers for the
+ *   resulting machspec interrupt are then called.
+ *
+ * - Nubus is a special case because its interrupts are hidden behind two
+ *   layers of hardware. Nubus interrupts come in as index 1 on VIA #2,
+ *   which translates to IRQ number 17. In this spot we install _another_
+ *   dispatcher. This dispatcher finds the interrupting slot number (9-F) and
+ *   then forms a new machspec interrupt number as above with the slot number
+ *   minus 9 in the low three bits and the pseudo-level 7 in the upper five
+ *   bits.  The handlers for this new machspec interrupt number are then
+ *   called. This puts Nubus interrupts into the range 56-62.
+ *
+ * - The Baboon interrupts (used on some PowerBooks) are an even more special
+ *   case. They're hidden behind the Nubus slot $C interrupt thus adding a
+ *   third layer of indirection. Why oh why did the Apple engineers do that?
+ *
+ * - We support "fast" and "slow" handlers, just like the Amiga port. The
+ *   fast handlers are called first and with all interrupts disabled. They
+ *   are expected to execute quickly (hence the name). The slow handlers are
+ *   called last with interrupts enabled and the interrupt level restored.
+ *   They must therefore be reentrant.
+ *
+ *   TODO:
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/interrupt.h> /* for intr_count */
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/bootinfo.h>
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/mac_via.h>
+#include <asm/mac_psc.h>
+#include <asm/hwtest.h>
+#include <asm/errno.h>
+#include <asm/macints.h>
+
+#define DEBUG_SPURIOUS
+#define SHUTUP_SONIC
+
+/*
+ * The mac_irq_list array is an array of linked lists of irq_node_t nodes.
+ * Each node contains one handler to be called whenever the interrupt
+ * occurs, with fast handlers listed before slow handlers.
+ */
+
+irq_node_t *mac_irq_list[NUM_MAC_SOURCES];
+
+/* SCC interrupt mask */
+
+static int scc_mask;
+
+/*
+ * VIA/RBV hooks
+ */
+
+extern void via_init(void);
+extern void via_register_interrupts(void);
+extern void via_irq_enable(int);
+extern void via_irq_disable(int);
+extern void via_irq_clear(int);
+extern int  via_irq_pending(int);
+
+/*
+ * OSS hooks
+ */
+
+extern int oss_present;
+
+extern void oss_init(void);
+extern void oss_register_interrupts(void);
+extern void oss_irq_enable(int);
+extern void oss_irq_disable(int);
+extern void oss_irq_clear(int);
+extern int  oss_irq_pending(int);
+
+/*
+ * PSC hooks
+ */
+
+extern int psc_present;
+
+extern void psc_init(void);
+extern void psc_register_interrupts(void);
+extern void psc_irq_enable(int);
+extern void psc_irq_disable(int);
+extern void psc_irq_clear(int);
+extern int  psc_irq_pending(int);
+
+/*
+ * IOP hooks
+ */
+
+extern void iop_register_interrupts(void);
+
+/*
+ * Baboon hooks
+ */
+
+extern int baboon_present;
+
+extern void baboon_init(void);
+extern void baboon_register_interrupts(void);
+extern void baboon_irq_enable(int);
+extern void baboon_irq_disable(int);
+extern void baboon_irq_clear(int);
+extern int  baboon_irq_pending(int);
+
+/*
+ * SCC interrupt routines
+ */
+
+static void scc_irq_enable(int);
+static void scc_irq_disable(int);
+
+/*
+ * console_loglevel determines NMI handler function
+ */
+
+extern irqreturn_t mac_bang(int, void *, struct pt_regs *);
+irqreturn_t mac_nmi_handler(int, void *, struct pt_regs *);
+irqreturn_t mac_debug_handler(int, void *, struct pt_regs *);
+
+/* #define DEBUG_MACINTS */
+
+void mac_init_IRQ(void)
+{
+        int i;
+
+#ifdef DEBUG_MACINTS
+	printk("mac_init_IRQ(): Setting things up...\n");
+#endif
+	/* Initialize the IRQ handler lists. Initially each list is empty, */
+
+	for (i = 0; i < NUM_MAC_SOURCES; i++) {
+		mac_irq_list[i] = NULL;
+	}
+
+	scc_mask = 0;
+
+	/* Make sure the SONIC interrupt is cleared or things get ugly */
+#ifdef SHUTUP_SONIC
+	printk("Killing onboard sonic... ");
+	/* This address should hopefully be mapped already */
+	if (hwreg_present((void*)(0x50f0a000))) {
+		*(long *)(0x50f0a014) = 0x7fffL;
+		*(long *)(0x50f0a010) = 0L;
+	}
+	printk("Done.\n");
+#endif /* SHUTUP_SONIC */
+
+	/*
+	 * Now register the handlers for the master IRQ handlers
+	 * at levels 1-7. Most of the work is done elsewhere.
+	 */
+
+	if (oss_present) {
+		oss_register_interrupts();
+	} else {
+		via_register_interrupts();
+	}
+	if (psc_present) psc_register_interrupts();
+	if (baboon_present) baboon_register_interrupts();
+	iop_register_interrupts();
+	cpu_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI",
+			mac_nmi_handler);
+#ifdef DEBUG_MACINTS
+	printk("mac_init_IRQ(): Done!\n");
+#endif
+}
+
+/*
+ * Routines to work with irq_node_t's on linked lists lifted from
+ * the Amiga code written by Roman Zippel.
+ */
+
+static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node)
+{
+	unsigned long flags;
+	irq_node_t *cur;
+
+	if (!node->dev_id)
+		printk("%s: Warning: dev_id of %s is zero\n",
+		       __FUNCTION__, node->devname);
+
+	local_irq_save(flags);
+
+	cur = *list;
+
+	if (node->flags & IRQ_FLG_FAST) {
+		node->flags &= ~IRQ_FLG_SLOW;
+		while (cur && cur->flags & IRQ_FLG_FAST) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	} else if (node->flags & IRQ_FLG_SLOW) {
+		while (cur) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	} else {
+		while (cur && !(cur->flags & IRQ_FLG_SLOW)) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	}
+
+	node->next = cur;
+	*list = node;
+
+	local_irq_restore(flags);
+}
+
+static inline void mac_delete_irq(irq_node_t **list, void *dev_id)
+{
+	unsigned long flags;
+	irq_node_t *node;
+
+	local_irq_save(flags);
+
+	for (node = *list; node; list = &node->next, node = *list) {
+		if (node->dev_id == dev_id) {
+			*list = node->next;
+			/* Mark it as free. */
+			node->handler = NULL;
+			local_irq_restore(flags);
+			return;
+		}
+	}
+	local_irq_restore(flags);
+	printk ("%s: tried to remove invalid irq\n", __FUNCTION__);
+}
+
+/*
+ * Call all the handlers for a given interrupt. Fast handlers are called
+ * first followed by slow handlers.
+ *
+ * This code taken from the original Amiga code written by Roman Zippel.
+ */
+
+void mac_do_irq_list(int irq, struct pt_regs *fp)
+{
+	irq_node_t *node, *slow_nodes;
+	unsigned long flags;
+
+	kstat_cpu(0).irqs[irq]++;
+
+#ifdef DEBUG_SPURIOUS
+	if (!mac_irq_list[irq] && (console_loglevel > 7)) {
+		printk("mac_do_irq_list: spurious interrupt %d!\n", irq);
+		return;
+	}
+#endif
+
+	/* serve first fast and normal handlers */
+	for (node = mac_irq_list[irq];
+	     node && (!(node->flags & IRQ_FLG_SLOW));
+	     node = node->next)
+		node->handler(irq, node->dev_id, fp);
+	if (!node) return;
+	local_save_flags(flags);
+	local_irq_restore((flags & ~0x0700) | (fp->sr & 0x0700));
+	/* if slow handlers exists, serve them now */
+	slow_nodes = node;
+	for (; node; node = node->next) {
+		node->handler(irq, node->dev_id, fp);
+	}
+}
+
+/*
+ *  mac_enable_irq - enable an interrupt source
+ * mac_disable_irq - disable an interrupt source
+ *   mac_clear_irq - clears a pending interrupt
+ * mac_pending_irq - Returns the pending status of an IRQ (nonzero = pending)
+ *
+ * These routines are just dispatchers to the VIA/OSS/PSC routines.
+ */
+
+void mac_enable_irq (unsigned int irq)
+{
+	int irq_src	= IRQ_SRC(irq);
+
+	switch(irq_src) {
+		case 1: via_irq_enable(irq);
+			break;
+		case 2:
+		case 7: if (oss_present) {
+				oss_irq_enable(irq);
+			} else {
+				via_irq_enable(irq);
+			}
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				psc_irq_enable(irq);
+			} else if (oss_present) {
+				oss_irq_enable(irq);
+			} else if (irq_src == 4) {
+				scc_irq_enable(irq);
+			}
+			break;
+		case 8: if (baboon_present) {
+				baboon_irq_enable(irq);
+			}
+			break;
+	}
+}
+
+void mac_disable_irq (unsigned int irq)
+{
+	int irq_src	= IRQ_SRC(irq);
+
+	switch(irq_src) {
+		case 1: via_irq_disable(irq);
+			break;
+		case 2:
+		case 7: if (oss_present) {
+				oss_irq_disable(irq);
+			} else {
+				via_irq_disable(irq);
+			}
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				psc_irq_disable(irq);
+			} else if (oss_present) {
+				oss_irq_disable(irq);
+			} else if (irq_src == 4) {
+				scc_irq_disable(irq);
+			}
+			break;
+		case 8: if (baboon_present) {
+				baboon_irq_disable(irq);
+			}
+			break;
+	}
+}
+
+void mac_clear_irq( unsigned int irq )
+{
+	switch(IRQ_SRC(irq)) {
+		case 1: via_irq_clear(irq);
+			break;
+		case 2:
+		case 7: if (oss_present) {
+				oss_irq_clear(irq);
+			} else {
+				via_irq_clear(irq);
+			}
+			break;
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				psc_irq_clear(irq);
+			} else if (oss_present) {
+				oss_irq_clear(irq);
+			}
+			break;
+		case 8: if (baboon_present) {
+				baboon_irq_clear(irq);
+			}
+			break;
+	}
+}
+
+int mac_irq_pending( unsigned int irq )
+{
+	switch(IRQ_SRC(irq)) {
+		case 1: return via_irq_pending(irq);
+		case 2:
+		case 7: if (oss_present) {
+				return oss_irq_pending(irq);
+			} else {
+				return via_irq_pending(irq);
+			}
+		case 3:
+		case 4:
+		case 5:
+		case 6: if (psc_present) {
+				return psc_irq_pending(irq);
+			} else if (oss_present) {
+				return oss_irq_pending(irq);
+			}
+	}
+	return 0;
+}
+
+/*
+ * Add an interrupt service routine to an interrupt source.
+ * Returns 0 on success.
+ *
+ * FIXME: You can register interrupts on nonexistent source (ie PSC4 on a
+ *        non-PSC machine). We should return -EINVAL in those cases.
+ */
+
+int mac_request_irq(unsigned int irq,
+		    irqreturn_t (*handler)(int, void *, struct pt_regs *),
+		    unsigned long flags, const char *devname, void *dev_id)
+{
+	irq_node_t *node;
+
+#ifdef DEBUG_MACINTS
+	printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname);
+#endif
+
+	if (irq < VIA1_SOURCE_BASE) {
+		return cpu_request_irq(irq, handler, flags, devname, dev_id);
+	}
+
+	if (irq >= NUM_MAC_SOURCES) {
+		printk ("%s: unknown irq %d requested by %s\n",
+		        __FUNCTION__, irq, devname);
+	}
+
+	/* Get a node and stick it onto the right list */
+
+	if (!(node = new_irq_node())) return -ENOMEM;
+
+	node->handler	= handler;
+	node->flags	= flags;
+	node->dev_id	= dev_id;
+	node->devname	= devname;
+	node->next	= NULL;
+	mac_insert_irq(&mac_irq_list[irq], node);
+
+	/* Now enable the IRQ source */
+
+	mac_enable_irq(irq);
+
+	return 0;
+}
+
+/*
+ * Removes an interrupt service routine from an interrupt source.
+ */
+
+void mac_free_irq(unsigned int irq, void *dev_id)
+{
+#ifdef DEBUG_MACINTS
+	printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id);
+#endif
+
+	if (irq < VIA1_SOURCE_BASE) {
+		cpu_free_irq(irq, dev_id);
+		return;
+	}
+
+	if (irq >= NUM_MAC_SOURCES) {
+		printk ("%s: unknown irq %d freed\n",
+		        __FUNCTION__, irq);
+		return;
+	}
+
+	mac_delete_irq(&mac_irq_list[irq], dev_id);
+
+	/* If the list for this interrupt is */
+	/* empty then disable the source.    */
+
+	if (!mac_irq_list[irq]) {
+		mac_disable_irq(irq);
+	}
+}
+
+/*
+ * Generate a pretty listing for /proc/interrupts
+ *
+ * By the time we're called the autovector interrupt list has already been
+ * generated, so we just need to do the machspec interrupts.
+ *
+ * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers.
+ *                Also removed display of num_spurious it is already
+ *		  displayed for us as autovector irq 0.
+ */
+
+int show_mac_interrupts(struct seq_file *p, void *v)
+{
+	int i;
+	irq_node_t *node;
+	char *base;
+
+	/* Don't do Nubus interrupts in this loop; we do them separately  */
+	/* below so that we can print slot numbers instead of IRQ numbers */
+
+	for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) {
+
+		/* Nonexistant interrupt or nothing registered; skip it. */
+
+		if ((node = mac_irq_list[i]) == NULL) continue;
+		if (node->flags & IRQ_FLG_STD) continue;
+
+		base = "";
+		switch(IRQ_SRC(i)) {
+			case 1: base = "via1";
+				break;
+			case 2: if (oss_present) {
+					base = "oss";
+				} else {
+					base = "via2";
+				}
+				break;
+			case 3:
+			case 4:
+			case 5:
+			case 6: if (psc_present) {
+					base = "psc";
+				} else if (oss_present) {
+					base = "oss";
+				} else {
+					if (IRQ_SRC(i) == 4) base = "scc";
+				}
+				break;
+			case 7: base = "nbus";
+				break;
+			case 8: base = "bbn";
+				break;
+		}
+		seq_printf(p, "%4s %2d: %10u ", base, i, kstat_cpu(0).irqs[i]);
+
+		do {
+			if (node->flags & IRQ_FLG_FAST) {
+				seq_puts(p, "F ");
+			} else if (node->flags & IRQ_FLG_SLOW) {
+				seq_puts(p, "S ");
+			} else {
+				seq_puts(p, "  ");
+			}
+			seq_printf(p, "%s\n", node->devname);
+			if ((node = node->next)) {
+				seq_puts(p, "                    ");
+			}
+		} while(node);
+
+	}
+	return 0;
+}
+
+void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+#ifdef DEBUG_SPURIOUS
+	printk("Unexpected IRQ %d on device %p\n", irq, dev_id);
+#endif
+}
+
+static int num_debug[8];
+
+irqreturn_t 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]++;
+	}
+	return IRQ_HANDLED;
+}
+
+static int in_nmi;
+static volatile int nmi_hold;
+
+irqreturn_t 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++;
+	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 ( 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--;
+	return IRQ_HANDLED;
+}
+
+/*
+ * Simple routines for masking and unmasking
+ * SCC interrupts in cases where this can't be
+ * done in hardware (only the PSC can do that.)
+ */
+
+static void scc_irq_enable(int irq) {
+	int irq_idx     = IRQ_IDX(irq);
+
+	scc_mask |= (1 << irq_idx);
+}
+
+static void scc_irq_disable(int irq) {
+	int irq_idx     = IRQ_IDX(irq);
+
+	scc_mask &= ~(1 << irq_idx);
+}
+
+/*
+ * SCC master interrupt handler. We have to do a bit of magic here
+ * to figure out what channel gave us the interrupt; putting this
+ * here is cleaner than hacking it into drivers/char/macserial.c.
+ */
+
+void mac_scc_dispatch(int irq, void *dev_id, struct pt_regs *regs)
+{
+	volatile unsigned char *scc = (unsigned char *) mac_bi_data.sccbase + 2;
+	unsigned char reg;
+	unsigned long flags;
+
+	/* Read RR3 from the chip. Always do this on channel A */
+	/* This must be an atomic operation so disable irqs.   */
+
+	local_irq_save(flags);
+	*scc = 3;
+	reg = *scc;
+	local_irq_restore(flags);
+
+	/* Now dispatch. Bits 0-2 are for channel B and */
+	/* bits 3-5 are for channel A. We can safely    */
+	/* ignore the remaining bits here.              */
+	/*                                              */
+	/* Note that we're ignoring scc_mask for now.   */
+	/* If we actually mask the ints then we tend to */
+	/* get hammered by very persistent SCC irqs,    */
+	/* and since they're autovector interrupts they */
+	/* pretty much kill the system.                 */
+
+	if (reg & 0x38) mac_do_irq_list(IRQ_SCCA, regs);
+	if (reg & 0x07) mac_do_irq_list(IRQ_SCCB, regs);
+}
diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c
new file mode 100644
index 0000000..5b80d7c
--- /dev/null
+++ b/arch/m68k/mac/misc.c
@@ -0,0 +1,651 @@
+/*
+ * Miscellaneous Mac68K-specific stuff
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/mm.h>
+
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/rtc.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/setup.h>
+#include <asm/macintosh.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+#define BOOTINFO_COMPAT_1_0
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+
+/* Offset between Unix time (1970-based) and Mac time (1904-based) */
+
+#define RTC_OFFSET 2082844800
+
+extern struct mac_booter_data mac_bi_data;
+static void (*rom_reset)(void);
+
+#ifdef CONFIG_ADB
+/*
+ * Return the current time as the number of seconds since January 1, 1904.
+ */
+
+static long adb_read_time(void)
+{
+	volatile struct adb_request req;
+	long time;
+
+	adb_request((struct adb_request *) &req, NULL,
+			ADBREQ_RAW|ADBREQ_SYNC,
+			2, CUDA_PACKET, CUDA_GET_TIME);
+
+	time = (req.reply[3] << 24) | (req.reply[4] << 16)
+		| (req.reply[5] << 8) | req.reply[6];
+	return time - RTC_OFFSET;
+}
+
+/*
+ * Set the current system time
+ */
+
+static void adb_write_time(long data)
+{
+	volatile struct adb_request req;
+
+	data += RTC_OFFSET;
+
+	adb_request((struct adb_request *) &req, NULL,
+			ADBREQ_RAW|ADBREQ_SYNC,
+			6, CUDA_PACKET, CUDA_SET_TIME,
+			(data >> 24) & 0xFF, (data >> 16) & 0xFF,
+			(data >> 8) & 0xFF, data & 0xFF);
+}
+
+/*
+ * Get a byte from the NVRAM
+ */
+
+static __u8 adb_read_pram(int offset)
+{
+	volatile struct adb_request req;
+
+	adb_request((struct adb_request *) &req, NULL,
+			ADBREQ_RAW|ADBREQ_SYNC,
+			4, CUDA_PACKET, CUDA_GET_PRAM,
+			(offset >> 8) & 0xFF, offset & 0xFF);
+	return req.reply[3];
+}
+
+/*
+ * Write a byte to the NVRAM
+ */
+
+static void adb_write_pram(int offset, __u8 data)
+{
+	volatile struct adb_request req;
+
+	adb_request((struct adb_request *) &req, NULL,
+			ADBREQ_RAW|ADBREQ_SYNC,
+			5, CUDA_PACKET, CUDA_SET_PRAM,
+			(offset >> 8) & 0xFF, offset & 0xFF,
+			data);
+}
+#endif /* CONFIG_ADB */
+
+/*
+ * VIA PRAM/RTC access routines
+ *
+ * Must be called with interrupts disabled and
+ * the RTC should be enabled.
+ */
+
+static __u8 via_pram_readbyte(void)
+{
+	int	i,reg;
+	__u8	data;
+
+	reg = via1[vBufB] & ~VIA1B_vRTCClk;
+
+	/* Set the RTC data line to be an input. */
+
+	via1[vDirB] &= ~VIA1B_vRTCData;
+
+	/* The bits of the byte come out in MSB order */
+
+	data = 0;
+	for (i = 0 ; i < 8 ; i++) {
+		via1[vBufB] = reg;
+		via1[vBufB] = reg | VIA1B_vRTCClk;
+		data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
+	}
+
+	/* Return RTC data line to output state */
+
+	via1[vDirB] |= VIA1B_vRTCData;
+
+	return data;
+}
+
+static void via_pram_writebyte(__u8 data)
+{
+	int	i,reg,bit;
+
+	reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
+
+	/* The bits of the byte go in in MSB order */
+
+	for (i = 0 ; i < 8 ; i++) {
+		bit = data & 0x80? 1 : 0;
+		data <<= 1;
+		via1[vBufB] = reg | bit;
+		via1[vBufB] = reg | bit | VIA1B_vRTCClk;
+	}
+}
+
+/*
+ * Execute a VIA PRAM/RTC command. For read commands
+ * data should point to a one-byte buffer for the
+ * resulting data. For write commands it should point
+ * to the data byte to for the command.
+ *
+ * This function disables all interrupts while running.
+ */
+
+static void via_pram_command(int command, __u8 *data)
+{
+	unsigned long flags;
+	int	is_read;
+
+	local_irq_save(flags);
+
+	/* Enable the RTC and make sure the strobe line is high */
+
+	via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
+
+	if (command & 0xFF00) {		/* extended (two-byte) command */
+		via_pram_writebyte((command & 0xFF00) >> 8);
+		via_pram_writebyte(command & 0xFF);
+		is_read = command & 0x8000;
+	} else {			/* one-byte command */
+		via_pram_writebyte(command);
+		is_read = command & 0x80;
+	}
+	if (is_read) {
+		*data = via_pram_readbyte();
+	} else {
+		via_pram_writebyte(*data);
+	}
+
+	/* All done, disable the RTC */
+
+	via1[vBufB] |= VIA1B_vRTCEnb;
+
+	local_irq_restore(flags);
+}
+
+static __u8 via_read_pram(int offset)
+{
+	return 0;
+}
+
+static void via_write_pram(int offset, __u8 data)
+{
+}
+
+/*
+ * Return the current time in seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+static long via_read_time(void)
+{
+	union {
+		__u8  cdata[4];
+		long  idata;
+	} result, last_result;
+	int	ct;
+
+	/*
+	 * The NetBSD guys say to loop until you get the same reading
+	 * twice in a row.
+	 */
+
+	ct = 0;
+	do {
+		if (++ct > 10) {
+			printk("via_read_time: couldn't get valid time, "
+			       "last read = 0x%08lx and 0x%08lx\n",
+			       last_result.idata, result.idata);
+			break;
+		}
+
+		last_result.idata = result.idata;
+		result.idata = 0;
+
+		via_pram_command(0x81, &result.cdata[3]);
+		via_pram_command(0x85, &result.cdata[2]);
+		via_pram_command(0x89, &result.cdata[1]);
+		via_pram_command(0x8D, &result.cdata[0]);
+	} while (result.idata != last_result.idata);
+
+	return result.idata - RTC_OFFSET;
+}
+
+/*
+ * Set the current time to a number of seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+static void via_write_time(long time)
+{
+	union {
+		__u8  cdata[4];
+		long  idata;
+	} data;
+	__u8	temp;
+
+	/* Clear the write protect bit */
+
+	temp = 0x55;
+	via_pram_command(0x35, &temp);
+
+	data.idata = time + RTC_OFFSET;
+	via_pram_command(0x01, &data.cdata[3]);
+	via_pram_command(0x05, &data.cdata[2]);
+	via_pram_command(0x09, &data.cdata[1]);
+	via_pram_command(0x0D, &data.cdata[0]);
+
+	/* Set the write protect bit */
+
+	temp = 0xD5;
+	via_pram_command(0x35, &temp);
+}
+
+static void via_shutdown(void)
+{
+	if (rbv_present) {
+		via2[rBufB] &= ~0x04;
+	} else {
+		/* Direction of vDirB is output */
+		via2[vDirB] |= 0x04;
+		/* Send a value of 0 on that line */
+		via2[vBufB] &= ~0x04;
+		mdelay(1000);
+	}
+}
+
+/*
+ * FIXME: not sure how this is supposed to work exactly...
+ */
+
+static void oss_shutdown(void)
+{
+	oss->rom_ctrl = OSS_POWEROFF;
+}
+
+#ifdef CONFIG_ADB_CUDA
+
+static void cuda_restart(void)
+{
+	adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+			2, CUDA_PACKET, CUDA_RESET_SYSTEM);
+}
+
+static void cuda_shutdown(void)
+{
+	adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+			2, CUDA_PACKET, CUDA_POWERDOWN);
+}
+
+#endif /* CONFIG_ADB_CUDA */
+
+#ifdef CONFIG_ADB_PMU
+
+void pmu_restart(void)
+{
+	adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+			3, PMU_PACKET, PMU_SET_INTR_MASK,
+			PMU_INT_ADB|PMU_INT_TICK);
+
+	adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+			2, PMU_PACKET, PMU_RESET);
+}
+
+void pmu_shutdown(void)
+{
+	adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+			3, PMU_PACKET, PMU_SET_INTR_MASK,
+			PMU_INT_ADB|PMU_INT_TICK);
+
+	adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+			6, PMU_PACKET, PMU_SHUTDOWN,
+			'M', 'A', 'T', 'T');
+}
+
+#endif /* CONFIG_ADB_PMU */
+
+/*
+ *-------------------------------------------------------------------
+ * Below this point are the generic routines; they'll dispatch to the
+ * correct routine for the hardware on which we're running.
+ *-------------------------------------------------------------------
+ */
+
+void mac_pram_read(int offset, __u8 *buffer, int len)
+{
+	__u8 (*func)(int) = NULL;
+	int i;
+
+	if (macintosh_config->adb_type == MAC_ADB_IISI ||
+	    macintosh_config->adb_type == MAC_ADB_PB1 ||
+	    macintosh_config->adb_type == MAC_ADB_PB2 ||
+	    macintosh_config->adb_type == MAC_ADB_CUDA) {
+#ifdef CONFIG_ADB
+		func = adb_read_pram;
+#else
+		return;
+#endif
+	} else {
+		func = via_read_pram;
+	}
+	for (i = 0 ; i < len ; i++) {
+		buffer[i] = (*func)(offset++);
+	}
+}
+
+void mac_pram_write(int offset, __u8 *buffer, int len)
+{
+	void (*func)(int, __u8) = NULL;
+	int i;
+
+	if (macintosh_config->adb_type == MAC_ADB_IISI ||
+	    macintosh_config->adb_type == MAC_ADB_PB1 ||
+	    macintosh_config->adb_type == MAC_ADB_PB2 ||
+	    macintosh_config->adb_type == MAC_ADB_CUDA) {
+#ifdef CONFIG_ADB
+		func = adb_write_pram;
+#else
+		return;
+#endif
+	} else {
+		func = via_write_pram;
+	}
+	for (i = 0 ; i < len ; i++) {
+		(*func)(offset++, buffer[i]);
+	}
+}
+
+void mac_poweroff(void)
+{
+	/*
+	 * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+	 * work using the ADB packet method.  --David Kilzer
+	 */
+
+	if (oss_present) {
+		oss_shutdown();
+	} else if (macintosh_config->adb_type == MAC_ADB_II) {
+		via_shutdown();
+#ifdef CONFIG_ADB_CUDA
+	} else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
+		cuda_shutdown();
+#endif
+#ifdef CONFIG_ADB_PMU
+	} else if (macintosh_config->adb_type == MAC_ADB_PB1
+		|| macintosh_config->adb_type == MAC_ADB_PB2) {
+		pmu_shutdown();
+#endif
+	}
+	local_irq_enable();
+	printk("It is now safe to turn off your Macintosh.\n");
+	while(1);
+}
+
+void mac_reset(void)
+{
+	if (macintosh_config->adb_type == MAC_ADB_II) {
+		unsigned long flags;
+
+		/* need ROMBASE in booter */
+		/* indeed, plus need to MAP THE ROM !! */
+
+		if (mac_bi_data.rombase == 0)
+			mac_bi_data.rombase = 0x40800000;
+
+		/* works on some */
+		rom_reset = (void *) (mac_bi_data.rombase + 0xa);
+
+		if (macintosh_config->ident == MAC_MODEL_SE30) {
+			/*
+			 * MSch: Machines known to crash on ROM reset ...
+			 */
+		} else {
+			local_irq_save(flags);
+
+			rom_reset();
+
+			local_irq_restore(flags);
+		}
+#ifdef CONFIG_ADB_CUDA
+	} else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
+		cuda_restart();
+#endif
+#ifdef CONFIG_ADB_PMU
+	} else if (macintosh_config->adb_type == MAC_ADB_PB1
+		|| macintosh_config->adb_type == MAC_ADB_PB2) {
+		pmu_restart();
+#endif
+	} else if (CPU_IS_030) {
+
+		/* 030-specific reset routine.  The idea is general, but the
+		 * specific registers to reset are '030-specific.  Until I
+		 * have a non-030 machine, I can't test anything else.
+		 *  -- C. Scott Ananian <cananian@alumni.princeton.edu>
+		 */
+
+		unsigned long rombase = 0x40000000;
+
+		/* make a 1-to-1 mapping, using the transparent tran. reg. */
+		unsigned long virt = (unsigned long) mac_reset;
+		unsigned long phys = virt_to_phys(mac_reset);
+		unsigned long offset = phys-virt;
+		local_irq_disable(); /* lets not screw this up, ok? */
+		__asm__ __volatile__(".chip 68030\n\t"
+				     "pmove %0,%/tt0\n\t"
+				     ".chip 68k"
+				     : : "m" ((phys&0xFF000000)|0x8777));
+		/* Now jump to physical address so we can disable MMU */
+		__asm__ __volatile__(
+                    ".chip 68030\n\t"
+		    "lea %/pc@(1f),%/a0\n\t"
+		    "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
+		    "addl %0,%/sp\n\t"
+		    "pflusha\n\t"
+		    "jmp %/a0@\n\t" /* jump into physical memory */
+		    "0:.long 0\n\t" /* a constant zero. */
+		    /* OK.  Now reset everything and jump to reset vector. */
+		    "1:\n\t"
+		    "lea %/pc@(0b),%/a0\n\t"
+		    "pmove %/a0@, %/tc\n\t" /* disable mmu */
+		    "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
+		    "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
+		    "movel #0, %/a0\n\t"
+		    "movec %/a0, %/vbr\n\t" /* clear vector base register */
+		    "movec %/a0, %/cacr\n\t" /* disable caches */
+		    "movel #0x0808,%/a0\n\t"
+		    "movec %/a0, %/cacr\n\t" /* flush i&d caches */
+		    "movew #0x2700,%/sr\n\t" /* set up status register */
+		    "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
+		    "movec %/a0, %/isp\n\t"
+		    "movel %1@(0x4),%/a0\n\t" /* load reset vector */
+		    "reset\n\t" /* reset external devices */
+		    "jmp %/a0@\n\t" /* jump to the reset vector */
+		    ".chip 68k"
+		    : : "r" (offset), "a" (rombase) : "a0");
+	}
+
+	/* should never get here */
+	local_irq_enable();
+	printk ("Restart failed.  Please restart manually.\n");
+	while(1);
+}
+
+/*
+ * This function translates seconds since 1970 into a proper date.
+ *
+ * Algorithm cribbed from glibc2.1, __offtime().
+ */
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR  (SECS_PER_MINUTE * 60)
+#define SECS_PER_DAY   (SECS_PER_HOUR * 24)
+
+static void unmktime(unsigned long time, long offset,
+		     int *yearp, int *monp, int *dayp,
+		     int *hourp, int *minp, int *secp)
+{
+        /* How many days come before each month (0-12).  */
+	static const unsigned short int __mon_yday[2][13] =
+	{
+		/* Normal years.  */
+		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+		/* Leap years.  */
+		{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+	};
+	long int days, rem, y, wday, yday;
+	const unsigned short int *ip;
+
+	days = time / SECS_PER_DAY;
+	rem = time % SECS_PER_DAY;
+	rem += offset;
+	while (rem < 0) {
+		rem += SECS_PER_DAY;
+		--days;
+	}
+	while (rem >= SECS_PER_DAY) {
+		rem -= SECS_PER_DAY;
+		++days;
+	}
+	*hourp = rem / SECS_PER_HOUR;
+	rem %= SECS_PER_HOUR;
+	*minp = rem / SECS_PER_MINUTE;
+	*secp = rem % SECS_PER_MINUTE;
+	/* January 1, 1970 was a Thursday. */
+	wday = (4 + days) % 7; /* Day in the week. Not currently used */
+	if (wday < 0) wday += 7;
+	y = 1970;
+
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+#define __isleap(year)	\
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+	while (days < 0 || days >= (__isleap (y) ? 366 : 365))
+	{
+		/* Guess a corrected year, assuming 365 days per year.  */
+		long int yg = y + days / 365 - (days % 365 < 0);
+
+		/* Adjust DAYS and Y to match the guessed year.  */
+		days -= ((yg - y) * 365
+			 + LEAPS_THRU_END_OF (yg - 1)
+			 - LEAPS_THRU_END_OF (y - 1));
+		y = yg;
+	}
+	*yearp = y - 1900;
+	yday = days; /* day in the year.  Not currently used. */
+	ip = __mon_yday[__isleap(y)];
+	for (y = 11; days < (long int) ip[y]; --y)
+		continue;
+	days -= ip[y];
+	*monp = y;
+	*dayp = days + 1; /* day in the month */
+	return;
+}
+
+/*
+ * Read/write the hardware clock.
+ */
+
+int mac_hwclk(int op, struct rtc_time *t)
+{
+	unsigned long now;
+
+	if (!op) { /* read */
+		if (macintosh_config->adb_type == MAC_ADB_II) {
+			now = via_read_time();
+		} else
+#ifdef CONFIG_ADB
+		if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+			   (macintosh_config->adb_type == MAC_ADB_PB1) ||
+			   (macintosh_config->adb_type == MAC_ADB_PB2) ||
+			   (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+			now = adb_read_time();
+		} else
+#endif
+		if (macintosh_config->adb_type == MAC_ADB_IOP) {
+			now = via_read_time();
+		} else {
+			now = 0;
+		}
+
+		t->tm_wday = 0;
+		unmktime(now, 0,
+			 &t->tm_year, &t->tm_mon, &t->tm_mday,
+			 &t->tm_hour, &t->tm_min, &t->tm_sec);
+		printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n",
+			t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+	} else { /* write */
+		printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
+			t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+
+#if 0	/* it trashes my rtc */
+		now = mktime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+			     t->tm_hour, t->tm_min, t->tm_sec);
+
+		if (macintosh_config->adb_type == MAC_ADB_II) {
+			via_write_time(now);
+		} else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+			   (macintosh_config->adb_type == MAC_ADB_PB1) ||
+			   (macintosh_config->adb_type == MAC_ADB_PB2) ||
+			   (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+			adb_write_time(now);
+		} else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+			via_write_time(now);
+		}
+#endif
+	}
+	return 0;
+}
+
+/*
+ * Set minutes/seconds in the hardware clock
+ */
+
+int mac_set_clock_mmss (unsigned long nowtime)
+{
+	struct rtc_time now;
+
+	mac_hwclk(0, &now);
+	now.tm_sec = nowtime % 60;
+	now.tm_min = (nowtime / 60) % 60;
+	mac_hwclk(1, &now);
+
+	return 0;
+}
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c
new file mode 100644
index 0000000..3335476
--- /dev/null
+++ b/arch/m68k/mac/oss.c
@@ -0,0 +1,301 @@
+/*
+ *	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;
+
+irqreturn_t oss_irq(int, void *, struct pt_regs *);
+irqreturn_t oss_nubus_irq(int, void *, struct pt_regs *);
+
+extern irqreturn_t via1_irq(int, void *, struct pt_regs *);
+extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *);
+
+/*
+ * 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.
+ */
+
+void __init 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.
+ */
+
+void __init oss_register_interrupts(void)
+{
+	cpu_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
+			"scsi", (void *) oss);
+	cpu_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK,
+			"scc", mac_scc_dispatch);
+	cpu_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
+			"nubus", (void *) oss);
+	cpu_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
+			"sound", (void *) oss);
+	cpu_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
+			"via1", (void *) via1);
+}
+
+/*
+ * Initialize OSS for Nubus access
+ */
+
+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.
+ */
+
+irqreturn_t 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 IRQ_NONE;
+
+#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) {
+		/* FIXME: call sound handler */
+		oss->irq_pending &= ~OSS_IP_SOUND;
+	} else if (events & OSS_IP_SCSI) {
+		oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED;
+		mac_do_irq_list(IRQ_MAC_SCSI, regs);
+		oss->irq_pending &= ~OSS_IP_SCSI;
+		oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
+	} else {
+		/* FIXME: error check here? */
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * Nubus IRQ handler, OSS style
+ *
+ * Unlike the VIA/RBV this is on its own autovector interrupt level.
+ */
+
+irqreturn_t 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 IRQ_NONE;
+
+#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;
+			mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs);
+			oss->irq_pending &= ~irq_bit;
+			oss->irq_level[i] = OSS_IRQLEV_NUBUS;
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * 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;
+}
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c
new file mode 100644
index 0000000..e72384e
--- /dev/null
+++ b/arch/m68k/mac/psc.c
@@ -0,0 +1,197 @@
+/*
+ *	Apple Peripheral System Controller (PSC)
+ *
+ *	The PSC is used on the AV Macs to control IO functions not handled
+ *	by the VIAs (Ethernet, DSP, SCC).
+ *
+ * TO DO:
+ *
+ * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be
+ * persisant interrupt conditions in those registers and I have no idea what
+ * they are. Granted it doesn't affect since we're not enabling any interrupts
+ * on those levels at the moment, but it would be nice to know. I have a feeling
+ * they aren't actually interrupt lines but data lines (to the DSP?)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/traps.h>
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_psc.h>
+
+#define DEBUG_PSC
+
+int psc_present;
+volatile __u8 *psc;
+
+irqreturn_t psc_irq(int, void *, struct pt_regs *);
+
+/*
+ * Debugging dump, used in various places to see what's going on.
+ */
+
+void psc_debug_dump(void)
+{
+	int	i;
+
+	if (!psc_present) return;
+	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
+		printk("PSC #%d:  IFR = 0x%02X IER = 0x%02X\n",
+			i >> 4,
+			(int) psc_read_byte(pIFRbase + i),
+			(int) psc_read_byte(pIERbase + i));
+	}
+}
+
+/*
+ * Try to kill all DMA channels on the PSC. Not sure how this his
+ * supposed to work; this is code lifted from macmace.c and then
+ * expanded to cover what I think are the other 7 channels.
+ */
+
+void psc_dma_die_die_die(void)
+{
+	int i;
+
+	printk("Killing all PSC DMA channels...");
+	for (i = 0 ; i < 9 ; i++) {
+		psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800);
+		psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000);
+		psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100);
+		psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100);
+	}
+	printk("done!\n");
+}
+
+/*
+ * Initialize the PSC. For now this just involves shutting down all
+ * interrupt sources using the IERs.
+ */
+
+void __init psc_init(void)
+{
+	int i;
+
+	if (macintosh_config->ident != MAC_MODEL_C660
+	 && macintosh_config->ident != MAC_MODEL_Q840)
+	{
+		psc = NULL;
+		psc_present = 0;
+		return;
+	}
+
+	/*
+	 * The PSC is always at the same spot, but using psc
+	 * keeps things consisant with the psc_xxxx functions.
+	 */
+
+	psc = (void *) PSC_BASE;
+	psc_present = 1;
+
+	printk("PSC detected at %p\n", psc);
+
+	psc_dma_die_die_die();
+
+#ifdef DEBUG_PSC
+	psc_debug_dump();
+#endif
+	/*
+	 * Mask and clear all possible interrupts
+	 */
+
+	for (i = 0x30 ; i < 0x70 ; i += 0x10) {
+		psc_write_byte(pIERbase + i, 0x0F);
+		psc_write_byte(pIFRbase + i, 0x0F);
+	}
+}
+
+/*
+ * Register the PSC interrupt dispatchers for autovector interrupts 3-6.
+ */
+
+void __init psc_register_interrupts(void)
+{
+	cpu_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3", (void *) 0x30);
+	cpu_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4", (void *) 0x40);
+	cpu_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5", (void *) 0x50);
+	cpu_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6", (void *) 0x60);
+}
+
+/*
+ * PSC interrupt handler. It's a lot like the VIA interrupt handler.
+ */
+
+irqreturn_t psc_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int pIFR	= pIFRbase + ((int) dev_id);
+	int pIER	= pIERbase + ((int) dev_id);
+	int base_irq;
+	int irq_bit,i;
+	unsigned char events;
+
+	base_irq = irq << 3;
+
+#ifdef DEBUG_IRQS
+	printk("psc_irq: irq %d pIFR = 0x%02X pIER = 0x%02X\n",
+		irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER));
+#endif
+
+	events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
+	if (!events)
+		return IRQ_NONE;
+
+	for (i = 0, irq_bit = 1 ; i < 4 ; i++, irq_bit <<= 1) {
+	        if (events & irq_bit) {
+			psc_write_byte(pIER, irq_bit);
+			mac_do_irq_list(base_irq + i, regs);
+			psc_write_byte(pIFR, irq_bit);
+			psc_write_byte(pIER, irq_bit | 0x80);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+void psc_irq_enable(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIER	= pIERbase + (irq_src << 4);
+
+#ifdef DEBUG_IRQUSE
+	printk("psc_irq_enable(%d)\n", irq);
+#endif
+	psc_write_byte(pIER, (1 << irq_idx) | 0x80);
+}
+
+void psc_irq_disable(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIER	= pIERbase + (irq_src << 4);
+
+#ifdef DEBUG_IRQUSE
+	printk("psc_irq_disable(%d)\n", irq);
+#endif
+	psc_write_byte(pIER, 1 << irq_idx);
+}
+
+void psc_irq_clear(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIFR	= pIERbase + (irq_src << 4);
+
+	psc_write_byte(pIFR, 1 << irq_idx);
+}
+
+int psc_irq_pending(int irq)
+{
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int pIFR	= pIERbase + (irq_src << 4);
+
+	return psc_read_byte(pIFR) & (1 << irq_idx);
+}
diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c
new file mode 100644
index 0000000..cd528bf
--- /dev/null
+++ b/arch/m68k/mac/via.c
@@ -0,0 +1,619 @@
+/*
+ *	6522 Versatile Interface Adapter (VIA)
+ *
+ *	There are two of these on the Mac II. Some IRQ's are vectored
+ *	via them as are assorted bits and bobs - eg RTC, ADB.
+ *
+ * CSA: Motorola seems to have removed documentation on the 6522 from
+ * their web site; try
+ *     http://nerini.drf.com/vectrex/other/text/chips/6522/
+ *     http://www.zymurgy.net/classic/vic20/vicdet1.htm
+ * and
+ *     http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html
+ * for info.  A full-text web search on 6522 AND VIA will probably also
+ * net some usefulness. <cananian@alumni.princeton.edu> 20apr1999
+ *
+ * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b
+ * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org)
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/traps.h>
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+#include <asm/mac_psc.h>
+
+volatile __u8 *via1, *via2;
+#if 0
+/* See note in mac_via.h about how this is possibly not useful */
+volatile long *via_memory_bogon=(long *)&via_memory_bogon;
+#endif
+int  rbv_present,via_alt_mapping;
+__u8 rbv_clear;
+
+/*
+ * Globals for accessing the VIA chip registers without having to
+ * check if we're hitting a real VIA or an RBV. Normally you could
+ * just hit the combined register (ie, vIER|rIER) but that seems to
+ * break on AV Macs...probably because they actually decode more than
+ * eight address bits. Why can't Apple engineers at least be
+ * _consistently_ lazy?                          - 1999-05-21 (jmt)
+ */
+
+static int gIER,gIFR,gBufA,gBufB;
+
+/*
+ * Timer defs.
+ */
+
+#define TICK_SIZE		10000
+#define MAC_CLOCK_TICK		(783300/HZ)		/* ticks per HZ */
+#define MAC_CLOCK_LOW		(MAC_CLOCK_TICK&0xFF)
+#define MAC_CLOCK_HIGH		(MAC_CLOCK_TICK>>8)
+
+static int  nubus_active;
+
+void via_debug_dump(void);
+irqreturn_t via1_irq(int, void *, struct pt_regs *);
+irqreturn_t via2_irq(int, void *, struct pt_regs *);
+irqreturn_t via_nubus_irq(int, void *, struct pt_regs *);
+void via_irq_enable(int irq);
+void via_irq_disable(int irq);
+void via_irq_clear(int irq);
+
+extern irqreturn_t mac_bang(int, void *, struct pt_regs *);
+extern irqreturn_t mac_scc_dispatch(int, void *, struct pt_regs *);
+extern int oss_present;
+
+/*
+ * Initialize the VIAs
+ *
+ * First we figure out where they actually _are_ as well as what type of
+ * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.)
+ * Then we pretty much clear them out and disable all IRQ sources.
+ *
+ * Note: the OSS is actually "detected" here and not in oss_init(). It just
+ *	 seems more logical to do it here since via_init() needs to know
+ *	 these things anyways.
+ */
+
+void __init via_init(void)
+{
+	switch(macintosh_config->via_type) {
+
+		/* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
+
+		case MAC_VIA_IIci:
+			via1 = (void *) VIA1_BASE;
+			if (macintosh_config->ident == MAC_MODEL_IIFX) {
+				via2 = NULL;
+				rbv_present = 0;
+				oss_present = 1;
+			} else {
+				via2 = (void *) RBV_BASE;
+				rbv_present = 1;
+				oss_present = 0;
+			}
+			if (macintosh_config->ident == MAC_MODEL_LCIII) {
+				rbv_clear = 0x00;
+			} else {
+				/* on most RBVs (& unlike the VIAs), you   */
+				/* need to set bit 7 when you write to IFR */
+				/* in order for your clear to occur.       */
+				rbv_clear = 0x80;
+			}
+			gIER = rIER;
+			gIFR = rIFR;
+			gBufA = rSIFR;
+			gBufB = rBufB;
+			break;
+
+		/* Quadra and early MacIIs agree on the VIA locations */
+
+		case MAC_VIA_QUADRA:
+		case MAC_VIA_II:
+			via1 = (void *) VIA1_BASE;
+			via2 = (void *) VIA2_BASE;
+			rbv_present = 0;
+			oss_present = 0;
+			rbv_clear = 0x00;
+			gIER = vIER;
+			gIFR = vIFR;
+			gBufA = vBufA;
+			gBufB = vBufB;
+			break;
+		default:
+			panic("UNKNOWN VIA TYPE");
+	}
+
+	printk(KERN_INFO "VIA1 at %p is a 6522 or clone\n", via1);
+
+	printk(KERN_INFO "VIA2 at %p is ", via2);
+	if (rbv_present) {
+		printk(KERN_INFO "an RBV\n");
+	} else if (oss_present) {
+		printk(KERN_INFO "an OSS\n");
+	} else {
+		printk(KERN_INFO "a 6522 or clone\n");
+	}
+
+#ifdef DEBUG_VIA
+	via_debug_dump();
+#endif
+
+	/*
+	 * Shut down all IRQ sources, reset the timers, and
+	 * kill the timer latch on VIA1.
+	 */
+
+	via1[vIER] = 0x7F;
+	via1[vIFR] = 0x7F;
+	via1[vT1LL] = 0;
+	via1[vT1LH] = 0;
+	via1[vT1CL] = 0;
+	via1[vT1CH] = 0;
+	via1[vT2CL] = 0;
+	via1[vT2CH] = 0;
+	via1[vACR] &= 0x3F;
+
+	/*
+	 * SE/30: disable video IRQ
+	 * XXX: testing for SE/30 VBL
+	 */
+
+	if (macintosh_config->ident == MAC_MODEL_SE30) {
+		via1[vDirB] |= 0x40;
+		via1[vBufB] |= 0x40;
+	}
+
+	/*
+	 * Set the RTC bits to a known state: all lines to outputs and
+	 * RTC disabled (yes that's 0 to enable and 1 to disable).
+	 */
+
+	via1[vDirB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk | VIA1B_vRTCData);
+	via1[vBufB] |= (VIA1B_vRTCEnb | VIA1B_vRTCClk);
+
+	/* Everything below this point is VIA2/RBV only... */
+
+	if (oss_present) return;
+
+#if 1
+	/* Some machines support an alternate IRQ mapping that spreads  */
+	/* Ethernet and Sound out to their own autolevel IRQs and moves */
+	/* VIA1 to level 6. A/UX uses this mapping and we do too.  Note */
+	/* that the IIfx emulates this alternate mapping using the OSS. */
+
+	switch(macintosh_config->ident) {
+		case MAC_MODEL_C610:
+		case MAC_MODEL_Q610:
+		case MAC_MODEL_C650:
+		case MAC_MODEL_Q650:
+		case MAC_MODEL_Q700:
+		case MAC_MODEL_Q800:
+		case MAC_MODEL_Q900:
+		case MAC_MODEL_Q950:
+			via_alt_mapping = 1;
+			via1[vDirB] |= 0x40;
+			via1[vBufB] &= ~0x40;
+			break;
+		default:
+			via_alt_mapping = 0;
+			break;
+	}
+#else
+	/* The alernate IRQ mapping seems to just not work. Anyone with a   */
+	/* supported machine is welcome to take a stab at fixing it. It     */
+	/* _should_ work on the following Quadras: 610,650,700,800,900,950  */
+	/*                                               - 1999-06-12 (jmt) */
+
+	via_alt_mapping = 0;
+#endif
+
+	/*
+	 * Now initialize VIA2. For RBV we just kill all interrupts;
+	 * for a regular VIA we also reset the timers and stuff.
+	 */
+
+	via2[gIER] = 0x7F;
+	via2[gIFR] = 0x7F | rbv_clear;
+	if (!rbv_present) {
+		via2[vT1LL] = 0;
+		via2[vT1LH] = 0;
+		via2[vT1CL] = 0;
+		via2[vT1CH] = 0;
+		via2[vT2CL] = 0;
+		via2[vT2CH] = 0;
+		via2[vACR] &= 0x3F;
+	}
+}
+
+/*
+ * Start the 100 Hz clock
+ */
+
+void __init via_init_clock(irqreturn_t (*func)(int, void *, struct pt_regs *))
+{
+	via1[vACR] |= 0x40;
+	via1[vT1LL] = MAC_CLOCK_LOW;
+	via1[vT1LH] = MAC_CLOCK_HIGH;
+	via1[vT1CL] = MAC_CLOCK_LOW;
+	via1[vT1CH] = MAC_CLOCK_HIGH;
+
+	request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func);
+}
+
+/*
+ * Register the interrupt dispatchers for VIA or RBV machines only.
+ */
+
+void __init via_register_interrupts(void)
+{
+	if (via_alt_mapping) {
+		cpu_request_irq(IRQ_AUTO_1, via1_irq,
+				IRQ_FLG_LOCK|IRQ_FLG_FAST, "software",
+				(void *) via1);
+		cpu_request_irq(IRQ_AUTO_6, via1_irq,
+				IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1",
+				(void *) via1);
+	} else {
+		cpu_request_irq(IRQ_AUTO_1, via1_irq,
+				IRQ_FLG_LOCK|IRQ_FLG_FAST, "via1",
+				(void *) via1);
+#if 0 /* interferes with serial on some machines */
+		if (!psc_present) {
+			cpu_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK,
+					"Off Switch", mac_bang);
+		}
+#endif
+	}
+	cpu_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+			"via2", (void *) via2);
+	if (!psc_present) {
+		cpu_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK,
+				"scc", mac_scc_dispatch);
+	}
+	request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+			"nubus", (void *) via2);
+}
+
+/*
+ * Debugging dump, used in various places to see what's going on.
+ */
+
+void via_debug_dump(void)
+{
+	printk(KERN_DEBUG "VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
+		(uint) via1[vDirA], (uint) via1[vDirB], (uint) via1[vACR]);
+	printk(KERN_DEBUG "         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
+		(uint) via1[vPCR], (uint) via1[vIFR], (uint) via1[vIER]);
+	if (oss_present) {
+		printk(KERN_DEBUG "VIA2: <OSS>\n");
+	} else if (rbv_present) {
+		printk(KERN_DEBUG "VIA2:  IFR = 0x%02X  IER = 0x%02X\n",
+			(uint) via2[rIFR], (uint) via2[rIER]);
+		printk(KERN_DEBUG "      SIFR = 0x%02X SIER = 0x%02X\n",
+			(uint) via2[rSIFR], (uint) via2[rSIER]);
+	} else {
+		printk(KERN_DEBUG "VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n",
+			(uint) via2[vDirA], (uint) via2[vDirB],
+			(uint) via2[vACR]);
+		printk(KERN_DEBUG "         PCR = 0x%02X  IFR = 0x%02X IER = 0x%02X\n",
+			(uint) via2[vPCR],
+			(uint) via2[vIFR], (uint) via2[vIER]);
+	}
+}
+
+/*
+ * This is always executed with interrupts disabled.
+ *
+ * TBI: get time offset between scheduling timer ticks
+ */
+
+unsigned long mac_gettimeoffset (void)
+{
+	unsigned long ticks, offset = 0;
+
+	/* read VIA1 timer 2 current value */
+	ticks = via1[vT1CL] | (via1[vT1CH] << 8);
+	/* The probability of underflow is less than 2% */
+	if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50)
+		/* Check for pending timer interrupt in VIA1 IFR */
+		if (via1[vIFR] & 0x40) offset = TICK_SIZE;
+
+	ticks = MAC_CLOCK_TICK - ticks;
+	ticks = ticks * 10000L / MAC_CLOCK_TICK;
+
+	return ticks + offset;
+}
+
+/*
+ * Flush the L2 cache on Macs that have it by flipping
+ * the system into 24-bit mode for an instant.
+ */
+
+void via_flush_cache(void)
+{
+	via2[gBufB] &= ~VIA2B_vMode32;
+	via2[gBufB] |= VIA2B_vMode32;
+}
+
+/*
+ * Return the status of the L2 cache on a IIci
+ */
+
+int via_get_cache_disable(void)
+{
+	/* Safeguard against being called accidentally */
+	if (!via2) {
+		printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n");
+		return 1;
+	}
+
+	return (int) via2[gBufB] & VIA2B_vCDis;
+}
+
+/*
+ * Initialize VIA2 for Nubus access
+ */
+
+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 (!rbv_present) {
+		/* set the line to be an output on non-RBV machines */
+		if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+		   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+			via2[vDirB] |= 0x02;
+		}
+	}
+
+	/* this seems to be an ADB bit on PMU machines */
+	/* according to MkLinux.  -- jmt               */
+
+	if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+	    (macintosh_config->adb_type != MAC_ADB_PB2)) {
+		via2[gBufB] |= 0x02;
+	}
+
+	/* disable nubus slot interrupts. */
+	if (rbv_present) {
+		via2[rSIER] = 0x7F;
+		via2[rSIER] = nubus_active | 0x80;
+	} else {
+		/* These are ADB bits on PMU */
+		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;
+			}
+		}
+	}
+}
+
+/*
+ * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
+ * via6522.c :-), disable/pending masks added.
+ *
+ * The new interrupt architecture in macints.c takes care of a lot of the
+ * gruntwork for us, including tallying the interrupts and calling the
+ * handlers on the linked list. All we need to do here is basically generate
+ * the machspec interrupt number after clearing the interrupt.
+ */
+
+irqreturn_t via1_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit, i;
+	unsigned char events, mask;
+
+	mask = via1[vIER] & 0x7F;
+	if (!(events = via1[vIFR] & mask))
+		return IRQ_NONE;
+
+	for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1)
+		if (events & irq_bit) {
+			via1[vIER] = irq_bit;
+			mac_do_irq_list(VIA1_SOURCE_BASE + i, regs);
+			via1[vIFR] = irq_bit;
+			via1[vIER] = irq_bit | 0x80;
+		}
+
+#if 0 /* freakin' pmu is doing weird stuff */
+	if (!oss_present) {
+		/* This (still) seems to be necessary to get IDE
+		   working.  However, if you enable VBL interrupts,
+		   you're screwed... */
+		/* FIXME: should we check the SLOTIRQ bit before
+                   pulling this stunt? */
+		/* No, it won't be set. that's why we're doing this. */
+		via_irq_disable(IRQ_MAC_NUBUS);
+		via_irq_clear(IRQ_MAC_NUBUS);
+		mac_do_irq_list(IRQ_MAC_NUBUS, regs);
+		via_irq_enable(IRQ_MAC_NUBUS);
+	}
+#endif
+	return IRQ_HANDLED;
+}
+
+irqreturn_t via2_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit, i;
+	unsigned char events, mask;
+
+	mask = via2[gIER] & 0x7F;
+	if (!(events = via2[gIFR] & mask))
+		return IRQ_NONE;
+
+	for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1)
+		if (events & irq_bit) {
+			via2[gIER] = irq_bit;
+			mac_do_irq_list(VIA2_SOURCE_BASE + i, regs);
+			via2[gIFR] = irq_bit | rbv_clear;
+			via2[gIER] = irq_bit | 0x80;
+		}
+	return IRQ_HANDLED;
+}
+
+/*
+ * Dispatch Nubus interrupts. We are called as a secondary dispatch by the
+ * VIA2 dispatcher as a fast interrupt handler.
+ */
+
+irqreturn_t via_nubus_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int irq_bit, i;
+	unsigned char events;
+
+	if (!(events = ~via2[gBufA] & nubus_active))
+		return IRQ_NONE;
+
+	for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) {
+		if (events & irq_bit) {
+			via_irq_disable(NUBUS_SOURCE_BASE + i);
+			mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs);
+			via_irq_enable(NUBUS_SOURCE_BASE + i);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+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;
+	} else if (irq_src == 2) {
+		/*
+		 * Set vPCR for SCSI interrupts (but not on RBV)
+		 */
+		if ((irq_idx == 0) && !rbv_present) {
+			if (macintosh_config->scsi_type == MAC_SCSI_OLD) {
+				/* CB2 (IRQ) indep. input, positive edge */
+				/* CA2 (DRQ) indep. input, positive edge */
+				via2[vPCR] = 0x66;
+			} else {
+				/* CB2 (IRQ) indep. input, negative edge */
+				/* CA2 (DRQ) indep. input, negative edge */
+				via2[vPCR] = 0x22;
+			}
+		}
+		via2[gIER] = irq_bit | 0x80;
+	} else if (irq_src == 7) {
+		if (rbv_present) {
+			/* 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... */
+			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;
+				}
+			}
+		}
+		nubus_active |= irq_bit;
+	}
+}
+
+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;
+	} else if (irq_src == 2) {
+		via2[gIER] = irq_bit;
+	} else if (irq_src == 7) {
+		if (rbv_present) {
+			/* disable the slot interrupt.  SIER works like IER. */
+			via2[rSIER] = IER_CLR_BIT(irq_idx);
+		} else {
+			/* disable the nubus irq by changing dir to output */
+			/* except on PMU */
+			if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+			   (macintosh_config->adb_type != MAC_ADB_PB2)) {
+				via2[vDirA] |= irq_bit;
+			}
+		}
+		nubus_active &= ~irq_bit;
+	}
+}
+
+void via_irq_clear(int irq) {
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int irq_bit	= 1 << irq_idx;
+
+	if (irq_src == 1) {
+		via1[vIFR] = irq_bit;
+	} else if (irq_src == 2) {
+		via2[gIFR] = irq_bit | rbv_clear;
+	} else if (irq_src == 7) {
+		/* FIXME: hmm.. */
+	}
+}
+
+/*
+ * Returns nonzero if an interrupt is pending on the given
+ * VIA/IRQ combination.
+ */
+
+int via_irq_pending(int irq)
+{
+	int irq_src	= IRQ_SRC(irq);
+	int irq_idx	= IRQ_IDX(irq);
+	int irq_bit	= 1 << irq_idx;
+
+	if (irq_src == 1) {
+		return via1[vIFR] & irq_bit;
+	} else if (irq_src == 2) {
+		return via2[gIFR] & irq_bit;
+	} else if (irq_src == 7) {
+		return ~via2[gBufA] & irq_bit;
+	}
+	return 0;
+}