diff options
Diffstat (limited to 'arch/m68k/sun3')
-rw-r--r-- | arch/m68k/sun3/Makefile | 7 | ||||
-rw-r--r-- | arch/m68k/sun3/config.c | 188 | ||||
-rw-r--r-- | arch/m68k/sun3/dvma.c | 71 | ||||
-rw-r--r-- | arch/m68k/sun3/idprom.c | 129 | ||||
-rw-r--r-- | arch/m68k/sun3/intersil.c | 76 | ||||
-rw-r--r-- | arch/m68k/sun3/leds.c | 13 | ||||
-rw-r--r-- | arch/m68k/sun3/mmu_emu.c | 427 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/Makefile | 7 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/console.c | 174 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/init.c | 89 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/misc.c | 94 | ||||
-rw-r--r-- | arch/m68k/sun3/prom/printf.c | 61 | ||||
-rw-r--r-- | arch/m68k/sun3/sbus.c | 27 | ||||
-rw-r--r-- | arch/m68k/sun3/sun3_ksyms.c | 13 | ||||
-rw-r--r-- | arch/m68k/sun3/sun3dvma.c | 379 | ||||
-rw-r--r-- | arch/m68k/sun3/sun3ints.c | 265 |
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 | |||
5 | obj-y := sun3_ksyms.o sun3ints.o sun3dvma.o sbus.o idprom.o | ||
6 | |||
7 | 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 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 | |||
34 | extern char _text, _end; | ||
35 | |||
36 | char sun3_reserved_pmeg[SUN3_PMEGS_NUM]; | ||
37 | |||
38 | extern unsigned long sun3_gettimeoffset(void); | ||
39 | extern int show_sun3_interrupts (struct seq_file *, void *); | ||
40 | extern void sun3_sched_init(irqreturn_t (*handler)(int, void *, struct pt_regs *)); | ||
41 | extern void sun3_get_model (char* model); | ||
42 | extern void idprom_init (void); | ||
43 | extern int sun3_hwclk(int set, struct rtc_time *t); | ||
44 | |||
45 | volatile char* clock_va; | ||
46 | extern volatile unsigned char* sun3_intreg; | ||
47 | extern unsigned long availmem; | ||
48 | unsigned long num_pages; | ||
49 | |||
50 | static 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 | |||
62 | void __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. */ | ||
106 | static void sun3_reboot (void) | ||
107 | { | ||
108 | prom_reboot ("vmlinux"); | ||
109 | } | ||
110 | |||
111 | static void sun3_halt (void) | ||
112 | { | ||
113 | prom_halt (); | ||
114 | } | ||
115 | |||
116 | /* sun3 bootmem allocation */ | ||
117 | |||
118 | void __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 | |||
139 | void __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 | |||
177 | void __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 | |||
20 | static unsigned long ptelist[120]; | ||
21 | |||
22 | inline 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 | |||
44 | int 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 | |||
65 | void 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 | |||
18 | struct idprom *idprom; | ||
19 | static 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 | */ | ||
25 | struct 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 | |||
57 | static 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 | |||
80 | void 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). */ | ||
95 | static 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. */ | ||
106 | void __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? */ | ||
28 | unsigned long sun3_gettimeoffset(void) | ||
29 | { | ||
30 | return 1; | ||
31 | } | ||
32 | |||
33 | |||
34 | /* get/set hwclock */ | ||
35 | |||
36 | int 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 | |||
5 | void 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 | |||
30 | extern 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 | |||
49 | unsigned long vmalloc_end; | ||
50 | EXPORT_SYMBOL(vmalloc_end); | ||
51 | |||
52 | unsigned long pmeg_vaddr[PMEGS_NUM]; | ||
53 | unsigned char pmeg_alloc[PMEGS_NUM]; | ||
54 | unsigned 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 */ | ||
58 | struct mm_struct *ctx_alloc[CONTEXTS_NUM] = { | ||
59 | [0] = (struct mm_struct *)0xffffffff | ||
60 | }; | ||
61 | |||
62 | /* has this context been mmdrop'd? */ | ||
63 | static 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. */ | ||
68 | unsigned long rom_pages[256]; | ||
69 | |||
70 | /* Print a PTE value in symbolic form. For debugging. */ | ||
71 | void 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. */ | ||
118 | void 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 | */ | ||
127 | void 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 */ | ||
209 | void 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 */ | ||
245 | unsigned 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 | |||
283 | inline 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 | ||
297 | printk("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 | |||
355 | int 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 | |||
6 | obj-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 | */ | ||
19 | int | ||
20 | prom_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 | */ | ||
34 | int | ||
35 | prom_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. */ | ||
47 | char | ||
48 | prom_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. */ | ||
56 | void | ||
57 | prom_putchar(char c) | ||
58 | { | ||
59 | while(prom_nbputchar(c) == -1) ; | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | /* Query for input device type */ | ||
64 | #if 0 | ||
65 | enum prom_input_device | ||
66 | prom_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" (¤t_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 | ||
116 | enum prom_output_device | ||
117 | prom_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" (¤t_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 | |||
15 | struct linux_romvec *romvec; | ||
16 | enum prom_major_version prom_vers; | ||
17 | unsigned int prom_rev, prom_prev; | ||
18 | |||
19 | /* The root node of the prom device tree. */ | ||
20 | int prom_root_node; | ||
21 | |||
22 | /* Pointer to the device tree operations structure. */ | ||
23 | struct 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 | |||
30 | extern void prom_meminit(void); | ||
31 | extern void prom_ranges_init(void); | ||
32 | |||
33 | void __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'. */ | ||
18 | void | ||
19 | prom_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 | */ | ||
30 | void | ||
31 | prom_cmdline(void) | ||
32 | { | ||
33 | } | ||
34 | |||
35 | /* Drop into the prom, but completely terminate the program. | ||
36 | * No chance of continuing. | ||
37 | */ | ||
38 | void | ||
39 | prom_halt(void) | ||
40 | { | ||
41 | unsigned long flags; | ||
42 | again: | ||
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 | |||
49 | typedef 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 | */ | ||
55 | unsigned char | ||
56 | prom_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. */ | ||
76 | int | ||
77 | prom_version(void) | ||
78 | { | ||
79 | return romvec->pv_romvers; | ||
80 | } | ||
81 | |||
82 | /* Get the prom plugin-revision. */ | ||
83 | int | ||
84 | prom_getrev(void) | ||
85 | { | ||
86 | return prom_rev; | ||
87 | } | ||
88 | |||
89 | /* Get the prom firmware print revision. */ | ||
90 | int | ||
91 | prom_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 | ||
18 | extern int kgdb_initialized; | ||
19 | #endif | ||
20 | |||
21 | static char ppbuf[1024]; | ||
22 | |||
23 | void | ||
24 | prom_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 | |||
16 | int __init sbus_init(void) | ||
17 | { | ||
18 | return 0; | ||
19 | } | ||
20 | |||
21 | void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, | ||
22 | u32 bus_type, int rdonly) | ||
23 | { | ||
24 | return (void *)address; | ||
25 | } | ||
26 | |||
27 | subsys_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 | */ | ||
9 | EXPORT_SYMBOL(dvma_map_align); | ||
10 | EXPORT_SYMBOL(dvma_unmap); | ||
11 | EXPORT_SYMBOL(dvma_malloc_align); | ||
12 | EXPORT_SYMBOL(dvma_free); | ||
13 | EXPORT_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 | ||
21 | extern void dvma_unmap_iommu(unsigned long baddr, int len); | ||
22 | #else | ||
23 | static inline void dvma_unmap_iommu(unsigned long a, int b) | ||
24 | { | ||
25 | } | ||
26 | #endif | ||
27 | |||
28 | #ifdef CONFIG_SUN3 | ||
29 | extern void sun3_dvma_init(void); | ||
30 | #endif | ||
31 | |||
32 | unsigned 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 | |||
38 | struct hole { | ||
39 | unsigned long start; | ||
40 | unsigned long end; | ||
41 | unsigned long size; | ||
42 | struct list_head list; | ||
43 | }; | ||
44 | |||
45 | static struct list_head hole_list; | ||
46 | static struct list_head hole_cache; | ||
47 | static struct hole initholes[64]; | ||
48 | |||
49 | #ifdef DVMA_DEBUG | ||
50 | |||
51 | static unsigned long dvma_allocs; | ||
52 | static unsigned long dvma_frees; | ||
53 | static unsigned long long dvma_alloc_bytes; | ||
54 | static unsigned long long dvma_free_bytes; | ||
55 | |||
56 | static 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 | |||
82 | static 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 | |||
103 | static 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 | |||
132 | static 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 | |||
150 | static 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 | |||
202 | static 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 | |||
249 | void 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 | |||
279 | inline 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 | |||
319 | void 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 | |||
335 | void *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 | |||
374 | void 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 | |||
21 | extern void sun3_leds (unsigned char); | ||
22 | static irqreturn_t sun3_inthandle(int irq, void *dev_id, struct pt_regs *fp); | ||
23 | |||
24 | void sun3_disable_interrupts(void) | ||
25 | { | ||
26 | sun3_disable_irq(0); | ||
27 | } | ||
28 | |||
29 | void sun3_enable_interrupts(void) | ||
30 | { | ||
31 | sun3_enable_irq(0); | ||
32 | } | ||
33 | |||
34 | int led_pattern[8] = { | ||
35 | ~(0x80), ~(0x01), | ||
36 | ~(0x40), ~(0x02), | ||
37 | ~(0x20), ~(0x04), | ||
38 | ~(0x10), ~(0x08) | ||
39 | }; | ||
40 | |||
41 | volatile unsigned char* sun3_intreg; | ||
42 | |||
43 | void sun3_insert_irq(irq_node_t **list, irq_node_t *node) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | void sun3_delete_irq(irq_node_t **list, void *dev_id) | ||
48 | { | ||
49 | } | ||
50 | |||
51 | void sun3_enable_irq(unsigned int irq) | ||
52 | { | ||
53 | *sun3_intreg |= (1<<irq); | ||
54 | } | ||
55 | |||
56 | void sun3_disable_irq(unsigned int irq) | ||
57 | { | ||
58 | *sun3_intreg &= ~(1<<irq); | ||
59 | } | ||
60 | |||
61 | inline 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 | |||
68 | static 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 | |||
77 | static 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 */ | ||
100 | irqreturn_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 | |||
111 | static const char *dev_names[SYS_IRQS] = { | ||
112 | [5] = "timer", | ||
113 | [7] = "int7 handler" | ||
114 | }; | ||
115 | static void *dev_ids[SYS_IRQS]; | ||
116 | static irqreturn_t (*sun3_inthandler[SYS_IRQS])(int, void *, struct pt_regs *) = { | ||
117 | [5] = sun3_int5, | ||
118 | [7] = sun3_int7 | ||
119 | }; | ||
120 | static irqreturn_t (*sun3_vechandler[SUN3_INT_VECS])(int, void *, struct pt_regs *); | ||
121 | static void *vec_ids[SUN3_INT_VECS]; | ||
122 | static const char *vec_names[SUN3_INT_VECS]; | ||
123 | static int vec_ints[SUN3_INT_VECS]; | ||
124 | |||
125 | |||
126 | int 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 | |||
142 | static 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 | |||
154 | static irqreturn_t sun3_vec255(int irq, void *dev_id, struct pt_regs *fp) | ||
155 | { | ||
156 | // intersil_clear(); | ||
157 | return IRQ_HANDLED; | ||
158 | } | ||
159 | |||
160 | void 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 | |||
179 | int 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 | |||
222 | void 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 | |||
249 | irqreturn_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 | } | ||