summaryrefslogtreecommitdiffstats
path: root/arch/m68k/mac
diff options
context:
space:
mode:
authorJinke Han <jinkehan@didiglobal.com>2026-09-08 15:37:42 +0800
committerIngo Molnar <mingo@kernel.org>2026-09-17 09:48:42 +0200
commita5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch)
tree73fef824f9a846cf935869d0dbc5070ef6e2f741 /arch/m68k/mac
downloadlinux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz
linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Jinke Han <jinkehan@didiglobal.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Acked-by: Yafang Shao <laoar.shao@gmail.com> Acked-by: Borislav Petkov <bp@alien8.de> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'arch/m68k/mac')
-rw-r--r--arch/m68k/mac/Makefile7
-rw-r--r--arch/m68k/mac/baboon.c90
-rw-r--r--arch/m68k/mac/config.c1099
-rw-r--r--arch/m68k/mac/iop.c590
-rw-r--r--arch/m68k/mac/mac.h25
-rw-r--r--arch/m68k/mac/mac_penguin.S76
-rw-r--r--arch/m68k/mac/macboing.c303
-rw-r--r--arch/m68k/mac/macints.c268
-rw-r--r--arch/m68k/mac/misc.c666
-rw-r--r--arch/m68k/mac/oss.c188
-rw-r--r--arch/m68k/mac/psc.c168
-rw-r--r--arch/m68k/mac/via.c654
12 files changed, 4134 insertions, 0 deletions
diff --git a/arch/m68k/mac/Makefile b/arch/m68k/mac/Makefile
new file mode 100644
index 000000000..aa0eae562
--- /dev/null
+++ b/arch/m68k/mac/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for Linux arch/m68k/mac source directory
+#
+
+obj-y := config.o macints.o iop.o via.o oss.o psc.o \
+ baboon.o macboing.o misc.o
diff --git a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c
new file mode 100644
index 000000000..5c97a7058
--- /dev/null
+++ b/arch/m68k/mac/baboon.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 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/irq.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_baboon.h>
+
+#include "mac.h"
+
+int baboon_present;
+static volatile struct baboon *baboon;
+
+/*
+ * 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;
+
+ pr_debug("Baboon detected at %p\n", baboon);
+}
+
+/*
+ * Baboon interrupt handler.
+ * XXX how do you clear a pending IRQ? is it even necessary?
+ */
+
+static void baboon_irq(struct irq_desc *desc)
+{
+ short events, irq_bit;
+ int irq_num;
+
+ events = baboon->mb_ifr & 0x07;
+ irq_num = IRQ_BABOON_0;
+ irq_bit = 1;
+ do {
+ if (events & irq_bit) {
+ events &= ~irq_bit;
+ generic_handle_irq(irq_num);
+ }
+ ++irq_num;
+ irq_bit <<= 1;
+ } while (events);
+}
+
+/*
+ * Register the Baboon interrupt dispatcher on nubus slot $C.
+ */
+
+void __init baboon_register_interrupts(void)
+{
+ irq_set_chained_handler(IRQ_NUBUS_C, baboon_irq);
+}
+
+/*
+ * The means for masking individual Baboon interrupts remains a mystery.
+ * However, since we only use the IDE IRQ, we can just enable/disable all
+ * Baboon interrupts. If/when we handle more than one Baboon IRQ, we must
+ * either figure out how to mask them individually or else implement the
+ * same workaround that's used for NuBus slots (see nubus_disabled and
+ * via_nubus_irq_shutdown).
+ */
+
+void baboon_irq_enable(int irq)
+{
+ mac_irq_enable(irq_get_irq_data(IRQ_NUBUS_C));
+}
+
+void baboon_irq_disable(int irq)
+{
+ mac_irq_disable(irq_get_irq_data(IRQ_NUBUS_C));
+}
diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c
new file mode 100644
index 000000000..c0033f885
--- /dev/null
+++ b/arch/m68k/mac/config.c
@@ -0,0 +1,1099 @@
+/*
+ * 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/errno.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/types.h>
+#include <linux/mm.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/platform_device.h>
+#include <linux/ata_platform.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+#include <linux/rtc.h>
+
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/bootinfo-mac.h>
+#include <asm/byteorder.h>
+
+#include <asm/io.h>
+#include <asm/irq.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>
+#include <asm/config.h>
+
+#include "mac.h"
+
+/* Mac bootinfo struct */
+struct mac_booter_data mac_bi_data;
+
+/* The phys. video addr. - might be bogus on some machines */
+static unsigned long mac_orig_videoaddr;
+
+static void mac_get_model(char *str);
+static void mac_identify(void);
+static void mac_report_hardware(void);
+
+static void __init mac_sched_init(void)
+{
+ via_init_clock();
+}
+
+/*
+ * Parse a Macintosh-specific record in the bootinfo
+ */
+
+int __init mac_parse_bootinfo(const struct bi_record *record)
+{
+ int unknown = 0;
+ const void *data = record->data;
+
+ switch (be16_to_cpu(record->tag)) {
+ case BI_MAC_MODEL:
+ mac_bi_data.id = be32_to_cpup(data);
+ break;
+ case BI_MAC_VADDR:
+ mac_bi_data.videoaddr = be32_to_cpup(data);
+ break;
+ case BI_MAC_VDEPTH:
+ mac_bi_data.videodepth = be32_to_cpup(data);
+ break;
+ case BI_MAC_VROW:
+ mac_bi_data.videorow = be32_to_cpup(data);
+ break;
+ case BI_MAC_VDIM:
+ mac_bi_data.dimensions = be32_to_cpup(data);
+ break;
+ case BI_MAC_VLOGICAL:
+ mac_orig_videoaddr = be32_to_cpup(data);
+ mac_bi_data.videological =
+ VIDEOMEMBASE + (mac_orig_videoaddr & ~VIDEOMEMMASK);
+ break;
+ case BI_MAC_SCCBASE:
+ mac_bi_data.sccbase = be32_to_cpup(data);
+ break;
+ case BI_MAC_BTIME:
+ mac_bi_data.boottime = be32_to_cpup(data);
+ break;
+ case BI_MAC_GMTBIAS:
+ mac_bi_data.gmtbias = be32_to_cpup(data);
+ break;
+ case BI_MAC_MEMSIZE:
+ mac_bi_data.memsize = be32_to_cpup(data);
+ break;
+ case BI_MAC_CPUID:
+ mac_bi_data.cpuid = be32_to_cpup(data);
+ break;
+ case BI_MAC_ROMBASE:
+ mac_bi_data.rombase = be32_to_cpup(data);
+ break;
+ default:
+ unknown = 1;
+ break;
+ }
+ return unknown;
+}
+
+void __init config_mac(void)
+{
+ if (!MACH_IS_MAC)
+ pr_err("ERROR: no Mac, but config_mac() called!!\n");
+
+ mach_sched_init = mac_sched_init;
+ mach_init_IRQ = mac_init_IRQ;
+ mach_get_model = mac_get_model;
+ mach_hwclk = mac_hwclk;
+ mach_reset = mac_reset;
+ mach_halt = mac_poweroff;
+#if IS_ENABLED(CONFIG_INPUT_M68K_BEEP)
+ mach_beep = mac_mksound;
+#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)
+ mach_l2_flush = via_l2_flush;
+
+ register_platform_power_off(mac_poweroff);
+}
+
+
+/*
+ * 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,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_UNSUPPORTED, /* IWM */
+ },
+
+ /*
+ * Original Mac II 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,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_UNSUPPORTED, /* IWM */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ },
+
+ /*
+ * Weirdified Mac II hardware - all subtly different. Gee thanks
+ * Apple. All these boxes seem to have VIA2 in a different place to
+ * the Mac II (+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,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_IIFX,
+ .name = "IIfx",
+ .adb_type = MAC_ADB_IOP,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_IIFX,
+ .scc_type = MAC_SCC_IOP,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_SWIM_IOP, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_IISI,
+ .name = "IIsi",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_OLD,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_IIVI,
+ .name = "IIvi",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_IIVX,
+ .name = "IIvx",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM */
+ },
+
+ /*
+ * Classic models (guessing: similar to SE/30? Nope, similar to LC...)
+ */
+
+ {
+ .ident = MAC_MODEL_CLII,
+ .name = "Classic II",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_CCL,
+ .name = "Color Classic",
+ .adb_type = MAC_ADB_CUDA,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ }, {
+ .ident = MAC_MODEL_CCLII,
+ .name = "Color Classic II",
+ .adb_type = MAC_ADB_CUDA,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ },
+
+ /*
+ * Some Mac LC machines. Basically the same as the IIci, ADB like IIsi
+ */
+
+ {
+ .ident = MAC_MODEL_LC,
+ .name = "LC",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_LCII,
+ .name = "LC II",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_LCIII,
+ .name = "LC III",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ },
+
+ /*
+ * 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,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_COMM,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ },
+ /* 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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_UNSUPPORTED, /* New Age */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_SWIM_IOP, /* SWIM */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_SWIM_IOP, /* SWIM */
+ },
+
+ /*
+ * Performa - more LC type machines
+ */
+
+ {
+ .ident = MAC_MODEL_P460,
+ .name = "Performa 460",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .ident = MAC_MODEL_P520,
+ .name = "Performa 520",
+ .adb_type = MAC_ADB_CUDA,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ }, {
+ .ident = MAC_MODEL_P550,
+ .name = "Performa 550",
+ .adb_type = MAC_ADB_CUDA,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_PDS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ },
+ /* These have the comm slot, and therefore possibly 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,
+ .expansion_type = MAC_EXP_PDS_COMM,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_COMM,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .ident = MAC_MODEL_TV,
+ .name = "TV",
+ .adb_type = MAC_ADB_CUDA,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM 2 */
+ }, {
+ .ident = MAC_MODEL_P600,
+ .name = "Performa 600",
+ .adb_type = MAC_ADB_EGRET,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_LC,
+ .scc_type = MAC_SCC_II,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_LC, /* SWIM */
+ },
+
+ /*
+ * 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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_QUADRA, /* SWIM 2 */
+ }, {
+ .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,
+ .expansion_type = MAC_EXP_PDS_NUBUS,
+ .floppy_type = MAC_FLOPPY_UNSUPPORTED, /* New Age */
+ },
+
+ /*
+ * 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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_PB150,
+ .name = "PowerBook 150",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_OLD,
+ .ide_type = MAC_IDE_PB,
+ .scc_type = MAC_SCC_QUADRA,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM 2 */
+ }, {
+ .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,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM 2 */
+ },
+
+ /*
+ * 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.
+ */
+
+ {
+ .ident = MAC_MODEL_PB210,
+ .name = "PowerBook Duo 210",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_DUO,
+ .scc_type = MAC_SCC_QUADRA,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_PB230,
+ .name = "PowerBook Duo 230",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_DUO,
+ .scc_type = MAC_SCC_QUADRA,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_PB250,
+ .name = "PowerBook Duo 250",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_DUO,
+ .scc_type = MAC_SCC_QUADRA,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_PB270C,
+ .name = "PowerBook Duo 270c",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_DUO,
+ .scc_type = MAC_SCC_QUADRA,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_PB280,
+ .name = "PowerBook Duo 280",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_DUO,
+ .scc_type = MAC_SCC_QUADRA,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ }, {
+ .ident = MAC_MODEL_PB280C,
+ .name = "PowerBook Duo 280c",
+ .adb_type = MAC_ADB_PB2,
+ .via_type = MAC_VIA_IICI,
+ .scsi_type = MAC_SCSI_DUO,
+ .scc_type = MAC_SCC_QUADRA,
+ .expansion_type = MAC_EXP_NUBUS,
+ .floppy_type = MAC_FLOPPY_OLD, /* SWIM */
+ },
+
+ /*
+ * Other stuff?
+ */
+
+ {
+ .ident = -1
+ }
+};
+
+static struct resource scc_a_rsrcs[] = {
+ { .flags = IORESOURCE_MEM },
+ { .flags = IORESOURCE_IRQ },
+};
+
+static struct resource scc_b_rsrcs[] = {
+ { .flags = IORESOURCE_MEM },
+ { .flags = IORESOURCE_IRQ },
+};
+
+struct platform_device scc_a_pdev = {
+ .name = "scc",
+ .id = 0,
+};
+EXPORT_SYMBOL(scc_a_pdev);
+
+struct platform_device scc_b_pdev = {
+ .name = "scc",
+ .id = 1,
+};
+EXPORT_SYMBOL(scc_b_pdev);
+
+static void __init 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;
+ pr_warn("No bootinfo model ID, using cpuid instead (obsolete bootloader?)\n");
+ }
+
+ macintosh_config = mac_data_table;
+ for (m = &mac_data_table[1]; m->ident != -1; m++) {
+ if (m->ident == model) {
+ macintosh_config = m;
+ break;
+ }
+ }
+
+ /* Set up serial port resources for the console initcall. */
+
+ scc_a_rsrcs[0].start = (resource_size_t)mac_bi_data.sccbase + 2;
+ scc_a_rsrcs[0].end = scc_a_rsrcs[0].start;
+ scc_a_pdev.num_resources = ARRAY_SIZE(scc_a_rsrcs);
+ scc_a_pdev.resource = scc_a_rsrcs;
+
+ scc_b_rsrcs[0].start = (resource_size_t)mac_bi_data.sccbase;
+ scc_b_rsrcs[0].end = scc_b_rsrcs[0].start;
+ scc_b_pdev.num_resources = ARRAY_SIZE(scc_b_rsrcs);
+ scc_b_pdev.resource = scc_b_rsrcs;
+
+ switch (macintosh_config->scc_type) {
+ case MAC_SCC_PSC:
+ scc_a_rsrcs[1].start = scc_a_rsrcs[1].end = IRQ_MAC_SCC_A;
+ scc_b_rsrcs[1].start = scc_b_rsrcs[1].end = IRQ_MAC_SCC_B;
+ break;
+ default:
+ /* On non-PSC machines, the serial ports share an IRQ. */
+ if (macintosh_config->ident == MAC_MODEL_IIFX) {
+ scc_a_rsrcs[1].start = scc_a_rsrcs[1].end = IRQ_MAC_SCC;
+ scc_b_rsrcs[1].start = scc_b_rsrcs[1].end = IRQ_MAC_SCC;
+ } else {
+ scc_a_rsrcs[1].start = scc_a_rsrcs[1].end = IRQ_AUTO_4;
+ scc_b_rsrcs[1].start = scc_b_rsrcs[1].end = IRQ_AUTO_4;
+ }
+ break;
+ }
+
+ pr_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);
+
+ iop_init();
+ oss_init();
+ via_init();
+ psc_init();
+ baboon_init();
+
+#ifdef CONFIG_ADB_CUDA
+ find_via_cuda();
+#endif
+#ifdef CONFIG_ADB_PMU
+ find_via_pmu();
+#endif
+}
+
+static void __init mac_report_hardware(void)
+{
+ pr_info("Apple Macintosh %s\n", macintosh_config->name);
+}
+
+static void mac_get_model(char *str)
+{
+ strcpy(str, "Macintosh ");
+ strcat(str, macintosh_config->name);
+}
+
+static const struct resource mac_scsi_iifx_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50008000,
+ .end = 0x50009FFF,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50008000,
+ .end = 0x50009FFF,
+ },
+};
+
+static const struct resource mac_scsi_duo_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = 0xFEE02000,
+ .end = 0xFEE03FFF,
+ },
+};
+
+static const struct resource mac_scsi_old_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50010000,
+ .end = 0x50011FFF,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50006000,
+ .end = 0x50007FFF,
+ },
+};
+
+static const struct resource mac_scsi_ccl_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50F10000,
+ .end = 0x50F11FFF,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50F06000,
+ .end = 0x50F07FFF,
+ },
+};
+
+static const struct resource mac_pata_quadra_rsrc[] __initconst = {
+ DEFINE_RES_MEM(0x50F1A000, 0x38),
+ DEFINE_RES_MEM(0x50F1A038, 0x04),
+ DEFINE_RES_IRQ(IRQ_NUBUS_F),
+};
+
+static const struct resource mac_pata_pb_rsrc[] __initconst = {
+ DEFINE_RES_MEM(0x50F1A000, 0x38),
+ DEFINE_RES_MEM(0x50F1A038, 0x04),
+ DEFINE_RES_IRQ(IRQ_NUBUS_C),
+};
+
+static const struct resource mac_pata_baboon_rsrc[] __initconst = {
+ DEFINE_RES_MEM(0x50F1A000, 0x38),
+ DEFINE_RES_MEM(0x50F1A038, 0x04),
+ DEFINE_RES_IRQ(IRQ_BABOON_1),
+};
+
+static const struct pata_platform_info mac_pata_data __initconst = {
+ .ioport_shift = 2,
+};
+
+static int __init mac_platform_init(void)
+{
+ phys_addr_t swim_base = 0;
+
+ if (!MACH_IS_MAC)
+ return -ENODEV;
+
+ /*
+ * Serial devices
+ */
+
+ platform_device_register(&scc_a_pdev);
+ platform_device_register(&scc_b_pdev);
+
+ /*
+ * Floppy device
+ */
+
+ switch (macintosh_config->floppy_type) {
+ case MAC_FLOPPY_QUADRA:
+ swim_base = 0x5001E000;
+ break;
+ case MAC_FLOPPY_OLD:
+ swim_base = 0x50016000;
+ break;
+ case MAC_FLOPPY_LC:
+ swim_base = 0x50F16000;
+ break;
+ }
+
+ if (swim_base) {
+ struct resource swim_rsrc = {
+ .flags = IORESOURCE_MEM,
+ .start = swim_base,
+ .end = swim_base + 0x1FFF,
+ };
+
+ platform_device_register_simple("swim", -1, &swim_rsrc, 1);
+ }
+
+ /*
+ * SCSI device(s)
+ */
+
+ switch (macintosh_config->scsi_type) {
+ case MAC_SCSI_QUADRA:
+ case MAC_SCSI_QUADRA3:
+ platform_device_register_simple("mac_esp", 0, NULL, 0);
+ break;
+ case MAC_SCSI_QUADRA2:
+ platform_device_register_simple("mac_esp", 0, NULL, 0);
+ if ((macintosh_config->ident == MAC_MODEL_Q900) ||
+ (macintosh_config->ident == MAC_MODEL_Q950))
+ platform_device_register_simple("mac_esp", 1, NULL, 0);
+ break;
+ case MAC_SCSI_IIFX:
+ /* Addresses from The Guide to Mac Family Hardware.
+ * $5000 8000 - $5000 9FFF: SCSI DMA
+ * $5000 A000 - $5000 BFFF: Alternate SCSI
+ * $5000 C000 - $5000 DFFF: Alternate SCSI (DMA)
+ * $5000 E000 - $5000 FFFF: Alternate SCSI (Hsk)
+ * The A/UX header file sys/uconfig.h says $50F0 8000.
+ * The "SCSI DMA" custom IC embeds the 53C80 core and
+ * supports Programmed IO, DMA and PDMA (hardware handshake).
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_iifx_rsrc, ARRAY_SIZE(mac_scsi_iifx_rsrc));
+ break;
+ case MAC_SCSI_DUO:
+ /* Addresses from the Duo Dock II Developer Note.
+ * $FEE0 2000 - $FEE0 3FFF: normal mode
+ * $FEE0 4000 - $FEE0 5FFF: pseudo DMA without /DRQ
+ * $FEE0 6000 - $FEE0 7FFF: pseudo DMA with /DRQ
+ * The NetBSD code indicates that both 5380 chips share
+ * an IRQ (?) which would need careful handling (see mac_esp).
+ */
+ platform_device_register_simple("mac_scsi", 1,
+ mac_scsi_duo_rsrc, ARRAY_SIZE(mac_scsi_duo_rsrc));
+ fallthrough;
+ case MAC_SCSI_OLD:
+ /* Addresses from Developer Notes for Duo System,
+ * PowerBook 180 & 160, 140 & 170, Macintosh IIsi
+ * and also from The Guide to Mac Family Hardware for
+ * SE/30, II, IIx, IIcx, IIci.
+ * $5000 6000 - $5000 7FFF: pseudo-DMA with /DRQ
+ * $5001 0000 - $5001 1FFF: normal mode
+ * $5001 2000 - $5001 3FFF: pseudo-DMA without /DRQ
+ * GMFH says that $5000 0000 - $50FF FFFF "wraps
+ * $5000 0000 - $5001 FFFF eight times" (!)
+ * mess.org says IIci and Color Classic do not alias
+ * I/O address space.
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_old_rsrc, ARRAY_SIZE(mac_scsi_old_rsrc));
+ break;
+ case MAC_SCSI_LC:
+ /* Addresses from Mac LC data in Designing Cards & Drivers 3ed.
+ * Also from the Developer Notes for Classic II, LC III,
+ * Color Classic and IIvx.
+ * $50F0 6000 - $50F0 7FFF: SCSI handshake
+ * $50F1 0000 - $50F1 1FFF: SCSI
+ * $50F1 2000 - $50F1 3FFF: SCSI DMA
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_ccl_rsrc, ARRAY_SIZE(mac_scsi_ccl_rsrc));
+ break;
+ }
+
+ /*
+ * IDE device
+ */
+
+ switch (macintosh_config->ide_type) {
+ case MAC_IDE_QUADRA:
+ platform_device_register_resndata(NULL, "pata_platform", -1,
+ mac_pata_quadra_rsrc, ARRAY_SIZE(mac_pata_quadra_rsrc),
+ &mac_pata_data, sizeof(mac_pata_data));
+ break;
+ case MAC_IDE_PB:
+ platform_device_register_resndata(NULL, "pata_platform", -1,
+ mac_pata_pb_rsrc, ARRAY_SIZE(mac_pata_pb_rsrc),
+ &mac_pata_data, sizeof(mac_pata_data));
+ break;
+ case MAC_IDE_BABOON:
+ platform_device_register_resndata(NULL, "pata_platform", -1,
+ mac_pata_baboon_rsrc, ARRAY_SIZE(mac_pata_baboon_rsrc),
+ &mac_pata_data, sizeof(mac_pata_data));
+ break;
+ }
+
+ /*
+ * Ethernet device
+ */
+
+ if (macintosh_config->ether_type == MAC_ETHER_SONIC ||
+ macintosh_config->expansion_type == MAC_EXP_PDS_COMM)
+ platform_device_register_simple("macsonic", -1, NULL, 0);
+
+ if (macintosh_config->expansion_type == MAC_EXP_PDS ||
+ macintosh_config->expansion_type == MAC_EXP_PDS_COMM)
+ platform_device_register_simple("mac89x0", -1, NULL, 0);
+
+ if (macintosh_config->ether_type == MAC_ETHER_MACE)
+ platform_device_register_simple("macmace", -1, NULL, 0);
+
+ return 0;
+}
+
+arch_initcall(mac_platform_init);
diff --git a/arch/m68k/mac/iop.c b/arch/m68k/mac/iop.c
new file mode 100644
index 000000000..a92740d53
--- /dev/null
+++ b/arch/m68k/mac/iop.c
@@ -0,0 +1,590 @@
+/*
+ * 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 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 The SCC IOP has to be placed in bypass mode before the serial console
+ * gets initialized. iop_init() would be one place to do that. Or the
+ * bootloader could do that. For now, the Serial Switch control panel
+ * is needed for that -- contrary to the changelog above.
+ * 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 connected 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 an 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 significant amount of time (handlers are called at interrupt time so they
+ * should execute quickly.)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_iop.h>
+
+#include "mac.h"
+
+#ifdef DEBUG
+#define iop_pr_debug(fmt, ...) \
+ printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__)
+#define iop_pr_cont(fmt, ...) \
+ printk(KERN_CONT fmt, ##__VA_ARGS__)
+#else
+#define iop_pr_debug(fmt, ...) \
+ no_printk(KERN_DEBUG "%s: " fmt, __func__, ##__VA_ARGS__)
+#define iop_pr_cont(fmt, ...) \
+ no_printk(KERN_CONT fmt, ##__VA_ARGS__)
+#endif
+
+/* Non-zero if the IOPs are present */
+
+int iop_scc_present, iop_ism_present;
+
+/* structure for tracking channel listeners */
+
+struct listener {
+ const char *devname;
+ void (*handler)(struct iop_msg *);
+};
+
+/*
+ * 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 *);
+
+/*
+ * 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_AUTOINC;
+}
+
+static __inline__ void iop_start(volatile struct mac_iop *iop)
+{
+ iop->status_ctrl = IOP_RUN | IOP_AUTOINC;
+}
+
+static __inline__ void iop_interrupt(volatile struct mac_iop *iop)
+{
+ iop->status_ctrl = IOP_IRQ | IOP_RUN | IOP_AUTOINC;
+}
+
+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_get_unused_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;
+}
+
+/*
+ * Initialize the IOPs, if present.
+ */
+
+void __init iop_init(void)
+{
+ int i;
+
+ 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_scc_present = 1;
+ pr_debug("SCC IOP detected at %p\n", iop_base[IOP_NUM_SCC]);
+ }
+ 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_ism_present = 1;
+ pr_debug("ISM IOP detected at %p\n", iop_base[IOP_NUM_ISM]);
+
+ iop_stop(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] = NULL;
+ iop_send_queue[IOP_NUM_ISM][i] = NULL;
+ 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;
+ }
+}
+
+/*
+ * Register the interrupt handler for the IOPs.
+ */
+
+void __init iop_register_interrupts(void)
+{
+ if (iop_ism_present) {
+ if (macintosh_config->ident == MAC_MODEL_IIFX) {
+ if (request_irq(IRQ_MAC_ADB, iop_ism_irq, 0,
+ "ISM IOP", (void *)IOP_NUM_ISM))
+ pr_err("Couldn't register ISM IOP interrupt\n");
+ } else {
+ if (request_irq(IRQ_VIA2_0, iop_ism_irq, 0, "ISM IOP",
+ (void *)IOP_NUM_ISM))
+ pr_err("Couldn't register ISM IOP interrupt\n");
+ }
+ if (!iop_alive(iop_base[IOP_NUM_ISM])) {
+ pr_warn("IOP: oh my god, they killed the ISM IOP!\n");
+ } else {
+ pr_warn("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 *),
+ 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;
+
+ iop_pr_debug("iop_num %d chan %d reply %*ph\n",
+ msg->iop_num, msg->channel, IOP_MSG_LEN, msg->reply);
+
+ 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]);
+
+ msg->status = IOP_MSGSTATUS_UNUSED;
+}
+
+/*
+ * 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;
+
+ iop_pr_debug("iop_num %d chan %d message %*ph\n",
+ msg->iop_num, msg->channel, IOP_MSG_LEN, msg->message);
+
+ 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)
+{
+ volatile struct mac_iop *iop = iop_base[iop_num];
+ struct iop_msg *msg;
+ int i,offset;
+
+ 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);
+ }
+ iop_pr_debug("iop_num %d chan %d reply %*ph\n",
+ iop_num, chan, IOP_MSG_LEN, msg->reply);
+
+ if (msg->handler) (*msg->handler)(msg);
+ msg->status = IOP_MSGSTATUS_UNUSED;
+ msg = msg->next;
+ iop_send_queue[iop_num][chan] = msg;
+ if (msg && iop_readb(iop, IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE)
+ 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)
+{
+ volatile struct mac_iop *iop = iop_base[iop_num];
+ int i,offset;
+ struct iop_msg *msg;
+
+ msg = iop_get_unused_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_pr_debug("iop_num %d chan %d message %*ph\n",
+ iop_num, chan, IOP_MSG_LEN, msg->message);
+
+ 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);
+ } else {
+ memset(msg->reply, 0, IOP_MSG_LEN);
+ 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 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_get_unused_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;
+ iop_do_send(msg);
+ } else {
+ while (q->next) q = q->next;
+ q->next = 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)
+{
+ uint iop_num = (uint) dev_id;
+ volatile struct mac_iop *iop = iop_base[iop_num];
+ int i,state;
+ u8 events = iop->status_ctrl & (IOP_INT0 | IOP_INT1);
+
+ do {
+ iop_pr_debug("iop_num %d status %02X\n", iop_num,
+ iop->status_ctrl);
+
+ /* INT0 indicates state change on an outgoing message channel */
+ if (events & IOP_INT0) {
+ iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC;
+ for (i = 0; i < NUM_IOP_CHAN; i++) {
+ state = iop_readb(iop, IOP_ADDR_SEND_STATE + i);
+ if (state == IOP_MSG_COMPLETE)
+ iop_handle_send(iop_num, i);
+ else if (state != IOP_MSG_IDLE)
+ iop_pr_debug("chan %d send state %02X\n",
+ i, state);
+ }
+ }
+
+ /* INT1 for incoming messages */
+ if (events & IOP_INT1) {
+ iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC;
+ for (i = 0; i < NUM_IOP_CHAN; i++) {
+ state = iop_readb(iop, IOP_ADDR_RECV_STATE + i);
+ if (state == IOP_MSG_NEW)
+ iop_handle_recv(iop_num, i);
+ else if (state != IOP_MSG_IDLE)
+ iop_pr_debug("chan %d recv state %02X\n",
+ i, state);
+ }
+ }
+
+ events = iop->status_ctrl & (IOP_INT0 | IOP_INT1);
+ } while (events);
+
+ return IRQ_HANDLED;
+}
+
+void iop_ism_irq_poll(uint iop_num)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ iop_ism_irq(0, (void *)iop_num);
+ local_irq_restore(flags);
+}
diff --git a/arch/m68k/mac/mac.h b/arch/m68k/mac/mac.h
new file mode 100644
index 000000000..d3d142cea
--- /dev/null
+++ b/arch/m68k/mac/mac.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+struct rtc_time;
+
+/* baboon.c */
+void baboon_init(void);
+
+/* iop.c */
+void iop_init(void);
+
+/* misc.c */
+int mac_hwclk(int op, struct rtc_time *t);
+
+/* macboing.c */
+void mac_mksound(unsigned int freq, unsigned int length);
+
+/* oss.c */
+void oss_init(void);
+
+/* psc.c */
+void psc_init(void);
+
+/* via.c */
+void via_init(void);
+void via_init_clock(void);
diff --git a/arch/m68k/mac/mac_penguin.S b/arch/m68k/mac/mac_penguin.S
new file mode 100644
index 000000000..359e63bb6
--- /dev/null
+++ b/arch/m68k/mac/mac_penguin.S
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+.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 000000000..6312d5b60
--- /dev/null
+++ b/arch/m68k/mac/macboing.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 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>
+
+#include "mac.h"
+
+static int mac_asc_inited;
+/*
+ * dumb triangular wave table
+ */
+static __u8 mac_asc_wave_tab[ 0x800 ];
+
+/*
+ * 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(struct timer_list *);
+static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int );
+static void mac_quadra_ring_bell(struct timer_list *);
+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 DEFINE_TIMER(mac_sound_timer, mac_nosound);
+
+/*
+ * 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."
+ *
+ * Technical Information Library (TIL) article number 16405.
+ * https://support.apple.com/kb/TA32601
+ *
+ * --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;
+ unsigned long 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);
+
+ timer_delete(&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(struct timer_list *unused)
+{
+ 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 )
+{
+ unsigned long 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(struct timer_list *unused)
+{
+ int i, count = mac_asc_samplespersec / HZ;
+ unsigned long 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);
+
+ timer_delete(&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 000000000..5cbaf6e94
--- /dev/null
+++ b/arch/m68k/mac/macints.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 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.'
+ *
+ * Levels 3-6 vary by machine type. For VIA or RBV Macintoshes:
+ *
+ * 3 - unused (?)
+ *
+ * 4 - SCC
+ *
+ * 5 - unused (?)
+ * [serial errors or special conditions seem to raise level 6
+ * interrupts on some models (LC4xx?)]
+ *
+ * 6 - off switch (?)
+ *
+ * Machines with Quadra-like VIA hardware, except PSC and PMU machines, support
+ * an alternate interrupt mapping, as used by A/UX. It spreads ethernet and
+ * sound out to their own autovector IRQs and gives VIA1 a higher priority:
+ *
+ * 1 - unused (?)
+ *
+ * 3 - on-board SONIC
+ *
+ * 5 - Apple Sound Chip (ASC)
+ *
+ * 6 - VIA1
+ *
+ * For OSS Macintoshes (IIfx only), we apply an interrupt mapping similar to
+ * the Quadra (A/UX) mapping:
+ *
+ * 1 - ISM IOP (ADB)
+ *
+ * 2 - SCSI
+ *
+ * 3 - NuBus
+ *
+ * 4 - SCC IOP
+ *
+ * 6 - VIA1
+ *
+ * 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?
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/sched/debug.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <asm/irq.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+#include <asm/mac_psc.h>
+#include <asm/mac_oss.h>
+#include <asm/mac_iop.h>
+#include <asm/mac_baboon.h>
+#include <asm/hwtest.h>
+#include <asm/irq_regs.h>
+#include <asm/processor.h>
+
+static unsigned int mac_irq_startup(struct irq_data *);
+static void mac_irq_shutdown(struct irq_data *);
+
+static struct irq_chip mac_irq_chip = {
+ .name = "mac",
+ .irq_enable = mac_irq_enable,
+ .irq_disable = mac_irq_disable,
+ .irq_startup = mac_irq_startup,
+ .irq_shutdown = mac_irq_shutdown,
+};
+
+static irqreturn_t mac_nmi_handler(int irq, void *dev_id)
+{
+ static volatile int in_nmi;
+
+ if (in_nmi)
+ return IRQ_HANDLED;
+ in_nmi = 1;
+
+ pr_info("Non-Maskable Interrupt\n");
+ show_registers(get_irq_regs());
+
+ in_nmi = 0;
+ return IRQ_HANDLED;
+}
+
+void __init mac_init_IRQ(void)
+{
+ m68k_setup_irq_controller(&mac_irq_chip, handle_simple_irq, IRQ_USER,
+ NUM_MAC_SOURCES - IRQ_USER);
+
+ /*
+ * 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)
+ psc_register_interrupts();
+ if (baboon_present)
+ baboon_register_interrupts();
+ iop_register_interrupts();
+ if (request_irq(IRQ_AUTO_7, mac_nmi_handler, 0, "NMI",
+ mac_nmi_handler))
+ pr_err("Couldn't register NMI\n");
+}
+
+/*
+ * mac_irq_enable - enable an interrupt source
+ * mac_irq_disable - disable an interrupt source
+ *
+ * These routines are just dispatchers to the VIA/OSS/PSC routines.
+ */
+
+void mac_irq_enable(struct irq_data *data)
+{
+ int irq = data->irq;
+ int irq_src = IRQ_SRC(irq);
+
+ switch(irq_src) {
+ case 1:
+ 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)
+ psc_irq_enable(irq);
+ else if (oss_present)
+ oss_irq_enable(irq);
+ break;
+ case 8:
+ if (baboon_present)
+ baboon_irq_enable(irq);
+ break;
+ }
+}
+
+void mac_irq_disable(struct irq_data *data)
+{
+ int irq = data->irq;
+ int irq_src = IRQ_SRC(irq);
+
+ switch(irq_src) {
+ case 1:
+ 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)
+ psc_irq_disable(irq);
+ else if (oss_present)
+ oss_irq_disable(irq);
+ break;
+ case 8:
+ if (baboon_present)
+ baboon_irq_disable(irq);
+ break;
+ }
+}
+
+static unsigned int mac_irq_startup(struct irq_data *data)
+{
+ int irq = data->irq;
+
+ if (IRQ_SRC(irq) == 7 && !oss_present)
+ via_nubus_irq_startup(irq);
+ else
+ mac_irq_enable(data);
+
+ return 0;
+}
+
+static void mac_irq_shutdown(struct irq_data *data)
+{
+ int irq = data->irq;
+
+ if (IRQ_SRC(irq) == 7 && !oss_present)
+ via_nubus_irq_shutdown(irq);
+ else
+ mac_irq_disable(data);
+}
diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c
new file mode 100644
index 000000000..e7f0f72c1
--- /dev/null
+++ b/arch/m68k/mac/misc.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Miscellaneous Mac68K-specific stuff
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.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 <linux/uaccess.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/macintosh.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+#include <asm/machdep.h>
+
+#include "mac.h"
+
+/*
+ * Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU
+ * times wrap in 2040. If we need to handle later times, the read_time functions
+ * need to be changed to interpret wrapped times as post-2040.
+ */
+
+#define RTC_OFFSET 2082844800
+
+static void (*rom_reset)(void);
+
+#if IS_ENABLED(CONFIG_NVRAM)
+#ifdef CONFIG_ADB_CUDA
+static unsigned char cuda_pram_read_byte(int offset)
+{
+ struct adb_request req;
+
+ if (cuda_request(&req, NULL, 4, CUDA_PACKET, CUDA_GET_PRAM,
+ (offset >> 8) & 0xFF, offset & 0xFF) < 0)
+ return 0;
+ while (!req.complete)
+ cuda_poll();
+ return req.reply[3];
+}
+
+static void cuda_pram_write_byte(unsigned char data, int offset)
+{
+ struct adb_request req;
+
+ if (cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_SET_PRAM,
+ (offset >> 8) & 0xFF, offset & 0xFF, data) < 0)
+ return;
+ while (!req.complete)
+ cuda_poll();
+}
+#endif /* CONFIG_ADB_CUDA */
+
+#ifdef CONFIG_ADB_PMU
+static unsigned char pmu_pram_read_byte(int offset)
+{
+ struct adb_request req;
+
+ if (pmu_request(&req, NULL, 3, PMU_READ_XPRAM,
+ offset & 0xFF, 1) < 0)
+ return 0;
+ pmu_wait_complete(&req);
+
+ return req.reply[0];
+}
+
+static void pmu_pram_write_byte(unsigned char data, int offset)
+{
+ struct adb_request req;
+
+ if (pmu_request(&req, NULL, 4, PMU_WRITE_XPRAM,
+ offset & 0xFF, 1, data) < 0)
+ return;
+ pmu_wait_complete(&req);
+}
+#endif /* CONFIG_ADB_PMU */
+#endif /* CONFIG_NVRAM */
+
+/*
+ * VIA PRAM/RTC access routines
+ *
+ * Must be called with interrupts disabled and
+ * the RTC should be enabled.
+ */
+
+static __u8 via_rtc_recv(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_rtc_send(__u8 data)
+{
+ int i, reg, bit;
+
+ reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
+
+ /* The bits of the byte go into the RTC 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;
+ }
+}
+
+/*
+ * These values can be found in Inside Macintosh vol. III ch. 2
+ * which has a description of the RTC chip in the original Mac.
+ */
+
+#define RTC_FLG_READ BIT(7)
+#define RTC_FLG_WRITE_PROTECT BIT(7)
+#define RTC_CMD_READ(r) (RTC_FLG_READ | (r << 2))
+#define RTC_CMD_WRITE(r) (r << 2)
+#define RTC_REG_SECONDS_0 0
+#define RTC_REG_SECONDS_1 1
+#define RTC_REG_SECONDS_2 2
+#define RTC_REG_SECONDS_3 3
+#define RTC_REG_WRITE_PROTECT 13
+
+/*
+ * Inside Mac has no information about two-byte RTC commands but
+ * the MAME/MESS source code has the essentials.
+ */
+
+#define RTC_REG_XPRAM 14
+#define RTC_CMD_XPRAM_READ (RTC_CMD_READ(RTC_REG_XPRAM) << 8)
+#define RTC_CMD_XPRAM_WRITE (RTC_CMD_WRITE(RTC_REG_XPRAM) << 8)
+#define RTC_CMD_XPRAM_ARG(a) (((a & 0xE0) << 3) | ((a & 0x1F) << 2))
+
+/*
+ * 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_rtc_command(int command, __u8 *data)
+{
+ unsigned long flags;
+ int is_read;
+
+ local_irq_save(flags);
+
+ /* The least significant bits must be 0b01 according to Inside Mac */
+
+ command = (command & ~3) | 1;
+
+ /* 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_rtc_send((command & 0xFF00) >> 8);
+ via_rtc_send(command & 0xFF);
+ is_read = command & (RTC_FLG_READ << 8);
+ } else { /* one-byte command */
+ via_rtc_send(command);
+ is_read = command & RTC_FLG_READ;
+ }
+ if (is_read) {
+ *data = via_rtc_recv();
+ } else {
+ via_rtc_send(*data);
+ }
+
+ /* All done, disable the RTC */
+
+ via1[vBufB] |= VIA1B_vRTCEnb;
+
+ local_irq_restore(flags);
+}
+
+#if IS_ENABLED(CONFIG_NVRAM)
+static unsigned char via_pram_read_byte(int offset)
+{
+ unsigned char temp;
+
+ via_rtc_command(RTC_CMD_XPRAM_READ | RTC_CMD_XPRAM_ARG(offset), &temp);
+
+ return temp;
+}
+
+static void via_pram_write_byte(unsigned char data, int offset)
+{
+ unsigned char temp;
+
+ temp = 0x55;
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
+
+ temp = data;
+ via_rtc_command(RTC_CMD_XPRAM_WRITE | RTC_CMD_XPRAM_ARG(offset), &temp);
+
+ temp = 0x55 | RTC_FLG_WRITE_PROTECT;
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
+}
+#endif /* CONFIG_NVRAM */
+
+/*
+ * 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 time64_t via_read_time(void)
+{
+ union {
+ __u8 cdata[4];
+ __u32 idata;
+ } result, last_result;
+ int count = 1;
+
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0), &last_result.cdata[3]);
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1), &last_result.cdata[2]);
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2), &last_result.cdata[1]);
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3), &last_result.cdata[0]);
+
+ /*
+ * The NetBSD guys say to loop until you get the same reading
+ * twice in a row.
+ */
+
+ while (1) {
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_0),
+ &result.cdata[3]);
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_1),
+ &result.cdata[2]);
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_2),
+ &result.cdata[1]);
+ via_rtc_command(RTC_CMD_READ(RTC_REG_SECONDS_3),
+ &result.cdata[0]);
+
+ if (result.idata == last_result.idata)
+ return (time64_t)result.idata - RTC_OFFSET;
+
+ if (++count > 10)
+ break;
+
+ last_result.idata = result.idata;
+ }
+
+ pr_err("%s: failed to read a stable value; got 0x%08x then 0x%08x\n",
+ __func__, last_result.idata, result.idata);
+
+ return 0;
+}
+
+/*
+ * 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_set_rtc_time(struct rtc_time *tm)
+{
+ union {
+ __u8 cdata[4];
+ __u32 idata;
+ } data;
+ __u8 temp;
+ time64_t time;
+
+ time = mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ /* Clear the write protect bit */
+
+ temp = 0x55;
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &temp);
+
+ data.idata = lower_32_bits(time + RTC_OFFSET);
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_0), &data.cdata[3]);
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_1), &data.cdata[2]);
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_2), &data.cdata[1]);
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_SECONDS_3), &data.cdata[0]);
+
+ /* Set the write protect bit */
+
+ temp = 0x55 | RTC_FLG_WRITE_PROTECT;
+ via_rtc_command(RTC_CMD_WRITE(RTC_REG_WRITE_PROTECT), &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);
+ }
+}
+
+static void oss_shutdown(void)
+{
+ oss->rom_ctrl = OSS_POWEROFF;
+}
+
+#ifdef CONFIG_ADB_CUDA
+static void cuda_restart(void)
+{
+ struct adb_request req;
+
+ if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM) < 0)
+ return;
+ while (!req.complete)
+ cuda_poll();
+}
+
+static void cuda_shutdown(void)
+{
+ struct adb_request req;
+
+ if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN) < 0)
+ return;
+
+ /* Avoid infinite polling loop when PSU is not under Cuda control */
+ switch (macintosh_config->ident) {
+ case MAC_MODEL_C660:
+ case MAC_MODEL_Q605:
+ case MAC_MODEL_Q605_ACC:
+ case MAC_MODEL_P475:
+ case MAC_MODEL_P475F:
+ return;
+ }
+
+ while (!req.complete)
+ cuda_poll();
+}
+#endif /* CONFIG_ADB_CUDA */
+
+/*
+ *-------------------------------------------------------------------
+ * Below this point are the generic routines; they'll dispatch to the
+ * correct routine for the hardware on which we're running.
+ *-------------------------------------------------------------------
+ */
+
+#if IS_ENABLED(CONFIG_NVRAM)
+unsigned char mac_pram_read_byte(int addr)
+{
+ switch (macintosh_config->adb_type) {
+ case MAC_ADB_IOP:
+ case MAC_ADB_II:
+ case MAC_ADB_PB1:
+ return via_pram_read_byte(addr);
+#ifdef CONFIG_ADB_CUDA
+ case MAC_ADB_EGRET:
+ case MAC_ADB_CUDA:
+ return cuda_pram_read_byte(addr);
+#endif
+#ifdef CONFIG_ADB_PMU
+ case MAC_ADB_PB2:
+ return pmu_pram_read_byte(addr);
+#endif
+ default:
+ return 0xFF;
+ }
+}
+
+void mac_pram_write_byte(unsigned char val, int addr)
+{
+ switch (macintosh_config->adb_type) {
+ case MAC_ADB_IOP:
+ case MAC_ADB_II:
+ case MAC_ADB_PB1:
+ via_pram_write_byte(val, addr);
+ break;
+#ifdef CONFIG_ADB_CUDA
+ case MAC_ADB_EGRET:
+ case MAC_ADB_CUDA:
+ cuda_pram_write_byte(val, addr);
+ break;
+#endif
+#ifdef CONFIG_ADB_PMU
+ case MAC_ADB_PB2:
+ pmu_pram_write_byte(val, addr);
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+ssize_t mac_pram_get_size(void)
+{
+ return 256;
+}
+#endif /* CONFIG_NVRAM */
+
+void mac_poweroff(void)
+{
+ 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_EGRET ||
+ macintosh_config->adb_type == MAC_ADB_CUDA) {
+ cuda_shutdown();
+#endif
+#ifdef CONFIG_ADB_PMU
+ } else if (macintosh_config->adb_type == MAC_ADB_PB2) {
+ pmu_shutdown();
+#endif
+ }
+
+ pr_crit("It is now safe to turn off your Macintosh.\n");
+ local_irq_disable();
+ while(1);
+}
+
+void mac_reset(void)
+{
+#ifdef CONFIG_ADB_CUDA
+ if (macintosh_config->adb_type == MAC_ADB_EGRET ||
+ macintosh_config->adb_type == MAC_ADB_CUDA) {
+ cuda_restart();
+ } else
+#endif
+#ifdef CONFIG_ADB_PMU
+ if (macintosh_config->adb_type == MAC_ADB_PB2) {
+ pmu_restart();
+ } else
+#endif
+ 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 addr = (phys&0xFF000000)|0x8777;
+ 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" (addr));
+ /* 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");
+ } else {
+ /* 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);
+
+ local_irq_disable();
+ rom_reset();
+ }
+
+ /* should never get here */
+ pr_crit("Restart failed. Please restart manually.\n");
+ local_irq_disable();
+ while(1);
+}
+
+/*
+ * This function translates seconds since 1970 into a proper date.
+ *
+ * Algorithm cribbed from glibc2.1, __offtime().
+ *
+ * This is roughly same as rtc_time64_to_tm(), which we should probably
+ * use here, but it's only available when CONFIG_RTC_LIB is enabled.
+ */
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR (SECS_PER_MINUTE * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+static void unmktime(time64_t 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 }
+ };
+ int days, rem, y, wday;
+ const unsigned short int *ip;
+
+ days = div_u64_rem(time, SECS_PER_DAY, &rem);
+ 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;
+ 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)
+{
+ time64_t now;
+
+ if (!op) { /* read */
+ switch (macintosh_config->adb_type) {
+ case MAC_ADB_IOP:
+ case MAC_ADB_II:
+ case MAC_ADB_PB1:
+ now = via_read_time();
+ break;
+#ifdef CONFIG_ADB_CUDA
+ case MAC_ADB_EGRET:
+ case MAC_ADB_CUDA:
+ now = cuda_get_time();
+ break;
+#endif
+#ifdef CONFIG_ADB_PMU
+ case MAC_ADB_PB2:
+ now = pmu_get_time();
+ break;
+#endif
+ default:
+ 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);
+ pr_debug("%s: read %ptR\n", __func__, t);
+ } else { /* write */
+ pr_debug("%s: tried to write %ptR\n", __func__, t);
+
+ switch (macintosh_config->adb_type) {
+ case MAC_ADB_IOP:
+ case MAC_ADB_II:
+ case MAC_ADB_PB1:
+ via_set_rtc_time(t);
+ break;
+#ifdef CONFIG_ADB_CUDA
+ case MAC_ADB_EGRET:
+ case MAC_ADB_CUDA:
+ cuda_set_rtc_time(t);
+ break;
+#endif
+#ifdef CONFIG_ADB_PMU
+ case MAC_ADB_PB2:
+ pmu_set_rtc_time(t);
+ break;
+#endif
+ default:
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c
new file mode 100644
index 000000000..1641607f3
--- /dev/null
+++ b/arch/m68k/mac/oss.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Operating System Services (OSS) chip 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 full 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 <linux/irq.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+#include "mac.h"
+
+int oss_present;
+volatile struct mac_oss *oss;
+
+/*
+ * Initialize the OSS
+ */
+
+void __init oss_init(void)
+{
+ int i;
+
+ if (macintosh_config->ident != MAC_MODEL_IIFX)
+ return;
+
+ oss = (struct mac_oss *) OSS_BASE;
+ pr_debug("OSS detected at %p", oss);
+ oss_present = 1;
+
+ /* 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] = 0;
+}
+
+/*
+ * Handle OSS interrupts.
+ * XXX how do you clear a pending IRQ? is it even necessary?
+ */
+
+static void oss_iopism_irq(struct irq_desc *desc)
+{
+ generic_handle_irq(IRQ_MAC_ADB);
+}
+
+static void oss_scsi_irq(struct irq_desc *desc)
+{
+ generic_handle_irq(IRQ_MAC_SCSI);
+}
+
+static void oss_nubus_irq(struct irq_desc *desc)
+{
+ u16 events, irq_bit;
+ int irq_num;
+
+ events = oss->irq_pending & OSS_IP_NUBUS;
+ irq_num = NUBUS_SOURCE_BASE + 5;
+ irq_bit = OSS_IP_NUBUS5;
+ do {
+ if (events & irq_bit) {
+ events &= ~irq_bit;
+ generic_handle_irq(irq_num);
+ }
+ --irq_num;
+ irq_bit >>= 1;
+ } while (events);
+}
+
+static void oss_iopscc_irq(struct irq_desc *desc)
+{
+ generic_handle_irq(IRQ_MAC_SCC);
+}
+
+/*
+ * Register the OSS and NuBus interrupt dispatchers.
+ *
+ * This IRQ mapping is laid out with two things in mind: first, we try to keep
+ * things on their own levels to avoid having to do double-dispatches. Second,
+ * the levels match as closely as possible the alternate IRQ mapping mode (aka
+ * "A/UX mode") available on some VIA machines.
+ */
+
+#define OSS_IRQLEV_IOPISM IRQ_AUTO_1
+#define OSS_IRQLEV_SCSI IRQ_AUTO_2
+#define OSS_IRQLEV_NUBUS IRQ_AUTO_3
+#define OSS_IRQLEV_IOPSCC IRQ_AUTO_4
+#define OSS_IRQLEV_VIA1 IRQ_AUTO_6
+
+void __init oss_register_interrupts(void)
+{
+ irq_set_chained_handler(OSS_IRQLEV_IOPISM, oss_iopism_irq);
+ irq_set_chained_handler(OSS_IRQLEV_SCSI, oss_scsi_irq);
+ irq_set_chained_handler(OSS_IRQLEV_NUBUS, oss_nubus_irq);
+ irq_set_chained_handler(OSS_IRQLEV_IOPSCC, oss_iopscc_irq);
+ irq_set_chained_handler(OSS_IRQLEV_VIA1, via1_irq);
+
+ /* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
+ oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1;
+}
+
+/*
+ * 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) {
+ switch(irq) {
+ case IRQ_MAC_SCC:
+ oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC;
+ return;
+ case IRQ_MAC_ADB:
+ oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM;
+ return;
+ case IRQ_MAC_SCSI:
+ oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI;
+ return;
+ 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;
+ return;
+ }
+
+ if (IRQ_SRC(irq) == 1)
+ via_irq_enable(irq);
+}
+
+/*
+ * 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) {
+ switch(irq) {
+ case IRQ_MAC_SCC:
+ oss->irq_level[OSS_IOPSCC] = 0;
+ return;
+ case IRQ_MAC_ADB:
+ oss->irq_level[OSS_IOPISM] = 0;
+ return;
+ case IRQ_MAC_SCSI:
+ oss->irq_level[OSS_SCSI] = 0;
+ return;
+ 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] = 0;
+ return;
+ }
+
+ if (IRQ_SRC(irq) == 1)
+ via_irq_disable(irq);
+}
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c
new file mode 100644
index 000000000..b4183cf66
--- /dev/null
+++ b/arch/m68k/mac/psc.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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 <linux/irq.h>
+
+#include <asm/traps.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_psc.h>
+
+#include "mac.h"
+
+#define DEBUG_PSC
+
+volatile __u8 *psc;
+EXPORT_SYMBOL_GPL(psc);
+
+/*
+ * Debugging dump, used in various places to see what's going on.
+ */
+
+static void psc_debug_dump(void)
+{
+ int i;
+
+ if (!psc)
+ return;
+
+ for (i = 0x30 ; i < 0x70 ; i += 0x10) {
+ printk(KERN_DEBUG "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.
+ */
+
+static __init void psc_dma_die_die_die(void)
+{
+ int i;
+
+ 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);
+ }
+}
+
+/*
+ * 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;
+ return;
+ }
+
+ /*
+ * The PSC is always at the same spot, but using psc
+ * keeps things consistent with the psc_xxxx functions.
+ */
+
+ psc = (void *) PSC_BASE;
+
+ pr_debug("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);
+ }
+}
+
+/*
+ * PSC interrupt handler. It's a lot like the VIA interrupt handler.
+ */
+
+static void psc_irq(struct irq_desc *desc)
+{
+ unsigned int offset = (unsigned int)irq_desc_get_handler_data(desc);
+ unsigned int irq = irq_desc_get_irq(desc);
+ int pIFR = pIFRbase + offset;
+ int pIER = pIERbase + offset;
+ int irq_num;
+ unsigned char irq_bit, events;
+
+ events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF;
+ if (!events)
+ return;
+
+ irq_num = irq << 3;
+ irq_bit = 1;
+ do {
+ if (events & irq_bit) {
+ psc_write_byte(pIFR, irq_bit);
+ generic_handle_irq(irq_num);
+ }
+ irq_num++;
+ irq_bit <<= 1;
+ } while (events >= irq_bit);
+}
+
+/*
+ * Register the PSC interrupt dispatchers for autovector interrupts 3-6.
+ */
+
+void __init psc_register_interrupts(void)
+{
+ irq_set_chained_handler_and_data(IRQ_AUTO_3, psc_irq, (void *)0x30);
+ irq_set_chained_handler_and_data(IRQ_AUTO_4, psc_irq, (void *)0x40);
+ irq_set_chained_handler_and_data(IRQ_AUTO_5, psc_irq, (void *)0x50);
+ irq_set_chained_handler_and_data(IRQ_AUTO_6, psc_irq, (void *)0x60);
+}
+
+void psc_irq_enable(int irq) {
+ int irq_src = IRQ_SRC(irq);
+ int irq_idx = IRQ_IDX(irq);
+ int pIER = pIERbase + (irq_src << 4);
+
+ 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);
+
+ psc_write_byte(pIER, 1 << irq_idx);
+}
diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c
new file mode 100644
index 000000000..9cb813eda
--- /dev/null
+++ b/arch/m68k/mac/via.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 6522 Versatile Interface Adapter (VIA)
+ *
+ * There are two of these on the Mac II. Some IRQs 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
+ *
+ * Additional data is here (the SY6522 was used in the Mac II etc):
+ * http://www.6502.org/documents/datasheets/synertek/synertek_sy6522.pdf
+ * http://www.6502.org/documents/datasheets/synertek/synertek_sy6522_programming_reference.pdf
+ *
+ * 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/clocksource.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/mac_via.h>
+#include <asm/mac_psc.h>
+#include <asm/mac_oss.h>
+
+#include "mac.h"
+
+volatile __u8 *via1, *via2;
+int rbv_present;
+int via_alt_mapping;
+EXPORT_SYMBOL(via_alt_mapping);
+static __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;
+
+/*
+ * On Macs with a genuine VIA chip there is no way to mask an individual slot
+ * interrupt. This limitation also seems to apply to VIA clone logic cores in
+ * Quadra-like ASICs. (RBV and OSS machines don't have this limitation.)
+ *
+ * We used to fake it by configuring the relevant VIA pin as an output
+ * (to mask the interrupt) or input (to unmask). That scheme did not work on
+ * (at least) the Quadra 700. A NuBus card's /NMRQ signal is an open-collector
+ * circuit (see Designing Cards and Drivers for Macintosh II and Macintosh SE,
+ * p. 10-11 etc) but VIA outputs are not (see datasheet).
+ *
+ * Driving these outputs high must cause the VIA to source current and the
+ * card to sink current when it asserts /NMRQ. Current will flow but the pin
+ * voltage is uncertain and so the /NMRQ condition may still cause a transition
+ * at the VIA2 CA1 input (which explains the lost interrupts). A side effect
+ * is that a disabled slot IRQ can never be tested as pending or not.
+ *
+ * Driving these outputs low doesn't work either. All the slot /NMRQ lines are
+ * (active low) OR'd together to generate the CA1 (aka "SLOTS") interrupt (see
+ * The Guide To Macintosh Family Hardware, 2nd edition p. 167). If we drive a
+ * disabled /NMRQ line low, the falling edge immediately triggers a CA1
+ * interrupt and all slot interrupts after that will generate no transition
+ * and therefore no interrupt, even after being re-enabled.
+ *
+ * So we make the VIA port A I/O lines inputs and use nubus_disabled to keep
+ * track of their states. When any slot IRQ becomes disabled we mask the CA1
+ * umbrella interrupt. Only when all slot IRQs become enabled do we unmask
+ * the CA1 interrupt. It must remain enabled even when cards have no interrupt
+ * handler registered. Drivers must therefore disable a slot interrupt at the
+ * device before they call free_irq (like shared and autovector interrupts).
+ *
+ * There is also a related problem when MacOS is used to boot Linux. A network
+ * card brought up by a MacOS driver may raise an interrupt while Linux boots.
+ * This can be fatal since it can't be handled until the right driver loads
+ * (if such a driver exists at all). Apparently related to this hardware
+ * limitation, "Designing Cards and Drivers", p. 9-8, says that a slot
+ * interrupt with no driver would crash MacOS (the book was written before
+ * the appearance of Macs with RBV or OSS).
+ */
+
+static u8 nubus_disabled;
+
+void via_debug_dump(void);
+static void via_nubus_init(void);
+
+/*
+ * 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.
+ */
+
+void __init via_init(void)
+{
+ via1 = (void *)VIA1_BASE;
+ pr_debug("VIA1 detected at %p\n", via1);
+
+ if (oss_present) {
+ via2 = NULL;
+ rbv_present = 0;
+ } else {
+ switch (macintosh_config->via_type) {
+
+ /* IIci, IIsi, IIvx, IIvi (P6xx), LC series */
+
+ case MAC_VIA_IICI:
+ via2 = (void *)RBV_BASE;
+ pr_debug("VIA2 (RBV) detected at %p\n", via2);
+ rbv_present = 1;
+ 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:
+ via2 = (void *) VIA2_BASE;
+ pr_debug("VIA2 detected at %p\n", via2);
+ rbv_present = 0;
+ rbv_clear = 0x00;
+ gIER = vIER;
+ gIFR = vIFR;
+ gBufA = vBufA;
+ gBufB = vBufB;
+ break;
+
+ default:
+ panic("UNKNOWN VIA TYPE");
+ }
+ }
+
+#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[vT1CL] = 0;
+ via1[vT1CH] = 0;
+ via1[vT2CL] = 0;
+ via1[vT2CH] = 0;
+ via1[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
+ via1[vACR] &= ~0x03; /* disable port A & B latches */
+
+ /*
+ * SE/30: disable video IRQ
+ */
+
+ if (macintosh_config->ident == MAC_MODEL_SE30) {
+ via1[vDirB] |= 0x40;
+ via1[vBufB] |= 0x40;
+ }
+
+ switch (macintosh_config->adb_type) {
+ case MAC_ADB_IOP:
+ case MAC_ADB_II:
+ case MAC_ADB_PB1:
+ /*
+ * 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;
+ break;
+ }
+
+ /* Everything below this point is VIA2/RBV only... */
+
+ if (oss_present)
+ return;
+
+ if ((macintosh_config->via_type == MAC_VIA_QUADRA) &&
+ (macintosh_config->adb_type != MAC_ADB_PB1) &&
+ (macintosh_config->adb_type != MAC_ADB_PB2) &&
+ (macintosh_config->ident != MAC_MODEL_C660) &&
+ (macintosh_config->ident != MAC_MODEL_Q840)) {
+ via_alt_mapping = 1;
+ via1[vDirB] |= 0x40;
+ via1[vBufB] &= ~0x40;
+ } else {
+ via_alt_mapping = 0;
+ }
+
+ /*
+ * 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[vT1CL] = 0;
+ via2[vT1CH] = 0;
+ via2[vT2CL] = 0;
+ via2[vT2CH] = 0;
+ via2[vACR] &= ~0xC0; /* setup T1 timer with no PB7 output */
+ via2[vACR] &= ~0x03; /* disable port A & B latches */
+ }
+
+ via_nubus_init();
+
+ /* Everything below this point is VIA2 only... */
+
+ if (rbv_present)
+ return;
+
+ /*
+ * Set vPCR for control line interrupts.
+ *
+ * CA1 (SLOTS IRQ), CB1 (ASC IRQ): negative edge trigger.
+ *
+ * Macs with ESP SCSI have a negative edge triggered SCSI interrupt.
+ * Testing reveals that PowerBooks do too. However, the SE/30
+ * schematic diagram shows an active high NCR5380 IRQ line.
+ */
+
+ pr_debug("VIA2 vPCR is 0x%02X\n", via2[vPCR]);
+ if (macintosh_config->via_type == MAC_VIA_II) {
+ /* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, pos. edge */
+ via2[vPCR] = 0x66;
+ } else {
+ /* CA2 (SCSI DRQ), CB2 (SCSI IRQ): indep. input, neg. edge */
+ via2[vPCR] = 0x22;
+ }
+}
+
+/*
+ * 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 (!via2)
+ return;
+ 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]);
+ }
+}
+
+/*
+ * Flush the L2 cache on Macs that have it by flipping
+ * the system into 24-bit mode for an instant.
+ */
+
+void via_l2_flush(int writeback)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ via2[gBufB] &= ~VIA2B_vMode32;
+ via2[gBufB] |= VIA2B_vMode32;
+ local_irq_restore(flags);
+}
+
+/*
+ * Initialize VIA2 for Nubus access
+ */
+
+static void __init via_nubus_init(void)
+{
+ /* unlock nubus transactions */
+
+ if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+ (macintosh_config->adb_type != MAC_ADB_PB2)) {
+ /* set the line to be an output on non-RBV machines */
+ if (!rbv_present)
+ via2[vDirB] |= 0x02;
+
+ /* this seems to be an ADB bit on PMU machines */
+ /* according to MkLinux. -- jmt */
+ via2[gBufB] |= 0x02;
+ }
+
+ /*
+ * Disable the slot interrupts. On some hardware that's not possible.
+ * On some hardware it's unclear what all of these I/O lines do.
+ */
+
+ switch (macintosh_config->via_type) {
+ case MAC_VIA_II:
+ case MAC_VIA_QUADRA:
+ pr_debug("VIA2 vDirA is 0x%02X\n", via2[vDirA]);
+ break;
+ case MAC_VIA_IICI:
+ /* RBV. Disable all the slot interrupts. SIER works like IER. */
+ via2[rSIER] = 0x7F;
+ break;
+ }
+}
+
+void via_nubus_irq_startup(int irq)
+{
+ int irq_idx = IRQ_IDX(irq);
+
+ switch (macintosh_config->via_type) {
+ case MAC_VIA_II:
+ case MAC_VIA_QUADRA:
+ /* Make the port A line an input. Probably redundant. */
+ if (macintosh_config->via_type == MAC_VIA_II) {
+ /* The top two bits are RAM size outputs. */
+ via2[vDirA] &= 0xC0 | ~(1 << irq_idx);
+ } else {
+ /* Allow NuBus slots 9 through F. */
+ via2[vDirA] &= 0x80 | ~(1 << irq_idx);
+ }
+ fallthrough;
+ case MAC_VIA_IICI:
+ via_irq_enable(irq);
+ break;
+ }
+}
+
+void via_nubus_irq_shutdown(int irq)
+{
+ switch (macintosh_config->via_type) {
+ case MAC_VIA_II:
+ case MAC_VIA_QUADRA:
+ /* Ensure that the umbrella CA1 interrupt remains enabled. */
+ via_irq_enable(irq);
+ break;
+ case MAC_VIA_IICI:
+ via_irq_disable(irq);
+ break;
+ }
+}
+
+/*
+ * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's
+ * via6522.c :-), disable/pending masks added.
+ */
+
+#define VIA_TIMER_1_INT BIT(6)
+
+void via1_irq(struct irq_desc *desc)
+{
+ int irq_num;
+ unsigned char irq_bit, events;
+
+ events = via1[vIFR] & via1[vIER] & 0x7F;
+ if (!events)
+ return;
+
+ irq_num = IRQ_MAC_TIMER_1;
+ irq_bit = VIA_TIMER_1_INT;
+ if (events & irq_bit) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+ via1[vIFR] = irq_bit;
+ generic_handle_irq(irq_num);
+ local_irq_restore(flags);
+
+ events &= ~irq_bit;
+ if (!events)
+ return;
+ }
+
+ irq_num = VIA1_SOURCE_BASE;
+ irq_bit = 1;
+ do {
+ if (events & irq_bit) {
+ via1[vIFR] = irq_bit;
+ generic_handle_irq(irq_num);
+ }
+ ++irq_num;
+ irq_bit <<= 1;
+ } while (events >= irq_bit);
+}
+
+static void via2_irq(struct irq_desc *desc)
+{
+ int irq_num;
+ unsigned char irq_bit, events;
+
+ events = via2[gIFR] & via2[gIER] & 0x7F;
+ if (!events)
+ return;
+
+ irq_num = VIA2_SOURCE_BASE;
+ irq_bit = 1;
+ do {
+ if (events & irq_bit) {
+ via2[gIFR] = irq_bit | rbv_clear;
+ generic_handle_irq(irq_num);
+ }
+ ++irq_num;
+ irq_bit <<= 1;
+ } while (events >= irq_bit);
+}
+
+/*
+ * Dispatch Nubus interrupts. We are called as a secondary dispatch by the
+ * VIA2 dispatcher as a fast interrupt handler.
+ */
+
+static void via_nubus_irq(struct irq_desc *desc)
+{
+ int slot_irq;
+ unsigned char slot_bit, events;
+
+ events = ~via2[gBufA] & 0x7F;
+ if (rbv_present)
+ events &= via2[rSIER];
+ else
+ events &= ~via2[vDirA];
+ if (!events)
+ return;
+
+ do {
+ slot_irq = IRQ_NUBUS_F;
+ slot_bit = 0x40;
+ do {
+ if (events & slot_bit) {
+ events &= ~slot_bit;
+ generic_handle_irq(slot_irq);
+ }
+ --slot_irq;
+ slot_bit >>= 1;
+ } while (events);
+
+ /* clear the CA1 interrupt and make certain there's no more. */
+ via2[gIFR] = 0x02 | rbv_clear;
+ events = ~via2[gBufA] & 0x7F;
+ if (rbv_present)
+ events &= via2[rSIER];
+ else
+ events &= ~via2[vDirA];
+ } while (events);
+}
+
+/*
+ * Register the interrupt dispatchers for VIA or RBV machines only.
+ */
+
+void __init via_register_interrupts(void)
+{
+ if (via_alt_mapping) {
+ /* software interrupt */
+ irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
+ /* via1 interrupt */
+ irq_set_chained_handler(IRQ_AUTO_6, via1_irq);
+ } else {
+ irq_set_chained_handler(IRQ_AUTO_1, via1_irq);
+ }
+ irq_set_chained_handler(IRQ_AUTO_2, via2_irq);
+ irq_set_chained_handler(IRQ_MAC_NUBUS, via_nubus_irq);
+}
+
+void via_irq_enable(int irq) {
+ int irq_src = IRQ_SRC(irq);
+ int irq_idx = IRQ_IDX(irq);
+
+ if (irq_src == 1) {
+ via1[vIER] = IER_SET_BIT(irq_idx);
+ } else if (irq_src == 2) {
+ if (irq != IRQ_MAC_NUBUS || nubus_disabled == 0)
+ via2[gIER] = IER_SET_BIT(irq_idx);
+ } else if (irq_src == 7) {
+ switch (macintosh_config->via_type) {
+ case MAC_VIA_II:
+ case MAC_VIA_QUADRA:
+ nubus_disabled &= ~(1 << irq_idx);
+ /* Enable the CA1 interrupt when no slot is disabled. */
+ if (!nubus_disabled)
+ via2[gIER] = IER_SET_BIT(1);
+ break;
+ case MAC_VIA_IICI:
+ /* On RBV, enable the slot interrupt.
+ * SIER works like IER.
+ */
+ via2[rSIER] = IER_SET_BIT(irq_idx);
+ break;
+ }
+ }
+}
+
+void via_irq_disable(int irq) {
+ int irq_src = IRQ_SRC(irq);
+ int irq_idx = IRQ_IDX(irq);
+
+ if (irq_src == 1) {
+ via1[vIER] = IER_CLR_BIT(irq_idx);
+ } else if (irq_src == 2) {
+ via2[gIER] = IER_CLR_BIT(irq_idx);
+ } else if (irq_src == 7) {
+ switch (macintosh_config->via_type) {
+ case MAC_VIA_II:
+ case MAC_VIA_QUADRA:
+ nubus_disabled |= 1 << irq_idx;
+ if (nubus_disabled)
+ via2[gIER] = IER_CLR_BIT(1);
+ break;
+ case MAC_VIA_IICI:
+ via2[rSIER] = IER_CLR_BIT(irq_idx);
+ break;
+ }
+ }
+}
+
+void via1_set_head(int head)
+{
+ if (head == 0)
+ via1[vBufA] &= ~VIA1A_vHeadSel;
+ else
+ via1[vBufA] |= VIA1A_vHeadSel;
+}
+EXPORT_SYMBOL(via1_set_head);
+
+int via2_scsi_drq_pending(void)
+{
+ return via2[gIFR] & (1 << IRQ_IDX(IRQ_MAC_SCSIDRQ));
+}
+EXPORT_SYMBOL(via2_scsi_drq_pending);
+
+/* timer and clock source */
+
+#define VIA_CLOCK_FREQ 783360 /* VIA "phase 2" clock in Hz */
+#define VIA_TIMER_CYCLES (VIA_CLOCK_FREQ / HZ) /* clock cycles per jiffy */
+
+#define VIA_TC (VIA_TIMER_CYCLES - 2) /* including 0 and -1 */
+#define VIA_TC_LOW (VIA_TC & 0xFF)
+#define VIA_TC_HIGH (VIA_TC >> 8)
+
+static u64 mac_read_clk(struct clocksource *cs);
+
+static struct clocksource mac_clk = {
+ .name = "via1",
+ .rating = 250,
+ .read = mac_read_clk,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static u32 clk_total, clk_offset;
+
+static irqreturn_t via_timer_handler(int irq, void *dev_id)
+{
+ clk_total += VIA_TIMER_CYCLES;
+ clk_offset = 0;
+ legacy_timer_tick(1);
+
+ return IRQ_HANDLED;
+}
+
+void __init via_init_clock(void)
+{
+ if (request_irq(IRQ_MAC_TIMER_1, via_timer_handler, IRQF_TIMER, "timer",
+ NULL)) {
+ pr_err("Couldn't register %s interrupt\n", "timer");
+ return;
+ }
+
+ via1[vT1CL] = VIA_TC_LOW;
+ via1[vT1CH] = VIA_TC_HIGH;
+ via1[vACR] |= 0x40;
+
+ clocksource_register_hz(&mac_clk, VIA_CLOCK_FREQ);
+}
+
+static u64 mac_read_clk(struct clocksource *cs)
+{
+ unsigned long flags;
+ u8 count_high;
+ u16 count;
+ u32 ticks;
+
+ /*
+ * Timer counter wrap-around is detected with the timer interrupt flag
+ * but reading the counter low byte (vT1CL) would reset the flag.
+ * Also, accessing both counter registers is essentially a data race.
+ * These problems are avoided by ignoring the low byte. Clock accuracy
+ * is 256 times worse (error can reach 0.327 ms) but CPU overhead is
+ * reduced by avoiding slow VIA register accesses.
+ *
+ * The VIA timer counter observably decrements to 0xFFFF before the
+ * counter reload interrupt gets raised. That complicates things a bit.
+ *
+ * State | vT1CH | VIA_TIMER_1_INT | inference drawn
+ * ------+------------+-----------------+-----------------------------
+ * i | FE thru 00 | false | counter is decrementing
+ * ii | FF | false | counter wrapped
+ * iii | FF | true | wrapped, interrupt raised
+ * iv | FF | false | wrapped, interrupt handled
+ * v | FE thru 00 | true | wrapped, interrupt unhandled
+ *
+ * State iv is never observed because handling the interrupt involves
+ * a 6522 register access and every access consumes a "phi 2" clock
+ * cycle. So 0xFF implies either state ii or state iii, depending on
+ * the value of the VIA_TIMER_1_INT bit.
+ */
+
+ local_irq_save(flags);
+ count_high = via1[vT1CH];
+ if (count_high == 0xFF)
+ count_high = 0;
+ if (count_high > 0 && (via1[vIFR] & VIA_TIMER_1_INT))
+ clk_offset = VIA_TIMER_CYCLES;
+ count = count_high << 8;
+ ticks = VIA_TIMER_CYCLES - count;
+ ticks += clk_offset + clk_total;
+ local_irq_restore(flags);
+
+ return ticks;
+}