File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/arch/m68k/sun3/Makefile b/arch/m68k/sun3/Makefile
new file mode 100644
index 0000000..38ba0e0
--- /dev/null
+++ b/arch/m68k/sun3/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Linux arch/m68k/sun3 source directory
+#
+
+obj-y	:= sun3ints.o sun3dvma.o idprom.o
+
+obj-$(CONFIG_SUN3) += config.o mmu_emu.o leds.o dvma.o intersil.o
diff --git a/arch/m68k/sun3/config.c b/arch/m68k/sun3/config.c
new file mode 100644
index 0000000..2a5f43a
--- /dev/null
+++ b/arch/m68k/sun3/config.c
@@ -0,0 +1,231 @@
+/*
+ *  linux/arch/m68k/sun3/config.c
+ *
+ *  Copyright (C) 1996,1997 Pekka Pietik{inen
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/platform_device.h>
+
+#include <asm/oplib.h>
+#include <asm/setup.h>
+#include <asm/contregs.h>
+#include <asm/movs.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/sun3-head.h>
+#include <asm/sun3mmu.h>
+#include <asm/rtc.h>
+#include <asm/machdep.h>
+#include <asm/machines.h>
+#include <asm/idprom.h>
+#include <asm/intersil.h>
+#include <asm/irq.h>
+#include <asm/sections.h>
+#include <asm/segment.h>
+#include <asm/sun3ints.h>
+
+char sun3_reserved_pmeg[SUN3_PMEGS_NUM];
+
+extern u32 sun3_gettimeoffset(void);
+static void sun3_sched_init(irq_handler_t handler);
+extern void sun3_get_model (char* model);
+extern int sun3_hwclk(int set, struct rtc_time *t);
+
+volatile char* clock_va;
+extern unsigned long availmem;
+unsigned long num_pages;
+
+static void sun3_get_hardware_list(struct seq_file *m)
+{
+	seq_printf(m, "PROM Revision:\t%s\n", romvec->pv_monid);
+}
+
+void __init sun3_init(void)
+{
+	unsigned char enable_register;
+	int i;
+
+	m68k_machtype= MACH_SUN3;
+	m68k_cputype = CPU_68020;
+	m68k_fputype = FPU_68881; /* mc68881 actually */
+	m68k_mmutype = MMU_SUN3;
+	clock_va    =          (char *) 0xfe06000;	/* dark  */
+	sun3_intreg = (unsigned char *) 0xfe0a000;	/* magic */
+	sun3_disable_interrupts();
+
+	prom_init((void *)LINUX_OPPROM_BEGVM);
+
+	GET_CONTROL_BYTE(AC_SENABLE,enable_register);
+	enable_register |= 0x50; /* Enable FPU */
+	SET_CONTROL_BYTE(AC_SENABLE,enable_register);
+	GET_CONTROL_BYTE(AC_SENABLE,enable_register);
+
+	/* This code looks suspicious, because it doesn't subtract
+           memory belonging to the kernel from the available space */
+
+
+	memset(sun3_reserved_pmeg, 0, sizeof(sun3_reserved_pmeg));
+
+	/* Reserve important PMEGS */
+	/* FIXME: These should be probed instead of hardcoded */
+
+	for (i=0; i<8; i++)		/* Kernel PMEGs */
+		sun3_reserved_pmeg[i] = 1;
+
+	sun3_reserved_pmeg[247] = 1;	/* ROM mapping  */
+	sun3_reserved_pmeg[248] = 1;	/* AMD Ethernet */
+	sun3_reserved_pmeg[251] = 1;	/* VB area      */
+	sun3_reserved_pmeg[254] = 1;	/* main I/O     */
+
+	sun3_reserved_pmeg[249] = 1;
+	sun3_reserved_pmeg[252] = 1;
+	sun3_reserved_pmeg[253] = 1;
+	set_fs(KERNEL_DS);
+}
+
+/* Without this, Bad Things happen when something calls arch_reset. */
+static void sun3_reboot (void)
+{
+	prom_reboot ("vmlinux");
+}
+
+static void sun3_halt (void)
+{
+	prom_halt ();
+}
+
+/* sun3 bootmem allocation */
+
+static void __init sun3_bootmem_alloc(unsigned long memory_start,
+				      unsigned long memory_end)
+{
+	unsigned long start_page;
+
+	/* align start/end to page boundaries */
+	memory_start = ((memory_start + (PAGE_SIZE-1)) & PAGE_MASK);
+	memory_end = memory_end & PAGE_MASK;
+
+	start_page = __pa(memory_start) >> PAGE_SHIFT;
+	max_pfn = num_pages = __pa(memory_end) >> PAGE_SHIFT;
+
+	high_memory = (void *)memory_end;
+	availmem = memory_start;
+
+	m68k_setup_node(0);
+	availmem += init_bootmem(start_page, num_pages);
+	availmem = (availmem + (PAGE_SIZE-1)) & PAGE_MASK;
+
+	free_bootmem(__pa(availmem), memory_end - (availmem));
+}
+
+
+void __init config_sun3(void)
+{
+	unsigned long memory_start, memory_end;
+
+	printk("ARCH: SUN3\n");
+	idprom_init();
+
+	/* Subtract kernel memory from available memory */
+
+        mach_sched_init      =  sun3_sched_init;
+        mach_init_IRQ        =  sun3_init_IRQ;
+        mach_reset           =  sun3_reboot;
+	arch_gettimeoffset   =  sun3_gettimeoffset;
+	mach_get_model	     =  sun3_get_model;
+	mach_hwclk           =  sun3_hwclk;
+	mach_halt	     =  sun3_halt;
+	mach_get_hardware_list = sun3_get_hardware_list;
+
+	memory_start = ((((unsigned long)_end) + 0x2000) & ~0x1fff);
+// PROM seems to want the last couple of physical pages. --m
+	memory_end   = *(romvec->pv_sun3mem) + PAGE_OFFSET - 2*PAGE_SIZE;
+
+	m68k_num_memory=1;
+        m68k_memory[0].size=*(romvec->pv_sun3mem);
+
+	sun3_bootmem_alloc(memory_start, memory_end);
+}
+
+static void __init sun3_sched_init(irq_handler_t timer_routine)
+{
+	sun3_disable_interrupts();
+        intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_DISABLE|INTERSIL_24H_MODE);
+        intersil_clock->int_reg=INTERSIL_HZ_100_MASK;
+	intersil_clear();
+        sun3_enable_irq(5);
+        intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_ENABLE|INTERSIL_24H_MODE);
+        sun3_enable_interrupts();
+        intersil_clear();
+}
+
+#ifdef CONFIG_SUN3_SCSI
+
+static const struct resource sun3_scsi_vme_rsrc[] __initconst = {
+	{
+		.flags = IORESOURCE_IRQ,
+		.start = SUN3_VEC_VMESCSI0,
+		.end   = SUN3_VEC_VMESCSI0,
+	}, {
+		.flags = IORESOURCE_MEM,
+		.start = 0xff200000,
+		.end   = 0xff200021,
+	}, {
+		.flags = IORESOURCE_IRQ,
+		.start = SUN3_VEC_VMESCSI1,
+		.end   = SUN3_VEC_VMESCSI1,
+	}, {
+		.flags = IORESOURCE_MEM,
+		.start = 0xff204000,
+		.end   = 0xff204021,
+	},
+};
+
+/*
+ * Int: level 2 autovector
+ * IO: type 1, base 0x00140000, 5 bits phys space: A<4..0>
+ */
+static const struct resource sun3_scsi_rsrc[] __initconst = {
+	{
+		.flags = IORESOURCE_IRQ,
+		.start = 2,
+		.end   = 2,
+	}, {
+		.flags = IORESOURCE_MEM,
+		.start = 0x00140000,
+		.end   = 0x0014001f,
+	},
+};
+
+int __init sun3_platform_init(void)
+{
+	switch (idprom->id_machtype) {
+	case SM_SUN3 | SM_3_160:
+	case SM_SUN3 | SM_3_260:
+		platform_device_register_simple("sun3_scsi_vme", -1,
+			sun3_scsi_vme_rsrc, ARRAY_SIZE(sun3_scsi_vme_rsrc));
+		break;
+	case SM_SUN3 | SM_3_50:
+	case SM_SUN3 | SM_3_60:
+		platform_device_register_simple("sun3_scsi", -1,
+			sun3_scsi_rsrc, ARRAY_SIZE(sun3_scsi_rsrc));
+		break;
+	}
+	return 0;
+}
+
+arch_initcall(sun3_platform_init);
+
+#endif
diff --git a/arch/m68k/sun3/dvma.c b/arch/m68k/sun3/dvma.c
new file mode 100644
index 0000000..d95506e
--- /dev/null
+++ b/arch/m68k/sun3/dvma.c
@@ -0,0 +1,69 @@
+/*
+ * linux/arch/m68k/sun3/dvma.c
+ *
+ * Written by Sam Creasey
+ *
+ * Sun3 IOMMU routines used for dvma accesses.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/list.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/sun3mmu.h>
+#include <asm/dvma.h>
+
+
+static unsigned long ptelist[120];
+
+static unsigned long dvma_page(unsigned long kaddr, unsigned long vaddr)
+{
+	unsigned long pte;
+	unsigned long j;
+	pte_t ptep;
+
+	j = *(volatile unsigned long *)kaddr;
+	*(volatile unsigned long *)kaddr = j;
+
+	ptep = pfn_pte(virt_to_pfn(kaddr), PAGE_KERNEL);
+	pte = pte_val(ptep);
+//		printk("dvma_remap: addr %lx -> %lx pte %08lx len %x\n",
+//		       kaddr, vaddr, pte, len);
+	if(ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] != pte) {
+		sun3_put_pte(vaddr, pte);
+		ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] = pte;
+	}
+
+	return (vaddr + (kaddr & ~PAGE_MASK));
+
+}
+
+int dvma_map_iommu(unsigned long kaddr, unsigned long baddr,
+			      int len)
+{
+
+	unsigned long end;
+	unsigned long vaddr;
+
+	vaddr = dvma_btov(baddr);
+
+	end = vaddr + len;
+
+	while(vaddr < end) {
+		dvma_page(kaddr, vaddr);
+		kaddr += PAGE_SIZE;
+		vaddr += PAGE_SIZE;
+	}
+
+	return 0;
+
+}
+
+void __init sun3_dvma_init(void)
+{
+	memset(ptelist, 0, sizeof(ptelist));
+}
diff --git a/arch/m68k/sun3/idprom.c b/arch/m68k/sun3/idprom.c
new file mode 100644
index 0000000..cfe9aa4
--- /dev/null
+++ b/arch/m68k/sun3/idprom.c
@@ -0,0 +1,129 @@
+/*
+ * idprom.c: Routines to load the idprom into kernel addresses and
+ *           interpret the data contained within.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Sun3/3x models added by David Monro (davidm@psrg.cs.usyd.edu.au)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <asm/oplib.h>
+#include <asm/idprom.h>
+#include <asm/machines.h>  /* Fun with Sun released architectures. */
+
+struct idprom *idprom;
+EXPORT_SYMBOL(idprom);
+
+static struct idprom idprom_buffer;
+
+/* Here is the master table of Sun machines which use some implementation
+ * of the Sparc CPU and have a meaningful IDPROM machtype value that we
+ * know about.  See asm-sparc/machines.h for empirical constants.
+ */
+static struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = {
+/* First, Sun3's */
+    { .name = "Sun 3/160 Series",	.id_machtype = (SM_SUN3 | SM_3_160) },
+    { .name = "Sun 3/50",		.id_machtype = (SM_SUN3 | SM_3_50) },
+    { .name = "Sun 3/260 Series",	.id_machtype = (SM_SUN3 | SM_3_260) },
+    { .name = "Sun 3/110 Series",	.id_machtype = (SM_SUN3 | SM_3_110) },
+    { .name = "Sun 3/60",		.id_machtype = (SM_SUN3 | SM_3_60) },
+    { .name = "Sun 3/E",		.id_machtype = (SM_SUN3 | SM_3_E) },
+/* Now, Sun3x's */
+    { .name = "Sun 3/460 Series",	.id_machtype = (SM_SUN3X | SM_3_460) },
+    { .name = "Sun 3/80",		.id_machtype = (SM_SUN3X | SM_3_80) },
+/* Then, Sun4's */
+// { .name = "Sun 4/100 Series",	.id_machtype = (SM_SUN4 | SM_4_110) },
+// { .name = "Sun 4/200 Series",	.id_machtype = (SM_SUN4 | SM_4_260) },
+// { .name = "Sun 4/300 Series",	.id_machtype = (SM_SUN4 | SM_4_330) },
+// { .name = "Sun 4/400 Series",	.id_machtype = (SM_SUN4 | SM_4_470) },
+/* And now, Sun4c's */
+// { .name = "Sun4c SparcStation 1",	.id_machtype = (SM_SUN4C | SM_4C_SS1) },
+// { .name = "Sun4c SparcStation IPC",	.id_machtype = (SM_SUN4C | SM_4C_IPC) },
+// { .name = "Sun4c SparcStation 1+",	.id_machtype = (SM_SUN4C | SM_4C_SS1PLUS) },
+// { .name = "Sun4c SparcStation SLC",	.id_machtype = (SM_SUN4C | SM_4C_SLC) },
+// { .name = "Sun4c SparcStation 2",	.id_machtype = (SM_SUN4C | SM_4C_SS2) },
+// { .name = "Sun4c SparcStation ELC",	.id_machtype = (SM_SUN4C | SM_4C_ELC) },
+// { .name = "Sun4c SparcStation IPX",	.id_machtype = (SM_SUN4C | SM_4C_IPX) },
+/* Finally, early Sun4m's */
+// { .name = "Sun4m SparcSystem600",	.id_machtype = (SM_SUN4M | SM_4M_SS60) },
+// { .name = "Sun4m SparcStation10/20",	.id_machtype = (SM_SUN4M | SM_4M_SS50) },
+// { .name = "Sun4m SparcStation5",	.id_machtype = (SM_SUN4M | SM_4M_SS40) },
+/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */
+// { .name = "Sun4M OBP based system",	.id_machtype = (SM_SUN4M_OBP | 0x0) }
+};
+
+static void __init display_system_type(unsigned char machtype)
+{
+	register int i;
+
+	for (i = 0; i < NUM_SUN_MACHINES; i++) {
+		if(Sun_Machines[i].id_machtype == machtype) {
+			if (machtype != (SM_SUN4M_OBP | 0x00))
+				printk("TYPE: %s\n", Sun_Machines[i].name);
+			else {
+#if 0
+				prom_getproperty(prom_root_node, "banner-name",
+						 sysname, sizeof(sysname));
+				printk("TYPE: %s\n", sysname);
+#endif
+			}
+			return;
+		}
+	}
+
+	prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype);
+	prom_halt();
+}
+
+void sun3_get_model(unsigned char* model)
+{
+	register int i;
+
+	for (i = 0; i < NUM_SUN_MACHINES; i++) {
+		if(Sun_Machines[i].id_machtype == idprom->id_machtype) {
+		        strcpy(model, Sun_Machines[i].name);
+			return;
+		}
+	}
+}
+
+
+
+/* Calculate the IDPROM checksum (xor of the data bytes). */
+static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
+{
+	unsigned char cksum, i, *ptr = (unsigned char *)idprom;
+
+	for (i = cksum = 0; i <= 0x0E; i++)
+		cksum ^= *ptr++;
+
+	return cksum;
+}
+
+/* Create a local IDPROM copy, verify integrity, and display information. */
+void __init idprom_init(void)
+{
+	prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer));
+
+	idprom = &idprom_buffer;
+
+	if (idprom->id_format != 0x01)  {
+		prom_printf("IDPROM: Unknown format type!\n");
+		prom_halt();
+	}
+
+	if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
+		prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n",
+			    idprom->id_cksum, calc_idprom_cksum(idprom));
+		prom_halt();
+	}
+
+	display_system_type(idprom->id_machtype);
+
+	printk("Ethernet address: %pM\n", idprom->id_ethaddr);
+}
diff --git a/arch/m68k/sun3/intersil.c b/arch/m68k/sun3/intersil.c
new file mode 100644
index 0000000..889829e
--- /dev/null
+++ b/arch/m68k/sun3/intersil.c
@@ -0,0 +1,74 @@
+/*
+ * arch/m68k/sun3/intersil.c
+ *
+ * basic routines for accessing the intersil clock within the sun3 machines
+ *
+ * started 11/12/1999 Sam Creasey
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+
+#include <asm/errno.h>
+#include <asm/rtc.h>
+#include <asm/intersil.h>
+
+
+/* bits to set for start/run of the intersil */
+#define STOP_VAL (INTERSIL_STOP | INTERSIL_INT_ENABLE | INTERSIL_24H_MODE)
+#define START_VAL (INTERSIL_RUN | INTERSIL_INT_ENABLE | INTERSIL_24H_MODE)
+
+/* does this need to be implemented? */
+u32 sun3_gettimeoffset(void)
+{
+  return 1000;
+}
+
+
+/* get/set hwclock */
+
+int sun3_hwclk(int set, struct rtc_time *t)
+{
+	volatile struct intersil_dt *todintersil;
+	unsigned long flags;
+
+        todintersil = (struct intersil_dt *) &intersil_clock->counter;
+
+	local_irq_save(flags);
+
+	intersil_clock->cmd_reg = STOP_VAL;
+
+	/* set or read the clock */
+	if(set) {
+		todintersil->csec = 0;
+		todintersil->hour = t->tm_hour;
+		todintersil->minute = t->tm_min;
+		todintersil->second = t->tm_sec;
+		todintersil->month = t->tm_mon;
+		todintersil->day = t->tm_mday;
+		todintersil->year = t->tm_year - 68;
+		todintersil->weekday = t->tm_wday;
+	} else {
+		/* read clock */
+		t->tm_sec = todintersil->csec;
+		t->tm_hour = todintersil->hour;
+		t->tm_min = todintersil->minute;
+		t->tm_sec = todintersil->second;
+		t->tm_mon = todintersil->month;
+		t->tm_mday = todintersil->day;
+		t->tm_year = todintersil->year + 68;
+		t->tm_wday = todintersil->weekday;
+	}
+
+	intersil_clock->cmd_reg = START_VAL;
+
+	local_irq_restore(flags);
+
+	return 0;
+
+}
+
diff --git a/arch/m68k/sun3/leds.c b/arch/m68k/sun3/leds.c
new file mode 100644
index 0000000..aad2e0a
--- /dev/null
+++ b/arch/m68k/sun3/leds.c
@@ -0,0 +1,13 @@
+#include <asm/contregs.h>
+#include <asm/sun3mmu.h>
+#include <asm/io.h>
+
+void sun3_leds(unsigned char byte)
+{
+	unsigned char dfc;
+
+	GET_DFC(dfc);
+	SET_DFC(FC_CONTROL);
+	SET_CONTROL_BYTE(AC_LEDS, byte);
+	SET_DFC(dfc);
+}
diff --git a/arch/m68k/sun3/mmu_emu.c b/arch/m68k/sun3/mmu_emu.c
new file mode 100644
index 0000000..3f258e2
--- /dev/null
+++ b/arch/m68k/sun3/mmu_emu.c
@@ -0,0 +1,426 @@
+/*
+** Tablewalk MMU emulator
+**
+** by Toshiyasu Morita
+**
+** Started 1/16/98 @ 2:22 am
+*/
+
+#include <linux/init.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/delay.h>
+#include <linux/bootmem.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+
+#include <asm/setup.h>
+#include <asm/traps.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/sun3mmu.h>
+#include <asm/segment.h>
+#include <asm/oplib.h>
+#include <asm/mmu_context.h>
+#include <asm/dvma.h>
+
+
+#undef DEBUG_MMU_EMU
+#define DEBUG_PROM_MAPS
+
+/*
+** Defines
+*/
+
+#define CONTEXTS_NUM		8
+#define SEGMAPS_PER_CONTEXT_NUM 2048
+#define PAGES_PER_SEGMENT	16
+#define PMEGS_NUM		256
+#define PMEG_MASK		0xFF
+
+/*
+** Globals
+*/
+
+unsigned long m68k_vmalloc_end;
+EXPORT_SYMBOL(m68k_vmalloc_end);
+
+unsigned long pmeg_vaddr[PMEGS_NUM];
+unsigned char pmeg_alloc[PMEGS_NUM];
+unsigned char pmeg_ctx[PMEGS_NUM];
+
+/* pointers to the mm structs for each task in each
+   context. 0xffffffff is a marker for kernel context */
+static struct mm_struct *ctx_alloc[CONTEXTS_NUM] = {
+    [0] = (struct mm_struct *)0xffffffff
+};
+
+/* has this context been mmdrop'd? */
+static unsigned char ctx_avail = CONTEXTS_NUM-1;
+
+/* array of pages to be marked off for the rom when we do mem_init later */
+/* 256 pages lets the rom take up to 2mb of physical ram..  I really
+   hope it never wants mote than that. */
+unsigned long rom_pages[256];
+
+/* Print a PTE value in symbolic form. For debugging. */
+void print_pte (pte_t pte)
+{
+#if 0
+	/* Verbose version. */
+	unsigned long val = pte_val (pte);
+	printk (" pte=%lx [addr=%lx",
+		val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
+	if (val & SUN3_PAGE_VALID)	printk (" valid");
+	if (val & SUN3_PAGE_WRITEABLE)	printk (" write");
+	if (val & SUN3_PAGE_SYSTEM)	printk (" sys");
+	if (val & SUN3_PAGE_NOCACHE)	printk (" nocache");
+	if (val & SUN3_PAGE_ACCESSED)	printk (" accessed");
+	if (val & SUN3_PAGE_MODIFIED)	printk (" modified");
+	switch (val & SUN3_PAGE_TYPE_MASK) {
+		case SUN3_PAGE_TYPE_MEMORY: printk (" memory"); break;
+		case SUN3_PAGE_TYPE_IO:     printk (" io");     break;
+		case SUN3_PAGE_TYPE_VME16:  printk (" vme16");  break;
+		case SUN3_PAGE_TYPE_VME32:  printk (" vme32");  break;
+	}
+	printk ("]\n");
+#else
+	/* Terse version. More likely to fit on a line. */
+	unsigned long val = pte_val (pte);
+	char flags[7], *type;
+
+	flags[0] = (val & SUN3_PAGE_VALID)     ? 'v' : '-';
+	flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-';
+	flags[2] = (val & SUN3_PAGE_SYSTEM)    ? 's' : '-';
+	flags[3] = (val & SUN3_PAGE_NOCACHE)   ? 'x' : '-';
+	flags[4] = (val & SUN3_PAGE_ACCESSED)  ? 'a' : '-';
+	flags[5] = (val & SUN3_PAGE_MODIFIED)  ? 'm' : '-';
+	flags[6] = '\0';
+
+	switch (val & SUN3_PAGE_TYPE_MASK) {
+		case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break;
+		case SUN3_PAGE_TYPE_IO:     type = "io"    ; break;
+		case SUN3_PAGE_TYPE_VME16:  type = "vme16" ; break;
+		case SUN3_PAGE_TYPE_VME32:  type = "vme32" ; break;
+		default: type = "unknown?"; break;
+	}
+
+	printk (" pte=%08lx [%07lx %s %s]\n",
+		val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type);
+#endif
+}
+
+/* Print the PTE value for a given virtual address. For debugging. */
+void print_pte_vaddr (unsigned long vaddr)
+{
+	printk (" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
+	print_pte (__pte (sun3_get_pte (vaddr)));
+}
+
+/*
+ * Initialise the MMU emulator.
+ */
+void __init mmu_emu_init(unsigned long bootmem_end)
+{
+	unsigned long seg, num;
+	int i,j;
+
+	memset(rom_pages, 0, sizeof(rom_pages));
+	memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr));
+	memset(pmeg_alloc, 0, sizeof(pmeg_alloc));
+	memset(pmeg_ctx, 0, sizeof(pmeg_ctx));
+
+	/* pmeg align the end of bootmem, adding another pmeg,
+	 * later bootmem allocations will likely need it */
+	bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK;
+
+	/* mark all of the pmegs used thus far as reserved */
+	for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i)
+		pmeg_alloc[i] = 2;
+
+
+	/* I'm thinking that most of the top pmeg's are going to be
+	   used for something, and we probably shouldn't risk it */
+	for(num = 0xf0; num <= 0xff; num++)
+		pmeg_alloc[num] = 2;
+
+	/* liberate all existing mappings in the rest of kernel space */
+	for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) {
+		i = sun3_get_segmap(seg);
+
+		if(!pmeg_alloc[i]) {
+#ifdef DEBUG_MMU_EMU
+			printk("freed: ");
+			print_pte_vaddr (seg);
+#endif
+			sun3_put_segmap(seg, SUN3_INVALID_PMEG);
+		}
+	}
+
+	j = 0;
+	for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) {
+		if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) {
+#ifdef DEBUG_PROM_MAPS
+			for(i = 0; i < 16; i++) {
+				printk ("mapped:");
+				print_pte_vaddr (seg + (i*PAGE_SIZE));
+				break;
+			}
+#endif
+			// the lowest mapping here is the end of our
+			// vmalloc region
+			if (!m68k_vmalloc_end)
+				m68k_vmalloc_end = seg;
+
+			// mark the segmap alloc'd, and reserve any
+			// of the first 0xbff pages the hardware is
+			// already using...  does any sun3 support > 24mb?
+			pmeg_alloc[sun3_get_segmap(seg)] = 2;
+		}
+	}
+
+	dvma_init();
+
+
+	/* blank everything below the kernel, and we've got the base
+	   mapping to start all the contexts off with... */
+	for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE)
+		sun3_put_segmap(seg, SUN3_INVALID_PMEG);
+
+	set_fs(MAKE_MM_SEG(3));
+	for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) {
+		i = sun3_get_segmap(seg);
+		for(j = 1; j < CONTEXTS_NUM; j++)
+			(*(romvec->pv_setctxt))(j, (void *)seg, i);
+	}
+	set_fs(KERNEL_DS);
+
+}
+
+/* erase the mappings for a dead context.  Uses the pg_dir for hints
+   as the pmeg tables proved somewhat unreliable, and unmapping all of
+   TASK_SIZE was much slower and no more stable. */
+/* todo: find a better way to keep track of the pmegs used by a
+   context for when they're cleared */
+void clear_context(unsigned long context)
+{
+     unsigned char oldctx;
+     unsigned long i;
+
+     if(context) {
+	     if(!ctx_alloc[context])
+		     panic("clear_context: context not allocated\n");
+
+	     ctx_alloc[context]->context = SUN3_INVALID_CONTEXT;
+	     ctx_alloc[context] = (struct mm_struct *)0;
+	     ctx_avail++;
+     }
+
+     oldctx = sun3_get_context();
+
+     sun3_put_context(context);
+
+     for(i = 0; i < SUN3_INVALID_PMEG; i++) {
+	     if((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) {
+		     sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG);
+		     pmeg_ctx[i] = 0;
+		     pmeg_alloc[i] = 0;
+		     pmeg_vaddr[i] = 0;
+	     }
+     }
+
+     sun3_put_context(oldctx);
+}
+
+/* gets an empty context.  if full, kills the next context listed to
+   die first */
+/* This context invalidation scheme is, well, totally arbitrary, I'm
+   sure it could be much more intelligent...  but it gets the job done
+   for now without much overhead in making it's decision. */
+/* todo: come up with optimized scheme for flushing contexts */
+unsigned long get_free_context(struct mm_struct *mm)
+{
+	unsigned long new = 1;
+	static unsigned char next_to_die = 1;
+
+	if(!ctx_avail) {
+		/* kill someone to get our context */
+		new = next_to_die;
+		clear_context(new);
+		next_to_die = (next_to_die + 1) & 0x7;
+		if(!next_to_die)
+			next_to_die++;
+	} else {
+		while(new < CONTEXTS_NUM) {
+			if(ctx_alloc[new])
+				new++;
+			else
+				break;
+		}
+		// check to make sure one was really free...
+		if(new == CONTEXTS_NUM)
+			panic("get_free_context: failed to find free context");
+	}
+
+	ctx_alloc[new] = mm;
+	ctx_avail--;
+
+	return new;
+}
+
+/*
+ * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in
+ * `context'. Maintain internal PMEG management structures. This doesn't
+ * actually map the physical address, but does clear the old mappings.
+ */
+//todo: better allocation scheme? but is extra complexity worthwhile?
+//todo: only clear old entries if necessary? how to tell?
+
+inline void mmu_emu_map_pmeg (int context, int vaddr)
+{
+	static unsigned char curr_pmeg = 128;
+	int i;
+
+	/* Round address to PMEG boundary. */
+	vaddr &= ~SUN3_PMEG_MASK;
+
+	/* Find a spare one. */
+	while (pmeg_alloc[curr_pmeg] == 2)
+		++curr_pmeg;
+
+
+#ifdef DEBUG_MMU_EMU
+printk("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
+       curr_pmeg, context, vaddr);
+#endif
+
+	/* Invalidate old mapping for the pmeg, if any */
+	if (pmeg_alloc[curr_pmeg] == 1) {
+		sun3_put_context(pmeg_ctx[curr_pmeg]);
+		sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG);
+		sun3_put_context(context);
+	}
+
+	/* Update PMEG management structures. */
+	// don't take pmeg's away from the kernel...
+	if(vaddr >= PAGE_OFFSET) {
+		/* map kernel pmegs into all contexts */
+		unsigned char i;
+
+		for(i = 0; i < CONTEXTS_NUM; i++) {
+			sun3_put_context(i);
+			sun3_put_segmap (vaddr, curr_pmeg);
+		}
+		sun3_put_context(context);
+		pmeg_alloc[curr_pmeg] = 2;
+		pmeg_ctx[curr_pmeg] = 0;
+
+	}
+	else {
+		pmeg_alloc[curr_pmeg] = 1;
+		pmeg_ctx[curr_pmeg] = context;
+		sun3_put_segmap (vaddr, curr_pmeg);
+
+	}
+	pmeg_vaddr[curr_pmeg] = vaddr;
+
+	/* Set hardware mapping and clear the old PTE entries. */
+	for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE)
+		sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM);
+
+	/* Consider a different one next time. */
+	++curr_pmeg;
+}
+
+/*
+ * Handle a pagefault at virtual address `vaddr'; check if there should be a
+ * page there (specifically, whether the software pagetables indicate that
+ * there is). This is necessary due to the limited size of the second-level
+ * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a
+ * mapping present, we select a `spare' PMEG and use it to create a mapping.
+ * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero
+ * if we successfully handled the fault.
+ */
+//todo: should we bump minor pagefault counter? if so, here or in caller?
+//todo: possibly inline this into bus_error030 in <asm/buserror.h> ?
+
+// kernel_fault is set when a kernel page couldn't be demand mapped,
+// and forces another try using the kernel page table.  basically a
+// hack so that vmalloc would work correctly.
+
+int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault)
+{
+	unsigned long segment, offset;
+	unsigned char context;
+	pte_t *pte;
+	pgd_t * crp;
+
+	if(current->mm == NULL) {
+		crp = swapper_pg_dir;
+		context = 0;
+	} else {
+		context = current->mm->context;
+		if(kernel_fault)
+			crp = swapper_pg_dir;
+		else
+			crp = current->mm->pgd;
+	}
+
+#ifdef DEBUG_MMU_EMU
+	printk ("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
+		vaddr, read_flag ? "read" : "write", crp);
+#endif
+
+	segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF;
+	offset  = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF;
+
+#ifdef DEBUG_MMU_EMU
+	printk ("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment, offset);
+#endif
+
+	pte = (pte_t *) pgd_val (*(crp + segment));
+
+//todo: next line should check for valid pmd properly.
+	if (!pte) {
+//                printk ("mmu_emu_handle_fault: invalid pmd\n");
+                return 0;
+        }
+
+	pte = (pte_t *) __va ((unsigned long)(pte + offset));
+
+	/* Make sure this is a valid page */
+	if (!(pte_val (*pte) & SUN3_PAGE_VALID))
+		return 0;
+
+	/* Make sure there's a pmeg allocated for the page */
+	if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
+		mmu_emu_map_pmeg (context, vaddr);
+
+	/* Write the pte value to hardware MMU */
+	sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte));
+
+	/* Update software copy of the pte value */
+// I'm not sure this is necessary. If this is required, we ought to simply
+// copy this out when we reuse the PMEG or at some other convenient time.
+// Doing it here is fairly meaningless, anyway, as we only know about the
+// first access to a given page. --m
+	if (!read_flag) {
+		if (pte_val (*pte) & SUN3_PAGE_WRITEABLE)
+			pte_val (*pte) |= (SUN3_PAGE_ACCESSED
+					   | SUN3_PAGE_MODIFIED);
+		else
+			return 0;	/* Write-protect error. */
+	} else
+		pte_val (*pte) |= SUN3_PAGE_ACCESSED;
+
+#ifdef DEBUG_MMU_EMU
+	printk ("seg:%d crp:%p ->", get_fs().seg, crp);
+	print_pte_vaddr (vaddr);
+	printk ("\n");
+#endif
+
+	return 1;
+}
diff --git a/arch/m68k/sun3/prom/Makefile b/arch/m68k/sun3/prom/Makefile
new file mode 100644
index 0000000..da7eac0
--- /dev/null
+++ b/arch/m68k/sun3/prom/Makefile
@@ -0,0 +1,6 @@
+# Makefile for the Sun Boot PROM interface library under
+# Linux.
+#
+
+obj-y := init.o console.o printf.o  misc.o
+#bootstr.o init.o misc.o segment.o console.o printf.o
diff --git a/arch/m68k/sun3/prom/console.c b/arch/m68k/sun3/prom/console.c
new file mode 100644
index 0000000..e923643
--- /dev/null
+++ b/arch/m68k/sun3/prom/console.c
@@ -0,0 +1,169 @@
+/*
+ * console.c: Routines that deal with sending and receiving IO
+ *            to/from the current console device using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <linux/string.h>
+
+/* Non blocking get character from console input device, returns -1
+ * if no input was taken.  This can be used for polling.
+ */
+int
+prom_nbgetchar(void)
+{
+	int i = -1;
+	unsigned long flags;
+
+	local_irq_save(flags);
+		i = (*(romvec->pv_nbgetchar))();
+	local_irq_restore(flags);
+	return i; /* Ugh, we could spin forever on unsupported proms ;( */
+}
+
+/* Non blocking put character to console device, returns -1 if
+ * unsuccessful.
+ */
+int
+prom_nbputchar(char c)
+{
+	unsigned long flags;
+	int i = -1;
+
+	local_irq_save(flags);
+		i = (*(romvec->pv_nbputchar))(c);
+	local_irq_restore(flags);
+	return i; /* Ugh, we could spin forever on unsupported proms ;( */
+}
+
+/* Blocking version of get character routine above. */
+char
+prom_getchar(void)
+{
+	int character;
+	while((character = prom_nbgetchar()) == -1) ;
+	return (char) character;
+}
+
+/* Blocking version of put character routine above. */
+void
+prom_putchar(char c)
+{
+	while(prom_nbputchar(c) == -1) ;
+	return;
+}
+
+/* Query for input device type */
+#if 0
+enum prom_input_device
+prom_query_input_device()
+{
+	unsigned long flags;
+	int st_p;
+	char propb[64];
+	char *p;
+
+	switch(prom_vers) {
+	case PROM_V0:
+	case PROM_V2:
+	default:
+		switch(*romvec->pv_stdin) {
+		case PROMDEV_KBD:	return PROMDEV_IKBD;
+		case PROMDEV_TTYA:	return PROMDEV_ITTYA;
+		case PROMDEV_TTYB:	return PROMDEV_ITTYB;
+		default:
+			return PROMDEV_I_UNK;
+		};
+	case PROM_V3:
+	case PROM_P1275:
+		local_irq_save(flags);
+		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
+		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+				     "r" (&current_set[smp_processor_id()]) :
+				     "memory");
+		local_irq_restore(flags);
+		if(prom_node_has_property(st_p, "keyboard"))
+			return PROMDEV_IKBD;
+		prom_getproperty(st_p, "device_type", propb, sizeof(propb));
+		if(strncmp(propb, "serial", sizeof("serial")))
+			return PROMDEV_I_UNK;
+		prom_getproperty(prom_root_node, "stdin-path", propb, sizeof(propb));
+		p = propb;
+		while(*p) p++; p -= 2;
+		if(p[0] == ':') {
+			if(p[1] == 'a')
+				return PROMDEV_ITTYA;
+			else if(p[1] == 'b')
+				return PROMDEV_ITTYB;
+		}
+		return PROMDEV_I_UNK;
+	};
+}
+#endif
+
+/* Query for output device type */
+
+#if 0
+enum prom_output_device
+prom_query_output_device()
+{
+	unsigned long flags;
+	int st_p;
+	char propb[64];
+	char *p;
+	int propl;
+
+	switch(prom_vers) {
+	case PROM_V0:
+		switch(*romvec->pv_stdin) {
+		case PROMDEV_SCREEN:	return PROMDEV_OSCREEN;
+		case PROMDEV_TTYA:	return PROMDEV_OTTYA;
+		case PROMDEV_TTYB:	return PROMDEV_OTTYB;
+		};
+		break;
+	case PROM_V2:
+	case PROM_V3:
+	case PROM_P1275:
+		local_irq_save(flags);
+		st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
+		__asm__ __volatile__("ld [%0], %%g6\n\t" : :
+				     "r" (&current_set[smp_processor_id()]) :
+				     "memory");
+		local_irq_restore(flags);
+		propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
+		if (propl >= 0 && propl == sizeof("display") &&
+			strncmp("display", propb, sizeof("display")) == 0)
+		{
+			return PROMDEV_OSCREEN;
+		}
+		if(prom_vers == PROM_V3) {
+			if(strncmp("serial", propb, sizeof("serial")))
+				return PROMDEV_O_UNK;
+			prom_getproperty(prom_root_node, "stdout-path", propb, sizeof(propb));
+			p = propb;
+			while(*p) p++; p -= 2;
+			if(p[0]==':') {
+				if(p[1] == 'a')
+					return PROMDEV_OTTYA;
+				else if(p[1] == 'b')
+					return PROMDEV_OTTYB;
+			}
+			return PROMDEV_O_UNK;
+		} else {
+			/* This works on SS-2 (an early OpenFirmware) still. */
+			switch(*romvec->pv_stdin) {
+			case PROMDEV_TTYA:	return PROMDEV_OTTYA;
+			case PROMDEV_TTYB:	return PROMDEV_OTTYB;
+			};
+		}
+		break;
+	};
+	return PROMDEV_O_UNK;
+}
+#endif
diff --git a/arch/m68k/sun3/prom/init.c b/arch/m68k/sun3/prom/init.c
new file mode 100644
index 0000000..eeba067
--- /dev/null
+++ b/arch/m68k/sun3/prom/init.c
@@ -0,0 +1,35 @@
+/*
+ * init.c:  Initialize internal variables used by the PROM
+ *          library functions.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+struct linux_romvec *romvec;
+enum prom_major_version prom_vers;
+unsigned int prom_rev, prom_prev;
+
+/* The root node of the prom device tree. */
+int prom_root_node;
+
+/* Pointer to the device tree operations structure. */
+struct linux_nodeops *prom_nodeops;
+
+/* You must call prom_init() before you attempt to use any of the
+ * routines in the prom library.
+ * It gets passed the pointer to the PROM vector.
+ */
+
+void __init prom_init(struct linux_romvec *rp)
+{
+	romvec = rp;
+
+	/* Initialization successful. */
+	return;
+}
diff --git a/arch/m68k/sun3/prom/misc.c b/arch/m68k/sun3/prom/misc.c
new file mode 100644
index 0000000..3d60e13
--- /dev/null
+++ b/arch/m68k/sun3/prom/misc.c
@@ -0,0 +1,94 @@
+/*
+ * misc.c:  Miscellaneous prom functions that don't belong
+ *          anywhere else.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/sun3-head.h>
+#include <asm/idprom.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/movs.h>
+
+/* Reset and reboot the machine with the command 'bcommand'. */
+void
+prom_reboot(char *bcommand)
+{
+	unsigned long flags;
+	local_irq_save(flags);
+	(*(romvec->pv_reboot))(bcommand);
+	local_irq_restore(flags);
+}
+
+/* Drop into the prom, with the chance to continue with the 'go'
+ * prom command.
+ */
+void
+prom_cmdline(void)
+{
+}
+
+/* Drop into the prom, but completely terminate the program.
+ * No chance of continuing.
+ */
+void
+prom_halt(void)
+{
+	unsigned long flags;
+again:
+	local_irq_save(flags);
+	(*(romvec->pv_halt))();
+	local_irq_restore(flags);
+	goto again; /* PROM is out to get me -DaveM */
+}
+
+typedef void (*sfunc_t)(void);
+
+/* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
+ * format type.  'num_bytes' is the number of bytes that your idbuf
+ * has space for.  Returns 0xff on error.
+ */
+unsigned char
+prom_get_idprom(char *idbuf, int num_bytes)
+{
+	int i, oldsfc;
+	GET_SFC(oldsfc);
+	SET_SFC(FC_CONTROL);
+	for(i=0;i<num_bytes; i++)
+	{
+		/* There is a problem with the GET_CONTROL_BYTE
+		macro; defining the extra variable
+		gets around it.
+		*/
+		int c;
+		GET_CONTROL_BYTE(SUN3_IDPROM_BASE + i, c);
+		idbuf[i] = c;
+	}
+	SET_SFC(oldsfc);
+	return idbuf[0];
+}
+
+/* Get the major prom version number. */
+int
+prom_version(void)
+{
+	return romvec->pv_romvers;
+}
+
+/* Get the prom plugin-revision. */
+int
+prom_getrev(void)
+{
+	return prom_rev;
+}
+
+/* Get the prom firmware print revision. */
+int
+prom_getprev(void)
+{
+	return prom_prev;
+}
diff --git a/arch/m68k/sun3/prom/printf.c b/arch/m68k/sun3/prom/printf.c
new file mode 100644
index 0000000..df85018
--- /dev/null
+++ b/arch/m68k/sun3/prom/printf.c
@@ -0,0 +1,55 @@
+/*
+ * printf.c:  Internal prom library printf facility.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* This routine is internal to the prom library, no one else should know
+ * about or use it!  It's simple and smelly anyway....
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+#ifdef CONFIG_KGDB
+extern int kgdb_initialized;
+#endif
+
+static char ppbuf[1024];
+
+void
+prom_printf(char *fmt, ...)
+{
+	va_list args;
+	char ch, *bptr;
+	int i;
+
+	va_start(args, fmt);
+
+#ifdef CONFIG_KGDB
+	ppbuf[0] = 'O';
+	i = vsprintf(ppbuf + 1, fmt, args) + 1;
+#else
+	i = vsprintf(ppbuf, fmt, args);
+#endif
+
+	bptr = ppbuf;
+
+#ifdef CONFIG_KGDB
+	if (kgdb_initialized) {
+		printk("kgdb_initialized = %d\n", kgdb_initialized);
+		putpacket(bptr, 1);
+	} else
+#else
+	while((ch = *(bptr++)) != 0) {
+		if(ch == '\n')
+			prom_putchar('\r');
+
+		prom_putchar(ch);
+	}
+#endif
+	va_end(args);
+	return;
+}
diff --git a/arch/m68k/sun3/sun3dvma.c b/arch/m68k/sun3/sun3dvma.c
new file mode 100644
index 0000000..b37521a
--- /dev/null
+++ b/arch/m68k/sun3/sun3dvma.c
@@ -0,0 +1,383 @@
+/*
+ * linux/arch/m68k/sun3/sun3dvma.c
+ *
+ * Copyright (C) 2000 Sam Creasey
+ *
+ * Contains common routines for sun3/sun3x DVMA management.
+ */
+
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/dvma.h>
+
+#undef DVMA_DEBUG
+
+#ifdef CONFIG_SUN3X
+extern void dvma_unmap_iommu(unsigned long baddr, int len);
+#else
+static inline void dvma_unmap_iommu(unsigned long a, int b)
+{
+}
+#endif
+
+#ifdef CONFIG_SUN3
+extern void sun3_dvma_init(void);
+#endif
+
+static unsigned long *iommu_use;
+
+#define dvma_index(baddr) ((baddr - DVMA_START) >> DVMA_PAGE_SHIFT)
+
+#define dvma_entry_use(baddr)		(iommu_use[dvma_index(baddr)])
+
+struct hole {
+	unsigned long start;
+	unsigned long end;
+	unsigned long size;
+	struct list_head list;
+};
+
+static struct list_head hole_list;
+static struct list_head hole_cache;
+static struct hole initholes[64];
+
+#ifdef DVMA_DEBUG
+
+static unsigned long dvma_allocs;
+static unsigned long dvma_frees;
+static unsigned long long dvma_alloc_bytes;
+static unsigned long long dvma_free_bytes;
+
+static void print_use(void)
+{
+
+	int i;
+	int j = 0;
+
+	printk("dvma entry usage:\n");
+
+	for(i = 0; i < IOMMU_TOTAL_ENTRIES; i++) {
+		if(!iommu_use[i])
+			continue;
+
+		j++;
+
+		printk("dvma entry: %08lx len %08lx\n",
+		       ( i << DVMA_PAGE_SHIFT) + DVMA_START,
+		       iommu_use[i]);
+	}
+
+	printk("%d entries in use total\n", j);
+
+	printk("allocation/free calls: %lu/%lu\n", dvma_allocs, dvma_frees);
+	printk("allocation/free bytes: %Lx/%Lx\n", dvma_alloc_bytes,
+	       dvma_free_bytes);
+}
+
+static void print_holes(struct list_head *holes)
+{
+
+	struct list_head *cur;
+	struct hole *hole;
+
+	printk("listing dvma holes\n");
+	list_for_each(cur, holes) {
+		hole = list_entry(cur, struct hole, list);
+
+		if((hole->start == 0) && (hole->end == 0) && (hole->size == 0))
+			continue;
+
+		printk("hole: start %08lx end %08lx size %08lx\n", hole->start, hole->end, hole->size);
+	}
+
+	printk("end of hole listing...\n");
+
+}
+#endif /* DVMA_DEBUG */
+
+static inline int refill(void)
+{
+
+	struct hole *hole;
+	struct hole *prev = NULL;
+	struct list_head *cur;
+	int ret = 0;
+
+	list_for_each(cur, &hole_list) {
+		hole = list_entry(cur, struct hole, list);
+
+		if(!prev) {
+			prev = hole;
+			continue;
+		}
+
+		if(hole->end == prev->start) {
+			hole->size += prev->size;
+			hole->end = prev->end;
+			list_move(&(prev->list), &hole_cache);
+			ret++;
+		}
+
+	}
+
+	return ret;
+}
+
+static inline struct hole *rmcache(void)
+{
+	struct hole *ret;
+
+	if(list_empty(&hole_cache)) {
+		if(!refill()) {
+			printk("out of dvma hole cache!\n");
+			BUG();
+		}
+	}
+
+	ret = list_entry(hole_cache.next, struct hole, list);
+	list_del(&(ret->list));
+
+	return ret;
+
+}
+
+static inline unsigned long get_baddr(int len, unsigned long align)
+{
+
+	struct list_head *cur;
+	struct hole *hole;
+
+	if(list_empty(&hole_list)) {
+#ifdef DVMA_DEBUG
+		printk("out of dvma holes! (printing hole cache)\n");
+		print_holes(&hole_cache);
+		print_use();
+#endif
+		BUG();
+	}
+
+	list_for_each(cur, &hole_list) {
+		unsigned long newlen;
+
+		hole = list_entry(cur, struct hole, list);
+
+		if(align > DVMA_PAGE_SIZE)
+			newlen = len + ((hole->end - len) & (align-1));
+		else
+			newlen = len;
+
+		if(hole->size > newlen) {
+			hole->end -= newlen;
+			hole->size -= newlen;
+			dvma_entry_use(hole->end) = newlen;
+#ifdef DVMA_DEBUG
+			dvma_allocs++;
+			dvma_alloc_bytes += newlen;
+#endif
+			return hole->end;
+		} else if(hole->size == newlen) {
+			list_move(&(hole->list), &hole_cache);
+			dvma_entry_use(hole->start) = newlen;
+#ifdef DVMA_DEBUG
+			dvma_allocs++;
+			dvma_alloc_bytes += newlen;
+#endif
+			return hole->start;
+		}
+
+	}
+
+	printk("unable to find dvma hole!\n");
+	BUG();
+	return 0;
+}
+
+static inline int free_baddr(unsigned long baddr)
+{
+
+	unsigned long len;
+	struct hole *hole;
+	struct list_head *cur;
+	unsigned long orig_baddr;
+
+	orig_baddr = baddr;
+	len = dvma_entry_use(baddr);
+	dvma_entry_use(baddr) = 0;
+	baddr &= DVMA_PAGE_MASK;
+	dvma_unmap_iommu(baddr, len);
+
+#ifdef DVMA_DEBUG
+	dvma_frees++;
+	dvma_free_bytes += len;
+#endif
+
+	list_for_each(cur, &hole_list) {
+		hole = list_entry(cur, struct hole, list);
+
+		if(hole->end == baddr) {
+			hole->end += len;
+			hole->size += len;
+			return 0;
+		} else if(hole->start == (baddr + len)) {
+			hole->start = baddr;
+			hole->size += len;
+			return 0;
+		}
+
+	}
+
+	hole = rmcache();
+
+	hole->start = baddr;
+	hole->end = baddr + len;
+	hole->size = len;
+
+//	list_add_tail(&(hole->list), cur);
+	list_add(&(hole->list), cur);
+
+	return 0;
+
+}
+
+void __init dvma_init(void)
+{
+
+	struct hole *hole;
+	int i;
+
+	INIT_LIST_HEAD(&hole_list);
+	INIT_LIST_HEAD(&hole_cache);
+
+	/* prepare the hole cache */
+	for(i = 0; i < 64; i++)
+		list_add(&(initholes[i].list), &hole_cache);
+
+	hole = rmcache();
+	hole->start = DVMA_START;
+	hole->end = DVMA_END;
+	hole->size = DVMA_SIZE;
+
+	list_add(&(hole->list), &hole_list);
+
+	iommu_use = alloc_bootmem(IOMMU_TOTAL_ENTRIES * sizeof(unsigned long));
+
+	dvma_unmap_iommu(DVMA_START, DVMA_SIZE);
+
+#ifdef CONFIG_SUN3
+	sun3_dvma_init();
+#endif
+
+}
+
+unsigned long dvma_map_align(unsigned long kaddr, int len, int align)
+{
+
+	unsigned long baddr;
+	unsigned long off;
+
+	if(!len)
+		len = 0x800;
+
+	if(!kaddr || !len) {
+//		printk("error: kaddr %lx len %x\n", kaddr, len);
+//		*(int *)4 = 0;
+		return 0;
+	}
+
+#ifdef DEBUG
+	printk("dvma_map request %08lx bytes from %08lx\n",
+	       len, kaddr);
+#endif
+	off = kaddr & ~DVMA_PAGE_MASK;
+	kaddr &= PAGE_MASK;
+	len += off;
+	len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
+
+	if(align == 0)
+		align = DVMA_PAGE_SIZE;
+	else
+		align = ((align + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
+
+	baddr = get_baddr(len, align);
+//	printk("using baddr %lx\n", baddr);
+
+	if(!dvma_map_iommu(kaddr, baddr, len))
+		return (baddr + off);
+
+	printk("dvma_map failed kaddr %lx baddr %lx len %x\n", kaddr, baddr, len);
+	BUG();
+	return 0;
+}
+EXPORT_SYMBOL(dvma_map_align);
+
+void dvma_unmap(void *baddr)
+{
+	unsigned long addr;
+
+	addr = (unsigned long)baddr;
+	/* check if this is a vme mapping */
+	if(!(addr & 0x00f00000))
+		addr |= 0xf00000;
+
+	free_baddr(addr);
+
+	return;
+
+}
+EXPORT_SYMBOL(dvma_unmap);
+
+void *dvma_malloc_align(unsigned long len, unsigned long align)
+{
+	unsigned long kaddr;
+	unsigned long baddr;
+	unsigned long vaddr;
+
+	if(!len)
+		return NULL;
+
+#ifdef DEBUG
+	printk("dvma_malloc request %lx bytes\n", len);
+#endif
+	len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
+
+        if((kaddr = __get_free_pages(GFP_ATOMIC, get_order(len))) == 0)
+		return NULL;
+
+	if((baddr = (unsigned long)dvma_map_align(kaddr, len, align)) == 0) {
+		free_pages(kaddr, get_order(len));
+		return NULL;
+	}
+
+	vaddr = dvma_btov(baddr);
+
+	if(dvma_map_cpu(kaddr, vaddr, len) < 0) {
+		dvma_unmap((void *)baddr);
+		free_pages(kaddr, get_order(len));
+		return NULL;
+	}
+
+#ifdef DEBUG
+	printk("mapped %08lx bytes %08lx kern -> %08lx bus\n",
+	       len, kaddr, baddr);
+#endif
+
+	return (void *)vaddr;
+
+}
+EXPORT_SYMBOL(dvma_malloc_align);
+
+void dvma_free(void *vaddr)
+{
+
+	return;
+
+}
+EXPORT_SYMBOL(dvma_free);
diff --git a/arch/m68k/sun3/sun3ints.c b/arch/m68k/sun3/sun3ints.c
new file mode 100644
index 0000000..6bbca30
--- /dev/null
+++ b/arch/m68k/sun3/sun3ints.c
@@ -0,0 +1,99 @@
+ /*
+ * linux/arch/m68k/sun3/sun3ints.c -- Sun-3(x) Linux interrupt handling code
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/interrupt.h>
+#include <asm/segment.h>
+#include <asm/intersil.h>
+#include <asm/oplib.h>
+#include <asm/sun3ints.h>
+#include <asm/irq_regs.h>
+#include <linux/seq_file.h>
+
+extern void sun3_leds (unsigned char);
+
+void sun3_disable_interrupts(void)
+{
+	sun3_disable_irq(0);
+}
+
+void sun3_enable_interrupts(void)
+{
+	sun3_enable_irq(0);
+}
+
+static int led_pattern[8] = {
+       ~(0x80), ~(0x01),
+       ~(0x40), ~(0x02),
+       ~(0x20), ~(0x04),
+       ~(0x10), ~(0x08)
+};
+
+volatile unsigned char* sun3_intreg;
+
+void sun3_enable_irq(unsigned int irq)
+{
+	*sun3_intreg |=  (1 << irq);
+}
+
+void sun3_disable_irq(unsigned int irq)
+{
+	*sun3_intreg &= ~(1 << irq);
+}
+
+static irqreturn_t sun3_int7(int irq, void *dev_id)
+{
+	unsigned int cnt;
+
+	cnt = kstat_irqs_cpu(irq, 0);
+	if (!(cnt % 2000))
+		sun3_leds(led_pattern[cnt % 16000 / 2000]);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sun3_int5(int irq, void *dev_id)
+{
+	unsigned int cnt;
+
+#ifdef CONFIG_SUN3
+	intersil_clear();
+#endif
+	sun3_disable_irq(5);
+	sun3_enable_irq(5);
+#ifdef CONFIG_SUN3
+	intersil_clear();
+#endif
+	xtime_update(1);
+	update_process_times(user_mode(get_irq_regs()));
+	cnt = kstat_irqs_cpu(irq, 0);
+	if (!(cnt % 20))
+		sun3_leds(led_pattern[cnt % 160 / 20]);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sun3_vec255(int irq, void *dev_id)
+{
+	return IRQ_HANDLED;
+}
+
+void __init sun3_init_IRQ(void)
+{
+	*sun3_intreg = 1;
+
+	m68k_setup_user_interrupt(VEC_USER, 128);
+
+	if (request_irq(IRQ_AUTO_5, sun3_int5, 0, "clock", NULL))
+		pr_err("Couldn't register %s interrupt\n", "int5");
+	if (request_irq(IRQ_AUTO_7, sun3_int7, 0, "nmi", NULL))
+		pr_err("Couldn't register %s interrupt\n", "int7");
+	if (request_irq(IRQ_USER+127, sun3_vec255, 0, "vec255", NULL))
+		pr_err("Couldn't register %s interrupt\n", "vec255");
+}