aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68k/sun3
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
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 000000000000..4d4f0695d985
--- /dev/null
+++ b/arch/m68k/sun3/Makefile
@@ -0,0 +1,7 @@
1#
2# Makefile for Linux arch/m68k/sun3 source directory
3#
4
5obj-y := sun3_ksyms.o sun3ints.o sun3dvma.o sbus.o idprom.o
6
7obj-$(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 000000000000..77d05bcc3221
--- /dev/null
+++ b/arch/m68k/sun3/config.c
@@ -0,0 +1,188 @@
1/*
2 * linux/arch/m68k/sun3/config.c
3 *
4 * Copyright (C) 1996,1997 Pekka Pietik{inen
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 */
10
11#include <linux/config.h>
12#include <linux/types.h>
13#include <linux/kernel.h>
14#include <linux/mm.h>
15#include <linux/tty.h>
16#include <linux/console.h>
17#include <linux/init.h>
18#include <linux/bootmem.h>
19
20#include <asm/oplib.h>
21#include <asm/setup.h>
22#include <asm/contregs.h>
23#include <asm/movs.h>
24#include <asm/pgtable.h>
25#include <asm/sun3-head.h>
26#include <asm/sun3mmu.h>
27#include <asm/rtc.h>
28#include <asm/machdep.h>
29#include <asm/intersil.h>
30#include <asm/irq.h>
31#include <asm/segment.h>
32#include <asm/sun3ints.h>
33
34extern char _text, _end;
35
36char sun3_reserved_pmeg[SUN3_PMEGS_NUM];
37
38extern unsigned long sun3_gettimeoffset(void);
39extern int show_sun3_interrupts (struct seq_file *, void *);
40extern void sun3_sched_init(irqreturn_t (*handler)(int, void *, struct pt_regs *));
41extern void sun3_get_model (char* model);
42extern void idprom_init (void);
43extern int sun3_hwclk(int set, struct rtc_time *t);
44
45volatile char* clock_va;
46extern volatile unsigned char* sun3_intreg;
47extern unsigned long availmem;
48unsigned long num_pages;
49
50static int sun3_get_hardware_list(char *buffer)
51{
52
53 int len = 0;
54
55 len += sprintf(buffer + len, "PROM Revision:\t%s\n",
56 romvec->pv_monid);
57
58 return len;
59
60}
61
62void __init sun3_init(void)
63{
64 unsigned char enable_register;
65 int i;
66
67 m68k_machtype= MACH_SUN3;
68 m68k_cputype = CPU_68020;
69 m68k_fputype = FPU_68881; /* mc68881 actually */
70 m68k_mmutype = MMU_SUN3;
71 clock_va = (char *) 0xfe06000; /* dark */
72 sun3_intreg = (unsigned char *) 0xfe0a000; /* magic */
73 sun3_disable_interrupts();
74
75 prom_init((void *)LINUX_OPPROM_BEGVM);
76
77 GET_CONTROL_BYTE(AC_SENABLE,enable_register);
78 enable_register |= 0x50; /* Enable FPU */
79 SET_CONTROL_BYTE(AC_SENABLE,enable_register);
80 GET_CONTROL_BYTE(AC_SENABLE,enable_register);
81
82 /* This code looks suspicious, because it doesn't subtract
83 memory belonging to the kernel from the available space */
84
85
86 memset(sun3_reserved_pmeg, 0, sizeof(sun3_reserved_pmeg));
87
88 /* Reserve important PMEGS */
89 /* FIXME: These should be probed instead of hardcoded */
90
91 for (i=0; i<8; i++) /* Kernel PMEGs */
92 sun3_reserved_pmeg[i] = 1;
93
94 sun3_reserved_pmeg[247] = 1; /* ROM mapping */
95 sun3_reserved_pmeg[248] = 1; /* AMD Ethernet */
96 sun3_reserved_pmeg[251] = 1; /* VB area */
97 sun3_reserved_pmeg[254] = 1; /* main I/O */
98
99 sun3_reserved_pmeg[249] = 1;
100 sun3_reserved_pmeg[252] = 1;
101 sun3_reserved_pmeg[253] = 1;
102 set_fs(KERNEL_DS);
103}
104
105/* Without this, Bad Things happen when something calls arch_reset. */
106static void sun3_reboot (void)
107{
108 prom_reboot ("vmlinux");
109}
110
111static void sun3_halt (void)
112{
113 prom_halt ();
114}
115
116/* sun3 bootmem allocation */
117
118void __init sun3_bootmem_alloc(unsigned long memory_start, unsigned long memory_end)
119{
120 unsigned long start_page;
121
122 /* align start/end to page boundaries */
123 memory_start = ((memory_start + (PAGE_SIZE-1)) & PAGE_MASK);
124 memory_end = memory_end & PAGE_MASK;
125
126 start_page = __pa(memory_start) >> PAGE_SHIFT;
127 num_pages = __pa(memory_end) >> PAGE_SHIFT;
128
129 high_memory = (void *)memory_end;
130 availmem = memory_start;
131
132 availmem += init_bootmem_node(NODE_DATA(0), start_page, 0, num_pages);
133 availmem = (availmem + (PAGE_SIZE-1)) & PAGE_MASK;
134
135 free_bootmem(__pa(availmem), memory_end - (availmem));
136}
137
138
139void __init config_sun3(void)
140{
141 unsigned long memory_start, memory_end;
142
143 printk("ARCH: SUN3\n");
144 idprom_init();
145
146 /* Subtract kernel memory from available memory */
147
148 mach_sched_init = sun3_sched_init;
149 mach_init_IRQ = sun3_init_IRQ;
150 mach_default_handler = &sun3_default_handler;
151 mach_request_irq = sun3_request_irq;
152 mach_free_irq = sun3_free_irq;
153 enable_irq = sun3_enable_irq;
154 disable_irq = sun3_disable_irq;
155 mach_process_int = sun3_process_int;
156 mach_get_irq_list = show_sun3_interrupts;
157 mach_reset = sun3_reboot;
158 mach_gettimeoffset = sun3_gettimeoffset;
159 mach_get_model = sun3_get_model;
160 mach_hwclk = sun3_hwclk;
161 mach_halt = sun3_halt;
162 mach_get_hardware_list = sun3_get_hardware_list;
163#if defined(CONFIG_DUMMY_CONSOLE)
164 conswitchp = &dummy_con;
165#endif
166
167 memory_start = ((((int)&_end) + 0x2000) & ~0x1fff);
168// PROM seems to want the last couple of physical pages. --m
169 memory_end = *(romvec->pv_sun3mem) + PAGE_OFFSET - 2*PAGE_SIZE;
170
171 m68k_num_memory=1;
172 m68k_memory[0].size=*(romvec->pv_sun3mem);
173
174 sun3_bootmem_alloc(memory_start, memory_end);
175}
176
177void __init sun3_sched_init(irqreturn_t (*timer_routine)(int, void *, struct pt_regs *))
178{
179 sun3_disable_interrupts();
180 intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_DISABLE|INTERSIL_24H_MODE);
181 intersil_clock->int_reg=INTERSIL_HZ_100_MASK;
182 intersil_clear();
183 sun3_enable_irq(5);
184 intersil_clock->cmd_reg=(INTERSIL_RUN|INTERSIL_INT_ENABLE|INTERSIL_24H_MODE);
185 sun3_enable_interrupts();
186 intersil_clear();
187}
188
diff --git a/arch/m68k/sun3/dvma.c b/arch/m68k/sun3/dvma.c
new file mode 100644
index 000000000000..d2b3093f2405
--- /dev/null
+++ b/arch/m68k/sun3/dvma.c
@@ -0,0 +1,71 @@
1/*
2 * linux/arch/m68k/sun3/dvma.c
3 *
4 * Written by Sam Creasey
5 *
6 * Sun3 IOMMU routines used for dvma accesses.
7 *
8 */
9
10#include <linux/kernel.h>
11#include <linux/mm.h>
12#include <linux/bootmem.h>
13#include <linux/list.h>
14#include <asm/page.h>
15#include <asm/pgtable.h>
16#include <asm/sun3mmu.h>
17#include <asm/dvma.h>
18
19
20static unsigned long ptelist[120];
21
22inline unsigned long dvma_page(unsigned long kaddr, unsigned long vaddr)
23{
24 unsigned long pte;
25 unsigned long j;
26 pte_t ptep;
27
28 j = *(volatile unsigned long *)kaddr;
29 *(volatile unsigned long *)kaddr = j;
30
31 ptep = pfn_pte(virt_to_pfn(kaddr), PAGE_KERNEL);
32 pte = pte_val(ptep);
33// printk("dvma_remap: addr %lx -> %lx pte %08lx len %x\n",
34// kaddr, vaddr, pte, len);
35 if(ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] != pte) {
36 sun3_put_pte(vaddr, pte);
37 ptelist[(vaddr & 0xff000) >> PAGE_SHIFT] = pte;
38 }
39
40 return (vaddr + (kaddr & ~PAGE_MASK));
41
42}
43
44int dvma_map_iommu(unsigned long kaddr, unsigned long baddr,
45 int len)
46{
47
48 unsigned long end;
49 unsigned long vaddr;
50
51 vaddr = dvma_btov(baddr);
52
53 end = vaddr + len;
54
55 while(vaddr < end) {
56 dvma_page(kaddr, vaddr);
57 kaddr += PAGE_SIZE;
58 vaddr += PAGE_SIZE;
59 }
60
61 return 0;
62
63}
64
65void sun3_dvma_init(void)
66{
67
68 memset(ptelist, 0, sizeof(ptelist));
69
70
71}
diff --git a/arch/m68k/sun3/idprom.c b/arch/m68k/sun3/idprom.c
new file mode 100644
index 000000000000..02c1fee6fe74
--- /dev/null
+++ b/arch/m68k/sun3/idprom.c
@@ -0,0 +1,129 @@
1/* $Id: idprom.c,v 1.22 1996/11/13 05:09:25 davem Exp $
2 * idprom.c: Routines to load the idprom into kernel addresses and
3 * interpret the data contained within.
4 *
5 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
6 * Sun3/3x models added by David Monro (davidm@psrg.cs.usyd.edu.au)
7 */
8
9#include <linux/kernel.h>
10#include <linux/types.h>
11#include <linux/init.h>
12#include <linux/string.h>
13
14#include <asm/oplib.h>
15#include <asm/idprom.h>
16#include <asm/machines.h> /* Fun with Sun released architectures. */
17
18struct idprom *idprom;
19static struct idprom idprom_buffer;
20
21/* Here is the master table of Sun machines which use some implementation
22 * of the Sparc CPU and have a meaningful IDPROM machtype value that we
23 * know about. See asm-sparc/machines.h for empirical constants.
24 */
25struct Sun_Machine_Models Sun_Machines[NUM_SUN_MACHINES] = {
26/* First, Sun3's */
27 { .name = "Sun 3/160 Series", .id_machtype = (SM_SUN3 | SM_3_160) },
28 { .name = "Sun 3/50", .id_machtype = (SM_SUN3 | SM_3_50) },
29 { .name = "Sun 3/260 Series", .id_machtype = (SM_SUN3 | SM_3_260) },
30 { .name = "Sun 3/110 Series", .id_machtype = (SM_SUN3 | SM_3_110) },
31 { .name = "Sun 3/60", .id_machtype = (SM_SUN3 | SM_3_60) },
32 { .name = "Sun 3/E", .id_machtype = (SM_SUN3 | SM_3_E) },
33/* Now, Sun3x's */
34 { .name = "Sun 3/460 Series", .id_machtype = (SM_SUN3X | SM_3_460) },
35 { .name = "Sun 3/80", .id_machtype = (SM_SUN3X | SM_3_80) },
36/* Then, Sun4's */
37// { .name = "Sun 4/100 Series", .id_machtype = (SM_SUN4 | SM_4_110) },
38// { .name = "Sun 4/200 Series", .id_machtype = (SM_SUN4 | SM_4_260) },
39// { .name = "Sun 4/300 Series", .id_machtype = (SM_SUN4 | SM_4_330) },
40// { .name = "Sun 4/400 Series", .id_machtype = (SM_SUN4 | SM_4_470) },
41/* And now, Sun4c's */
42// { .name = "Sun4c SparcStation 1", .id_machtype = (SM_SUN4C | SM_4C_SS1) },
43// { .name = "Sun4c SparcStation IPC", .id_machtype = (SM_SUN4C | SM_4C_IPC) },
44// { .name = "Sun4c SparcStation 1+", .id_machtype = (SM_SUN4C | SM_4C_SS1PLUS) },
45// { .name = "Sun4c SparcStation SLC", .id_machtype = (SM_SUN4C | SM_4C_SLC) },
46// { .name = "Sun4c SparcStation 2", .id_machtype = (SM_SUN4C | SM_4C_SS2) },
47// { .name = "Sun4c SparcStation ELC", .id_machtype = (SM_SUN4C | SM_4C_ELC) },
48// { .name = "Sun4c SparcStation IPX", .id_machtype = (SM_SUN4C | SM_4C_IPX) },
49/* Finally, early Sun4m's */
50// { .name = "Sun4m SparcSystem600", .id_machtype = (SM_SUN4M | SM_4M_SS60) },
51// { .name = "Sun4m SparcStation10/20", .id_machtype = (SM_SUN4M | SM_4M_SS50) },
52// { .name = "Sun4m SparcStation5", .id_machtype = (SM_SUN4M | SM_4M_SS40) },
53/* One entry for the OBP arch's which are sun4d, sun4e, and newer sun4m's */
54// { .name = "Sun4M OBP based system", .id_machtype = (SM_SUN4M_OBP | 0x0) }
55};
56
57static void __init display_system_type(unsigned char machtype)
58{
59 register int i;
60
61 for (i = 0; i < NUM_SUN_MACHINES; i++) {
62 if(Sun_Machines[i].id_machtype == machtype) {
63 if (machtype != (SM_SUN4M_OBP | 0x00))
64 printk("TYPE: %s\n", Sun_Machines[i].name);
65 else {
66#if 0
67 prom_getproperty(prom_root_node, "banner-name",
68 sysname, sizeof(sysname));
69 printk("TYPE: %s\n", sysname);
70#endif
71 }
72 return;
73 }
74 }
75
76 prom_printf("IDPROM: Bogus id_machtype value, 0x%x\n", machtype);
77 prom_halt();
78}
79
80void sun3_get_model(unsigned char* model)
81{
82 register int i;
83
84 for (i = 0; i < NUM_SUN_MACHINES; i++) {
85 if(Sun_Machines[i].id_machtype == idprom->id_machtype) {
86 strcpy(model, Sun_Machines[i].name);
87 return;
88 }
89 }
90}
91
92
93
94/* Calculate the IDPROM checksum (xor of the data bytes). */
95static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
96{
97 unsigned char cksum, i, *ptr = (unsigned char *)idprom;
98
99 for (i = cksum = 0; i <= 0x0E; i++)
100 cksum ^= *ptr++;
101
102 return cksum;
103}
104
105/* Create a local IDPROM copy, verify integrity, and display information. */
106void __init idprom_init(void)
107{
108 prom_get_idprom((char *) &idprom_buffer, sizeof(idprom_buffer));
109
110 idprom = &idprom_buffer;
111
112 if (idprom->id_format != 0x01) {
113 prom_printf("IDPROM: Unknown format type!\n");
114 prom_halt();
115 }
116
117 if (idprom->id_cksum != calc_idprom_cksum(idprom)) {
118 prom_printf("IDPROM: Checksum failure (nvram=%x, calc=%x)!\n",
119 idprom->id_cksum, calc_idprom_cksum(idprom));
120 prom_halt();
121 }
122
123 display_system_type(idprom->id_machtype);
124
125 printk("Ethernet address: %x:%x:%x:%x:%x:%x\n",
126 idprom->id_ethaddr[0], idprom->id_ethaddr[1],
127 idprom->id_ethaddr[2], idprom->id_ethaddr[3],
128 idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
129}
diff --git a/arch/m68k/sun3/intersil.c b/arch/m68k/sun3/intersil.c
new file mode 100644
index 000000000000..db359d7402a6
--- /dev/null
+++ b/arch/m68k/sun3/intersil.c
@@ -0,0 +1,76 @@
1/*
2 * arch/m68k/sun3/intersil.c
3 *
4 * basic routines for accessing the intersil clock within the sun3 machines
5 *
6 * started 11/12/1999 Sam Creasey
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file COPYING in the main directory of this archive
10 * for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/rtc.h>
15
16#include <asm/errno.h>
17#include <asm/system.h>
18#include <asm/semaphore.h>
19#include <asm/rtc.h>
20#include <asm/intersil.h>
21
22
23/* bits to set for start/run of the intersil */
24#define STOP_VAL (INTERSIL_STOP | INTERSIL_INT_ENABLE | INTERSIL_24H_MODE)
25#define START_VAL (INTERSIL_RUN | INTERSIL_INT_ENABLE | INTERSIL_24H_MODE)
26
27/* does this need to be implemented? */
28unsigned long sun3_gettimeoffset(void)
29{
30 return 1;
31}
32
33
34/* get/set hwclock */
35
36int sun3_hwclk(int set, struct rtc_time *t)
37{
38 volatile struct intersil_dt *todintersil;
39 unsigned long flags;
40
41 todintersil = (struct intersil_dt *) &intersil_clock->counter;
42
43 local_irq_save(flags);
44
45 intersil_clock->cmd_reg = STOP_VAL;
46
47 /* set or read the clock */
48 if(set) {
49 todintersil->csec = 0;
50 todintersil->hour = t->tm_hour;
51 todintersil->minute = t->tm_min;
52 todintersil->second = t->tm_sec;
53 todintersil->month = t->tm_mon;
54 todintersil->day = t->tm_mday;
55 todintersil->year = t->tm_year - 68;
56 todintersil->weekday = t->tm_wday;
57 } else {
58 /* read clock */
59 t->tm_sec = todintersil->csec;
60 t->tm_hour = todintersil->hour;
61 t->tm_min = todintersil->minute;
62 t->tm_sec = todintersil->second;
63 t->tm_mon = todintersil->month;
64 t->tm_mday = todintersil->day;
65 t->tm_year = todintersil->year + 68;
66 t->tm_wday = todintersil->weekday;
67 }
68
69 intersil_clock->cmd_reg = START_VAL;
70
71 local_irq_restore(flags);
72
73 return 0;
74
75}
76
diff --git a/arch/m68k/sun3/leds.c b/arch/m68k/sun3/leds.c
new file mode 100644
index 000000000000..a3e948463982
--- /dev/null
+++ b/arch/m68k/sun3/leds.c
@@ -0,0 +1,13 @@
1#include <asm/contregs.h>
2#include <asm/sun3mmu.h>
3#include <asm/io.h>
4
5void sun3_leds(unsigned char byte)
6{
7 unsigned char dfc;
8
9 GET_DFC(dfc);
10 SET_DFC(FC_CONTROL);
11 SET_CONTROL_BYTE(AC_LEDS,byte);
12 SET_DFC(dfc);
13}
diff --git a/arch/m68k/sun3/mmu_emu.c b/arch/m68k/sun3/mmu_emu.c
new file mode 100644
index 000000000000..7a0e3a220687
--- /dev/null
+++ b/arch/m68k/sun3/mmu_emu.c
@@ -0,0 +1,427 @@
1/*
2** Tablewalk MMU emulator
3**
4** by Toshiyasu Morita
5**
6** Started 1/16/98 @ 2:22 am
7*/
8
9#include <linux/mman.h>
10#include <linux/mm.h>
11#include <linux/kernel.h>
12#include <linux/ptrace.h>
13#include <linux/delay.h>
14#include <linux/bootmem.h>
15#include <linux/bitops.h>
16#include <linux/module.h>
17
18#include <asm/setup.h>
19#include <asm/traps.h>
20#include <asm/system.h>
21#include <asm/uaccess.h>
22#include <asm/page.h>
23#include <asm/pgtable.h>
24#include <asm/sun3mmu.h>
25#include <asm/segment.h>
26#include <asm/oplib.h>
27#include <asm/mmu_context.h>
28#include <asm/dvma.h>
29
30extern void prom_reboot (char *) __attribute__ ((__noreturn__));
31
32#undef DEBUG_MMU_EMU
33#define DEBUG_PROM_MAPS
34
35/*
36** Defines
37*/
38
39#define CONTEXTS_NUM 8
40#define SEGMAPS_PER_CONTEXT_NUM 2048
41#define PAGES_PER_SEGMENT 16
42#define PMEGS_NUM 256
43#define PMEG_MASK 0xFF
44
45/*
46** Globals
47*/
48
49unsigned long vmalloc_end;
50EXPORT_SYMBOL(vmalloc_end);
51
52unsigned long pmeg_vaddr[PMEGS_NUM];
53unsigned char pmeg_alloc[PMEGS_NUM];
54unsigned char pmeg_ctx[PMEGS_NUM];
55
56/* pointers to the mm structs for each task in each
57 context. 0xffffffff is a marker for kernel context */
58struct mm_struct *ctx_alloc[CONTEXTS_NUM] = {
59 [0] = (struct mm_struct *)0xffffffff
60};
61
62/* has this context been mmdrop'd? */
63static unsigned char ctx_avail = CONTEXTS_NUM-1;
64
65/* array of pages to be marked off for the rom when we do mem_init later */
66/* 256 pages lets the rom take up to 2mb of physical ram.. I really
67 hope it never wants mote than that. */
68unsigned long rom_pages[256];
69
70/* Print a PTE value in symbolic form. For debugging. */
71void print_pte (pte_t pte)
72{
73#if 0
74 /* Verbose version. */
75 unsigned long val = pte_val (pte);
76 printk (" pte=%lx [addr=%lx",
77 val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT);
78 if (val & SUN3_PAGE_VALID) printk (" valid");
79 if (val & SUN3_PAGE_WRITEABLE) printk (" write");
80 if (val & SUN3_PAGE_SYSTEM) printk (" sys");
81 if (val & SUN3_PAGE_NOCACHE) printk (" nocache");
82 if (val & SUN3_PAGE_ACCESSED) printk (" accessed");
83 if (val & SUN3_PAGE_MODIFIED) printk (" modified");
84 switch (val & SUN3_PAGE_TYPE_MASK) {
85 case SUN3_PAGE_TYPE_MEMORY: printk (" memory"); break;
86 case SUN3_PAGE_TYPE_IO: printk (" io"); break;
87 case SUN3_PAGE_TYPE_VME16: printk (" vme16"); break;
88 case SUN3_PAGE_TYPE_VME32: printk (" vme32"); break;
89 }
90 printk ("]\n");
91#else
92 /* Terse version. More likely to fit on a line. */
93 unsigned long val = pte_val (pte);
94 char flags[7], *type;
95
96 flags[0] = (val & SUN3_PAGE_VALID) ? 'v' : '-';
97 flags[1] = (val & SUN3_PAGE_WRITEABLE) ? 'w' : '-';
98 flags[2] = (val & SUN3_PAGE_SYSTEM) ? 's' : '-';
99 flags[3] = (val & SUN3_PAGE_NOCACHE) ? 'x' : '-';
100 flags[4] = (val & SUN3_PAGE_ACCESSED) ? 'a' : '-';
101 flags[5] = (val & SUN3_PAGE_MODIFIED) ? 'm' : '-';
102 flags[6] = '\0';
103
104 switch (val & SUN3_PAGE_TYPE_MASK) {
105 case SUN3_PAGE_TYPE_MEMORY: type = "memory"; break;
106 case SUN3_PAGE_TYPE_IO: type = "io" ; break;
107 case SUN3_PAGE_TYPE_VME16: type = "vme16" ; break;
108 case SUN3_PAGE_TYPE_VME32: type = "vme32" ; break;
109 default: type = "unknown?"; break;
110 }
111
112 printk (" pte=%08lx [%07lx %s %s]\n",
113 val, (val & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT, flags, type);
114#endif
115}
116
117/* Print the PTE value for a given virtual address. For debugging. */
118void print_pte_vaddr (unsigned long vaddr)
119{
120 printk (" vaddr=%lx [%02lx]", vaddr, sun3_get_segmap (vaddr));
121 print_pte (__pte (sun3_get_pte (vaddr)));
122}
123
124/*
125 * Initialise the MMU emulator.
126 */
127void mmu_emu_init(unsigned long bootmem_end)
128{
129 unsigned long seg, num;
130 int i,j;
131
132 memset(rom_pages, 0, sizeof(rom_pages));
133 memset(pmeg_vaddr, 0, sizeof(pmeg_vaddr));
134 memset(pmeg_alloc, 0, sizeof(pmeg_alloc));
135 memset(pmeg_ctx, 0, sizeof(pmeg_ctx));
136
137 /* pmeg align the end of bootmem, adding another pmeg,
138 * later bootmem allocations will likely need it */
139 bootmem_end = (bootmem_end + (2 * SUN3_PMEG_SIZE)) & ~SUN3_PMEG_MASK;
140
141 /* mark all of the pmegs used thus far as reserved */
142 for (i=0; i < __pa(bootmem_end) / SUN3_PMEG_SIZE ; ++i)
143 pmeg_alloc[i] = 2;
144
145
146 /* I'm thinking that most of the top pmeg's are going to be
147 used for something, and we probably shouldn't risk it */
148 for(num = 0xf0; num <= 0xff; num++)
149 pmeg_alloc[num] = 2;
150
151 /* liberate all existing mappings in the rest of kernel space */
152 for(seg = bootmem_end; seg < 0x0f800000; seg += SUN3_PMEG_SIZE) {
153 i = sun3_get_segmap(seg);
154
155 if(!pmeg_alloc[i]) {
156#ifdef DEBUG_MMU_EMU
157 printk("freed: ");
158 print_pte_vaddr (seg);
159#endif
160 sun3_put_segmap(seg, SUN3_INVALID_PMEG);
161 }
162 }
163
164 j = 0;
165 for (num=0, seg=0x0F800000; seg<0x10000000; seg+=16*PAGE_SIZE) {
166 if (sun3_get_segmap (seg) != SUN3_INVALID_PMEG) {
167#ifdef DEBUG_PROM_MAPS
168 for(i = 0; i < 16; i++) {
169 printk ("mapped:");
170 print_pte_vaddr (seg + (i*PAGE_SIZE));
171 break;
172 }
173#endif
174 // the lowest mapping here is the end of our
175 // vmalloc region
176 if(!vmalloc_end)
177 vmalloc_end = seg;
178
179 // mark the segmap alloc'd, and reserve any
180 // of the first 0xbff pages the hardware is
181 // already using... does any sun3 support > 24mb?
182 pmeg_alloc[sun3_get_segmap(seg)] = 2;
183 }
184 }
185
186 dvma_init();
187
188
189 /* blank everything below the kernel, and we've got the base
190 mapping to start all the contexts off with... */
191 for(seg = 0; seg < PAGE_OFFSET; seg += SUN3_PMEG_SIZE)
192 sun3_put_segmap(seg, SUN3_INVALID_PMEG);
193
194 set_fs(MAKE_MM_SEG(3));
195 for(seg = 0; seg < 0x10000000; seg += SUN3_PMEG_SIZE) {
196 i = sun3_get_segmap(seg);
197 for(j = 1; j < CONTEXTS_NUM; j++)
198 (*(romvec->pv_setctxt))(j, (void *)seg, i);
199 }
200 set_fs(KERNEL_DS);
201
202}
203
204/* erase the mappings for a dead context. Uses the pg_dir for hints
205 as the pmeg tables proved somewhat unreliable, and unmapping all of
206 TASK_SIZE was much slower and no more stable. */
207/* todo: find a better way to keep track of the pmegs used by a
208 context for when they're cleared */
209void clear_context(unsigned long context)
210{
211 unsigned char oldctx;
212 unsigned long i;
213
214 if(context) {
215 if(!ctx_alloc[context])
216 panic("clear_context: context not allocated\n");
217
218 ctx_alloc[context]->context = SUN3_INVALID_CONTEXT;
219 ctx_alloc[context] = (struct mm_struct *)0;
220 ctx_avail++;
221 }
222
223 oldctx = sun3_get_context();
224
225 sun3_put_context(context);
226
227 for(i = 0; i < SUN3_INVALID_PMEG; i++) {
228 if((pmeg_ctx[i] == context) && (pmeg_alloc[i] == 1)) {
229 sun3_put_segmap(pmeg_vaddr[i], SUN3_INVALID_PMEG);
230 pmeg_ctx[i] = 0;
231 pmeg_alloc[i] = 0;
232 pmeg_vaddr[i] = 0;
233 }
234 }
235
236 sun3_put_context(oldctx);
237}
238
239/* gets an empty context. if full, kills the next context listed to
240 die first */
241/* This context invalidation scheme is, well, totally arbitrary, I'm
242 sure it could be much more intellegent... but it gets the job done
243 for now without much overhead in making it's decision. */
244/* todo: come up with optimized scheme for flushing contexts */
245unsigned long get_free_context(struct mm_struct *mm)
246{
247 unsigned long new = 1;
248 static unsigned char next_to_die = 1;
249
250 if(!ctx_avail) {
251 /* kill someone to get our context */
252 new = next_to_die;
253 clear_context(new);
254 next_to_die = (next_to_die + 1) & 0x7;
255 if(!next_to_die)
256 next_to_die++;
257 } else {
258 while(new < CONTEXTS_NUM) {
259 if(ctx_alloc[new])
260 new++;
261 else
262 break;
263 }
264 // check to make sure one was really free...
265 if(new == CONTEXTS_NUM)
266 panic("get_free_context: failed to find free context");
267 }
268
269 ctx_alloc[new] = mm;
270 ctx_avail--;
271
272 return new;
273}
274
275/*
276 * Dynamically select a `spare' PMEG and use it to map virtual `vaddr' in
277 * `context'. Maintain internal PMEG management structures. This doesn't
278 * actually map the physical address, but does clear the old mappings.
279 */
280//todo: better allocation scheme? but is extra complexity worthwhile?
281//todo: only clear old entries if necessary? how to tell?
282
283inline void mmu_emu_map_pmeg (int context, int vaddr)
284{
285 static unsigned char curr_pmeg = 128;
286 int i;
287
288 /* Round address to PMEG boundary. */
289 vaddr &= ~SUN3_PMEG_MASK;
290
291 /* Find a spare one. */
292 while (pmeg_alloc[curr_pmeg] == 2)
293 ++curr_pmeg;
294
295
296#ifdef DEBUG_MMU_EMU
297printk("mmu_emu_map_pmeg: pmeg %x to context %d vaddr %x\n",
298 curr_pmeg, context, vaddr);
299#endif
300
301 /* Invalidate old mapping for the pmeg, if any */
302 if (pmeg_alloc[curr_pmeg] == 1) {
303 sun3_put_context(pmeg_ctx[curr_pmeg]);
304 sun3_put_segmap (pmeg_vaddr[curr_pmeg], SUN3_INVALID_PMEG);
305 sun3_put_context(context);
306 }
307
308 /* Update PMEG management structures. */
309 // don't take pmeg's away from the kernel...
310 if(vaddr >= PAGE_OFFSET) {
311 /* map kernel pmegs into all contexts */
312 unsigned char i;
313
314 for(i = 0; i < CONTEXTS_NUM; i++) {
315 sun3_put_context(i);
316 sun3_put_segmap (vaddr, curr_pmeg);
317 }
318 sun3_put_context(context);
319 pmeg_alloc[curr_pmeg] = 2;
320 pmeg_ctx[curr_pmeg] = 0;
321
322 }
323 else {
324 pmeg_alloc[curr_pmeg] = 1;
325 pmeg_ctx[curr_pmeg] = context;
326 sun3_put_segmap (vaddr, curr_pmeg);
327
328 }
329 pmeg_vaddr[curr_pmeg] = vaddr;
330
331 /* Set hardware mapping and clear the old PTE entries. */
332 for (i=0; i<SUN3_PMEG_SIZE; i+=SUN3_PTE_SIZE)
333 sun3_put_pte (vaddr + i, SUN3_PAGE_SYSTEM);
334
335 /* Consider a different one next time. */
336 ++curr_pmeg;
337}
338
339/*
340 * Handle a pagefault at virtual address `vaddr'; check if there should be a
341 * page there (specifically, whether the software pagetables indicate that
342 * there is). This is necessary due to the limited size of the second-level
343 * Sun3 hardware pagetables (256 groups of 16 pages). If there should be a
344 * mapping present, we select a `spare' PMEG and use it to create a mapping.
345 * `read_flag' is nonzero for a read fault; zero for a write. Returns nonzero
346 * if we successfully handled the fault.
347 */
348//todo: should we bump minor pagefault counter? if so, here or in caller?
349//todo: possibly inline this into bus_error030 in <asm/buserror.h> ?
350
351// kernel_fault is set when a kernel page couldn't be demand mapped,
352// and forces another try using the kernel page table. basically a
353// hack so that vmalloc would work correctly.
354
355int mmu_emu_handle_fault (unsigned long vaddr, int read_flag, int kernel_fault)
356{
357 unsigned long segment, offset;
358 unsigned char context;
359 pte_t *pte;
360 pgd_t * crp;
361
362 if(current->mm == NULL) {
363 crp = swapper_pg_dir;
364 context = 0;
365 } else {
366 context = current->mm->context;
367 if(kernel_fault)
368 crp = swapper_pg_dir;
369 else
370 crp = current->mm->pgd;
371 }
372
373#ifdef DEBUG_MMU_EMU
374 printk ("mmu_emu_handle_fault: vaddr=%lx type=%s crp=%p\n",
375 vaddr, read_flag ? "read" : "write", crp);
376#endif
377
378 segment = (vaddr >> SUN3_PMEG_SIZE_BITS) & 0x7FF;
379 offset = (vaddr >> SUN3_PTE_SIZE_BITS) & 0xF;
380
381#ifdef DEBUG_MMU_EMU
382 printk ("mmu_emu_handle_fault: segment=%lx offset=%lx\n", segment, offset);
383#endif
384
385 pte = (pte_t *) pgd_val (*(crp + segment));
386
387//todo: next line should check for valid pmd properly.
388 if (!pte) {
389// printk ("mmu_emu_handle_fault: invalid pmd\n");
390 return 0;
391 }
392
393 pte = (pte_t *) __va ((unsigned long)(pte + offset));
394
395 /* Make sure this is a valid page */
396 if (!(pte_val (*pte) & SUN3_PAGE_VALID))
397 return 0;
398
399 /* Make sure there's a pmeg allocated for the page */
400 if (sun3_get_segmap (vaddr&~SUN3_PMEG_MASK) == SUN3_INVALID_PMEG)
401 mmu_emu_map_pmeg (context, vaddr);
402
403 /* Write the pte value to hardware MMU */
404 sun3_put_pte (vaddr&PAGE_MASK, pte_val (*pte));
405
406 /* Update software copy of the pte value */
407// I'm not sure this is necessary. If this is required, we ought to simply
408// copy this out when we reuse the PMEG or at some other convenient time.
409// Doing it here is fairly meaningless, anyway, as we only know about the
410// first access to a given page. --m
411 if (!read_flag) {
412 if (pte_val (*pte) & SUN3_PAGE_WRITEABLE)
413 pte_val (*pte) |= (SUN3_PAGE_ACCESSED
414 | SUN3_PAGE_MODIFIED);
415 else
416 return 0; /* Write-protect error. */
417 } else
418 pte_val (*pte) |= SUN3_PAGE_ACCESSED;
419
420#ifdef DEBUG_MMU_EMU
421 printk ("seg:%d crp:%p ->", get_fs().seg, crp);
422 print_pte_vaddr (vaddr);
423 printk ("\n");
424#endif
425
426 return 1;
427}
diff --git a/arch/m68k/sun3/prom/Makefile b/arch/m68k/sun3/prom/Makefile
new file mode 100644
index 000000000000..6e48ae2a7175
--- /dev/null
+++ b/arch/m68k/sun3/prom/Makefile
@@ -0,0 +1,7 @@
1# $Id: Makefile,v 1.5 1995/11/25 00:59:48 davem Exp $
2# Makefile for the Sun Boot PROM interface library under
3# Linux.
4#
5
6obj-y := init.o console.o printf.o misc.o
7#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 000000000000..52c1427863de
--- /dev/null
+++ b/arch/m68k/sun3/prom/console.c
@@ -0,0 +1,174 @@
1/* $Id: console.c,v 1.10 1996/12/18 06:46:54 tridge Exp $
2 * console.c: Routines that deal with sending and receiving IO
3 * to/from the current console device using the PROM.
4 *
5 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
6 */
7
8#include <linux/types.h>
9#include <linux/kernel.h>
10#include <linux/sched.h>
11#include <asm/openprom.h>
12#include <asm/oplib.h>
13#include <asm/system.h>
14#include <linux/string.h>
15
16/* Non blocking get character from console input device, returns -1
17 * if no input was taken. This can be used for polling.
18 */
19int
20prom_nbgetchar(void)
21{
22 int i = -1;
23 unsigned long flags;
24
25 local_irq_save(flags);
26 i = (*(romvec->pv_nbgetchar))();
27 local_irq_restore(flags);
28 return i; /* Ugh, we could spin forever on unsupported proms ;( */
29}
30
31/* Non blocking put character to console device, returns -1 if
32 * unsuccessful.
33 */
34int
35prom_nbputchar(char c)
36{
37 unsigned long flags;
38 int i = -1;
39
40 local_irq_save(flags);
41 i = (*(romvec->pv_nbputchar))(c);
42 local_irq_restore(flags);
43 return i; /* Ugh, we could spin forever on unsupported proms ;( */
44}
45
46/* Blocking version of get character routine above. */
47char
48prom_getchar(void)
49{
50 int character;
51 while((character = prom_nbgetchar()) == -1) ;
52 return (char) character;
53}
54
55/* Blocking version of put character routine above. */
56void
57prom_putchar(char c)
58{
59 while(prom_nbputchar(c) == -1) ;
60 return;
61}
62
63/* Query for input device type */
64#if 0
65enum prom_input_device
66prom_query_input_device()
67{
68 unsigned long flags;
69 int st_p;
70 char propb[64];
71 char *p;
72
73 switch(prom_vers) {
74 case PROM_V0:
75 case PROM_V2:
76 default:
77 switch(*romvec->pv_stdin) {
78 case PROMDEV_KBD: return PROMDEV_IKBD;
79 case PROMDEV_TTYA: return PROMDEV_ITTYA;
80 case PROMDEV_TTYB: return PROMDEV_ITTYB;
81 default:
82 return PROMDEV_I_UNK;
83 };
84 case PROM_V3:
85 case PROM_P1275:
86 local_irq_save(flags);
87 st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdin);
88 __asm__ __volatile__("ld [%0], %%g6\n\t" : :
89 "r" (&current_set[smp_processor_id()]) :
90 "memory");
91 local_irq_restore(flags);
92 if(prom_node_has_property(st_p, "keyboard"))
93 return PROMDEV_IKBD;
94 prom_getproperty(st_p, "device_type", propb, sizeof(propb));
95 if(strncmp(propb, "serial", sizeof("serial")))
96 return PROMDEV_I_UNK;
97 prom_getproperty(prom_root_node, "stdin-path", propb, sizeof(propb));
98 p = propb;
99 while(*p) p++; p -= 2;
100 if(p[0] == ':') {
101 if(p[1] == 'a')
102 return PROMDEV_ITTYA;
103 else if(p[1] == 'b')
104 return PROMDEV_ITTYB;
105 }
106 return PROMDEV_I_UNK;
107 case PROM_AP1000:
108 return PROMDEV_I_UNK;
109 };
110}
111#endif
112
113/* Query for output device type */
114
115#if 0
116enum prom_output_device
117prom_query_output_device()
118{
119 unsigned long flags;
120 int st_p;
121 char propb[64];
122 char *p;
123 int propl;
124
125 switch(prom_vers) {
126 case PROM_V0:
127 switch(*romvec->pv_stdin) {
128 case PROMDEV_SCREEN: return PROMDEV_OSCREEN;
129 case PROMDEV_TTYA: return PROMDEV_OTTYA;
130 case PROMDEV_TTYB: return PROMDEV_OTTYB;
131 };
132 break;
133 case PROM_V2:
134 case PROM_V3:
135 case PROM_P1275:
136 local_irq_save(flags);
137 st_p = (*romvec->pv_v2devops.v2_inst2pkg)(*romvec->pv_v2bootargs.fd_stdout);
138 __asm__ __volatile__("ld [%0], %%g6\n\t" : :
139 "r" (&current_set[smp_processor_id()]) :
140 "memory");
141 local_irq_restore(flags);
142 propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
143 if (propl >= 0 && propl == sizeof("display") &&
144 strncmp("display", propb, sizeof("display")) == 0)
145 {
146 return PROMDEV_OSCREEN;
147 }
148 if(prom_vers == PROM_V3) {
149 if(strncmp("serial", propb, sizeof("serial")))
150 return PROMDEV_O_UNK;
151 prom_getproperty(prom_root_node, "stdout-path", propb, sizeof(propb));
152 p = propb;
153 while(*p) p++; p -= 2;
154 if(p[0]==':') {
155 if(p[1] == 'a')
156 return PROMDEV_OTTYA;
157 else if(p[1] == 'b')
158 return PROMDEV_OTTYB;
159 }
160 return PROMDEV_O_UNK;
161 } else {
162 /* This works on SS-2 (an early OpenFirmware) still. */
163 switch(*romvec->pv_stdin) {
164 case PROMDEV_TTYA: return PROMDEV_OTTYA;
165 case PROMDEV_TTYB: return PROMDEV_OTTYB;
166 };
167 }
168 break;
169 case PROM_AP1000:
170 return PROMDEV_I_UNK;
171 };
172 return PROMDEV_O_UNK;
173}
174#endif
diff --git a/arch/m68k/sun3/prom/init.c b/arch/m68k/sun3/prom/init.c
new file mode 100644
index 000000000000..2e6ae56aec12
--- /dev/null
+++ b/arch/m68k/sun3/prom/init.c
@@ -0,0 +1,89 @@
1/* $Id: init.c,v 1.9 1996/12/18 06:46:55 tridge Exp $
2 * init.c: Initialize internal variables used by the PROM
3 * library functions.
4 *
5 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
6 */
7
8#include <linux/config.h>
9#include <linux/kernel.h>
10#include <linux/init.h>
11
12#include <asm/openprom.h>
13#include <asm/oplib.h>
14
15struct linux_romvec *romvec;
16enum prom_major_version prom_vers;
17unsigned int prom_rev, prom_prev;
18
19/* The root node of the prom device tree. */
20int prom_root_node;
21
22/* Pointer to the device tree operations structure. */
23struct linux_nodeops *prom_nodeops;
24
25/* You must call prom_init() before you attempt to use any of the
26 * routines in the prom library. It returns 0 on success, 1 on
27 * failure. It gets passed the pointer to the PROM vector.
28 */
29
30extern void prom_meminit(void);
31extern void prom_ranges_init(void);
32
33void __init prom_init(struct linux_romvec *rp)
34{
35#ifdef CONFIG_AP1000
36 extern struct linux_romvec *ap_prom_init(void);
37 rp = ap_prom_init();
38#endif
39
40 romvec = rp;
41#ifndef CONFIG_SUN3
42 switch(romvec->pv_romvers) {
43 case 0:
44 prom_vers = PROM_V0;
45 break;
46 case 2:
47 prom_vers = PROM_V2;
48 break;
49 case 3:
50 prom_vers = PROM_V3;
51 break;
52 case 4:
53 prom_vers = PROM_P1275;
54 prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n");
55 prom_halt();
56 break;
57 case 42: /* why not :-) */
58 prom_vers = PROM_AP1000;
59 break;
60
61 default:
62 prom_printf("PROMLIB: Bad PROM version %d\n",
63 romvec->pv_romvers);
64 prom_halt();
65 break;
66 };
67
68 prom_rev = romvec->pv_plugin_revision;
69 prom_prev = romvec->pv_printrev;
70 prom_nodeops = romvec->pv_nodeops;
71
72 prom_root_node = prom_getsibling(0);
73 if((prom_root_node == 0) || (prom_root_node == -1))
74 prom_halt();
75
76 if((((unsigned long) prom_nodeops) == 0) ||
77 (((unsigned long) prom_nodeops) == -1))
78 prom_halt();
79
80 prom_meminit();
81
82 prom_ranges_init();
83#endif
84// printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
85// romvec->pv_romvers, prom_rev);
86
87 /* Initialization successful. */
88 return;
89}
diff --git a/arch/m68k/sun3/prom/misc.c b/arch/m68k/sun3/prom/misc.c
new file mode 100644
index 000000000000..b88716f2c68c
--- /dev/null
+++ b/arch/m68k/sun3/prom/misc.c
@@ -0,0 +1,94 @@
1/* $Id: misc.c,v 1.15 1997/05/14 20:45:00 davem Exp $
2 * misc.c: Miscellaneous prom functions that don't belong
3 * anywhere else.
4 *
5 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
6 */
7
8#include <linux/types.h>
9#include <linux/kernel.h>
10#include <linux/sched.h>
11#include <asm/sun3-head.h>
12#include <asm/idprom.h>
13#include <asm/openprom.h>
14#include <asm/oplib.h>
15#include <asm/movs.h>
16
17/* Reset and reboot the machine with the command 'bcommand'. */
18void
19prom_reboot(char *bcommand)
20{
21 unsigned long flags;
22 local_irq_save(flags);
23 (*(romvec->pv_reboot))(bcommand);
24 local_irq_restore(flags);
25}
26
27/* Drop into the prom, with the chance to continue with the 'go'
28 * prom command.
29 */
30void
31prom_cmdline(void)
32{
33}
34
35/* Drop into the prom, but completely terminate the program.
36 * No chance of continuing.
37 */
38void
39prom_halt(void)
40{
41 unsigned long flags;
42again:
43 local_irq_save(flags);
44 (*(romvec->pv_halt))();
45 local_irq_restore(flags);
46 goto again; /* PROM is out to get me -DaveM */
47}
48
49typedef void (*sfunc_t)(void);
50
51/* Get the idprom and stuff it into buffer 'idbuf'. Returns the
52 * format type. 'num_bytes' is the number of bytes that your idbuf
53 * has space for. Returns 0xff on error.
54 */
55unsigned char
56prom_get_idprom(char *idbuf, int num_bytes)
57{
58 int i, oldsfc;
59 GET_SFC(oldsfc);
60 SET_SFC(FC_CONTROL);
61 for(i=0;i<num_bytes; i++)
62 {
63 /* There is a problem with the GET_CONTROL_BYTE
64 macro; defining the extra variable
65 gets around it.
66 */
67 int c;
68 GET_CONTROL_BYTE(SUN3_IDPROM_BASE + i, c);
69 idbuf[i] = c;
70 }
71 SET_SFC(oldsfc);
72 return idbuf[0];
73}
74
75/* Get the major prom version number. */
76int
77prom_version(void)
78{
79 return romvec->pv_romvers;
80}
81
82/* Get the prom plugin-revision. */
83int
84prom_getrev(void)
85{
86 return prom_rev;
87}
88
89/* Get the prom firmware print revision. */
90int
91prom_getprev(void)
92{
93 return prom_prev;
94}
diff --git a/arch/m68k/sun3/prom/printf.c b/arch/m68k/sun3/prom/printf.c
new file mode 100644
index 000000000000..e6ee1006344e
--- /dev/null
+++ b/arch/m68k/sun3/prom/printf.c
@@ -0,0 +1,61 @@
1/* $Id: printf.c,v 1.5 1996/04/04 16:31:07 tridge Exp $
2 * printf.c: Internal prom library printf facility.
3 *
4 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
5 */
6
7/* This routine is internal to the prom library, no one else should know
8 * about or use it! It's simple and smelly anyway....
9 */
10
11#include <linux/config.h>
12#include <linux/kernel.h>
13
14#include <asm/openprom.h>
15#include <asm/oplib.h>
16
17#ifdef CONFIG_KGDB
18extern int kgdb_initialized;
19#endif
20
21static char ppbuf[1024];
22
23void
24prom_printf(char *fmt, ...)
25{
26 va_list args;
27 char ch, *bptr;
28 int i;
29
30 va_start(args, fmt);
31
32#ifdef CONFIG_KGDB
33 ppbuf[0] = 'O';
34 i = vsprintf(ppbuf + 1, fmt, args) + 1;
35#else
36 i = vsprintf(ppbuf, fmt, args);
37#endif
38
39 bptr = ppbuf;
40
41#ifdef CONFIG_AP1000
42 ap_write(1,bptr,strlen(bptr));
43#else
44
45#ifdef CONFIG_KGDB
46 if (kgdb_initialized) {
47 printk("kgdb_initialized = %d\n", kgdb_initialized);
48 putpacket(bptr, 1);
49 } else
50#else
51 while((ch = *(bptr++)) != 0) {
52 if(ch == '\n')
53 prom_putchar('\r');
54
55 prom_putchar(ch);
56 }
57#endif
58#endif
59 va_end(args);
60 return;
61}
diff --git a/arch/m68k/sun3/sbus.c b/arch/m68k/sun3/sbus.c
new file mode 100644
index 000000000000..babdbfa3cda7
--- /dev/null
+++ b/arch/m68k/sun3/sbus.c
@@ -0,0 +1,27 @@
1/*
2 * SBus helper functions
3 *
4 * Sun3 don't have a sbus, but many of the used devices are also
5 * used on Sparc machines with sbus. To avoid having a lot of
6 * duplicate code, we provide necessary glue stuff to make using
7 * of the sbus driver code possible.
8 *
9 * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
10 */
11
12#include <linux/types.h>
13#include <linux/compiler.h>
14#include <linux/init.h>
15
16int __init sbus_init(void)
17{
18 return 0;
19}
20
21void *sparc_alloc_io (u32 address, void *virtual, int len, char *name,
22 u32 bus_type, int rdonly)
23{
24 return (void *)address;
25}
26
27subsys_initcall(sbus_init);
diff --git a/arch/m68k/sun3/sun3_ksyms.c b/arch/m68k/sun3/sun3_ksyms.c
new file mode 100644
index 000000000000..43e5a9af8abd
--- /dev/null
+++ b/arch/m68k/sun3/sun3_ksyms.c
@@ -0,0 +1,13 @@
1#include <linux/module.h>
2#include <linux/types.h>
3#include <asm/dvma.h>
4#include <asm/idprom.h>
5
6/*
7 * Add things here when you find the need for it.
8 */
9EXPORT_SYMBOL(dvma_map_align);
10EXPORT_SYMBOL(dvma_unmap);
11EXPORT_SYMBOL(dvma_malloc_align);
12EXPORT_SYMBOL(dvma_free);
13EXPORT_SYMBOL(idprom);
diff --git a/arch/m68k/sun3/sun3dvma.c b/arch/m68k/sun3/sun3dvma.c
new file mode 100644
index 000000000000..f04a1d25f1a2
--- /dev/null
+++ b/arch/m68k/sun3/sun3dvma.c
@@ -0,0 +1,379 @@
1/*
2 * linux/arch/m68k/mm/sun3dvma.c
3 *
4 * Copyright (C) 2000 Sam Creasey
5 *
6 * Contains common routines for sun3/sun3x DVMA management.
7 */
8
9#include <linux/config.h>
10#include <linux/kernel.h>
11#include <linux/mm.h>
12#include <linux/list.h>
13
14#include <asm/page.h>
15#include <asm/pgtable.h>
16#include <asm/dvma.h>
17
18#undef DVMA_DEBUG
19
20#ifdef CONFIG_SUN3X
21extern void dvma_unmap_iommu(unsigned long baddr, int len);
22#else
23static inline void dvma_unmap_iommu(unsigned long a, int b)
24{
25}
26#endif
27
28#ifdef CONFIG_SUN3
29extern void sun3_dvma_init(void);
30#endif
31
32unsigned long iommu_use[IOMMU_TOTAL_ENTRIES];
33
34#define dvma_index(baddr) ((baddr - DVMA_START) >> DVMA_PAGE_SHIFT)
35
36#define dvma_entry_use(baddr) (iommu_use[dvma_index(baddr)])
37
38struct hole {
39 unsigned long start;
40 unsigned long end;
41 unsigned long size;
42 struct list_head list;
43};
44
45static struct list_head hole_list;
46static struct list_head hole_cache;
47static struct hole initholes[64];
48
49#ifdef DVMA_DEBUG
50
51static unsigned long dvma_allocs;
52static unsigned long dvma_frees;
53static unsigned long long dvma_alloc_bytes;
54static unsigned long long dvma_free_bytes;
55
56static void print_use(void)
57{
58
59 int i;
60 int j = 0;
61
62 printk("dvma entry usage:\n");
63
64 for(i = 0; i < IOMMU_TOTAL_ENTRIES; i++) {
65 if(!iommu_use[i])
66 continue;
67
68 j++;
69
70 printk("dvma entry: %08lx len %08lx\n",
71 ( i << DVMA_PAGE_SHIFT) + DVMA_START,
72 iommu_use[i]);
73 }
74
75 printk("%d entries in use total\n", j);
76
77 printk("allocation/free calls: %lu/%lu\n", dvma_allocs, dvma_frees);
78 printk("allocation/free bytes: %Lx/%Lx\n", dvma_alloc_bytes,
79 dvma_free_bytes);
80}
81
82static void print_holes(struct list_head *holes)
83{
84
85 struct list_head *cur;
86 struct hole *hole;
87
88 printk("listing dvma holes\n");
89 list_for_each(cur, holes) {
90 hole = list_entry(cur, struct hole, list);
91
92 if((hole->start == 0) && (hole->end == 0) && (hole->size == 0))
93 continue;
94
95 printk("hole: start %08lx end %08lx size %08lx\n", hole->start, hole->end, hole->size);
96 }
97
98 printk("end of hole listing...\n");
99
100}
101#endif /* DVMA_DEBUG */
102
103static inline int refill(void)
104{
105
106 struct hole *hole;
107 struct hole *prev = NULL;
108 struct list_head *cur;
109 int ret = 0;
110
111 list_for_each(cur, &hole_list) {
112 hole = list_entry(cur, struct hole, list);
113
114 if(!prev) {
115 prev = hole;
116 continue;
117 }
118
119 if(hole->end == prev->start) {
120 hole->size += prev->size;
121 hole->end = prev->end;
122 list_del(&(prev->list));
123 list_add(&(prev->list), &hole_cache);
124 ret++;
125 }
126
127 }
128
129 return ret;
130}
131
132static inline struct hole *rmcache(void)
133{
134 struct hole *ret;
135
136 if(list_empty(&hole_cache)) {
137 if(!refill()) {
138 printk("out of dvma hole cache!\n");
139 BUG();
140 }
141 }
142
143 ret = list_entry(hole_cache.next, struct hole, list);
144 list_del(&(ret->list));
145
146 return ret;
147
148}
149
150static inline unsigned long get_baddr(int len, unsigned long align)
151{
152
153 struct list_head *cur;
154 struct hole *hole;
155
156 if(list_empty(&hole_list)) {
157#ifdef DVMA_DEBUG
158 printk("out of dvma holes! (printing hole cache)\n");
159 print_holes(&hole_cache);
160 print_use();
161#endif
162 BUG();
163 }
164
165 list_for_each(cur, &hole_list) {
166 unsigned long newlen;
167
168 hole = list_entry(cur, struct hole, list);
169
170 if(align > DVMA_PAGE_SIZE)
171 newlen = len + ((hole->end - len) & (align-1));
172 else
173 newlen = len;
174
175 if(hole->size > newlen) {
176 hole->end -= newlen;
177 hole->size -= newlen;
178 dvma_entry_use(hole->end) = newlen;
179#ifdef DVMA_DEBUG
180 dvma_allocs++;
181 dvma_alloc_bytes += newlen;
182#endif
183 return hole->end;
184 } else if(hole->size == newlen) {
185 list_del(&(hole->list));
186 list_add(&(hole->list), &hole_cache);
187 dvma_entry_use(hole->start) = newlen;
188#ifdef DVMA_DEBUG
189 dvma_allocs++;
190 dvma_alloc_bytes += newlen;
191#endif
192 return hole->start;
193 }
194
195 }
196
197 printk("unable to find dvma hole!\n");
198 BUG();
199 return 0;
200}
201
202static inline int free_baddr(unsigned long baddr)
203{
204
205 unsigned long len;
206 struct hole *hole;
207 struct list_head *cur;
208 unsigned long orig_baddr;
209
210 orig_baddr = baddr;
211 len = dvma_entry_use(baddr);
212 dvma_entry_use(baddr) = 0;
213 baddr &= DVMA_PAGE_MASK;
214 dvma_unmap_iommu(baddr, len);
215
216#ifdef DVMA_DEBUG
217 dvma_frees++;
218 dvma_free_bytes += len;
219#endif
220
221 list_for_each(cur, &hole_list) {
222 hole = list_entry(cur, struct hole, list);
223
224 if(hole->end == baddr) {
225 hole->end += len;
226 hole->size += len;
227 return 0;
228 } else if(hole->start == (baddr + len)) {
229 hole->start = baddr;
230 hole->size += len;
231 return 0;
232 }
233
234 }
235
236 hole = rmcache();
237
238 hole->start = baddr;
239 hole->end = baddr + len;
240 hole->size = len;
241
242// list_add_tail(&(hole->list), cur);
243 list_add(&(hole->list), cur);
244
245 return 0;
246
247}
248
249void dvma_init(void)
250{
251
252 struct hole *hole;
253 int i;
254
255 INIT_LIST_HEAD(&hole_list);
256 INIT_LIST_HEAD(&hole_cache);
257
258 /* prepare the hole cache */
259 for(i = 0; i < 64; i++)
260 list_add(&(initholes[i].list), &hole_cache);
261
262 hole = rmcache();
263 hole->start = DVMA_START;
264 hole->end = DVMA_END;
265 hole->size = DVMA_SIZE;
266
267 list_add(&(hole->list), &hole_list);
268
269 memset(iommu_use, 0, sizeof(iommu_use));
270
271 dvma_unmap_iommu(DVMA_START, DVMA_SIZE);
272
273#ifdef CONFIG_SUN3
274 sun3_dvma_init();
275#endif
276
277}
278
279inline unsigned long dvma_map_align(unsigned long kaddr, int len, int align)
280{
281
282 unsigned long baddr;
283 unsigned long off;
284
285 if(!len)
286 len = 0x800;
287
288 if(!kaddr || !len) {
289// printk("error: kaddr %lx len %x\n", kaddr, len);
290// *(int *)4 = 0;
291 return 0;
292 }
293
294#ifdef DEBUG
295 printk("dvma_map request %08lx bytes from %08lx\n",
296 len, kaddr);
297#endif
298 off = kaddr & ~DVMA_PAGE_MASK;
299 kaddr &= PAGE_MASK;
300 len += off;
301 len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
302
303 if(align == 0)
304 align = DVMA_PAGE_SIZE;
305 else
306 align = ((align + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
307
308 baddr = get_baddr(len, align);
309// printk("using baddr %lx\n", baddr);
310
311 if(!dvma_map_iommu(kaddr, baddr, len))
312 return (baddr + off);
313
314 printk("dvma_map failed kaddr %lx baddr %lx len %x\n", kaddr, baddr, len);
315 BUG();
316 return 0;
317}
318
319void dvma_unmap(void *baddr)
320{
321 unsigned long addr;
322
323 addr = (unsigned long)baddr;
324 /* check if this is a vme mapping */
325 if(!(addr & 0x00f00000))
326 addr |= 0xf00000;
327
328 free_baddr(addr);
329
330 return;
331
332}
333
334
335void *dvma_malloc_align(unsigned long len, unsigned long align)
336{
337 unsigned long kaddr;
338 unsigned long baddr;
339 unsigned long vaddr;
340
341 if(!len)
342 return NULL;
343
344#ifdef DEBUG
345 printk("dvma_malloc request %lx bytes\n", len);
346#endif
347 len = ((len + (DVMA_PAGE_SIZE-1)) & DVMA_PAGE_MASK);
348
349 if((kaddr = __get_free_pages(GFP_ATOMIC, get_order(len))) == 0)
350 return NULL;
351
352 if((baddr = (unsigned long)dvma_map_align(kaddr, len, align)) == 0) {
353 free_pages(kaddr, get_order(len));
354 return NULL;
355 }
356
357 vaddr = dvma_btov(baddr);
358
359 if(dvma_map_cpu(kaddr, vaddr, len) < 0) {
360 dvma_unmap((void *)baddr);
361 free_pages(kaddr, get_order(len));
362 return NULL;
363 }
364
365#ifdef DEBUG
366 printk("mapped %08lx bytes %08lx kern -> %08lx bus\n",
367 len, kaddr, baddr);
368#endif
369
370 return (void *)vaddr;
371
372}
373
374void dvma_free(void *vaddr)
375{
376
377 return;
378
379}
diff --git a/arch/m68k/sun3/sun3ints.c b/arch/m68k/sun3/sun3ints.c
new file mode 100644
index 000000000000..e62a033cd493
--- /dev/null
+++ b/arch/m68k/sun3/sun3ints.c
@@ -0,0 +1,265 @@
1 /*
2 * linux/arch/m68k/sun3/sun3ints.c -- Sun-3(x) Linux interrupt handling code
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file COPYING in the main directory of this archive
6 * for more details.
7 */
8
9#include <linux/config.h>
10#include <linux/types.h>
11#include <linux/kernel.h>
12#include <linux/sched.h>
13#include <linux/kernel_stat.h>
14#include <linux/interrupt.h>
15#include <asm/segment.h>
16#include <asm/intersil.h>
17#include <asm/oplib.h>
18#include <asm/sun3ints.h>
19#include <linux/seq_file.h>
20
21extern void sun3_leds (unsigned char);
22static irqreturn_t sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp);
23
24void sun3_disable_interrupts(void)
25{
26 sun3_disable_irq(0);
27}
28
29void sun3_enable_interrupts(void)
30{
31 sun3_enable_irq(0);
32}
33
34int led_pattern[8] = {
35 ~(0x80), ~(0x01),
36 ~(0x40), ~(0x02),
37 ~(0x20), ~(0x04),
38 ~(0x10), ~(0x08)
39};
40
41volatile unsigned char* sun3_intreg;
42
43void sun3_insert_irq(irq_node_t **list, irq_node_t *node)
44{
45}
46
47void sun3_delete_irq(irq_node_t **list, void *dev_id)
48{
49}
50
51void sun3_enable_irq(unsigned int irq)
52{
53 *sun3_intreg |= (1<<irq);
54}
55
56void sun3_disable_irq(unsigned int irq)
57{
58 *sun3_intreg &= ~(1<<irq);
59}
60
61inline void sun3_do_irq(int irq, struct pt_regs *fp)
62{
63 kstat_cpu(0).irqs[SYS_IRQS + irq]++;
64 *sun3_intreg &= ~(1<<irq);
65 *sun3_intreg |= (1<<irq);
66}
67
68static irqreturn_t sun3_int7(int irq, void *dev_id, struct pt_regs *fp)
69{
70 sun3_do_irq(irq,fp);
71 if(!(kstat_cpu(0).irqs[SYS_IRQS + irq] % 2000))
72 sun3_leds(led_pattern[(kstat_cpu(0).irqs[SYS_IRQS+irq]%16000)
73 /2000]);
74 return IRQ_HANDLED;
75}
76
77static irqreturn_t sun3_int5(int irq, void *dev_id, struct pt_regs *fp)
78{
79 kstat_cpu(0).irqs[SYS_IRQS + irq]++;
80#ifdef CONFIG_SUN3
81 intersil_clear();
82#endif
83 *sun3_intreg &= ~(1<<irq);
84 *sun3_intreg |= (1<<irq);
85#ifdef CONFIG_SUN3
86 intersil_clear();
87#endif
88 do_timer(fp);
89#ifndef CONFIG_SMP
90 update_process_times(user_mode(fp));
91#endif
92 if(!(kstat_cpu(0).irqs[SYS_IRQS + irq] % 20))
93 sun3_leds(led_pattern[(kstat_cpu(0).irqs[SYS_IRQS+irq]%160)
94 /20]);
95 return IRQ_HANDLED;
96}
97
98/* handle requested ints, excepting 5 and 7, which always do the same
99 thing */
100irqreturn_t (*sun3_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
101 [0] = sun3_inthandle,
102 [1] = sun3_inthandle,
103 [2] = sun3_inthandle,
104 [3] = sun3_inthandle,
105 [4] = sun3_inthandle,
106 [5] = sun3_int5,
107 [6] = sun3_inthandle,
108 [7] = sun3_int7
109};
110
111static const char *dev_names[SYS_IRQS] = {
112 [5] = "timer",
113 [7] = "int7 handler"
114};
115static void *dev_ids[SYS_IRQS];
116static irqreturn_t (*sun3_inthandler[SYS_IRQS])(int, void *, struct pt_regs *) = {
117 [5] = sun3_int5,
118 [7] = sun3_int7
119};
120static irqreturn_t (*sun3_vechandler[SUN3_INT_VECS])(int, void *, struct pt_regs *);
121static void *vec_ids[SUN3_INT_VECS];
122static const char *vec_names[SUN3_INT_VECS];
123static int vec_ints[SUN3_INT_VECS];
124
125
126int show_sun3_interrupts(struct seq_file *p, void *v)
127{
128 int i;
129
130 for(i = 0; i < (SUN3_INT_VECS-1); i++) {
131 if(sun3_vechandler[i] != NULL) {
132 seq_printf(p, "vec %3d: %10u %s\n", i+64,
133 vec_ints[i],
134 (vec_names[i]) ? vec_names[i] :
135 "sun3_vechandler");
136 }
137 }
138
139 return 0;
140}
141
142static irqreturn_t sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp)
143{
144 if(sun3_inthandler[irq] == NULL)
145 panic ("bad interrupt %d received (id %p)\n",irq, dev_id);
146
147 kstat_cpu(0).irqs[SYS_IRQS + irq]++;
148 *sun3_intreg &= ~(1<<irq);
149
150 sun3_inthandler[irq](irq, dev_ids[irq], fp);
151 return IRQ_HANDLED;
152}
153
154static irqreturn_t sun3_vec255(int irq, void *dev_id, struct pt_regs *fp)
155{
156// intersil_clear();
157 return IRQ_HANDLED;
158}
159
160void sun3_init_IRQ(void)
161{
162 int i;
163
164 *sun3_intreg = 1;
165
166 for(i = 0; i < SYS_IRQS; i++)
167 {
168 if(dev_names[i])
169 cpu_request_irq(i, sun3_default_handler[i], 0,
170 dev_names[i], NULL);
171 }
172
173 for(i = 0; i < 192; i++)
174 sun3_vechandler[i] = NULL;
175
176 sun3_vechandler[191] = sun3_vec255;
177}
178
179int sun3_request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
180 unsigned long flags, const char *devname, void *dev_id)
181{
182
183 if(irq < SYS_IRQS) {
184 if(sun3_inthandler[irq] != NULL) {
185 printk("sun3_request_irq: request for irq %d -- already taken!\n", irq);
186 return 1;
187 }
188
189 sun3_inthandler[irq] = handler;
190 dev_ids[irq] = dev_id;
191 dev_names[irq] = devname;
192
193 /* setting devname would be nice */
194 cpu_request_irq(irq, sun3_default_handler[irq], 0, devname,
195 NULL);
196
197 return 0;
198 } else {
199 if((irq >= 64) && (irq <= 255)) {
200 int vec;
201
202 vec = irq - 64;
203 if(sun3_vechandler[vec] != NULL) {
204 printk("sun3_request_irq: request for vec %d -- already taken!\n", irq);
205 return 1;
206 }
207
208 sun3_vechandler[vec] = handler;
209 vec_ids[vec] = dev_id;
210 vec_names[vec] = devname;
211 vec_ints[vec] = 0;
212
213 return 0;
214 }
215 }
216
217 printk("sun3_request_irq: invalid irq %d\n", irq);
218 return 1;
219
220}
221
222void sun3_free_irq(unsigned int irq, void *dev_id)
223{
224
225 if(irq < SYS_IRQS) {
226 if(sun3_inthandler[irq] == NULL)
227 panic("sun3_free_int: attempt to free unused irq %d\n", irq);
228 if(dev_ids[irq] != dev_id)
229 panic("sun3_free_int: incorrect dev_id for irq %d\n", irq);
230
231 sun3_inthandler[irq] = NULL;
232 return;
233 } else if((irq >= 64) && (irq <= 255)) {
234 int vec;
235
236 vec = irq - 64;
237 if(sun3_vechandler[vec] == NULL)
238 panic("sun3_free_int: attempt to free unused vector %d\n", irq);
239 if(vec_ids[irq] != dev_id)
240 panic("sun3_free_int: incorrect dev_id for vec %d\n", irq);
241
242 sun3_vechandler[vec] = NULL;
243 return;
244 } else {
245 panic("sun3_free_irq: invalid irq %d\n", irq);
246 }
247}
248
249irqreturn_t sun3_process_int(int irq, struct pt_regs *regs)
250{
251
252 if((irq >= 64) && (irq <= 255)) {
253 int vec;
254
255 vec = irq - 64;
256 if(sun3_vechandler[vec] == NULL)
257 panic ("bad interrupt vector %d received\n",irq);
258
259 vec_ints[vec]++;
260 return sun3_vechandler[vec](irq, vec_ids[vec], regs);
261 } else {
262 panic("sun3_process_int: unable to handle interrupt vector %d\n",
263 irq);
264 }
265}