diff options
Diffstat (limited to 'arch/mips/mm')
33 files changed, 10655 insertions, 0 deletions
diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile new file mode 100644 index 000000000000..f61e038b4440 --- /dev/null +++ b/arch/mips/mm/Makefile | |||
@@ -0,0 +1,44 @@ | |||
1 | # | ||
2 | # Makefile for the Linux/MIPS-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | obj-y += cache.o extable.o fault.o init.o pgtable.o \ | ||
6 | tlbex.o tlbex-fault.o | ||
7 | |||
8 | obj-$(CONFIG_MIPS32) += ioremap.o pgtable-32.o | ||
9 | obj-$(CONFIG_MIPS64) += pgtable-64.o | ||
10 | obj-$(CONFIG_HIGHMEM) += highmem.o | ||
11 | |||
12 | obj-$(CONFIG_CPU_MIPS32) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
13 | obj-$(CONFIG_CPU_MIPS64) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
14 | obj-$(CONFIG_CPU_NEVADA) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
15 | obj-$(CONFIG_CPU_R10000) += c-r4k.o cex-gen.o pg-r4k.o tlb-andes.o | ||
16 | obj-$(CONFIG_CPU_R3000) += c-r3k.o tlb-r3k.o pg-r4k.o | ||
17 | obj-$(CONFIG_CPU_R4300) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
18 | obj-$(CONFIG_CPU_R4X00) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
19 | obj-$(CONFIG_CPU_R5000) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
20 | obj-$(CONFIG_CPU_R5432) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
21 | obj-$(CONFIG_CPU_R8000) += c-r4k.o cex-gen.o pg-r4k.o tlb-r8k.o | ||
22 | obj-$(CONFIG_CPU_RM7000) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
23 | obj-$(CONFIG_CPU_RM9000) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
24 | obj-$(CONFIG_CPU_SB1) += c-sb1.o cerr-sb1.o cex-sb1.o pg-sb1.o \ | ||
25 | tlb-sb1.o | ||
26 | obj-$(CONFIG_CPU_TX39XX) += c-tx39.o pg-r4k.o tlb-r3k.o | ||
27 | obj-$(CONFIG_CPU_TX49XX) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
28 | obj-$(CONFIG_CPU_VR41XX) += c-r4k.o cex-gen.o pg-r4k.o tlb-r4k.o | ||
29 | |||
30 | obj-$(CONFIG_IP22_CPU_SCACHE) += sc-ip22.o | ||
31 | obj-$(CONFIG_R5000_CPU_SCACHE) += sc-r5k.o | ||
32 | obj-$(CONFIG_RM7000_CPU_SCACHE) += sc-rm7k.o | ||
33 | |||
34 | # | ||
35 | # Choose one DMA coherency model | ||
36 | # | ||
37 | ifndef CONFIG_OWN_DMA | ||
38 | obj-$(CONFIG_DMA_COHERENT) += dma-coherent.o | ||
39 | obj-$(CONFIG_DMA_NONCOHERENT) += dma-noncoherent.o | ||
40 | endif | ||
41 | obj-$(CONFIG_DMA_IP27) += dma-ip27.o | ||
42 | obj-$(CONFIG_DMA_IP32) += dma-ip32.o | ||
43 | |||
44 | EXTRA_AFLAGS := $(CFLAGS) | ||
diff --git a/arch/mips/mm/c-r3k.c b/arch/mips/mm/c-r3k.c new file mode 100644 index 000000000000..c659f99eb39a --- /dev/null +++ b/arch/mips/mm/c-r3k.c | |||
@@ -0,0 +1,349 @@ | |||
1 | /* | ||
2 | * r2300.c: R2000 and R3000 specific mmu/cache code. | ||
3 | * | ||
4 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
5 | * | ||
6 | * with a lot of changes to make this thing work for R3000s | ||
7 | * Tx39XX R4k style caches added. HK | ||
8 | * Copyright (C) 1998, 1999, 2000 Harald Koerfgen | ||
9 | * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov | ||
10 | * Copyright (C) 2001, 2004 Maciej W. Rozycki | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/mm.h> | ||
16 | |||
17 | #include <asm/page.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/mmu_context.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/isadep.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/bootinfo.h> | ||
24 | #include <asm/cpu.h> | ||
25 | |||
26 | static unsigned long icache_size, dcache_size; /* Size in bytes */ | ||
27 | static unsigned long icache_lsize, dcache_lsize; /* Size in bytes */ | ||
28 | |||
29 | #undef DEBUG_CACHE | ||
30 | |||
31 | unsigned long __init r3k_cache_size(unsigned long ca_flags) | ||
32 | { | ||
33 | unsigned long flags, status, dummy, size; | ||
34 | volatile unsigned long *p; | ||
35 | |||
36 | p = (volatile unsigned long *) KSEG0; | ||
37 | |||
38 | flags = read_c0_status(); | ||
39 | |||
40 | /* isolate cache space */ | ||
41 | write_c0_status((ca_flags|flags)&~ST0_IEC); | ||
42 | |||
43 | *p = 0xa5a55a5a; | ||
44 | dummy = *p; | ||
45 | status = read_c0_status(); | ||
46 | |||
47 | if (dummy != 0xa5a55a5a || (status & ST0_CM)) { | ||
48 | size = 0; | ||
49 | } else { | ||
50 | for (size = 128; size <= 0x40000; size <<= 1) | ||
51 | *(p + size) = 0; | ||
52 | *p = -1; | ||
53 | for (size = 128; | ||
54 | (size <= 0x40000) && (*(p + size) == 0); | ||
55 | size <<= 1) | ||
56 | ; | ||
57 | if (size > 0x40000) | ||
58 | size = 0; | ||
59 | } | ||
60 | |||
61 | write_c0_status(flags); | ||
62 | |||
63 | return size * sizeof(*p); | ||
64 | } | ||
65 | |||
66 | unsigned long __init r3k_cache_lsize(unsigned long ca_flags) | ||
67 | { | ||
68 | unsigned long flags, status, lsize, i; | ||
69 | volatile unsigned long *p; | ||
70 | |||
71 | p = (volatile unsigned long *) KSEG0; | ||
72 | |||
73 | flags = read_c0_status(); | ||
74 | |||
75 | /* isolate cache space */ | ||
76 | write_c0_status((ca_flags|flags)&~ST0_IEC); | ||
77 | |||
78 | for (i = 0; i < 128; i++) | ||
79 | *(p + i) = 0; | ||
80 | *(volatile unsigned char *)p = 0; | ||
81 | for (lsize = 1; lsize < 128; lsize <<= 1) { | ||
82 | *(p + lsize); | ||
83 | status = read_c0_status(); | ||
84 | if (!(status & ST0_CM)) | ||
85 | break; | ||
86 | } | ||
87 | for (i = 0; i < 128; i += lsize) | ||
88 | *(volatile unsigned char *)(p + i) = 0; | ||
89 | |||
90 | write_c0_status(flags); | ||
91 | |||
92 | return lsize * sizeof(*p); | ||
93 | } | ||
94 | |||
95 | static void __init r3k_probe_cache(void) | ||
96 | { | ||
97 | dcache_size = r3k_cache_size(ST0_ISC); | ||
98 | if (dcache_size) | ||
99 | dcache_lsize = r3k_cache_lsize(ST0_ISC); | ||
100 | |||
101 | icache_size = r3k_cache_size(ST0_ISC|ST0_SWC); | ||
102 | if (icache_size) | ||
103 | icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC); | ||
104 | } | ||
105 | |||
106 | static void r3k_flush_icache_range(unsigned long start, unsigned long end) | ||
107 | { | ||
108 | unsigned long size, i, flags; | ||
109 | volatile unsigned char *p; | ||
110 | |||
111 | size = end - start; | ||
112 | if (size > icache_size || KSEGX(start) != KSEG0) { | ||
113 | start = KSEG0; | ||
114 | size = icache_size; | ||
115 | } | ||
116 | p = (char *)start; | ||
117 | |||
118 | flags = read_c0_status(); | ||
119 | |||
120 | /* isolate cache space */ | ||
121 | write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC); | ||
122 | |||
123 | for (i = 0; i < size; i += 0x080) { | ||
124 | asm ( "sb\t$0, 0x000(%0)\n\t" | ||
125 | "sb\t$0, 0x004(%0)\n\t" | ||
126 | "sb\t$0, 0x008(%0)\n\t" | ||
127 | "sb\t$0, 0x00c(%0)\n\t" | ||
128 | "sb\t$0, 0x010(%0)\n\t" | ||
129 | "sb\t$0, 0x014(%0)\n\t" | ||
130 | "sb\t$0, 0x018(%0)\n\t" | ||
131 | "sb\t$0, 0x01c(%0)\n\t" | ||
132 | "sb\t$0, 0x020(%0)\n\t" | ||
133 | "sb\t$0, 0x024(%0)\n\t" | ||
134 | "sb\t$0, 0x028(%0)\n\t" | ||
135 | "sb\t$0, 0x02c(%0)\n\t" | ||
136 | "sb\t$0, 0x030(%0)\n\t" | ||
137 | "sb\t$0, 0x034(%0)\n\t" | ||
138 | "sb\t$0, 0x038(%0)\n\t" | ||
139 | "sb\t$0, 0x03c(%0)\n\t" | ||
140 | "sb\t$0, 0x040(%0)\n\t" | ||
141 | "sb\t$0, 0x044(%0)\n\t" | ||
142 | "sb\t$0, 0x048(%0)\n\t" | ||
143 | "sb\t$0, 0x04c(%0)\n\t" | ||
144 | "sb\t$0, 0x050(%0)\n\t" | ||
145 | "sb\t$0, 0x054(%0)\n\t" | ||
146 | "sb\t$0, 0x058(%0)\n\t" | ||
147 | "sb\t$0, 0x05c(%0)\n\t" | ||
148 | "sb\t$0, 0x060(%0)\n\t" | ||
149 | "sb\t$0, 0x064(%0)\n\t" | ||
150 | "sb\t$0, 0x068(%0)\n\t" | ||
151 | "sb\t$0, 0x06c(%0)\n\t" | ||
152 | "sb\t$0, 0x070(%0)\n\t" | ||
153 | "sb\t$0, 0x074(%0)\n\t" | ||
154 | "sb\t$0, 0x078(%0)\n\t" | ||
155 | "sb\t$0, 0x07c(%0)\n\t" | ||
156 | : : "r" (p) ); | ||
157 | p += 0x080; | ||
158 | } | ||
159 | |||
160 | write_c0_status(flags); | ||
161 | } | ||
162 | |||
163 | static void r3k_flush_dcache_range(unsigned long start, unsigned long end) | ||
164 | { | ||
165 | unsigned long size, i, flags; | ||
166 | volatile unsigned char *p; | ||
167 | |||
168 | size = end - start; | ||
169 | if (size > dcache_size || KSEGX(start) != KSEG0) { | ||
170 | start = KSEG0; | ||
171 | size = dcache_size; | ||
172 | } | ||
173 | p = (char *)start; | ||
174 | |||
175 | flags = read_c0_status(); | ||
176 | |||
177 | /* isolate cache space */ | ||
178 | write_c0_status((ST0_ISC|flags)&~ST0_IEC); | ||
179 | |||
180 | for (i = 0; i < size; i += 0x080) { | ||
181 | asm ( "sb\t$0, 0x000(%0)\n\t" | ||
182 | "sb\t$0, 0x004(%0)\n\t" | ||
183 | "sb\t$0, 0x008(%0)\n\t" | ||
184 | "sb\t$0, 0x00c(%0)\n\t" | ||
185 | "sb\t$0, 0x010(%0)\n\t" | ||
186 | "sb\t$0, 0x014(%0)\n\t" | ||
187 | "sb\t$0, 0x018(%0)\n\t" | ||
188 | "sb\t$0, 0x01c(%0)\n\t" | ||
189 | "sb\t$0, 0x020(%0)\n\t" | ||
190 | "sb\t$0, 0x024(%0)\n\t" | ||
191 | "sb\t$0, 0x028(%0)\n\t" | ||
192 | "sb\t$0, 0x02c(%0)\n\t" | ||
193 | "sb\t$0, 0x030(%0)\n\t" | ||
194 | "sb\t$0, 0x034(%0)\n\t" | ||
195 | "sb\t$0, 0x038(%0)\n\t" | ||
196 | "sb\t$0, 0x03c(%0)\n\t" | ||
197 | "sb\t$0, 0x040(%0)\n\t" | ||
198 | "sb\t$0, 0x044(%0)\n\t" | ||
199 | "sb\t$0, 0x048(%0)\n\t" | ||
200 | "sb\t$0, 0x04c(%0)\n\t" | ||
201 | "sb\t$0, 0x050(%0)\n\t" | ||
202 | "sb\t$0, 0x054(%0)\n\t" | ||
203 | "sb\t$0, 0x058(%0)\n\t" | ||
204 | "sb\t$0, 0x05c(%0)\n\t" | ||
205 | "sb\t$0, 0x060(%0)\n\t" | ||
206 | "sb\t$0, 0x064(%0)\n\t" | ||
207 | "sb\t$0, 0x068(%0)\n\t" | ||
208 | "sb\t$0, 0x06c(%0)\n\t" | ||
209 | "sb\t$0, 0x070(%0)\n\t" | ||
210 | "sb\t$0, 0x074(%0)\n\t" | ||
211 | "sb\t$0, 0x078(%0)\n\t" | ||
212 | "sb\t$0, 0x07c(%0)\n\t" | ||
213 | : : "r" (p) ); | ||
214 | p += 0x080; | ||
215 | } | ||
216 | |||
217 | write_c0_status(flags); | ||
218 | } | ||
219 | |||
220 | static inline unsigned long get_phys_page (unsigned long addr, | ||
221 | struct mm_struct *mm) | ||
222 | { | ||
223 | pgd_t *pgd; | ||
224 | pmd_t *pmd; | ||
225 | pte_t *pte; | ||
226 | unsigned long physpage; | ||
227 | |||
228 | pgd = pgd_offset(mm, addr); | ||
229 | pmd = pmd_offset(pgd, addr); | ||
230 | pte = pte_offset(pmd, addr); | ||
231 | |||
232 | if ((physpage = pte_val(*pte)) & _PAGE_VALID) | ||
233 | return KSEG0ADDR(physpage & PAGE_MASK); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static inline void r3k_flush_cache_all(void) | ||
239 | { | ||
240 | } | ||
241 | |||
242 | static inline void r3k___flush_cache_all(void) | ||
243 | { | ||
244 | r3k_flush_dcache_range(KSEG0, KSEG0 + dcache_size); | ||
245 | r3k_flush_icache_range(KSEG0, KSEG0 + icache_size); | ||
246 | } | ||
247 | |||
248 | static void r3k_flush_cache_mm(struct mm_struct *mm) | ||
249 | { | ||
250 | } | ||
251 | |||
252 | static void r3k_flush_cache_range(struct vm_area_struct *vma, | ||
253 | unsigned long start, unsigned long end) | ||
254 | { | ||
255 | } | ||
256 | |||
257 | static void r3k_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn) | ||
258 | { | ||
259 | } | ||
260 | |||
261 | static void r3k_flush_data_cache_page(unsigned long addr) | ||
262 | { | ||
263 | } | ||
264 | |||
265 | static void r3k_flush_icache_page(struct vm_area_struct *vma, struct page *page) | ||
266 | { | ||
267 | struct mm_struct *mm = vma->vm_mm; | ||
268 | unsigned long physpage; | ||
269 | |||
270 | if (cpu_context(smp_processor_id(), mm) == 0) | ||
271 | return; | ||
272 | |||
273 | if (!(vma->vm_flags & VM_EXEC)) | ||
274 | return; | ||
275 | |||
276 | #ifdef DEBUG_CACHE | ||
277 | printk("cpage[%d,%08lx]", cpu_context(smp_processor_id(), mm), page); | ||
278 | #endif | ||
279 | |||
280 | physpage = (unsigned long) page_address(page); | ||
281 | if (physpage) | ||
282 | r3k_flush_icache_range(physpage, physpage + PAGE_SIZE); | ||
283 | } | ||
284 | |||
285 | static void r3k_flush_cache_sigtramp(unsigned long addr) | ||
286 | { | ||
287 | unsigned long flags; | ||
288 | |||
289 | #ifdef DEBUG_CACHE | ||
290 | printk("csigtramp[%08lx]", addr); | ||
291 | #endif | ||
292 | |||
293 | flags = read_c0_status(); | ||
294 | |||
295 | write_c0_status(flags&~ST0_IEC); | ||
296 | |||
297 | /* Fill the TLB to avoid an exception with caches isolated. */ | ||
298 | asm ( "lw\t$0, 0x000(%0)\n\t" | ||
299 | "lw\t$0, 0x004(%0)\n\t" | ||
300 | : : "r" (addr) ); | ||
301 | |||
302 | write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC); | ||
303 | |||
304 | asm ( "sb\t$0, 0x000(%0)\n\t" | ||
305 | "sb\t$0, 0x004(%0)\n\t" | ||
306 | : : "r" (addr) ); | ||
307 | |||
308 | write_c0_status(flags); | ||
309 | } | ||
310 | |||
311 | static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size) | ||
312 | { | ||
313 | /* Catch bad driver code */ | ||
314 | BUG_ON(size == 0); | ||
315 | |||
316 | iob(); | ||
317 | r3k_flush_dcache_range(start, start + size); | ||
318 | } | ||
319 | |||
320 | void __init ld_mmu_r23000(void) | ||
321 | { | ||
322 | extern void build_clear_page(void); | ||
323 | extern void build_copy_page(void); | ||
324 | |||
325 | r3k_probe_cache(); | ||
326 | |||
327 | flush_cache_all = r3k_flush_cache_all; | ||
328 | __flush_cache_all = r3k___flush_cache_all; | ||
329 | flush_cache_mm = r3k_flush_cache_mm; | ||
330 | flush_cache_range = r3k_flush_cache_range; | ||
331 | flush_cache_page = r3k_flush_cache_page; | ||
332 | flush_icache_page = r3k_flush_icache_page; | ||
333 | flush_icache_range = r3k_flush_icache_range; | ||
334 | |||
335 | flush_cache_sigtramp = r3k_flush_cache_sigtramp; | ||
336 | flush_data_cache_page = r3k_flush_data_cache_page; | ||
337 | |||
338 | _dma_cache_wback_inv = r3k_dma_cache_wback_inv; | ||
339 | _dma_cache_wback = r3k_dma_cache_wback_inv; | ||
340 | _dma_cache_inv = r3k_dma_cache_wback_inv; | ||
341 | |||
342 | printk("Primary instruction cache %ldkB, linesize %ld bytes.\n", | ||
343 | icache_size >> 10, icache_lsize); | ||
344 | printk("Primary data cache %ldkB, linesize %ld bytes.\n", | ||
345 | dcache_size >> 10, dcache_lsize); | ||
346 | |||
347 | build_clear_page(); | ||
348 | build_copy_page(); | ||
349 | } | ||
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c new file mode 100644 index 000000000000..a03ebb2cba67 --- /dev/null +++ b/arch/mips/mm/c-r4k.c | |||
@@ -0,0 +1,1260 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
7 | * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Ralf Baechle (ralf@gnu.org) | ||
8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | ||
9 | */ | ||
10 | #include <linux/config.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/bitops.h> | ||
16 | |||
17 | #include <asm/bcache.h> | ||
18 | #include <asm/bootinfo.h> | ||
19 | #include <asm/cacheops.h> | ||
20 | #include <asm/cpu.h> | ||
21 | #include <asm/cpu-features.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/page.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/r4kcache.h> | ||
26 | #include <asm/system.h> | ||
27 | #include <asm/mmu_context.h> | ||
28 | #include <asm/war.h> | ||
29 | |||
30 | static unsigned long icache_size, dcache_size, scache_size; | ||
31 | |||
32 | /* | ||
33 | * Dummy cache handling routines for machines without boardcaches | ||
34 | */ | ||
35 | static void no_sc_noop(void) {} | ||
36 | |||
37 | static struct bcache_ops no_sc_ops = { | ||
38 | .bc_enable = (void *)no_sc_noop, | ||
39 | .bc_disable = (void *)no_sc_noop, | ||
40 | .bc_wback_inv = (void *)no_sc_noop, | ||
41 | .bc_inv = (void *)no_sc_noop | ||
42 | }; | ||
43 | |||
44 | struct bcache_ops *bcops = &no_sc_ops; | ||
45 | |||
46 | #define cpu_is_r4600_v1_x() ((read_c0_prid() & 0xfffffff0) == 0x2010) | ||
47 | #define cpu_is_r4600_v2_x() ((read_c0_prid() & 0xfffffff0) == 0x2020) | ||
48 | |||
49 | #define R4600_HIT_CACHEOP_WAR_IMPL \ | ||
50 | do { \ | ||
51 | if (R4600_V2_HIT_CACHEOP_WAR && cpu_is_r4600_v2_x()) \ | ||
52 | *(volatile unsigned long *)CKSEG1; \ | ||
53 | if (R4600_V1_HIT_CACHEOP_WAR) \ | ||
54 | __asm__ __volatile__("nop;nop;nop;nop"); \ | ||
55 | } while (0) | ||
56 | |||
57 | static void (*r4k_blast_dcache_page)(unsigned long addr); | ||
58 | |||
59 | static inline void r4k_blast_dcache_page_dc32(unsigned long addr) | ||
60 | { | ||
61 | R4600_HIT_CACHEOP_WAR_IMPL; | ||
62 | blast_dcache32_page(addr); | ||
63 | } | ||
64 | |||
65 | static inline void r4k_blast_dcache_page_setup(void) | ||
66 | { | ||
67 | unsigned long dc_lsize = cpu_dcache_line_size(); | ||
68 | |||
69 | if (dc_lsize == 16) | ||
70 | r4k_blast_dcache_page = blast_dcache16_page; | ||
71 | else if (dc_lsize == 32) | ||
72 | r4k_blast_dcache_page = r4k_blast_dcache_page_dc32; | ||
73 | } | ||
74 | |||
75 | static void (* r4k_blast_dcache_page_indexed)(unsigned long addr); | ||
76 | |||
77 | static inline void r4k_blast_dcache_page_indexed_setup(void) | ||
78 | { | ||
79 | unsigned long dc_lsize = cpu_dcache_line_size(); | ||
80 | |||
81 | if (dc_lsize == 16) | ||
82 | r4k_blast_dcache_page_indexed = blast_dcache16_page_indexed; | ||
83 | else if (dc_lsize == 32) | ||
84 | r4k_blast_dcache_page_indexed = blast_dcache32_page_indexed; | ||
85 | } | ||
86 | |||
87 | static void (* r4k_blast_dcache)(void); | ||
88 | |||
89 | static inline void r4k_blast_dcache_setup(void) | ||
90 | { | ||
91 | unsigned long dc_lsize = cpu_dcache_line_size(); | ||
92 | |||
93 | if (dc_lsize == 16) | ||
94 | r4k_blast_dcache = blast_dcache16; | ||
95 | else if (dc_lsize == 32) | ||
96 | r4k_blast_dcache = blast_dcache32; | ||
97 | } | ||
98 | |||
99 | /* force code alignment (used for TX49XX_ICACHE_INDEX_INV_WAR) */ | ||
100 | #define JUMP_TO_ALIGN(order) \ | ||
101 | __asm__ __volatile__( \ | ||
102 | "b\t1f\n\t" \ | ||
103 | ".align\t" #order "\n\t" \ | ||
104 | "1:\n\t" \ | ||
105 | ) | ||
106 | #define CACHE32_UNROLL32_ALIGN JUMP_TO_ALIGN(10) /* 32 * 32 = 1024 */ | ||
107 | #define CACHE32_UNROLL32_ALIGN2 JUMP_TO_ALIGN(11) | ||
108 | |||
109 | static inline void blast_r4600_v1_icache32(void) | ||
110 | { | ||
111 | unsigned long flags; | ||
112 | |||
113 | local_irq_save(flags); | ||
114 | blast_icache32(); | ||
115 | local_irq_restore(flags); | ||
116 | } | ||
117 | |||
118 | static inline void tx49_blast_icache32(void) | ||
119 | { | ||
120 | unsigned long start = INDEX_BASE; | ||
121 | unsigned long end = start + current_cpu_data.icache.waysize; | ||
122 | unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; | ||
123 | unsigned long ws_end = current_cpu_data.icache.ways << | ||
124 | current_cpu_data.icache.waybit; | ||
125 | unsigned long ws, addr; | ||
126 | |||
127 | CACHE32_UNROLL32_ALIGN2; | ||
128 | /* I'm in even chunk. blast odd chunks */ | ||
129 | for (ws = 0; ws < ws_end; ws += ws_inc) | ||
130 | for (addr = start + 0x400; addr < end; addr += 0x400 * 2) | ||
131 | cache32_unroll32(addr|ws,Index_Invalidate_I); | ||
132 | CACHE32_UNROLL32_ALIGN; | ||
133 | /* I'm in odd chunk. blast even chunks */ | ||
134 | for (ws = 0; ws < ws_end; ws += ws_inc) | ||
135 | for (addr = start; addr < end; addr += 0x400 * 2) | ||
136 | cache32_unroll32(addr|ws,Index_Invalidate_I); | ||
137 | } | ||
138 | |||
139 | static inline void blast_icache32_r4600_v1_page_indexed(unsigned long page) | ||
140 | { | ||
141 | unsigned long flags; | ||
142 | |||
143 | local_irq_save(flags); | ||
144 | blast_icache32_page_indexed(page); | ||
145 | local_irq_restore(flags); | ||
146 | } | ||
147 | |||
148 | static inline void tx49_blast_icache32_page_indexed(unsigned long page) | ||
149 | { | ||
150 | unsigned long start = page; | ||
151 | unsigned long end = start + PAGE_SIZE; | ||
152 | unsigned long ws_inc = 1UL << current_cpu_data.icache.waybit; | ||
153 | unsigned long ws_end = current_cpu_data.icache.ways << | ||
154 | current_cpu_data.icache.waybit; | ||
155 | unsigned long ws, addr; | ||
156 | |||
157 | CACHE32_UNROLL32_ALIGN2; | ||
158 | /* I'm in even chunk. blast odd chunks */ | ||
159 | for (ws = 0; ws < ws_end; ws += ws_inc) | ||
160 | for (addr = start + 0x400; addr < end; addr += 0x400 * 2) | ||
161 | cache32_unroll32(addr|ws,Index_Invalidate_I); | ||
162 | CACHE32_UNROLL32_ALIGN; | ||
163 | /* I'm in odd chunk. blast even chunks */ | ||
164 | for (ws = 0; ws < ws_end; ws += ws_inc) | ||
165 | for (addr = start; addr < end; addr += 0x400 * 2) | ||
166 | cache32_unroll32(addr|ws,Index_Invalidate_I); | ||
167 | } | ||
168 | |||
169 | static void (* r4k_blast_icache_page)(unsigned long addr); | ||
170 | |||
171 | static inline void r4k_blast_icache_page_setup(void) | ||
172 | { | ||
173 | unsigned long ic_lsize = cpu_icache_line_size(); | ||
174 | |||
175 | if (ic_lsize == 16) | ||
176 | r4k_blast_icache_page = blast_icache16_page; | ||
177 | else if (ic_lsize == 32) | ||
178 | r4k_blast_icache_page = blast_icache32_page; | ||
179 | else if (ic_lsize == 64) | ||
180 | r4k_blast_icache_page = blast_icache64_page; | ||
181 | } | ||
182 | |||
183 | |||
184 | static void (* r4k_blast_icache_page_indexed)(unsigned long addr); | ||
185 | |||
186 | static inline void r4k_blast_icache_page_indexed_setup(void) | ||
187 | { | ||
188 | unsigned long ic_lsize = cpu_icache_line_size(); | ||
189 | |||
190 | if (ic_lsize == 16) | ||
191 | r4k_blast_icache_page_indexed = blast_icache16_page_indexed; | ||
192 | else if (ic_lsize == 32) { | ||
193 | if (TX49XX_ICACHE_INDEX_INV_WAR) | ||
194 | r4k_blast_icache_page_indexed = | ||
195 | tx49_blast_icache32_page_indexed; | ||
196 | else if (R4600_V1_INDEX_ICACHEOP_WAR && cpu_is_r4600_v1_x()) | ||
197 | r4k_blast_icache_page_indexed = | ||
198 | blast_icache32_r4600_v1_page_indexed; | ||
199 | else | ||
200 | r4k_blast_icache_page_indexed = | ||
201 | blast_icache32_page_indexed; | ||
202 | } else if (ic_lsize == 64) | ||
203 | r4k_blast_icache_page_indexed = blast_icache64_page_indexed; | ||
204 | } | ||
205 | |||
206 | static void (* r4k_blast_icache)(void); | ||
207 | |||
208 | static inline void r4k_blast_icache_setup(void) | ||
209 | { | ||
210 | unsigned long ic_lsize = cpu_icache_line_size(); | ||
211 | |||
212 | if (ic_lsize == 16) | ||
213 | r4k_blast_icache = blast_icache16; | ||
214 | else if (ic_lsize == 32) { | ||
215 | if (R4600_V1_INDEX_ICACHEOP_WAR && cpu_is_r4600_v1_x()) | ||
216 | r4k_blast_icache = blast_r4600_v1_icache32; | ||
217 | else if (TX49XX_ICACHE_INDEX_INV_WAR) | ||
218 | r4k_blast_icache = tx49_blast_icache32; | ||
219 | else | ||
220 | r4k_blast_icache = blast_icache32; | ||
221 | } else if (ic_lsize == 64) | ||
222 | r4k_blast_icache = blast_icache64; | ||
223 | } | ||
224 | |||
225 | static void (* r4k_blast_scache_page)(unsigned long addr); | ||
226 | |||
227 | static inline void r4k_blast_scache_page_setup(void) | ||
228 | { | ||
229 | unsigned long sc_lsize = cpu_scache_line_size(); | ||
230 | |||
231 | if (sc_lsize == 16) | ||
232 | r4k_blast_scache_page = blast_scache16_page; | ||
233 | else if (sc_lsize == 32) | ||
234 | r4k_blast_scache_page = blast_scache32_page; | ||
235 | else if (sc_lsize == 64) | ||
236 | r4k_blast_scache_page = blast_scache64_page; | ||
237 | else if (sc_lsize == 128) | ||
238 | r4k_blast_scache_page = blast_scache128_page; | ||
239 | } | ||
240 | |||
241 | static void (* r4k_blast_scache_page_indexed)(unsigned long addr); | ||
242 | |||
243 | static inline void r4k_blast_scache_page_indexed_setup(void) | ||
244 | { | ||
245 | unsigned long sc_lsize = cpu_scache_line_size(); | ||
246 | |||
247 | if (sc_lsize == 16) | ||
248 | r4k_blast_scache_page_indexed = blast_scache16_page_indexed; | ||
249 | else if (sc_lsize == 32) | ||
250 | r4k_blast_scache_page_indexed = blast_scache32_page_indexed; | ||
251 | else if (sc_lsize == 64) | ||
252 | r4k_blast_scache_page_indexed = blast_scache64_page_indexed; | ||
253 | else if (sc_lsize == 128) | ||
254 | r4k_blast_scache_page_indexed = blast_scache128_page_indexed; | ||
255 | } | ||
256 | |||
257 | static void (* r4k_blast_scache)(void); | ||
258 | |||
259 | static inline void r4k_blast_scache_setup(void) | ||
260 | { | ||
261 | unsigned long sc_lsize = cpu_scache_line_size(); | ||
262 | |||
263 | if (sc_lsize == 16) | ||
264 | r4k_blast_scache = blast_scache16; | ||
265 | else if (sc_lsize == 32) | ||
266 | r4k_blast_scache = blast_scache32; | ||
267 | else if (sc_lsize == 64) | ||
268 | r4k_blast_scache = blast_scache64; | ||
269 | else if (sc_lsize == 128) | ||
270 | r4k_blast_scache = blast_scache128; | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * This is former mm's flush_cache_all() which really should be | ||
275 | * flush_cache_vunmap these days ... | ||
276 | */ | ||
277 | static inline void local_r4k_flush_cache_all(void * args) | ||
278 | { | ||
279 | r4k_blast_dcache(); | ||
280 | r4k_blast_icache(); | ||
281 | } | ||
282 | |||
283 | static void r4k_flush_cache_all(void) | ||
284 | { | ||
285 | if (!cpu_has_dc_aliases) | ||
286 | return; | ||
287 | |||
288 | on_each_cpu(local_r4k_flush_cache_all, NULL, 1, 1); | ||
289 | } | ||
290 | |||
291 | static inline void local_r4k___flush_cache_all(void * args) | ||
292 | { | ||
293 | r4k_blast_dcache(); | ||
294 | r4k_blast_icache(); | ||
295 | |||
296 | switch (current_cpu_data.cputype) { | ||
297 | case CPU_R4000SC: | ||
298 | case CPU_R4000MC: | ||
299 | case CPU_R4400SC: | ||
300 | case CPU_R4400MC: | ||
301 | case CPU_R10000: | ||
302 | case CPU_R12000: | ||
303 | r4k_blast_scache(); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | static void r4k___flush_cache_all(void) | ||
308 | { | ||
309 | on_each_cpu(local_r4k___flush_cache_all, NULL, 1, 1); | ||
310 | } | ||
311 | |||
312 | static inline void local_r4k_flush_cache_range(void * args) | ||
313 | { | ||
314 | struct vm_area_struct *vma = args; | ||
315 | int exec; | ||
316 | |||
317 | if (!(cpu_context(smp_processor_id(), vma->vm_mm))) | ||
318 | return; | ||
319 | |||
320 | exec = vma->vm_flags & VM_EXEC; | ||
321 | if (cpu_has_dc_aliases || exec) | ||
322 | r4k_blast_dcache(); | ||
323 | if (exec) | ||
324 | r4k_blast_icache(); | ||
325 | } | ||
326 | |||
327 | static void r4k_flush_cache_range(struct vm_area_struct *vma, | ||
328 | unsigned long start, unsigned long end) | ||
329 | { | ||
330 | on_each_cpu(local_r4k_flush_cache_range, vma, 1, 1); | ||
331 | } | ||
332 | |||
333 | static inline void local_r4k_flush_cache_mm(void * args) | ||
334 | { | ||
335 | struct mm_struct *mm = args; | ||
336 | |||
337 | if (!cpu_context(smp_processor_id(), mm)) | ||
338 | return; | ||
339 | |||
340 | r4k_blast_dcache(); | ||
341 | r4k_blast_icache(); | ||
342 | |||
343 | /* | ||
344 | * Kludge alert. For obscure reasons R4000SC and R4400SC go nuts if we | ||
345 | * only flush the primary caches but R10000 and R12000 behave sane ... | ||
346 | */ | ||
347 | if (current_cpu_data.cputype == CPU_R4000SC || | ||
348 | current_cpu_data.cputype == CPU_R4000MC || | ||
349 | current_cpu_data.cputype == CPU_R4400SC || | ||
350 | current_cpu_data.cputype == CPU_R4400MC) | ||
351 | r4k_blast_scache(); | ||
352 | } | ||
353 | |||
354 | static void r4k_flush_cache_mm(struct mm_struct *mm) | ||
355 | { | ||
356 | if (!cpu_has_dc_aliases) | ||
357 | return; | ||
358 | |||
359 | on_each_cpu(local_r4k_flush_cache_mm, mm, 1, 1); | ||
360 | } | ||
361 | |||
362 | struct flush_cache_page_args { | ||
363 | struct vm_area_struct *vma; | ||
364 | unsigned long page; | ||
365 | }; | ||
366 | |||
367 | static inline void local_r4k_flush_cache_page(void *args) | ||
368 | { | ||
369 | struct flush_cache_page_args *fcp_args = args; | ||
370 | struct vm_area_struct *vma = fcp_args->vma; | ||
371 | unsigned long page = fcp_args->page; | ||
372 | int exec = vma->vm_flags & VM_EXEC; | ||
373 | struct mm_struct *mm = vma->vm_mm; | ||
374 | pgd_t *pgdp; | ||
375 | pmd_t *pmdp; | ||
376 | pte_t *ptep; | ||
377 | |||
378 | page &= PAGE_MASK; | ||
379 | pgdp = pgd_offset(mm, page); | ||
380 | pmdp = pmd_offset(pgdp, page); | ||
381 | ptep = pte_offset(pmdp, page); | ||
382 | |||
383 | /* | ||
384 | * If the page isn't marked valid, the page cannot possibly be | ||
385 | * in the cache. | ||
386 | */ | ||
387 | if (!(pte_val(*ptep) & _PAGE_PRESENT)) | ||
388 | return; | ||
389 | |||
390 | /* | ||
391 | * Doing flushes for another ASID than the current one is | ||
392 | * too difficult since stupid R4k caches do a TLB translation | ||
393 | * for every cache flush operation. So we do indexed flushes | ||
394 | * in that case, which doesn't overly flush the cache too much. | ||
395 | */ | ||
396 | if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { | ||
397 | if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) { | ||
398 | r4k_blast_dcache_page(page); | ||
399 | if (exec && !cpu_icache_snoops_remote_store) | ||
400 | r4k_blast_scache_page(page); | ||
401 | } | ||
402 | if (exec) | ||
403 | r4k_blast_icache_page(page); | ||
404 | |||
405 | return; | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * Do indexed flush, too much work to get the (possible) TLB refills | ||
410 | * to work correctly. | ||
411 | */ | ||
412 | page = INDEX_BASE + (page & (dcache_size - 1)); | ||
413 | if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) { | ||
414 | r4k_blast_dcache_page_indexed(page); | ||
415 | if (exec && !cpu_icache_snoops_remote_store) | ||
416 | r4k_blast_scache_page_indexed(page); | ||
417 | } | ||
418 | if (exec) { | ||
419 | if (cpu_has_vtag_icache) { | ||
420 | int cpu = smp_processor_id(); | ||
421 | |||
422 | if (cpu_context(cpu, vma->vm_mm) != 0) | ||
423 | drop_mmu_context(vma->vm_mm, cpu); | ||
424 | } else | ||
425 | r4k_blast_icache_page_indexed(page); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | static void r4k_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn) | ||
430 | { | ||
431 | struct flush_cache_page_args args; | ||
432 | |||
433 | /* | ||
434 | * If ownes no valid ASID yet, cannot possibly have gotten | ||
435 | * this page into the cache. | ||
436 | */ | ||
437 | if (cpu_context(smp_processor_id(), vma->vm_mm) == 0) | ||
438 | return; | ||
439 | |||
440 | args.vma = vma; | ||
441 | args.page = page; | ||
442 | |||
443 | on_each_cpu(local_r4k_flush_cache_page, &args, 1, 1); | ||
444 | } | ||
445 | |||
446 | static inline void local_r4k_flush_data_cache_page(void * addr) | ||
447 | { | ||
448 | r4k_blast_dcache_page((unsigned long) addr); | ||
449 | } | ||
450 | |||
451 | static void r4k_flush_data_cache_page(unsigned long addr) | ||
452 | { | ||
453 | on_each_cpu(local_r4k_flush_data_cache_page, (void *) addr, 1, 1); | ||
454 | } | ||
455 | |||
456 | struct flush_icache_range_args { | ||
457 | unsigned long start; | ||
458 | unsigned long end; | ||
459 | }; | ||
460 | |||
461 | static inline void local_r4k_flush_icache_range(void *args) | ||
462 | { | ||
463 | struct flush_icache_range_args *fir_args = args; | ||
464 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
465 | unsigned long ic_lsize = current_cpu_data.icache.linesz; | ||
466 | unsigned long sc_lsize = current_cpu_data.scache.linesz; | ||
467 | unsigned long start = fir_args->start; | ||
468 | unsigned long end = fir_args->end; | ||
469 | unsigned long addr, aend; | ||
470 | |||
471 | if (!cpu_has_ic_fills_f_dc) { | ||
472 | if (end - start > dcache_size) { | ||
473 | r4k_blast_dcache(); | ||
474 | } else { | ||
475 | addr = start & ~(dc_lsize - 1); | ||
476 | aend = (end - 1) & ~(dc_lsize - 1); | ||
477 | |||
478 | while (1) { | ||
479 | /* Hit_Writeback_Inv_D */ | ||
480 | protected_writeback_dcache_line(addr); | ||
481 | if (addr == aend) | ||
482 | break; | ||
483 | addr += dc_lsize; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | if (!cpu_icache_snoops_remote_store) { | ||
488 | if (end - start > scache_size) { | ||
489 | r4k_blast_scache(); | ||
490 | } else { | ||
491 | addr = start & ~(sc_lsize - 1); | ||
492 | aend = (end - 1) & ~(sc_lsize - 1); | ||
493 | |||
494 | while (1) { | ||
495 | /* Hit_Writeback_Inv_D */ | ||
496 | protected_writeback_scache_line(addr); | ||
497 | if (addr == aend) | ||
498 | break; | ||
499 | addr += sc_lsize; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | |||
505 | if (end - start > icache_size) | ||
506 | r4k_blast_icache(); | ||
507 | else { | ||
508 | addr = start & ~(ic_lsize - 1); | ||
509 | aend = (end - 1) & ~(ic_lsize - 1); | ||
510 | while (1) { | ||
511 | /* Hit_Invalidate_I */ | ||
512 | protected_flush_icache_line(addr); | ||
513 | if (addr == aend) | ||
514 | break; | ||
515 | addr += ic_lsize; | ||
516 | } | ||
517 | } | ||
518 | } | ||
519 | |||
520 | static void r4k_flush_icache_range(unsigned long start, unsigned long end) | ||
521 | { | ||
522 | struct flush_icache_range_args args; | ||
523 | |||
524 | args.start = start; | ||
525 | args.end = end; | ||
526 | |||
527 | on_each_cpu(local_r4k_flush_icache_range, &args, 1, 1); | ||
528 | } | ||
529 | |||
530 | /* | ||
531 | * Ok, this seriously sucks. We use them to flush a user page but don't | ||
532 | * know the virtual address, so we have to blast away the whole icache | ||
533 | * which is significantly more expensive than the real thing. Otoh we at | ||
534 | * least know the kernel address of the page so we can flush it | ||
535 | * selectivly. | ||
536 | */ | ||
537 | |||
538 | struct flush_icache_page_args { | ||
539 | struct vm_area_struct *vma; | ||
540 | struct page *page; | ||
541 | }; | ||
542 | |||
543 | static inline void local_r4k_flush_icache_page(void *args) | ||
544 | { | ||
545 | struct flush_icache_page_args *fip_args = args; | ||
546 | struct vm_area_struct *vma = fip_args->vma; | ||
547 | struct page *page = fip_args->page; | ||
548 | |||
549 | /* | ||
550 | * Tricky ... Because we don't know the virtual address we've got the | ||
551 | * choice of either invalidating the entire primary and secondary | ||
552 | * caches or invalidating the secondary caches also. With the subset | ||
553 | * enforcment on R4000SC, R4400SC, R10000 and R12000 invalidating the | ||
554 | * secondary cache will result in any entries in the primary caches | ||
555 | * also getting invalidated which hopefully is a bit more economical. | ||
556 | */ | ||
557 | if (cpu_has_subset_pcaches) { | ||
558 | unsigned long addr = (unsigned long) page_address(page); | ||
559 | |||
560 | r4k_blast_scache_page(addr); | ||
561 | ClearPageDcacheDirty(page); | ||
562 | |||
563 | return; | ||
564 | } | ||
565 | |||
566 | if (!cpu_has_ic_fills_f_dc) { | ||
567 | unsigned long addr = (unsigned long) page_address(page); | ||
568 | r4k_blast_dcache_page(addr); | ||
569 | if (!cpu_icache_snoops_remote_store) | ||
570 | r4k_blast_scache_page(addr); | ||
571 | ClearPageDcacheDirty(page); | ||
572 | } | ||
573 | |||
574 | /* | ||
575 | * We're not sure of the virtual address(es) involved here, so | ||
576 | * we have to flush the entire I-cache. | ||
577 | */ | ||
578 | if (cpu_has_vtag_icache) { | ||
579 | int cpu = smp_processor_id(); | ||
580 | |||
581 | if (cpu_context(cpu, vma->vm_mm) != 0) | ||
582 | drop_mmu_context(vma->vm_mm, cpu); | ||
583 | } else | ||
584 | r4k_blast_icache(); | ||
585 | } | ||
586 | |||
587 | static void r4k_flush_icache_page(struct vm_area_struct *vma, | ||
588 | struct page *page) | ||
589 | { | ||
590 | struct flush_icache_page_args args; | ||
591 | |||
592 | /* | ||
593 | * If there's no context yet, or the page isn't executable, no I-cache | ||
594 | * flush is needed. | ||
595 | */ | ||
596 | if (!(vma->vm_flags & VM_EXEC)) | ||
597 | return; | ||
598 | |||
599 | args.vma = vma; | ||
600 | args.page = page; | ||
601 | |||
602 | on_each_cpu(local_r4k_flush_icache_page, &args, 1, 1); | ||
603 | } | ||
604 | |||
605 | |||
606 | #ifdef CONFIG_DMA_NONCOHERENT | ||
607 | |||
608 | static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size) | ||
609 | { | ||
610 | unsigned long end, a; | ||
611 | |||
612 | /* Catch bad driver code */ | ||
613 | BUG_ON(size == 0); | ||
614 | |||
615 | if (cpu_has_subset_pcaches) { | ||
616 | unsigned long sc_lsize = current_cpu_data.scache.linesz; | ||
617 | |||
618 | if (size >= scache_size) { | ||
619 | r4k_blast_scache(); | ||
620 | return; | ||
621 | } | ||
622 | |||
623 | a = addr & ~(sc_lsize - 1); | ||
624 | end = (addr + size - 1) & ~(sc_lsize - 1); | ||
625 | while (1) { | ||
626 | flush_scache_line(a); /* Hit_Writeback_Inv_SD */ | ||
627 | if (a == end) | ||
628 | break; | ||
629 | a += sc_lsize; | ||
630 | } | ||
631 | return; | ||
632 | } | ||
633 | |||
634 | /* | ||
635 | * Either no secondary cache or the available caches don't have the | ||
636 | * subset property so we have to flush the primary caches | ||
637 | * explicitly | ||
638 | */ | ||
639 | if (size >= dcache_size) { | ||
640 | r4k_blast_dcache(); | ||
641 | } else { | ||
642 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
643 | |||
644 | R4600_HIT_CACHEOP_WAR_IMPL; | ||
645 | a = addr & ~(dc_lsize - 1); | ||
646 | end = (addr + size - 1) & ~(dc_lsize - 1); | ||
647 | while (1) { | ||
648 | flush_dcache_line(a); /* Hit_Writeback_Inv_D */ | ||
649 | if (a == end) | ||
650 | break; | ||
651 | a += dc_lsize; | ||
652 | } | ||
653 | } | ||
654 | |||
655 | bc_wback_inv(addr, size); | ||
656 | } | ||
657 | |||
658 | static void r4k_dma_cache_inv(unsigned long addr, unsigned long size) | ||
659 | { | ||
660 | unsigned long end, a; | ||
661 | |||
662 | /* Catch bad driver code */ | ||
663 | BUG_ON(size == 0); | ||
664 | |||
665 | if (cpu_has_subset_pcaches) { | ||
666 | unsigned long sc_lsize = current_cpu_data.scache.linesz; | ||
667 | |||
668 | if (size >= scache_size) { | ||
669 | r4k_blast_scache(); | ||
670 | return; | ||
671 | } | ||
672 | |||
673 | a = addr & ~(sc_lsize - 1); | ||
674 | end = (addr + size - 1) & ~(sc_lsize - 1); | ||
675 | while (1) { | ||
676 | flush_scache_line(a); /* Hit_Writeback_Inv_SD */ | ||
677 | if (a == end) | ||
678 | break; | ||
679 | a += sc_lsize; | ||
680 | } | ||
681 | return; | ||
682 | } | ||
683 | |||
684 | if (size >= dcache_size) { | ||
685 | r4k_blast_dcache(); | ||
686 | } else { | ||
687 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
688 | |||
689 | R4600_HIT_CACHEOP_WAR_IMPL; | ||
690 | a = addr & ~(dc_lsize - 1); | ||
691 | end = (addr + size - 1) & ~(dc_lsize - 1); | ||
692 | while (1) { | ||
693 | flush_dcache_line(a); /* Hit_Writeback_Inv_D */ | ||
694 | if (a == end) | ||
695 | break; | ||
696 | a += dc_lsize; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | bc_inv(addr, size); | ||
701 | } | ||
702 | #endif /* CONFIG_DMA_NONCOHERENT */ | ||
703 | |||
704 | /* | ||
705 | * While we're protected against bad userland addresses we don't care | ||
706 | * very much about what happens in that case. Usually a segmentation | ||
707 | * fault will dump the process later on anyway ... | ||
708 | */ | ||
709 | static void local_r4k_flush_cache_sigtramp(void * arg) | ||
710 | { | ||
711 | unsigned long ic_lsize = current_cpu_data.icache.linesz; | ||
712 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
713 | unsigned long sc_lsize = current_cpu_data.scache.linesz; | ||
714 | unsigned long addr = (unsigned long) arg; | ||
715 | |||
716 | R4600_HIT_CACHEOP_WAR_IMPL; | ||
717 | protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); | ||
718 | if (!cpu_icache_snoops_remote_store) | ||
719 | protected_writeback_scache_line(addr & ~(sc_lsize - 1)); | ||
720 | protected_flush_icache_line(addr & ~(ic_lsize - 1)); | ||
721 | if (MIPS4K_ICACHE_REFILL_WAR) { | ||
722 | __asm__ __volatile__ ( | ||
723 | ".set push\n\t" | ||
724 | ".set noat\n\t" | ||
725 | ".set mips3\n\t" | ||
726 | #ifdef CONFIG_MIPS32 | ||
727 | "la $at,1f\n\t" | ||
728 | #endif | ||
729 | #ifdef CONFIG_MIPS64 | ||
730 | "dla $at,1f\n\t" | ||
731 | #endif | ||
732 | "cache %0,($at)\n\t" | ||
733 | "nop; nop; nop\n" | ||
734 | "1:\n\t" | ||
735 | ".set pop" | ||
736 | : | ||
737 | : "i" (Hit_Invalidate_I)); | ||
738 | } | ||
739 | if (MIPS_CACHE_SYNC_WAR) | ||
740 | __asm__ __volatile__ ("sync"); | ||
741 | } | ||
742 | |||
743 | static void r4k_flush_cache_sigtramp(unsigned long addr) | ||
744 | { | ||
745 | on_each_cpu(local_r4k_flush_cache_sigtramp, (void *) addr, 1, 1); | ||
746 | } | ||
747 | |||
748 | static void r4k_flush_icache_all(void) | ||
749 | { | ||
750 | if (cpu_has_vtag_icache) | ||
751 | r4k_blast_icache(); | ||
752 | } | ||
753 | |||
754 | static inline void rm7k_erratum31(void) | ||
755 | { | ||
756 | const unsigned long ic_lsize = 32; | ||
757 | unsigned long addr; | ||
758 | |||
759 | /* RM7000 erratum #31. The icache is screwed at startup. */ | ||
760 | write_c0_taglo(0); | ||
761 | write_c0_taghi(0); | ||
762 | |||
763 | for (addr = INDEX_BASE; addr <= INDEX_BASE + 4096; addr += ic_lsize) { | ||
764 | __asm__ __volatile__ ( | ||
765 | ".set noreorder\n\t" | ||
766 | ".set mips3\n\t" | ||
767 | "cache\t%1, 0(%0)\n\t" | ||
768 | "cache\t%1, 0x1000(%0)\n\t" | ||
769 | "cache\t%1, 0x2000(%0)\n\t" | ||
770 | "cache\t%1, 0x3000(%0)\n\t" | ||
771 | "cache\t%2, 0(%0)\n\t" | ||
772 | "cache\t%2, 0x1000(%0)\n\t" | ||
773 | "cache\t%2, 0x2000(%0)\n\t" | ||
774 | "cache\t%2, 0x3000(%0)\n\t" | ||
775 | "cache\t%1, 0(%0)\n\t" | ||
776 | "cache\t%1, 0x1000(%0)\n\t" | ||
777 | "cache\t%1, 0x2000(%0)\n\t" | ||
778 | "cache\t%1, 0x3000(%0)\n\t" | ||
779 | ".set\tmips0\n\t" | ||
780 | ".set\treorder\n\t" | ||
781 | : | ||
782 | : "r" (addr), "i" (Index_Store_Tag_I), "i" (Fill)); | ||
783 | } | ||
784 | } | ||
785 | |||
786 | static char *way_string[] __initdata = { NULL, "direct mapped", "2-way", | ||
787 | "3-way", "4-way", "5-way", "6-way", "7-way", "8-way" | ||
788 | }; | ||
789 | |||
790 | static void __init probe_pcache(void) | ||
791 | { | ||
792 | struct cpuinfo_mips *c = ¤t_cpu_data; | ||
793 | unsigned int config = read_c0_config(); | ||
794 | unsigned int prid = read_c0_prid(); | ||
795 | unsigned long config1; | ||
796 | unsigned int lsize; | ||
797 | |||
798 | switch (c->cputype) { | ||
799 | case CPU_R4600: /* QED style two way caches? */ | ||
800 | case CPU_R4700: | ||
801 | case CPU_R5000: | ||
802 | case CPU_NEVADA: | ||
803 | icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); | ||
804 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
805 | c->icache.ways = 2; | ||
806 | c->icache.waybit = ffs(icache_size/2) - 1; | ||
807 | |||
808 | dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); | ||
809 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
810 | c->dcache.ways = 2; | ||
811 | c->dcache.waybit= ffs(dcache_size/2) - 1; | ||
812 | |||
813 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
814 | break; | ||
815 | |||
816 | case CPU_R5432: | ||
817 | case CPU_R5500: | ||
818 | icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); | ||
819 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
820 | c->icache.ways = 2; | ||
821 | c->icache.waybit= 0; | ||
822 | |||
823 | dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); | ||
824 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
825 | c->dcache.ways = 2; | ||
826 | c->dcache.waybit = 0; | ||
827 | |||
828 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
829 | break; | ||
830 | |||
831 | case CPU_TX49XX: | ||
832 | icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); | ||
833 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
834 | c->icache.ways = 4; | ||
835 | c->icache.waybit= 0; | ||
836 | |||
837 | dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); | ||
838 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
839 | c->dcache.ways = 4; | ||
840 | c->dcache.waybit = 0; | ||
841 | |||
842 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
843 | break; | ||
844 | |||
845 | case CPU_R4000PC: | ||
846 | case CPU_R4000SC: | ||
847 | case CPU_R4000MC: | ||
848 | case CPU_R4400PC: | ||
849 | case CPU_R4400SC: | ||
850 | case CPU_R4400MC: | ||
851 | case CPU_R4300: | ||
852 | icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); | ||
853 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
854 | c->icache.ways = 1; | ||
855 | c->icache.waybit = 0; /* doesn't matter */ | ||
856 | |||
857 | dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); | ||
858 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
859 | c->dcache.ways = 1; | ||
860 | c->dcache.waybit = 0; /* does not matter */ | ||
861 | |||
862 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
863 | break; | ||
864 | |||
865 | case CPU_R10000: | ||
866 | case CPU_R12000: | ||
867 | icache_size = 1 << (12 + ((config & R10K_CONF_IC) >> 29)); | ||
868 | c->icache.linesz = 64; | ||
869 | c->icache.ways = 2; | ||
870 | c->icache.waybit = 0; | ||
871 | |||
872 | dcache_size = 1 << (12 + ((config & R10K_CONF_DC) >> 26)); | ||
873 | c->dcache.linesz = 32; | ||
874 | c->dcache.ways = 2; | ||
875 | c->dcache.waybit = 0; | ||
876 | |||
877 | c->options |= MIPS_CPU_PREFETCH; | ||
878 | break; | ||
879 | |||
880 | case CPU_VR4133: | ||
881 | write_c0_config(config & ~CONF_EB); | ||
882 | case CPU_VR4131: | ||
883 | /* Workaround for cache instruction bug of VR4131 */ | ||
884 | if (c->processor_id == 0x0c80U || c->processor_id == 0x0c81U || | ||
885 | c->processor_id == 0x0c82U) { | ||
886 | config &= ~0x00000030U; | ||
887 | config |= 0x00410000U; | ||
888 | write_c0_config(config); | ||
889 | } | ||
890 | icache_size = 1 << (10 + ((config & CONF_IC) >> 9)); | ||
891 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
892 | c->icache.ways = 2; | ||
893 | c->icache.waybit = ffs(icache_size/2) - 1; | ||
894 | |||
895 | dcache_size = 1 << (10 + ((config & CONF_DC) >> 6)); | ||
896 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
897 | c->dcache.ways = 2; | ||
898 | c->dcache.waybit = ffs(dcache_size/2) - 1; | ||
899 | |||
900 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
901 | break; | ||
902 | |||
903 | case CPU_VR41XX: | ||
904 | case CPU_VR4111: | ||
905 | case CPU_VR4121: | ||
906 | case CPU_VR4122: | ||
907 | case CPU_VR4181: | ||
908 | case CPU_VR4181A: | ||
909 | icache_size = 1 << (10 + ((config & CONF_IC) >> 9)); | ||
910 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
911 | c->icache.ways = 1; | ||
912 | c->icache.waybit = 0; /* doesn't matter */ | ||
913 | |||
914 | dcache_size = 1 << (10 + ((config & CONF_DC) >> 6)); | ||
915 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
916 | c->dcache.ways = 1; | ||
917 | c->dcache.waybit = 0; /* does not matter */ | ||
918 | |||
919 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
920 | break; | ||
921 | |||
922 | case CPU_RM7000: | ||
923 | rm7k_erratum31(); | ||
924 | |||
925 | case CPU_RM9000: | ||
926 | icache_size = 1 << (12 + ((config & CONF_IC) >> 9)); | ||
927 | c->icache.linesz = 16 << ((config & CONF_IB) >> 5); | ||
928 | c->icache.ways = 4; | ||
929 | c->icache.waybit = ffs(icache_size / c->icache.ways) - 1; | ||
930 | |||
931 | dcache_size = 1 << (12 + ((config & CONF_DC) >> 6)); | ||
932 | c->dcache.linesz = 16 << ((config & CONF_DB) >> 4); | ||
933 | c->dcache.ways = 4; | ||
934 | c->dcache.waybit = ffs(dcache_size / c->dcache.ways) - 1; | ||
935 | |||
936 | #if !defined(CONFIG_SMP) || !defined(RM9000_CDEX_SMP_WAR) | ||
937 | c->options |= MIPS_CPU_CACHE_CDEX_P; | ||
938 | #endif | ||
939 | c->options |= MIPS_CPU_PREFETCH; | ||
940 | break; | ||
941 | |||
942 | default: | ||
943 | if (!(config & MIPS_CONF_M)) | ||
944 | panic("Don't know how to probe P-caches on this cpu."); | ||
945 | |||
946 | /* | ||
947 | * So we seem to be a MIPS32 or MIPS64 CPU | ||
948 | * So let's probe the I-cache ... | ||
949 | */ | ||
950 | config1 = read_c0_config1(); | ||
951 | |||
952 | if ((lsize = ((config1 >> 19) & 7))) | ||
953 | c->icache.linesz = 2 << lsize; | ||
954 | else | ||
955 | c->icache.linesz = lsize; | ||
956 | c->icache.sets = 64 << ((config1 >> 22) & 7); | ||
957 | c->icache.ways = 1 + ((config1 >> 16) & 7); | ||
958 | |||
959 | icache_size = c->icache.sets * | ||
960 | c->icache.ways * | ||
961 | c->icache.linesz; | ||
962 | c->icache.waybit = ffs(icache_size/c->icache.ways) - 1; | ||
963 | |||
964 | if (config & 0x8) /* VI bit */ | ||
965 | c->icache.flags |= MIPS_CACHE_VTAG; | ||
966 | |||
967 | /* | ||
968 | * Now probe the MIPS32 / MIPS64 data cache. | ||
969 | */ | ||
970 | c->dcache.flags = 0; | ||
971 | |||
972 | if ((lsize = ((config1 >> 10) & 7))) | ||
973 | c->dcache.linesz = 2 << lsize; | ||
974 | else | ||
975 | c->dcache.linesz= lsize; | ||
976 | c->dcache.sets = 64 << ((config1 >> 13) & 7); | ||
977 | c->dcache.ways = 1 + ((config1 >> 7) & 7); | ||
978 | |||
979 | dcache_size = c->dcache.sets * | ||
980 | c->dcache.ways * | ||
981 | c->dcache.linesz; | ||
982 | c->dcache.waybit = ffs(dcache_size/c->dcache.ways) - 1; | ||
983 | |||
984 | c->options |= MIPS_CPU_PREFETCH; | ||
985 | break; | ||
986 | } | ||
987 | |||
988 | /* | ||
989 | * Processor configuration sanity check for the R4000SC erratum | ||
990 | * #5. With page sizes larger than 32kB there is no possibility | ||
991 | * to get a VCE exception anymore so we don't care about this | ||
992 | * misconfiguration. The case is rather theoretical anyway; | ||
993 | * presumably no vendor is shipping his hardware in the "bad" | ||
994 | * configuration. | ||
995 | */ | ||
996 | if ((prid & 0xff00) == PRID_IMP_R4000 && (prid & 0xff) < 0x40 && | ||
997 | !(config & CONF_SC) && c->icache.linesz != 16 && | ||
998 | PAGE_SIZE <= 0x8000) | ||
999 | panic("Improper R4000SC processor configuration detected"); | ||
1000 | |||
1001 | /* compute a couple of other cache variables */ | ||
1002 | c->icache.waysize = icache_size / c->icache.ways; | ||
1003 | c->dcache.waysize = dcache_size / c->dcache.ways; | ||
1004 | |||
1005 | c->icache.sets = icache_size / (c->icache.linesz * c->icache.ways); | ||
1006 | c->dcache.sets = dcache_size / (c->dcache.linesz * c->dcache.ways); | ||
1007 | |||
1008 | /* | ||
1009 | * R10000 and R12000 P-caches are odd in a positive way. They're 32kB | ||
1010 | * 2-way virtually indexed so normally would suffer from aliases. So | ||
1011 | * normally they'd suffer from aliases but magic in the hardware deals | ||
1012 | * with that for us so we don't need to take care ourselves. | ||
1013 | */ | ||
1014 | if (c->cputype != CPU_R10000 && c->cputype != CPU_R12000) | ||
1015 | if (c->dcache.waysize > PAGE_SIZE) | ||
1016 | c->dcache.flags |= MIPS_CACHE_ALIASES; | ||
1017 | |||
1018 | switch (c->cputype) { | ||
1019 | case CPU_20KC: | ||
1020 | /* | ||
1021 | * Some older 20Kc chips doesn't have the 'VI' bit in | ||
1022 | * the config register. | ||
1023 | */ | ||
1024 | c->icache.flags |= MIPS_CACHE_VTAG; | ||
1025 | break; | ||
1026 | |||
1027 | case CPU_AU1500: | ||
1028 | c->icache.flags |= MIPS_CACHE_IC_F_DC; | ||
1029 | break; | ||
1030 | } | ||
1031 | |||
1032 | printk("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n", | ||
1033 | icache_size >> 10, | ||
1034 | cpu_has_vtag_icache ? "virtually tagged" : "physically tagged", | ||
1035 | way_string[c->icache.ways], c->icache.linesz); | ||
1036 | |||
1037 | printk("Primary data cache %ldkB, %s, linesize %d bytes.\n", | ||
1038 | dcache_size >> 10, way_string[c->dcache.ways], c->dcache.linesz); | ||
1039 | } | ||
1040 | |||
1041 | /* | ||
1042 | * If you even _breathe_ on this function, look at the gcc output and make sure | ||
1043 | * it does not pop things on and off the stack for the cache sizing loop that | ||
1044 | * executes in KSEG1 space or else you will crash and burn badly. You have | ||
1045 | * been warned. | ||
1046 | */ | ||
1047 | static int __init probe_scache(void) | ||
1048 | { | ||
1049 | extern unsigned long stext; | ||
1050 | unsigned long flags, addr, begin, end, pow2; | ||
1051 | unsigned int config = read_c0_config(); | ||
1052 | struct cpuinfo_mips *c = ¤t_cpu_data; | ||
1053 | int tmp; | ||
1054 | |||
1055 | if (config & CONF_SC) | ||
1056 | return 0; | ||
1057 | |||
1058 | begin = (unsigned long) &stext; | ||
1059 | begin &= ~((4 * 1024 * 1024) - 1); | ||
1060 | end = begin + (4 * 1024 * 1024); | ||
1061 | |||
1062 | /* | ||
1063 | * This is such a bitch, you'd think they would make it easy to do | ||
1064 | * this. Away you daemons of stupidity! | ||
1065 | */ | ||
1066 | local_irq_save(flags); | ||
1067 | |||
1068 | /* Fill each size-multiple cache line with a valid tag. */ | ||
1069 | pow2 = (64 * 1024); | ||
1070 | for (addr = begin; addr < end; addr = (begin + pow2)) { | ||
1071 | unsigned long *p = (unsigned long *) addr; | ||
1072 | __asm__ __volatile__("nop" : : "r" (*p)); /* whee... */ | ||
1073 | pow2 <<= 1; | ||
1074 | } | ||
1075 | |||
1076 | /* Load first line with zero (therefore invalid) tag. */ | ||
1077 | write_c0_taglo(0); | ||
1078 | write_c0_taghi(0); | ||
1079 | __asm__ __volatile__("nop; nop; nop; nop;"); /* avoid the hazard */ | ||
1080 | cache_op(Index_Store_Tag_I, begin); | ||
1081 | cache_op(Index_Store_Tag_D, begin); | ||
1082 | cache_op(Index_Store_Tag_SD, begin); | ||
1083 | |||
1084 | /* Now search for the wrap around point. */ | ||
1085 | pow2 = (128 * 1024); | ||
1086 | tmp = 0; | ||
1087 | for (addr = begin + (128 * 1024); addr < end; addr = begin + pow2) { | ||
1088 | cache_op(Index_Load_Tag_SD, addr); | ||
1089 | __asm__ __volatile__("nop; nop; nop; nop;"); /* hazard... */ | ||
1090 | if (!read_c0_taglo()) | ||
1091 | break; | ||
1092 | pow2 <<= 1; | ||
1093 | } | ||
1094 | local_irq_restore(flags); | ||
1095 | addr -= begin; | ||
1096 | |||
1097 | scache_size = addr; | ||
1098 | c->scache.linesz = 16 << ((config & R4K_CONF_SB) >> 22); | ||
1099 | c->scache.ways = 1; | ||
1100 | c->dcache.waybit = 0; /* does not matter */ | ||
1101 | |||
1102 | return 1; | ||
1103 | } | ||
1104 | |||
1105 | typedef int (*probe_func_t)(unsigned long); | ||
1106 | extern int r5k_sc_init(void); | ||
1107 | extern int rm7k_sc_init(void); | ||
1108 | |||
1109 | static void __init setup_scache(void) | ||
1110 | { | ||
1111 | struct cpuinfo_mips *c = ¤t_cpu_data; | ||
1112 | unsigned int config = read_c0_config(); | ||
1113 | probe_func_t probe_scache_kseg1; | ||
1114 | int sc_present = 0; | ||
1115 | |||
1116 | /* | ||
1117 | * Do the probing thing on R4000SC and R4400SC processors. Other | ||
1118 | * processors don't have a S-cache that would be relevant to the | ||
1119 | * Linux memory managment. | ||
1120 | */ | ||
1121 | switch (c->cputype) { | ||
1122 | case CPU_R4000SC: | ||
1123 | case CPU_R4000MC: | ||
1124 | case CPU_R4400SC: | ||
1125 | case CPU_R4400MC: | ||
1126 | probe_scache_kseg1 = (probe_func_t) (CKSEG1ADDR(&probe_scache)); | ||
1127 | sc_present = probe_scache_kseg1(config); | ||
1128 | if (sc_present) | ||
1129 | c->options |= MIPS_CPU_CACHE_CDEX_S; | ||
1130 | break; | ||
1131 | |||
1132 | case CPU_R10000: | ||
1133 | case CPU_R12000: | ||
1134 | scache_size = 0x80000 << ((config & R10K_CONF_SS) >> 16); | ||
1135 | c->scache.linesz = 64 << ((config >> 13) & 1); | ||
1136 | c->scache.ways = 2; | ||
1137 | c->scache.waybit= 0; | ||
1138 | sc_present = 1; | ||
1139 | break; | ||
1140 | |||
1141 | case CPU_R5000: | ||
1142 | case CPU_NEVADA: | ||
1143 | #ifdef CONFIG_R5000_CPU_SCACHE | ||
1144 | r5k_sc_init(); | ||
1145 | #endif | ||
1146 | return; | ||
1147 | |||
1148 | case CPU_RM7000: | ||
1149 | case CPU_RM9000: | ||
1150 | #ifdef CONFIG_RM7000_CPU_SCACHE | ||
1151 | rm7k_sc_init(); | ||
1152 | #endif | ||
1153 | return; | ||
1154 | |||
1155 | default: | ||
1156 | sc_present = 0; | ||
1157 | } | ||
1158 | |||
1159 | if (!sc_present) | ||
1160 | return; | ||
1161 | |||
1162 | if ((c->isa_level == MIPS_CPU_ISA_M32 || | ||
1163 | c->isa_level == MIPS_CPU_ISA_M64) && | ||
1164 | !(c->scache.flags & MIPS_CACHE_NOT_PRESENT)) | ||
1165 | panic("Dunno how to handle MIPS32 / MIPS64 second level cache"); | ||
1166 | |||
1167 | /* compute a couple of other cache variables */ | ||
1168 | c->scache.waysize = scache_size / c->scache.ways; | ||
1169 | |||
1170 | c->scache.sets = scache_size / (c->scache.linesz * c->scache.ways); | ||
1171 | |||
1172 | printk("Unified secondary cache %ldkB %s, linesize %d bytes.\n", | ||
1173 | scache_size >> 10, way_string[c->scache.ways], c->scache.linesz); | ||
1174 | |||
1175 | c->options |= MIPS_CPU_SUBSET_CACHES; | ||
1176 | } | ||
1177 | |||
1178 | static inline void coherency_setup(void) | ||
1179 | { | ||
1180 | change_c0_config(CONF_CM_CMASK, CONF_CM_DEFAULT); | ||
1181 | |||
1182 | /* | ||
1183 | * c0_status.cu=0 specifies that updates by the sc instruction use | ||
1184 | * the coherency mode specified by the TLB; 1 means cachable | ||
1185 | * coherent update on write will be used. Not all processors have | ||
1186 | * this bit and; some wire it to zero, others like Toshiba had the | ||
1187 | * silly idea of putting something else there ... | ||
1188 | */ | ||
1189 | switch (current_cpu_data.cputype) { | ||
1190 | case CPU_R4000PC: | ||
1191 | case CPU_R4000SC: | ||
1192 | case CPU_R4000MC: | ||
1193 | case CPU_R4400PC: | ||
1194 | case CPU_R4400SC: | ||
1195 | case CPU_R4400MC: | ||
1196 | clear_c0_config(CONF_CU); | ||
1197 | break; | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | void __init ld_mmu_r4xx0(void) | ||
1202 | { | ||
1203 | extern void build_clear_page(void); | ||
1204 | extern void build_copy_page(void); | ||
1205 | extern char except_vec2_generic; | ||
1206 | struct cpuinfo_mips *c = ¤t_cpu_data; | ||
1207 | |||
1208 | /* Default cache error handler for R4000 and R5000 family */ | ||
1209 | memcpy((void *)(CAC_BASE + 0x100), &except_vec2_generic, 0x80); | ||
1210 | memcpy((void *)(UNCAC_BASE + 0x100), &except_vec2_generic, 0x80); | ||
1211 | |||
1212 | probe_pcache(); | ||
1213 | setup_scache(); | ||
1214 | |||
1215 | if (c->dcache.sets * c->dcache.ways > PAGE_SIZE) | ||
1216 | c->dcache.flags |= MIPS_CACHE_ALIASES; | ||
1217 | |||
1218 | r4k_blast_dcache_page_setup(); | ||
1219 | r4k_blast_dcache_page_indexed_setup(); | ||
1220 | r4k_blast_dcache_setup(); | ||
1221 | r4k_blast_icache_page_setup(); | ||
1222 | r4k_blast_icache_page_indexed_setup(); | ||
1223 | r4k_blast_icache_setup(); | ||
1224 | r4k_blast_scache_page_setup(); | ||
1225 | r4k_blast_scache_page_indexed_setup(); | ||
1226 | r4k_blast_scache_setup(); | ||
1227 | |||
1228 | /* | ||
1229 | * Some MIPS32 and MIPS64 processors have physically indexed caches. | ||
1230 | * This code supports virtually indexed processors and will be | ||
1231 | * unnecessarily inefficient on physically indexed processors. | ||
1232 | */ | ||
1233 | shm_align_mask = max_t( unsigned long, | ||
1234 | c->dcache.sets * c->dcache.linesz - 1, | ||
1235 | PAGE_SIZE - 1); | ||
1236 | |||
1237 | flush_cache_all = r4k_flush_cache_all; | ||
1238 | __flush_cache_all = r4k___flush_cache_all; | ||
1239 | flush_cache_mm = r4k_flush_cache_mm; | ||
1240 | flush_cache_page = r4k_flush_cache_page; | ||
1241 | flush_icache_page = r4k_flush_icache_page; | ||
1242 | flush_cache_range = r4k_flush_cache_range; | ||
1243 | |||
1244 | flush_cache_sigtramp = r4k_flush_cache_sigtramp; | ||
1245 | flush_icache_all = r4k_flush_icache_all; | ||
1246 | flush_data_cache_page = r4k_flush_data_cache_page; | ||
1247 | flush_icache_range = r4k_flush_icache_range; | ||
1248 | |||
1249 | #ifdef CONFIG_DMA_NONCOHERENT | ||
1250 | _dma_cache_wback_inv = r4k_dma_cache_wback_inv; | ||
1251 | _dma_cache_wback = r4k_dma_cache_wback_inv; | ||
1252 | _dma_cache_inv = r4k_dma_cache_inv; | ||
1253 | #endif | ||
1254 | |||
1255 | __flush_cache_all(); | ||
1256 | coherency_setup(); | ||
1257 | |||
1258 | build_clear_page(); | ||
1259 | build_copy_page(); | ||
1260 | } | ||
diff --git a/arch/mips/mm/c-sb1.c b/arch/mips/mm/c-sb1.c new file mode 100644 index 000000000000..ab30afd63b32 --- /dev/null +++ b/arch/mips/mm/c-sb1.c | |||
@@ -0,0 +1,558 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
3 | * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) | ||
4 | * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation | ||
5 | * Copyright (C) 2004 Maciej W. Rozycki | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version 2 | ||
10 | * of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | */ | ||
21 | #include <linux/config.h> | ||
22 | #include <linux/init.h> | ||
23 | |||
24 | #include <asm/asm.h> | ||
25 | #include <asm/bootinfo.h> | ||
26 | #include <asm/cacheops.h> | ||
27 | #include <asm/cpu.h> | ||
28 | #include <asm/mipsregs.h> | ||
29 | #include <asm/mmu_context.h> | ||
30 | #include <asm/uaccess.h> | ||
31 | |||
32 | extern void sb1_dma_init(void); | ||
33 | |||
34 | /* These are probed at ld_mmu time */ | ||
35 | static unsigned long icache_size; | ||
36 | static unsigned long dcache_size; | ||
37 | |||
38 | static unsigned short icache_line_size; | ||
39 | static unsigned short dcache_line_size; | ||
40 | |||
41 | static unsigned int icache_index_mask; | ||
42 | static unsigned int dcache_index_mask; | ||
43 | |||
44 | static unsigned short icache_assoc; | ||
45 | static unsigned short dcache_assoc; | ||
46 | |||
47 | static unsigned short icache_sets; | ||
48 | static unsigned short dcache_sets; | ||
49 | |||
50 | static unsigned int icache_range_cutoff; | ||
51 | static unsigned int dcache_range_cutoff; | ||
52 | |||
53 | /* | ||
54 | * The dcache is fully coherent to the system, with one | ||
55 | * big caveat: the instruction stream. In other words, | ||
56 | * if we miss in the icache, and have dirty data in the | ||
57 | * L1 dcache, then we'll go out to memory (or the L2) and | ||
58 | * get the not-as-recent data. | ||
59 | * | ||
60 | * So the only time we have to flush the dcache is when | ||
61 | * we're flushing the icache. Since the L2 is fully | ||
62 | * coherent to everything, including I/O, we never have | ||
63 | * to flush it | ||
64 | */ | ||
65 | |||
66 | #define cache_set_op(op, addr) \ | ||
67 | __asm__ __volatile__( \ | ||
68 | " .set noreorder \n" \ | ||
69 | " .set mips64\n\t \n" \ | ||
70 | " cache %0, (0<<13)(%1) \n" \ | ||
71 | " cache %0, (1<<13)(%1) \n" \ | ||
72 | " cache %0, (2<<13)(%1) \n" \ | ||
73 | " cache %0, (3<<13)(%1) \n" \ | ||
74 | " .set mips0 \n" \ | ||
75 | " .set reorder" \ | ||
76 | : \ | ||
77 | : "i" (op), "r" (addr)) | ||
78 | |||
79 | #define sync() \ | ||
80 | __asm__ __volatile( \ | ||
81 | " .set mips64\n\t \n" \ | ||
82 | " sync \n" \ | ||
83 | " .set mips0") | ||
84 | |||
85 | #define mispredict() \ | ||
86 | __asm__ __volatile__( \ | ||
87 | " bnezl $0, 1f \n" /* Force mispredict */ \ | ||
88 | "1: \n"); | ||
89 | |||
90 | /* | ||
91 | * Writeback and invalidate the entire dcache | ||
92 | */ | ||
93 | static inline void __sb1_writeback_inv_dcache_all(void) | ||
94 | { | ||
95 | unsigned long addr = 0; | ||
96 | |||
97 | while (addr < dcache_line_size * dcache_sets) { | ||
98 | cache_set_op(Index_Writeback_Inv_D, addr); | ||
99 | addr += dcache_line_size; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Writeback and invalidate a range of the dcache. The addresses are | ||
105 | * virtual, and since we're using index ops and bit 12 is part of both | ||
106 | * the virtual frame and physical index, we have to clear both sets | ||
107 | * (bit 12 set and cleared). | ||
108 | */ | ||
109 | static inline void __sb1_writeback_inv_dcache_range(unsigned long start, | ||
110 | unsigned long end) | ||
111 | { | ||
112 | unsigned long index; | ||
113 | |||
114 | start &= ~(dcache_line_size - 1); | ||
115 | end = (end + dcache_line_size - 1) & ~(dcache_line_size - 1); | ||
116 | |||
117 | while (start != end) { | ||
118 | index = start & dcache_index_mask; | ||
119 | cache_set_op(Index_Writeback_Inv_D, index); | ||
120 | cache_set_op(Index_Writeback_Inv_D, index ^ (1<<12)); | ||
121 | start += dcache_line_size; | ||
122 | } | ||
123 | sync(); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Writeback and invalidate a range of the dcache. With physical | ||
128 | * addresseses, we don't have to worry about possible bit 12 aliasing. | ||
129 | * XXXKW is it worth turning on KX and using hit ops with xkphys? | ||
130 | */ | ||
131 | static inline void __sb1_writeback_inv_dcache_phys_range(unsigned long start, | ||
132 | unsigned long end) | ||
133 | { | ||
134 | start &= ~(dcache_line_size - 1); | ||
135 | end = (end + dcache_line_size - 1) & ~(dcache_line_size - 1); | ||
136 | |||
137 | while (start != end) { | ||
138 | cache_set_op(Index_Writeback_Inv_D, start & dcache_index_mask); | ||
139 | start += dcache_line_size; | ||
140 | } | ||
141 | sync(); | ||
142 | } | ||
143 | |||
144 | |||
145 | /* | ||
146 | * Invalidate the entire icache | ||
147 | */ | ||
148 | static inline void __sb1_flush_icache_all(void) | ||
149 | { | ||
150 | unsigned long addr = 0; | ||
151 | |||
152 | while (addr < icache_line_size * icache_sets) { | ||
153 | cache_set_op(Index_Invalidate_I, addr); | ||
154 | addr += icache_line_size; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Flush the icache for a given physical page. Need to writeback the | ||
160 | * dcache first, then invalidate the icache. If the page isn't | ||
161 | * executable, nothing is required. | ||
162 | */ | ||
163 | static void local_sb1_flush_cache_page(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) | ||
164 | { | ||
165 | int cpu = smp_processor_id(); | ||
166 | |||
167 | #ifndef CONFIG_SMP | ||
168 | if (!(vma->vm_flags & VM_EXEC)) | ||
169 | return; | ||
170 | #endif | ||
171 | |||
172 | __sb1_writeback_inv_dcache_range(addr, addr + PAGE_SIZE); | ||
173 | |||
174 | /* | ||
175 | * Bumping the ASID is probably cheaper than the flush ... | ||
176 | */ | ||
177 | if (cpu_context(cpu, vma->vm_mm) != 0) | ||
178 | drop_mmu_context(vma->vm_mm, cpu); | ||
179 | } | ||
180 | |||
181 | #ifdef CONFIG_SMP | ||
182 | struct flush_cache_page_args { | ||
183 | struct vm_area_struct *vma; | ||
184 | unsigned long addr; | ||
185 | unsigned long pfn; | ||
186 | }; | ||
187 | |||
188 | static void sb1_flush_cache_page_ipi(void *info) | ||
189 | { | ||
190 | struct flush_cache_page_args *args = info; | ||
191 | |||
192 | local_sb1_flush_cache_page(args->vma, args->addr, args->pfn); | ||
193 | } | ||
194 | |||
195 | /* Dirty dcache could be on another CPU, so do the IPIs */ | ||
196 | static void sb1_flush_cache_page(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) | ||
197 | { | ||
198 | struct flush_cache_page_args args; | ||
199 | |||
200 | if (!(vma->vm_flags & VM_EXEC)) | ||
201 | return; | ||
202 | |||
203 | addr &= PAGE_MASK; | ||
204 | args.vma = vma; | ||
205 | args.addr = addr; | ||
206 | args.pfn = pfn; | ||
207 | on_each_cpu(sb1_flush_cache_page_ipi, (void *) &args, 1, 1); | ||
208 | } | ||
209 | #else | ||
210 | void sb1_flush_cache_page(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) | ||
211 | __attribute__((alias("local_sb1_flush_cache_page"))); | ||
212 | #endif | ||
213 | |||
214 | /* | ||
215 | * Invalidate a range of the icache. The addresses are virtual, and | ||
216 | * the cache is virtually indexed and tagged. However, we don't | ||
217 | * necessarily have the right ASID context, so use index ops instead | ||
218 | * of hit ops. | ||
219 | */ | ||
220 | static inline void __sb1_flush_icache_range(unsigned long start, | ||
221 | unsigned long end) | ||
222 | { | ||
223 | start &= ~(icache_line_size - 1); | ||
224 | end = (end + icache_line_size - 1) & ~(icache_line_size - 1); | ||
225 | |||
226 | while (start != end) { | ||
227 | cache_set_op(Index_Invalidate_I, start & icache_index_mask); | ||
228 | start += icache_line_size; | ||
229 | } | ||
230 | mispredict(); | ||
231 | sync(); | ||
232 | } | ||
233 | |||
234 | |||
235 | /* | ||
236 | * Invalidate all caches on this CPU | ||
237 | */ | ||
238 | static void local_sb1___flush_cache_all(void) | ||
239 | { | ||
240 | __sb1_writeback_inv_dcache_all(); | ||
241 | __sb1_flush_icache_all(); | ||
242 | } | ||
243 | |||
244 | #ifdef CONFIG_SMP | ||
245 | void sb1___flush_cache_all_ipi(void *ignored) | ||
246 | __attribute__((alias("local_sb1___flush_cache_all"))); | ||
247 | |||
248 | static void sb1___flush_cache_all(void) | ||
249 | { | ||
250 | on_each_cpu(sb1___flush_cache_all_ipi, 0, 1, 1); | ||
251 | } | ||
252 | #else | ||
253 | void sb1___flush_cache_all(void) | ||
254 | __attribute__((alias("local_sb1___flush_cache_all"))); | ||
255 | #endif | ||
256 | |||
257 | /* | ||
258 | * When flushing a range in the icache, we have to first writeback | ||
259 | * the dcache for the same range, so new ifetches will see any | ||
260 | * data that was dirty in the dcache. | ||
261 | * | ||
262 | * The start/end arguments are Kseg addresses (possibly mapped Kseg). | ||
263 | */ | ||
264 | |||
265 | static void local_sb1_flush_icache_range(unsigned long start, | ||
266 | unsigned long end) | ||
267 | { | ||
268 | /* Just wb-inv the whole dcache if the range is big enough */ | ||
269 | if ((end - start) > dcache_range_cutoff) | ||
270 | __sb1_writeback_inv_dcache_all(); | ||
271 | else | ||
272 | __sb1_writeback_inv_dcache_range(start, end); | ||
273 | |||
274 | /* Just flush the whole icache if the range is big enough */ | ||
275 | if ((end - start) > icache_range_cutoff) | ||
276 | __sb1_flush_icache_all(); | ||
277 | else | ||
278 | __sb1_flush_icache_range(start, end); | ||
279 | } | ||
280 | |||
281 | #ifdef CONFIG_SMP | ||
282 | struct flush_icache_range_args { | ||
283 | unsigned long start; | ||
284 | unsigned long end; | ||
285 | }; | ||
286 | |||
287 | static void sb1_flush_icache_range_ipi(void *info) | ||
288 | { | ||
289 | struct flush_icache_range_args *args = info; | ||
290 | |||
291 | local_sb1_flush_icache_range(args->start, args->end); | ||
292 | } | ||
293 | |||
294 | void sb1_flush_icache_range(unsigned long start, unsigned long end) | ||
295 | { | ||
296 | struct flush_icache_range_args args; | ||
297 | |||
298 | args.start = start; | ||
299 | args.end = end; | ||
300 | on_each_cpu(sb1_flush_icache_range_ipi, &args, 1, 1); | ||
301 | } | ||
302 | #else | ||
303 | void sb1_flush_icache_range(unsigned long start, unsigned long end) | ||
304 | __attribute__((alias("local_sb1_flush_icache_range"))); | ||
305 | #endif | ||
306 | |||
307 | /* | ||
308 | * Flush the icache for a given physical page. Need to writeback the | ||
309 | * dcache first, then invalidate the icache. If the page isn't | ||
310 | * executable, nothing is required. | ||
311 | */ | ||
312 | static void local_sb1_flush_icache_page(struct vm_area_struct *vma, | ||
313 | struct page *page) | ||
314 | { | ||
315 | unsigned long start; | ||
316 | int cpu = smp_processor_id(); | ||
317 | |||
318 | #ifndef CONFIG_SMP | ||
319 | if (!(vma->vm_flags & VM_EXEC)) | ||
320 | return; | ||
321 | #endif | ||
322 | |||
323 | /* Need to writeback any dirty data for that page, we have the PA */ | ||
324 | start = (unsigned long)(page-mem_map) << PAGE_SHIFT; | ||
325 | __sb1_writeback_inv_dcache_phys_range(start, start + PAGE_SIZE); | ||
326 | /* | ||
327 | * If there's a context, bump the ASID (cheaper than a flush, | ||
328 | * since we don't know VAs!) | ||
329 | */ | ||
330 | if (cpu_context(cpu, vma->vm_mm) != 0) { | ||
331 | drop_mmu_context(vma->vm_mm, cpu); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | #ifdef CONFIG_SMP | ||
336 | struct flush_icache_page_args { | ||
337 | struct vm_area_struct *vma; | ||
338 | struct page *page; | ||
339 | }; | ||
340 | |||
341 | static void sb1_flush_icache_page_ipi(void *info) | ||
342 | { | ||
343 | struct flush_icache_page_args *args = info; | ||
344 | local_sb1_flush_icache_page(args->vma, args->page); | ||
345 | } | ||
346 | |||
347 | /* Dirty dcache could be on another CPU, so do the IPIs */ | ||
348 | static void sb1_flush_icache_page(struct vm_area_struct *vma, | ||
349 | struct page *page) | ||
350 | { | ||
351 | struct flush_icache_page_args args; | ||
352 | |||
353 | if (!(vma->vm_flags & VM_EXEC)) | ||
354 | return; | ||
355 | args.vma = vma; | ||
356 | args.page = page; | ||
357 | on_each_cpu(sb1_flush_icache_page_ipi, (void *) &args, 1, 1); | ||
358 | } | ||
359 | #else | ||
360 | void sb1_flush_icache_page(struct vm_area_struct *vma, struct page *page) | ||
361 | __attribute__((alias("local_sb1_flush_icache_page"))); | ||
362 | #endif | ||
363 | |||
364 | /* | ||
365 | * A signal trampoline must fit into a single cacheline. | ||
366 | */ | ||
367 | static void local_sb1_flush_cache_sigtramp(unsigned long addr) | ||
368 | { | ||
369 | cache_set_op(Index_Writeback_Inv_D, addr & dcache_index_mask); | ||
370 | cache_set_op(Index_Writeback_Inv_D, (addr ^ (1<<12)) & dcache_index_mask); | ||
371 | cache_set_op(Index_Invalidate_I, addr & icache_index_mask); | ||
372 | mispredict(); | ||
373 | } | ||
374 | |||
375 | #ifdef CONFIG_SMP | ||
376 | static void sb1_flush_cache_sigtramp_ipi(void *info) | ||
377 | { | ||
378 | unsigned long iaddr = (unsigned long) info; | ||
379 | local_sb1_flush_cache_sigtramp(iaddr); | ||
380 | } | ||
381 | |||
382 | static void sb1_flush_cache_sigtramp(unsigned long addr) | ||
383 | { | ||
384 | on_each_cpu(sb1_flush_cache_sigtramp_ipi, (void *) addr, 1, 1); | ||
385 | } | ||
386 | #else | ||
387 | void sb1_flush_cache_sigtramp(unsigned long addr) | ||
388 | __attribute__((alias("local_sb1_flush_cache_sigtramp"))); | ||
389 | #endif | ||
390 | |||
391 | |||
392 | /* | ||
393 | * Anything that just flushes dcache state can be ignored, as we're always | ||
394 | * coherent in dcache space. This is just a dummy function that all the | ||
395 | * nop'ed routines point to | ||
396 | */ | ||
397 | static void sb1_nop(void) | ||
398 | { | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * Cache set values (from the mips64 spec) | ||
403 | * 0 - 64 | ||
404 | * 1 - 128 | ||
405 | * 2 - 256 | ||
406 | * 3 - 512 | ||
407 | * 4 - 1024 | ||
408 | * 5 - 2048 | ||
409 | * 6 - 4096 | ||
410 | * 7 - Reserved | ||
411 | */ | ||
412 | |||
413 | static unsigned int decode_cache_sets(unsigned int config_field) | ||
414 | { | ||
415 | if (config_field == 7) { | ||
416 | /* JDCXXX - Find a graceful way to abort. */ | ||
417 | return 0; | ||
418 | } | ||
419 | return (1<<(config_field + 6)); | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * Cache line size values (from the mips64 spec) | ||
424 | * 0 - No cache present. | ||
425 | * 1 - 4 bytes | ||
426 | * 2 - 8 bytes | ||
427 | * 3 - 16 bytes | ||
428 | * 4 - 32 bytes | ||
429 | * 5 - 64 bytes | ||
430 | * 6 - 128 bytes | ||
431 | * 7 - Reserved | ||
432 | */ | ||
433 | |||
434 | static unsigned int decode_cache_line_size(unsigned int config_field) | ||
435 | { | ||
436 | if (config_field == 0) { | ||
437 | return 0; | ||
438 | } else if (config_field == 7) { | ||
439 | /* JDCXXX - Find a graceful way to abort. */ | ||
440 | return 0; | ||
441 | } | ||
442 | return (1<<(config_field + 1)); | ||
443 | } | ||
444 | |||
445 | /* | ||
446 | * Relevant bits of the config1 register format (from the MIPS32/MIPS64 specs) | ||
447 | * | ||
448 | * 24:22 Icache sets per way | ||
449 | * 21:19 Icache line size | ||
450 | * 18:16 Icache Associativity | ||
451 | * 15:13 Dcache sets per way | ||
452 | * 12:10 Dcache line size | ||
453 | * 9:7 Dcache Associativity | ||
454 | */ | ||
455 | |||
456 | static char *way_string[] = { | ||
457 | "direct mapped", "2-way", "3-way", "4-way", | ||
458 | "5-way", "6-way", "7-way", "8-way", | ||
459 | }; | ||
460 | |||
461 | static __init void probe_cache_sizes(void) | ||
462 | { | ||
463 | u32 config1; | ||
464 | |||
465 | config1 = read_c0_config1(); | ||
466 | icache_line_size = decode_cache_line_size((config1 >> 19) & 0x7); | ||
467 | dcache_line_size = decode_cache_line_size((config1 >> 10) & 0x7); | ||
468 | icache_sets = decode_cache_sets((config1 >> 22) & 0x7); | ||
469 | dcache_sets = decode_cache_sets((config1 >> 13) & 0x7); | ||
470 | icache_assoc = ((config1 >> 16) & 0x7) + 1; | ||
471 | dcache_assoc = ((config1 >> 7) & 0x7) + 1; | ||
472 | icache_size = icache_line_size * icache_sets * icache_assoc; | ||
473 | dcache_size = dcache_line_size * dcache_sets * dcache_assoc; | ||
474 | /* Need to remove non-index bits for index ops */ | ||
475 | icache_index_mask = (icache_sets - 1) * icache_line_size; | ||
476 | dcache_index_mask = (dcache_sets - 1) * dcache_line_size; | ||
477 | /* | ||
478 | * These are for choosing range (index ops) versus all. | ||
479 | * icache flushes all ways for each set, so drop icache_assoc. | ||
480 | * dcache flushes all ways and each setting of bit 12 for each | ||
481 | * index, so drop dcache_assoc and halve the dcache_sets. | ||
482 | */ | ||
483 | icache_range_cutoff = icache_sets * icache_line_size; | ||
484 | dcache_range_cutoff = (dcache_sets / 2) * icache_line_size; | ||
485 | |||
486 | printk("Primary instruction cache %ldkB, %s, linesize %d bytes.\n", | ||
487 | icache_size >> 10, way_string[icache_assoc - 1], | ||
488 | icache_line_size); | ||
489 | printk("Primary data cache %ldkB, %s, linesize %d bytes.\n", | ||
490 | dcache_size >> 10, way_string[dcache_assoc - 1], | ||
491 | dcache_line_size); | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * This is called from loadmmu.c. We have to set up all the | ||
496 | * memory management function pointers, as well as initialize | ||
497 | * the caches and tlbs | ||
498 | */ | ||
499 | void ld_mmu_sb1(void) | ||
500 | { | ||
501 | extern char except_vec2_sb1; | ||
502 | extern char handle_vec2_sb1; | ||
503 | |||
504 | /* Special cache error handler for SB1 */ | ||
505 | memcpy((void *)(CAC_BASE + 0x100), &except_vec2_sb1, 0x80); | ||
506 | memcpy((void *)(UNCAC_BASE + 0x100), &except_vec2_sb1, 0x80); | ||
507 | memcpy((void *)CKSEG1ADDR(&handle_vec2_sb1), &handle_vec2_sb1, 0x80); | ||
508 | |||
509 | probe_cache_sizes(); | ||
510 | |||
511 | #ifdef CONFIG_SIBYTE_DMA_PAGEOPS | ||
512 | sb1_dma_init(); | ||
513 | #endif | ||
514 | |||
515 | /* | ||
516 | * None of these are needed for the SB1 - the Dcache is | ||
517 | * physically indexed and tagged, so no virtual aliasing can | ||
518 | * occur | ||
519 | */ | ||
520 | flush_cache_range = (void *) sb1_nop; | ||
521 | flush_cache_mm = (void (*)(struct mm_struct *))sb1_nop; | ||
522 | flush_cache_all = sb1_nop; | ||
523 | |||
524 | /* These routines are for Icache coherence with the Dcache */ | ||
525 | flush_icache_range = sb1_flush_icache_range; | ||
526 | flush_icache_page = sb1_flush_icache_page; | ||
527 | flush_icache_all = __sb1_flush_icache_all; /* local only */ | ||
528 | |||
529 | /* This implies an Icache flush too, so can't be nop'ed */ | ||
530 | flush_cache_page = sb1_flush_cache_page; | ||
531 | |||
532 | flush_cache_sigtramp = sb1_flush_cache_sigtramp; | ||
533 | flush_data_cache_page = (void *) sb1_nop; | ||
534 | |||
535 | /* Full flush */ | ||
536 | __flush_cache_all = sb1___flush_cache_all; | ||
537 | |||
538 | change_c0_config(CONF_CM_CMASK, CONF_CM_DEFAULT); | ||
539 | |||
540 | /* | ||
541 | * This is the only way to force the update of K0 to complete | ||
542 | * before subsequent instruction fetch. | ||
543 | */ | ||
544 | __asm__ __volatile__( | ||
545 | ".set push \n" | ||
546 | " .set noat \n" | ||
547 | " .set noreorder \n" | ||
548 | " .set mips3 \n" | ||
549 | " " STR(PTR_LA) " $1, 1f \n" | ||
550 | " " STR(MTC0) " $1, $14 \n" | ||
551 | " eret \n" | ||
552 | "1: .set pop" | ||
553 | : | ||
554 | : | ||
555 | : "memory"); | ||
556 | |||
557 | flush_cache_all(); | ||
558 | } | ||
diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c new file mode 100644 index 000000000000..ff5afab64b2f --- /dev/null +++ b/arch/mips/mm/c-tx39.c | |||
@@ -0,0 +1,493 @@ | |||
1 | /* | ||
2 | * r2300.c: R2000 and R3000 specific mmu/cache code. | ||
3 | * | ||
4 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
5 | * | ||
6 | * with a lot of changes to make this thing work for R3000s | ||
7 | * Tx39XX R4k style caches added. HK | ||
8 | * Copyright (C) 1998, 1999, 2000 Harald Koerfgen | ||
9 | * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov | ||
10 | */ | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/mm.h> | ||
15 | |||
16 | #include <asm/cacheops.h> | ||
17 | #include <asm/page.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/mmu_context.h> | ||
20 | #include <asm/system.h> | ||
21 | #include <asm/isadep.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/bootinfo.h> | ||
24 | #include <asm/cpu.h> | ||
25 | |||
26 | /* For R3000 cores with R4000 style caches */ | ||
27 | static unsigned long icache_size, dcache_size; /* Size in bytes */ | ||
28 | |||
29 | #include <asm/r4kcache.h> | ||
30 | |||
31 | extern int r3k_have_wired_reg; /* in r3k-tlb.c */ | ||
32 | |||
33 | /* This sequence is required to ensure icache is disabled immediately */ | ||
34 | #define TX39_STOP_STREAMING() \ | ||
35 | __asm__ __volatile__( \ | ||
36 | ".set push\n\t" \ | ||
37 | ".set noreorder\n\t" \ | ||
38 | "b 1f\n\t" \ | ||
39 | "nop\n\t" \ | ||
40 | "1:\n\t" \ | ||
41 | ".set pop" \ | ||
42 | ) | ||
43 | |||
44 | /* TX39H-style cache flush routines. */ | ||
45 | static void tx39h_flush_icache_all(void) | ||
46 | { | ||
47 | unsigned long start = KSEG0; | ||
48 | unsigned long end = (start + icache_size); | ||
49 | unsigned long flags, config; | ||
50 | |||
51 | /* disable icache (set ICE#) */ | ||
52 | local_irq_save(flags); | ||
53 | config = read_c0_conf(); | ||
54 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
55 | TX39_STOP_STREAMING(); | ||
56 | |||
57 | /* invalidate icache */ | ||
58 | while (start < end) { | ||
59 | cache16_unroll32(start, Index_Invalidate_I); | ||
60 | start += 0x200; | ||
61 | } | ||
62 | |||
63 | write_c0_conf(config); | ||
64 | local_irq_restore(flags); | ||
65 | } | ||
66 | |||
67 | static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size) | ||
68 | { | ||
69 | unsigned long end, a; | ||
70 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
71 | |||
72 | /* Catch bad driver code */ | ||
73 | BUG_ON(size == 0); | ||
74 | |||
75 | iob(); | ||
76 | a = addr & ~(dc_lsize - 1); | ||
77 | end = (addr + size - 1) & ~(dc_lsize - 1); | ||
78 | while (1) { | ||
79 | invalidate_dcache_line(a); /* Hit_Invalidate_D */ | ||
80 | if (a == end) break; | ||
81 | a += dc_lsize; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | |||
86 | /* TX39H2,TX39H3 */ | ||
87 | static inline void tx39_blast_dcache_page(unsigned long addr) | ||
88 | { | ||
89 | if (current_cpu_data.cputype != CPU_TX3912) | ||
90 | blast_dcache16_page(addr); | ||
91 | } | ||
92 | |||
93 | static inline void tx39_blast_dcache_page_indexed(unsigned long addr) | ||
94 | { | ||
95 | blast_dcache16_page_indexed(addr); | ||
96 | } | ||
97 | |||
98 | static inline void tx39_blast_dcache(void) | ||
99 | { | ||
100 | blast_dcache16(); | ||
101 | } | ||
102 | |||
103 | static inline void tx39_blast_icache_page(unsigned long addr) | ||
104 | { | ||
105 | unsigned long flags, config; | ||
106 | /* disable icache (set ICE#) */ | ||
107 | local_irq_save(flags); | ||
108 | config = read_c0_conf(); | ||
109 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
110 | TX39_STOP_STREAMING(); | ||
111 | blast_icache16_page(addr); | ||
112 | write_c0_conf(config); | ||
113 | local_irq_restore(flags); | ||
114 | } | ||
115 | |||
116 | static inline void tx39_blast_icache_page_indexed(unsigned long addr) | ||
117 | { | ||
118 | unsigned long flags, config; | ||
119 | /* disable icache (set ICE#) */ | ||
120 | local_irq_save(flags); | ||
121 | config = read_c0_conf(); | ||
122 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
123 | TX39_STOP_STREAMING(); | ||
124 | blast_icache16_page_indexed(addr); | ||
125 | write_c0_conf(config); | ||
126 | local_irq_restore(flags); | ||
127 | } | ||
128 | |||
129 | static inline void tx39_blast_icache(void) | ||
130 | { | ||
131 | unsigned long flags, config; | ||
132 | /* disable icache (set ICE#) */ | ||
133 | local_irq_save(flags); | ||
134 | config = read_c0_conf(); | ||
135 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
136 | TX39_STOP_STREAMING(); | ||
137 | blast_icache16(); | ||
138 | write_c0_conf(config); | ||
139 | local_irq_restore(flags); | ||
140 | } | ||
141 | |||
142 | static inline void tx39_flush_cache_all(void) | ||
143 | { | ||
144 | if (!cpu_has_dc_aliases) | ||
145 | return; | ||
146 | |||
147 | tx39_blast_dcache(); | ||
148 | tx39_blast_icache(); | ||
149 | } | ||
150 | |||
151 | static inline void tx39___flush_cache_all(void) | ||
152 | { | ||
153 | tx39_blast_dcache(); | ||
154 | tx39_blast_icache(); | ||
155 | } | ||
156 | |||
157 | static void tx39_flush_cache_mm(struct mm_struct *mm) | ||
158 | { | ||
159 | if (!cpu_has_dc_aliases) | ||
160 | return; | ||
161 | |||
162 | if (cpu_context(smp_processor_id(), mm) != 0) { | ||
163 | tx39_flush_cache_all(); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | static void tx39_flush_cache_range(struct vm_area_struct *vma, | ||
168 | unsigned long start, unsigned long end) | ||
169 | { | ||
170 | struct mm_struct *mm = vma->vm_mm; | ||
171 | |||
172 | if (!cpu_has_dc_aliases) | ||
173 | return; | ||
174 | |||
175 | if (cpu_context(smp_processor_id(), mm) != 0) { | ||
176 | tx39_blast_dcache(); | ||
177 | tx39_blast_icache(); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn) | ||
182 | { | ||
183 | int exec = vma->vm_flags & VM_EXEC; | ||
184 | struct mm_struct *mm = vma->vm_mm; | ||
185 | pgd_t *pgdp; | ||
186 | pmd_t *pmdp; | ||
187 | pte_t *ptep; | ||
188 | |||
189 | /* | ||
190 | * If ownes no valid ASID yet, cannot possibly have gotten | ||
191 | * this page into the cache. | ||
192 | */ | ||
193 | if (cpu_context(smp_processor_id(), mm) == 0) | ||
194 | return; | ||
195 | |||
196 | page &= PAGE_MASK; | ||
197 | pgdp = pgd_offset(mm, page); | ||
198 | pmdp = pmd_offset(pgdp, page); | ||
199 | ptep = pte_offset(pmdp, page); | ||
200 | |||
201 | /* | ||
202 | * If the page isn't marked valid, the page cannot possibly be | ||
203 | * in the cache. | ||
204 | */ | ||
205 | if (!(pte_val(*ptep) & _PAGE_PRESENT)) | ||
206 | return; | ||
207 | |||
208 | /* | ||
209 | * Doing flushes for another ASID than the current one is | ||
210 | * too difficult since stupid R4k caches do a TLB translation | ||
211 | * for every cache flush operation. So we do indexed flushes | ||
212 | * in that case, which doesn't overly flush the cache too much. | ||
213 | */ | ||
214 | if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { | ||
215 | if (cpu_has_dc_aliases || exec) | ||
216 | tx39_blast_dcache_page(page); | ||
217 | if (exec) | ||
218 | tx39_blast_icache_page(page); | ||
219 | |||
220 | return; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Do indexed flush, too much work to get the (possible) TLB refills | ||
225 | * to work correctly. | ||
226 | */ | ||
227 | page = (KSEG0 + (page & (dcache_size - 1))); | ||
228 | if (cpu_has_dc_aliases || exec) | ||
229 | tx39_blast_dcache_page_indexed(page); | ||
230 | if (exec) | ||
231 | tx39_blast_icache_page_indexed(page); | ||
232 | } | ||
233 | |||
234 | static void tx39_flush_data_cache_page(unsigned long addr) | ||
235 | { | ||
236 | tx39_blast_dcache_page(addr); | ||
237 | } | ||
238 | |||
239 | static void tx39_flush_icache_range(unsigned long start, unsigned long end) | ||
240 | { | ||
241 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
242 | unsigned long addr, aend; | ||
243 | |||
244 | if (end - start > dcache_size) | ||
245 | tx39_blast_dcache(); | ||
246 | else { | ||
247 | addr = start & ~(dc_lsize - 1); | ||
248 | aend = (end - 1) & ~(dc_lsize - 1); | ||
249 | |||
250 | while (1) { | ||
251 | /* Hit_Writeback_Inv_D */ | ||
252 | protected_writeback_dcache_line(addr); | ||
253 | if (addr == aend) | ||
254 | break; | ||
255 | addr += dc_lsize; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | if (end - start > icache_size) | ||
260 | tx39_blast_icache(); | ||
261 | else { | ||
262 | unsigned long flags, config; | ||
263 | addr = start & ~(dc_lsize - 1); | ||
264 | aend = (end - 1) & ~(dc_lsize - 1); | ||
265 | /* disable icache (set ICE#) */ | ||
266 | local_irq_save(flags); | ||
267 | config = read_c0_conf(); | ||
268 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
269 | TX39_STOP_STREAMING(); | ||
270 | while (1) { | ||
271 | /* Hit_Invalidate_I */ | ||
272 | protected_flush_icache_line(addr); | ||
273 | if (addr == aend) | ||
274 | break; | ||
275 | addr += dc_lsize; | ||
276 | } | ||
277 | write_c0_conf(config); | ||
278 | local_irq_restore(flags); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Ok, this seriously sucks. We use them to flush a user page but don't | ||
284 | * know the virtual address, so we have to blast away the whole icache | ||
285 | * which is significantly more expensive than the real thing. Otoh we at | ||
286 | * least know the kernel address of the page so we can flush it | ||
287 | * selectivly. | ||
288 | */ | ||
289 | static void tx39_flush_icache_page(struct vm_area_struct *vma, struct page *page) | ||
290 | { | ||
291 | unsigned long addr; | ||
292 | /* | ||
293 | * If there's no context yet, or the page isn't executable, no icache | ||
294 | * flush is needed. | ||
295 | */ | ||
296 | if (!(vma->vm_flags & VM_EXEC)) | ||
297 | return; | ||
298 | |||
299 | addr = (unsigned long) page_address(page); | ||
300 | tx39_blast_dcache_page(addr); | ||
301 | |||
302 | /* | ||
303 | * We're not sure of the virtual address(es) involved here, so | ||
304 | * we have to flush the entire I-cache. | ||
305 | */ | ||
306 | tx39_blast_icache(); | ||
307 | } | ||
308 | |||
309 | static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size) | ||
310 | { | ||
311 | unsigned long end, a; | ||
312 | |||
313 | if (((size | addr) & (PAGE_SIZE - 1)) == 0) { | ||
314 | end = addr + size; | ||
315 | do { | ||
316 | tx39_blast_dcache_page(addr); | ||
317 | addr += PAGE_SIZE; | ||
318 | } while(addr != end); | ||
319 | } else if (size > dcache_size) { | ||
320 | tx39_blast_dcache(); | ||
321 | } else { | ||
322 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
323 | a = addr & ~(dc_lsize - 1); | ||
324 | end = (addr + size - 1) & ~(dc_lsize - 1); | ||
325 | while (1) { | ||
326 | flush_dcache_line(a); /* Hit_Writeback_Inv_D */ | ||
327 | if (a == end) break; | ||
328 | a += dc_lsize; | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | |||
333 | static void tx39_dma_cache_inv(unsigned long addr, unsigned long size) | ||
334 | { | ||
335 | unsigned long end, a; | ||
336 | |||
337 | if (((size | addr) & (PAGE_SIZE - 1)) == 0) { | ||
338 | end = addr + size; | ||
339 | do { | ||
340 | tx39_blast_dcache_page(addr); | ||
341 | addr += PAGE_SIZE; | ||
342 | } while(addr != end); | ||
343 | } else if (size > dcache_size) { | ||
344 | tx39_blast_dcache(); | ||
345 | } else { | ||
346 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
347 | a = addr & ~(dc_lsize - 1); | ||
348 | end = (addr + size - 1) & ~(dc_lsize - 1); | ||
349 | while (1) { | ||
350 | invalidate_dcache_line(a); /* Hit_Invalidate_D */ | ||
351 | if (a == end) break; | ||
352 | a += dc_lsize; | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | static void tx39_flush_cache_sigtramp(unsigned long addr) | ||
358 | { | ||
359 | unsigned long ic_lsize = current_cpu_data.icache.linesz; | ||
360 | unsigned long dc_lsize = current_cpu_data.dcache.linesz; | ||
361 | unsigned long config; | ||
362 | unsigned long flags; | ||
363 | |||
364 | protected_writeback_dcache_line(addr & ~(dc_lsize - 1)); | ||
365 | |||
366 | /* disable icache (set ICE#) */ | ||
367 | local_irq_save(flags); | ||
368 | config = read_c0_conf(); | ||
369 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
370 | TX39_STOP_STREAMING(); | ||
371 | protected_flush_icache_line(addr & ~(ic_lsize - 1)); | ||
372 | write_c0_conf(config); | ||
373 | local_irq_restore(flags); | ||
374 | } | ||
375 | |||
376 | static __init void tx39_probe_cache(void) | ||
377 | { | ||
378 | unsigned long config; | ||
379 | |||
380 | config = read_c0_conf(); | ||
381 | |||
382 | icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >> | ||
383 | TX39_CONF_ICS_SHIFT)); | ||
384 | dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >> | ||
385 | TX39_CONF_DCS_SHIFT)); | ||
386 | |||
387 | current_cpu_data.icache.linesz = 16; | ||
388 | switch (current_cpu_data.cputype) { | ||
389 | case CPU_TX3912: | ||
390 | current_cpu_data.icache.ways = 1; | ||
391 | current_cpu_data.dcache.ways = 1; | ||
392 | current_cpu_data.dcache.linesz = 4; | ||
393 | break; | ||
394 | |||
395 | case CPU_TX3927: | ||
396 | current_cpu_data.icache.ways = 2; | ||
397 | current_cpu_data.dcache.ways = 2; | ||
398 | current_cpu_data.dcache.linesz = 16; | ||
399 | break; | ||
400 | |||
401 | case CPU_TX3922: | ||
402 | default: | ||
403 | current_cpu_data.icache.ways = 1; | ||
404 | current_cpu_data.dcache.ways = 1; | ||
405 | current_cpu_data.dcache.linesz = 16; | ||
406 | break; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | void __init ld_mmu_tx39(void) | ||
411 | { | ||
412 | extern void build_clear_page(void); | ||
413 | extern void build_copy_page(void); | ||
414 | unsigned long config; | ||
415 | |||
416 | config = read_c0_conf(); | ||
417 | config &= ~TX39_CONF_WBON; | ||
418 | write_c0_conf(config); | ||
419 | |||
420 | tx39_probe_cache(); | ||
421 | |||
422 | switch (current_cpu_data.cputype) { | ||
423 | case CPU_TX3912: | ||
424 | /* TX39/H core (writethru direct-map cache) */ | ||
425 | flush_cache_all = tx39h_flush_icache_all; | ||
426 | __flush_cache_all = tx39h_flush_icache_all; | ||
427 | flush_cache_mm = (void *) tx39h_flush_icache_all; | ||
428 | flush_cache_range = (void *) tx39h_flush_icache_all; | ||
429 | flush_cache_page = (void *) tx39h_flush_icache_all; | ||
430 | flush_icache_page = (void *) tx39h_flush_icache_all; | ||
431 | flush_icache_range = (void *) tx39h_flush_icache_all; | ||
432 | |||
433 | flush_cache_sigtramp = (void *) tx39h_flush_icache_all; | ||
434 | flush_data_cache_page = (void *) tx39h_flush_icache_all; | ||
435 | |||
436 | _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; | ||
437 | |||
438 | shm_align_mask = PAGE_SIZE - 1; | ||
439 | |||
440 | break; | ||
441 | |||
442 | case CPU_TX3922: | ||
443 | case CPU_TX3927: | ||
444 | default: | ||
445 | /* TX39/H2,H3 core (writeback 2way-set-associative cache) */ | ||
446 | r3k_have_wired_reg = 1; | ||
447 | write_c0_wired(0); /* set 8 on reset... */ | ||
448 | /* board-dependent init code may set WBON */ | ||
449 | |||
450 | flush_cache_all = tx39_flush_cache_all; | ||
451 | __flush_cache_all = tx39___flush_cache_all; | ||
452 | flush_cache_mm = tx39_flush_cache_mm; | ||
453 | flush_cache_range = tx39_flush_cache_range; | ||
454 | flush_cache_page = tx39_flush_cache_page; | ||
455 | flush_icache_page = tx39_flush_icache_page; | ||
456 | flush_icache_range = tx39_flush_icache_range; | ||
457 | |||
458 | flush_cache_sigtramp = tx39_flush_cache_sigtramp; | ||
459 | flush_data_cache_page = tx39_flush_data_cache_page; | ||
460 | |||
461 | _dma_cache_wback_inv = tx39_dma_cache_wback_inv; | ||
462 | _dma_cache_wback = tx39_dma_cache_wback_inv; | ||
463 | _dma_cache_inv = tx39_dma_cache_inv; | ||
464 | |||
465 | shm_align_mask = max_t(unsigned long, | ||
466 | (dcache_size / current_cpu_data.dcache.ways) - 1, | ||
467 | PAGE_SIZE - 1); | ||
468 | |||
469 | break; | ||
470 | } | ||
471 | |||
472 | current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways; | ||
473 | current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways; | ||
474 | |||
475 | current_cpu_data.icache.sets = | ||
476 | current_cpu_data.icache.waysize / current_cpu_data.icache.linesz; | ||
477 | current_cpu_data.dcache.sets = | ||
478 | current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz; | ||
479 | |||
480 | if (current_cpu_data.dcache.waysize > PAGE_SIZE) | ||
481 | current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES; | ||
482 | |||
483 | current_cpu_data.icache.waybit = 0; | ||
484 | current_cpu_data.dcache.waybit = 0; | ||
485 | |||
486 | printk("Primary instruction cache %ldkB, linesize %d bytes\n", | ||
487 | icache_size >> 10, current_cpu_data.icache.linesz); | ||
488 | printk("Primary data cache %ldkB, linesize %d bytes\n", | ||
489 | dcache_size >> 10, current_cpu_data.dcache.linesz); | ||
490 | |||
491 | build_clear_page(); | ||
492 | build_copy_page(); | ||
493 | } | ||
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c new file mode 100644 index 000000000000..1d95cdb77bed --- /dev/null +++ b/arch/mips/mm/cache.c | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1994 - 2003 by Ralf Baechle | ||
7 | */ | ||
8 | #include <linux/config.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/mm.h> | ||
14 | |||
15 | #include <asm/cacheflush.h> | ||
16 | #include <asm/processor.h> | ||
17 | #include <asm/cpu.h> | ||
18 | #include <asm/cpu-features.h> | ||
19 | |||
20 | /* Cache operations. */ | ||
21 | void (*flush_cache_all)(void); | ||
22 | void (*__flush_cache_all)(void); | ||
23 | void (*flush_cache_mm)(struct mm_struct *mm); | ||
24 | void (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start, | ||
25 | unsigned long end); | ||
26 | void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); | ||
27 | void (*flush_icache_range)(unsigned long start, unsigned long end); | ||
28 | void (*flush_icache_page)(struct vm_area_struct *vma, struct page *page); | ||
29 | |||
30 | /* MIPS specific cache operations */ | ||
31 | void (*flush_cache_sigtramp)(unsigned long addr); | ||
32 | void (*flush_data_cache_page)(unsigned long addr); | ||
33 | void (*flush_icache_all)(void); | ||
34 | |||
35 | #ifdef CONFIG_DMA_NONCOHERENT | ||
36 | |||
37 | /* DMA cache operations. */ | ||
38 | void (*_dma_cache_wback_inv)(unsigned long start, unsigned long size); | ||
39 | void (*_dma_cache_wback)(unsigned long start, unsigned long size); | ||
40 | void (*_dma_cache_inv)(unsigned long start, unsigned long size); | ||
41 | |||
42 | EXPORT_SYMBOL(_dma_cache_wback_inv); | ||
43 | EXPORT_SYMBOL(_dma_cache_wback); | ||
44 | EXPORT_SYMBOL(_dma_cache_inv); | ||
45 | |||
46 | #endif /* CONFIG_DMA_NONCOHERENT */ | ||
47 | |||
48 | /* | ||
49 | * We could optimize the case where the cache argument is not BCACHE but | ||
50 | * that seems very atypical use ... | ||
51 | */ | ||
52 | asmlinkage int sys_cacheflush(unsigned long addr, unsigned long int bytes, | ||
53 | unsigned int cache) | ||
54 | { | ||
55 | if (!access_ok(VERIFY_WRITE, (void *) addr, bytes)) | ||
56 | return -EFAULT; | ||
57 | |||
58 | flush_icache_range(addr, addr + bytes); | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | void __flush_dcache_page(struct page *page) | ||
64 | { | ||
65 | struct address_space *mapping = page_mapping(page); | ||
66 | unsigned long addr; | ||
67 | |||
68 | if (mapping && !mapping_mapped(mapping)) { | ||
69 | SetPageDcacheDirty(page); | ||
70 | return; | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * We could delay the flush for the !page_mapping case too. But that | ||
75 | * case is for exec env/arg pages and those are %99 certainly going to | ||
76 | * get faulted into the tlb (and thus flushed) anyways. | ||
77 | */ | ||
78 | addr = (unsigned long) page_address(page); | ||
79 | flush_data_cache_page(addr); | ||
80 | } | ||
81 | |||
82 | EXPORT_SYMBOL(__flush_dcache_page); | ||
83 | |||
84 | void __update_cache(struct vm_area_struct *vma, unsigned long address, | ||
85 | pte_t pte) | ||
86 | { | ||
87 | struct page *page; | ||
88 | unsigned long pfn, addr; | ||
89 | |||
90 | pfn = pte_pfn(pte); | ||
91 | if (pfn_valid(pfn) && (page = pfn_to_page(pfn), page_mapping(page)) && | ||
92 | Page_dcache_dirty(page)) { | ||
93 | if (pages_do_alias((unsigned long)page_address(page), | ||
94 | address & PAGE_MASK)) { | ||
95 | addr = (unsigned long) page_address(page); | ||
96 | flush_data_cache_page(addr); | ||
97 | } | ||
98 | |||
99 | ClearPageDcacheDirty(page); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | extern void ld_mmu_r23000(void); | ||
104 | extern void ld_mmu_r4xx0(void); | ||
105 | extern void ld_mmu_tx39(void); | ||
106 | extern void ld_mmu_r6000(void); | ||
107 | extern void ld_mmu_tfp(void); | ||
108 | extern void ld_mmu_andes(void); | ||
109 | extern void ld_mmu_sb1(void); | ||
110 | |||
111 | void __init cpu_cache_init(void) | ||
112 | { | ||
113 | if (cpu_has_4ktlb) { | ||
114 | #if defined(CONFIG_CPU_R4X00) || defined(CONFIG_CPU_VR41XX) || \ | ||
115 | defined(CONFIG_CPU_R4300) || defined(CONFIG_CPU_R5000) || \ | ||
116 | defined(CONFIG_CPU_NEVADA) || defined(CONFIG_CPU_R5432) || \ | ||
117 | defined(CONFIG_CPU_R5500) || defined(CONFIG_CPU_MIPS32) || \ | ||
118 | defined(CONFIG_CPU_MIPS64) || defined(CONFIG_CPU_TX49XX) || \ | ||
119 | defined(CONFIG_CPU_RM7000) || defined(CONFIG_CPU_RM9000) | ||
120 | ld_mmu_r4xx0(); | ||
121 | #endif | ||
122 | } else switch (current_cpu_data.cputype) { | ||
123 | #ifdef CONFIG_CPU_R3000 | ||
124 | case CPU_R2000: | ||
125 | case CPU_R3000: | ||
126 | case CPU_R3000A: | ||
127 | case CPU_R3081E: | ||
128 | ld_mmu_r23000(); | ||
129 | break; | ||
130 | #endif | ||
131 | #ifdef CONFIG_CPU_TX39XX | ||
132 | case CPU_TX3912: | ||
133 | case CPU_TX3922: | ||
134 | case CPU_TX3927: | ||
135 | ld_mmu_tx39(); | ||
136 | break; | ||
137 | #endif | ||
138 | #ifdef CONFIG_CPU_R10000 | ||
139 | case CPU_R10000: | ||
140 | case CPU_R12000: | ||
141 | ld_mmu_r4xx0(); | ||
142 | break; | ||
143 | #endif | ||
144 | #ifdef CONFIG_CPU_SB1 | ||
145 | case CPU_SB1: | ||
146 | ld_mmu_sb1(); | ||
147 | break; | ||
148 | #endif | ||
149 | |||
150 | case CPU_R8000: | ||
151 | panic("R8000 is unsupported"); | ||
152 | break; | ||
153 | |||
154 | default: | ||
155 | panic("Yeee, unsupported cache architecture."); | ||
156 | } | ||
157 | } | ||
diff --git a/arch/mips/mm/cerr-sb1.c b/arch/mips/mm/cerr-sb1.c new file mode 100644 index 000000000000..13d96d62764e --- /dev/null +++ b/arch/mips/mm/cerr-sb1.c | |||
@@ -0,0 +1,543 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001,2002,2003 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version 2 | ||
7 | * of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | */ | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <asm/mipsregs.h> | ||
21 | #include <asm/sibyte/sb1250.h> | ||
22 | |||
23 | #ifndef CONFIG_SIBYTE_BUS_WATCHER | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/sibyte/sb1250_regs.h> | ||
26 | #include <asm/sibyte/sb1250_scd.h> | ||
27 | #endif | ||
28 | |||
29 | /* SB1 definitions */ | ||
30 | |||
31 | /* XXX should come from config1 XXX */ | ||
32 | #define SB1_CACHE_INDEX_MASK 0x1fe0 | ||
33 | |||
34 | #define CP0_ERRCTL_RECOVERABLE (1 << 31) | ||
35 | #define CP0_ERRCTL_DCACHE (1 << 30) | ||
36 | #define CP0_ERRCTL_ICACHE (1 << 29) | ||
37 | #define CP0_ERRCTL_MULTIBUS (1 << 23) | ||
38 | #define CP0_ERRCTL_MC_TLB (1 << 15) | ||
39 | #define CP0_ERRCTL_MC_TIMEOUT (1 << 14) | ||
40 | |||
41 | #define CP0_CERRI_TAG_PARITY (1 << 29) | ||
42 | #define CP0_CERRI_DATA_PARITY (1 << 28) | ||
43 | #define CP0_CERRI_EXTERNAL (1 << 26) | ||
44 | |||
45 | #define CP0_CERRI_IDX_VALID(c) (!((c) & CP0_CERRI_EXTERNAL)) | ||
46 | #define CP0_CERRI_DATA (CP0_CERRI_DATA_PARITY) | ||
47 | |||
48 | #define CP0_CERRD_MULTIPLE (1 << 31) | ||
49 | #define CP0_CERRD_TAG_STATE (1 << 30) | ||
50 | #define CP0_CERRD_TAG_ADDRESS (1 << 29) | ||
51 | #define CP0_CERRD_DATA_SBE (1 << 28) | ||
52 | #define CP0_CERRD_DATA_DBE (1 << 27) | ||
53 | #define CP0_CERRD_EXTERNAL (1 << 26) | ||
54 | #define CP0_CERRD_LOAD (1 << 25) | ||
55 | #define CP0_CERRD_STORE (1 << 24) | ||
56 | #define CP0_CERRD_FILLWB (1 << 23) | ||
57 | #define CP0_CERRD_COHERENCY (1 << 22) | ||
58 | #define CP0_CERRD_DUPTAG (1 << 21) | ||
59 | |||
60 | #define CP0_CERRD_DPA_VALID(c) (!((c) & CP0_CERRD_EXTERNAL)) | ||
61 | #define CP0_CERRD_IDX_VALID(c) \ | ||
62 | (((c) & (CP0_CERRD_LOAD | CP0_CERRD_STORE)) ? (!((c) & CP0_CERRD_EXTERNAL)) : 0) | ||
63 | #define CP0_CERRD_CAUSES \ | ||
64 | (CP0_CERRD_LOAD | CP0_CERRD_STORE | CP0_CERRD_FILLWB | CP0_CERRD_COHERENCY | CP0_CERRD_DUPTAG) | ||
65 | #define CP0_CERRD_TYPES \ | ||
66 | (CP0_CERRD_TAG_STATE | CP0_CERRD_TAG_ADDRESS | CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE | CP0_CERRD_EXTERNAL) | ||
67 | #define CP0_CERRD_DATA (CP0_CERRD_DATA_SBE | CP0_CERRD_DATA_DBE) | ||
68 | |||
69 | static uint32_t extract_ic(unsigned short addr, int data); | ||
70 | static uint32_t extract_dc(unsigned short addr, int data); | ||
71 | |||
72 | static inline void breakout_errctl(unsigned int val) | ||
73 | { | ||
74 | if (val & CP0_ERRCTL_RECOVERABLE) | ||
75 | prom_printf(" recoverable"); | ||
76 | if (val & CP0_ERRCTL_DCACHE) | ||
77 | prom_printf(" dcache"); | ||
78 | if (val & CP0_ERRCTL_ICACHE) | ||
79 | prom_printf(" icache"); | ||
80 | if (val & CP0_ERRCTL_MULTIBUS) | ||
81 | prom_printf(" multiple-buserr"); | ||
82 | prom_printf("\n"); | ||
83 | } | ||
84 | |||
85 | static inline void breakout_cerri(unsigned int val) | ||
86 | { | ||
87 | if (val & CP0_CERRI_TAG_PARITY) | ||
88 | prom_printf(" tag-parity"); | ||
89 | if (val & CP0_CERRI_DATA_PARITY) | ||
90 | prom_printf(" data-parity"); | ||
91 | if (val & CP0_CERRI_EXTERNAL) | ||
92 | prom_printf(" external"); | ||
93 | prom_printf("\n"); | ||
94 | } | ||
95 | |||
96 | static inline void breakout_cerrd(unsigned int val) | ||
97 | { | ||
98 | switch (val & CP0_CERRD_CAUSES) { | ||
99 | case CP0_CERRD_LOAD: | ||
100 | prom_printf(" load,"); | ||
101 | break; | ||
102 | case CP0_CERRD_STORE: | ||
103 | prom_printf(" store,"); | ||
104 | break; | ||
105 | case CP0_CERRD_FILLWB: | ||
106 | prom_printf(" fill/wb,"); | ||
107 | break; | ||
108 | case CP0_CERRD_COHERENCY: | ||
109 | prom_printf(" coherency,"); | ||
110 | break; | ||
111 | case CP0_CERRD_DUPTAG: | ||
112 | prom_printf(" duptags,"); | ||
113 | break; | ||
114 | default: | ||
115 | prom_printf(" NO CAUSE,"); | ||
116 | break; | ||
117 | } | ||
118 | if (!(val & CP0_CERRD_TYPES)) | ||
119 | prom_printf(" NO TYPE"); | ||
120 | else { | ||
121 | if (val & CP0_CERRD_MULTIPLE) | ||
122 | prom_printf(" multi-err"); | ||
123 | if (val & CP0_CERRD_TAG_STATE) | ||
124 | prom_printf(" tag-state"); | ||
125 | if (val & CP0_CERRD_TAG_ADDRESS) | ||
126 | prom_printf(" tag-address"); | ||
127 | if (val & CP0_CERRD_DATA_SBE) | ||
128 | prom_printf(" data-SBE"); | ||
129 | if (val & CP0_CERRD_DATA_DBE) | ||
130 | prom_printf(" data-DBE"); | ||
131 | if (val & CP0_CERRD_EXTERNAL) | ||
132 | prom_printf(" external"); | ||
133 | } | ||
134 | prom_printf("\n"); | ||
135 | } | ||
136 | |||
137 | #ifndef CONFIG_SIBYTE_BUS_WATCHER | ||
138 | |||
139 | static void check_bus_watcher(void) | ||
140 | { | ||
141 | uint32_t status, l2_err, memio_err; | ||
142 | |||
143 | /* Destructive read, clears register and interrupt */ | ||
144 | status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS)); | ||
145 | /* Bit 31 is always on, but there's no #define for that */ | ||
146 | if (status & ~(1UL << 31)) { | ||
147 | l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS)); | ||
148 | memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS)); | ||
149 | prom_printf("Bus watcher error counters: %08x %08x\n", l2_err, memio_err); | ||
150 | prom_printf("\nLast recorded signature:\n"); | ||
151 | prom_printf("Request %02x from %d, answered by %d with Dcode %d\n", | ||
152 | (unsigned int)(G_SCD_BERR_TID(status) & 0x3f), | ||
153 | (int)(G_SCD_BERR_TID(status) >> 6), | ||
154 | (int)G_SCD_BERR_RID(status), | ||
155 | (int)G_SCD_BERR_DCODE(status)); | ||
156 | } else { | ||
157 | prom_printf("Bus watcher indicates no error\n"); | ||
158 | } | ||
159 | } | ||
160 | #else | ||
161 | extern void check_bus_watcher(void); | ||
162 | #endif | ||
163 | |||
164 | asmlinkage void sb1_cache_error(void) | ||
165 | { | ||
166 | uint64_t cerr_dpa; | ||
167 | uint32_t errctl, cerr_i, cerr_d, dpalo, dpahi, eepc, res; | ||
168 | |||
169 | prom_printf("Cache error exception on CPU %x:\n", | ||
170 | (read_c0_prid() >> 25) & 0x7); | ||
171 | |||
172 | __asm__ __volatile__ ( | ||
173 | " .set push\n\t" | ||
174 | " .set mips64\n\t" | ||
175 | " .set noat\n\t" | ||
176 | " mfc0 %0, $26\n\t" | ||
177 | " mfc0 %1, $27\n\t" | ||
178 | " mfc0 %2, $27, 1\n\t" | ||
179 | " dmfc0 $1, $27, 3\n\t" | ||
180 | " dsrl32 %3, $1, 0 \n\t" | ||
181 | " sll %4, $1, 0 \n\t" | ||
182 | " mfc0 %5, $30\n\t" | ||
183 | " .set pop" | ||
184 | : "=r" (errctl), "=r" (cerr_i), "=r" (cerr_d), | ||
185 | "=r" (dpahi), "=r" (dpalo), "=r" (eepc)); | ||
186 | |||
187 | cerr_dpa = (((uint64_t)dpahi) << 32) | dpalo; | ||
188 | prom_printf(" c0_errorepc == %08x\n", eepc); | ||
189 | prom_printf(" c0_errctl == %08x", errctl); | ||
190 | breakout_errctl(errctl); | ||
191 | if (errctl & CP0_ERRCTL_ICACHE) { | ||
192 | prom_printf(" c0_cerr_i == %08x", cerr_i); | ||
193 | breakout_cerri(cerr_i); | ||
194 | if (CP0_CERRI_IDX_VALID(cerr_i)) { | ||
195 | /* Check index of EPC, allowing for delay slot */ | ||
196 | if (((eepc & SB1_CACHE_INDEX_MASK) != (cerr_i & SB1_CACHE_INDEX_MASK)) && | ||
197 | ((eepc & SB1_CACHE_INDEX_MASK) != ((cerr_i & SB1_CACHE_INDEX_MASK) - 4))) | ||
198 | prom_printf(" cerr_i idx doesn't match eepc\n"); | ||
199 | else { | ||
200 | res = extract_ic(cerr_i & SB1_CACHE_INDEX_MASK, | ||
201 | (cerr_i & CP0_CERRI_DATA) != 0); | ||
202 | if (!(res & cerr_i)) | ||
203 | prom_printf("...didn't see indicated icache problem\n"); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | if (errctl & CP0_ERRCTL_DCACHE) { | ||
208 | prom_printf(" c0_cerr_d == %08x", cerr_d); | ||
209 | breakout_cerrd(cerr_d); | ||
210 | if (CP0_CERRD_DPA_VALID(cerr_d)) { | ||
211 | prom_printf(" c0_cerr_dpa == %010llx\n", cerr_dpa); | ||
212 | if (!CP0_CERRD_IDX_VALID(cerr_d)) { | ||
213 | res = extract_dc(cerr_dpa & SB1_CACHE_INDEX_MASK, | ||
214 | (cerr_d & CP0_CERRD_DATA) != 0); | ||
215 | if (!(res & cerr_d)) | ||
216 | prom_printf("...didn't see indicated dcache problem\n"); | ||
217 | } else { | ||
218 | if ((cerr_dpa & SB1_CACHE_INDEX_MASK) != (cerr_d & SB1_CACHE_INDEX_MASK)) | ||
219 | prom_printf(" cerr_d idx doesn't match cerr_dpa\n"); | ||
220 | else { | ||
221 | res = extract_dc(cerr_d & SB1_CACHE_INDEX_MASK, | ||
222 | (cerr_d & CP0_CERRD_DATA) != 0); | ||
223 | if (!(res & cerr_d)) | ||
224 | prom_printf("...didn't see indicated problem\n"); | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | check_bus_watcher(); | ||
231 | |||
232 | while (1); | ||
233 | /* | ||
234 | * This tends to make things get really ugly; let's just stall instead. | ||
235 | * panic("Can't handle the cache error!"); | ||
236 | */ | ||
237 | } | ||
238 | |||
239 | |||
240 | /* Parity lookup table. */ | ||
241 | static const uint8_t parity[256] = { | ||
242 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, | ||
243 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, | ||
244 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, | ||
245 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, | ||
246 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, | ||
247 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, | ||
248 | 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, | ||
249 | 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 | ||
250 | }; | ||
251 | |||
252 | /* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */ | ||
253 | static const uint64_t mask_72_64[8] = { | ||
254 | 0x0738C808099264FFULL, | ||
255 | 0x38C808099264FF07ULL, | ||
256 | 0xC808099264FF0738ULL, | ||
257 | 0x08099264FF0738C8ULL, | ||
258 | 0x099264FF0738C808ULL, | ||
259 | 0x9264FF0738C80809ULL, | ||
260 | 0x64FF0738C8080992ULL, | ||
261 | 0xFF0738C808099264ULL | ||
262 | }; | ||
263 | |||
264 | /* Calculate the parity on a range of bits */ | ||
265 | static char range_parity(uint64_t dword, int max, int min) | ||
266 | { | ||
267 | char parity = 0; | ||
268 | int i; | ||
269 | dword >>= min; | ||
270 | for (i=max-min; i>=0; i--) { | ||
271 | if (dword & 0x1) | ||
272 | parity = !parity; | ||
273 | dword >>= 1; | ||
274 | } | ||
275 | return parity; | ||
276 | } | ||
277 | |||
278 | /* Calculate the 4-bit even byte-parity for an instruction */ | ||
279 | static unsigned char inst_parity(uint32_t word) | ||
280 | { | ||
281 | int i, j; | ||
282 | char parity = 0; | ||
283 | for (j=0; j<4; j++) { | ||
284 | char byte_parity = 0; | ||
285 | for (i=0; i<8; i++) { | ||
286 | if (word & 0x80000000) | ||
287 | byte_parity = !byte_parity; | ||
288 | word <<= 1; | ||
289 | } | ||
290 | parity <<= 1; | ||
291 | parity |= byte_parity; | ||
292 | } | ||
293 | return parity; | ||
294 | } | ||
295 | |||
296 | static uint32_t extract_ic(unsigned short addr, int data) | ||
297 | { | ||
298 | unsigned short way; | ||
299 | int valid; | ||
300 | uint64_t taglo, va, tlo_tmp; | ||
301 | uint32_t taghi, taglolo, taglohi; | ||
302 | uint8_t lru; | ||
303 | int res = 0; | ||
304 | |||
305 | prom_printf("Icache index 0x%04x ", addr); | ||
306 | for (way = 0; way < 4; way++) { | ||
307 | /* Index-load-tag-I */ | ||
308 | __asm__ __volatile__ ( | ||
309 | " .set push \n\t" | ||
310 | " .set noreorder \n\t" | ||
311 | " .set mips64 \n\t" | ||
312 | " .set noat \n\t" | ||
313 | " cache 4, 0(%3) \n\t" | ||
314 | " mfc0 %0, $29 \n\t" | ||
315 | " dmfc0 $1, $28 \n\t" | ||
316 | " dsrl32 %1, $1, 0 \n\t" | ||
317 | " sll %2, $1, 0 \n\t" | ||
318 | " .set pop" | ||
319 | : "=r" (taghi), "=r" (taglohi), "=r" (taglolo) | ||
320 | : "r" ((way << 13) | addr)); | ||
321 | |||
322 | taglo = ((unsigned long long)taglohi << 32) | taglolo; | ||
323 | if (way == 0) { | ||
324 | lru = (taghi >> 14) & 0xff; | ||
325 | prom_printf("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n", | ||
326 | ((addr >> 5) & 0x3), /* bank */ | ||
327 | ((addr >> 7) & 0x3f), /* index */ | ||
328 | (lru & 0x3), | ||
329 | ((lru >> 2) & 0x3), | ||
330 | ((lru >> 4) & 0x3), | ||
331 | ((lru >> 6) & 0x3)); | ||
332 | } | ||
333 | va = (taglo & 0xC0000FFFFFFFE000ULL) | addr; | ||
334 | if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3)) | ||
335 | va |= 0x3FFFF00000000000ULL; | ||
336 | valid = ((taghi >> 29) & 1); | ||
337 | if (valid) { | ||
338 | tlo_tmp = taglo & 0xfff3ff; | ||
339 | if (((taglo >> 10) & 1) ^ range_parity(tlo_tmp, 23, 0)) { | ||
340 | prom_printf(" ** bad parity in VTag0/G/ASID\n"); | ||
341 | res |= CP0_CERRI_TAG_PARITY; | ||
342 | } | ||
343 | if (((taglo >> 11) & 1) ^ range_parity(taglo, 63, 24)) { | ||
344 | prom_printf(" ** bad parity in R/VTag1\n"); | ||
345 | res |= CP0_CERRI_TAG_PARITY; | ||
346 | } | ||
347 | } | ||
348 | if (valid ^ ((taghi >> 27) & 1)) { | ||
349 | prom_printf(" ** bad parity for valid bit\n"); | ||
350 | res |= CP0_CERRI_TAG_PARITY; | ||
351 | } | ||
352 | prom_printf(" %d [VA %016llx] [Vld? %d] raw tags: %08X-%016llX\n", | ||
353 | way, va, valid, taghi, taglo); | ||
354 | |||
355 | if (data) { | ||
356 | uint32_t datahi, insta, instb; | ||
357 | uint8_t predecode; | ||
358 | int offset; | ||
359 | |||
360 | /* (hit all banks and ways) */ | ||
361 | for (offset = 0; offset < 4; offset++) { | ||
362 | /* Index-load-data-I */ | ||
363 | __asm__ __volatile__ ( | ||
364 | " .set push\n\t" | ||
365 | " .set noreorder\n\t" | ||
366 | " .set mips64\n\t" | ||
367 | " .set noat\n\t" | ||
368 | " cache 6, 0(%3) \n\t" | ||
369 | " mfc0 %0, $29, 1\n\t" | ||
370 | " dmfc0 $1, $28, 1\n\t" | ||
371 | " dsrl32 %1, $1, 0 \n\t" | ||
372 | " sll %2, $1, 0 \n\t" | ||
373 | " .set pop \n" | ||
374 | : "=r" (datahi), "=r" (insta), "=r" (instb) | ||
375 | : "r" ((way << 13) | addr | (offset << 3))); | ||
376 | predecode = (datahi >> 8) & 0xff; | ||
377 | if (((datahi >> 16) & 1) != (uint32_t)range_parity(predecode, 7, 0)) { | ||
378 | prom_printf(" ** bad parity in predecode\n"); | ||
379 | res |= CP0_CERRI_DATA_PARITY; | ||
380 | } | ||
381 | /* XXXKW should/could check predecode bits themselves */ | ||
382 | if (((datahi >> 4) & 0xf) ^ inst_parity(insta)) { | ||
383 | prom_printf(" ** bad parity in instruction a\n"); | ||
384 | res |= CP0_CERRI_DATA_PARITY; | ||
385 | } | ||
386 | if ((datahi & 0xf) ^ inst_parity(instb)) { | ||
387 | prom_printf(" ** bad parity in instruction b\n"); | ||
388 | res |= CP0_CERRI_DATA_PARITY; | ||
389 | } | ||
390 | prom_printf(" %05X-%08X%08X", datahi, insta, instb); | ||
391 | } | ||
392 | prom_printf("\n"); | ||
393 | } | ||
394 | } | ||
395 | return res; | ||
396 | } | ||
397 | |||
398 | /* Compute the ECC for a data doubleword */ | ||
399 | static uint8_t dc_ecc(uint64_t dword) | ||
400 | { | ||
401 | uint64_t t; | ||
402 | uint32_t w; | ||
403 | uint8_t p; | ||
404 | int i; | ||
405 | |||
406 | p = 0; | ||
407 | for (i = 7; i >= 0; i--) | ||
408 | { | ||
409 | p <<= 1; | ||
410 | t = dword & mask_72_64[i]; | ||
411 | w = (uint32_t)(t >> 32); | ||
412 | p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF] | ||
413 | ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]); | ||
414 | w = (uint32_t)(t & 0xFFFFFFFF); | ||
415 | p ^= (parity[w>>24] ^ parity[(w>>16) & 0xFF] | ||
416 | ^ parity[(w>>8) & 0xFF] ^ parity[w & 0xFF]); | ||
417 | } | ||
418 | return p; | ||
419 | } | ||
420 | |||
421 | struct dc_state { | ||
422 | unsigned char val; | ||
423 | char *name; | ||
424 | }; | ||
425 | |||
426 | static struct dc_state dc_states[] = { | ||
427 | { 0x00, "INVALID" }, | ||
428 | { 0x0f, "COH-SHD" }, | ||
429 | { 0x13, "NCO-E-C" }, | ||
430 | { 0x19, "NCO-E-D" }, | ||
431 | { 0x16, "COH-E-C" }, | ||
432 | { 0x1c, "COH-E-D" }, | ||
433 | { 0xff, "*ERROR*" } | ||
434 | }; | ||
435 | |||
436 | #define DC_TAG_VALID(state) \ | ||
437 | (((state) == 0xf) || ((state) == 0x13) || ((state) == 0x19) || ((state == 0x16)) || ((state) == 0x1c)) | ||
438 | |||
439 | static char *dc_state_str(unsigned char state) | ||
440 | { | ||
441 | struct dc_state *dsc = dc_states; | ||
442 | while (dsc->val != 0xff) { | ||
443 | if (dsc->val == state) | ||
444 | break; | ||
445 | dsc++; | ||
446 | } | ||
447 | return dsc->name; | ||
448 | } | ||
449 | |||
450 | static uint32_t extract_dc(unsigned short addr, int data) | ||
451 | { | ||
452 | int valid, way; | ||
453 | unsigned char state; | ||
454 | uint64_t taglo, pa; | ||
455 | uint32_t taghi, taglolo, taglohi; | ||
456 | uint8_t ecc, lru; | ||
457 | int res = 0; | ||
458 | |||
459 | prom_printf("Dcache index 0x%04x ", addr); | ||
460 | for (way = 0; way < 4; way++) { | ||
461 | __asm__ __volatile__ ( | ||
462 | " .set push\n\t" | ||
463 | " .set noreorder\n\t" | ||
464 | " .set mips64\n\t" | ||
465 | " .set noat\n\t" | ||
466 | " cache 5, 0(%3)\n\t" /* Index-load-tag-D */ | ||
467 | " mfc0 %0, $29, 2\n\t" | ||
468 | " dmfc0 $1, $28, 2\n\t" | ||
469 | " dsrl32 %1, $1, 0\n\t" | ||
470 | " sll %2, $1, 0\n\t" | ||
471 | " .set pop" | ||
472 | : "=r" (taghi), "=r" (taglohi), "=r" (taglolo) | ||
473 | : "r" ((way << 13) | addr)); | ||
474 | |||
475 | taglo = ((unsigned long long)taglohi << 32) | taglolo; | ||
476 | pa = (taglo & 0xFFFFFFE000ULL) | addr; | ||
477 | if (way == 0) { | ||
478 | lru = (taghi >> 14) & 0xff; | ||
479 | prom_printf("[Bank %d Set 0x%02x] LRU > %d %d %d %d > MRU\n", | ||
480 | ((addr >> 11) & 0x2) | ((addr >> 5) & 1), /* bank */ | ||
481 | ((addr >> 6) & 0x3f), /* index */ | ||
482 | (lru & 0x3), | ||
483 | ((lru >> 2) & 0x3), | ||
484 | ((lru >> 4) & 0x3), | ||
485 | ((lru >> 6) & 0x3)); | ||
486 | } | ||
487 | state = (taghi >> 25) & 0x1f; | ||
488 | valid = DC_TAG_VALID(state); | ||
489 | prom_printf(" %d [PA %010llx] [state %s (%02x)] raw tags: %08X-%016llX\n", | ||
490 | way, pa, dc_state_str(state), state, taghi, taglo); | ||
491 | if (valid) { | ||
492 | if (((taglo >> 11) & 1) ^ range_parity(taglo, 39, 26)) { | ||
493 | prom_printf(" ** bad parity in PTag1\n"); | ||
494 | res |= CP0_CERRD_TAG_ADDRESS; | ||
495 | } | ||
496 | if (((taglo >> 10) & 1) ^ range_parity(taglo, 25, 13)) { | ||
497 | prom_printf(" ** bad parity in PTag0\n"); | ||
498 | res |= CP0_CERRD_TAG_ADDRESS; | ||
499 | } | ||
500 | } else { | ||
501 | res |= CP0_CERRD_TAG_STATE; | ||
502 | } | ||
503 | |||
504 | if (data) { | ||
505 | uint64_t datalo; | ||
506 | uint32_t datalohi, datalolo, datahi; | ||
507 | int offset; | ||
508 | |||
509 | for (offset = 0; offset < 4; offset++) { | ||
510 | /* Index-load-data-D */ | ||
511 | __asm__ __volatile__ ( | ||
512 | " .set push\n\t" | ||
513 | " .set noreorder\n\t" | ||
514 | " .set mips64\n\t" | ||
515 | " .set noat\n\t" | ||
516 | " cache 7, 0(%3)\n\t" /* Index-load-data-D */ | ||
517 | " mfc0 %0, $29, 3\n\t" | ||
518 | " dmfc0 $1, $28, 3\n\t" | ||
519 | " dsrl32 %1, $1, 0 \n\t" | ||
520 | " sll %2, $1, 0 \n\t" | ||
521 | " .set pop" | ||
522 | : "=r" (datahi), "=r" (datalohi), "=r" (datalolo) | ||
523 | : "r" ((way << 13) | addr | (offset << 3))); | ||
524 | datalo = ((unsigned long long)datalohi << 32) | datalolo; | ||
525 | ecc = dc_ecc(datalo); | ||
526 | if (ecc != datahi) { | ||
527 | int bits = 0; | ||
528 | prom_printf(" ** bad ECC (%02x %02x) ->", | ||
529 | datahi, ecc); | ||
530 | ecc ^= datahi; | ||
531 | while (ecc) { | ||
532 | if (ecc & 1) bits++; | ||
533 | ecc >>= 1; | ||
534 | } | ||
535 | res |= (bits == 1) ? CP0_CERRD_DATA_SBE : CP0_CERRD_DATA_DBE; | ||
536 | } | ||
537 | prom_printf(" %02X-%016llX", datahi, datalo); | ||
538 | } | ||
539 | prom_printf("\n"); | ||
540 | } | ||
541 | } | ||
542 | return res; | ||
543 | } | ||
diff --git a/arch/mips/mm/cex-gen.S b/arch/mips/mm/cex-gen.S new file mode 100644 index 000000000000..e743622fd24d --- /dev/null +++ b/arch/mips/mm/cex-gen.S | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1995 - 1999 Ralf Baechle | ||
7 | * Copyright (C) 1999 Silicon Graphics, Inc. | ||
8 | * | ||
9 | * Cache error handler | ||
10 | */ | ||
11 | #include <asm/asm.h> | ||
12 | #include <asm/regdef.h> | ||
13 | #include <asm/mipsregs.h> | ||
14 | #include <asm/stackframe.h> | ||
15 | |||
16 | /* | ||
17 | * Game over. Go to the button. Press gently. Swear where allowed by | ||
18 | * legislation. | ||
19 | */ | ||
20 | LEAF(except_vec2_generic) | ||
21 | .set noreorder | ||
22 | .set noat | ||
23 | .set mips0 | ||
24 | /* | ||
25 | * This is a very bad place to be. Our cache error | ||
26 | * detection has triggered. If we have write-back data | ||
27 | * in the cache, we may not be able to recover. As a | ||
28 | * first-order desperate measure, turn off KSEG0 cacheing. | ||
29 | */ | ||
30 | mfc0 k0,CP0_CONFIG | ||
31 | li k1,~CONF_CM_CMASK | ||
32 | and k0,k0,k1 | ||
33 | ori k0,k0,CONF_CM_UNCACHED | ||
34 | mtc0 k0,CP0_CONFIG | ||
35 | /* Give it a few cycles to sink in... */ | ||
36 | nop | ||
37 | nop | ||
38 | nop | ||
39 | |||
40 | j cache_parity_error | ||
41 | nop | ||
42 | END(except_vec2_generic) | ||
diff --git a/arch/mips/mm/cex-sb1.S b/arch/mips/mm/cex-sb1.S new file mode 100644 index 000000000000..2c3a23aa88c3 --- /dev/null +++ b/arch/mips/mm/cex-sb1.S | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001,2002,2003 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version 2 | ||
7 | * of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | */ | ||
18 | #include <linux/init.h> | ||
19 | |||
20 | #include <asm/asm.h> | ||
21 | #include <asm/regdef.h> | ||
22 | #include <asm/mipsregs.h> | ||
23 | #include <asm/stackframe.h> | ||
24 | #include <asm/cacheops.h> | ||
25 | #include <asm/sibyte/board.h> | ||
26 | |||
27 | #define C0_ERRCTL $26 /* CP0: Error info */ | ||
28 | #define C0_CERR_I $27 /* CP0: Icache error */ | ||
29 | #define C0_CERR_D $27,1 /* CP0: Dcache error */ | ||
30 | |||
31 | /* | ||
32 | * Based on SiByte sample software cache-err/cerr.S | ||
33 | * CVS revision 1.8. Only the 'unrecoverable' case | ||
34 | * is changed. | ||
35 | */ | ||
36 | |||
37 | __INIT | ||
38 | |||
39 | .set mips64 | ||
40 | .set noreorder | ||
41 | .set noat | ||
42 | |||
43 | /* | ||
44 | * sb1_cerr_vec: code to be copied to the Cache Error | ||
45 | * Exception vector. The code must be pushed out to memory | ||
46 | * (either by copying to Kseg0 and Kseg1 both, or by flushing | ||
47 | * the L1 and L2) since it is fetched as 0xa0000100. | ||
48 | * | ||
49 | * NOTE: Be sure this handler is at most 28 instructions long | ||
50 | * since the final 16 bytes of the exception vector memory | ||
51 | * (0x170-0x17f) are used to preserve k0, k1, and ra. | ||
52 | */ | ||
53 | |||
54 | LEAF(except_vec2_sb1) | ||
55 | /* | ||
56 | * If this error is recoverable, we need to exit the handler | ||
57 | * without having dirtied any registers. To do this, | ||
58 | * save/restore k0 and k1 from low memory (Useg is direct | ||
59 | * mapped while ERL=1). Note that we can't save to a | ||
60 | * CPU-specific location without ruining a register in the | ||
61 | * process. This means we are vulnerable to data corruption | ||
62 | * whenever the handler is reentered by a second CPU. | ||
63 | */ | ||
64 | sd k0,0x170($0) | ||
65 | sd k1,0x178($0) | ||
66 | |||
67 | /* | ||
68 | * M_ERRCTL_RECOVERABLE is bit 31, which makes it easy to tell | ||
69 | * if we can fast-path out of here for a h/w-recovered error. | ||
70 | */ | ||
71 | mfc0 k1,C0_ERRCTL | ||
72 | bgtz k1,attempt_recovery | ||
73 | sll k0,k1,1 | ||
74 | |||
75 | recovered_dcache: | ||
76 | /* | ||
77 | * Unlock CacheErr-D (which in turn unlocks CacheErr-DPA). | ||
78 | * Ought to log the occurence of this recovered dcache error. | ||
79 | */ | ||
80 | b recovered | ||
81 | mtc0 $0,C0_CERR_D | ||
82 | |||
83 | attempt_recovery: | ||
84 | /* | ||
85 | * k0 has C0_ERRCTL << 1, which puts 'DC' at bit 31. Any | ||
86 | * Dcache errors we can recover from will take more extensive | ||
87 | * processing. For now, they are considered "unrecoverable". | ||
88 | * Note that 'DC' becoming set (outside of ERL mode) will | ||
89 | * cause 'IC' to clear; so if there's an Icache error, we'll | ||
90 | * only find out about it if we recover from this error and | ||
91 | * continue executing. | ||
92 | */ | ||
93 | bltz k0,unrecoverable | ||
94 | sll k0,1 | ||
95 | |||
96 | /* | ||
97 | * k0 has C0_ERRCTL << 2, which puts 'IC' at bit 31. If an | ||
98 | * Icache error isn't indicated, I'm not sure why we got here. | ||
99 | * Consider that case "unrecoverable" for now. | ||
100 | */ | ||
101 | bgez k0,unrecoverable | ||
102 | |||
103 | attempt_icache_recovery: | ||
104 | /* | ||
105 | * External icache errors are due to uncorrectable ECC errors | ||
106 | * in the L2 cache or Memory Controller and cannot be | ||
107 | * recovered here. | ||
108 | */ | ||
109 | mfc0 k0,C0_CERR_I /* delay slot */ | ||
110 | li k1,1 << 26 /* ICACHE_EXTERNAL */ | ||
111 | and k1,k0 | ||
112 | bnez k1,unrecoverable | ||
113 | andi k0,0x1fe0 | ||
114 | |||
115 | /* | ||
116 | * Since the error is internal, the 'IDX' field from | ||
117 | * CacheErr-I is valid and we can just invalidate all blocks | ||
118 | * in that set. | ||
119 | */ | ||
120 | cache Index_Invalidate_I,(0<<13)(k0) | ||
121 | cache Index_Invalidate_I,(1<<13)(k0) | ||
122 | cache Index_Invalidate_I,(2<<13)(k0) | ||
123 | cache Index_Invalidate_I,(3<<13)(k0) | ||
124 | |||
125 | /* Ought to log this recovered icache error */ | ||
126 | |||
127 | recovered: | ||
128 | /* Restore the saved registers */ | ||
129 | ld k0,0x170($0) | ||
130 | ld k1,0x178($0) | ||
131 | eret | ||
132 | |||
133 | unrecoverable: | ||
134 | /* Unrecoverable Icache or Dcache error; log it and/or fail */ | ||
135 | j handle_vec2_sb1 | ||
136 | nop | ||
137 | |||
138 | END(except_vec2_sb1) | ||
139 | |||
140 | __FINIT | ||
141 | |||
142 | LEAF(handle_vec2_sb1) | ||
143 | mfc0 k0,CP0_CONFIG | ||
144 | li k1,~CONF_CM_CMASK | ||
145 | and k0,k0,k1 | ||
146 | ori k0,k0,CONF_CM_UNCACHED | ||
147 | mtc0 k0,CP0_CONFIG | ||
148 | |||
149 | SSNOP | ||
150 | SSNOP | ||
151 | SSNOP | ||
152 | SSNOP | ||
153 | bnezl $0, 1f | ||
154 | 1: | ||
155 | mfc0 k0, CP0_STATUS | ||
156 | sll k0, k0, 3 # check CU0 (kernel?) | ||
157 | bltz k0, 2f | ||
158 | nop | ||
159 | |||
160 | /* Get a valid Kseg0 stack pointer. Any task's stack pointer | ||
161 | * will do, although if we ever want to resume execution we | ||
162 | * better not have corrupted any state. */ | ||
163 | get_saved_sp | ||
164 | move sp, k1 | ||
165 | |||
166 | 2: | ||
167 | j sb1_cache_error | ||
168 | nop | ||
169 | |||
170 | END(handle_vec2_sb1) | ||
diff --git a/arch/mips/mm/dma-coherent.c b/arch/mips/mm/dma-coherent.c new file mode 100644 index 000000000000..97a50d38c98f --- /dev/null +++ b/arch/mips/mm/dma-coherent.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com> | ||
7 | * Copyright (C) 2000, 2001 Ralf Baechle <ralf@gnu.org> | ||
8 | * swiped from i386, and cloned for MIPS by Geert, polished by Ralf. | ||
9 | */ | ||
10 | #include <linux/config.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/pci.h> | ||
16 | |||
17 | #include <asm/cache.h> | ||
18 | #include <asm/io.h> | ||
19 | |||
20 | void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||
21 | dma_addr_t * dma_handle, int gfp) | ||
22 | { | ||
23 | void *ret; | ||
24 | /* ignore region specifiers */ | ||
25 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
26 | |||
27 | if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) | ||
28 | gfp |= GFP_DMA; | ||
29 | ret = (void *) __get_free_pages(gfp, get_order(size)); | ||
30 | |||
31 | if (ret != NULL) { | ||
32 | memset(ret, 0, size); | ||
33 | *dma_handle = virt_to_phys(ret); | ||
34 | } | ||
35 | |||
36 | return ret; | ||
37 | } | ||
38 | |||
39 | EXPORT_SYMBOL(dma_alloc_noncoherent); | ||
40 | |||
41 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
42 | dma_addr_t * dma_handle, int gfp) | ||
43 | __attribute__((alias("dma_alloc_noncoherent"))); | ||
44 | |||
45 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
46 | |||
47 | void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, | ||
48 | dma_addr_t dma_handle) | ||
49 | { | ||
50 | unsigned long addr = (unsigned long) vaddr; | ||
51 | |||
52 | free_pages(addr, get_order(size)); | ||
53 | } | ||
54 | |||
55 | EXPORT_SYMBOL(dma_free_noncoherent); | ||
56 | |||
57 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
58 | dma_addr_t dma_handle) __attribute__((alias("dma_free_noncoherent"))); | ||
59 | |||
60 | EXPORT_SYMBOL(dma_free_coherent); | ||
61 | |||
62 | dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, | ||
63 | enum dma_data_direction direction) | ||
64 | { | ||
65 | BUG_ON(direction == DMA_NONE); | ||
66 | |||
67 | return __pa(ptr); | ||
68 | } | ||
69 | |||
70 | EXPORT_SYMBOL(dma_map_single); | ||
71 | |||
72 | void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
73 | enum dma_data_direction direction) | ||
74 | { | ||
75 | BUG_ON(direction == DMA_NONE); | ||
76 | } | ||
77 | |||
78 | EXPORT_SYMBOL(dma_unmap_single); | ||
79 | |||
80 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
81 | enum dma_data_direction direction) | ||
82 | { | ||
83 | int i; | ||
84 | |||
85 | BUG_ON(direction == DMA_NONE); | ||
86 | |||
87 | for (i = 0; i < nents; i++, sg++) { | ||
88 | sg->dma_address = (dma_addr_t)page_to_phys(sg->page) + sg->offset; | ||
89 | } | ||
90 | |||
91 | return nents; | ||
92 | } | ||
93 | |||
94 | EXPORT_SYMBOL(dma_map_sg); | ||
95 | |||
96 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
97 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
98 | { | ||
99 | BUG_ON(direction == DMA_NONE); | ||
100 | |||
101 | return page_to_phys(page) + offset; | ||
102 | } | ||
103 | |||
104 | EXPORT_SYMBOL(dma_map_page); | ||
105 | |||
106 | void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, | ||
107 | enum dma_data_direction direction) | ||
108 | { | ||
109 | BUG_ON(direction == DMA_NONE); | ||
110 | } | ||
111 | |||
112 | EXPORT_SYMBOL(dma_unmap_page); | ||
113 | |||
114 | void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, | ||
115 | enum dma_data_direction direction) | ||
116 | { | ||
117 | BUG_ON(direction == DMA_NONE); | ||
118 | } | ||
119 | |||
120 | EXPORT_SYMBOL(dma_unmap_sg); | ||
121 | |||
122 | void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
123 | size_t size, enum dma_data_direction direction) | ||
124 | { | ||
125 | BUG_ON(direction == DMA_NONE); | ||
126 | } | ||
127 | |||
128 | EXPORT_SYMBOL(dma_sync_single_for_cpu); | ||
129 | |||
130 | void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, | ||
131 | size_t size, enum dma_data_direction direction) | ||
132 | { | ||
133 | BUG_ON(direction == DMA_NONE); | ||
134 | } | ||
135 | |||
136 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
137 | |||
138 | void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
139 | unsigned long offset, size_t size, | ||
140 | enum dma_data_direction direction) | ||
141 | { | ||
142 | BUG_ON(direction == DMA_NONE); | ||
143 | } | ||
144 | |||
145 | EXPORT_SYMBOL(dma_sync_single_range_for_cpu); | ||
146 | |||
147 | void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, | ||
148 | unsigned long offset, size_t size, | ||
149 | enum dma_data_direction direction) | ||
150 | { | ||
151 | BUG_ON(direction == DMA_NONE); | ||
152 | } | ||
153 | |||
154 | EXPORT_SYMBOL(dma_sync_single_range_for_device); | ||
155 | |||
156 | void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, | ||
157 | enum dma_data_direction direction) | ||
158 | { | ||
159 | BUG_ON(direction == DMA_NONE); | ||
160 | } | ||
161 | |||
162 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | ||
163 | |||
164 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, | ||
165 | enum dma_data_direction direction) | ||
166 | { | ||
167 | BUG_ON(direction == DMA_NONE); | ||
168 | } | ||
169 | |||
170 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
171 | |||
172 | int dma_mapping_error(dma_addr_t dma_addr) | ||
173 | { | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | EXPORT_SYMBOL(dma_mapping_error); | ||
178 | |||
179 | int dma_supported(struct device *dev, u64 mask) | ||
180 | { | ||
181 | /* | ||
182 | * we fall back to GFP_DMA when the mask isn't all 1s, | ||
183 | * so we can't guarantee allocations that must be | ||
184 | * within a tighter range than GFP_DMA.. | ||
185 | */ | ||
186 | if (mask < 0x00ffffff) | ||
187 | return 0; | ||
188 | |||
189 | return 1; | ||
190 | } | ||
191 | |||
192 | EXPORT_SYMBOL(dma_supported); | ||
193 | |||
194 | int dma_is_consistent(dma_addr_t dma_addr) | ||
195 | { | ||
196 | return 1; | ||
197 | } | ||
198 | |||
199 | EXPORT_SYMBOL(dma_is_consistent); | ||
200 | |||
201 | void dma_cache_sync(void *vaddr, size_t size, | ||
202 | enum dma_data_direction direction) | ||
203 | { | ||
204 | BUG_ON(direction == DMA_NONE); | ||
205 | } | ||
206 | |||
207 | EXPORT_SYMBOL(dma_cache_sync); | ||
208 | |||
209 | /* The DAC routines are a PCIism.. */ | ||
210 | |||
211 | #ifdef CONFIG_PCI | ||
212 | |||
213 | #include <linux/pci.h> | ||
214 | |||
215 | dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, | ||
216 | struct page *page, unsigned long offset, int direction) | ||
217 | { | ||
218 | return (dma64_addr_t)page_to_phys(page) + offset; | ||
219 | } | ||
220 | |||
221 | EXPORT_SYMBOL(pci_dac_page_to_dma); | ||
222 | |||
223 | struct page *pci_dac_dma_to_page(struct pci_dev *pdev, | ||
224 | dma64_addr_t dma_addr) | ||
225 | { | ||
226 | return mem_map + (dma_addr >> PAGE_SHIFT); | ||
227 | } | ||
228 | |||
229 | EXPORT_SYMBOL(pci_dac_dma_to_page); | ||
230 | |||
231 | unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, | ||
232 | dma64_addr_t dma_addr) | ||
233 | { | ||
234 | return dma_addr & ~PAGE_MASK; | ||
235 | } | ||
236 | |||
237 | EXPORT_SYMBOL(pci_dac_dma_to_offset); | ||
238 | |||
239 | void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, | ||
240 | dma64_addr_t dma_addr, size_t len, int direction) | ||
241 | { | ||
242 | BUG_ON(direction == PCI_DMA_NONE); | ||
243 | } | ||
244 | |||
245 | EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); | ||
246 | |||
247 | void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, | ||
248 | dma64_addr_t dma_addr, size_t len, int direction) | ||
249 | { | ||
250 | BUG_ON(direction == PCI_DMA_NONE); | ||
251 | } | ||
252 | |||
253 | EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); | ||
254 | |||
255 | #endif /* CONFIG_PCI */ | ||
diff --git a/arch/mips/mm/dma-ip27.c b/arch/mips/mm/dma-ip27.c new file mode 100644 index 000000000000..aa7c94b5d781 --- /dev/null +++ b/arch/mips/mm/dma-ip27.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com> | ||
7 | * Copyright (C) 2000, 2001 Ralf Baechle <ralf@gnu.org> | ||
8 | * swiped from i386, and cloned for MIPS by Geert, polished by Ralf. | ||
9 | */ | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/pci.h> | ||
15 | |||
16 | #include <asm/cache.h> | ||
17 | #include <asm/pci/bridge.h> | ||
18 | |||
19 | #define pdev_to_baddr(pdev, addr) \ | ||
20 | (BRIDGE_CONTROLLER(pdev->bus)->baddr + (addr)) | ||
21 | #define dev_to_baddr(dev, addr) \ | ||
22 | pdev_to_baddr(to_pci_dev(dev), (addr)) | ||
23 | |||
24 | void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||
25 | dma_addr_t * dma_handle, int gfp) | ||
26 | { | ||
27 | void *ret; | ||
28 | |||
29 | /* ignore region specifiers */ | ||
30 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
31 | |||
32 | if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) | ||
33 | gfp |= GFP_DMA; | ||
34 | ret = (void *) __get_free_pages(gfp, get_order(size)); | ||
35 | |||
36 | if (ret != NULL) { | ||
37 | memset(ret, 0, size); | ||
38 | *dma_handle = dev_to_baddr(dev, virt_to_phys(ret)); | ||
39 | } | ||
40 | |||
41 | return ret; | ||
42 | } | ||
43 | |||
44 | EXPORT_SYMBOL(dma_alloc_noncoherent); | ||
45 | |||
46 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
47 | dma_addr_t * dma_handle, int gfp) | ||
48 | __attribute__((alias("dma_alloc_noncoherent"))); | ||
49 | |||
50 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
51 | |||
52 | void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, | ||
53 | dma_addr_t dma_handle) | ||
54 | { | ||
55 | unsigned long addr = (unsigned long) vaddr; | ||
56 | |||
57 | free_pages(addr, get_order(size)); | ||
58 | } | ||
59 | |||
60 | EXPORT_SYMBOL(dma_free_noncoherent); | ||
61 | |||
62 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
63 | dma_addr_t dma_handle) __attribute__((alias("dma_free_noncoherent"))); | ||
64 | |||
65 | EXPORT_SYMBOL(dma_free_coherent); | ||
66 | |||
67 | dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, | ||
68 | enum dma_data_direction direction) | ||
69 | { | ||
70 | BUG_ON(direction == DMA_NONE); | ||
71 | |||
72 | return dev_to_baddr(dev, __pa(ptr)); | ||
73 | } | ||
74 | |||
75 | EXPORT_SYMBOL(dma_map_single); | ||
76 | |||
77 | void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
78 | enum dma_data_direction direction) | ||
79 | { | ||
80 | BUG_ON(direction == DMA_NONE); | ||
81 | } | ||
82 | |||
83 | EXPORT_SYMBOL(dma_unmap_single); | ||
84 | |||
85 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
86 | enum dma_data_direction direction) | ||
87 | { | ||
88 | int i; | ||
89 | |||
90 | BUG_ON(direction == DMA_NONE); | ||
91 | |||
92 | for (i = 0; i < nents; i++, sg++) { | ||
93 | sg->dma_address = (dma_addr_t) dev_to_baddr(dev, | ||
94 | page_to_phys(sg->page) + sg->offset); | ||
95 | } | ||
96 | |||
97 | return nents; | ||
98 | } | ||
99 | |||
100 | EXPORT_SYMBOL(dma_map_sg); | ||
101 | |||
102 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
103 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
104 | { | ||
105 | BUG_ON(direction == DMA_NONE); | ||
106 | |||
107 | return dev_to_baddr(dev, page_to_phys(page) + offset); | ||
108 | } | ||
109 | |||
110 | EXPORT_SYMBOL(dma_map_page); | ||
111 | |||
112 | void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, | ||
113 | enum dma_data_direction direction) | ||
114 | { | ||
115 | BUG_ON(direction == DMA_NONE); | ||
116 | } | ||
117 | |||
118 | EXPORT_SYMBOL(dma_unmap_page); | ||
119 | |||
120 | void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, | ||
121 | enum dma_data_direction direction) | ||
122 | { | ||
123 | BUG_ON(direction == DMA_NONE); | ||
124 | } | ||
125 | |||
126 | EXPORT_SYMBOL(dma_unmap_sg); | ||
127 | |||
128 | void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, | ||
129 | enum dma_data_direction direction) | ||
130 | { | ||
131 | BUG_ON(direction == DMA_NONE); | ||
132 | } | ||
133 | |||
134 | EXPORT_SYMBOL(dma_sync_single_for_cpu); | ||
135 | |||
136 | void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, | ||
137 | enum dma_data_direction direction) | ||
138 | { | ||
139 | BUG_ON(direction == DMA_NONE); | ||
140 | } | ||
141 | |||
142 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
143 | |||
144 | void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
145 | unsigned long offset, size_t size, | ||
146 | enum dma_data_direction direction) | ||
147 | { | ||
148 | BUG_ON(direction == DMA_NONE); | ||
149 | } | ||
150 | |||
151 | EXPORT_SYMBOL(dma_sync_single_range_for_cpu); | ||
152 | |||
153 | void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, | ||
154 | unsigned long offset, size_t size, | ||
155 | enum dma_data_direction direction) | ||
156 | { | ||
157 | BUG_ON(direction == DMA_NONE); | ||
158 | } | ||
159 | |||
160 | EXPORT_SYMBOL(dma_sync_single_range_for_device); | ||
161 | |||
162 | void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, | ||
163 | enum dma_data_direction direction) | ||
164 | { | ||
165 | BUG_ON(direction == DMA_NONE); | ||
166 | } | ||
167 | |||
168 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | ||
169 | |||
170 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, | ||
171 | enum dma_data_direction direction) | ||
172 | { | ||
173 | BUG_ON(direction == DMA_NONE); | ||
174 | } | ||
175 | |||
176 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
177 | |||
178 | int dma_mapping_error(dma_addr_t dma_addr) | ||
179 | { | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | EXPORT_SYMBOL(dma_mapping_error); | ||
184 | |||
185 | int dma_supported(struct device *dev, u64 mask) | ||
186 | { | ||
187 | /* | ||
188 | * we fall back to GFP_DMA when the mask isn't all 1s, | ||
189 | * so we can't guarantee allocations that must be | ||
190 | * within a tighter range than GFP_DMA.. | ||
191 | */ | ||
192 | if (mask < 0x00ffffff) | ||
193 | return 0; | ||
194 | |||
195 | return 1; | ||
196 | } | ||
197 | |||
198 | EXPORT_SYMBOL(dma_supported); | ||
199 | |||
200 | int dma_is_consistent(dma_addr_t dma_addr) | ||
201 | { | ||
202 | return 1; | ||
203 | } | ||
204 | |||
205 | EXPORT_SYMBOL(dma_is_consistent); | ||
206 | |||
207 | void dma_cache_sync(void *vaddr, size_t size, | ||
208 | enum dma_data_direction direction) | ||
209 | { | ||
210 | BUG_ON(direction == DMA_NONE); | ||
211 | } | ||
212 | |||
213 | EXPORT_SYMBOL(dma_cache_sync); | ||
214 | |||
215 | dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, | ||
216 | struct page *page, unsigned long offset, int direction) | ||
217 | { | ||
218 | dma64_addr_t addr = page_to_phys(page) + offset; | ||
219 | |||
220 | return (dma64_addr_t) pdev_to_baddr(pdev, addr); | ||
221 | } | ||
222 | |||
223 | EXPORT_SYMBOL(pci_dac_page_to_dma); | ||
224 | |||
225 | struct page *pci_dac_dma_to_page(struct pci_dev *pdev, | ||
226 | dma64_addr_t dma_addr) | ||
227 | { | ||
228 | struct bridge_controller *bc = BRIDGE_CONTROLLER(pdev->bus); | ||
229 | |||
230 | return pfn_to_page((dma_addr - bc->baddr) >> PAGE_SHIFT); | ||
231 | } | ||
232 | |||
233 | EXPORT_SYMBOL(pci_dac_dma_to_page); | ||
234 | |||
235 | unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, | ||
236 | dma64_addr_t dma_addr) | ||
237 | { | ||
238 | return dma_addr & ~PAGE_MASK; | ||
239 | } | ||
240 | |||
241 | EXPORT_SYMBOL(pci_dac_dma_to_offset); | ||
242 | |||
243 | void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, | ||
244 | dma64_addr_t dma_addr, size_t len, int direction) | ||
245 | { | ||
246 | BUG_ON(direction == PCI_DMA_NONE); | ||
247 | } | ||
248 | |||
249 | EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); | ||
250 | |||
251 | void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, | ||
252 | dma64_addr_t dma_addr, size_t len, int direction) | ||
253 | { | ||
254 | BUG_ON(direction == PCI_DMA_NONE); | ||
255 | } | ||
256 | |||
257 | EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); | ||
diff --git a/arch/mips/mm/dma-ip32.c b/arch/mips/mm/dma-ip32.c new file mode 100644 index 000000000000..2cbe196c35fb --- /dev/null +++ b/arch/mips/mm/dma-ip32.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com> | ||
7 | * Copyright (C) 2000, 2001 Ralf Baechle <ralf@gnu.org> | ||
8 | * Copyright (C) 2005 Ilya A. Volynets-Evenbakh <ilya@total-knowledge.com> | ||
9 | * swiped from i386, and cloned for MIPS by Geert, polished by Ralf. | ||
10 | * IP32 changes by Ilya. | ||
11 | */ | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | |||
18 | #include <asm/cache.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/ip32/crime.h> | ||
21 | |||
22 | /* | ||
23 | * Warning on the terminology - Linux calls an uncached area coherent; | ||
24 | * MIPS terminology calls memory areas with hardware maintained coherency | ||
25 | * coherent. | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | * Few notes. | ||
30 | * 1. CPU sees memory as two chunks: 0-256M@0x0, and the rest @0x40000000+256M | ||
31 | * 2. PCI sees memory as one big chunk @0x0 (or we could use 0x40000000 for native-endian) | ||
32 | * 3. All other devices see memory as one big chunk at 0x40000000 | ||
33 | * 4. Non-PCI devices will pass NULL as struct device* | ||
34 | * Thus we translate differently, depending on device. | ||
35 | */ | ||
36 | |||
37 | #define RAM_OFFSET_MASK 0x3fffffff | ||
38 | |||
39 | void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||
40 | dma_addr_t * dma_handle, int gfp) | ||
41 | { | ||
42 | void *ret; | ||
43 | /* ignore region specifiers */ | ||
44 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
45 | |||
46 | if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) | ||
47 | gfp |= GFP_DMA; | ||
48 | ret = (void *) __get_free_pages(gfp, get_order(size)); | ||
49 | |||
50 | if (ret != NULL) { | ||
51 | unsigned long addr = virt_to_phys(ret)&RAM_OFFSET_MASK; | ||
52 | memset(ret, 0, size); | ||
53 | if(dev==NULL) | ||
54 | addr+= CRIME_HI_MEM_BASE; | ||
55 | *dma_handle = addr; | ||
56 | } | ||
57 | |||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | EXPORT_SYMBOL(dma_alloc_noncoherent); | ||
62 | |||
63 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
64 | dma_addr_t * dma_handle, int gfp) | ||
65 | { | ||
66 | void *ret; | ||
67 | |||
68 | ret = dma_alloc_noncoherent(dev, size, dma_handle, gfp); | ||
69 | if (ret) { | ||
70 | dma_cache_wback_inv((unsigned long) ret, size); | ||
71 | ret = UNCAC_ADDR(ret); | ||
72 | } | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
78 | |||
79 | void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, | ||
80 | dma_addr_t dma_handle) | ||
81 | { | ||
82 | free_pages((unsigned long) vaddr, get_order(size)); | ||
83 | } | ||
84 | |||
85 | EXPORT_SYMBOL(dma_free_noncoherent); | ||
86 | |||
87 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
88 | dma_addr_t dma_handle) | ||
89 | { | ||
90 | unsigned long addr = (unsigned long) vaddr; | ||
91 | |||
92 | addr = CAC_ADDR(addr); | ||
93 | free_pages(addr, get_order(size)); | ||
94 | } | ||
95 | |||
96 | EXPORT_SYMBOL(dma_free_coherent); | ||
97 | |||
98 | static inline void __dma_sync(unsigned long addr, size_t size, | ||
99 | enum dma_data_direction direction) | ||
100 | { | ||
101 | switch (direction) { | ||
102 | case DMA_TO_DEVICE: | ||
103 | dma_cache_wback(addr, size); | ||
104 | break; | ||
105 | |||
106 | case DMA_FROM_DEVICE: | ||
107 | dma_cache_inv(addr, size); | ||
108 | break; | ||
109 | |||
110 | case DMA_BIDIRECTIONAL: | ||
111 | dma_cache_wback_inv(addr, size); | ||
112 | break; | ||
113 | |||
114 | default: | ||
115 | BUG(); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, | ||
120 | enum dma_data_direction direction) | ||
121 | { | ||
122 | unsigned long addr = (unsigned long) ptr; | ||
123 | |||
124 | switch (direction) { | ||
125 | case DMA_TO_DEVICE: | ||
126 | dma_cache_wback(addr, size); | ||
127 | break; | ||
128 | |||
129 | case DMA_FROM_DEVICE: | ||
130 | dma_cache_inv(addr, size); | ||
131 | break; | ||
132 | |||
133 | case DMA_BIDIRECTIONAL: | ||
134 | dma_cache_wback_inv(addr, size); | ||
135 | break; | ||
136 | |||
137 | default: | ||
138 | BUG(); | ||
139 | } | ||
140 | |||
141 | addr = virt_to_phys(ptr)&RAM_OFFSET_MASK;; | ||
142 | if(dev == NULL) | ||
143 | addr+=CRIME_HI_MEM_BASE; | ||
144 | return (dma_addr_t)addr; | ||
145 | } | ||
146 | |||
147 | EXPORT_SYMBOL(dma_map_single); | ||
148 | |||
149 | void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
150 | enum dma_data_direction direction) | ||
151 | { | ||
152 | switch (direction) { | ||
153 | case DMA_TO_DEVICE: | ||
154 | break; | ||
155 | |||
156 | case DMA_FROM_DEVICE: | ||
157 | break; | ||
158 | |||
159 | case DMA_BIDIRECTIONAL: | ||
160 | break; | ||
161 | |||
162 | default: | ||
163 | BUG(); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | EXPORT_SYMBOL(dma_unmap_single); | ||
168 | |||
169 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
170 | enum dma_data_direction direction) | ||
171 | { | ||
172 | int i; | ||
173 | |||
174 | BUG_ON(direction == DMA_NONE); | ||
175 | |||
176 | for (i = 0; i < nents; i++, sg++) { | ||
177 | unsigned long addr; | ||
178 | |||
179 | addr = (unsigned long) page_address(sg->page)+sg->offset; | ||
180 | if (addr) | ||
181 | __dma_sync(addr, sg->length, direction); | ||
182 | addr = __pa(addr)&RAM_OFFSET_MASK;; | ||
183 | if(dev == NULL) | ||
184 | addr += CRIME_HI_MEM_BASE; | ||
185 | sg->dma_address = (dma_addr_t)addr; | ||
186 | } | ||
187 | |||
188 | return nents; | ||
189 | } | ||
190 | |||
191 | EXPORT_SYMBOL(dma_map_sg); | ||
192 | |||
193 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
194 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
195 | { | ||
196 | unsigned long addr; | ||
197 | |||
198 | BUG_ON(direction == DMA_NONE); | ||
199 | |||
200 | addr = (unsigned long) page_address(page) + offset; | ||
201 | dma_cache_wback_inv(addr, size); | ||
202 | addr = __pa(addr)&RAM_OFFSET_MASK;; | ||
203 | if(dev == NULL) | ||
204 | addr += CRIME_HI_MEM_BASE; | ||
205 | |||
206 | return (dma_addr_t)addr; | ||
207 | } | ||
208 | |||
209 | EXPORT_SYMBOL(dma_map_page); | ||
210 | |||
211 | void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, | ||
212 | enum dma_data_direction direction) | ||
213 | { | ||
214 | BUG_ON(direction == DMA_NONE); | ||
215 | |||
216 | if (direction != DMA_TO_DEVICE) { | ||
217 | unsigned long addr; | ||
218 | |||
219 | dma_address&=RAM_OFFSET_MASK; | ||
220 | addr = dma_address + PAGE_OFFSET; | ||
221 | if(dma_address>=256*1024*1024) | ||
222 | addr+=CRIME_HI_MEM_BASE; | ||
223 | dma_cache_wback_inv(addr, size); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | EXPORT_SYMBOL(dma_unmap_page); | ||
228 | |||
229 | void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, | ||
230 | enum dma_data_direction direction) | ||
231 | { | ||
232 | unsigned long addr; | ||
233 | int i; | ||
234 | |||
235 | BUG_ON(direction == DMA_NONE); | ||
236 | |||
237 | if (direction == DMA_TO_DEVICE) | ||
238 | return; | ||
239 | |||
240 | for (i = 0; i < nhwentries; i++, sg++) { | ||
241 | addr = (unsigned long) page_address(sg->page); | ||
242 | if (!addr) | ||
243 | continue; | ||
244 | dma_cache_wback_inv(addr + sg->offset, sg->length); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | EXPORT_SYMBOL(dma_unmap_sg); | ||
249 | |||
250 | void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
251 | size_t size, enum dma_data_direction direction) | ||
252 | { | ||
253 | unsigned long addr; | ||
254 | |||
255 | BUG_ON(direction == DMA_NONE); | ||
256 | |||
257 | dma_handle&=RAM_OFFSET_MASK; | ||
258 | addr = dma_handle + PAGE_OFFSET; | ||
259 | if(dma_handle>=256*1024*1024) | ||
260 | addr+=CRIME_HI_MEM_BASE; | ||
261 | __dma_sync(addr, size, direction); | ||
262 | } | ||
263 | |||
264 | EXPORT_SYMBOL(dma_sync_single_for_cpu); | ||
265 | |||
266 | void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, | ||
267 | size_t size, enum dma_data_direction direction) | ||
268 | { | ||
269 | unsigned long addr; | ||
270 | |||
271 | BUG_ON(direction == DMA_NONE); | ||
272 | |||
273 | dma_handle&=RAM_OFFSET_MASK; | ||
274 | addr = dma_handle + PAGE_OFFSET; | ||
275 | if(dma_handle>=256*1024*1024) | ||
276 | addr+=CRIME_HI_MEM_BASE; | ||
277 | __dma_sync(addr, size, direction); | ||
278 | } | ||
279 | |||
280 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
281 | |||
282 | void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
283 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
284 | { | ||
285 | unsigned long addr; | ||
286 | |||
287 | BUG_ON(direction == DMA_NONE); | ||
288 | |||
289 | dma_handle&=RAM_OFFSET_MASK; | ||
290 | addr = dma_handle + offset + PAGE_OFFSET; | ||
291 | if(dma_handle>=256*1024*1024) | ||
292 | addr+=CRIME_HI_MEM_BASE; | ||
293 | __dma_sync(addr, size, direction); | ||
294 | } | ||
295 | |||
296 | EXPORT_SYMBOL(dma_sync_single_range_for_cpu); | ||
297 | |||
298 | void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, | ||
299 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
300 | { | ||
301 | unsigned long addr; | ||
302 | |||
303 | BUG_ON(direction == DMA_NONE); | ||
304 | |||
305 | dma_handle&=RAM_OFFSET_MASK; | ||
306 | addr = dma_handle + offset + PAGE_OFFSET; | ||
307 | if(dma_handle>=256*1024*1024) | ||
308 | addr+=CRIME_HI_MEM_BASE; | ||
309 | __dma_sync(addr, size, direction); | ||
310 | } | ||
311 | |||
312 | EXPORT_SYMBOL(dma_sync_single_range_for_device); | ||
313 | |||
314 | void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, | ||
315 | enum dma_data_direction direction) | ||
316 | { | ||
317 | int i; | ||
318 | |||
319 | BUG_ON(direction == DMA_NONE); | ||
320 | |||
321 | /* Make sure that gcc doesn't leave the empty loop body. */ | ||
322 | for (i = 0; i < nelems; i++, sg++) | ||
323 | __dma_sync((unsigned long)page_address(sg->page), | ||
324 | sg->length, direction); | ||
325 | } | ||
326 | |||
327 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | ||
328 | |||
329 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, | ||
330 | enum dma_data_direction direction) | ||
331 | { | ||
332 | int i; | ||
333 | |||
334 | BUG_ON(direction == DMA_NONE); | ||
335 | |||
336 | /* Make sure that gcc doesn't leave the empty loop body. */ | ||
337 | for (i = 0; i < nelems; i++, sg++) | ||
338 | __dma_sync((unsigned long)page_address(sg->page), | ||
339 | sg->length, direction); | ||
340 | } | ||
341 | |||
342 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
343 | |||
344 | int dma_mapping_error(dma_addr_t dma_addr) | ||
345 | { | ||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | EXPORT_SYMBOL(dma_mapping_error); | ||
350 | |||
351 | int dma_supported(struct device *dev, u64 mask) | ||
352 | { | ||
353 | /* | ||
354 | * we fall back to GFP_DMA when the mask isn't all 1s, | ||
355 | * so we can't guarantee allocations that must be | ||
356 | * within a tighter range than GFP_DMA.. | ||
357 | */ | ||
358 | if (mask < 0x00ffffff) | ||
359 | return 0; | ||
360 | |||
361 | return 1; | ||
362 | } | ||
363 | |||
364 | EXPORT_SYMBOL(dma_supported); | ||
365 | |||
366 | int dma_is_consistent(dma_addr_t dma_addr) | ||
367 | { | ||
368 | return 1; | ||
369 | } | ||
370 | |||
371 | EXPORT_SYMBOL(dma_is_consistent); | ||
372 | |||
373 | void dma_cache_sync(void *vaddr, size_t size, enum dma_data_direction direction) | ||
374 | { | ||
375 | if (direction == DMA_NONE) | ||
376 | return; | ||
377 | |||
378 | dma_cache_wback_inv((unsigned long)vaddr, size); | ||
379 | } | ||
380 | |||
381 | EXPORT_SYMBOL(dma_cache_sync); | ||
382 | |||
diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c new file mode 100644 index 000000000000..9895e32b0fce --- /dev/null +++ b/arch/mips/mm/dma-noncoherent.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2000 Ani Joshi <ajoshi@unixbox.com> | ||
7 | * Copyright (C) 2000, 2001 Ralf Baechle <ralf@gnu.org> | ||
8 | * swiped from i386, and cloned for MIPS by Geert, polished by Ralf. | ||
9 | */ | ||
10 | #include <linux/config.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | |||
17 | #include <asm/cache.h> | ||
18 | #include <asm/io.h> | ||
19 | |||
20 | /* | ||
21 | * Warning on the terminology - Linux calls an uncached area coherent; | ||
22 | * MIPS terminology calls memory areas with hardware maintained coherency | ||
23 | * coherent. | ||
24 | */ | ||
25 | |||
26 | void *dma_alloc_noncoherent(struct device *dev, size_t size, | ||
27 | dma_addr_t * dma_handle, int gfp) | ||
28 | { | ||
29 | void *ret; | ||
30 | /* ignore region specifiers */ | ||
31 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
32 | |||
33 | if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) | ||
34 | gfp |= GFP_DMA; | ||
35 | ret = (void *) __get_free_pages(gfp, get_order(size)); | ||
36 | |||
37 | if (ret != NULL) { | ||
38 | memset(ret, 0, size); | ||
39 | *dma_handle = virt_to_phys(ret); | ||
40 | } | ||
41 | |||
42 | return ret; | ||
43 | } | ||
44 | |||
45 | EXPORT_SYMBOL(dma_alloc_noncoherent); | ||
46 | |||
47 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
48 | dma_addr_t * dma_handle, int gfp) | ||
49 | { | ||
50 | void *ret; | ||
51 | |||
52 | ret = dma_alloc_noncoherent(dev, size, dma_handle, gfp); | ||
53 | if (ret) { | ||
54 | dma_cache_wback_inv((unsigned long) ret, size); | ||
55 | ret = UNCAC_ADDR(ret); | ||
56 | } | ||
57 | |||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
62 | |||
63 | void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, | ||
64 | dma_addr_t dma_handle) | ||
65 | { | ||
66 | free_pages((unsigned long) vaddr, get_order(size)); | ||
67 | } | ||
68 | |||
69 | EXPORT_SYMBOL(dma_free_noncoherent); | ||
70 | |||
71 | void dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
72 | dma_addr_t dma_handle) | ||
73 | { | ||
74 | unsigned long addr = (unsigned long) vaddr; | ||
75 | |||
76 | addr = CAC_ADDR(addr); | ||
77 | free_pages(addr, get_order(size)); | ||
78 | } | ||
79 | |||
80 | EXPORT_SYMBOL(dma_free_coherent); | ||
81 | |||
82 | static inline void __dma_sync(unsigned long addr, size_t size, | ||
83 | enum dma_data_direction direction) | ||
84 | { | ||
85 | switch (direction) { | ||
86 | case DMA_TO_DEVICE: | ||
87 | dma_cache_wback(addr, size); | ||
88 | break; | ||
89 | |||
90 | case DMA_FROM_DEVICE: | ||
91 | dma_cache_inv(addr, size); | ||
92 | break; | ||
93 | |||
94 | case DMA_BIDIRECTIONAL: | ||
95 | dma_cache_wback_inv(addr, size); | ||
96 | break; | ||
97 | |||
98 | default: | ||
99 | BUG(); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, | ||
104 | enum dma_data_direction direction) | ||
105 | { | ||
106 | unsigned long addr = (unsigned long) ptr; | ||
107 | |||
108 | switch (direction) { | ||
109 | case DMA_TO_DEVICE: | ||
110 | dma_cache_wback(addr, size); | ||
111 | break; | ||
112 | |||
113 | case DMA_FROM_DEVICE: | ||
114 | dma_cache_inv(addr, size); | ||
115 | break; | ||
116 | |||
117 | case DMA_BIDIRECTIONAL: | ||
118 | dma_cache_wback_inv(addr, size); | ||
119 | break; | ||
120 | |||
121 | default: | ||
122 | BUG(); | ||
123 | } | ||
124 | |||
125 | return virt_to_phys(ptr); | ||
126 | } | ||
127 | |||
128 | EXPORT_SYMBOL(dma_map_single); | ||
129 | |||
130 | void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
131 | enum dma_data_direction direction) | ||
132 | { | ||
133 | unsigned long addr; | ||
134 | addr = dma_addr + PAGE_OFFSET; | ||
135 | |||
136 | switch (direction) { | ||
137 | case DMA_TO_DEVICE: | ||
138 | //dma_cache_wback(addr, size); | ||
139 | break; | ||
140 | |||
141 | case DMA_FROM_DEVICE: | ||
142 | //dma_cache_inv(addr, size); | ||
143 | break; | ||
144 | |||
145 | case DMA_BIDIRECTIONAL: | ||
146 | //dma_cache_wback_inv(addr, size); | ||
147 | break; | ||
148 | |||
149 | default: | ||
150 | BUG(); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | EXPORT_SYMBOL(dma_unmap_single); | ||
155 | |||
156 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
157 | enum dma_data_direction direction) | ||
158 | { | ||
159 | int i; | ||
160 | |||
161 | BUG_ON(direction == DMA_NONE); | ||
162 | |||
163 | for (i = 0; i < nents; i++, sg++) { | ||
164 | unsigned long addr; | ||
165 | |||
166 | addr = (unsigned long) page_address(sg->page); | ||
167 | if (addr) | ||
168 | __dma_sync(addr + sg->offset, sg->length, direction); | ||
169 | sg->dma_address = (dma_addr_t) | ||
170 | (page_to_phys(sg->page) + sg->offset); | ||
171 | } | ||
172 | |||
173 | return nents; | ||
174 | } | ||
175 | |||
176 | EXPORT_SYMBOL(dma_map_sg); | ||
177 | |||
178 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
179 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
180 | { | ||
181 | unsigned long addr; | ||
182 | |||
183 | BUG_ON(direction == DMA_NONE); | ||
184 | |||
185 | addr = (unsigned long) page_address(page) + offset; | ||
186 | dma_cache_wback_inv(addr, size); | ||
187 | |||
188 | return page_to_phys(page) + offset; | ||
189 | } | ||
190 | |||
191 | EXPORT_SYMBOL(dma_map_page); | ||
192 | |||
193 | void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, | ||
194 | enum dma_data_direction direction) | ||
195 | { | ||
196 | BUG_ON(direction == DMA_NONE); | ||
197 | |||
198 | if (direction != DMA_TO_DEVICE) { | ||
199 | unsigned long addr; | ||
200 | |||
201 | addr = dma_address + PAGE_OFFSET; | ||
202 | dma_cache_wback_inv(addr, size); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | EXPORT_SYMBOL(dma_unmap_page); | ||
207 | |||
208 | void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, | ||
209 | enum dma_data_direction direction) | ||
210 | { | ||
211 | unsigned long addr; | ||
212 | int i; | ||
213 | |||
214 | BUG_ON(direction == DMA_NONE); | ||
215 | |||
216 | if (direction == DMA_TO_DEVICE) | ||
217 | return; | ||
218 | |||
219 | for (i = 0; i < nhwentries; i++, sg++) { | ||
220 | addr = (unsigned long) page_address(sg->page); | ||
221 | if (!addr) | ||
222 | continue; | ||
223 | dma_cache_wback_inv(addr + sg->offset, sg->length); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | EXPORT_SYMBOL(dma_unmap_sg); | ||
228 | |||
229 | void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
230 | size_t size, enum dma_data_direction direction) | ||
231 | { | ||
232 | unsigned long addr; | ||
233 | |||
234 | BUG_ON(direction == DMA_NONE); | ||
235 | |||
236 | addr = dma_handle + PAGE_OFFSET; | ||
237 | __dma_sync(addr, size, direction); | ||
238 | } | ||
239 | |||
240 | EXPORT_SYMBOL(dma_sync_single_for_cpu); | ||
241 | |||
242 | void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, | ||
243 | size_t size, enum dma_data_direction direction) | ||
244 | { | ||
245 | unsigned long addr; | ||
246 | |||
247 | BUG_ON(direction == DMA_NONE); | ||
248 | |||
249 | addr = dma_handle + PAGE_OFFSET; | ||
250 | __dma_sync(addr, size, direction); | ||
251 | } | ||
252 | |||
253 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
254 | |||
255 | void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, | ||
256 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
257 | { | ||
258 | unsigned long addr; | ||
259 | |||
260 | BUG_ON(direction == DMA_NONE); | ||
261 | |||
262 | addr = dma_handle + offset + PAGE_OFFSET; | ||
263 | __dma_sync(addr, size, direction); | ||
264 | } | ||
265 | |||
266 | EXPORT_SYMBOL(dma_sync_single_range_for_cpu); | ||
267 | |||
268 | void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, | ||
269 | unsigned long offset, size_t size, enum dma_data_direction direction) | ||
270 | { | ||
271 | unsigned long addr; | ||
272 | |||
273 | BUG_ON(direction == DMA_NONE); | ||
274 | |||
275 | addr = dma_handle + offset + PAGE_OFFSET; | ||
276 | __dma_sync(addr, size, direction); | ||
277 | } | ||
278 | |||
279 | EXPORT_SYMBOL(dma_sync_single_range_for_device); | ||
280 | |||
281 | void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, | ||
282 | enum dma_data_direction direction) | ||
283 | { | ||
284 | int i; | ||
285 | |||
286 | BUG_ON(direction == DMA_NONE); | ||
287 | |||
288 | /* Make sure that gcc doesn't leave the empty loop body. */ | ||
289 | for (i = 0; i < nelems; i++, sg++) | ||
290 | __dma_sync((unsigned long)page_address(sg->page), | ||
291 | sg->length, direction); | ||
292 | } | ||
293 | |||
294 | EXPORT_SYMBOL(dma_sync_sg_for_cpu); | ||
295 | |||
296 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, | ||
297 | enum dma_data_direction direction) | ||
298 | { | ||
299 | int i; | ||
300 | |||
301 | BUG_ON(direction == DMA_NONE); | ||
302 | |||
303 | /* Make sure that gcc doesn't leave the empty loop body. */ | ||
304 | for (i = 0; i < nelems; i++, sg++) | ||
305 | __dma_sync((unsigned long)page_address(sg->page), | ||
306 | sg->length, direction); | ||
307 | } | ||
308 | |||
309 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
310 | |||
311 | int dma_mapping_error(dma_addr_t dma_addr) | ||
312 | { | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | EXPORT_SYMBOL(dma_mapping_error); | ||
317 | |||
318 | int dma_supported(struct device *dev, u64 mask) | ||
319 | { | ||
320 | /* | ||
321 | * we fall back to GFP_DMA when the mask isn't all 1s, | ||
322 | * so we can't guarantee allocations that must be | ||
323 | * within a tighter range than GFP_DMA.. | ||
324 | */ | ||
325 | if (mask < 0x00ffffff) | ||
326 | return 0; | ||
327 | |||
328 | return 1; | ||
329 | } | ||
330 | |||
331 | EXPORT_SYMBOL(dma_supported); | ||
332 | |||
333 | int dma_is_consistent(dma_addr_t dma_addr) | ||
334 | { | ||
335 | return 1; | ||
336 | } | ||
337 | |||
338 | EXPORT_SYMBOL(dma_is_consistent); | ||
339 | |||
340 | void dma_cache_sync(void *vaddr, size_t size, enum dma_data_direction direction) | ||
341 | { | ||
342 | if (direction == DMA_NONE) | ||
343 | return; | ||
344 | |||
345 | dma_cache_wback_inv((unsigned long)vaddr, size); | ||
346 | } | ||
347 | |||
348 | EXPORT_SYMBOL(dma_cache_sync); | ||
349 | |||
350 | /* The DAC routines are a PCIism.. */ | ||
351 | |||
352 | #ifdef CONFIG_PCI | ||
353 | |||
354 | #include <linux/pci.h> | ||
355 | |||
356 | dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, | ||
357 | struct page *page, unsigned long offset, int direction) | ||
358 | { | ||
359 | return (dma64_addr_t)page_to_phys(page) + offset; | ||
360 | } | ||
361 | |||
362 | EXPORT_SYMBOL(pci_dac_page_to_dma); | ||
363 | |||
364 | struct page *pci_dac_dma_to_page(struct pci_dev *pdev, | ||
365 | dma64_addr_t dma_addr) | ||
366 | { | ||
367 | return mem_map + (dma_addr >> PAGE_SHIFT); | ||
368 | } | ||
369 | |||
370 | EXPORT_SYMBOL(pci_dac_dma_to_page); | ||
371 | |||
372 | unsigned long pci_dac_dma_to_offset(struct pci_dev *pdev, | ||
373 | dma64_addr_t dma_addr) | ||
374 | { | ||
375 | return dma_addr & ~PAGE_MASK; | ||
376 | } | ||
377 | |||
378 | EXPORT_SYMBOL(pci_dac_dma_to_offset); | ||
379 | |||
380 | void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, | ||
381 | dma64_addr_t dma_addr, size_t len, int direction) | ||
382 | { | ||
383 | BUG_ON(direction == PCI_DMA_NONE); | ||
384 | |||
385 | dma_cache_wback_inv(dma_addr + PAGE_OFFSET, len); | ||
386 | } | ||
387 | |||
388 | EXPORT_SYMBOL(pci_dac_dma_sync_single_for_cpu); | ||
389 | |||
390 | void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, | ||
391 | dma64_addr_t dma_addr, size_t len, int direction) | ||
392 | { | ||
393 | BUG_ON(direction == PCI_DMA_NONE); | ||
394 | |||
395 | dma_cache_wback_inv(dma_addr + PAGE_OFFSET, len); | ||
396 | } | ||
397 | |||
398 | EXPORT_SYMBOL(pci_dac_dma_sync_single_for_device); | ||
399 | |||
400 | #endif /* CONFIG_PCI */ | ||
diff --git a/arch/mips/mm/extable.c b/arch/mips/mm/extable.c new file mode 100644 index 000000000000..297fb9f390dc --- /dev/null +++ b/arch/mips/mm/extable.c | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * linux/arch/mips/mm/extable.c | ||
3 | */ | ||
4 | #include <linux/module.h> | ||
5 | #include <linux/spinlock.h> | ||
6 | #include <asm/branch.h> | ||
7 | #include <asm/uaccess.h> | ||
8 | |||
9 | int fixup_exception(struct pt_regs *regs) | ||
10 | { | ||
11 | const struct exception_table_entry *fixup; | ||
12 | |||
13 | fixup = search_exception_tables(exception_epc(regs)); | ||
14 | if (fixup) { | ||
15 | regs->cp0_epc = fixup->nextinsn; | ||
16 | |||
17 | return 1; | ||
18 | } | ||
19 | |||
20 | return 0; | ||
21 | } | ||
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c new file mode 100644 index 000000000000..ec8077c74e9c --- /dev/null +++ b/arch/mips/mm/fault.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1995 - 2000 by Ralf Baechle | ||
7 | */ | ||
8 | #include <linux/signal.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/ptrace.h> | ||
16 | #include <linux/mman.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/smp.h> | ||
19 | #include <linux/smp_lock.h> | ||
20 | #include <linux/vt_kern.h> /* For unblank_screen() */ | ||
21 | #include <linux/module.h> | ||
22 | |||
23 | #include <asm/branch.h> | ||
24 | #include <asm/mmu_context.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/ptrace.h> | ||
28 | |||
29 | /* | ||
30 | * This routine handles page faults. It determines the address, | ||
31 | * and the problem, and then passes it off to one of the appropriate | ||
32 | * routines. | ||
33 | */ | ||
34 | asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write, | ||
35 | unsigned long address) | ||
36 | { | ||
37 | struct vm_area_struct * vma = NULL; | ||
38 | struct task_struct *tsk = current; | ||
39 | struct mm_struct *mm = tsk->mm; | ||
40 | const int field = sizeof(unsigned long) * 2; | ||
41 | siginfo_t info; | ||
42 | |||
43 | #if 0 | ||
44 | printk("Cpu%d[%s:%d:%0*lx:%ld:%0*lx]\n", smp_processor_id(), | ||
45 | current->comm, current->pid, field, address, write, | ||
46 | field, regs->cp0_epc); | ||
47 | #endif | ||
48 | |||
49 | info.si_code = SEGV_MAPERR; | ||
50 | |||
51 | /* | ||
52 | * We fault-in kernel-space virtual memory on-demand. The | ||
53 | * 'reference' page table is init_mm.pgd. | ||
54 | * | ||
55 | * NOTE! We MUST NOT take any locks for this case. We may | ||
56 | * be in an interrupt or a critical region, and should | ||
57 | * only copy the information from the master page table, | ||
58 | * nothing more. | ||
59 | */ | ||
60 | if (unlikely(address >= VMALLOC_START)) | ||
61 | goto vmalloc_fault; | ||
62 | |||
63 | /* | ||
64 | * If we're in an interrupt or have no user | ||
65 | * context, we must not take the fault.. | ||
66 | */ | ||
67 | if (in_atomic() || !mm) | ||
68 | goto bad_area_nosemaphore; | ||
69 | |||
70 | down_read(&mm->mmap_sem); | ||
71 | vma = find_vma(mm, address); | ||
72 | if (!vma) | ||
73 | goto bad_area; | ||
74 | if (vma->vm_start <= address) | ||
75 | goto good_area; | ||
76 | if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
77 | goto bad_area; | ||
78 | if (expand_stack(vma, address)) | ||
79 | goto bad_area; | ||
80 | /* | ||
81 | * Ok, we have a good vm_area for this memory access, so | ||
82 | * we can handle it.. | ||
83 | */ | ||
84 | good_area: | ||
85 | info.si_code = SEGV_ACCERR; | ||
86 | |||
87 | if (write) { | ||
88 | if (!(vma->vm_flags & VM_WRITE)) | ||
89 | goto bad_area; | ||
90 | } else { | ||
91 | if (!(vma->vm_flags & (VM_READ | VM_EXEC))) | ||
92 | goto bad_area; | ||
93 | } | ||
94 | |||
95 | survive: | ||
96 | /* | ||
97 | * If for any reason at all we couldn't handle the fault, | ||
98 | * make sure we exit gracefully rather than endlessly redo | ||
99 | * the fault. | ||
100 | */ | ||
101 | switch (handle_mm_fault(mm, vma, address, write)) { | ||
102 | case VM_FAULT_MINOR: | ||
103 | tsk->min_flt++; | ||
104 | break; | ||
105 | case VM_FAULT_MAJOR: | ||
106 | tsk->maj_flt++; | ||
107 | break; | ||
108 | case VM_FAULT_SIGBUS: | ||
109 | goto do_sigbus; | ||
110 | case VM_FAULT_OOM: | ||
111 | goto out_of_memory; | ||
112 | default: | ||
113 | BUG(); | ||
114 | } | ||
115 | |||
116 | up_read(&mm->mmap_sem); | ||
117 | return; | ||
118 | |||
119 | /* | ||
120 | * Something tried to access memory that isn't in our memory map.. | ||
121 | * Fix it, but check if it's kernel or user first.. | ||
122 | */ | ||
123 | bad_area: | ||
124 | up_read(&mm->mmap_sem); | ||
125 | |||
126 | bad_area_nosemaphore: | ||
127 | /* User mode accesses just cause a SIGSEGV */ | ||
128 | if (user_mode(regs)) { | ||
129 | tsk->thread.cp0_badvaddr = address; | ||
130 | tsk->thread.error_code = write; | ||
131 | #if 0 | ||
132 | printk("do_page_fault() #2: sending SIGSEGV to %s for " | ||
133 | "invalid %s\n%0*lx (epc == %0*lx, ra == %0*lx)\n", | ||
134 | tsk->comm, | ||
135 | write ? "write access to" : "read access from", | ||
136 | field, address, | ||
137 | field, (unsigned long) regs->cp0_epc, | ||
138 | field, (unsigned long) regs->regs[31]); | ||
139 | #endif | ||
140 | info.si_signo = SIGSEGV; | ||
141 | info.si_errno = 0; | ||
142 | /* info.si_code has been set above */ | ||
143 | info.si_addr = (void *) address; | ||
144 | force_sig_info(SIGSEGV, &info, tsk); | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | no_context: | ||
149 | /* Are we prepared to handle this kernel fault? */ | ||
150 | if (fixup_exception(regs)) { | ||
151 | current->thread.cp0_baduaddr = address; | ||
152 | return; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * Oops. The kernel tried to access some bad page. We'll have to | ||
157 | * terminate things with extreme prejudice. | ||
158 | */ | ||
159 | |||
160 | bust_spinlocks(1); | ||
161 | |||
162 | printk(KERN_ALERT "CPU %d Unable to handle kernel paging request at " | ||
163 | "virtual address %0*lx, epc == %0*lx, ra == %0*lx\n", | ||
164 | smp_processor_id(), field, address, field, regs->cp0_epc, | ||
165 | field, regs->regs[31]); | ||
166 | die("Oops", regs); | ||
167 | |||
168 | /* | ||
169 | * We ran out of memory, or some other thing happened to us that made | ||
170 | * us unable to handle the page fault gracefully. | ||
171 | */ | ||
172 | out_of_memory: | ||
173 | up_read(&mm->mmap_sem); | ||
174 | if (tsk->pid == 1) { | ||
175 | yield(); | ||
176 | down_read(&mm->mmap_sem); | ||
177 | goto survive; | ||
178 | } | ||
179 | printk("VM: killing process %s\n", tsk->comm); | ||
180 | if (user_mode(regs)) | ||
181 | do_exit(SIGKILL); | ||
182 | goto no_context; | ||
183 | |||
184 | do_sigbus: | ||
185 | up_read(&mm->mmap_sem); | ||
186 | |||
187 | /* Kernel mode? Handle exceptions or die */ | ||
188 | if (!user_mode(regs)) | ||
189 | goto no_context; | ||
190 | |||
191 | /* | ||
192 | * Send a sigbus, regardless of whether we were in kernel | ||
193 | * or user mode. | ||
194 | */ | ||
195 | tsk->thread.cp0_badvaddr = address; | ||
196 | info.si_signo = SIGBUS; | ||
197 | info.si_errno = 0; | ||
198 | info.si_code = BUS_ADRERR; | ||
199 | info.si_addr = (void *) address; | ||
200 | force_sig_info(SIGBUS, &info, tsk); | ||
201 | |||
202 | return; | ||
203 | |||
204 | vmalloc_fault: | ||
205 | { | ||
206 | /* | ||
207 | * Synchronize this task's top level page-table | ||
208 | * with the 'reference' page table. | ||
209 | * | ||
210 | * Do _not_ use "tsk" here. We might be inside | ||
211 | * an interrupt in the middle of a task switch.. | ||
212 | */ | ||
213 | int offset = __pgd_offset(address); | ||
214 | pgd_t *pgd, *pgd_k; | ||
215 | pmd_t *pmd, *pmd_k; | ||
216 | pte_t *pte_k; | ||
217 | |||
218 | pgd = (pgd_t *) pgd_current[smp_processor_id()] + offset; | ||
219 | pgd_k = init_mm.pgd + offset; | ||
220 | |||
221 | if (!pgd_present(*pgd_k)) | ||
222 | goto no_context; | ||
223 | set_pgd(pgd, *pgd_k); | ||
224 | |||
225 | pmd = pmd_offset(pgd, address); | ||
226 | pmd_k = pmd_offset(pgd_k, address); | ||
227 | if (!pmd_present(*pmd_k)) | ||
228 | goto no_context; | ||
229 | set_pmd(pmd, *pmd_k); | ||
230 | |||
231 | pte_k = pte_offset_kernel(pmd_k, address); | ||
232 | if (!pte_present(*pte_k)) | ||
233 | goto no_context; | ||
234 | return; | ||
235 | } | ||
236 | } | ||
diff --git a/arch/mips/mm/highmem.c b/arch/mips/mm/highmem.c new file mode 100644 index 000000000000..dd5e2e31885b --- /dev/null +++ b/arch/mips/mm/highmem.c | |||
@@ -0,0 +1,103 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/module.h> | ||
3 | #include <linux/highmem.h> | ||
4 | #include <asm/tlbflush.h> | ||
5 | |||
6 | void *__kmap(struct page *page) | ||
7 | { | ||
8 | void *addr; | ||
9 | |||
10 | might_sleep(); | ||
11 | if (!PageHighMem(page)) | ||
12 | return page_address(page); | ||
13 | addr = kmap_high(page); | ||
14 | flush_tlb_one((unsigned long)addr); | ||
15 | |||
16 | return addr; | ||
17 | } | ||
18 | |||
19 | void __kunmap(struct page *page) | ||
20 | { | ||
21 | if (in_interrupt()) | ||
22 | BUG(); | ||
23 | if (!PageHighMem(page)) | ||
24 | return; | ||
25 | kunmap_high(page); | ||
26 | } | ||
27 | |||
28 | /* | ||
29 | * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because | ||
30 | * no global lock is needed and because the kmap code must perform a global TLB | ||
31 | * invalidation when the kmap pool wraps. | ||
32 | * | ||
33 | * However when holding an atomic kmap is is not legal to sleep, so atomic | ||
34 | * kmaps are appropriate for short, tight code paths only. | ||
35 | */ | ||
36 | |||
37 | void *__kmap_atomic(struct page *page, enum km_type type) | ||
38 | { | ||
39 | enum fixed_addresses idx; | ||
40 | unsigned long vaddr; | ||
41 | |||
42 | /* even !CONFIG_PREEMPT needs this, for in_atomic in do_page_fault */ | ||
43 | inc_preempt_count(); | ||
44 | if (!PageHighMem(page)) | ||
45 | return page_address(page); | ||
46 | |||
47 | idx = type + KM_TYPE_NR*smp_processor_id(); | ||
48 | vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); | ||
49 | #ifdef CONFIG_DEBUG_HIGHMEM | ||
50 | if (!pte_none(*(kmap_pte-idx))) | ||
51 | BUG(); | ||
52 | #endif | ||
53 | set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); | ||
54 | local_flush_tlb_one((unsigned long)vaddr); | ||
55 | |||
56 | return (void*) vaddr; | ||
57 | } | ||
58 | |||
59 | void __kunmap_atomic(void *kvaddr, enum km_type type) | ||
60 | { | ||
61 | #ifdef CONFIG_DEBUG_HIGHMEM | ||
62 | unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; | ||
63 | enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id(); | ||
64 | |||
65 | if (vaddr < FIXADDR_START) { // FIXME | ||
66 | dec_preempt_count(); | ||
67 | preempt_check_resched(); | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx)) | ||
72 | BUG(); | ||
73 | |||
74 | /* | ||
75 | * force other mappings to Oops if they'll try to access | ||
76 | * this pte without first remap it | ||
77 | */ | ||
78 | pte_clear(&init_mm, vaddr, kmap_pte-idx); | ||
79 | local_flush_tlb_one(vaddr); | ||
80 | #endif | ||
81 | |||
82 | dec_preempt_count(); | ||
83 | preempt_check_resched(); | ||
84 | } | ||
85 | |||
86 | struct page *__kmap_atomic_to_page(void *ptr) | ||
87 | { | ||
88 | unsigned long idx, vaddr = (unsigned long)ptr; | ||
89 | pte_t *pte; | ||
90 | |||
91 | if (vaddr < FIXADDR_START) | ||
92 | return virt_to_page(ptr); | ||
93 | |||
94 | idx = virt_to_fix(vaddr); | ||
95 | pte = kmap_pte - (idx - FIX_KMAP_BEGIN); | ||
96 | return pte_page(*pte); | ||
97 | } | ||
98 | |||
99 | EXPORT_SYMBOL(__kmap); | ||
100 | EXPORT_SYMBOL(__kunmap); | ||
101 | EXPORT_SYMBOL(__kmap_atomic); | ||
102 | EXPORT_SYMBOL(__kunmap_atomic); | ||
103 | EXPORT_SYMBOL(__kmap_atomic_to_page); | ||
diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c new file mode 100644 index 000000000000..b027ce7efbc6 --- /dev/null +++ b/arch/mips/mm/init.c | |||
@@ -0,0 +1,304 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1994 - 2000 Ralf Baechle | ||
7 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | ||
8 | * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com | ||
9 | * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. | ||
10 | */ | ||
11 | #include <linux/config.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/signal.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/pagemap.h> | ||
21 | #include <linux/ptrace.h> | ||
22 | #include <linux/mman.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/bootmem.h> | ||
25 | #include <linux/highmem.h> | ||
26 | #include <linux/swap.h> | ||
27 | |||
28 | #include <asm/bootinfo.h> | ||
29 | #include <asm/cachectl.h> | ||
30 | #include <asm/cpu.h> | ||
31 | #include <asm/dma.h> | ||
32 | #include <asm/mmu_context.h> | ||
33 | #include <asm/sections.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/pgalloc.h> | ||
36 | #include <asm/tlb.h> | ||
37 | |||
38 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
39 | |||
40 | unsigned long highstart_pfn, highend_pfn; | ||
41 | |||
42 | /* | ||
43 | * We have up to 8 empty zeroed pages so we can map one of the right colour | ||
44 | * when needed. This is necessary only on R4000 / R4400 SC and MC versions | ||
45 | * where we have to avoid VCED / VECI exceptions for good performance at | ||
46 | * any price. Since page is never written to after the initialization we | ||
47 | * don't have to care about aliases on other CPUs. | ||
48 | */ | ||
49 | unsigned long empty_zero_page, zero_page_mask; | ||
50 | |||
51 | /* | ||
52 | * Not static inline because used by IP27 special magic initialization code | ||
53 | */ | ||
54 | unsigned long setup_zero_pages(void) | ||
55 | { | ||
56 | unsigned long order, size; | ||
57 | struct page *page; | ||
58 | |||
59 | if (cpu_has_vce) | ||
60 | order = 3; | ||
61 | else | ||
62 | order = 0; | ||
63 | |||
64 | empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); | ||
65 | if (!empty_zero_page) | ||
66 | panic("Oh boy, that early out of memory?"); | ||
67 | |||
68 | page = virt_to_page(empty_zero_page); | ||
69 | while (page < virt_to_page(empty_zero_page + (PAGE_SIZE << order))) { | ||
70 | set_bit(PG_reserved, &page->flags); | ||
71 | set_page_count(page, 0); | ||
72 | page++; | ||
73 | } | ||
74 | |||
75 | size = PAGE_SIZE << order; | ||
76 | zero_page_mask = (size - 1) & PAGE_MASK; | ||
77 | |||
78 | return 1UL << order; | ||
79 | } | ||
80 | |||
81 | #ifdef CONFIG_HIGHMEM | ||
82 | pte_t *kmap_pte; | ||
83 | pgprot_t kmap_prot; | ||
84 | |||
85 | #define kmap_get_fixmap_pte(vaddr) \ | ||
86 | pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) | ||
87 | |||
88 | static void __init kmap_init(void) | ||
89 | { | ||
90 | unsigned long kmap_vstart; | ||
91 | |||
92 | /* cache the first kmap pte */ | ||
93 | kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); | ||
94 | kmap_pte = kmap_get_fixmap_pte(kmap_vstart); | ||
95 | |||
96 | kmap_prot = PAGE_KERNEL; | ||
97 | } | ||
98 | |||
99 | #ifdef CONFIG_MIPS64 | ||
100 | static void __init fixrange_init(unsigned long start, unsigned long end, | ||
101 | pgd_t *pgd_base) | ||
102 | { | ||
103 | pgd_t *pgd; | ||
104 | pmd_t *pmd; | ||
105 | pte_t *pte; | ||
106 | int i, j; | ||
107 | unsigned long vaddr; | ||
108 | |||
109 | vaddr = start; | ||
110 | i = __pgd_offset(vaddr); | ||
111 | j = __pmd_offset(vaddr); | ||
112 | pgd = pgd_base + i; | ||
113 | |||
114 | for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) { | ||
115 | pmd = (pmd_t *)pgd; | ||
116 | for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) { | ||
117 | if (pmd_none(*pmd)) { | ||
118 | pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); | ||
119 | set_pmd(pmd, __pmd(pte)); | ||
120 | if (pte != pte_offset_kernel(pmd, 0)) | ||
121 | BUG(); | ||
122 | } | ||
123 | vaddr += PMD_SIZE; | ||
124 | } | ||
125 | j = 0; | ||
126 | } | ||
127 | } | ||
128 | #endif /* CONFIG_MIPS64 */ | ||
129 | #endif /* CONFIG_HIGHMEM */ | ||
130 | |||
131 | #ifndef CONFIG_DISCONTIGMEM | ||
132 | extern void pagetable_init(void); | ||
133 | |||
134 | void __init paging_init(void) | ||
135 | { | ||
136 | unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; | ||
137 | unsigned long max_dma, high, low; | ||
138 | |||
139 | pagetable_init(); | ||
140 | |||
141 | #ifdef CONFIG_HIGHMEM | ||
142 | kmap_init(); | ||
143 | #endif | ||
144 | |||
145 | max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; | ||
146 | low = max_low_pfn; | ||
147 | high = highend_pfn; | ||
148 | |||
149 | #ifdef CONFIG_ISA | ||
150 | if (low < max_dma) | ||
151 | zones_size[ZONE_DMA] = low; | ||
152 | else { | ||
153 | zones_size[ZONE_DMA] = max_dma; | ||
154 | zones_size[ZONE_NORMAL] = low - max_dma; | ||
155 | } | ||
156 | #else | ||
157 | zones_size[ZONE_DMA] = low; | ||
158 | #endif | ||
159 | #ifdef CONFIG_HIGHMEM | ||
160 | if (cpu_has_dc_aliases) { | ||
161 | printk(KERN_WARNING "This processor doesn't support highmem."); | ||
162 | if (high - low) | ||
163 | printk(" %ldk highmem ignored", high - low); | ||
164 | printk("\n"); | ||
165 | } else | ||
166 | zones_size[ZONE_HIGHMEM] = high - low; | ||
167 | #endif | ||
168 | |||
169 | free_area_init(zones_size); | ||
170 | } | ||
171 | |||
172 | #define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT) | ||
173 | #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
174 | |||
175 | static inline int page_is_ram(unsigned long pagenr) | ||
176 | { | ||
177 | int i; | ||
178 | |||
179 | for (i = 0; i < boot_mem_map.nr_map; i++) { | ||
180 | unsigned long addr, end; | ||
181 | |||
182 | if (boot_mem_map.map[i].type != BOOT_MEM_RAM) | ||
183 | /* not usable memory */ | ||
184 | continue; | ||
185 | |||
186 | addr = PFN_UP(boot_mem_map.map[i].addr); | ||
187 | end = PFN_DOWN(boot_mem_map.map[i].addr + | ||
188 | boot_mem_map.map[i].size); | ||
189 | |||
190 | if (pagenr >= addr && pagenr < end) | ||
191 | return 1; | ||
192 | } | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | void __init mem_init(void) | ||
198 | { | ||
199 | unsigned long codesize, reservedpages, datasize, initsize; | ||
200 | unsigned long tmp, ram; | ||
201 | |||
202 | #ifdef CONFIG_HIGHMEM | ||
203 | #ifdef CONFIG_DISCONTIGMEM | ||
204 | #error "CONFIG_HIGHMEM and CONFIG_DISCONTIGMEM dont work together yet" | ||
205 | #endif | ||
206 | max_mapnr = num_physpages = highend_pfn; | ||
207 | #else | ||
208 | max_mapnr = num_physpages = max_low_pfn; | ||
209 | #endif | ||
210 | high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); | ||
211 | |||
212 | totalram_pages += free_all_bootmem(); | ||
213 | totalram_pages -= setup_zero_pages(); /* Setup zeroed pages. */ | ||
214 | |||
215 | reservedpages = ram = 0; | ||
216 | for (tmp = 0; tmp < max_low_pfn; tmp++) | ||
217 | if (page_is_ram(tmp)) { | ||
218 | ram++; | ||
219 | if (PageReserved(mem_map+tmp)) | ||
220 | reservedpages++; | ||
221 | } | ||
222 | |||
223 | #ifdef CONFIG_HIGHMEM | ||
224 | for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { | ||
225 | struct page *page = mem_map + tmp; | ||
226 | |||
227 | if (!page_is_ram(tmp)) { | ||
228 | SetPageReserved(page); | ||
229 | continue; | ||
230 | } | ||
231 | ClearPageReserved(page); | ||
232 | #ifdef CONFIG_LIMITED_DMA | ||
233 | set_page_address(page, lowmem_page_address(page)); | ||
234 | #endif | ||
235 | set_bit(PG_highmem, &page->flags); | ||
236 | set_page_count(page, 1); | ||
237 | __free_page(page); | ||
238 | totalhigh_pages++; | ||
239 | } | ||
240 | totalram_pages += totalhigh_pages; | ||
241 | #endif | ||
242 | |||
243 | codesize = (unsigned long) &_etext - (unsigned long) &_text; | ||
244 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | ||
245 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | ||
246 | |||
247 | printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " | ||
248 | "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n", | ||
249 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), | ||
250 | ram << (PAGE_SHIFT-10), | ||
251 | codesize >> 10, | ||
252 | reservedpages << (PAGE_SHIFT-10), | ||
253 | datasize >> 10, | ||
254 | initsize >> 10, | ||
255 | (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))); | ||
256 | } | ||
257 | #endif /* !CONFIG_DISCONTIGMEM */ | ||
258 | |||
259 | #ifdef CONFIG_BLK_DEV_INITRD | ||
260 | void free_initrd_mem(unsigned long start, unsigned long end) | ||
261 | { | ||
262 | #ifdef CONFIG_MIPS64 | ||
263 | /* Switch from KSEG0 to XKPHYS addresses */ | ||
264 | start = (unsigned long)phys_to_virt(CPHYSADDR(start)); | ||
265 | end = (unsigned long)phys_to_virt(CPHYSADDR(end)); | ||
266 | #endif | ||
267 | if (start < end) | ||
268 | printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", | ||
269 | (end - start) >> 10); | ||
270 | |||
271 | for (; start < end; start += PAGE_SIZE) { | ||
272 | ClearPageReserved(virt_to_page(start)); | ||
273 | set_page_count(virt_to_page(start), 1); | ||
274 | free_page(start); | ||
275 | totalram_pages++; | ||
276 | } | ||
277 | } | ||
278 | #endif | ||
279 | |||
280 | extern unsigned long prom_free_prom_memory(void); | ||
281 | |||
282 | void free_initmem(void) | ||
283 | { | ||
284 | unsigned long addr, page, freed; | ||
285 | |||
286 | freed = prom_free_prom_memory(); | ||
287 | |||
288 | addr = (unsigned long) &__init_begin; | ||
289 | while (addr < (unsigned long) &__init_end) { | ||
290 | #ifdef CONFIG_MIPS64 | ||
291 | page = PAGE_OFFSET | CPHYSADDR(addr); | ||
292 | #else | ||
293 | page = addr; | ||
294 | #endif | ||
295 | ClearPageReserved(virt_to_page(page)); | ||
296 | set_page_count(virt_to_page(page), 1); | ||
297 | free_page(page); | ||
298 | totalram_pages++; | ||
299 | freed += PAGE_SIZE; | ||
300 | addr += PAGE_SIZE; | ||
301 | } | ||
302 | printk(KERN_INFO "Freeing unused kernel memory: %ldk freed\n", | ||
303 | freed >> 10); | ||
304 | } | ||
diff --git a/arch/mips/mm/ioremap.c b/arch/mips/mm/ioremap.c new file mode 100644 index 000000000000..adf352273f63 --- /dev/null +++ b/arch/mips/mm/ioremap.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * (C) Copyright 1995 1996 Linus Torvalds | ||
7 | * (C) Copyright 2001, 2002 Ralf Baechle | ||
8 | */ | ||
9 | #include <linux/module.h> | ||
10 | #include <asm/addrspace.h> | ||
11 | #include <asm/byteorder.h> | ||
12 | |||
13 | #include <linux/vmalloc.h> | ||
14 | #include <asm/cacheflush.h> | ||
15 | #include <asm/io.h> | ||
16 | #include <asm/tlbflush.h> | ||
17 | |||
18 | static inline void remap_area_pte(pte_t * pte, unsigned long address, | ||
19 | phys_t size, phys_t phys_addr, unsigned long flags) | ||
20 | { | ||
21 | phys_t end; | ||
22 | unsigned long pfn; | ||
23 | pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | __READABLE | ||
24 | | __WRITEABLE | flags); | ||
25 | |||
26 | address &= ~PMD_MASK; | ||
27 | end = address + size; | ||
28 | if (end > PMD_SIZE) | ||
29 | end = PMD_SIZE; | ||
30 | if (address >= end) | ||
31 | BUG(); | ||
32 | pfn = phys_addr >> PAGE_SHIFT; | ||
33 | do { | ||
34 | if (!pte_none(*pte)) { | ||
35 | printk("remap_area_pte: page already exists\n"); | ||
36 | BUG(); | ||
37 | } | ||
38 | set_pte(pte, pfn_pte(pfn, pgprot)); | ||
39 | address += PAGE_SIZE; | ||
40 | pfn++; | ||
41 | pte++; | ||
42 | } while (address && (address < end)); | ||
43 | } | ||
44 | |||
45 | static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, | ||
46 | phys_t size, phys_t phys_addr, unsigned long flags) | ||
47 | { | ||
48 | phys_t end; | ||
49 | |||
50 | address &= ~PGDIR_MASK; | ||
51 | end = address + size; | ||
52 | if (end > PGDIR_SIZE) | ||
53 | end = PGDIR_SIZE; | ||
54 | phys_addr -= address; | ||
55 | if (address >= end) | ||
56 | BUG(); | ||
57 | do { | ||
58 | pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); | ||
59 | if (!pte) | ||
60 | return -ENOMEM; | ||
61 | remap_area_pte(pte, address, end - address, address + phys_addr, flags); | ||
62 | address = (address + PMD_SIZE) & PMD_MASK; | ||
63 | pmd++; | ||
64 | } while (address && (address < end)); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int remap_area_pages(unsigned long address, phys_t phys_addr, | ||
69 | phys_t size, unsigned long flags) | ||
70 | { | ||
71 | int error; | ||
72 | pgd_t * dir; | ||
73 | unsigned long end = address + size; | ||
74 | |||
75 | phys_addr -= address; | ||
76 | dir = pgd_offset(&init_mm, address); | ||
77 | flush_cache_all(); | ||
78 | if (address >= end) | ||
79 | BUG(); | ||
80 | spin_lock(&init_mm.page_table_lock); | ||
81 | do { | ||
82 | pmd_t *pmd; | ||
83 | pmd = pmd_alloc(&init_mm, dir, address); | ||
84 | error = -ENOMEM; | ||
85 | if (!pmd) | ||
86 | break; | ||
87 | if (remap_area_pmd(pmd, address, end - address, | ||
88 | phys_addr + address, flags)) | ||
89 | break; | ||
90 | error = 0; | ||
91 | address = (address + PGDIR_SIZE) & PGDIR_MASK; | ||
92 | dir++; | ||
93 | } while (address && (address < end)); | ||
94 | spin_unlock(&init_mm.page_table_lock); | ||
95 | flush_tlb_all(); | ||
96 | return error; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Allow physical addresses to be fixed up to help 36 bit peripherals. | ||
101 | */ | ||
102 | phys_t __attribute__ ((weak)) | ||
103 | fixup_bigphys_addr(phys_t phys_addr, phys_t size) | ||
104 | { | ||
105 | return phys_addr; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Generic mapping function (not visible outside): | ||
110 | */ | ||
111 | |||
112 | /* | ||
113 | * Remap an arbitrary physical address space into the kernel virtual | ||
114 | * address space. Needed when the kernel wants to access high addresses | ||
115 | * directly. | ||
116 | * | ||
117 | * NOTE! We need to allow non-page-aligned mappings too: we will obviously | ||
118 | * have to convert them into an offset in a page-aligned mapping, but the | ||
119 | * caller shouldn't need to know that small detail. | ||
120 | */ | ||
121 | |||
122 | #define IS_LOW512(addr) (!((phys_t)(addr) & (phys_t) ~0x1fffffffULL)) | ||
123 | |||
124 | void * __ioremap(phys_t phys_addr, phys_t size, unsigned long flags) | ||
125 | { | ||
126 | struct vm_struct * area; | ||
127 | unsigned long offset; | ||
128 | phys_t last_addr; | ||
129 | void * addr; | ||
130 | |||
131 | phys_addr = fixup_bigphys_addr(phys_addr, size); | ||
132 | |||
133 | /* Don't allow wraparound or zero size */ | ||
134 | last_addr = phys_addr + size - 1; | ||
135 | if (!size || last_addr < phys_addr) | ||
136 | return NULL; | ||
137 | |||
138 | /* | ||
139 | * Map uncached objects in the low 512mb of address space using KSEG1, | ||
140 | * otherwise map using page tables. | ||
141 | */ | ||
142 | if (IS_LOW512(phys_addr) && IS_LOW512(last_addr) && | ||
143 | flags == _CACHE_UNCACHED) | ||
144 | return (void *) KSEG1ADDR(phys_addr); | ||
145 | |||
146 | /* | ||
147 | * Don't allow anybody to remap normal RAM that we're using.. | ||
148 | */ | ||
149 | if (phys_addr < virt_to_phys(high_memory)) { | ||
150 | char *t_addr, *t_end; | ||
151 | struct page *page; | ||
152 | |||
153 | t_addr = __va(phys_addr); | ||
154 | t_end = t_addr + (size - 1); | ||
155 | |||
156 | for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) | ||
157 | if(!PageReserved(page)) | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * Mappings have to be page-aligned | ||
163 | */ | ||
164 | offset = phys_addr & ~PAGE_MASK; | ||
165 | phys_addr &= PAGE_MASK; | ||
166 | size = PAGE_ALIGN(last_addr + 1) - phys_addr; | ||
167 | |||
168 | /* | ||
169 | * Ok, go for it.. | ||
170 | */ | ||
171 | area = get_vm_area(size, VM_IOREMAP); | ||
172 | if (!area) | ||
173 | return NULL; | ||
174 | addr = area->addr; | ||
175 | if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { | ||
176 | vunmap(addr); | ||
177 | return NULL; | ||
178 | } | ||
179 | |||
180 | return (void *) (offset + (char *)addr); | ||
181 | } | ||
182 | |||
183 | #define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == KSEG1) | ||
184 | |||
185 | void __iounmap(volatile void __iomem *addr) | ||
186 | { | ||
187 | struct vm_struct *p; | ||
188 | |||
189 | if (IS_KSEG1(addr)) | ||
190 | return; | ||
191 | |||
192 | p = remove_vm_area((void *) (PAGE_MASK & (unsigned long __force) addr)); | ||
193 | if (!p) { | ||
194 | printk(KERN_ERR "iounmap: bad address %p\n", addr); | ||
195 | return; | ||
196 | } | ||
197 | |||
198 | kfree(p); | ||
199 | } | ||
200 | |||
201 | EXPORT_SYMBOL(__ioremap); | ||
202 | EXPORT_SYMBOL(__iounmap); | ||
diff --git a/arch/mips/mm/pg-r4k.c b/arch/mips/mm/pg-r4k.c new file mode 100644 index 000000000000..9f8b16541577 --- /dev/null +++ b/arch/mips/mm/pg-r4k.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org) | ||
7 | */ | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/proc_fs.h> | ||
14 | |||
15 | #include <asm/cacheops.h> | ||
16 | #include <asm/inst.h> | ||
17 | #include <asm/io.h> | ||
18 | #include <asm/page.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/prefetch.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/bootinfo.h> | ||
23 | #include <asm/mipsregs.h> | ||
24 | #include <asm/mmu_context.h> | ||
25 | #include <asm/cpu.h> | ||
26 | #include <asm/war.h> | ||
27 | |||
28 | #define half_scache_line_size() (cpu_scache_line_size() >> 1) | ||
29 | |||
30 | /* | ||
31 | * Maximum sizes: | ||
32 | * | ||
33 | * R4000 128 bytes S-cache: 0x58 bytes | ||
34 | * R4600 v1.7: 0x5c bytes | ||
35 | * R4600 v2.0: 0x60 bytes | ||
36 | * With prefetching, 16 byte strides 0xa0 bytes | ||
37 | */ | ||
38 | |||
39 | static unsigned int clear_page_array[0x130 / 4]; | ||
40 | |||
41 | void clear_page(void * page) __attribute__((alias("clear_page_array"))); | ||
42 | |||
43 | EXPORT_SYMBOL(clear_page); | ||
44 | |||
45 | /* | ||
46 | * Maximum sizes: | ||
47 | * | ||
48 | * R4000 128 bytes S-cache: 0x11c bytes | ||
49 | * R4600 v1.7: 0x080 bytes | ||
50 | * R4600 v2.0: 0x07c bytes | ||
51 | * With prefetching, 16 byte strides 0x0b8 bytes | ||
52 | */ | ||
53 | static unsigned int copy_page_array[0x148 / 4]; | ||
54 | |||
55 | void copy_page(void *to, void *from) __attribute__((alias("copy_page_array"))); | ||
56 | |||
57 | EXPORT_SYMBOL(copy_page); | ||
58 | |||
59 | /* | ||
60 | * This is suboptimal for 32-bit kernels; we assume that R10000 is only used | ||
61 | * with 64-bit kernels. The prefetch offsets have been experimentally tuned | ||
62 | * an Origin 200. | ||
63 | */ | ||
64 | static int pref_offset_clear __initdata = 512; | ||
65 | static int pref_offset_copy __initdata = 256; | ||
66 | |||
67 | static unsigned int pref_src_mode __initdata; | ||
68 | static unsigned int pref_dst_mode __initdata; | ||
69 | |||
70 | static int load_offset __initdata; | ||
71 | static int store_offset __initdata; | ||
72 | |||
73 | static unsigned int __initdata *dest, *epc; | ||
74 | |||
75 | static unsigned int instruction_pending; | ||
76 | static union mips_instruction delayed_mi; | ||
77 | |||
78 | static void __init emit_instruction(union mips_instruction mi) | ||
79 | { | ||
80 | if (instruction_pending) | ||
81 | *epc++ = delayed_mi.word; | ||
82 | |||
83 | instruction_pending = 1; | ||
84 | delayed_mi = mi; | ||
85 | } | ||
86 | |||
87 | static inline void flush_delay_slot_or_nop(void) | ||
88 | { | ||
89 | if (instruction_pending) { | ||
90 | *epc++ = delayed_mi.word; | ||
91 | instruction_pending = 0; | ||
92 | return; | ||
93 | } | ||
94 | |||
95 | *epc++ = 0; | ||
96 | } | ||
97 | |||
98 | static inline unsigned int *label(void) | ||
99 | { | ||
100 | if (instruction_pending) { | ||
101 | *epc++ = delayed_mi.word; | ||
102 | instruction_pending = 0; | ||
103 | } | ||
104 | |||
105 | return epc; | ||
106 | } | ||
107 | |||
108 | static inline void build_insn_word(unsigned int word) | ||
109 | { | ||
110 | union mips_instruction mi; | ||
111 | |||
112 | mi.word = word; | ||
113 | |||
114 | emit_instruction(mi); | ||
115 | } | ||
116 | |||
117 | static inline void build_nop(void) | ||
118 | { | ||
119 | build_insn_word(0); /* nop */ | ||
120 | } | ||
121 | |||
122 | static inline void build_src_pref(int advance) | ||
123 | { | ||
124 | if (!(load_offset & (cpu_dcache_line_size() - 1))) { | ||
125 | union mips_instruction mi; | ||
126 | |||
127 | mi.i_format.opcode = pref_op; | ||
128 | mi.i_format.rs = 5; /* $a1 */ | ||
129 | mi.i_format.rt = pref_src_mode; | ||
130 | mi.i_format.simmediate = load_offset + advance; | ||
131 | |||
132 | emit_instruction(mi); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | static inline void __build_load_reg(int reg) | ||
137 | { | ||
138 | union mips_instruction mi; | ||
139 | unsigned int width; | ||
140 | |||
141 | if (cpu_has_64bit_gp_regs) { | ||
142 | mi.i_format.opcode = ld_op; | ||
143 | width = 8; | ||
144 | } else { | ||
145 | mi.i_format.opcode = lw_op; | ||
146 | width = 4; | ||
147 | } | ||
148 | mi.i_format.rs = 5; /* $a1 */ | ||
149 | mi.i_format.rt = reg; /* $reg */ | ||
150 | mi.i_format.simmediate = load_offset; | ||
151 | |||
152 | load_offset += width; | ||
153 | emit_instruction(mi); | ||
154 | } | ||
155 | |||
156 | static inline void build_load_reg(int reg) | ||
157 | { | ||
158 | if (cpu_has_prefetch) | ||
159 | build_src_pref(pref_offset_copy); | ||
160 | |||
161 | __build_load_reg(reg); | ||
162 | } | ||
163 | |||
164 | static inline void build_dst_pref(int advance) | ||
165 | { | ||
166 | if (!(store_offset & (cpu_dcache_line_size() - 1))) { | ||
167 | union mips_instruction mi; | ||
168 | |||
169 | mi.i_format.opcode = pref_op; | ||
170 | mi.i_format.rs = 4; /* $a0 */ | ||
171 | mi.i_format.rt = pref_dst_mode; | ||
172 | mi.i_format.simmediate = store_offset + advance; | ||
173 | |||
174 | emit_instruction(mi); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | static inline void build_cdex_s(void) | ||
179 | { | ||
180 | union mips_instruction mi; | ||
181 | |||
182 | if ((store_offset & (cpu_scache_line_size() - 1))) | ||
183 | return; | ||
184 | |||
185 | mi.c_format.opcode = cache_op; | ||
186 | mi.c_format.rs = 4; /* $a0 */ | ||
187 | mi.c_format.c_op = 3; /* Create Dirty Exclusive */ | ||
188 | mi.c_format.cache = 3; /* Secondary Data Cache */ | ||
189 | mi.c_format.simmediate = store_offset; | ||
190 | |||
191 | emit_instruction(mi); | ||
192 | } | ||
193 | |||
194 | static inline void build_cdex_p(void) | ||
195 | { | ||
196 | union mips_instruction mi; | ||
197 | |||
198 | if (store_offset & (cpu_dcache_line_size() - 1)) | ||
199 | return; | ||
200 | |||
201 | if (R4600_V1_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2010)) { | ||
202 | build_nop(); | ||
203 | build_nop(); | ||
204 | build_nop(); | ||
205 | build_nop(); | ||
206 | } | ||
207 | |||
208 | if (R4600_V2_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2020)) | ||
209 | build_insn_word(0x8c200000); /* lw $zero, ($at) */ | ||
210 | |||
211 | mi.c_format.opcode = cache_op; | ||
212 | mi.c_format.rs = 4; /* $a0 */ | ||
213 | mi.c_format.c_op = 3; /* Create Dirty Exclusive */ | ||
214 | mi.c_format.cache = 1; /* Data Cache */ | ||
215 | mi.c_format.simmediate = store_offset; | ||
216 | |||
217 | emit_instruction(mi); | ||
218 | } | ||
219 | |||
220 | static void __init __build_store_reg(int reg) | ||
221 | { | ||
222 | union mips_instruction mi; | ||
223 | unsigned int width; | ||
224 | |||
225 | if (cpu_has_64bit_gp_regs || | ||
226 | (cpu_has_64bit_zero_reg && reg == 0)) { | ||
227 | mi.i_format.opcode = sd_op; | ||
228 | width = 8; | ||
229 | } else { | ||
230 | mi.i_format.opcode = sw_op; | ||
231 | width = 4; | ||
232 | } | ||
233 | mi.i_format.rs = 4; /* $a0 */ | ||
234 | mi.i_format.rt = reg; /* $reg */ | ||
235 | mi.i_format.simmediate = store_offset; | ||
236 | |||
237 | store_offset += width; | ||
238 | emit_instruction(mi); | ||
239 | } | ||
240 | |||
241 | static inline void build_store_reg(int reg) | ||
242 | { | ||
243 | if (cpu_has_prefetch) | ||
244 | if (reg) | ||
245 | build_dst_pref(pref_offset_copy); | ||
246 | else | ||
247 | build_dst_pref(pref_offset_clear); | ||
248 | else if (cpu_has_cache_cdex_s) | ||
249 | build_cdex_s(); | ||
250 | else if (cpu_has_cache_cdex_p) | ||
251 | build_cdex_p(); | ||
252 | |||
253 | __build_store_reg(reg); | ||
254 | } | ||
255 | |||
256 | static inline void build_addiu_a2_a0(unsigned long offset) | ||
257 | { | ||
258 | union mips_instruction mi; | ||
259 | |||
260 | BUG_ON(offset > 0x7fff); | ||
261 | |||
262 | mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op; | ||
263 | mi.i_format.rs = 4; /* $a0 */ | ||
264 | mi.i_format.rt = 6; /* $a2 */ | ||
265 | mi.i_format.simmediate = offset; | ||
266 | |||
267 | emit_instruction(mi); | ||
268 | } | ||
269 | |||
270 | static inline void build_addiu_a1(unsigned long offset) | ||
271 | { | ||
272 | union mips_instruction mi; | ||
273 | |||
274 | BUG_ON(offset > 0x7fff); | ||
275 | |||
276 | mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op; | ||
277 | mi.i_format.rs = 5; /* $a1 */ | ||
278 | mi.i_format.rt = 5; /* $a1 */ | ||
279 | mi.i_format.simmediate = offset; | ||
280 | |||
281 | load_offset -= offset; | ||
282 | |||
283 | emit_instruction(mi); | ||
284 | } | ||
285 | |||
286 | static inline void build_addiu_a0(unsigned long offset) | ||
287 | { | ||
288 | union mips_instruction mi; | ||
289 | |||
290 | BUG_ON(offset > 0x7fff); | ||
291 | |||
292 | mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op; | ||
293 | mi.i_format.rs = 4; /* $a0 */ | ||
294 | mi.i_format.rt = 4; /* $a0 */ | ||
295 | mi.i_format.simmediate = offset; | ||
296 | |||
297 | store_offset -= offset; | ||
298 | |||
299 | emit_instruction(mi); | ||
300 | } | ||
301 | |||
302 | static inline void build_bne(unsigned int *dest) | ||
303 | { | ||
304 | union mips_instruction mi; | ||
305 | |||
306 | mi.i_format.opcode = bne_op; | ||
307 | mi.i_format.rs = 6; /* $a2 */ | ||
308 | mi.i_format.rt = 4; /* $a0 */ | ||
309 | mi.i_format.simmediate = dest - epc - 1; | ||
310 | |||
311 | *epc++ = mi.word; | ||
312 | flush_delay_slot_or_nop(); | ||
313 | } | ||
314 | |||
315 | static inline void build_jr_ra(void) | ||
316 | { | ||
317 | union mips_instruction mi; | ||
318 | |||
319 | mi.r_format.opcode = spec_op; | ||
320 | mi.r_format.rs = 31; | ||
321 | mi.r_format.rt = 0; | ||
322 | mi.r_format.rd = 0; | ||
323 | mi.r_format.re = 0; | ||
324 | mi.r_format.func = jr_op; | ||
325 | |||
326 | *epc++ = mi.word; | ||
327 | flush_delay_slot_or_nop(); | ||
328 | } | ||
329 | |||
330 | void __init build_clear_page(void) | ||
331 | { | ||
332 | unsigned int loop_start; | ||
333 | |||
334 | epc = (unsigned int *) &clear_page_array; | ||
335 | instruction_pending = 0; | ||
336 | store_offset = 0; | ||
337 | |||
338 | if (cpu_has_prefetch) { | ||
339 | switch (current_cpu_data.cputype) { | ||
340 | case CPU_RM9000: | ||
341 | /* | ||
342 | * As a workaround for erratum G105 which make the | ||
343 | * PrepareForStore hint unusable we fall back to | ||
344 | * StoreRetained on the RM9000. Once it is known which | ||
345 | * versions of the RM9000 we'll be able to condition- | ||
346 | * alize this. | ||
347 | */ | ||
348 | |||
349 | case CPU_R10000: | ||
350 | case CPU_R12000: | ||
351 | pref_src_mode = Pref_LoadStreamed; | ||
352 | pref_dst_mode = Pref_StoreStreamed; | ||
353 | break; | ||
354 | |||
355 | default: | ||
356 | pref_src_mode = Pref_LoadStreamed; | ||
357 | pref_dst_mode = Pref_PrepareForStore; | ||
358 | break; | ||
359 | } | ||
360 | } | ||
361 | |||
362 | build_addiu_a2_a0(PAGE_SIZE - (cpu_has_prefetch ? pref_offset_clear : 0)); | ||
363 | |||
364 | if (R4600_V2_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2020)) | ||
365 | build_insn_word(0x3c01a000); /* lui $at, 0xa000 */ | ||
366 | |||
367 | dest = label(); | ||
368 | do { | ||
369 | build_store_reg(0); | ||
370 | build_store_reg(0); | ||
371 | build_store_reg(0); | ||
372 | build_store_reg(0); | ||
373 | } while (store_offset < half_scache_line_size()); | ||
374 | build_addiu_a0(2 * store_offset); | ||
375 | loop_start = store_offset; | ||
376 | do { | ||
377 | build_store_reg(0); | ||
378 | build_store_reg(0); | ||
379 | build_store_reg(0); | ||
380 | build_store_reg(0); | ||
381 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
382 | build_bne(dest); | ||
383 | |||
384 | if (cpu_has_prefetch && pref_offset_clear) { | ||
385 | build_addiu_a2_a0(pref_offset_clear); | ||
386 | dest = label(); | ||
387 | loop_start = store_offset; | ||
388 | do { | ||
389 | __build_store_reg(0); | ||
390 | __build_store_reg(0); | ||
391 | __build_store_reg(0); | ||
392 | __build_store_reg(0); | ||
393 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
394 | build_addiu_a0(2 * store_offset); | ||
395 | loop_start = store_offset; | ||
396 | do { | ||
397 | __build_store_reg(0); | ||
398 | __build_store_reg(0); | ||
399 | __build_store_reg(0); | ||
400 | __build_store_reg(0); | ||
401 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
402 | build_bne(dest); | ||
403 | } | ||
404 | |||
405 | build_jr_ra(); | ||
406 | |||
407 | flush_icache_range((unsigned long)&clear_page_array, | ||
408 | (unsigned long) epc); | ||
409 | |||
410 | BUG_ON(epc > clear_page_array + ARRAY_SIZE(clear_page_array)); | ||
411 | } | ||
412 | |||
413 | void __init build_copy_page(void) | ||
414 | { | ||
415 | unsigned int loop_start; | ||
416 | |||
417 | epc = (unsigned int *) ©_page_array; | ||
418 | store_offset = load_offset = 0; | ||
419 | instruction_pending = 0; | ||
420 | |||
421 | build_addiu_a2_a0(PAGE_SIZE - (cpu_has_prefetch ? pref_offset_copy : 0)); | ||
422 | |||
423 | if (R4600_V2_HIT_CACHEOP_WAR && ((read_c0_prid() & 0xfff0) == 0x2020)) | ||
424 | build_insn_word(0x3c01a000); /* lui $at, 0xa000 */ | ||
425 | |||
426 | dest = label(); | ||
427 | loop_start = store_offset; | ||
428 | do { | ||
429 | build_load_reg( 8); | ||
430 | build_load_reg( 9); | ||
431 | build_load_reg(10); | ||
432 | build_load_reg(11); | ||
433 | build_store_reg( 8); | ||
434 | build_store_reg( 9); | ||
435 | build_store_reg(10); | ||
436 | build_store_reg(11); | ||
437 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
438 | build_addiu_a0(2 * store_offset); | ||
439 | build_addiu_a1(2 * load_offset); | ||
440 | loop_start = store_offset; | ||
441 | do { | ||
442 | build_load_reg( 8); | ||
443 | build_load_reg( 9); | ||
444 | build_load_reg(10); | ||
445 | build_load_reg(11); | ||
446 | build_store_reg( 8); | ||
447 | build_store_reg( 9); | ||
448 | build_store_reg(10); | ||
449 | build_store_reg(11); | ||
450 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
451 | build_bne(dest); | ||
452 | |||
453 | if (cpu_has_prefetch && pref_offset_copy) { | ||
454 | build_addiu_a2_a0(pref_offset_copy); | ||
455 | dest = label(); | ||
456 | loop_start = store_offset; | ||
457 | do { | ||
458 | __build_load_reg( 8); | ||
459 | __build_load_reg( 9); | ||
460 | __build_load_reg(10); | ||
461 | __build_load_reg(11); | ||
462 | __build_store_reg( 8); | ||
463 | __build_store_reg( 9); | ||
464 | __build_store_reg(10); | ||
465 | __build_store_reg(11); | ||
466 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
467 | build_addiu_a0(2 * store_offset); | ||
468 | build_addiu_a1(2 * load_offset); | ||
469 | loop_start = store_offset; | ||
470 | do { | ||
471 | __build_load_reg( 8); | ||
472 | __build_load_reg( 9); | ||
473 | __build_load_reg(10); | ||
474 | __build_load_reg(11); | ||
475 | __build_store_reg( 8); | ||
476 | __build_store_reg( 9); | ||
477 | __build_store_reg(10); | ||
478 | __build_store_reg(11); | ||
479 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
480 | build_bne(dest); | ||
481 | } | ||
482 | |||
483 | build_jr_ra(); | ||
484 | |||
485 | flush_icache_range((unsigned long)©_page_array, | ||
486 | (unsigned long) epc); | ||
487 | |||
488 | BUG_ON(epc > copy_page_array + ARRAY_SIZE(copy_page_array)); | ||
489 | } | ||
diff --git a/arch/mips/mm/pg-sb1.c b/arch/mips/mm/pg-sb1.c new file mode 100644 index 000000000000..59d131b5e536 --- /dev/null +++ b/arch/mips/mm/pg-sb1.c | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
3 | * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) | ||
4 | * Copyright (C) 2000 SiByte, Inc. | ||
5 | * Copyright (C) 2005 Thiemo Seufer | ||
6 | * | ||
7 | * Written by Justin Carlson of SiByte, Inc. | ||
8 | * and Kip Walker of Broadcom Corp. | ||
9 | * | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version 2 | ||
14 | * of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
24 | */ | ||
25 | #include <linux/config.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/smp.h> | ||
29 | |||
30 | #include <asm/io.h> | ||
31 | #include <asm/sibyte/sb1250.h> | ||
32 | #include <asm/sibyte/sb1250_regs.h> | ||
33 | #include <asm/sibyte/sb1250_dma.h> | ||
34 | |||
35 | #ifdef CONFIG_SB1_PASS_1_WORKAROUNDS | ||
36 | #define SB1_PREF_LOAD_STREAMED_HINT "0" | ||
37 | #define SB1_PREF_STORE_STREAMED_HINT "1" | ||
38 | #else | ||
39 | #define SB1_PREF_LOAD_STREAMED_HINT "4" | ||
40 | #define SB1_PREF_STORE_STREAMED_HINT "5" | ||
41 | #endif | ||
42 | |||
43 | static inline void clear_page_cpu(void *page) | ||
44 | { | ||
45 | unsigned char *addr = (unsigned char *) page; | ||
46 | unsigned char *end = addr + PAGE_SIZE; | ||
47 | |||
48 | /* | ||
49 | * JDCXXX - This should be bottlenecked by the write buffer, but these | ||
50 | * things tend to be mildly unpredictable...should check this on the | ||
51 | * performance model | ||
52 | * | ||
53 | * We prefetch 4 lines ahead. We're also "cheating" slightly here... | ||
54 | * since we know we're on an SB1, we force the assembler to take | ||
55 | * 64-bit operands to speed things up | ||
56 | */ | ||
57 | __asm__ __volatile__( | ||
58 | " .set push \n" | ||
59 | " .set mips4 \n" | ||
60 | " .set noreorder \n" | ||
61 | #ifdef CONFIG_CPU_HAS_PREFETCH | ||
62 | " daddiu %0, %0, 128 \n" | ||
63 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -128(%0) \n" /* Prefetch the first 4 lines */ | ||
64 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -96(%0) \n" | ||
65 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -64(%0) \n" | ||
66 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -32(%0) \n" | ||
67 | "1: sd $0, -128(%0) \n" /* Throw out a cacheline of 0's */ | ||
68 | " sd $0, -120(%0) \n" | ||
69 | " sd $0, -112(%0) \n" | ||
70 | " sd $0, -104(%0) \n" | ||
71 | " daddiu %0, %0, 32 \n" | ||
72 | " bnel %0, %1, 1b \n" | ||
73 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -32(%0) \n" | ||
74 | " daddiu %0, %0, -128 \n" | ||
75 | #endif | ||
76 | " sd $0, 0(%0) \n" /* Throw out a cacheline of 0's */ | ||
77 | "1: sd $0, 8(%0) \n" | ||
78 | " sd $0, 16(%0) \n" | ||
79 | " sd $0, 24(%0) \n" | ||
80 | " daddiu %0, %0, 32 \n" | ||
81 | " bnel %0, %1, 1b \n" | ||
82 | " sd $0, 0(%0) \n" | ||
83 | " .set pop \n" | ||
84 | : "+r" (addr) | ||
85 | : "r" (end) | ||
86 | : "memory"); | ||
87 | } | ||
88 | |||
89 | static inline void copy_page_cpu(void *to, void *from) | ||
90 | { | ||
91 | unsigned char *src = (unsigned char *)from; | ||
92 | unsigned char *dst = (unsigned char *)to; | ||
93 | unsigned char *end = src + PAGE_SIZE; | ||
94 | |||
95 | /* | ||
96 | * The pref's used here are using "streaming" hints, which cause the | ||
97 | * copied data to be kicked out of the cache sooner. A page copy often | ||
98 | * ends up copying a lot more data than is commonly used, so this seems | ||
99 | * to make sense in terms of reducing cache pollution, but I've no real | ||
100 | * performance data to back this up | ||
101 | */ | ||
102 | __asm__ __volatile__( | ||
103 | " .set push \n" | ||
104 | " .set mips4 \n" | ||
105 | " .set noreorder \n" | ||
106 | #ifdef CONFIG_CPU_HAS_PREFETCH | ||
107 | " daddiu %0, %0, 128 \n" | ||
108 | " daddiu %1, %1, 128 \n" | ||
109 | " pref " SB1_PREF_LOAD_STREAMED_HINT ", -128(%0)\n" /* Prefetch the first 4 lines */ | ||
110 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -128(%1)\n" | ||
111 | " pref " SB1_PREF_LOAD_STREAMED_HINT ", -96(%0)\n" | ||
112 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -96(%1)\n" | ||
113 | " pref " SB1_PREF_LOAD_STREAMED_HINT ", -64(%0)\n" | ||
114 | " pref " SB1_PREF_STORE_STREAMED_HINT ", -64(%1)\n" | ||
115 | " pref " SB1_PREF_LOAD_STREAMED_HINT ", -32(%0)\n" | ||
116 | "1: pref " SB1_PREF_STORE_STREAMED_HINT ", -32(%1)\n" | ||
117 | # ifdef CONFIG_MIPS64 | ||
118 | " ld $8, -128(%0) \n" /* Block copy a cacheline */ | ||
119 | " ld $9, -120(%0) \n" | ||
120 | " ld $10, -112(%0) \n" | ||
121 | " ld $11, -104(%0) \n" | ||
122 | " sd $8, -128(%1) \n" | ||
123 | " sd $9, -120(%1) \n" | ||
124 | " sd $10, -112(%1) \n" | ||
125 | " sd $11, -104(%1) \n" | ||
126 | # else | ||
127 | " lw $2, -128(%0) \n" /* Block copy a cacheline */ | ||
128 | " lw $3, -124(%0) \n" | ||
129 | " lw $6, -120(%0) \n" | ||
130 | " lw $7, -116(%0) \n" | ||
131 | " lw $8, -112(%0) \n" | ||
132 | " lw $9, -108(%0) \n" | ||
133 | " lw $10, -104(%0) \n" | ||
134 | " lw $11, -100(%0) \n" | ||
135 | " sw $2, -128(%1) \n" | ||
136 | " sw $3, -124(%1) \n" | ||
137 | " sw $6, -120(%1) \n" | ||
138 | " sw $7, -116(%1) \n" | ||
139 | " sw $8, -112(%1) \n" | ||
140 | " sw $9, -108(%1) \n" | ||
141 | " sw $10, -104(%1) \n" | ||
142 | " sw $11, -100(%1) \n" | ||
143 | # endif | ||
144 | " daddiu %0, %0, 32 \n" | ||
145 | " daddiu %1, %1, 32 \n" | ||
146 | " bnel %0, %2, 1b \n" | ||
147 | " pref " SB1_PREF_LOAD_STREAMED_HINT ", -32(%0)\n" | ||
148 | " daddiu %0, %0, -128 \n" | ||
149 | " daddiu %1, %1, -128 \n" | ||
150 | #endif | ||
151 | #ifdef CONFIG_MIPS64 | ||
152 | " ld $8, 0(%0) \n" /* Block copy a cacheline */ | ||
153 | "1: ld $9, 8(%0) \n" | ||
154 | " ld $10, 16(%0) \n" | ||
155 | " ld $11, 24(%0) \n" | ||
156 | " sd $8, 0(%1) \n" | ||
157 | " sd $9, 8(%1) \n" | ||
158 | " sd $10, 16(%1) \n" | ||
159 | " sd $11, 24(%1) \n" | ||
160 | #else | ||
161 | " lw $2, 0(%0) \n" /* Block copy a cacheline */ | ||
162 | "1: lw $3, 4(%0) \n" | ||
163 | " lw $6, 8(%0) \n" | ||
164 | " lw $7, 12(%0) \n" | ||
165 | " lw $8, 16(%0) \n" | ||
166 | " lw $9, 20(%0) \n" | ||
167 | " lw $10, 24(%0) \n" | ||
168 | " lw $11, 28(%0) \n" | ||
169 | " sw $2, 0(%1) \n" | ||
170 | " sw $3, 4(%1) \n" | ||
171 | " sw $6, 8(%1) \n" | ||
172 | " sw $7, 12(%1) \n" | ||
173 | " sw $8, 16(%1) \n" | ||
174 | " sw $9, 20(%1) \n" | ||
175 | " sw $10, 24(%1) \n" | ||
176 | " sw $11, 28(%1) \n" | ||
177 | #endif | ||
178 | " daddiu %0, %0, 32 \n" | ||
179 | " daddiu %1, %1, 32 \n" | ||
180 | " bnel %0, %2, 1b \n" | ||
181 | #ifdef CONFIG_MIPS64 | ||
182 | " ld $8, 0(%0) \n" | ||
183 | #else | ||
184 | " lw $2, 0(%0) \n" | ||
185 | #endif | ||
186 | " .set pop \n" | ||
187 | : "+r" (src), "+r" (dst) | ||
188 | : "r" (end) | ||
189 | #ifdef CONFIG_MIPS64 | ||
190 | : "$8","$9","$10","$11","memory"); | ||
191 | #else | ||
192 | : "$2","$3","$6","$7","$8","$9","$10","$11","memory"); | ||
193 | #endif | ||
194 | } | ||
195 | |||
196 | |||
197 | #ifdef CONFIG_SIBYTE_DMA_PAGEOPS | ||
198 | |||
199 | /* | ||
200 | * Pad descriptors to cacheline, since each is exclusively owned by a | ||
201 | * particular CPU. | ||
202 | */ | ||
203 | typedef struct dmadscr_s { | ||
204 | u64 dscr_a; | ||
205 | u64 dscr_b; | ||
206 | u64 pad_a; | ||
207 | u64 pad_b; | ||
208 | } dmadscr_t; | ||
209 | |||
210 | static dmadscr_t page_descr[NR_CPUS] __attribute__((aligned(SMP_CACHE_BYTES))); | ||
211 | |||
212 | void sb1_dma_init(void) | ||
213 | { | ||
214 | int cpu = smp_processor_id(); | ||
215 | u64 base_val = CPHYSADDR(&page_descr[cpu]) | V_DM_DSCR_BASE_RINGSZ(1); | ||
216 | |||
217 | bus_writeq(base_val, | ||
218 | (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE))); | ||
219 | bus_writeq(base_val | M_DM_DSCR_BASE_RESET, | ||
220 | (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE))); | ||
221 | bus_writeq(base_val | M_DM_DSCR_BASE_ENABL, | ||
222 | (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE))); | ||
223 | } | ||
224 | |||
225 | void clear_page(void *page) | ||
226 | { | ||
227 | int cpu = smp_processor_id(); | ||
228 | |||
229 | /* if the page is above Kseg0, use old way */ | ||
230 | if ((long)KSEGX(page) != (long)CKSEG0) | ||
231 | return clear_page_cpu(page); | ||
232 | |||
233 | page_descr[cpu].dscr_a = CPHYSADDR(page) | M_DM_DSCRA_ZERO_MEM | M_DM_DSCRA_L2C_DEST | M_DM_DSCRA_INTERRUPT; | ||
234 | page_descr[cpu].dscr_b = V_DM_DSCRB_SRC_LENGTH(PAGE_SIZE); | ||
235 | bus_writeq(1, (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_COUNT))); | ||
236 | |||
237 | /* | ||
238 | * Don't really want to do it this way, but there's no | ||
239 | * reliable way to delay completion detection. | ||
240 | */ | ||
241 | while (!(bus_readq((void *)(IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE_DEBUG)) & | ||
242 | M_DM_DSCR_BASE_INTERRUPT)))) | ||
243 | ; | ||
244 | bus_readq((void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE))); | ||
245 | } | ||
246 | |||
247 | void copy_page(void *to, void *from) | ||
248 | { | ||
249 | unsigned long from_phys = CPHYSADDR(from); | ||
250 | unsigned long to_phys = CPHYSADDR(to); | ||
251 | int cpu = smp_processor_id(); | ||
252 | |||
253 | /* if either page is above Kseg0, use old way */ | ||
254 | if ((long)KSEGX(to) != (long)CKSEG0 | ||
255 | || (long)KSEGX(from) != (long)CKSEG0) | ||
256 | return copy_page_cpu(to, from); | ||
257 | |||
258 | page_descr[cpu].dscr_a = CPHYSADDR(to_phys) | M_DM_DSCRA_L2C_DEST | M_DM_DSCRA_INTERRUPT; | ||
259 | page_descr[cpu].dscr_b = CPHYSADDR(from_phys) | V_DM_DSCRB_SRC_LENGTH(PAGE_SIZE); | ||
260 | bus_writeq(1, (void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_COUNT))); | ||
261 | |||
262 | /* | ||
263 | * Don't really want to do it this way, but there's no | ||
264 | * reliable way to delay completion detection. | ||
265 | */ | ||
266 | while (!(bus_readq((void *)(IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE_DEBUG)) & | ||
267 | M_DM_DSCR_BASE_INTERRUPT)))) | ||
268 | ; | ||
269 | bus_readq((void *)IOADDR(A_DM_REGISTER(cpu, R_DM_DSCR_BASE))); | ||
270 | } | ||
271 | |||
272 | #else /* !CONFIG_SIBYTE_DMA_PAGEOPS */ | ||
273 | |||
274 | void clear_page(void *page) | ||
275 | { | ||
276 | return clear_page_cpu(page); | ||
277 | } | ||
278 | |||
279 | void copy_page(void *to, void *from) | ||
280 | { | ||
281 | return copy_page_cpu(to, from); | ||
282 | } | ||
283 | |||
284 | #endif /* !CONFIG_SIBYTE_DMA_PAGEOPS */ | ||
285 | |||
286 | EXPORT_SYMBOL(clear_page); | ||
287 | EXPORT_SYMBOL(copy_page); | ||
diff --git a/arch/mips/mm/pgtable-32.c b/arch/mips/mm/pgtable-32.c new file mode 100644 index 000000000000..4f07f81e8500 --- /dev/null +++ b/arch/mips/mm/pgtable-32.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2003 by Ralf Baechle | ||
7 | */ | ||
8 | #include <linux/config.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <linux/bootmem.h> | ||
12 | #include <linux/highmem.h> | ||
13 | #include <asm/pgtable.h> | ||
14 | |||
15 | void pgd_init(unsigned long page) | ||
16 | { | ||
17 | unsigned long *p = (unsigned long *) page; | ||
18 | int i; | ||
19 | |||
20 | for (i = 0; i < USER_PTRS_PER_PGD; i+=8) { | ||
21 | p[i + 0] = (unsigned long) invalid_pte_table; | ||
22 | p[i + 1] = (unsigned long) invalid_pte_table; | ||
23 | p[i + 2] = (unsigned long) invalid_pte_table; | ||
24 | p[i + 3] = (unsigned long) invalid_pte_table; | ||
25 | p[i + 4] = (unsigned long) invalid_pte_table; | ||
26 | p[i + 5] = (unsigned long) invalid_pte_table; | ||
27 | p[i + 6] = (unsigned long) invalid_pte_table; | ||
28 | p[i + 7] = (unsigned long) invalid_pte_table; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | #ifdef CONFIG_HIGHMEM | ||
33 | static void __init fixrange_init (unsigned long start, unsigned long end, | ||
34 | pgd_t *pgd_base) | ||
35 | { | ||
36 | pgd_t *pgd; | ||
37 | pmd_t *pmd; | ||
38 | pte_t *pte; | ||
39 | int i, j; | ||
40 | unsigned long vaddr; | ||
41 | |||
42 | vaddr = start; | ||
43 | i = __pgd_offset(vaddr); | ||
44 | j = __pmd_offset(vaddr); | ||
45 | pgd = pgd_base + i; | ||
46 | |||
47 | for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) { | ||
48 | pmd = (pmd_t *)pgd; | ||
49 | for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) { | ||
50 | if (pmd_none(*pmd)) { | ||
51 | pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); | ||
52 | set_pmd(pmd, __pmd((unsigned long)pte)); | ||
53 | if (pte != pte_offset_kernel(pmd, 0)) | ||
54 | BUG(); | ||
55 | } | ||
56 | vaddr += PMD_SIZE; | ||
57 | } | ||
58 | j = 0; | ||
59 | } | ||
60 | } | ||
61 | #endif | ||
62 | |||
63 | void __init pagetable_init(void) | ||
64 | { | ||
65 | #ifdef CONFIG_HIGHMEM | ||
66 | unsigned long vaddr; | ||
67 | pgd_t *pgd, *pgd_base; | ||
68 | pmd_t *pmd; | ||
69 | pte_t *pte; | ||
70 | #endif | ||
71 | |||
72 | /* Initialize the entire pgd. */ | ||
73 | pgd_init((unsigned long)swapper_pg_dir); | ||
74 | pgd_init((unsigned long)swapper_pg_dir | ||
75 | + sizeof(pgd_t) * USER_PTRS_PER_PGD); | ||
76 | |||
77 | #ifdef CONFIG_HIGHMEM | ||
78 | pgd_base = swapper_pg_dir; | ||
79 | |||
80 | /* | ||
81 | * Fixed mappings: | ||
82 | */ | ||
83 | vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; | ||
84 | fixrange_init(vaddr, 0, pgd_base); | ||
85 | |||
86 | /* | ||
87 | * Permanent kmaps: | ||
88 | */ | ||
89 | vaddr = PKMAP_BASE; | ||
90 | fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base); | ||
91 | |||
92 | pgd = swapper_pg_dir + __pgd_offset(vaddr); | ||
93 | pmd = pmd_offset(pgd, vaddr); | ||
94 | pte = pte_offset_kernel(pmd, vaddr); | ||
95 | pkmap_page_table = pte; | ||
96 | #endif | ||
97 | } | ||
diff --git a/arch/mips/mm/pgtable-64.c b/arch/mips/mm/pgtable-64.c new file mode 100644 index 000000000000..44b5e97fff65 --- /dev/null +++ b/arch/mips/mm/pgtable-64.c | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1999, 2000 by Silicon Graphics | ||
7 | * Copyright (C) 2003 by Ralf Baechle | ||
8 | */ | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <asm/pgtable.h> | ||
12 | |||
13 | void pgd_init(unsigned long page) | ||
14 | { | ||
15 | unsigned long *p, *end; | ||
16 | |||
17 | p = (unsigned long *) page; | ||
18 | end = p + PTRS_PER_PGD; | ||
19 | |||
20 | while (p < end) { | ||
21 | p[0] = (unsigned long) invalid_pmd_table; | ||
22 | p[1] = (unsigned long) invalid_pmd_table; | ||
23 | p[2] = (unsigned long) invalid_pmd_table; | ||
24 | p[3] = (unsigned long) invalid_pmd_table; | ||
25 | p[4] = (unsigned long) invalid_pmd_table; | ||
26 | p[5] = (unsigned long) invalid_pmd_table; | ||
27 | p[6] = (unsigned long) invalid_pmd_table; | ||
28 | p[7] = (unsigned long) invalid_pmd_table; | ||
29 | p += 8; | ||
30 | } | ||
31 | } | ||
32 | |||
33 | void pmd_init(unsigned long addr, unsigned long pagetable) | ||
34 | { | ||
35 | unsigned long *p, *end; | ||
36 | |||
37 | p = (unsigned long *) addr; | ||
38 | end = p + PTRS_PER_PMD; | ||
39 | |||
40 | while (p < end) { | ||
41 | p[0] = (unsigned long)pagetable; | ||
42 | p[1] = (unsigned long)pagetable; | ||
43 | p[2] = (unsigned long)pagetable; | ||
44 | p[3] = (unsigned long)pagetable; | ||
45 | p[4] = (unsigned long)pagetable; | ||
46 | p[5] = (unsigned long)pagetable; | ||
47 | p[6] = (unsigned long)pagetable; | ||
48 | p[7] = (unsigned long)pagetable; | ||
49 | p += 8; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | void __init pagetable_init(void) | ||
54 | { | ||
55 | /* Initialize the entire pgd. */ | ||
56 | pgd_init((unsigned long)swapper_pg_dir); | ||
57 | pmd_init((unsigned long)invalid_pmd_table, (unsigned long)invalid_pte_table); | ||
58 | } | ||
diff --git a/arch/mips/mm/pgtable.c b/arch/mips/mm/pgtable.c new file mode 100644 index 000000000000..3b88fdeef329 --- /dev/null +++ b/arch/mips/mm/pgtable.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/mm.h> | ||
4 | #include <linux/swap.h> | ||
5 | |||
6 | void show_mem(void) | ||
7 | { | ||
8 | #ifndef CONFIG_DISCONTIGMEM /* XXX(hch): later.. */ | ||
9 | int pfn, total = 0, reserved = 0; | ||
10 | int shared = 0, cached = 0; | ||
11 | int highmem = 0; | ||
12 | struct page *page; | ||
13 | |||
14 | printk("Mem-info:\n"); | ||
15 | show_free_areas(); | ||
16 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | ||
17 | pfn = max_mapnr; | ||
18 | while (pfn-- > 0) { | ||
19 | page = pfn_to_page(pfn); | ||
20 | total++; | ||
21 | if (PageHighMem(page)) | ||
22 | highmem++; | ||
23 | if (PageReserved(page)) | ||
24 | reserved++; | ||
25 | else if (PageSwapCache(page)) | ||
26 | cached++; | ||
27 | else if (page_count(page)) | ||
28 | shared += page_count(page) - 1; | ||
29 | } | ||
30 | printk("%d pages of RAM\n", total); | ||
31 | printk("%d pages of HIGHMEM\n",highmem); | ||
32 | printk("%d reserved pages\n",reserved); | ||
33 | printk("%d pages shared\n",shared); | ||
34 | printk("%d pages swap cached\n",cached); | ||
35 | #endif | ||
36 | } | ||
diff --git a/arch/mips/mm/sc-ip22.c b/arch/mips/mm/sc-ip22.c new file mode 100644 index 000000000000..d236cf8b7374 --- /dev/null +++ b/arch/mips/mm/sc-ip22.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * sc-ip22.c: Indy cache management functions. | ||
3 | * | ||
4 | * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org), | ||
5 | * derived from r4xx0.c by David S. Miller (dm@engr.sgi.com). | ||
6 | */ | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/mm.h> | ||
11 | |||
12 | #include <asm/bcache.h> | ||
13 | #include <asm/page.h> | ||
14 | #include <asm/pgtable.h> | ||
15 | #include <asm/system.h> | ||
16 | #include <asm/bootinfo.h> | ||
17 | #include <asm/sgi/ip22.h> | ||
18 | #include <asm/sgi/mc.h> | ||
19 | |||
20 | /* Secondary cache size in bytes, if present. */ | ||
21 | static unsigned long scache_size; | ||
22 | |||
23 | #undef DEBUG_CACHE | ||
24 | |||
25 | #define SC_SIZE 0x00080000 | ||
26 | #define SC_LINE 32 | ||
27 | #define CI_MASK (SC_SIZE - SC_LINE) | ||
28 | #define SC_INDEX(n) ((n) & CI_MASK) | ||
29 | |||
30 | static inline void indy_sc_wipe(unsigned long first, unsigned long last) | ||
31 | { | ||
32 | unsigned long tmp; | ||
33 | |||
34 | __asm__ __volatile__( | ||
35 | ".set\tpush\t\t\t# indy_sc_wipe\n\t" | ||
36 | ".set\tnoreorder\n\t" | ||
37 | ".set\tmips3\n\t" | ||
38 | ".set\tnoat\n\t" | ||
39 | "mfc0\t%2, $12\n\t" | ||
40 | "li\t$1, 0x80\t\t\t# Go 64 bit\n\t" | ||
41 | "mtc0\t$1, $12\n\t" | ||
42 | |||
43 | "dli\t$1, 0x9000000080000000\n\t" | ||
44 | "or\t%0, $1\t\t\t# first line to flush\n\t" | ||
45 | "or\t%1, $1\t\t\t# last line to flush\n\t" | ||
46 | ".set\tat\n\t" | ||
47 | |||
48 | "1:\tsw\t$0, 0(%0)\n\t" | ||
49 | "bne\t%0, %1, 1b\n\t" | ||
50 | " daddu\t%0, 32\n\t" | ||
51 | |||
52 | "mtc0\t%2, $12\t\t\t# Back to 32 bit\n\t" | ||
53 | "nop; nop; nop; nop;\n\t" | ||
54 | ".set\tpop" | ||
55 | : "=r" (first), "=r" (last), "=&r" (tmp) | ||
56 | : "0" (first), "1" (last)); | ||
57 | } | ||
58 | |||
59 | static void indy_sc_wback_invalidate(unsigned long addr, unsigned long size) | ||
60 | { | ||
61 | unsigned long first_line, last_line; | ||
62 | unsigned long flags; | ||
63 | |||
64 | #ifdef DEBUG_CACHE | ||
65 | printk("indy_sc_wback_invalidate[%08lx,%08lx]", addr, size); | ||
66 | #endif | ||
67 | |||
68 | /* Catch bad driver code */ | ||
69 | BUG_ON(size == 0); | ||
70 | |||
71 | /* Which lines to flush? */ | ||
72 | first_line = SC_INDEX(addr); | ||
73 | last_line = SC_INDEX(addr + size - 1); | ||
74 | |||
75 | local_irq_save(flags); | ||
76 | if (first_line <= last_line) { | ||
77 | indy_sc_wipe(first_line, last_line); | ||
78 | goto out; | ||
79 | } | ||
80 | |||
81 | indy_sc_wipe(first_line, SC_SIZE - SC_LINE); | ||
82 | indy_sc_wipe(0, last_line); | ||
83 | out: | ||
84 | local_irq_restore(flags); | ||
85 | } | ||
86 | |||
87 | static void indy_sc_enable(void) | ||
88 | { | ||
89 | unsigned long addr, tmp1, tmp2; | ||
90 | |||
91 | /* This is really cool... */ | ||
92 | #ifdef DEBUG_CACHE | ||
93 | printk("Enabling R4600 SCACHE\n"); | ||
94 | #endif | ||
95 | __asm__ __volatile__( | ||
96 | ".set\tpush\n\t" | ||
97 | ".set\tnoreorder\n\t" | ||
98 | ".set\tmips3\n\t" | ||
99 | "mfc0\t%2, $12\n\t" | ||
100 | "nop; nop; nop; nop;\n\t" | ||
101 | "li\t%1, 0x80\n\t" | ||
102 | "mtc0\t%1, $12\n\t" | ||
103 | "nop; nop; nop; nop;\n\t" | ||
104 | "li\t%0, 0x1\n\t" | ||
105 | "dsll\t%0, 31\n\t" | ||
106 | "lui\t%1, 0x9000\n\t" | ||
107 | "dsll32\t%1, 0\n\t" | ||
108 | "or\t%0, %1, %0\n\t" | ||
109 | "sb\t$0, 0(%0)\n\t" | ||
110 | "mtc0\t$0, $12\n\t" | ||
111 | "nop; nop; nop; nop;\n\t" | ||
112 | "mtc0\t%2, $12\n\t" | ||
113 | "nop; nop; nop; nop;\n\t" | ||
114 | ".set\tpop" | ||
115 | : "=r" (tmp1), "=r" (tmp2), "=r" (addr)); | ||
116 | } | ||
117 | |||
118 | static void indy_sc_disable(void) | ||
119 | { | ||
120 | unsigned long tmp1, tmp2, tmp3; | ||
121 | |||
122 | #ifdef DEBUG_CACHE | ||
123 | printk("Disabling R4600 SCACHE\n"); | ||
124 | #endif | ||
125 | __asm__ __volatile__( | ||
126 | ".set\tpush\n\t" | ||
127 | ".set\tnoreorder\n\t" | ||
128 | ".set\tmips3\n\t" | ||
129 | "li\t%0, 0x1\n\t" | ||
130 | "dsll\t%0, 31\n\t" | ||
131 | "lui\t%1, 0x9000\n\t" | ||
132 | "dsll32\t%1, 0\n\t" | ||
133 | "or\t%0, %1, %0\n\t" | ||
134 | "mfc0\t%2, $12\n\t" | ||
135 | "nop; nop; nop; nop\n\t" | ||
136 | "li\t%1, 0x80\n\t" | ||
137 | "mtc0\t%1, $12\n\t" | ||
138 | "nop; nop; nop; nop\n\t" | ||
139 | "sh\t$0, 0(%0)\n\t" | ||
140 | "mtc0\t$0, $12\n\t" | ||
141 | "nop; nop; nop; nop\n\t" | ||
142 | "mtc0\t%2, $12\n\t" | ||
143 | "nop; nop; nop; nop\n\t" | ||
144 | ".set\tpop" | ||
145 | : "=r" (tmp1), "=r" (tmp2), "=r" (tmp3)); | ||
146 | } | ||
147 | |||
148 | static inline int __init indy_sc_probe(void) | ||
149 | { | ||
150 | unsigned int size = ip22_eeprom_read(&sgimc->eeprom, 17); | ||
151 | if (size == 0) | ||
152 | return 0; | ||
153 | |||
154 | size <<= PAGE_SHIFT; | ||
155 | printk(KERN_INFO "R4600/R5000 SCACHE size %dK, linesize 32 bytes.\n", | ||
156 | size >> 10); | ||
157 | scache_size = size; | ||
158 | |||
159 | return 1; | ||
160 | } | ||
161 | |||
162 | /* XXX Check with wje if the Indy caches can differenciate between | ||
163 | writeback + invalidate and just invalidate. */ | ||
164 | struct bcache_ops indy_sc_ops = { | ||
165 | .bc_enable = indy_sc_enable, | ||
166 | .bc_disable = indy_sc_disable, | ||
167 | .bc_wback_inv = indy_sc_wback_invalidate, | ||
168 | .bc_inv = indy_sc_wback_invalidate | ||
169 | }; | ||
170 | |||
171 | void __init indy_sc_init(void) | ||
172 | { | ||
173 | if (indy_sc_probe()) { | ||
174 | indy_sc_enable(); | ||
175 | bcops = &indy_sc_ops; | ||
176 | } | ||
177 | } | ||
diff --git a/arch/mips/mm/sc-r5k.c b/arch/mips/mm/sc-r5k.c new file mode 100644 index 000000000000..d35b6c1103a3 --- /dev/null +++ b/arch/mips/mm/sc-r5k.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org), | ||
3 | * derived from r4xx0.c by David S. Miller (dm@engr.sgi.com). | ||
4 | */ | ||
5 | #include <linux/init.h> | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/sched.h> | ||
8 | #include <linux/mm.h> | ||
9 | |||
10 | #include <asm/mipsregs.h> | ||
11 | #include <asm/bcache.h> | ||
12 | #include <asm/cacheops.h> | ||
13 | #include <asm/page.h> | ||
14 | #include <asm/pgtable.h> | ||
15 | #include <asm/system.h> | ||
16 | #include <asm/mmu_context.h> | ||
17 | #include <asm/r4kcache.h> | ||
18 | |||
19 | /* Secondary cache size in bytes, if present. */ | ||
20 | static unsigned long scache_size; | ||
21 | |||
22 | #define SC_LINE 32 | ||
23 | #define SC_PAGE (128*SC_LINE) | ||
24 | |||
25 | static inline void blast_r5000_scache(void) | ||
26 | { | ||
27 | unsigned long start = INDEX_BASE; | ||
28 | unsigned long end = start + scache_size; | ||
29 | |||
30 | while(start < end) { | ||
31 | cache_op(R5K_Page_Invalidate_S, start); | ||
32 | start += SC_PAGE; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | static void r5k_dma_cache_inv_sc(unsigned long addr, unsigned long size) | ||
37 | { | ||
38 | unsigned long end, a; | ||
39 | |||
40 | /* Catch bad driver code */ | ||
41 | BUG_ON(size == 0); | ||
42 | |||
43 | if (size >= scache_size) { | ||
44 | blast_r5000_scache(); | ||
45 | return; | ||
46 | } | ||
47 | |||
48 | /* On the R5000 secondary cache we cannot | ||
49 | * invalidate less than a page at a time. | ||
50 | * The secondary cache is physically indexed, write-through. | ||
51 | */ | ||
52 | a = addr & ~(SC_PAGE - 1); | ||
53 | end = (addr + size - 1) & ~(SC_PAGE - 1); | ||
54 | while (a <= end) { | ||
55 | cache_op(R5K_Page_Invalidate_S, a); | ||
56 | a += SC_PAGE; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static void r5k_sc_enable(void) | ||
61 | { | ||
62 | unsigned long flags; | ||
63 | |||
64 | local_irq_save(flags); | ||
65 | set_c0_config(R5K_CONF_SE); | ||
66 | blast_r5000_scache(); | ||
67 | local_irq_restore(flags); | ||
68 | } | ||
69 | |||
70 | static void r5k_sc_disable(void) | ||
71 | { | ||
72 | unsigned long flags; | ||
73 | |||
74 | local_irq_save(flags); | ||
75 | blast_r5000_scache(); | ||
76 | clear_c0_config(R5K_CONF_SE); | ||
77 | local_irq_restore(flags); | ||
78 | } | ||
79 | |||
80 | static inline int __init r5k_sc_probe(void) | ||
81 | { | ||
82 | unsigned long config = read_c0_config(); | ||
83 | |||
84 | if (config & CONF_SC) | ||
85 | return(0); | ||
86 | |||
87 | scache_size = (512 * 1024) << ((config & R5K_CONF_SS) >> 20); | ||
88 | |||
89 | printk("R5000 SCACHE size %ldkB, linesize 32 bytes.\n", | ||
90 | scache_size >> 10); | ||
91 | |||
92 | return 1; | ||
93 | } | ||
94 | |||
95 | static struct bcache_ops r5k_sc_ops = { | ||
96 | .bc_enable = r5k_sc_enable, | ||
97 | .bc_disable = r5k_sc_disable, | ||
98 | .bc_wback_inv = r5k_dma_cache_inv_sc, | ||
99 | .bc_inv = r5k_dma_cache_inv_sc | ||
100 | }; | ||
101 | |||
102 | void __init r5k_sc_init(void) | ||
103 | { | ||
104 | if (r5k_sc_probe()) { | ||
105 | r5k_sc_enable(); | ||
106 | bcops = &r5k_sc_ops; | ||
107 | } | ||
108 | } | ||
diff --git a/arch/mips/mm/sc-rm7k.c b/arch/mips/mm/sc-rm7k.c new file mode 100644 index 000000000000..4e92f931aaba --- /dev/null +++ b/arch/mips/mm/sc-rm7k.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * sc-rm7k.c: RM7000 cache management functions. | ||
3 | * | ||
4 | * Copyright (C) 1997, 2001, 2003, 2004 Ralf Baechle (ralf@linux-mips.org) | ||
5 | */ | ||
6 | |||
7 | #undef DEBUG | ||
8 | |||
9 | #include <linux/init.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/mm.h> | ||
12 | |||
13 | #include <asm/addrspace.h> | ||
14 | #include <asm/bcache.h> | ||
15 | #include <asm/cacheops.h> | ||
16 | #include <asm/mipsregs.h> | ||
17 | #include <asm/processor.h> | ||
18 | |||
19 | /* Primary cache parameters. */ | ||
20 | #define sc_lsize 32 | ||
21 | #define tc_pagesize (32*128) | ||
22 | |||
23 | /* Secondary cache parameters. */ | ||
24 | #define scache_size (256*1024) /* Fixed to 256KiB on RM7000 */ | ||
25 | |||
26 | extern unsigned long icache_way_size, dcache_way_size; | ||
27 | |||
28 | #include <asm/r4kcache.h> | ||
29 | |||
30 | int rm7k_tcache_enabled; | ||
31 | |||
32 | /* | ||
33 | * Writeback and invalidate the primary cache dcache before DMA. | ||
34 | * (XXX These need to be fixed ...) | ||
35 | */ | ||
36 | static void rm7k_sc_wback_inv(unsigned long addr, unsigned long size) | ||
37 | { | ||
38 | unsigned long end, a; | ||
39 | |||
40 | pr_debug("rm7k_sc_wback_inv[%08lx,%08lx]", addr, size); | ||
41 | |||
42 | /* Catch bad driver code */ | ||
43 | BUG_ON(size == 0); | ||
44 | |||
45 | a = addr & ~(sc_lsize - 1); | ||
46 | end = (addr + size - 1) & ~(sc_lsize - 1); | ||
47 | while (1) { | ||
48 | flush_scache_line(a); /* Hit_Writeback_Inv_SD */ | ||
49 | if (a == end) | ||
50 | break; | ||
51 | a += sc_lsize; | ||
52 | } | ||
53 | |||
54 | if (!rm7k_tcache_enabled) | ||
55 | return; | ||
56 | |||
57 | a = addr & ~(tc_pagesize - 1); | ||
58 | end = (addr + size - 1) & ~(tc_pagesize - 1); | ||
59 | while(1) { | ||
60 | invalidate_tcache_page(a); /* Page_Invalidate_T */ | ||
61 | if (a == end) | ||
62 | break; | ||
63 | a += tc_pagesize; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | static void rm7k_sc_inv(unsigned long addr, unsigned long size) | ||
68 | { | ||
69 | unsigned long end, a; | ||
70 | |||
71 | pr_debug("rm7k_sc_inv[%08lx,%08lx]", addr, size); | ||
72 | |||
73 | /* Catch bad driver code */ | ||
74 | BUG_ON(size == 0); | ||
75 | |||
76 | a = addr & ~(sc_lsize - 1); | ||
77 | end = (addr + size - 1) & ~(sc_lsize - 1); | ||
78 | while (1) { | ||
79 | invalidate_scache_line(a); /* Hit_Invalidate_SD */ | ||
80 | if (a == end) | ||
81 | break; | ||
82 | a += sc_lsize; | ||
83 | } | ||
84 | |||
85 | if (!rm7k_tcache_enabled) | ||
86 | return; | ||
87 | |||
88 | a = addr & ~(tc_pagesize - 1); | ||
89 | end = (addr + size - 1) & ~(tc_pagesize - 1); | ||
90 | while(1) { | ||
91 | invalidate_tcache_page(a); /* Page_Invalidate_T */ | ||
92 | if (a == end) | ||
93 | break; | ||
94 | a += tc_pagesize; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * This function is executed in the uncached segment CKSEG1. | ||
100 | * It must not touch the stack, because the stack pointer still points | ||
101 | * into CKSEG0. | ||
102 | * | ||
103 | * Three options: | ||
104 | * - Write it in assembly and guarantee that we don't use the stack. | ||
105 | * - Disable caching for CKSEG0 before calling it. | ||
106 | * - Pray that GCC doesn't randomly start using the stack. | ||
107 | * | ||
108 | * This being Linux, we obviously take the least sane of those options - | ||
109 | * following DaveM's lead in c-r4k.c | ||
110 | * | ||
111 | * It seems we get our kicks from relying on unguaranteed behaviour in GCC | ||
112 | */ | ||
113 | static __init void __rm7k_sc_enable(void) | ||
114 | { | ||
115 | int i; | ||
116 | |||
117 | set_c0_config(1 << 3); /* CONF_SE */ | ||
118 | |||
119 | write_c0_taglo(0); | ||
120 | write_c0_taghi(0); | ||
121 | |||
122 | for (i = 0; i < scache_size; i += sc_lsize) { | ||
123 | __asm__ __volatile__ ( | ||
124 | ".set noreorder\n\t" | ||
125 | ".set mips3\n\t" | ||
126 | "cache %1, (%0)\n\t" | ||
127 | ".set mips0\n\t" | ||
128 | ".set reorder" | ||
129 | : | ||
130 | : "r" (KSEG0ADDR(i)), "i" (Index_Store_Tag_SD)); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | static __init void rm7k_sc_enable(void) | ||
135 | { | ||
136 | void (*func)(void) = (void *) KSEG1ADDR(&__rm7k_sc_enable); | ||
137 | |||
138 | if (read_c0_config() & 0x08) /* CONF_SE */ | ||
139 | return; | ||
140 | |||
141 | printk(KERN_INFO "Enabling secondary cache..."); | ||
142 | func(); | ||
143 | } | ||
144 | |||
145 | static void rm7k_sc_disable(void) | ||
146 | { | ||
147 | clear_c0_config(1<<3); /* CONF_SE */ | ||
148 | } | ||
149 | |||
150 | struct bcache_ops rm7k_sc_ops = { | ||
151 | .bc_enable = rm7k_sc_enable, | ||
152 | .bc_disable = rm7k_sc_disable, | ||
153 | .bc_wback_inv = rm7k_sc_wback_inv, | ||
154 | .bc_inv = rm7k_sc_inv | ||
155 | }; | ||
156 | |||
157 | void __init rm7k_sc_init(void) | ||
158 | { | ||
159 | unsigned int config = read_c0_config(); | ||
160 | |||
161 | if ((config >> 31) & 1) /* Bit 31 set -> no S-Cache */ | ||
162 | return; | ||
163 | |||
164 | printk(KERN_INFO "Secondary cache size %dK, linesize %d bytes.\n", | ||
165 | (scache_size >> 10), sc_lsize); | ||
166 | |||
167 | if (!((config >> 3) & 1)) /* CONF_SE */ | ||
168 | rm7k_sc_enable(); | ||
169 | |||
170 | /* | ||
171 | * While we're at it let's deal with the tertiary cache. | ||
172 | */ | ||
173 | if (!((config >> 17) & 1)) { | ||
174 | |||
175 | /* | ||
176 | * We can't enable the L3 cache yet. There may be board-specific | ||
177 | * magic necessary to turn it on, and blindly asking the CPU to | ||
178 | * start using it would may give cache errors. | ||
179 | * | ||
180 | * Also, board-specific knowledge may allow us to use the | ||
181 | * CACHE Flash_Invalidate_T instruction if the tag RAM supports | ||
182 | * it, and may specify the size of the L3 cache so we don't have | ||
183 | * to probe it. | ||
184 | */ | ||
185 | printk(KERN_INFO "Tertiary cache present, %s enabled\n", | ||
186 | config&(1<<12) ? "already" : "not (yet)"); | ||
187 | |||
188 | if ((config >> 12) & 1) | ||
189 | rm7k_tcache_enabled = 1; | ||
190 | } | ||
191 | |||
192 | bcops = &rm7k_sc_ops; | ||
193 | } | ||
diff --git a/arch/mips/mm/tlb-andes.c b/arch/mips/mm/tlb-andes.c new file mode 100644 index 000000000000..167e08e9661a --- /dev/null +++ b/arch/mips/mm/tlb-andes.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1997, 1998, 1999 Ralf Baechle (ralf@gnu.org) | ||
7 | * Copyright (C) 1999 Silicon Graphics, Inc. | ||
8 | * Copyright (C) 2000 Kanoj Sarcar (kanoj@sgi.com) | ||
9 | */ | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <asm/page.h> | ||
15 | #include <asm/pgtable.h> | ||
16 | #include <asm/system.h> | ||
17 | #include <asm/mmu_context.h> | ||
18 | |||
19 | extern void build_tlb_refill_handler(void); | ||
20 | |||
21 | #define NTLB_ENTRIES 64 | ||
22 | #define NTLB_ENTRIES_HALF 32 | ||
23 | |||
24 | void local_flush_tlb_all(void) | ||
25 | { | ||
26 | unsigned long flags; | ||
27 | unsigned long old_ctx; | ||
28 | unsigned long entry; | ||
29 | |||
30 | local_irq_save(flags); | ||
31 | /* Save old context and create impossible VPN2 value */ | ||
32 | old_ctx = read_c0_entryhi() & ASID_MASK; | ||
33 | write_c0_entryhi(CKSEG0); | ||
34 | write_c0_entrylo0(0); | ||
35 | write_c0_entrylo1(0); | ||
36 | |||
37 | entry = read_c0_wired(); | ||
38 | |||
39 | /* Blast 'em all away. */ | ||
40 | while (entry < NTLB_ENTRIES) { | ||
41 | write_c0_index(entry); | ||
42 | tlb_write_indexed(); | ||
43 | entry++; | ||
44 | } | ||
45 | write_c0_entryhi(old_ctx); | ||
46 | local_irq_restore(flags); | ||
47 | } | ||
48 | |||
49 | void local_flush_tlb_mm(struct mm_struct *mm) | ||
50 | { | ||
51 | int cpu = smp_processor_id(); | ||
52 | if (cpu_context(cpu, mm) != 0) { | ||
53 | drop_mmu_context(mm,cpu); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
58 | unsigned long end) | ||
59 | { | ||
60 | struct mm_struct *mm = vma->vm_mm; | ||
61 | int cpu = smp_processor_id(); | ||
62 | |||
63 | if (cpu_context(cpu, mm) != 0) { | ||
64 | unsigned long flags; | ||
65 | int size; | ||
66 | |||
67 | local_irq_save(flags); | ||
68 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
69 | size = (size + 1) >> 1; | ||
70 | if (size <= NTLB_ENTRIES_HALF) { | ||
71 | int oldpid = (read_c0_entryhi() & ASID_MASK); | ||
72 | int newpid = (cpu_context(smp_processor_id(), mm) | ||
73 | & ASID_MASK); | ||
74 | |||
75 | start &= (PAGE_MASK << 1); | ||
76 | end += ((PAGE_SIZE << 1) - 1); | ||
77 | end &= (PAGE_MASK << 1); | ||
78 | while(start < end) { | ||
79 | int idx; | ||
80 | |||
81 | write_c0_entryhi(start | newpid); | ||
82 | start += (PAGE_SIZE << 1); | ||
83 | tlb_probe(); | ||
84 | idx = read_c0_index(); | ||
85 | write_c0_entrylo0(0); | ||
86 | write_c0_entrylo1(0); | ||
87 | write_c0_entryhi(CKSEG0); | ||
88 | if(idx < 0) | ||
89 | continue; | ||
90 | tlb_write_indexed(); | ||
91 | } | ||
92 | write_c0_entryhi(oldpid); | ||
93 | } else { | ||
94 | drop_mmu_context(mm, cpu); | ||
95 | } | ||
96 | local_irq_restore(flags); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
101 | { | ||
102 | unsigned long flags; | ||
103 | int size; | ||
104 | |||
105 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
106 | size = (size + 1) >> 1; | ||
107 | |||
108 | local_irq_save(flags); | ||
109 | if (size <= NTLB_ENTRIES_HALF) { | ||
110 | int pid = read_c0_entryhi(); | ||
111 | |||
112 | start &= (PAGE_MASK << 1); | ||
113 | end += ((PAGE_SIZE << 1) - 1); | ||
114 | end &= (PAGE_MASK << 1); | ||
115 | |||
116 | while (start < end) { | ||
117 | int idx; | ||
118 | |||
119 | write_c0_entryhi(start); | ||
120 | start += (PAGE_SIZE << 1); | ||
121 | tlb_probe(); | ||
122 | idx = read_c0_index(); | ||
123 | write_c0_entrylo0(0); | ||
124 | write_c0_entrylo1(0); | ||
125 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT+1))); | ||
126 | if (idx < 0) | ||
127 | continue; | ||
128 | tlb_write_indexed(); | ||
129 | } | ||
130 | write_c0_entryhi(pid); | ||
131 | } else { | ||
132 | local_flush_tlb_all(); | ||
133 | } | ||
134 | local_irq_restore(flags); | ||
135 | } | ||
136 | |||
137 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
138 | { | ||
139 | if (cpu_context(smp_processor_id(), vma->vm_mm) != 0) { | ||
140 | unsigned long flags; | ||
141 | int oldpid, newpid, idx; | ||
142 | |||
143 | newpid = (cpu_context(smp_processor_id(), vma->vm_mm) & | ||
144 | ASID_MASK); | ||
145 | page &= (PAGE_MASK << 1); | ||
146 | local_irq_save(flags); | ||
147 | oldpid = (read_c0_entryhi() & ASID_MASK); | ||
148 | write_c0_entryhi(page | newpid); | ||
149 | tlb_probe(); | ||
150 | idx = read_c0_index(); | ||
151 | write_c0_entrylo0(0); | ||
152 | write_c0_entrylo1(0); | ||
153 | write_c0_entryhi(CKSEG0); | ||
154 | if (idx < 0) | ||
155 | goto finish; | ||
156 | tlb_write_indexed(); | ||
157 | |||
158 | finish: | ||
159 | write_c0_entryhi(oldpid); | ||
160 | local_irq_restore(flags); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * This one is only used for pages with the global bit set so we don't care | ||
166 | * much about the ASID. | ||
167 | */ | ||
168 | void local_flush_tlb_one(unsigned long page) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | int oldpid, idx; | ||
172 | |||
173 | local_irq_save(flags); | ||
174 | page &= (PAGE_MASK << 1); | ||
175 | oldpid = read_c0_entryhi() & 0xff; | ||
176 | write_c0_entryhi(page); | ||
177 | tlb_probe(); | ||
178 | idx = read_c0_index(); | ||
179 | write_c0_entrylo0(0); | ||
180 | write_c0_entrylo1(0); | ||
181 | if (idx >= 0) { | ||
182 | /* Make sure all entries differ. */ | ||
183 | write_c0_entryhi(CKSEG0+(idx<<(PAGE_SHIFT+1))); | ||
184 | tlb_write_indexed(); | ||
185 | } | ||
186 | write_c0_entryhi(oldpid); | ||
187 | |||
188 | local_irq_restore(flags); | ||
189 | } | ||
190 | |||
191 | /* XXX Simplify this. On the R10000 writing a TLB entry for an virtual | ||
192 | address that already exists will overwrite the old entry and not result | ||
193 | in TLB malfunction or TLB shutdown. */ | ||
194 | void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) | ||
195 | { | ||
196 | unsigned long flags; | ||
197 | pgd_t *pgdp; | ||
198 | pmd_t *pmdp; | ||
199 | pte_t *ptep; | ||
200 | int idx, pid; | ||
201 | |||
202 | /* | ||
203 | * Handle debugger faulting in for debugee. | ||
204 | */ | ||
205 | if (current->active_mm != vma->vm_mm) | ||
206 | return; | ||
207 | |||
208 | pid = read_c0_entryhi() & ASID_MASK; | ||
209 | |||
210 | if ((pid != (cpu_context(smp_processor_id(), vma->vm_mm) & ASID_MASK)) | ||
211 | || (cpu_context(smp_processor_id(), vma->vm_mm) == 0)) { | ||
212 | printk(KERN_WARNING | ||
213 | "%s: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n", | ||
214 | __FUNCTION__, (int) (cpu_context(smp_processor_id(), | ||
215 | vma->vm_mm) & ASID_MASK), pid); | ||
216 | } | ||
217 | |||
218 | local_irq_save(flags); | ||
219 | address &= (PAGE_MASK << 1); | ||
220 | write_c0_entryhi(address | (pid)); | ||
221 | pgdp = pgd_offset(vma->vm_mm, address); | ||
222 | tlb_probe(); | ||
223 | pmdp = pmd_offset(pgdp, address); | ||
224 | idx = read_c0_index(); | ||
225 | ptep = pte_offset_map(pmdp, address); | ||
226 | write_c0_entrylo0(pte_val(*ptep++) >> 6); | ||
227 | write_c0_entrylo1(pte_val(*ptep) >> 6); | ||
228 | write_c0_entryhi(address | pid); | ||
229 | if (idx < 0) { | ||
230 | tlb_write_random(); | ||
231 | } else { | ||
232 | tlb_write_indexed(); | ||
233 | } | ||
234 | write_c0_entryhi(pid); | ||
235 | local_irq_restore(flags); | ||
236 | } | ||
237 | |||
238 | void __init tlb_init(void) | ||
239 | { | ||
240 | /* | ||
241 | * You should never change this register: | ||
242 | * - On R4600 1.7 the tlbp never hits for pages smaller than | ||
243 | * the value in the c0_pagemask register. | ||
244 | * - The entire mm handling assumes the c0_pagemask register to | ||
245 | * be set for 4kb pages. | ||
246 | */ | ||
247 | write_c0_pagemask(PM_4K); | ||
248 | write_c0_wired(0); | ||
249 | write_c0_framemask(0); | ||
250 | |||
251 | /* From this point on the ARC firmware is dead. */ | ||
252 | local_flush_tlb_all(); | ||
253 | |||
254 | /* Did I tell you that ARC SUCKS? */ | ||
255 | |||
256 | build_tlb_refill_handler(); | ||
257 | } | ||
diff --git a/arch/mips/mm/tlb-r3k.c b/arch/mips/mm/tlb-r3k.c new file mode 100644 index 000000000000..7948e9a5e372 --- /dev/null +++ b/arch/mips/mm/tlb-r3k.c | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * r2300.c: R2000 and R3000 specific mmu/cache code. | ||
3 | * | ||
4 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
5 | * | ||
6 | * with a lot of changes to make this thing work for R3000s | ||
7 | * Tx39XX R4k style caches added. HK | ||
8 | * Copyright (C) 1998, 1999, 2000 Harald Koerfgen | ||
9 | * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov | ||
10 | * Copyright (C) 2002 Ralf Baechle | ||
11 | * Copyright (C) 2002 Maciej W. Rozycki | ||
12 | */ | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/mm.h> | ||
17 | |||
18 | #include <asm/page.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/mmu_context.h> | ||
21 | #include <asm/system.h> | ||
22 | #include <asm/isadep.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/bootinfo.h> | ||
25 | #include <asm/cpu.h> | ||
26 | |||
27 | #undef DEBUG_TLB | ||
28 | |||
29 | extern void build_tlb_refill_handler(void); | ||
30 | |||
31 | /* CP0 hazard avoidance. */ | ||
32 | #define BARRIER \ | ||
33 | __asm__ __volatile__( \ | ||
34 | ".set push\n\t" \ | ||
35 | ".set noreorder\n\t" \ | ||
36 | "nop\n\t" \ | ||
37 | ".set pop\n\t") | ||
38 | |||
39 | int r3k_have_wired_reg; /* should be in cpu_data? */ | ||
40 | |||
41 | /* TLB operations. */ | ||
42 | void local_flush_tlb_all(void) | ||
43 | { | ||
44 | unsigned long flags; | ||
45 | unsigned long old_ctx; | ||
46 | int entry; | ||
47 | |||
48 | #ifdef DEBUG_TLB | ||
49 | printk("[tlball]"); | ||
50 | #endif | ||
51 | |||
52 | local_irq_save(flags); | ||
53 | old_ctx = read_c0_entryhi() & ASID_MASK; | ||
54 | write_c0_entrylo0(0); | ||
55 | entry = r3k_have_wired_reg ? read_c0_wired() : 8; | ||
56 | for (; entry < current_cpu_data.tlbsize; entry++) { | ||
57 | write_c0_index(entry << 8); | ||
58 | write_c0_entryhi((entry | 0x80000) << 12); | ||
59 | BARRIER; | ||
60 | tlb_write_indexed(); | ||
61 | } | ||
62 | write_c0_entryhi(old_ctx); | ||
63 | local_irq_restore(flags); | ||
64 | } | ||
65 | |||
66 | void local_flush_tlb_mm(struct mm_struct *mm) | ||
67 | { | ||
68 | int cpu = smp_processor_id(); | ||
69 | |||
70 | if (cpu_context(cpu, mm) != 0) { | ||
71 | #ifdef DEBUG_TLB | ||
72 | printk("[tlbmm<%lu>]", (unsigned long)cpu_context(cpu, mm)); | ||
73 | #endif | ||
74 | drop_mmu_context(mm, cpu); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
79 | unsigned long end) | ||
80 | { | ||
81 | struct mm_struct *mm = vma->vm_mm; | ||
82 | int cpu = smp_processor_id(); | ||
83 | |||
84 | if (cpu_context(cpu, mm) != 0) { | ||
85 | unsigned long flags; | ||
86 | int size; | ||
87 | |||
88 | #ifdef DEBUG_TLB | ||
89 | printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", | ||
90 | cpu_context(cpu, mm) & ASID_MASK, start, end); | ||
91 | #endif | ||
92 | local_irq_save(flags); | ||
93 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
94 | if (size <= current_cpu_data.tlbsize) { | ||
95 | int oldpid = read_c0_entryhi() & ASID_MASK; | ||
96 | int newpid = cpu_context(cpu, mm) & ASID_MASK; | ||
97 | |||
98 | start &= PAGE_MASK; | ||
99 | end += PAGE_SIZE - 1; | ||
100 | end &= PAGE_MASK; | ||
101 | while (start < end) { | ||
102 | int idx; | ||
103 | |||
104 | write_c0_entryhi(start | newpid); | ||
105 | start += PAGE_SIZE; /* BARRIER */ | ||
106 | tlb_probe(); | ||
107 | idx = read_c0_index(); | ||
108 | write_c0_entrylo0(0); | ||
109 | write_c0_entryhi(KSEG0); | ||
110 | if (idx < 0) /* BARRIER */ | ||
111 | continue; | ||
112 | tlb_write_indexed(); | ||
113 | } | ||
114 | write_c0_entryhi(oldpid); | ||
115 | } else { | ||
116 | drop_mmu_context(mm, cpu); | ||
117 | } | ||
118 | local_irq_restore(flags); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
123 | { | ||
124 | unsigned long flags; | ||
125 | int size; | ||
126 | |||
127 | #ifdef DEBUG_TLB | ||
128 | printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end); | ||
129 | #endif | ||
130 | local_irq_save(flags); | ||
131 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
132 | if (size <= current_cpu_data.tlbsize) { | ||
133 | int pid = read_c0_entryhi(); | ||
134 | |||
135 | start &= PAGE_MASK; | ||
136 | end += PAGE_SIZE - 1; | ||
137 | end &= PAGE_MASK; | ||
138 | |||
139 | while (start < end) { | ||
140 | int idx; | ||
141 | |||
142 | write_c0_entryhi(start); | ||
143 | start += PAGE_SIZE; /* BARRIER */ | ||
144 | tlb_probe(); | ||
145 | idx = read_c0_index(); | ||
146 | write_c0_entrylo0(0); | ||
147 | write_c0_entryhi(KSEG0); | ||
148 | if (idx < 0) /* BARRIER */ | ||
149 | continue; | ||
150 | tlb_write_indexed(); | ||
151 | } | ||
152 | write_c0_entryhi(pid); | ||
153 | } else { | ||
154 | local_flush_tlb_all(); | ||
155 | } | ||
156 | local_irq_restore(flags); | ||
157 | } | ||
158 | |||
159 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
160 | { | ||
161 | int cpu = smp_processor_id(); | ||
162 | |||
163 | if (!vma || cpu_context(cpu, vma->vm_mm) != 0) { | ||
164 | unsigned long flags; | ||
165 | int oldpid, newpid, idx; | ||
166 | |||
167 | #ifdef DEBUG_TLB | ||
168 | printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page); | ||
169 | #endif | ||
170 | newpid = cpu_context(cpu, vma->vm_mm) & ASID_MASK; | ||
171 | page &= PAGE_MASK; | ||
172 | local_irq_save(flags); | ||
173 | oldpid = read_c0_entryhi() & ASID_MASK; | ||
174 | write_c0_entryhi(page | newpid); | ||
175 | BARRIER; | ||
176 | tlb_probe(); | ||
177 | idx = read_c0_index(); | ||
178 | write_c0_entrylo0(0); | ||
179 | write_c0_entryhi(KSEG0); | ||
180 | if (idx < 0) /* BARRIER */ | ||
181 | goto finish; | ||
182 | tlb_write_indexed(); | ||
183 | |||
184 | finish: | ||
185 | write_c0_entryhi(oldpid); | ||
186 | local_irq_restore(flags); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) | ||
191 | { | ||
192 | unsigned long flags; | ||
193 | int idx, pid; | ||
194 | |||
195 | /* | ||
196 | * Handle debugger faulting in for debugee. | ||
197 | */ | ||
198 | if (current->active_mm != vma->vm_mm) | ||
199 | return; | ||
200 | |||
201 | pid = read_c0_entryhi() & ASID_MASK; | ||
202 | |||
203 | #ifdef DEBUG_TLB | ||
204 | if ((pid != (cpu_context(cpu, vma->vm_mm) & ASID_MASK)) || (cpu_context(cpu, vma->vm_mm) == 0)) { | ||
205 | printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n", | ||
206 | (cpu_context(cpu, vma->vm_mm)), pid); | ||
207 | } | ||
208 | #endif | ||
209 | |||
210 | local_irq_save(flags); | ||
211 | address &= PAGE_MASK; | ||
212 | write_c0_entryhi(address | pid); | ||
213 | BARRIER; | ||
214 | tlb_probe(); | ||
215 | idx = read_c0_index(); | ||
216 | write_c0_entrylo0(pte_val(pte)); | ||
217 | write_c0_entryhi(address | pid); | ||
218 | if (idx < 0) { /* BARRIER */ | ||
219 | tlb_write_random(); | ||
220 | } else { | ||
221 | tlb_write_indexed(); | ||
222 | } | ||
223 | write_c0_entryhi(pid); | ||
224 | local_irq_restore(flags); | ||
225 | } | ||
226 | |||
227 | void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, | ||
228 | unsigned long entryhi, unsigned long pagemask) | ||
229 | { | ||
230 | unsigned long flags; | ||
231 | unsigned long old_ctx; | ||
232 | static unsigned long wired = 0; | ||
233 | |||
234 | if (r3k_have_wired_reg) { /* TX39XX */ | ||
235 | unsigned long old_pagemask; | ||
236 | unsigned long w; | ||
237 | |||
238 | #ifdef DEBUG_TLB | ||
239 | printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n", | ||
240 | entrylo0, entryhi, pagemask); | ||
241 | #endif | ||
242 | |||
243 | local_irq_save(flags); | ||
244 | /* Save old context and create impossible VPN2 value */ | ||
245 | old_ctx = read_c0_entryhi() & ASID_MASK; | ||
246 | old_pagemask = read_c0_pagemask(); | ||
247 | w = read_c0_wired(); | ||
248 | write_c0_wired(w + 1); | ||
249 | if (read_c0_wired() != w + 1) { | ||
250 | printk("[tlbwired] No WIRED reg?\n"); | ||
251 | return; | ||
252 | } | ||
253 | write_c0_index(w << 8); | ||
254 | write_c0_pagemask(pagemask); | ||
255 | write_c0_entryhi(entryhi); | ||
256 | write_c0_entrylo0(entrylo0); | ||
257 | BARRIER; | ||
258 | tlb_write_indexed(); | ||
259 | |||
260 | write_c0_entryhi(old_ctx); | ||
261 | write_c0_pagemask(old_pagemask); | ||
262 | local_flush_tlb_all(); | ||
263 | local_irq_restore(flags); | ||
264 | |||
265 | } else if (wired < 8) { | ||
266 | #ifdef DEBUG_TLB | ||
267 | printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n", | ||
268 | entrylo0, entryhi); | ||
269 | #endif | ||
270 | |||
271 | local_irq_save(flags); | ||
272 | old_ctx = read_c0_entryhi() & ASID_MASK; | ||
273 | write_c0_entrylo0(entrylo0); | ||
274 | write_c0_entryhi(entryhi); | ||
275 | write_c0_index(wired); | ||
276 | wired++; /* BARRIER */ | ||
277 | tlb_write_indexed(); | ||
278 | write_c0_entryhi(old_ctx); | ||
279 | local_flush_tlb_all(); | ||
280 | local_irq_restore(flags); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | void __init tlb_init(void) | ||
285 | { | ||
286 | local_flush_tlb_all(); | ||
287 | |||
288 | build_tlb_refill_handler(); | ||
289 | } | ||
diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c new file mode 100644 index 000000000000..59d38bc05b69 --- /dev/null +++ b/arch/mips/mm/tlb-r4k.c | |||
@@ -0,0 +1,419 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
7 | * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org | ||
8 | * Carsten Langgaard, carstenl@mips.com | ||
9 | * Copyright (C) 2002 MIPS Technologies, Inc. All rights reserved. | ||
10 | */ | ||
11 | #include <linux/config.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/mm.h> | ||
15 | |||
16 | #include <asm/cpu.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/mmu_context.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/system.h> | ||
21 | |||
22 | extern void build_tlb_refill_handler(void); | ||
23 | |||
24 | /* CP0 hazard avoidance. */ | ||
25 | #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ | ||
26 | "nop; nop; nop; nop; nop; nop;\n\t" \ | ||
27 | ".set reorder\n\t") | ||
28 | |||
29 | void local_flush_tlb_all(void) | ||
30 | { | ||
31 | unsigned long flags; | ||
32 | unsigned long old_ctx; | ||
33 | int entry; | ||
34 | |||
35 | local_irq_save(flags); | ||
36 | /* Save old context and create impossible VPN2 value */ | ||
37 | old_ctx = read_c0_entryhi(); | ||
38 | write_c0_entrylo0(0); | ||
39 | write_c0_entrylo1(0); | ||
40 | |||
41 | entry = read_c0_wired(); | ||
42 | |||
43 | /* Blast 'em all away. */ | ||
44 | while (entry < current_cpu_data.tlbsize) { | ||
45 | /* | ||
46 | * Make sure all entries differ. If they're not different | ||
47 | * MIPS32 will take revenge ... | ||
48 | */ | ||
49 | write_c0_entryhi(CKSEG0 + (entry << (PAGE_SHIFT + 1))); | ||
50 | write_c0_index(entry); | ||
51 | mtc0_tlbw_hazard(); | ||
52 | tlb_write_indexed(); | ||
53 | entry++; | ||
54 | } | ||
55 | tlbw_use_hazard(); | ||
56 | write_c0_entryhi(old_ctx); | ||
57 | local_irq_restore(flags); | ||
58 | } | ||
59 | |||
60 | void local_flush_tlb_mm(struct mm_struct *mm) | ||
61 | { | ||
62 | int cpu = smp_processor_id(); | ||
63 | |||
64 | if (cpu_context(cpu, mm) != 0) | ||
65 | drop_mmu_context(mm,cpu); | ||
66 | } | ||
67 | |||
68 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
69 | unsigned long end) | ||
70 | { | ||
71 | struct mm_struct *mm = vma->vm_mm; | ||
72 | int cpu = smp_processor_id(); | ||
73 | |||
74 | if (cpu_context(cpu, mm) != 0) { | ||
75 | unsigned long flags; | ||
76 | int size; | ||
77 | |||
78 | local_irq_save(flags); | ||
79 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
80 | size = (size + 1) >> 1; | ||
81 | if (size <= current_cpu_data.tlbsize/2) { | ||
82 | int oldpid = read_c0_entryhi(); | ||
83 | int newpid = cpu_asid(cpu, mm); | ||
84 | |||
85 | start &= (PAGE_MASK << 1); | ||
86 | end += ((PAGE_SIZE << 1) - 1); | ||
87 | end &= (PAGE_MASK << 1); | ||
88 | while (start < end) { | ||
89 | int idx; | ||
90 | |||
91 | write_c0_entryhi(start | newpid); | ||
92 | start += (PAGE_SIZE << 1); | ||
93 | mtc0_tlbw_hazard(); | ||
94 | tlb_probe(); | ||
95 | BARRIER; | ||
96 | idx = read_c0_index(); | ||
97 | write_c0_entrylo0(0); | ||
98 | write_c0_entrylo1(0); | ||
99 | if (idx < 0) | ||
100 | continue; | ||
101 | /* Make sure all entries differ. */ | ||
102 | write_c0_entryhi(CKSEG0 + | ||
103 | (idx << (PAGE_SHIFT + 1))); | ||
104 | mtc0_tlbw_hazard(); | ||
105 | tlb_write_indexed(); | ||
106 | } | ||
107 | tlbw_use_hazard(); | ||
108 | write_c0_entryhi(oldpid); | ||
109 | } else { | ||
110 | drop_mmu_context(mm, cpu); | ||
111 | } | ||
112 | local_irq_restore(flags); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
117 | { | ||
118 | unsigned long flags; | ||
119 | int size; | ||
120 | |||
121 | local_irq_save(flags); | ||
122 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
123 | size = (size + 1) >> 1; | ||
124 | if (size <= current_cpu_data.tlbsize / 2) { | ||
125 | int pid = read_c0_entryhi(); | ||
126 | |||
127 | start &= (PAGE_MASK << 1); | ||
128 | end += ((PAGE_SIZE << 1) - 1); | ||
129 | end &= (PAGE_MASK << 1); | ||
130 | |||
131 | while (start < end) { | ||
132 | int idx; | ||
133 | |||
134 | write_c0_entryhi(start); | ||
135 | start += (PAGE_SIZE << 1); | ||
136 | mtc0_tlbw_hazard(); | ||
137 | tlb_probe(); | ||
138 | BARRIER; | ||
139 | idx = read_c0_index(); | ||
140 | write_c0_entrylo0(0); | ||
141 | write_c0_entrylo1(0); | ||
142 | if (idx < 0) | ||
143 | continue; | ||
144 | /* Make sure all entries differ. */ | ||
145 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); | ||
146 | mtc0_tlbw_hazard(); | ||
147 | tlb_write_indexed(); | ||
148 | } | ||
149 | tlbw_use_hazard(); | ||
150 | write_c0_entryhi(pid); | ||
151 | } else { | ||
152 | local_flush_tlb_all(); | ||
153 | } | ||
154 | local_irq_restore(flags); | ||
155 | } | ||
156 | |||
157 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
158 | { | ||
159 | int cpu = smp_processor_id(); | ||
160 | |||
161 | if (cpu_context(cpu, vma->vm_mm) != 0) { | ||
162 | unsigned long flags; | ||
163 | int oldpid, newpid, idx; | ||
164 | |||
165 | newpid = cpu_asid(cpu, vma->vm_mm); | ||
166 | page &= (PAGE_MASK << 1); | ||
167 | local_irq_save(flags); | ||
168 | oldpid = read_c0_entryhi(); | ||
169 | write_c0_entryhi(page | newpid); | ||
170 | mtc0_tlbw_hazard(); | ||
171 | tlb_probe(); | ||
172 | BARRIER; | ||
173 | idx = read_c0_index(); | ||
174 | write_c0_entrylo0(0); | ||
175 | write_c0_entrylo1(0); | ||
176 | if (idx < 0) | ||
177 | goto finish; | ||
178 | /* Make sure all entries differ. */ | ||
179 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); | ||
180 | mtc0_tlbw_hazard(); | ||
181 | tlb_write_indexed(); | ||
182 | tlbw_use_hazard(); | ||
183 | |||
184 | finish: | ||
185 | write_c0_entryhi(oldpid); | ||
186 | local_irq_restore(flags); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * This one is only used for pages with the global bit set so we don't care | ||
192 | * much about the ASID. | ||
193 | */ | ||
194 | void local_flush_tlb_one(unsigned long page) | ||
195 | { | ||
196 | unsigned long flags; | ||
197 | int oldpid, idx; | ||
198 | |||
199 | local_irq_save(flags); | ||
200 | page &= (PAGE_MASK << 1); | ||
201 | oldpid = read_c0_entryhi(); | ||
202 | write_c0_entryhi(page); | ||
203 | mtc0_tlbw_hazard(); | ||
204 | tlb_probe(); | ||
205 | BARRIER; | ||
206 | idx = read_c0_index(); | ||
207 | write_c0_entrylo0(0); | ||
208 | write_c0_entrylo1(0); | ||
209 | if (idx >= 0) { | ||
210 | /* Make sure all entries differ. */ | ||
211 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); | ||
212 | mtc0_tlbw_hazard(); | ||
213 | tlb_write_indexed(); | ||
214 | tlbw_use_hazard(); | ||
215 | } | ||
216 | write_c0_entryhi(oldpid); | ||
217 | |||
218 | local_irq_restore(flags); | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * We will need multiple versions of update_mmu_cache(), one that just | ||
223 | * updates the TLB with the new pte(s), and another which also checks | ||
224 | * for the R4k "end of page" hardware bug and does the needy. | ||
225 | */ | ||
226 | void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) | ||
227 | { | ||
228 | unsigned long flags; | ||
229 | pgd_t *pgdp; | ||
230 | pmd_t *pmdp; | ||
231 | pte_t *ptep; | ||
232 | int idx, pid; | ||
233 | |||
234 | /* | ||
235 | * Handle debugger faulting in for debugee. | ||
236 | */ | ||
237 | if (current->active_mm != vma->vm_mm) | ||
238 | return; | ||
239 | |||
240 | pid = read_c0_entryhi() & ASID_MASK; | ||
241 | |||
242 | local_irq_save(flags); | ||
243 | address &= (PAGE_MASK << 1); | ||
244 | write_c0_entryhi(address | pid); | ||
245 | pgdp = pgd_offset(vma->vm_mm, address); | ||
246 | mtc0_tlbw_hazard(); | ||
247 | tlb_probe(); | ||
248 | BARRIER; | ||
249 | pmdp = pmd_offset(pgdp, address); | ||
250 | idx = read_c0_index(); | ||
251 | ptep = pte_offset_map(pmdp, address); | ||
252 | |||
253 | #if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32) | ||
254 | write_c0_entrylo0(ptep->pte_high); | ||
255 | ptep++; | ||
256 | write_c0_entrylo1(ptep->pte_high); | ||
257 | #else | ||
258 | write_c0_entrylo0(pte_val(*ptep++) >> 6); | ||
259 | write_c0_entrylo1(pte_val(*ptep) >> 6); | ||
260 | #endif | ||
261 | write_c0_entryhi(address | pid); | ||
262 | mtc0_tlbw_hazard(); | ||
263 | if (idx < 0) | ||
264 | tlb_write_random(); | ||
265 | else | ||
266 | tlb_write_indexed(); | ||
267 | tlbw_use_hazard(); | ||
268 | write_c0_entryhi(pid); | ||
269 | local_irq_restore(flags); | ||
270 | } | ||
271 | |||
272 | #if 0 | ||
273 | static void r4k_update_mmu_cache_hwbug(struct vm_area_struct * vma, | ||
274 | unsigned long address, pte_t pte) | ||
275 | { | ||
276 | unsigned long flags; | ||
277 | unsigned int asid; | ||
278 | pgd_t *pgdp; | ||
279 | pmd_t *pmdp; | ||
280 | pte_t *ptep; | ||
281 | int idx; | ||
282 | |||
283 | local_irq_save(flags); | ||
284 | address &= (PAGE_MASK << 1); | ||
285 | asid = read_c0_entryhi() & ASID_MASK; | ||
286 | write_c0_entryhi(address | asid); | ||
287 | pgdp = pgd_offset(vma->vm_mm, address); | ||
288 | mtc0_tlbw_hazard(); | ||
289 | tlb_probe(); | ||
290 | BARRIER; | ||
291 | pmdp = pmd_offset(pgdp, address); | ||
292 | idx = read_c0_index(); | ||
293 | ptep = pte_offset_map(pmdp, address); | ||
294 | write_c0_entrylo0(pte_val(*ptep++) >> 6); | ||
295 | write_c0_entrylo1(pte_val(*ptep) >> 6); | ||
296 | mtc0_tlbw_hazard(); | ||
297 | if (idx < 0) | ||
298 | tlb_write_random(); | ||
299 | else | ||
300 | tlb_write_indexed(); | ||
301 | tlbw_use_hazard(); | ||
302 | local_irq_restore(flags); | ||
303 | } | ||
304 | #endif | ||
305 | |||
306 | void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, | ||
307 | unsigned long entryhi, unsigned long pagemask) | ||
308 | { | ||
309 | unsigned long flags; | ||
310 | unsigned long wired; | ||
311 | unsigned long old_pagemask; | ||
312 | unsigned long old_ctx; | ||
313 | |||
314 | local_irq_save(flags); | ||
315 | /* Save old context and create impossible VPN2 value */ | ||
316 | old_ctx = read_c0_entryhi(); | ||
317 | old_pagemask = read_c0_pagemask(); | ||
318 | wired = read_c0_wired(); | ||
319 | write_c0_wired(wired + 1); | ||
320 | write_c0_index(wired); | ||
321 | BARRIER; | ||
322 | write_c0_pagemask(pagemask); | ||
323 | write_c0_entryhi(entryhi); | ||
324 | write_c0_entrylo0(entrylo0); | ||
325 | write_c0_entrylo1(entrylo1); | ||
326 | mtc0_tlbw_hazard(); | ||
327 | tlb_write_indexed(); | ||
328 | tlbw_use_hazard(); | ||
329 | |||
330 | write_c0_entryhi(old_ctx); | ||
331 | BARRIER; | ||
332 | write_c0_pagemask(old_pagemask); | ||
333 | local_flush_tlb_all(); | ||
334 | local_irq_restore(flags); | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * Used for loading TLB entries before trap_init() has started, when we | ||
339 | * don't actually want to add a wired entry which remains throughout the | ||
340 | * lifetime of the system | ||
341 | */ | ||
342 | |||
343 | static int temp_tlb_entry __initdata; | ||
344 | |||
345 | __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, | ||
346 | unsigned long entryhi, unsigned long pagemask) | ||
347 | { | ||
348 | int ret = 0; | ||
349 | unsigned long flags; | ||
350 | unsigned long wired; | ||
351 | unsigned long old_pagemask; | ||
352 | unsigned long old_ctx; | ||
353 | |||
354 | local_irq_save(flags); | ||
355 | /* Save old context and create impossible VPN2 value */ | ||
356 | old_ctx = read_c0_entryhi(); | ||
357 | old_pagemask = read_c0_pagemask(); | ||
358 | wired = read_c0_wired(); | ||
359 | if (--temp_tlb_entry < wired) { | ||
360 | printk(KERN_WARNING "No TLB space left for add_temporary_entry\n"); | ||
361 | ret = -ENOSPC; | ||
362 | goto out; | ||
363 | } | ||
364 | |||
365 | write_c0_index(temp_tlb_entry); | ||
366 | write_c0_pagemask(pagemask); | ||
367 | write_c0_entryhi(entryhi); | ||
368 | write_c0_entrylo0(entrylo0); | ||
369 | write_c0_entrylo1(entrylo1); | ||
370 | mtc0_tlbw_hazard(); | ||
371 | tlb_write_indexed(); | ||
372 | tlbw_use_hazard(); | ||
373 | |||
374 | write_c0_entryhi(old_ctx); | ||
375 | write_c0_pagemask(old_pagemask); | ||
376 | out: | ||
377 | local_irq_restore(flags); | ||
378 | return ret; | ||
379 | } | ||
380 | |||
381 | static void __init probe_tlb(unsigned long config) | ||
382 | { | ||
383 | struct cpuinfo_mips *c = ¤t_cpu_data; | ||
384 | unsigned int reg; | ||
385 | |||
386 | /* | ||
387 | * If this isn't a MIPS32 / MIPS64 compliant CPU. Config 1 register | ||
388 | * is not supported, we assume R4k style. Cpu probing already figured | ||
389 | * out the number of tlb entries. | ||
390 | */ | ||
391 | if ((c->processor_id & 0xff0000) == PRID_COMP_LEGACY) | ||
392 | return; | ||
393 | |||
394 | reg = read_c0_config1(); | ||
395 | if (!((config >> 7) & 3)) | ||
396 | panic("No TLB present"); | ||
397 | |||
398 | c->tlbsize = ((reg >> 25) & 0x3f) + 1; | ||
399 | } | ||
400 | |||
401 | void __init tlb_init(void) | ||
402 | { | ||
403 | unsigned int config = read_c0_config(); | ||
404 | |||
405 | /* | ||
406 | * You should never change this register: | ||
407 | * - On R4600 1.7 the tlbp never hits for pages smaller than | ||
408 | * the value in the c0_pagemask register. | ||
409 | * - The entire mm handling assumes the c0_pagemask register to | ||
410 | * be set for 4kb pages. | ||
411 | */ | ||
412 | probe_tlb(config); | ||
413 | write_c0_pagemask(PM_DEFAULT_MASK); | ||
414 | write_c0_wired(0); | ||
415 | temp_tlb_entry = current_cpu_data.tlbsize - 1; | ||
416 | local_flush_tlb_all(); | ||
417 | |||
418 | build_tlb_refill_handler(); | ||
419 | } | ||
diff --git a/arch/mips/mm/tlb-r8k.c b/arch/mips/mm/tlb-r8k.c new file mode 100644 index 000000000000..1bfb09198ce3 --- /dev/null +++ b/arch/mips/mm/tlb-r8k.c | |||
@@ -0,0 +1,250 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
7 | * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org | ||
8 | * Carsten Langgaard, carstenl@mips.com | ||
9 | * Copyright (C) 2002 MIPS Technologies, Inc. All rights reserved. | ||
10 | */ | ||
11 | #include <linux/config.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/mm.h> | ||
15 | |||
16 | #include <asm/cpu.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/mmu_context.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/system.h> | ||
21 | |||
22 | extern void build_tlb_refill_handler(void); | ||
23 | |||
24 | #define TFP_TLB_SIZE 384 | ||
25 | #define TFP_TLB_SET_SHIFT 7 | ||
26 | |||
27 | /* CP0 hazard avoidance. */ | ||
28 | #define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ | ||
29 | "nop; nop; nop; nop; nop; nop;\n\t" \ | ||
30 | ".set reorder\n\t") | ||
31 | |||
32 | void local_flush_tlb_all(void) | ||
33 | { | ||
34 | unsigned long flags; | ||
35 | unsigned long old_ctx; | ||
36 | int entry; | ||
37 | |||
38 | local_irq_save(flags); | ||
39 | /* Save old context and create impossible VPN2 value */ | ||
40 | old_ctx = read_c0_entryhi(); | ||
41 | write_c0_entrylo(0); | ||
42 | |||
43 | for (entry = 0; entry < TFP_TLB_SIZE; entry++) { | ||
44 | write_c0_tlbset(entry >> TFP_TLB_SET_SHIFT); | ||
45 | write_c0_vaddr(entry << PAGE_SHIFT); | ||
46 | write_c0_entryhi(CKSEG0 + (entry << (PAGE_SHIFT + 1))); | ||
47 | mtc0_tlbw_hazard(); | ||
48 | tlb_write(); | ||
49 | } | ||
50 | tlbw_use_hazard(); | ||
51 | write_c0_entryhi(old_ctx); | ||
52 | local_irq_restore(flags); | ||
53 | } | ||
54 | |||
55 | void local_flush_tlb_mm(struct mm_struct *mm) | ||
56 | { | ||
57 | int cpu = smp_processor_id(); | ||
58 | |||
59 | if (cpu_context(cpu, mm) != 0) | ||
60 | drop_mmu_context(mm,cpu); | ||
61 | } | ||
62 | |||
63 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
64 | unsigned long end) | ||
65 | { | ||
66 | struct mm_struct *mm = vma->vm_mm; | ||
67 | int cpu = smp_processor_id(); | ||
68 | unsigned long flags; | ||
69 | int oldpid, newpid, size; | ||
70 | |||
71 | if (!cpu_context(cpu, mm)) | ||
72 | return; | ||
73 | |||
74 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
75 | size = (size + 1) >> 1; | ||
76 | |||
77 | local_irq_save(flags); | ||
78 | |||
79 | if (size > TFP_TLB_SIZE / 2) { | ||
80 | drop_mmu_context(mm, cpu); | ||
81 | goto out_restore; | ||
82 | } | ||
83 | |||
84 | oldpid = read_c0_entryhi(); | ||
85 | newpid = cpu_asid(cpu, mm); | ||
86 | |||
87 | write_c0_entrylo(0); | ||
88 | |||
89 | start &= PAGE_MASK; | ||
90 | end += (PAGE_SIZE - 1); | ||
91 | end &= PAGE_MASK; | ||
92 | while (start < end) { | ||
93 | signed long idx; | ||
94 | |||
95 | write_c0_vaddr(start); | ||
96 | write_c0_entryhi(start); | ||
97 | start += PAGE_SIZE; | ||
98 | tlb_probe(); | ||
99 | idx = read_c0_tlbset(); | ||
100 | if (idx < 0) | ||
101 | continue; | ||
102 | |||
103 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); | ||
104 | tlb_write(); | ||
105 | } | ||
106 | write_c0_entryhi(oldpid); | ||
107 | |||
108 | out_restore: | ||
109 | local_irq_restore(flags); | ||
110 | } | ||
111 | |||
112 | /* Usable for KV1 addresses only! */ | ||
113 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | int size; | ||
117 | |||
118 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
119 | size = (size + 1) >> 1; | ||
120 | |||
121 | if (size > TFP_TLB_SIZE / 2) { | ||
122 | local_flush_tlb_all(); | ||
123 | return; | ||
124 | } | ||
125 | |||
126 | local_irq_save(flags); | ||
127 | |||
128 | write_c0_entrylo(0); | ||
129 | |||
130 | start &= PAGE_MASK; | ||
131 | end += (PAGE_SIZE - 1); | ||
132 | end &= PAGE_MASK; | ||
133 | while (start < end) { | ||
134 | signed long idx; | ||
135 | |||
136 | write_c0_vaddr(start); | ||
137 | write_c0_entryhi(start); | ||
138 | start += PAGE_SIZE; | ||
139 | tlb_probe(); | ||
140 | idx = read_c0_tlbset(); | ||
141 | if (idx < 0) | ||
142 | continue; | ||
143 | |||
144 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); | ||
145 | tlb_write(); | ||
146 | } | ||
147 | |||
148 | local_irq_restore(flags); | ||
149 | } | ||
150 | |||
151 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
152 | { | ||
153 | int cpu = smp_processor_id(); | ||
154 | unsigned long flags; | ||
155 | int oldpid, newpid; | ||
156 | signed long idx; | ||
157 | |||
158 | if (!cpu_context(cpu, vma->vm_mm)) | ||
159 | return; | ||
160 | |||
161 | newpid = cpu_asid(cpu, vma->vm_mm); | ||
162 | page &= PAGE_MASK; | ||
163 | local_irq_save(flags); | ||
164 | oldpid = read_c0_entryhi(); | ||
165 | write_c0_vaddr(page); | ||
166 | write_c0_entryhi(newpid); | ||
167 | tlb_probe(); | ||
168 | idx = read_c0_tlbset(); | ||
169 | if (idx < 0) | ||
170 | goto finish; | ||
171 | |||
172 | write_c0_entrylo(0); | ||
173 | write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1))); | ||
174 | tlb_write(); | ||
175 | |||
176 | finish: | ||
177 | write_c0_entryhi(oldpid); | ||
178 | local_irq_restore(flags); | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * We will need multiple versions of update_mmu_cache(), one that just | ||
183 | * updates the TLB with the new pte(s), and another which also checks | ||
184 | * for the R4k "end of page" hardware bug and does the needy. | ||
185 | */ | ||
186 | void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) | ||
187 | { | ||
188 | unsigned long flags; | ||
189 | pgd_t *pgdp; | ||
190 | pmd_t *pmdp; | ||
191 | pte_t *ptep; | ||
192 | int pid; | ||
193 | |||
194 | /* | ||
195 | * Handle debugger faulting in for debugee. | ||
196 | */ | ||
197 | if (current->active_mm != vma->vm_mm) | ||
198 | return; | ||
199 | |||
200 | pid = read_c0_entryhi() & ASID_MASK; | ||
201 | |||
202 | local_irq_save(flags); | ||
203 | address &= PAGE_MASK; | ||
204 | write_c0_vaddr(address); | ||
205 | write_c0_entryhi(pid); | ||
206 | pgdp = pgd_offset(vma->vm_mm, address); | ||
207 | pmdp = pmd_offset(pgdp, address); | ||
208 | ptep = pte_offset_map(pmdp, address); | ||
209 | tlb_probe(); | ||
210 | |||
211 | write_c0_entrylo(pte_val(*ptep++) >> 6); | ||
212 | tlb_write(); | ||
213 | |||
214 | write_c0_entryhi(pid); | ||
215 | local_irq_restore(flags); | ||
216 | } | ||
217 | |||
218 | static void __init probe_tlb(unsigned long config) | ||
219 | { | ||
220 | struct cpuinfo_mips *c = ¤t_cpu_data; | ||
221 | |||
222 | c->tlbsize = 3 * 128; /* 3 sets each 128 entries */ | ||
223 | } | ||
224 | |||
225 | void __init tlb_init(void) | ||
226 | { | ||
227 | unsigned int config = read_c0_config(); | ||
228 | unsigned long status; | ||
229 | |||
230 | probe_tlb(config); | ||
231 | |||
232 | status = read_c0_status(); | ||
233 | status &= ~(ST0_UPS | ST0_KPS); | ||
234 | #ifdef CONFIG_PAGE_SIZE_4KB | ||
235 | status |= (TFP_PAGESIZE_4K << 32) | (TFP_PAGESIZE_4K << 36); | ||
236 | #elif defined(CONFIG_PAGE_SIZE_8KB) | ||
237 | status |= (TFP_PAGESIZE_8K << 32) | (TFP_PAGESIZE_8K << 36); | ||
238 | #elif defined(CONFIG_PAGE_SIZE_16KB) | ||
239 | status |= (TFP_PAGESIZE_16K << 32) | (TFP_PAGESIZE_16K << 36); | ||
240 | #elif defined(CONFIG_PAGE_SIZE_64KB) | ||
241 | status |= (TFP_PAGESIZE_64K << 32) | (TFP_PAGESIZE_64K << 36); | ||
242 | #endif | ||
243 | write_c0_status(status); | ||
244 | |||
245 | write_c0_wired(0); | ||
246 | |||
247 | local_flush_tlb_all(); | ||
248 | |||
249 | build_tlb_refill_handler(); | ||
250 | } | ||
diff --git a/arch/mips/mm/tlb-sb1.c b/arch/mips/mm/tlb-sb1.c new file mode 100644 index 000000000000..6256cafcf3a2 --- /dev/null +++ b/arch/mips/mm/tlb-sb1.c | |||
@@ -0,0 +1,376 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | ||
3 | * Copyright (C) 1997, 2001 Ralf Baechle (ralf@gnu.org) | ||
4 | * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version 2 | ||
9 | * of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | #include <linux/init.h> | ||
21 | #include <asm/mmu_context.h> | ||
22 | #include <asm/bootinfo.h> | ||
23 | #include <asm/cpu.h> | ||
24 | |||
25 | extern void build_tlb_refill_handler(void); | ||
26 | |||
27 | #define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) | ||
28 | |||
29 | /* Dump the current entry* and pagemask registers */ | ||
30 | static inline void dump_cur_tlb_regs(void) | ||
31 | { | ||
32 | unsigned int entryhihi, entryhilo, entrylo0hi, entrylo0lo, entrylo1hi; | ||
33 | unsigned int entrylo1lo, pagemask; | ||
34 | |||
35 | __asm__ __volatile__ ( | ||
36 | ".set push \n" | ||
37 | ".set noreorder \n" | ||
38 | ".set mips64 \n" | ||
39 | ".set noat \n" | ||
40 | " tlbr \n" | ||
41 | " dmfc0 $1, $10 \n" | ||
42 | " dsrl32 %0, $1, 0 \n" | ||
43 | " sll %1, $1, 0 \n" | ||
44 | " dmfc0 $1, $2 \n" | ||
45 | " dsrl32 %2, $1, 0 \n" | ||
46 | " sll %3, $1, 0 \n" | ||
47 | " dmfc0 $1, $3 \n" | ||
48 | " dsrl32 %4, $1, 0 \n" | ||
49 | " sll %5, $1, 0 \n" | ||
50 | " mfc0 %6, $5 \n" | ||
51 | ".set pop \n" | ||
52 | : "=r" (entryhihi), "=r" (entryhilo), | ||
53 | "=r" (entrylo0hi), "=r" (entrylo0lo), | ||
54 | "=r" (entrylo1hi), "=r" (entrylo1lo), | ||
55 | "=r" (pagemask)); | ||
56 | |||
57 | printk("%08X%08X %08X%08X %08X%08X %08X", | ||
58 | entryhihi, entryhilo, | ||
59 | entrylo0hi, entrylo0lo, | ||
60 | entrylo1hi, entrylo1lo, | ||
61 | pagemask); | ||
62 | } | ||
63 | |||
64 | void sb1_dump_tlb(void) | ||
65 | { | ||
66 | unsigned long old_ctx; | ||
67 | unsigned long flags; | ||
68 | int entry; | ||
69 | local_irq_save(flags); | ||
70 | old_ctx = read_c0_entryhi(); | ||
71 | printk("Current TLB registers state:\n" | ||
72 | " EntryHi EntryLo0 EntryLo1 PageMask Index\n" | ||
73 | "--------------------------------------------------------------------\n"); | ||
74 | dump_cur_tlb_regs(); | ||
75 | printk(" %08X\n", read_c0_index()); | ||
76 | printk("\n\nFull TLB Dump:\n" | ||
77 | "Idx EntryHi EntryLo0 EntryLo1 PageMask\n" | ||
78 | "--------------------------------------------------------------\n"); | ||
79 | for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { | ||
80 | write_c0_index(entry); | ||
81 | printk("\n%02i ", entry); | ||
82 | dump_cur_tlb_regs(); | ||
83 | } | ||
84 | printk("\n"); | ||
85 | write_c0_entryhi(old_ctx); | ||
86 | local_irq_restore(flags); | ||
87 | } | ||
88 | |||
89 | void local_flush_tlb_all(void) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | unsigned long old_ctx; | ||
93 | int entry; | ||
94 | |||
95 | local_irq_save(flags); | ||
96 | /* Save old context and create impossible VPN2 value */ | ||
97 | old_ctx = read_c0_entryhi() & ASID_MASK; | ||
98 | write_c0_entrylo0(0); | ||
99 | write_c0_entrylo1(0); | ||
100 | |||
101 | entry = read_c0_wired(); | ||
102 | while (entry < current_cpu_data.tlbsize) { | ||
103 | write_c0_entryhi(UNIQUE_ENTRYHI(entry)); | ||
104 | write_c0_index(entry); | ||
105 | tlb_write_indexed(); | ||
106 | entry++; | ||
107 | } | ||
108 | write_c0_entryhi(old_ctx); | ||
109 | local_irq_restore(flags); | ||
110 | } | ||
111 | |||
112 | |||
113 | /* | ||
114 | * Use a bogus region of memory (starting at 0) to sanitize the TLB's. | ||
115 | * Use increments of the maximum page size (16MB), and check for duplicate | ||
116 | * entries before doing a given write. Then, when we're safe from collisions | ||
117 | * with the firmware, go back and give all the entries invalid addresses with | ||
118 | * the normal flush routine. Wired entries will be killed as well! | ||
119 | */ | ||
120 | static void __init sb1_sanitize_tlb(void) | ||
121 | { | ||
122 | int entry; | ||
123 | long addr = 0; | ||
124 | |||
125 | long inc = 1<<24; /* 16MB */ | ||
126 | /* Save old context and create impossible VPN2 value */ | ||
127 | write_c0_entrylo0(0); | ||
128 | write_c0_entrylo1(0); | ||
129 | for (entry = 0; entry < current_cpu_data.tlbsize; entry++) { | ||
130 | do { | ||
131 | addr += inc; | ||
132 | write_c0_entryhi(addr); | ||
133 | tlb_probe(); | ||
134 | } while ((int)(read_c0_index()) >= 0); | ||
135 | write_c0_index(entry); | ||
136 | tlb_write_indexed(); | ||
137 | } | ||
138 | /* Now that we know we're safe from collisions, we can safely flush | ||
139 | the TLB with the "normal" routine. */ | ||
140 | local_flush_tlb_all(); | ||
141 | } | ||
142 | |||
143 | void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | ||
144 | unsigned long end) | ||
145 | { | ||
146 | struct mm_struct *mm = vma->vm_mm; | ||
147 | unsigned long flags; | ||
148 | int cpu; | ||
149 | |||
150 | local_irq_save(flags); | ||
151 | cpu = smp_processor_id(); | ||
152 | if (cpu_context(cpu, mm) != 0) { | ||
153 | int size; | ||
154 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
155 | size = (size + 1) >> 1; | ||
156 | if (size <= (current_cpu_data.tlbsize/2)) { | ||
157 | int oldpid = read_c0_entryhi() & ASID_MASK; | ||
158 | int newpid = cpu_asid(cpu, mm); | ||
159 | |||
160 | start &= (PAGE_MASK << 1); | ||
161 | end += ((PAGE_SIZE << 1) - 1); | ||
162 | end &= (PAGE_MASK << 1); | ||
163 | while (start < end) { | ||
164 | int idx; | ||
165 | |||
166 | write_c0_entryhi(start | newpid); | ||
167 | start += (PAGE_SIZE << 1); | ||
168 | tlb_probe(); | ||
169 | idx = read_c0_index(); | ||
170 | write_c0_entrylo0(0); | ||
171 | write_c0_entrylo1(0); | ||
172 | write_c0_entryhi(UNIQUE_ENTRYHI(idx)); | ||
173 | if (idx < 0) | ||
174 | continue; | ||
175 | tlb_write_indexed(); | ||
176 | } | ||
177 | write_c0_entryhi(oldpid); | ||
178 | } else { | ||
179 | drop_mmu_context(mm, cpu); | ||
180 | } | ||
181 | } | ||
182 | local_irq_restore(flags); | ||
183 | } | ||
184 | |||
185 | void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) | ||
186 | { | ||
187 | unsigned long flags; | ||
188 | int size; | ||
189 | |||
190 | size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; | ||
191 | size = (size + 1) >> 1; | ||
192 | |||
193 | local_irq_save(flags); | ||
194 | if (size <= (current_cpu_data.tlbsize/2)) { | ||
195 | int pid = read_c0_entryhi(); | ||
196 | |||
197 | start &= (PAGE_MASK << 1); | ||
198 | end += ((PAGE_SIZE << 1) - 1); | ||
199 | end &= (PAGE_MASK << 1); | ||
200 | |||
201 | while (start < end) { | ||
202 | int idx; | ||
203 | |||
204 | write_c0_entryhi(start); | ||
205 | start += (PAGE_SIZE << 1); | ||
206 | tlb_probe(); | ||
207 | idx = read_c0_index(); | ||
208 | write_c0_entrylo0(0); | ||
209 | write_c0_entrylo1(0); | ||
210 | write_c0_entryhi(UNIQUE_ENTRYHI(idx)); | ||
211 | if (idx < 0) | ||
212 | continue; | ||
213 | tlb_write_indexed(); | ||
214 | } | ||
215 | write_c0_entryhi(pid); | ||
216 | } else { | ||
217 | local_flush_tlb_all(); | ||
218 | } | ||
219 | local_irq_restore(flags); | ||
220 | } | ||
221 | |||
222 | void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) | ||
223 | { | ||
224 | unsigned long flags; | ||
225 | int cpu = smp_processor_id(); | ||
226 | |||
227 | local_irq_save(flags); | ||
228 | if (cpu_context(cpu, vma->vm_mm) != 0) { | ||
229 | int oldpid, newpid, idx; | ||
230 | newpid = cpu_asid(cpu, vma->vm_mm); | ||
231 | page &= (PAGE_MASK << 1); | ||
232 | oldpid = read_c0_entryhi() & ASID_MASK; | ||
233 | write_c0_entryhi(page | newpid); | ||
234 | tlb_probe(); | ||
235 | idx = read_c0_index(); | ||
236 | write_c0_entrylo0(0); | ||
237 | write_c0_entrylo1(0); | ||
238 | if (idx < 0) | ||
239 | goto finish; | ||
240 | /* Make sure all entries differ. */ | ||
241 | write_c0_entryhi(UNIQUE_ENTRYHI(idx)); | ||
242 | tlb_write_indexed(); | ||
243 | finish: | ||
244 | write_c0_entryhi(oldpid); | ||
245 | } | ||
246 | local_irq_restore(flags); | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Remove one kernel space TLB entry. This entry is assumed to be marked | ||
251 | * global so we don't do the ASID thing. | ||
252 | */ | ||
253 | void local_flush_tlb_one(unsigned long page) | ||
254 | { | ||
255 | unsigned long flags; | ||
256 | int oldpid, idx; | ||
257 | |||
258 | page &= (PAGE_MASK << 1); | ||
259 | oldpid = read_c0_entryhi() & ASID_MASK; | ||
260 | |||
261 | local_irq_save(flags); | ||
262 | write_c0_entryhi(page); | ||
263 | tlb_probe(); | ||
264 | idx = read_c0_index(); | ||
265 | if (idx >= 0) { | ||
266 | /* Make sure all entries differ. */ | ||
267 | write_c0_entryhi(UNIQUE_ENTRYHI(idx)); | ||
268 | write_c0_entrylo0(0); | ||
269 | write_c0_entrylo1(0); | ||
270 | tlb_write_indexed(); | ||
271 | } | ||
272 | |||
273 | write_c0_entryhi(oldpid); | ||
274 | local_irq_restore(flags); | ||
275 | } | ||
276 | |||
277 | /* All entries common to a mm share an asid. To effectively flush | ||
278 | these entries, we just bump the asid. */ | ||
279 | void local_flush_tlb_mm(struct mm_struct *mm) | ||
280 | { | ||
281 | int cpu; | ||
282 | |||
283 | preempt_disable(); | ||
284 | |||
285 | cpu = smp_processor_id(); | ||
286 | |||
287 | if (cpu_context(cpu, mm) != 0) { | ||
288 | drop_mmu_context(mm, cpu); | ||
289 | } | ||
290 | |||
291 | preempt_enable(); | ||
292 | } | ||
293 | |||
294 | /* Stolen from mips32 routines */ | ||
295 | |||
296 | void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte) | ||
297 | { | ||
298 | unsigned long flags; | ||
299 | pgd_t *pgdp; | ||
300 | pmd_t *pmdp; | ||
301 | pte_t *ptep; | ||
302 | int idx, pid; | ||
303 | |||
304 | /* | ||
305 | * Handle debugger faulting in for debugee. | ||
306 | */ | ||
307 | if (current->active_mm != vma->vm_mm) | ||
308 | return; | ||
309 | |||
310 | local_irq_save(flags); | ||
311 | |||
312 | pid = read_c0_entryhi() & ASID_MASK; | ||
313 | address &= (PAGE_MASK << 1); | ||
314 | write_c0_entryhi(address | (pid)); | ||
315 | pgdp = pgd_offset(vma->vm_mm, address); | ||
316 | tlb_probe(); | ||
317 | pmdp = pmd_offset(pgdp, address); | ||
318 | idx = read_c0_index(); | ||
319 | ptep = pte_offset_map(pmdp, address); | ||
320 | write_c0_entrylo0(pte_val(*ptep++) >> 6); | ||
321 | write_c0_entrylo1(pte_val(*ptep) >> 6); | ||
322 | if (idx < 0) { | ||
323 | tlb_write_random(); | ||
324 | } else { | ||
325 | tlb_write_indexed(); | ||
326 | } | ||
327 | local_irq_restore(flags); | ||
328 | } | ||
329 | |||
330 | void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, | ||
331 | unsigned long entryhi, unsigned long pagemask) | ||
332 | { | ||
333 | unsigned long flags; | ||
334 | unsigned long wired; | ||
335 | unsigned long old_pagemask; | ||
336 | unsigned long old_ctx; | ||
337 | |||
338 | local_irq_save(flags); | ||
339 | old_ctx = read_c0_entryhi() & 0xff; | ||
340 | old_pagemask = read_c0_pagemask(); | ||
341 | wired = read_c0_wired(); | ||
342 | write_c0_wired(wired + 1); | ||
343 | write_c0_index(wired); | ||
344 | |||
345 | write_c0_pagemask(pagemask); | ||
346 | write_c0_entryhi(entryhi); | ||
347 | write_c0_entrylo0(entrylo0); | ||
348 | write_c0_entrylo1(entrylo1); | ||
349 | tlb_write_indexed(); | ||
350 | |||
351 | write_c0_entryhi(old_ctx); | ||
352 | write_c0_pagemask(old_pagemask); | ||
353 | |||
354 | local_flush_tlb_all(); | ||
355 | local_irq_restore(flags); | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * This is called from loadmmu.c. We have to set up all the | ||
360 | * memory management function pointers, as well as initialize | ||
361 | * the caches and tlbs | ||
362 | */ | ||
363 | void tlb_init(void) | ||
364 | { | ||
365 | write_c0_pagemask(PM_DEFAULT_MASK); | ||
366 | write_c0_wired(0); | ||
367 | |||
368 | /* | ||
369 | * We don't know what state the firmware left the TLB's in, so this is | ||
370 | * the ultra-conservative way to flush the TLB's and avoid machine | ||
371 | * check exceptions due to duplicate TLB entries | ||
372 | */ | ||
373 | sb1_sanitize_tlb(); | ||
374 | |||
375 | build_tlb_refill_handler(); | ||
376 | } | ||
diff --git a/arch/mips/mm/tlbex-fault.S b/arch/mips/mm/tlbex-fault.S new file mode 100644 index 000000000000..9e7f4175b493 --- /dev/null +++ b/arch/mips/mm/tlbex-fault.S | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 1999 Ralf Baechle | ||
7 | * Copyright (C) 1999 Silicon Graphics, Inc. | ||
8 | */ | ||
9 | #include <asm/mipsregs.h> | ||
10 | #include <asm/page.h> | ||
11 | #include <asm/regdef.h> | ||
12 | #include <asm/stackframe.h> | ||
13 | |||
14 | .macro tlb_do_page_fault, write | ||
15 | NESTED(tlb_do_page_fault_\write, PT_SIZE, sp) | ||
16 | SAVE_ALL | ||
17 | MFC0 a2, CP0_BADVADDR | ||
18 | KMODE | ||
19 | move a0, sp | ||
20 | REG_S a2, PT_BVADDR(sp) | ||
21 | li a1, \write | ||
22 | jal do_page_fault | ||
23 | j ret_from_exception | ||
24 | END(tlb_do_page_fault_\write) | ||
25 | .endm | ||
26 | |||
27 | tlb_do_page_fault 0 | ||
28 | tlb_do_page_fault 1 | ||
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c new file mode 100644 index 000000000000..87e229f4d3d5 --- /dev/null +++ b/arch/mips/mm/tlbex.c | |||
@@ -0,0 +1,1815 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Synthesize TLB refill handlers at runtime. | ||
7 | * | ||
8 | * Copyright (C) 2004,2005 by Thiemo Seufer | ||
9 | */ | ||
10 | |||
11 | #include <stdarg.h> | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/init.h> | ||
19 | |||
20 | #include <asm/pgtable.h> | ||
21 | #include <asm/cacheflush.h> | ||
22 | #include <asm/mmu_context.h> | ||
23 | #include <asm/inst.h> | ||
24 | #include <asm/elf.h> | ||
25 | #include <asm/smp.h> | ||
26 | #include <asm/war.h> | ||
27 | |||
28 | /* #define DEBUG_TLB */ | ||
29 | |||
30 | static __init int __attribute__((unused)) r45k_bvahwbug(void) | ||
31 | { | ||
32 | /* XXX: We should probe for the presence of this bug, but we don't. */ | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | static __init int __attribute__((unused)) r4k_250MHZhwbug(void) | ||
37 | { | ||
38 | /* XXX: We should probe for the presence of this bug, but we don't. */ | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static __init int __attribute__((unused)) bcm1250_m3_war(void) | ||
43 | { | ||
44 | return BCM1250_M3_WAR; | ||
45 | } | ||
46 | |||
47 | static __init int __attribute__((unused)) r10000_llsc_war(void) | ||
48 | { | ||
49 | return R10000_LLSC_WAR; | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * A little micro-assembler, intended for TLB refill handler | ||
54 | * synthesizing. It is intentionally kept simple, does only support | ||
55 | * a subset of instructions, and does not try to hide pipeline effects | ||
56 | * like branch delay slots. | ||
57 | */ | ||
58 | |||
59 | enum fields | ||
60 | { | ||
61 | RS = 0x001, | ||
62 | RT = 0x002, | ||
63 | RD = 0x004, | ||
64 | RE = 0x008, | ||
65 | SIMM = 0x010, | ||
66 | UIMM = 0x020, | ||
67 | BIMM = 0x040, | ||
68 | JIMM = 0x080, | ||
69 | FUNC = 0x100, | ||
70 | }; | ||
71 | |||
72 | #define OP_MASK 0x2f | ||
73 | #define OP_SH 26 | ||
74 | #define RS_MASK 0x1f | ||
75 | #define RS_SH 21 | ||
76 | #define RT_MASK 0x1f | ||
77 | #define RT_SH 16 | ||
78 | #define RD_MASK 0x1f | ||
79 | #define RD_SH 11 | ||
80 | #define RE_MASK 0x1f | ||
81 | #define RE_SH 6 | ||
82 | #define IMM_MASK 0xffff | ||
83 | #define IMM_SH 0 | ||
84 | #define JIMM_MASK 0x3ffffff | ||
85 | #define JIMM_SH 0 | ||
86 | #define FUNC_MASK 0x2f | ||
87 | #define FUNC_SH 0 | ||
88 | |||
89 | enum opcode { | ||
90 | insn_invalid, | ||
91 | insn_addu, insn_addiu, insn_and, insn_andi, insn_beq, | ||
92 | insn_beql, insn_bgez, insn_bgezl, insn_bltz, insn_bltzl, | ||
93 | insn_bne, insn_daddu, insn_daddiu, insn_dmfc0, insn_dmtc0, | ||
94 | insn_dsll, insn_dsll32, insn_dsra, insn_dsrl, insn_dsrl32, | ||
95 | insn_dsubu, insn_eret, insn_j, insn_jal, insn_jr, insn_ld, | ||
96 | insn_ll, insn_lld, insn_lui, insn_lw, insn_mfc0, insn_mtc0, | ||
97 | insn_ori, insn_rfe, insn_sc, insn_scd, insn_sd, insn_sll, | ||
98 | insn_sra, insn_srl, insn_subu, insn_sw, insn_tlbp, insn_tlbwi, | ||
99 | insn_tlbwr, insn_xor, insn_xori | ||
100 | }; | ||
101 | |||
102 | struct insn { | ||
103 | enum opcode opcode; | ||
104 | u32 match; | ||
105 | enum fields fields; | ||
106 | }; | ||
107 | |||
108 | /* This macro sets the non-variable bits of an instruction. */ | ||
109 | #define M(a, b, c, d, e, f) \ | ||
110 | ((a) << OP_SH \ | ||
111 | | (b) << RS_SH \ | ||
112 | | (c) << RT_SH \ | ||
113 | | (d) << RD_SH \ | ||
114 | | (e) << RE_SH \ | ||
115 | | (f) << FUNC_SH) | ||
116 | |||
117 | static __initdata struct insn insn_table[] = { | ||
118 | { insn_addiu, M(addiu_op,0,0,0,0,0), RS | RT | SIMM }, | ||
119 | { insn_addu, M(spec_op,0,0,0,0,addu_op), RS | RT | RD }, | ||
120 | { insn_and, M(spec_op,0,0,0,0,and_op), RS | RT | RD }, | ||
121 | { insn_andi, M(andi_op,0,0,0,0,0), RS | RT | UIMM }, | ||
122 | { insn_beq, M(beq_op,0,0,0,0,0), RS | RT | BIMM }, | ||
123 | { insn_beql, M(beql_op,0,0,0,0,0), RS | RT | BIMM }, | ||
124 | { insn_bgez, M(bcond_op,0,bgez_op,0,0,0), RS | BIMM }, | ||
125 | { insn_bgezl, M(bcond_op,0,bgezl_op,0,0,0), RS | BIMM }, | ||
126 | { insn_bltz, M(bcond_op,0,bltz_op,0,0,0), RS | BIMM }, | ||
127 | { insn_bltzl, M(bcond_op,0,bltzl_op,0,0,0), RS | BIMM }, | ||
128 | { insn_bne, M(bne_op,0,0,0,0,0), RS | RT | BIMM }, | ||
129 | { insn_daddiu, M(daddiu_op,0,0,0,0,0), RS | RT | SIMM }, | ||
130 | { insn_daddu, M(spec_op,0,0,0,0,daddu_op), RS | RT | RD }, | ||
131 | { insn_dmfc0, M(cop0_op,dmfc_op,0,0,0,0), RT | RD }, | ||
132 | { insn_dmtc0, M(cop0_op,dmtc_op,0,0,0,0), RT | RD }, | ||
133 | { insn_dsll, M(spec_op,0,0,0,0,dsll_op), RT | RD | RE }, | ||
134 | { insn_dsll32, M(spec_op,0,0,0,0,dsll32_op), RT | RD | RE }, | ||
135 | { insn_dsra, M(spec_op,0,0,0,0,dsra_op), RT | RD | RE }, | ||
136 | { insn_dsrl, M(spec_op,0,0,0,0,dsrl_op), RT | RD | RE }, | ||
137 | { insn_dsrl32, M(spec_op,0,0,0,0,dsrl32_op), RT | RD | RE }, | ||
138 | { insn_dsubu, M(spec_op,0,0,0,0,dsubu_op), RS | RT | RD }, | ||
139 | { insn_eret, M(cop0_op,cop_op,0,0,0,eret_op), 0 }, | ||
140 | { insn_j, M(j_op,0,0,0,0,0), JIMM }, | ||
141 | { insn_jal, M(jal_op,0,0,0,0,0), JIMM }, | ||
142 | { insn_jr, M(spec_op,0,0,0,0,jr_op), RS }, | ||
143 | { insn_ld, M(ld_op,0,0,0,0,0), RS | RT | SIMM }, | ||
144 | { insn_ll, M(ll_op,0,0,0,0,0), RS | RT | SIMM }, | ||
145 | { insn_lld, M(lld_op,0,0,0,0,0), RS | RT | SIMM }, | ||
146 | { insn_lui, M(lui_op,0,0,0,0,0), RT | SIMM }, | ||
147 | { insn_lw, M(lw_op,0,0,0,0,0), RS | RT | SIMM }, | ||
148 | { insn_mfc0, M(cop0_op,mfc_op,0,0,0,0), RT | RD }, | ||
149 | { insn_mtc0, M(cop0_op,mtc_op,0,0,0,0), RT | RD }, | ||
150 | { insn_ori, M(ori_op,0,0,0,0,0), RS | RT | UIMM }, | ||
151 | { insn_rfe, M(cop0_op,cop_op,0,0,0,rfe_op), 0 }, | ||
152 | { insn_sc, M(sc_op,0,0,0,0,0), RS | RT | SIMM }, | ||
153 | { insn_scd, M(scd_op,0,0,0,0,0), RS | RT | SIMM }, | ||
154 | { insn_sd, M(sd_op,0,0,0,0,0), RS | RT | SIMM }, | ||
155 | { insn_sll, M(spec_op,0,0,0,0,sll_op), RT | RD | RE }, | ||
156 | { insn_sra, M(spec_op,0,0,0,0,sra_op), RT | RD | RE }, | ||
157 | { insn_srl, M(spec_op,0,0,0,0,srl_op), RT | RD | RE }, | ||
158 | { insn_subu, M(spec_op,0,0,0,0,subu_op), RS | RT | RD }, | ||
159 | { insn_sw, M(sw_op,0,0,0,0,0), RS | RT | SIMM }, | ||
160 | { insn_tlbp, M(cop0_op,cop_op,0,0,0,tlbp_op), 0 }, | ||
161 | { insn_tlbwi, M(cop0_op,cop_op,0,0,0,tlbwi_op), 0 }, | ||
162 | { insn_tlbwr, M(cop0_op,cop_op,0,0,0,tlbwr_op), 0 }, | ||
163 | { insn_xor, M(spec_op,0,0,0,0,xor_op), RS | RT | RD }, | ||
164 | { insn_xori, M(xori_op,0,0,0,0,0), RS | RT | UIMM }, | ||
165 | { insn_invalid, 0, 0 } | ||
166 | }; | ||
167 | |||
168 | #undef M | ||
169 | |||
170 | static __init u32 build_rs(u32 arg) | ||
171 | { | ||
172 | if (arg & ~RS_MASK) | ||
173 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
174 | |||
175 | return (arg & RS_MASK) << RS_SH; | ||
176 | } | ||
177 | |||
178 | static __init u32 build_rt(u32 arg) | ||
179 | { | ||
180 | if (arg & ~RT_MASK) | ||
181 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
182 | |||
183 | return (arg & RT_MASK) << RT_SH; | ||
184 | } | ||
185 | |||
186 | static __init u32 build_rd(u32 arg) | ||
187 | { | ||
188 | if (arg & ~RD_MASK) | ||
189 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
190 | |||
191 | return (arg & RD_MASK) << RD_SH; | ||
192 | } | ||
193 | |||
194 | static __init u32 build_re(u32 arg) | ||
195 | { | ||
196 | if (arg & ~RE_MASK) | ||
197 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
198 | |||
199 | return (arg & RE_MASK) << RE_SH; | ||
200 | } | ||
201 | |||
202 | static __init u32 build_simm(s32 arg) | ||
203 | { | ||
204 | if (arg > 0x7fff || arg < -0x8000) | ||
205 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
206 | |||
207 | return arg & 0xffff; | ||
208 | } | ||
209 | |||
210 | static __init u32 build_uimm(u32 arg) | ||
211 | { | ||
212 | if (arg & ~IMM_MASK) | ||
213 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
214 | |||
215 | return arg & IMM_MASK; | ||
216 | } | ||
217 | |||
218 | static __init u32 build_bimm(s32 arg) | ||
219 | { | ||
220 | if (arg > 0x1ffff || arg < -0x20000) | ||
221 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
222 | |||
223 | if (arg & 0x3) | ||
224 | printk(KERN_WARNING "Invalid TLB synthesizer branch target\n"); | ||
225 | |||
226 | return ((arg < 0) ? (1 << 15) : 0) | ((arg >> 2) & 0x7fff); | ||
227 | } | ||
228 | |||
229 | static __init u32 build_jimm(u32 arg) | ||
230 | { | ||
231 | if (arg & ~((JIMM_MASK) << 2)) | ||
232 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
233 | |||
234 | return (arg >> 2) & JIMM_MASK; | ||
235 | } | ||
236 | |||
237 | static __init u32 build_func(u32 arg) | ||
238 | { | ||
239 | if (arg & ~FUNC_MASK) | ||
240 | printk(KERN_WARNING "TLB synthesizer field overflow\n"); | ||
241 | |||
242 | return arg & FUNC_MASK; | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | * The order of opcode arguments is implicitly left to right, | ||
247 | * starting with RS and ending with FUNC or IMM. | ||
248 | */ | ||
249 | static void __init build_insn(u32 **buf, enum opcode opc, ...) | ||
250 | { | ||
251 | struct insn *ip = NULL; | ||
252 | unsigned int i; | ||
253 | va_list ap; | ||
254 | u32 op; | ||
255 | |||
256 | for (i = 0; insn_table[i].opcode != insn_invalid; i++) | ||
257 | if (insn_table[i].opcode == opc) { | ||
258 | ip = &insn_table[i]; | ||
259 | break; | ||
260 | } | ||
261 | |||
262 | if (!ip) | ||
263 | panic("Unsupported TLB synthesizer instruction %d", opc); | ||
264 | |||
265 | op = ip->match; | ||
266 | va_start(ap, opc); | ||
267 | if (ip->fields & RS) op |= build_rs(va_arg(ap, u32)); | ||
268 | if (ip->fields & RT) op |= build_rt(va_arg(ap, u32)); | ||
269 | if (ip->fields & RD) op |= build_rd(va_arg(ap, u32)); | ||
270 | if (ip->fields & RE) op |= build_re(va_arg(ap, u32)); | ||
271 | if (ip->fields & SIMM) op |= build_simm(va_arg(ap, s32)); | ||
272 | if (ip->fields & UIMM) op |= build_uimm(va_arg(ap, u32)); | ||
273 | if (ip->fields & BIMM) op |= build_bimm(va_arg(ap, s32)); | ||
274 | if (ip->fields & JIMM) op |= build_jimm(va_arg(ap, u32)); | ||
275 | if (ip->fields & FUNC) op |= build_func(va_arg(ap, u32)); | ||
276 | va_end(ap); | ||
277 | |||
278 | **buf = op; | ||
279 | (*buf)++; | ||
280 | } | ||
281 | |||
282 | #define I_u1u2u3(op) \ | ||
283 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
284 | unsigned int b, unsigned int c) \ | ||
285 | { \ | ||
286 | build_insn(buf, insn##op, a, b, c); \ | ||
287 | } | ||
288 | |||
289 | #define I_u2u1u3(op) \ | ||
290 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
291 | unsigned int b, unsigned int c) \ | ||
292 | { \ | ||
293 | build_insn(buf, insn##op, b, a, c); \ | ||
294 | } | ||
295 | |||
296 | #define I_u3u1u2(op) \ | ||
297 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
298 | unsigned int b, unsigned int c) \ | ||
299 | { \ | ||
300 | build_insn(buf, insn##op, b, c, a); \ | ||
301 | } | ||
302 | |||
303 | #define I_u1u2s3(op) \ | ||
304 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
305 | unsigned int b, signed int c) \ | ||
306 | { \ | ||
307 | build_insn(buf, insn##op, a, b, c); \ | ||
308 | } | ||
309 | |||
310 | #define I_u2s3u1(op) \ | ||
311 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
312 | signed int b, unsigned int c) \ | ||
313 | { \ | ||
314 | build_insn(buf, insn##op, c, a, b); \ | ||
315 | } | ||
316 | |||
317 | #define I_u2u1s3(op) \ | ||
318 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
319 | unsigned int b, signed int c) \ | ||
320 | { \ | ||
321 | build_insn(buf, insn##op, b, a, c); \ | ||
322 | } | ||
323 | |||
324 | #define I_u1u2(op) \ | ||
325 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
326 | unsigned int b) \ | ||
327 | { \ | ||
328 | build_insn(buf, insn##op, a, b); \ | ||
329 | } | ||
330 | |||
331 | #define I_u1s2(op) \ | ||
332 | static inline void i##op(u32 **buf, unsigned int a, \ | ||
333 | signed int b) \ | ||
334 | { \ | ||
335 | build_insn(buf, insn##op, a, b); \ | ||
336 | } | ||
337 | |||
338 | #define I_u1(op) \ | ||
339 | static inline void i##op(u32 **buf, unsigned int a) \ | ||
340 | { \ | ||
341 | build_insn(buf, insn##op, a); \ | ||
342 | } | ||
343 | |||
344 | #define I_0(op) \ | ||
345 | static inline void i##op(u32 **buf) \ | ||
346 | { \ | ||
347 | build_insn(buf, insn##op); \ | ||
348 | } | ||
349 | |||
350 | I_u2u1s3(_addiu); | ||
351 | I_u3u1u2(_addu); | ||
352 | I_u2u1u3(_andi); | ||
353 | I_u3u1u2(_and); | ||
354 | I_u1u2s3(_beq); | ||
355 | I_u1u2s3(_beql); | ||
356 | I_u1s2(_bgez); | ||
357 | I_u1s2(_bgezl); | ||
358 | I_u1s2(_bltz); | ||
359 | I_u1s2(_bltzl); | ||
360 | I_u1u2s3(_bne); | ||
361 | I_u1u2(_dmfc0); | ||
362 | I_u1u2(_dmtc0); | ||
363 | I_u2u1s3(_daddiu); | ||
364 | I_u3u1u2(_daddu); | ||
365 | I_u2u1u3(_dsll); | ||
366 | I_u2u1u3(_dsll32); | ||
367 | I_u2u1u3(_dsra); | ||
368 | I_u2u1u3(_dsrl); | ||
369 | I_u2u1u3(_dsrl32); | ||
370 | I_u3u1u2(_dsubu); | ||
371 | I_0(_eret); | ||
372 | I_u1(_j); | ||
373 | I_u1(_jal); | ||
374 | I_u1(_jr); | ||
375 | I_u2s3u1(_ld); | ||
376 | I_u2s3u1(_ll); | ||
377 | I_u2s3u1(_lld); | ||
378 | I_u1s2(_lui); | ||
379 | I_u2s3u1(_lw); | ||
380 | I_u1u2(_mfc0); | ||
381 | I_u1u2(_mtc0); | ||
382 | I_u2u1u3(_ori); | ||
383 | I_0(_rfe); | ||
384 | I_u2s3u1(_sc); | ||
385 | I_u2s3u1(_scd); | ||
386 | I_u2s3u1(_sd); | ||
387 | I_u2u1u3(_sll); | ||
388 | I_u2u1u3(_sra); | ||
389 | I_u2u1u3(_srl); | ||
390 | I_u3u1u2(_subu); | ||
391 | I_u2s3u1(_sw); | ||
392 | I_0(_tlbp); | ||
393 | I_0(_tlbwi); | ||
394 | I_0(_tlbwr); | ||
395 | I_u3u1u2(_xor) | ||
396 | I_u2u1u3(_xori); | ||
397 | |||
398 | /* | ||
399 | * handling labels | ||
400 | */ | ||
401 | |||
402 | enum label_id { | ||
403 | label_invalid, | ||
404 | label_second_part, | ||
405 | label_leave, | ||
406 | label_vmalloc, | ||
407 | label_vmalloc_done, | ||
408 | label_tlbw_hazard, | ||
409 | label_split, | ||
410 | label_nopage_tlbl, | ||
411 | label_nopage_tlbs, | ||
412 | label_nopage_tlbm, | ||
413 | label_smp_pgtable_change, | ||
414 | label_r3000_write_probe_fail, | ||
415 | label_r3000_write_probe_ok | ||
416 | }; | ||
417 | |||
418 | struct label { | ||
419 | u32 *addr; | ||
420 | enum label_id lab; | ||
421 | }; | ||
422 | |||
423 | static __init void build_label(struct label **lab, u32 *addr, | ||
424 | enum label_id l) | ||
425 | { | ||
426 | (*lab)->addr = addr; | ||
427 | (*lab)->lab = l; | ||
428 | (*lab)++; | ||
429 | } | ||
430 | |||
431 | #define L_LA(lb) \ | ||
432 | static inline void l##lb(struct label **lab, u32 *addr) \ | ||
433 | { \ | ||
434 | build_label(lab, addr, label##lb); \ | ||
435 | } | ||
436 | |||
437 | L_LA(_second_part) | ||
438 | L_LA(_leave) | ||
439 | L_LA(_vmalloc) | ||
440 | L_LA(_vmalloc_done) | ||
441 | L_LA(_tlbw_hazard) | ||
442 | L_LA(_split) | ||
443 | L_LA(_nopage_tlbl) | ||
444 | L_LA(_nopage_tlbs) | ||
445 | L_LA(_nopage_tlbm) | ||
446 | L_LA(_smp_pgtable_change) | ||
447 | L_LA(_r3000_write_probe_fail) | ||
448 | L_LA(_r3000_write_probe_ok) | ||
449 | |||
450 | /* convenience macros for instructions */ | ||
451 | #ifdef CONFIG_MIPS64 | ||
452 | # define i_LW(buf, rs, rt, off) i_ld(buf, rs, rt, off) | ||
453 | # define i_SW(buf, rs, rt, off) i_sd(buf, rs, rt, off) | ||
454 | # define i_SLL(buf, rs, rt, sh) i_dsll(buf, rs, rt, sh) | ||
455 | # define i_SRA(buf, rs, rt, sh) i_dsra(buf, rs, rt, sh) | ||
456 | # define i_SRL(buf, rs, rt, sh) i_dsrl(buf, rs, rt, sh) | ||
457 | # define i_MFC0(buf, rt, rd) i_dmfc0(buf, rt, rd) | ||
458 | # define i_MTC0(buf, rt, rd) i_dmtc0(buf, rt, rd) | ||
459 | # define i_ADDIU(buf, rs, rt, val) i_daddiu(buf, rs, rt, val) | ||
460 | # define i_ADDU(buf, rs, rt, rd) i_daddu(buf, rs, rt, rd) | ||
461 | # define i_SUBU(buf, rs, rt, rd) i_dsubu(buf, rs, rt, rd) | ||
462 | # define i_LL(buf, rs, rt, off) i_lld(buf, rs, rt, off) | ||
463 | # define i_SC(buf, rs, rt, off) i_scd(buf, rs, rt, off) | ||
464 | #else | ||
465 | # define i_LW(buf, rs, rt, off) i_lw(buf, rs, rt, off) | ||
466 | # define i_SW(buf, rs, rt, off) i_sw(buf, rs, rt, off) | ||
467 | # define i_SLL(buf, rs, rt, sh) i_sll(buf, rs, rt, sh) | ||
468 | # define i_SRA(buf, rs, rt, sh) i_sra(buf, rs, rt, sh) | ||
469 | # define i_SRL(buf, rs, rt, sh) i_srl(buf, rs, rt, sh) | ||
470 | # define i_MFC0(buf, rt, rd) i_mfc0(buf, rt, rd) | ||
471 | # define i_MTC0(buf, rt, rd) i_mtc0(buf, rt, rd) | ||
472 | # define i_ADDIU(buf, rs, rt, val) i_addiu(buf, rs, rt, val) | ||
473 | # define i_ADDU(buf, rs, rt, rd) i_addu(buf, rs, rt, rd) | ||
474 | # define i_SUBU(buf, rs, rt, rd) i_subu(buf, rs, rt, rd) | ||
475 | # define i_LL(buf, rs, rt, off) i_ll(buf, rs, rt, off) | ||
476 | # define i_SC(buf, rs, rt, off) i_sc(buf, rs, rt, off) | ||
477 | #endif | ||
478 | |||
479 | #define i_b(buf, off) i_beq(buf, 0, 0, off) | ||
480 | #define i_beqz(buf, rs, off) i_beq(buf, rs, 0, off) | ||
481 | #define i_beqzl(buf, rs, off) i_beql(buf, rs, 0, off) | ||
482 | #define i_bnez(buf, rs, off) i_bne(buf, rs, 0, off) | ||
483 | #define i_bnezl(buf, rs, off) i_bnel(buf, rs, 0, off) | ||
484 | #define i_move(buf, a, b) i_ADDU(buf, a, 0, b) | ||
485 | #define i_nop(buf) i_sll(buf, 0, 0, 0) | ||
486 | #define i_ssnop(buf) i_sll(buf, 0, 0, 1) | ||
487 | #define i_ehb(buf) i_sll(buf, 0, 0, 3) | ||
488 | |||
489 | #ifdef CONFIG_MIPS64 | ||
490 | static __init int __attribute__((unused)) in_compat_space_p(long addr) | ||
491 | { | ||
492 | /* Is this address in 32bit compat space? */ | ||
493 | return (((addr) & 0xffffffff00000000) == 0xffffffff00000000); | ||
494 | } | ||
495 | |||
496 | static __init int __attribute__((unused)) rel_highest(long val) | ||
497 | { | ||
498 | return ((((val + 0x800080008000L) >> 48) & 0xffff) ^ 0x8000) - 0x8000; | ||
499 | } | ||
500 | |||
501 | static __init int __attribute__((unused)) rel_higher(long val) | ||
502 | { | ||
503 | return ((((val + 0x80008000L) >> 32) & 0xffff) ^ 0x8000) - 0x8000; | ||
504 | } | ||
505 | #endif | ||
506 | |||
507 | static __init int rel_hi(long val) | ||
508 | { | ||
509 | return ((((val + 0x8000L) >> 16) & 0xffff) ^ 0x8000) - 0x8000; | ||
510 | } | ||
511 | |||
512 | static __init int rel_lo(long val) | ||
513 | { | ||
514 | return ((val & 0xffff) ^ 0x8000) - 0x8000; | ||
515 | } | ||
516 | |||
517 | static __init void i_LA_mostly(u32 **buf, unsigned int rs, long addr) | ||
518 | { | ||
519 | #if CONFIG_MIPS64 | ||
520 | if (!in_compat_space_p(addr)) { | ||
521 | i_lui(buf, rs, rel_highest(addr)); | ||
522 | if (rel_higher(addr)) | ||
523 | i_daddiu(buf, rs, rs, rel_higher(addr)); | ||
524 | if (rel_hi(addr)) { | ||
525 | i_dsll(buf, rs, rs, 16); | ||
526 | i_daddiu(buf, rs, rs, rel_hi(addr)); | ||
527 | i_dsll(buf, rs, rs, 16); | ||
528 | } else | ||
529 | i_dsll32(buf, rs, rs, 0); | ||
530 | } else | ||
531 | #endif | ||
532 | i_lui(buf, rs, rel_hi(addr)); | ||
533 | } | ||
534 | |||
535 | static __init void __attribute__((unused)) i_LA(u32 **buf, unsigned int rs, | ||
536 | long addr) | ||
537 | { | ||
538 | i_LA_mostly(buf, rs, addr); | ||
539 | if (rel_lo(addr)) | ||
540 | i_ADDIU(buf, rs, rs, rel_lo(addr)); | ||
541 | } | ||
542 | |||
543 | /* | ||
544 | * handle relocations | ||
545 | */ | ||
546 | |||
547 | struct reloc { | ||
548 | u32 *addr; | ||
549 | unsigned int type; | ||
550 | enum label_id lab; | ||
551 | }; | ||
552 | |||
553 | static __init void r_mips_pc16(struct reloc **rel, u32 *addr, | ||
554 | enum label_id l) | ||
555 | { | ||
556 | (*rel)->addr = addr; | ||
557 | (*rel)->type = R_MIPS_PC16; | ||
558 | (*rel)->lab = l; | ||
559 | (*rel)++; | ||
560 | } | ||
561 | |||
562 | static inline void __resolve_relocs(struct reloc *rel, struct label *lab) | ||
563 | { | ||
564 | long laddr = (long)lab->addr; | ||
565 | long raddr = (long)rel->addr; | ||
566 | |||
567 | switch (rel->type) { | ||
568 | case R_MIPS_PC16: | ||
569 | *rel->addr |= build_bimm(laddr - (raddr + 4)); | ||
570 | break; | ||
571 | |||
572 | default: | ||
573 | panic("Unsupported TLB synthesizer relocation %d", | ||
574 | rel->type); | ||
575 | } | ||
576 | } | ||
577 | |||
578 | static __init void resolve_relocs(struct reloc *rel, struct label *lab) | ||
579 | { | ||
580 | struct label *l; | ||
581 | |||
582 | for (; rel->lab != label_invalid; rel++) | ||
583 | for (l = lab; l->lab != label_invalid; l++) | ||
584 | if (rel->lab == l->lab) | ||
585 | __resolve_relocs(rel, l); | ||
586 | } | ||
587 | |||
588 | static __init void move_relocs(struct reloc *rel, u32 *first, u32 *end, | ||
589 | long off) | ||
590 | { | ||
591 | for (; rel->lab != label_invalid; rel++) | ||
592 | if (rel->addr >= first && rel->addr < end) | ||
593 | rel->addr += off; | ||
594 | } | ||
595 | |||
596 | static __init void move_labels(struct label *lab, u32 *first, u32 *end, | ||
597 | long off) | ||
598 | { | ||
599 | for (; lab->lab != label_invalid; lab++) | ||
600 | if (lab->addr >= first && lab->addr < end) | ||
601 | lab->addr += off; | ||
602 | } | ||
603 | |||
604 | static __init void copy_handler(struct reloc *rel, struct label *lab, | ||
605 | u32 *first, u32 *end, u32 *target) | ||
606 | { | ||
607 | long off = (long)(target - first); | ||
608 | |||
609 | memcpy(target, first, (end - first) * sizeof(u32)); | ||
610 | |||
611 | move_relocs(rel, first, end, off); | ||
612 | move_labels(lab, first, end, off); | ||
613 | } | ||
614 | |||
615 | static __init int __attribute__((unused)) insn_has_bdelay(struct reloc *rel, | ||
616 | u32 *addr) | ||
617 | { | ||
618 | for (; rel->lab != label_invalid; rel++) { | ||
619 | if (rel->addr == addr | ||
620 | && (rel->type == R_MIPS_PC16 | ||
621 | || rel->type == R_MIPS_26)) | ||
622 | return 1; | ||
623 | } | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | /* convenience functions for labeled branches */ | ||
629 | static void __attribute__((unused)) il_bltz(u32 **p, struct reloc **r, | ||
630 | unsigned int reg, enum label_id l) | ||
631 | { | ||
632 | r_mips_pc16(r, *p, l); | ||
633 | i_bltz(p, reg, 0); | ||
634 | } | ||
635 | |||
636 | static void __attribute__((unused)) il_b(u32 **p, struct reloc **r, | ||
637 | enum label_id l) | ||
638 | { | ||
639 | r_mips_pc16(r, *p, l); | ||
640 | i_b(p, 0); | ||
641 | } | ||
642 | |||
643 | static void il_beqz(u32 **p, struct reloc **r, unsigned int reg, | ||
644 | enum label_id l) | ||
645 | { | ||
646 | r_mips_pc16(r, *p, l); | ||
647 | i_beqz(p, reg, 0); | ||
648 | } | ||
649 | |||
650 | static void __attribute__((unused)) | ||
651 | il_beqzl(u32 **p, struct reloc **r, unsigned int reg, enum label_id l) | ||
652 | { | ||
653 | r_mips_pc16(r, *p, l); | ||
654 | i_beqzl(p, reg, 0); | ||
655 | } | ||
656 | |||
657 | static void il_bnez(u32 **p, struct reloc **r, unsigned int reg, | ||
658 | enum label_id l) | ||
659 | { | ||
660 | r_mips_pc16(r, *p, l); | ||
661 | i_bnez(p, reg, 0); | ||
662 | } | ||
663 | |||
664 | static void il_bgezl(u32 **p, struct reloc **r, unsigned int reg, | ||
665 | enum label_id l) | ||
666 | { | ||
667 | r_mips_pc16(r, *p, l); | ||
668 | i_bgezl(p, reg, 0); | ||
669 | } | ||
670 | |||
671 | /* The only general purpose registers allowed in TLB handlers. */ | ||
672 | #define K0 26 | ||
673 | #define K1 27 | ||
674 | |||
675 | /* Some CP0 registers */ | ||
676 | #define C0_INDEX 0 | ||
677 | #define C0_ENTRYLO0 2 | ||
678 | #define C0_ENTRYLO1 3 | ||
679 | #define C0_CONTEXT 4 | ||
680 | #define C0_BADVADDR 8 | ||
681 | #define C0_ENTRYHI 10 | ||
682 | #define C0_EPC 14 | ||
683 | #define C0_XCONTEXT 20 | ||
684 | |||
685 | #ifdef CONFIG_MIPS64 | ||
686 | # define GET_CONTEXT(buf, reg) i_MFC0(buf, reg, C0_XCONTEXT) | ||
687 | #else | ||
688 | # define GET_CONTEXT(buf, reg) i_MFC0(buf, reg, C0_CONTEXT) | ||
689 | #endif | ||
690 | |||
691 | /* The worst case length of the handler is around 18 instructions for | ||
692 | * R3000-style TLBs and up to 63 instructions for R4000-style TLBs. | ||
693 | * Maximum space available is 32 instructions for R3000 and 64 | ||
694 | * instructions for R4000. | ||
695 | * | ||
696 | * We deliberately chose a buffer size of 128, so we won't scribble | ||
697 | * over anything important on overflow before we panic. | ||
698 | */ | ||
699 | static __initdata u32 tlb_handler[128]; | ||
700 | |||
701 | /* simply assume worst case size for labels and relocs */ | ||
702 | static __initdata struct label labels[128]; | ||
703 | static __initdata struct reloc relocs[128]; | ||
704 | |||
705 | /* | ||
706 | * The R3000 TLB handler is simple. | ||
707 | */ | ||
708 | static void __init build_r3000_tlb_refill_handler(void) | ||
709 | { | ||
710 | long pgdc = (long)pgd_current; | ||
711 | u32 *p; | ||
712 | |||
713 | memset(tlb_handler, 0, sizeof(tlb_handler)); | ||
714 | p = tlb_handler; | ||
715 | |||
716 | i_mfc0(&p, K0, C0_BADVADDR); | ||
717 | i_lui(&p, K1, rel_hi(pgdc)); /* cp0 delay */ | ||
718 | i_lw(&p, K1, rel_lo(pgdc), K1); | ||
719 | i_srl(&p, K0, K0, 22); /* load delay */ | ||
720 | i_sll(&p, K0, K0, 2); | ||
721 | i_addu(&p, K1, K1, K0); | ||
722 | i_mfc0(&p, K0, C0_CONTEXT); | ||
723 | i_lw(&p, K1, 0, K1); /* cp0 delay */ | ||
724 | i_andi(&p, K0, K0, 0xffc); /* load delay */ | ||
725 | i_addu(&p, K1, K1, K0); | ||
726 | i_lw(&p, K0, 0, K1); | ||
727 | i_nop(&p); /* load delay */ | ||
728 | i_mtc0(&p, K0, C0_ENTRYLO0); | ||
729 | i_mfc0(&p, K1, C0_EPC); /* cp0 delay */ | ||
730 | i_tlbwr(&p); /* cp0 delay */ | ||
731 | i_jr(&p, K1); | ||
732 | i_rfe(&p); /* branch delay */ | ||
733 | |||
734 | if (p > tlb_handler + 32) | ||
735 | panic("TLB refill handler space exceeded"); | ||
736 | |||
737 | printk("Synthesized TLB handler (%u instructions).\n", | ||
738 | (unsigned int)(p - tlb_handler)); | ||
739 | #ifdef DEBUG_TLB | ||
740 | { | ||
741 | int i; | ||
742 | |||
743 | for (i = 0; i < (p - tlb_handler); i++) | ||
744 | printk("%08x\n", tlb_handler[i]); | ||
745 | } | ||
746 | #endif | ||
747 | |||
748 | memcpy((void *)CAC_BASE, tlb_handler, 0x80); | ||
749 | flush_icache_range(CAC_BASE, CAC_BASE + 0x80); | ||
750 | } | ||
751 | |||
752 | /* | ||
753 | * The R4000 TLB handler is much more complicated. We have two | ||
754 | * consecutive handler areas with 32 instructions space each. | ||
755 | * Since they aren't used at the same time, we can overflow in the | ||
756 | * other one.To keep things simple, we first assume linear space, | ||
757 | * then we relocate it to the final handler layout as needed. | ||
758 | */ | ||
759 | static __initdata u32 final_handler[64]; | ||
760 | |||
761 | /* | ||
762 | * Hazards | ||
763 | * | ||
764 | * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0: | ||
765 | * 2. A timing hazard exists for the TLBP instruction. | ||
766 | * | ||
767 | * stalling_instruction | ||
768 | * TLBP | ||
769 | * | ||
770 | * The JTLB is being read for the TLBP throughout the stall generated by the | ||
771 | * previous instruction. This is not really correct as the stalling instruction | ||
772 | * can modify the address used to access the JTLB. The failure symptom is that | ||
773 | * the TLBP instruction will use an address created for the stalling instruction | ||
774 | * and not the address held in C0_ENHI and thus report the wrong results. | ||
775 | * | ||
776 | * The software work-around is to not allow the instruction preceding the TLBP | ||
777 | * to stall - make it an NOP or some other instruction guaranteed not to stall. | ||
778 | * | ||
779 | * Errata 2 will not be fixed. This errata is also on the R5000. | ||
780 | * | ||
781 | * As if we MIPS hackers wouldn't know how to nop pipelines happy ... | ||
782 | */ | ||
783 | static __init void __attribute__((unused)) build_tlb_probe_entry(u32 **p) | ||
784 | { | ||
785 | switch (current_cpu_data.cputype) { | ||
786 | case CPU_R5000: | ||
787 | case CPU_R5000A: | ||
788 | case CPU_NEVADA: | ||
789 | i_nop(p); | ||
790 | i_tlbp(p); | ||
791 | break; | ||
792 | |||
793 | default: | ||
794 | i_tlbp(p); | ||
795 | break; | ||
796 | } | ||
797 | } | ||
798 | |||
799 | /* | ||
800 | * Write random or indexed TLB entry, and care about the hazards from | ||
801 | * the preceeding mtc0 and for the following eret. | ||
802 | */ | ||
803 | enum tlb_write_entry { tlb_random, tlb_indexed }; | ||
804 | |||
805 | static __init void build_tlb_write_entry(u32 **p, struct label **l, | ||
806 | struct reloc **r, | ||
807 | enum tlb_write_entry wmode) | ||
808 | { | ||
809 | void(*tlbw)(u32 **) = NULL; | ||
810 | |||
811 | switch (wmode) { | ||
812 | case tlb_random: tlbw = i_tlbwr; break; | ||
813 | case tlb_indexed: tlbw = i_tlbwi; break; | ||
814 | } | ||
815 | |||
816 | switch (current_cpu_data.cputype) { | ||
817 | case CPU_R4000PC: | ||
818 | case CPU_R4000SC: | ||
819 | case CPU_R4000MC: | ||
820 | case CPU_R4400PC: | ||
821 | case CPU_R4400SC: | ||
822 | case CPU_R4400MC: | ||
823 | /* | ||
824 | * This branch uses up a mtc0 hazard nop slot and saves | ||
825 | * two nops after the tlbw instruction. | ||
826 | */ | ||
827 | il_bgezl(p, r, 0, label_tlbw_hazard); | ||
828 | tlbw(p); | ||
829 | l_tlbw_hazard(l, *p); | ||
830 | i_nop(p); | ||
831 | break; | ||
832 | |||
833 | case CPU_R4600: | ||
834 | case CPU_R4700: | ||
835 | case CPU_R5000: | ||
836 | case CPU_R5000A: | ||
837 | case CPU_5KC: | ||
838 | case CPU_TX49XX: | ||
839 | case CPU_AU1000: | ||
840 | case CPU_AU1100: | ||
841 | case CPU_AU1500: | ||
842 | case CPU_AU1550: | ||
843 | i_nop(p); | ||
844 | tlbw(p); | ||
845 | break; | ||
846 | |||
847 | case CPU_R10000: | ||
848 | case CPU_R12000: | ||
849 | case CPU_4KC: | ||
850 | case CPU_SB1: | ||
851 | case CPU_4KSC: | ||
852 | case CPU_20KC: | ||
853 | case CPU_25KF: | ||
854 | tlbw(p); | ||
855 | break; | ||
856 | |||
857 | case CPU_NEVADA: | ||
858 | i_nop(p); /* QED specifies 2 nops hazard */ | ||
859 | /* | ||
860 | * This branch uses up a mtc0 hazard nop slot and saves | ||
861 | * a nop after the tlbw instruction. | ||
862 | */ | ||
863 | il_bgezl(p, r, 0, label_tlbw_hazard); | ||
864 | tlbw(p); | ||
865 | l_tlbw_hazard(l, *p); | ||
866 | break; | ||
867 | |||
868 | case CPU_RM7000: | ||
869 | i_nop(p); | ||
870 | i_nop(p); | ||
871 | i_nop(p); | ||
872 | i_nop(p); | ||
873 | tlbw(p); | ||
874 | break; | ||
875 | |||
876 | case CPU_4KEC: | ||
877 | case CPU_24K: | ||
878 | i_ehb(p); | ||
879 | tlbw(p); | ||
880 | break; | ||
881 | |||
882 | case CPU_RM9000: | ||
883 | /* | ||
884 | * When the JTLB is updated by tlbwi or tlbwr, a subsequent | ||
885 | * use of the JTLB for instructions should not occur for 4 | ||
886 | * cpu cycles and use for data translations should not occur | ||
887 | * for 3 cpu cycles. | ||
888 | */ | ||
889 | i_ssnop(p); | ||
890 | i_ssnop(p); | ||
891 | i_ssnop(p); | ||
892 | i_ssnop(p); | ||
893 | tlbw(p); | ||
894 | i_ssnop(p); | ||
895 | i_ssnop(p); | ||
896 | i_ssnop(p); | ||
897 | i_ssnop(p); | ||
898 | break; | ||
899 | |||
900 | case CPU_VR4111: | ||
901 | case CPU_VR4121: | ||
902 | case CPU_VR4122: | ||
903 | case CPU_VR4181: | ||
904 | case CPU_VR4181A: | ||
905 | i_nop(p); | ||
906 | i_nop(p); | ||
907 | tlbw(p); | ||
908 | i_nop(p); | ||
909 | i_nop(p); | ||
910 | break; | ||
911 | |||
912 | case CPU_VR4131: | ||
913 | case CPU_VR4133: | ||
914 | i_nop(p); | ||
915 | i_nop(p); | ||
916 | tlbw(p); | ||
917 | break; | ||
918 | |||
919 | default: | ||
920 | panic("No TLB refill handler yet (CPU type: %d)", | ||
921 | current_cpu_data.cputype); | ||
922 | break; | ||
923 | } | ||
924 | } | ||
925 | |||
926 | #ifdef CONFIG_MIPS64 | ||
927 | /* | ||
928 | * TMP and PTR are scratch. | ||
929 | * TMP will be clobbered, PTR will hold the pmd entry. | ||
930 | */ | ||
931 | static __init void | ||
932 | build_get_pmde64(u32 **p, struct label **l, struct reloc **r, | ||
933 | unsigned int tmp, unsigned int ptr) | ||
934 | { | ||
935 | long pgdc = (long)pgd_current; | ||
936 | |||
937 | /* | ||
938 | * The vmalloc handling is not in the hotpath. | ||
939 | */ | ||
940 | i_dmfc0(p, tmp, C0_BADVADDR); | ||
941 | il_bltz(p, r, tmp, label_vmalloc); | ||
942 | /* No i_nop needed here, since the next insn doesn't touch TMP. */ | ||
943 | |||
944 | #ifdef CONFIG_SMP | ||
945 | /* | ||
946 | * 64 bit SMP has the lower part of &pgd_current[smp_processor_id()] | ||
947 | * stored in CONTEXT. | ||
948 | */ | ||
949 | if (in_compat_space_p(pgdc)) { | ||
950 | i_dmfc0(p, ptr, C0_CONTEXT); | ||
951 | i_dsra(p, ptr, ptr, 23); | ||
952 | i_ld(p, ptr, 0, ptr); | ||
953 | } else { | ||
954 | #ifdef CONFIG_BUILD_ELF64 | ||
955 | i_dmfc0(p, ptr, C0_CONTEXT); | ||
956 | i_dsrl(p, ptr, ptr, 23); | ||
957 | i_dsll(p, ptr, ptr, 3); | ||
958 | i_LA_mostly(p, tmp, pgdc); | ||
959 | i_daddu(p, ptr, ptr, tmp); | ||
960 | i_dmfc0(p, tmp, C0_BADVADDR); | ||
961 | i_ld(p, ptr, rel_lo(pgdc), ptr); | ||
962 | #else | ||
963 | i_dmfc0(p, ptr, C0_CONTEXT); | ||
964 | i_lui(p, tmp, rel_highest(pgdc)); | ||
965 | i_dsll(p, ptr, ptr, 9); | ||
966 | i_daddiu(p, tmp, tmp, rel_higher(pgdc)); | ||
967 | i_dsrl32(p, ptr, ptr, 0); | ||
968 | i_and(p, ptr, ptr, tmp); | ||
969 | i_dmfc0(p, tmp, C0_BADVADDR); | ||
970 | i_ld(p, ptr, 0, ptr); | ||
971 | #endif | ||
972 | } | ||
973 | #else | ||
974 | i_LA_mostly(p, ptr, pgdc); | ||
975 | i_ld(p, ptr, rel_lo(pgdc), ptr); | ||
976 | #endif | ||
977 | |||
978 | l_vmalloc_done(l, *p); | ||
979 | i_dsrl(p, tmp, tmp, PGDIR_SHIFT-3); /* get pgd offset in bytes */ | ||
980 | i_andi(p, tmp, tmp, (PTRS_PER_PGD - 1)<<3); | ||
981 | i_daddu(p, ptr, ptr, tmp); /* add in pgd offset */ | ||
982 | i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */ | ||
983 | i_ld(p, ptr, 0, ptr); /* get pmd pointer */ | ||
984 | i_dsrl(p, tmp, tmp, PMD_SHIFT-3); /* get pmd offset in bytes */ | ||
985 | i_andi(p, tmp, tmp, (PTRS_PER_PMD - 1)<<3); | ||
986 | i_daddu(p, ptr, ptr, tmp); /* add in pmd offset */ | ||
987 | } | ||
988 | |||
989 | /* | ||
990 | * BVADDR is the faulting address, PTR is scratch. | ||
991 | * PTR will hold the pgd for vmalloc. | ||
992 | */ | ||
993 | static __init void | ||
994 | build_get_pgd_vmalloc64(u32 **p, struct label **l, struct reloc **r, | ||
995 | unsigned int bvaddr, unsigned int ptr) | ||
996 | { | ||
997 | long swpd = (long)swapper_pg_dir; | ||
998 | |||
999 | l_vmalloc(l, *p); | ||
1000 | i_LA(p, ptr, VMALLOC_START); | ||
1001 | i_dsubu(p, bvaddr, bvaddr, ptr); | ||
1002 | |||
1003 | if (in_compat_space_p(swpd) && !rel_lo(swpd)) { | ||
1004 | il_b(p, r, label_vmalloc_done); | ||
1005 | i_lui(p, ptr, rel_hi(swpd)); | ||
1006 | } else { | ||
1007 | i_LA_mostly(p, ptr, swpd); | ||
1008 | il_b(p, r, label_vmalloc_done); | ||
1009 | i_daddiu(p, ptr, ptr, rel_lo(swpd)); | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | #else /* !CONFIG_MIPS64 */ | ||
1014 | |||
1015 | /* | ||
1016 | * TMP and PTR are scratch. | ||
1017 | * TMP will be clobbered, PTR will hold the pgd entry. | ||
1018 | */ | ||
1019 | static __init void __attribute__((unused)) | ||
1020 | build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr) | ||
1021 | { | ||
1022 | long pgdc = (long)pgd_current; | ||
1023 | |||
1024 | /* 32 bit SMP has smp_processor_id() stored in CONTEXT. */ | ||
1025 | #ifdef CONFIG_SMP | ||
1026 | i_mfc0(p, ptr, C0_CONTEXT); | ||
1027 | i_LA_mostly(p, tmp, pgdc); | ||
1028 | i_srl(p, ptr, ptr, 23); | ||
1029 | i_sll(p, ptr, ptr, 2); | ||
1030 | i_addu(p, ptr, tmp, ptr); | ||
1031 | #else | ||
1032 | i_LA_mostly(p, ptr, pgdc); | ||
1033 | #endif | ||
1034 | i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */ | ||
1035 | i_lw(p, ptr, rel_lo(pgdc), ptr); | ||
1036 | i_srl(p, tmp, tmp, PGDIR_SHIFT); /* get pgd only bits */ | ||
1037 | i_sll(p, tmp, tmp, PGD_T_LOG2); | ||
1038 | i_addu(p, ptr, ptr, tmp); /* add in pgd offset */ | ||
1039 | } | ||
1040 | |||
1041 | #endif /* !CONFIG_MIPS64 */ | ||
1042 | |||
1043 | static __init void build_adjust_context(u32 **p, unsigned int ctx) | ||
1044 | { | ||
1045 | unsigned int shift = 4 - (PTE_T_LOG2 + 1); | ||
1046 | unsigned int mask = (PTRS_PER_PTE / 2 - 1) << (PTE_T_LOG2 + 1); | ||
1047 | |||
1048 | switch (current_cpu_data.cputype) { | ||
1049 | case CPU_VR41XX: | ||
1050 | case CPU_VR4111: | ||
1051 | case CPU_VR4121: | ||
1052 | case CPU_VR4122: | ||
1053 | case CPU_VR4131: | ||
1054 | case CPU_VR4181: | ||
1055 | case CPU_VR4181A: | ||
1056 | case CPU_VR4133: | ||
1057 | shift += 2; | ||
1058 | break; | ||
1059 | |||
1060 | default: | ||
1061 | break; | ||
1062 | } | ||
1063 | |||
1064 | if (shift) | ||
1065 | i_SRL(p, ctx, ctx, shift); | ||
1066 | i_andi(p, ctx, ctx, mask); | ||
1067 | } | ||
1068 | |||
1069 | static __init void build_get_ptep(u32 **p, unsigned int tmp, unsigned int ptr) | ||
1070 | { | ||
1071 | /* | ||
1072 | * Bug workaround for the Nevada. It seems as if under certain | ||
1073 | * circumstances the move from cp0_context might produce a | ||
1074 | * bogus result when the mfc0 instruction and its consumer are | ||
1075 | * in a different cacheline or a load instruction, probably any | ||
1076 | * memory reference, is between them. | ||
1077 | */ | ||
1078 | switch (current_cpu_data.cputype) { | ||
1079 | case CPU_NEVADA: | ||
1080 | i_LW(p, ptr, 0, ptr); | ||
1081 | GET_CONTEXT(p, tmp); /* get context reg */ | ||
1082 | break; | ||
1083 | |||
1084 | default: | ||
1085 | GET_CONTEXT(p, tmp); /* get context reg */ | ||
1086 | i_LW(p, ptr, 0, ptr); | ||
1087 | break; | ||
1088 | } | ||
1089 | |||
1090 | build_adjust_context(p, tmp); | ||
1091 | i_ADDU(p, ptr, ptr, tmp); /* add in offset */ | ||
1092 | } | ||
1093 | |||
1094 | static __init void build_update_entries(u32 **p, unsigned int tmp, | ||
1095 | unsigned int ptep) | ||
1096 | { | ||
1097 | /* | ||
1098 | * 64bit address support (36bit on a 32bit CPU) in a 32bit | ||
1099 | * Kernel is a special case. Only a few CPUs use it. | ||
1100 | */ | ||
1101 | #ifdef CONFIG_64BIT_PHYS_ADDR | ||
1102 | if (cpu_has_64bits) { | ||
1103 | i_ld(p, tmp, 0, ptep); /* get even pte */ | ||
1104 | i_ld(p, ptep, sizeof(pte_t), ptep); /* get odd pte */ | ||
1105 | i_dsrl(p, tmp, tmp, 6); /* convert to entrylo0 */ | ||
1106 | i_mtc0(p, tmp, C0_ENTRYLO0); /* load it */ | ||
1107 | i_dsrl(p, ptep, ptep, 6); /* convert to entrylo1 */ | ||
1108 | i_mtc0(p, ptep, C0_ENTRYLO1); /* load it */ | ||
1109 | } else { | ||
1110 | int pte_off_even = sizeof(pte_t) / 2; | ||
1111 | int pte_off_odd = pte_off_even + sizeof(pte_t); | ||
1112 | |||
1113 | /* The pte entries are pre-shifted */ | ||
1114 | i_lw(p, tmp, pte_off_even, ptep); /* get even pte */ | ||
1115 | i_mtc0(p, tmp, C0_ENTRYLO0); /* load it */ | ||
1116 | i_lw(p, ptep, pte_off_odd, ptep); /* get odd pte */ | ||
1117 | i_mtc0(p, ptep, C0_ENTRYLO1); /* load it */ | ||
1118 | } | ||
1119 | #else | ||
1120 | i_LW(p, tmp, 0, ptep); /* get even pte */ | ||
1121 | i_LW(p, ptep, sizeof(pte_t), ptep); /* get odd pte */ | ||
1122 | if (r45k_bvahwbug()) | ||
1123 | build_tlb_probe_entry(p); | ||
1124 | i_SRL(p, tmp, tmp, 6); /* convert to entrylo0 */ | ||
1125 | if (r4k_250MHZhwbug()) | ||
1126 | i_mtc0(p, 0, C0_ENTRYLO0); | ||
1127 | i_mtc0(p, tmp, C0_ENTRYLO0); /* load it */ | ||
1128 | i_SRL(p, ptep, ptep, 6); /* convert to entrylo1 */ | ||
1129 | if (r45k_bvahwbug()) | ||
1130 | i_mfc0(p, tmp, C0_INDEX); | ||
1131 | if (r4k_250MHZhwbug()) | ||
1132 | i_mtc0(p, 0, C0_ENTRYLO1); | ||
1133 | i_mtc0(p, ptep, C0_ENTRYLO1); /* load it */ | ||
1134 | #endif | ||
1135 | } | ||
1136 | |||
1137 | static void __init build_r4000_tlb_refill_handler(void) | ||
1138 | { | ||
1139 | u32 *p = tlb_handler; | ||
1140 | struct label *l = labels; | ||
1141 | struct reloc *r = relocs; | ||
1142 | u32 *f; | ||
1143 | unsigned int final_len; | ||
1144 | |||
1145 | memset(tlb_handler, 0, sizeof(tlb_handler)); | ||
1146 | memset(labels, 0, sizeof(labels)); | ||
1147 | memset(relocs, 0, sizeof(relocs)); | ||
1148 | memset(final_handler, 0, sizeof(final_handler)); | ||
1149 | |||
1150 | /* | ||
1151 | * create the plain linear handler | ||
1152 | */ | ||
1153 | if (bcm1250_m3_war()) { | ||
1154 | i_MFC0(&p, K0, C0_BADVADDR); | ||
1155 | i_MFC0(&p, K1, C0_ENTRYHI); | ||
1156 | i_xor(&p, K0, K0, K1); | ||
1157 | i_SRL(&p, K0, K0, PAGE_SHIFT + 1); | ||
1158 | il_bnez(&p, &r, K0, label_leave); | ||
1159 | /* No need for i_nop */ | ||
1160 | } | ||
1161 | |||
1162 | #ifdef CONFIG_MIPS64 | ||
1163 | build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */ | ||
1164 | #else | ||
1165 | build_get_pgde32(&p, K0, K1); /* get pgd in K1 */ | ||
1166 | #endif | ||
1167 | |||
1168 | build_get_ptep(&p, K0, K1); | ||
1169 | build_update_entries(&p, K0, K1); | ||
1170 | build_tlb_write_entry(&p, &l, &r, tlb_random); | ||
1171 | l_leave(&l, p); | ||
1172 | i_eret(&p); /* return from trap */ | ||
1173 | |||
1174 | #ifdef CONFIG_MIPS64 | ||
1175 | build_get_pgd_vmalloc64(&p, &l, &r, K0, K1); | ||
1176 | #endif | ||
1177 | |||
1178 | /* | ||
1179 | * Overflow check: For the 64bit handler, we need at least one | ||
1180 | * free instruction slot for the wrap-around branch. In worst | ||
1181 | * case, if the intended insertion point is a delay slot, we | ||
1182 | * need three, with the the second nop'ed and the third being | ||
1183 | * unused. | ||
1184 | */ | ||
1185 | #ifdef CONFIG_MIPS32 | ||
1186 | if ((p - tlb_handler) > 64) | ||
1187 | panic("TLB refill handler space exceeded"); | ||
1188 | #else | ||
1189 | if (((p - tlb_handler) > 63) | ||
1190 | || (((p - tlb_handler) > 61) | ||
1191 | && insn_has_bdelay(relocs, tlb_handler + 29))) | ||
1192 | panic("TLB refill handler space exceeded"); | ||
1193 | #endif | ||
1194 | |||
1195 | /* | ||
1196 | * Now fold the handler in the TLB refill handler space. | ||
1197 | */ | ||
1198 | #ifdef CONFIG_MIPS32 | ||
1199 | f = final_handler; | ||
1200 | /* Simplest case, just copy the handler. */ | ||
1201 | copy_handler(relocs, labels, tlb_handler, p, f); | ||
1202 | final_len = p - tlb_handler; | ||
1203 | #else /* CONFIG_MIPS64 */ | ||
1204 | f = final_handler + 32; | ||
1205 | if ((p - tlb_handler) <= 32) { | ||
1206 | /* Just copy the handler. */ | ||
1207 | copy_handler(relocs, labels, tlb_handler, p, f); | ||
1208 | final_len = p - tlb_handler; | ||
1209 | } else { | ||
1210 | u32 *split = tlb_handler + 30; | ||
1211 | |||
1212 | /* | ||
1213 | * Find the split point. | ||
1214 | */ | ||
1215 | if (insn_has_bdelay(relocs, split - 1)) | ||
1216 | split--; | ||
1217 | |||
1218 | /* Copy first part of the handler. */ | ||
1219 | copy_handler(relocs, labels, tlb_handler, split, f); | ||
1220 | f += split - tlb_handler; | ||
1221 | |||
1222 | /* Insert branch. */ | ||
1223 | l_split(&l, final_handler); | ||
1224 | il_b(&f, &r, label_split); | ||
1225 | if (insn_has_bdelay(relocs, split)) | ||
1226 | i_nop(&f); | ||
1227 | else { | ||
1228 | copy_handler(relocs, labels, split, split + 1, f); | ||
1229 | move_labels(labels, f, f + 1, -1); | ||
1230 | f++; | ||
1231 | split++; | ||
1232 | } | ||
1233 | |||
1234 | /* Copy the rest of the handler. */ | ||
1235 | copy_handler(relocs, labels, split, p, final_handler); | ||
1236 | final_len = (f - (final_handler + 32)) + (p - split); | ||
1237 | } | ||
1238 | #endif /* CONFIG_MIPS64 */ | ||
1239 | |||
1240 | resolve_relocs(relocs, labels); | ||
1241 | printk("Synthesized TLB refill handler (%u instructions).\n", | ||
1242 | final_len); | ||
1243 | |||
1244 | #ifdef DEBUG_TLB | ||
1245 | { | ||
1246 | int i; | ||
1247 | |||
1248 | for (i = 0; i < 64; i++) | ||
1249 | printk("%08x\n", final_handler[i]); | ||
1250 | } | ||
1251 | #endif | ||
1252 | |||
1253 | memcpy((void *)CAC_BASE, final_handler, 0x100); | ||
1254 | flush_icache_range(CAC_BASE, CAC_BASE + 0x100); | ||
1255 | } | ||
1256 | |||
1257 | /* | ||
1258 | * TLB load/store/modify handlers. | ||
1259 | * | ||
1260 | * Only the fastpath gets synthesized at runtime, the slowpath for | ||
1261 | * do_page_fault remains normal asm. | ||
1262 | */ | ||
1263 | extern void tlb_do_page_fault_0(void); | ||
1264 | extern void tlb_do_page_fault_1(void); | ||
1265 | |||
1266 | #define __tlb_handler_align \ | ||
1267 | __attribute__((__aligned__(1 << CONFIG_MIPS_L1_CACHE_SHIFT))) | ||
1268 | |||
1269 | /* | ||
1270 | * 128 instructions for the fastpath handler is generous and should | ||
1271 | * never be exceeded. | ||
1272 | */ | ||
1273 | #define FASTPATH_SIZE 128 | ||
1274 | |||
1275 | u32 __tlb_handler_align handle_tlbl[FASTPATH_SIZE]; | ||
1276 | u32 __tlb_handler_align handle_tlbs[FASTPATH_SIZE]; | ||
1277 | u32 __tlb_handler_align handle_tlbm[FASTPATH_SIZE]; | ||
1278 | |||
1279 | static void __init | ||
1280 | iPTE_LW(u32 **p, struct label **l, unsigned int pte, int offset, | ||
1281 | unsigned int ptr) | ||
1282 | { | ||
1283 | #ifdef CONFIG_SMP | ||
1284 | # ifdef CONFIG_64BIT_PHYS_ADDR | ||
1285 | if (cpu_has_64bits) | ||
1286 | i_lld(p, pte, offset, ptr); | ||
1287 | else | ||
1288 | # endif | ||
1289 | i_LL(p, pte, offset, ptr); | ||
1290 | #else | ||
1291 | # ifdef CONFIG_64BIT_PHYS_ADDR | ||
1292 | if (cpu_has_64bits) | ||
1293 | i_ld(p, pte, offset, ptr); | ||
1294 | else | ||
1295 | # endif | ||
1296 | i_LW(p, pte, offset, ptr); | ||
1297 | #endif | ||
1298 | } | ||
1299 | |||
1300 | static void __init | ||
1301 | iPTE_SW(u32 **p, struct reloc **r, unsigned int pte, int offset, | ||
1302 | unsigned int ptr) | ||
1303 | { | ||
1304 | #ifdef CONFIG_SMP | ||
1305 | # ifdef CONFIG_64BIT_PHYS_ADDR | ||
1306 | if (cpu_has_64bits) | ||
1307 | i_scd(p, pte, offset, ptr); | ||
1308 | else | ||
1309 | # endif | ||
1310 | i_SC(p, pte, offset, ptr); | ||
1311 | |||
1312 | if (r10000_llsc_war()) | ||
1313 | il_beqzl(p, r, pte, label_smp_pgtable_change); | ||
1314 | else | ||
1315 | il_beqz(p, r, pte, label_smp_pgtable_change); | ||
1316 | |||
1317 | # ifdef CONFIG_64BIT_PHYS_ADDR | ||
1318 | if (!cpu_has_64bits) { | ||
1319 | /* no i_nop needed */ | ||
1320 | i_ll(p, pte, sizeof(pte_t) / 2, ptr); | ||
1321 | i_ori(p, pte, pte, _PAGE_VALID); | ||
1322 | i_sc(p, pte, sizeof(pte_t) / 2, ptr); | ||
1323 | il_beqz(p, r, pte, label_smp_pgtable_change); | ||
1324 | /* no i_nop needed */ | ||
1325 | i_lw(p, pte, 0, ptr); | ||
1326 | } else | ||
1327 | i_nop(p); | ||
1328 | # else | ||
1329 | i_nop(p); | ||
1330 | # endif | ||
1331 | #else | ||
1332 | # ifdef CONFIG_64BIT_PHYS_ADDR | ||
1333 | if (cpu_has_64bits) | ||
1334 | i_sd(p, pte, offset, ptr); | ||
1335 | else | ||
1336 | # endif | ||
1337 | i_SW(p, pte, offset, ptr); | ||
1338 | |||
1339 | # ifdef CONFIG_64BIT_PHYS_ADDR | ||
1340 | if (!cpu_has_64bits) { | ||
1341 | i_lw(p, pte, sizeof(pte_t) / 2, ptr); | ||
1342 | i_ori(p, pte, pte, _PAGE_VALID); | ||
1343 | i_sw(p, pte, sizeof(pte_t) / 2, ptr); | ||
1344 | i_lw(p, pte, 0, ptr); | ||
1345 | } | ||
1346 | # endif | ||
1347 | #endif | ||
1348 | } | ||
1349 | |||
1350 | /* | ||
1351 | * Check if PTE is present, if not then jump to LABEL. PTR points to | ||
1352 | * the page table where this PTE is located, PTE will be re-loaded | ||
1353 | * with it's original value. | ||
1354 | */ | ||
1355 | static void __init | ||
1356 | build_pte_present(u32 **p, struct label **l, struct reloc **r, | ||
1357 | unsigned int pte, unsigned int ptr, enum label_id lid) | ||
1358 | { | ||
1359 | i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_READ); | ||
1360 | i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_READ); | ||
1361 | il_bnez(p, r, pte, lid); | ||
1362 | iPTE_LW(p, l, pte, 0, ptr); | ||
1363 | } | ||
1364 | |||
1365 | /* Make PTE valid, store result in PTR. */ | ||
1366 | static void __init | ||
1367 | build_make_valid(u32 **p, struct reloc **r, unsigned int pte, | ||
1368 | unsigned int ptr) | ||
1369 | { | ||
1370 | i_ori(p, pte, pte, _PAGE_VALID | _PAGE_ACCESSED); | ||
1371 | iPTE_SW(p, r, pte, 0, ptr); | ||
1372 | } | ||
1373 | |||
1374 | /* | ||
1375 | * Check if PTE can be written to, if not branch to LABEL. Regardless | ||
1376 | * restore PTE with value from PTR when done. | ||
1377 | */ | ||
1378 | static void __init | ||
1379 | build_pte_writable(u32 **p, struct label **l, struct reloc **r, | ||
1380 | unsigned int pte, unsigned int ptr, enum label_id lid) | ||
1381 | { | ||
1382 | i_andi(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE); | ||
1383 | i_xori(p, pte, pte, _PAGE_PRESENT | _PAGE_WRITE); | ||
1384 | il_bnez(p, r, pte, lid); | ||
1385 | iPTE_LW(p, l, pte, 0, ptr); | ||
1386 | } | ||
1387 | |||
1388 | /* Make PTE writable, update software status bits as well, then store | ||
1389 | * at PTR. | ||
1390 | */ | ||
1391 | static void __init | ||
1392 | build_make_write(u32 **p, struct reloc **r, unsigned int pte, | ||
1393 | unsigned int ptr) | ||
1394 | { | ||
1395 | i_ori(p, pte, pte, | ||
1396 | _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY); | ||
1397 | iPTE_SW(p, r, pte, 0, ptr); | ||
1398 | } | ||
1399 | |||
1400 | /* | ||
1401 | * Check if PTE can be modified, if not branch to LABEL. Regardless | ||
1402 | * restore PTE with value from PTR when done. | ||
1403 | */ | ||
1404 | static void __init | ||
1405 | build_pte_modifiable(u32 **p, struct label **l, struct reloc **r, | ||
1406 | unsigned int pte, unsigned int ptr, enum label_id lid) | ||
1407 | { | ||
1408 | i_andi(p, pte, pte, _PAGE_WRITE); | ||
1409 | il_beqz(p, r, pte, lid); | ||
1410 | iPTE_LW(p, l, pte, 0, ptr); | ||
1411 | } | ||
1412 | |||
1413 | /* | ||
1414 | * R3000 style TLB load/store/modify handlers. | ||
1415 | */ | ||
1416 | |||
1417 | /* This places the pte in the page table at PTR into ENTRYLO0. */ | ||
1418 | static void __init | ||
1419 | build_r3000_pte_reload(u32 **p, unsigned int ptr) | ||
1420 | { | ||
1421 | i_lw(p, ptr, 0, ptr); | ||
1422 | i_nop(p); /* load delay */ | ||
1423 | i_mtc0(p, ptr, C0_ENTRYLO0); | ||
1424 | i_nop(p); /* cp0 delay */ | ||
1425 | } | ||
1426 | |||
1427 | /* | ||
1428 | * The index register may have the probe fail bit set, | ||
1429 | * because we would trap on access kseg2, i.e. without refill. | ||
1430 | */ | ||
1431 | static void __init | ||
1432 | build_r3000_tlb_write(u32 **p, struct label **l, struct reloc **r, | ||
1433 | unsigned int tmp) | ||
1434 | { | ||
1435 | i_mfc0(p, tmp, C0_INDEX); | ||
1436 | i_nop(p); /* cp0 delay */ | ||
1437 | il_bltz(p, r, tmp, label_r3000_write_probe_fail); | ||
1438 | i_nop(p); /* branch delay */ | ||
1439 | i_tlbwi(p); | ||
1440 | il_b(p, r, label_r3000_write_probe_ok); | ||
1441 | i_nop(p); /* branch delay */ | ||
1442 | l_r3000_write_probe_fail(l, *p); | ||
1443 | i_tlbwr(p); | ||
1444 | l_r3000_write_probe_ok(l, *p); | ||
1445 | } | ||
1446 | |||
1447 | static void __init | ||
1448 | build_r3000_tlbchange_handler_head(u32 **p, unsigned int pte, | ||
1449 | unsigned int ptr) | ||
1450 | { | ||
1451 | long pgdc = (long)pgd_current; | ||
1452 | |||
1453 | i_mfc0(p, pte, C0_BADVADDR); | ||
1454 | i_lui(p, ptr, rel_hi(pgdc)); /* cp0 delay */ | ||
1455 | i_lw(p, ptr, rel_lo(pgdc), ptr); | ||
1456 | i_srl(p, pte, pte, 22); /* load delay */ | ||
1457 | i_sll(p, pte, pte, 2); | ||
1458 | i_addu(p, ptr, ptr, pte); | ||
1459 | i_mfc0(p, pte, C0_CONTEXT); | ||
1460 | i_lw(p, ptr, 0, ptr); /* cp0 delay */ | ||
1461 | i_andi(p, pte, pte, 0xffc); /* load delay */ | ||
1462 | i_addu(p, ptr, ptr, pte); | ||
1463 | i_lw(p, pte, 0, ptr); | ||
1464 | i_nop(p); /* load delay */ | ||
1465 | i_tlbp(p); | ||
1466 | } | ||
1467 | |||
1468 | static void __init | ||
1469 | build_r3000_tlbchange_handler_tail(u32 **p, unsigned int tmp) | ||
1470 | { | ||
1471 | i_mfc0(p, tmp, C0_EPC); | ||
1472 | i_nop(p); /* cp0 delay */ | ||
1473 | i_jr(p, tmp); | ||
1474 | i_rfe(p); /* branch delay */ | ||
1475 | } | ||
1476 | |||
1477 | static void __init build_r3000_tlb_load_handler(void) | ||
1478 | { | ||
1479 | u32 *p = handle_tlbl; | ||
1480 | struct label *l = labels; | ||
1481 | struct reloc *r = relocs; | ||
1482 | |||
1483 | memset(handle_tlbl, 0, sizeof(handle_tlbl)); | ||
1484 | memset(labels, 0, sizeof(labels)); | ||
1485 | memset(relocs, 0, sizeof(relocs)); | ||
1486 | |||
1487 | build_r3000_tlbchange_handler_head(&p, K0, K1); | ||
1488 | build_pte_present(&p, &l, &r, K0, K1, label_nopage_tlbl); | ||
1489 | build_make_valid(&p, &r, K0, K1); | ||
1490 | build_r3000_pte_reload(&p, K1); | ||
1491 | build_r3000_tlb_write(&p, &l, &r, K0); | ||
1492 | build_r3000_tlbchange_handler_tail(&p, K0); | ||
1493 | |||
1494 | l_nopage_tlbl(&l, p); | ||
1495 | i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff); | ||
1496 | i_nop(&p); | ||
1497 | |||
1498 | if ((p - handle_tlbl) > FASTPATH_SIZE) | ||
1499 | panic("TLB load handler fastpath space exceeded"); | ||
1500 | |||
1501 | resolve_relocs(relocs, labels); | ||
1502 | printk("Synthesized TLB load handler fastpath (%u instructions).\n", | ||
1503 | (unsigned int)(p - handle_tlbl)); | ||
1504 | |||
1505 | #ifdef DEBUG_TLB | ||
1506 | { | ||
1507 | int i; | ||
1508 | |||
1509 | for (i = 0; i < FASTPATH_SIZE; i++) | ||
1510 | printk("%08x\n", handle_tlbl[i]); | ||
1511 | } | ||
1512 | #endif | ||
1513 | |||
1514 | flush_icache_range((unsigned long)handle_tlbl, | ||
1515 | (unsigned long)handle_tlbl + FASTPATH_SIZE * sizeof(u32)); | ||
1516 | } | ||
1517 | |||
1518 | static void __init build_r3000_tlb_store_handler(void) | ||
1519 | { | ||
1520 | u32 *p = handle_tlbs; | ||
1521 | struct label *l = labels; | ||
1522 | struct reloc *r = relocs; | ||
1523 | |||
1524 | memset(handle_tlbs, 0, sizeof(handle_tlbs)); | ||
1525 | memset(labels, 0, sizeof(labels)); | ||
1526 | memset(relocs, 0, sizeof(relocs)); | ||
1527 | |||
1528 | build_r3000_tlbchange_handler_head(&p, K0, K1); | ||
1529 | build_pte_writable(&p, &l, &r, K0, K1, label_nopage_tlbs); | ||
1530 | build_make_write(&p, &r, K0, K1); | ||
1531 | build_r3000_pte_reload(&p, K1); | ||
1532 | build_r3000_tlb_write(&p, &l, &r, K0); | ||
1533 | build_r3000_tlbchange_handler_tail(&p, K0); | ||
1534 | |||
1535 | l_nopage_tlbs(&l, p); | ||
1536 | i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); | ||
1537 | i_nop(&p); | ||
1538 | |||
1539 | if ((p - handle_tlbs) > FASTPATH_SIZE) | ||
1540 | panic("TLB store handler fastpath space exceeded"); | ||
1541 | |||
1542 | resolve_relocs(relocs, labels); | ||
1543 | printk("Synthesized TLB store handler fastpath (%u instructions).\n", | ||
1544 | (unsigned int)(p - handle_tlbs)); | ||
1545 | |||
1546 | #ifdef DEBUG_TLB | ||
1547 | { | ||
1548 | int i; | ||
1549 | |||
1550 | for (i = 0; i < FASTPATH_SIZE; i++) | ||
1551 | printk("%08x\n", handle_tlbs[i]); | ||
1552 | } | ||
1553 | #endif | ||
1554 | |||
1555 | flush_icache_range((unsigned long)handle_tlbs, | ||
1556 | (unsigned long)handle_tlbs + FASTPATH_SIZE * sizeof(u32)); | ||
1557 | } | ||
1558 | |||
1559 | static void __init build_r3000_tlb_modify_handler(void) | ||
1560 | { | ||
1561 | u32 *p = handle_tlbm; | ||
1562 | struct label *l = labels; | ||
1563 | struct reloc *r = relocs; | ||
1564 | |||
1565 | memset(handle_tlbm, 0, sizeof(handle_tlbm)); | ||
1566 | memset(labels, 0, sizeof(labels)); | ||
1567 | memset(relocs, 0, sizeof(relocs)); | ||
1568 | |||
1569 | build_r3000_tlbchange_handler_head(&p, K0, K1); | ||
1570 | build_pte_modifiable(&p, &l, &r, K0, K1, label_nopage_tlbm); | ||
1571 | build_make_write(&p, &r, K0, K1); | ||
1572 | build_r3000_pte_reload(&p, K1); | ||
1573 | i_tlbwi(&p); | ||
1574 | build_r3000_tlbchange_handler_tail(&p, K0); | ||
1575 | |||
1576 | l_nopage_tlbm(&l, p); | ||
1577 | i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); | ||
1578 | i_nop(&p); | ||
1579 | |||
1580 | if ((p - handle_tlbm) > FASTPATH_SIZE) | ||
1581 | panic("TLB modify handler fastpath space exceeded"); | ||
1582 | |||
1583 | resolve_relocs(relocs, labels); | ||
1584 | printk("Synthesized TLB modify handler fastpath (%u instructions).\n", | ||
1585 | (unsigned int)(p - handle_tlbm)); | ||
1586 | |||
1587 | #ifdef DEBUG_TLB | ||
1588 | { | ||
1589 | int i; | ||
1590 | |||
1591 | for (i = 0; i < FASTPATH_SIZE; i++) | ||
1592 | printk("%08x\n", handle_tlbm[i]); | ||
1593 | } | ||
1594 | #endif | ||
1595 | |||
1596 | flush_icache_range((unsigned long)handle_tlbm, | ||
1597 | (unsigned long)handle_tlbm + FASTPATH_SIZE * sizeof(u32)); | ||
1598 | } | ||
1599 | |||
1600 | /* | ||
1601 | * R4000 style TLB load/store/modify handlers. | ||
1602 | */ | ||
1603 | static void __init | ||
1604 | build_r4000_tlbchange_handler_head(u32 **p, struct label **l, | ||
1605 | struct reloc **r, unsigned int pte, | ||
1606 | unsigned int ptr) | ||
1607 | { | ||
1608 | #ifdef CONFIG_MIPS64 | ||
1609 | build_get_pmde64(p, l, r, pte, ptr); /* get pmd in ptr */ | ||
1610 | #else | ||
1611 | build_get_pgde32(p, pte, ptr); /* get pgd in ptr */ | ||
1612 | #endif | ||
1613 | |||
1614 | i_MFC0(p, pte, C0_BADVADDR); | ||
1615 | i_LW(p, ptr, 0, ptr); | ||
1616 | i_SRL(p, pte, pte, PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2); | ||
1617 | i_andi(p, pte, pte, (PTRS_PER_PTE - 1) << PTE_T_LOG2); | ||
1618 | i_ADDU(p, ptr, ptr, pte); | ||
1619 | |||
1620 | #ifdef CONFIG_SMP | ||
1621 | l_smp_pgtable_change(l, *p); | ||
1622 | # endif | ||
1623 | iPTE_LW(p, l, pte, 0, ptr); /* get even pte */ | ||
1624 | build_tlb_probe_entry(p); | ||
1625 | } | ||
1626 | |||
1627 | static void __init | ||
1628 | build_r4000_tlbchange_handler_tail(u32 **p, struct label **l, | ||
1629 | struct reloc **r, unsigned int tmp, | ||
1630 | unsigned int ptr) | ||
1631 | { | ||
1632 | i_ori(p, ptr, ptr, sizeof(pte_t)); | ||
1633 | i_xori(p, ptr, ptr, sizeof(pte_t)); | ||
1634 | build_update_entries(p, tmp, ptr); | ||
1635 | build_tlb_write_entry(p, l, r, tlb_indexed); | ||
1636 | l_leave(l, *p); | ||
1637 | i_eret(p); /* return from trap */ | ||
1638 | |||
1639 | #ifdef CONFIG_MIPS64 | ||
1640 | build_get_pgd_vmalloc64(p, l, r, tmp, ptr); | ||
1641 | #endif | ||
1642 | } | ||
1643 | |||
1644 | static void __init build_r4000_tlb_load_handler(void) | ||
1645 | { | ||
1646 | u32 *p = handle_tlbl; | ||
1647 | struct label *l = labels; | ||
1648 | struct reloc *r = relocs; | ||
1649 | |||
1650 | memset(handle_tlbl, 0, sizeof(handle_tlbl)); | ||
1651 | memset(labels, 0, sizeof(labels)); | ||
1652 | memset(relocs, 0, sizeof(relocs)); | ||
1653 | |||
1654 | if (bcm1250_m3_war()) { | ||
1655 | i_MFC0(&p, K0, C0_BADVADDR); | ||
1656 | i_MFC0(&p, K1, C0_ENTRYHI); | ||
1657 | i_xor(&p, K0, K0, K1); | ||
1658 | i_SRL(&p, K0, K0, PAGE_SHIFT + 1); | ||
1659 | il_bnez(&p, &r, K0, label_leave); | ||
1660 | /* No need for i_nop */ | ||
1661 | } | ||
1662 | |||
1663 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); | ||
1664 | build_pte_present(&p, &l, &r, K0, K1, label_nopage_tlbl); | ||
1665 | build_make_valid(&p, &r, K0, K1); | ||
1666 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); | ||
1667 | |||
1668 | l_nopage_tlbl(&l, p); | ||
1669 | i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff); | ||
1670 | i_nop(&p); | ||
1671 | |||
1672 | if ((p - handle_tlbl) > FASTPATH_SIZE) | ||
1673 | panic("TLB load handler fastpath space exceeded"); | ||
1674 | |||
1675 | resolve_relocs(relocs, labels); | ||
1676 | printk("Synthesized TLB load handler fastpath (%u instructions).\n", | ||
1677 | (unsigned int)(p - handle_tlbl)); | ||
1678 | |||
1679 | #ifdef DEBUG_TLB | ||
1680 | { | ||
1681 | int i; | ||
1682 | |||
1683 | for (i = 0; i < FASTPATH_SIZE; i++) | ||
1684 | printk("%08x\n", handle_tlbl[i]); | ||
1685 | } | ||
1686 | #endif | ||
1687 | |||
1688 | flush_icache_range((unsigned long)handle_tlbl, | ||
1689 | (unsigned long)handle_tlbl + FASTPATH_SIZE * sizeof(u32)); | ||
1690 | } | ||
1691 | |||
1692 | static void __init build_r4000_tlb_store_handler(void) | ||
1693 | { | ||
1694 | u32 *p = handle_tlbs; | ||
1695 | struct label *l = labels; | ||
1696 | struct reloc *r = relocs; | ||
1697 | |||
1698 | memset(handle_tlbs, 0, sizeof(handle_tlbs)); | ||
1699 | memset(labels, 0, sizeof(labels)); | ||
1700 | memset(relocs, 0, sizeof(relocs)); | ||
1701 | |||
1702 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); | ||
1703 | build_pte_writable(&p, &l, &r, K0, K1, label_nopage_tlbs); | ||
1704 | build_make_write(&p, &r, K0, K1); | ||
1705 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); | ||
1706 | |||
1707 | l_nopage_tlbs(&l, p); | ||
1708 | i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); | ||
1709 | i_nop(&p); | ||
1710 | |||
1711 | if ((p - handle_tlbs) > FASTPATH_SIZE) | ||
1712 | panic("TLB store handler fastpath space exceeded"); | ||
1713 | |||
1714 | resolve_relocs(relocs, labels); | ||
1715 | printk("Synthesized TLB store handler fastpath (%u instructions).\n", | ||
1716 | (unsigned int)(p - handle_tlbs)); | ||
1717 | |||
1718 | #ifdef DEBUG_TLB | ||
1719 | { | ||
1720 | int i; | ||
1721 | |||
1722 | for (i = 0; i < FASTPATH_SIZE; i++) | ||
1723 | printk("%08x\n", handle_tlbs[i]); | ||
1724 | } | ||
1725 | #endif | ||
1726 | |||
1727 | flush_icache_range((unsigned long)handle_tlbs, | ||
1728 | (unsigned long)handle_tlbs + FASTPATH_SIZE * sizeof(u32)); | ||
1729 | } | ||
1730 | |||
1731 | static void __init build_r4000_tlb_modify_handler(void) | ||
1732 | { | ||
1733 | u32 *p = handle_tlbm; | ||
1734 | struct label *l = labels; | ||
1735 | struct reloc *r = relocs; | ||
1736 | |||
1737 | memset(handle_tlbm, 0, sizeof(handle_tlbm)); | ||
1738 | memset(labels, 0, sizeof(labels)); | ||
1739 | memset(relocs, 0, sizeof(relocs)); | ||
1740 | |||
1741 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); | ||
1742 | build_pte_modifiable(&p, &l, &r, K0, K1, label_nopage_tlbm); | ||
1743 | /* Present and writable bits set, set accessed and dirty bits. */ | ||
1744 | build_make_write(&p, &r, K0, K1); | ||
1745 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); | ||
1746 | |||
1747 | l_nopage_tlbm(&l, p); | ||
1748 | i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff); | ||
1749 | i_nop(&p); | ||
1750 | |||
1751 | if ((p - handle_tlbm) > FASTPATH_SIZE) | ||
1752 | panic("TLB modify handler fastpath space exceeded"); | ||
1753 | |||
1754 | resolve_relocs(relocs, labels); | ||
1755 | printk("Synthesized TLB modify handler fastpath (%u instructions).\n", | ||
1756 | (unsigned int)(p - handle_tlbm)); | ||
1757 | |||
1758 | #ifdef DEBUG_TLB | ||
1759 | { | ||
1760 | int i; | ||
1761 | |||
1762 | for (i = 0; i < FASTPATH_SIZE; i++) | ||
1763 | printk("%08x\n", handle_tlbm[i]); | ||
1764 | } | ||
1765 | #endif | ||
1766 | |||
1767 | flush_icache_range((unsigned long)handle_tlbm, | ||
1768 | (unsigned long)handle_tlbm + FASTPATH_SIZE * sizeof(u32)); | ||
1769 | } | ||
1770 | |||
1771 | void __init build_tlb_refill_handler(void) | ||
1772 | { | ||
1773 | /* | ||
1774 | * The refill handler is generated per-CPU, multi-node systems | ||
1775 | * may have local storage for it. The other handlers are only | ||
1776 | * needed once. | ||
1777 | */ | ||
1778 | static int run_once = 0; | ||
1779 | |||
1780 | switch (current_cpu_data.cputype) { | ||
1781 | case CPU_R2000: | ||
1782 | case CPU_R3000: | ||
1783 | case CPU_R3000A: | ||
1784 | case CPU_R3081E: | ||
1785 | case CPU_TX3912: | ||
1786 | case CPU_TX3922: | ||
1787 | case CPU_TX3927: | ||
1788 | build_r3000_tlb_refill_handler(); | ||
1789 | if (!run_once) { | ||
1790 | build_r3000_tlb_load_handler(); | ||
1791 | build_r3000_tlb_store_handler(); | ||
1792 | build_r3000_tlb_modify_handler(); | ||
1793 | run_once++; | ||
1794 | } | ||
1795 | break; | ||
1796 | |||
1797 | case CPU_R6000: | ||
1798 | case CPU_R6000A: | ||
1799 | panic("No R6000 TLB refill handler yet"); | ||
1800 | break; | ||
1801 | |||
1802 | case CPU_R8000: | ||
1803 | panic("No R8000 TLB refill handler yet"); | ||
1804 | break; | ||
1805 | |||
1806 | default: | ||
1807 | build_r4000_tlb_refill_handler(); | ||
1808 | if (!run_once) { | ||
1809 | build_r4000_tlb_load_handler(); | ||
1810 | build_r4000_tlb_store_handler(); | ||
1811 | build_r4000_tlb_modify_handler(); | ||
1812 | run_once++; | ||
1813 | } | ||
1814 | } | ||
1815 | } | ||