aboutsummaryrefslogtreecommitdiff
path: root/arch/m68k/sun3
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68k/sun3
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/m68k/sun3')
-rw-r--r--arch/m68k/sun3/Makefile7
-rw-r--r--arch/m68k/sun3/config.c188
-rw-r--r--arch/m68k/sun3/dvma.c71
-rw-r--r--arch/m68k/sun3/idprom.c129
-rw-r--r--arch/m68k/sun3/intersil.c76
-rw-r--r--arch/m68k/sun3/leds.c13
-rw-r--r--arch/m68k/sun3/mmu_emu.c427
-rw-r--r--arch/m68k/sun3/prom/Makefile7
-rw-r--r--arch/m68k/sun3/prom/console.c174
-rw-r--r--arch/m68k/sun3/prom/init.c89
-rw-r--r--arch/m68k/sun3/prom/misc.c94
-rw-r--r--arch/m68k/sun3/prom/printf.c61
-rw-r--r--arch/m68k/sun3/sbus.c27
-rw-r--r--arch/m68k/sun3/sun3_ksyms.c13
-rw-r--r--arch/m68k/sun3/sun3dvma.c379
-rw-r--r--arch/m68k/sun3/sun3ints.c265
16 files changed, 2020 insertions, 0 deletions
diff --git a/arch/m68k/sun3/Makefile b/arch/m68k/sun3/Makefile
new file mode 100644
index 00000000000..4d4f0695d98
--- /dev/null
+++ b/arch/m68k/sun3/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Linux arch/m68k/sun3 source directory
+#
+
+obj-y := sun3_ksyms.o sun3ints.o sun3dvma.o sbus.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 00000000000..77d05bcc322
--- /dev/null
+++ b/arch/m68k/sun3/config.c
@@ -0,0 +1,188 @@
+/*
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/oplib.h>
+#include <asm/setup.h>
+#include <asm/contregs.h>
+#include <asm/movs.h>
+#include <asm/pgtable.h>
+#include <asm/sun3-head.h>
+#include <asm/sun3mmu.h>
+#include <asm/rtc.h>
+#include <asm/machdep.h>
+#include <asm/intersil.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/sun3ints.h>
+
+extern char _text, _end;
+
+char sun3_reserved_pmeg[SUN3_PMEGS_NUM];
+
+extern unsigned long sun3_gettimeoffset(void);
+extern int show_sun3_interrupts (struct seq_file *, void *);
+extern void sun3_sched_init(irqreturn_t (*handler)(int, void *, struct pt_regs *));
+extern void sun3_get_model (char* model);
+extern void idprom_init (void);
+extern int sun3_hwclk(int set, struct rtc_time *t);
+
+volatile char* clock_va;
+extern volatile unsigned char* sun3_intreg;
+extern unsigned long availmem;
+unsigned long num_pages;
+
+static int sun3_get_hardware_list(char *buffer)
+{
+
+ int len = 0;
+
+ len += sprintf(buffer + len, "PROM Revision:\t%s\n",
+ romvec->pv_monid);
+
+ return len;
+
+}
+
+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 */
+
+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;
+ num_pages = __pa(memory_end) >> PAGE_SHIFT;
+
+ high_memory = (void *)memory_end;
+ availmem = memory_start;
+
+ availmem += init_bootmem_node(NODE_DATA(0), start_page, 0, 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_default_handler = &sun3_default_handler;
+ mach_request_irq = sun3_request_irq;
+ mach_free_irq = sun3_free_irq;
+ enable_irq = sun3_enable_irq;
+ disable_irq = sun3_disable_irq;
+ mach_process_int = sun3_process_int;
+ mach_get_irq_list = show_sun3_interrupts;
+ mach_reset = sun3_reboot;
+ mach_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;
+#if defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+#endif
+
+ memory_start = ((((int)&_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);
+}
+
+void __init sun3_sched_init(irqreturn_t (*timer_routine)(int, void *, struct pt_regs *))
+{
+ 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();
+}
+
diff --git a/arch/m68k/sun3/dvma.c b/arch/m68k/sun3/dvma.c
new file mode 100644
index 00000000000..d2b3093f240
--- /dev/null
+++ b/arch/m68k/sun3/dvma.c
@@ -0,0 +1,71 @@
+/*
+ * linux/arch/m68k/sun3/dvma.c
+ *
+ * Written by Sam Creasey
+ *
+ * Sun3 IOMMU routines used for dvma accesses.
+ *
+ */
+
+#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];
+
+inline 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 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 00000000000..02c1fee6fe7
--- /dev/null
+++ b/arch/m68k/sun3/idprom.c
@@ -0,0 +1,129 @@
+/* $Id: idprom.c,v 1.22 1996/11/13 05:09:25 davem Exp $
+ * 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/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;
+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.
+ */
+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: %x:%x:%x:%x:%x:%x\n",
+ idprom->id_ethaddr[0], idprom->id_ethaddr[1],
+ idprom->id_ethaddr[2], idprom->id_ethaddr[3],
+ idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
+}
diff --git a/arch/m68k/sun3/intersil.c b/arch/m68k/sun3/intersil.c
new file mode 100644
index 00000000000..db359d7402a
--- /dev/null
+++ b/arch/m68k/sun3/intersil.c
@@ -0,0 +1,76 @@
+/*
+ * 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/system.h>
+#include <asm/semaphore.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? */
+unsigned long sun3_gettimeoffset(void)
+{
+ return 1;
+}
+
+
+/* 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 00000000000..a3e94846398
--- /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 00000000000..7a0e3a22068
--- /dev/null
+++ b/arch/m68k/sun3/mmu_emu.c
@@ -0,0 +1,427 @@
+/*
+** Tablewalk MMU emulator
+**
+** by Toshiyasu Morita
+**
+** Started 1/16/98 @ 2:22 am
+*/
+
+#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/system.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>
+
+extern void prom_reboot (char *) __attribute__ ((__noreturn__));
+
+#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 vmalloc_end;
+EXPORT_SYMBOL(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 */
+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 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(!vmalloc_end)
+ 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 intellegent... 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 00000000000..6e48ae2a717
--- /dev/null
+++ b/arch/m68k/sun3/prom/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $
+# 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 00000000000..52c1427863d
--- /dev/null
+++ b/arch/m68k/sun3/prom/console.c
@@ -0,0 +1,174 @@
+/* $Id: console.c,v 1.10 1996/12/18 06:46:54 tridge Exp $
+ * 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 <asm/system.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);