diff options
Diffstat (limited to 'arch/mips/mm/pg-r4k.c')
-rw-r--r-- | arch/mips/mm/pg-r4k.c | 534 |
1 files changed, 0 insertions, 534 deletions
diff --git a/arch/mips/mm/pg-r4k.c b/arch/mips/mm/pg-r4k.c deleted file mode 100644 index 455dedb5b39e..000000000000 --- a/arch/mips/mm/pg-r4k.c +++ /dev/null | |||
@@ -1,534 +0,0 @@ | |||
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 | * Copyright (C) 2007 Maciej W. Rozycki | ||
8 | */ | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/proc_fs.h> | ||
15 | |||
16 | #include <asm/bugs.h> | ||
17 | #include <asm/cacheops.h> | ||
18 | #include <asm/inst.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/page.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/prefetch.h> | ||
23 | #include <asm/system.h> | ||
24 | #include <asm/bootinfo.h> | ||
25 | #include <asm/mipsregs.h> | ||
26 | #include <asm/mmu_context.h> | ||
27 | #include <asm/cpu.h> | ||
28 | #include <asm/war.h> | ||
29 | |||
30 | #define half_scache_line_size() (cpu_scache_line_size() >> 1) | ||
31 | #define cpu_is_r4600_v1_x() ((read_c0_prid() & 0xfffffff0) == 0x00002010) | ||
32 | #define cpu_is_r4600_v2_x() ((read_c0_prid() & 0xfffffff0) == 0x00002020) | ||
33 | |||
34 | |||
35 | /* | ||
36 | * Maximum sizes: | ||
37 | * | ||
38 | * R4000 128 bytes S-cache: 0x58 bytes | ||
39 | * R4600 v1.7: 0x5c bytes | ||
40 | * R4600 v2.0: 0x60 bytes | ||
41 | * With prefetching, 16 byte strides 0xa0 bytes | ||
42 | */ | ||
43 | |||
44 | static unsigned int clear_page_array[0x130 / 4]; | ||
45 | |||
46 | void clear_page(void * page) __attribute__((alias("clear_page_array"))); | ||
47 | |||
48 | EXPORT_SYMBOL(clear_page); | ||
49 | |||
50 | /* | ||
51 | * Maximum sizes: | ||
52 | * | ||
53 | * R4000 128 bytes S-cache: 0x11c bytes | ||
54 | * R4600 v1.7: 0x080 bytes | ||
55 | * R4600 v2.0: 0x07c bytes | ||
56 | * With prefetching, 16 byte strides 0x0b8 bytes | ||
57 | */ | ||
58 | static unsigned int copy_page_array[0x148 / 4]; | ||
59 | |||
60 | void copy_page(void *to, void *from) __attribute__((alias("copy_page_array"))); | ||
61 | |||
62 | EXPORT_SYMBOL(copy_page); | ||
63 | |||
64 | /* | ||
65 | * This is suboptimal for 32-bit kernels; we assume that R10000 is only used | ||
66 | * with 64-bit kernels. The prefetch offsets have been experimentally tuned | ||
67 | * an Origin 200. | ||
68 | */ | ||
69 | static int pref_offset_clear __cpuinitdata = 512; | ||
70 | static int pref_offset_copy __cpuinitdata = 256; | ||
71 | |||
72 | static unsigned int pref_src_mode __cpuinitdata; | ||
73 | static unsigned int pref_dst_mode __cpuinitdata; | ||
74 | |||
75 | static int load_offset __cpuinitdata; | ||
76 | static int store_offset __cpuinitdata; | ||
77 | |||
78 | static unsigned int __cpuinitdata *dest, *epc; | ||
79 | |||
80 | static unsigned int instruction_pending; | ||
81 | static union mips_instruction delayed_mi; | ||
82 | |||
83 | static void __cpuinit emit_instruction(union mips_instruction mi) | ||
84 | { | ||
85 | if (instruction_pending) | ||
86 | *epc++ = delayed_mi.word; | ||
87 | |||
88 | instruction_pending = 1; | ||
89 | delayed_mi = mi; | ||
90 | } | ||
91 | |||
92 | static inline void flush_delay_slot_or_nop(void) | ||
93 | { | ||
94 | if (instruction_pending) { | ||
95 | *epc++ = delayed_mi.word; | ||
96 | instruction_pending = 0; | ||
97 | return; | ||
98 | } | ||
99 | |||
100 | *epc++ = 0; | ||
101 | } | ||
102 | |||
103 | static inline unsigned int *label(void) | ||
104 | { | ||
105 | if (instruction_pending) { | ||
106 | *epc++ = delayed_mi.word; | ||
107 | instruction_pending = 0; | ||
108 | } | ||
109 | |||
110 | return epc; | ||
111 | } | ||
112 | |||
113 | static inline void build_insn_word(unsigned int word) | ||
114 | { | ||
115 | union mips_instruction mi; | ||
116 | |||
117 | mi.word = word; | ||
118 | |||
119 | emit_instruction(mi); | ||
120 | } | ||
121 | |||
122 | static inline void build_nop(void) | ||
123 | { | ||
124 | build_insn_word(0); /* nop */ | ||
125 | } | ||
126 | |||
127 | static inline void build_src_pref(int advance) | ||
128 | { | ||
129 | if (!(load_offset & (cpu_dcache_line_size() - 1)) && advance) { | ||
130 | union mips_instruction mi; | ||
131 | |||
132 | mi.i_format.opcode = pref_op; | ||
133 | mi.i_format.rs = 5; /* $a1 */ | ||
134 | mi.i_format.rt = pref_src_mode; | ||
135 | mi.i_format.simmediate = load_offset + advance; | ||
136 | |||
137 | emit_instruction(mi); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static inline void __build_load_reg(int reg) | ||
142 | { | ||
143 | union mips_instruction mi; | ||
144 | unsigned int width; | ||
145 | |||
146 | if (cpu_has_64bit_gp_regs) { | ||
147 | mi.i_format.opcode = ld_op; | ||
148 | width = 8; | ||
149 | } else { | ||
150 | mi.i_format.opcode = lw_op; | ||
151 | width = 4; | ||
152 | } | ||
153 | mi.i_format.rs = 5; /* $a1 */ | ||
154 | mi.i_format.rt = reg; /* $reg */ | ||
155 | mi.i_format.simmediate = load_offset; | ||
156 | |||
157 | load_offset += width; | ||
158 | emit_instruction(mi); | ||
159 | } | ||
160 | |||
161 | static inline void build_load_reg(int reg) | ||
162 | { | ||
163 | if (cpu_has_prefetch) | ||
164 | build_src_pref(pref_offset_copy); | ||
165 | |||
166 | __build_load_reg(reg); | ||
167 | } | ||
168 | |||
169 | static inline void build_dst_pref(int advance) | ||
170 | { | ||
171 | if (!(store_offset & (cpu_dcache_line_size() - 1)) && advance) { | ||
172 | union mips_instruction mi; | ||
173 | |||
174 | mi.i_format.opcode = pref_op; | ||
175 | mi.i_format.rs = 4; /* $a0 */ | ||
176 | mi.i_format.rt = pref_dst_mode; | ||
177 | mi.i_format.simmediate = store_offset + advance; | ||
178 | |||
179 | emit_instruction(mi); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | static inline void build_cdex_s(void) | ||
184 | { | ||
185 | union mips_instruction mi; | ||
186 | |||
187 | if ((store_offset & (cpu_scache_line_size() - 1))) | ||
188 | return; | ||
189 | |||
190 | mi.c_format.opcode = cache_op; | ||
191 | mi.c_format.rs = 4; /* $a0 */ | ||
192 | mi.c_format.c_op = 3; /* Create Dirty Exclusive */ | ||
193 | mi.c_format.cache = 3; /* Secondary Data Cache */ | ||
194 | mi.c_format.simmediate = store_offset; | ||
195 | |||
196 | emit_instruction(mi); | ||
197 | } | ||
198 | |||
199 | static inline void build_cdex_p(void) | ||
200 | { | ||
201 | union mips_instruction mi; | ||
202 | |||
203 | if (store_offset & (cpu_dcache_line_size() - 1)) | ||
204 | return; | ||
205 | |||
206 | if (R4600_V1_HIT_CACHEOP_WAR && cpu_is_r4600_v1_x()) { | ||
207 | build_nop(); | ||
208 | build_nop(); | ||
209 | build_nop(); | ||
210 | build_nop(); | ||
211 | } | ||
212 | |||
213 | if (R4600_V2_HIT_CACHEOP_WAR && cpu_is_r4600_v2_x()) | ||
214 | build_insn_word(0x8c200000); /* lw $zero, ($at) */ | ||
215 | |||
216 | mi.c_format.opcode = cache_op; | ||
217 | mi.c_format.rs = 4; /* $a0 */ | ||
218 | mi.c_format.c_op = 3; /* Create Dirty Exclusive */ | ||
219 | mi.c_format.cache = 1; /* Data Cache */ | ||
220 | mi.c_format.simmediate = store_offset; | ||
221 | |||
222 | emit_instruction(mi); | ||
223 | } | ||
224 | |||
225 | static void __cpuinit __build_store_reg(int reg) | ||
226 | { | ||
227 | union mips_instruction mi; | ||
228 | unsigned int width; | ||
229 | |||
230 | if (cpu_has_64bit_gp_regs || | ||
231 | (cpu_has_64bit_zero_reg && reg == 0)) { | ||
232 | mi.i_format.opcode = sd_op; | ||
233 | width = 8; | ||
234 | } else { | ||
235 | mi.i_format.opcode = sw_op; | ||
236 | width = 4; | ||
237 | } | ||
238 | mi.i_format.rs = 4; /* $a0 */ | ||
239 | mi.i_format.rt = reg; /* $reg */ | ||
240 | mi.i_format.simmediate = store_offset; | ||
241 | |||
242 | store_offset += width; | ||
243 | emit_instruction(mi); | ||
244 | } | ||
245 | |||
246 | static inline void build_store_reg(int reg) | ||
247 | { | ||
248 | int pref_off = cpu_has_prefetch ? | ||
249 | (reg ? pref_offset_copy : pref_offset_clear) : 0; | ||
250 | if (pref_off) | ||
251 | build_dst_pref(pref_off); | ||
252 | else if (cpu_has_cache_cdex_s) | ||
253 | build_cdex_s(); | ||
254 | else if (cpu_has_cache_cdex_p) | ||
255 | build_cdex_p(); | ||
256 | |||
257 | __build_store_reg(reg); | ||
258 | } | ||
259 | |||
260 | static inline void build_addiu_rt_rs(unsigned int rt, unsigned int rs, | ||
261 | unsigned long offset) | ||
262 | { | ||
263 | union mips_instruction mi; | ||
264 | |||
265 | BUG_ON(offset > 0x7fff); | ||
266 | |||
267 | if (cpu_has_64bit_gp_regs && DADDI_WAR && r4k_daddiu_bug()) { | ||
268 | mi.i_format.opcode = addiu_op; | ||
269 | mi.i_format.rs = 0; /* $zero */ | ||
270 | mi.i_format.rt = 25; /* $t9 */ | ||
271 | mi.i_format.simmediate = offset; | ||
272 | emit_instruction(mi); | ||
273 | |||
274 | mi.r_format.opcode = spec_op; | ||
275 | mi.r_format.rs = rs; | ||
276 | mi.r_format.rt = 25; /* $t9 */ | ||
277 | mi.r_format.rd = rt; | ||
278 | mi.r_format.re = 0; | ||
279 | mi.r_format.func = daddu_op; | ||
280 | } else { | ||
281 | mi.i_format.opcode = cpu_has_64bit_gp_regs ? | ||
282 | daddiu_op : addiu_op; | ||
283 | mi.i_format.rs = rs; | ||
284 | mi.i_format.rt = rt; | ||
285 | mi.i_format.simmediate = offset; | ||
286 | } | ||
287 | emit_instruction(mi); | ||
288 | } | ||
289 | |||
290 | static inline void build_addiu_a2_a0(unsigned long offset) | ||
291 | { | ||
292 | build_addiu_rt_rs(6, 4, offset); /* $a2, $a0, offset */ | ||
293 | } | ||
294 | |||
295 | static inline void build_addiu_a2(unsigned long offset) | ||
296 | { | ||
297 | build_addiu_rt_rs(6, 6, offset); /* $a2, $a2, offset */ | ||
298 | } | ||
299 | |||
300 | static inline void build_addiu_a1(unsigned long offset) | ||
301 | { | ||
302 | build_addiu_rt_rs(5, 5, offset); /* $a1, $a1, offset */ | ||
303 | |||
304 | load_offset -= offset; | ||
305 | } | ||
306 | |||
307 | static inline void build_addiu_a0(unsigned long offset) | ||
308 | { | ||
309 | build_addiu_rt_rs(4, 4, offset); /* $a0, $a0, offset */ | ||
310 | |||
311 | store_offset -= offset; | ||
312 | } | ||
313 | |||
314 | static inline void build_bne(unsigned int *dest) | ||
315 | { | ||
316 | union mips_instruction mi; | ||
317 | |||
318 | mi.i_format.opcode = bne_op; | ||
319 | mi.i_format.rs = 6; /* $a2 */ | ||
320 | mi.i_format.rt = 4; /* $a0 */ | ||
321 | mi.i_format.simmediate = dest - epc - 1; | ||
322 | |||
323 | *epc++ = mi.word; | ||
324 | flush_delay_slot_or_nop(); | ||
325 | } | ||
326 | |||
327 | static inline void build_jr_ra(void) | ||
328 | { | ||
329 | union mips_instruction mi; | ||
330 | |||
331 | mi.r_format.opcode = spec_op; | ||
332 | mi.r_format.rs = 31; | ||
333 | mi.r_format.rt = 0; | ||
334 | mi.r_format.rd = 0; | ||
335 | mi.r_format.re = 0; | ||
336 | mi.r_format.func = jr_op; | ||
337 | |||
338 | *epc++ = mi.word; | ||
339 | flush_delay_slot_or_nop(); | ||
340 | } | ||
341 | |||
342 | void __cpuinit build_clear_page(void) | ||
343 | { | ||
344 | unsigned int loop_start; | ||
345 | unsigned long off; | ||
346 | int i; | ||
347 | |||
348 | epc = (unsigned int *) &clear_page_array; | ||
349 | instruction_pending = 0; | ||
350 | store_offset = 0; | ||
351 | |||
352 | if (cpu_has_prefetch) { | ||
353 | switch (current_cpu_type()) { | ||
354 | case CPU_TX49XX: | ||
355 | /* TX49 supports only Pref_Load */ | ||
356 | pref_offset_clear = 0; | ||
357 | pref_offset_copy = 0; | ||
358 | break; | ||
359 | |||
360 | case CPU_RM9000: | ||
361 | /* | ||
362 | * As a workaround for erratum G105 which make the | ||
363 | * PrepareForStore hint unusable we fall back to | ||
364 | * StoreRetained on the RM9000. Once it is known which | ||
365 | * versions of the RM9000 we'll be able to condition- | ||
366 | * alize this. | ||
367 | */ | ||
368 | |||
369 | case CPU_R10000: | ||
370 | case CPU_R12000: | ||
371 | case CPU_R14000: | ||
372 | pref_src_mode = Pref_LoadStreamed; | ||
373 | pref_dst_mode = Pref_StoreStreamed; | ||
374 | break; | ||
375 | |||
376 | default: | ||
377 | pref_src_mode = Pref_LoadStreamed; | ||
378 | pref_dst_mode = Pref_PrepareForStore; | ||
379 | break; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | off = PAGE_SIZE - (cpu_has_prefetch ? pref_offset_clear : 0); | ||
384 | if (off > 0x7fff) { | ||
385 | build_addiu_a2_a0(off >> 1); | ||
386 | build_addiu_a2(off >> 1); | ||
387 | } else | ||
388 | build_addiu_a2_a0(off); | ||
389 | |||
390 | if (R4600_V2_HIT_CACHEOP_WAR && cpu_is_r4600_v2_x()) | ||
391 | build_insn_word(0x3c01a000); /* lui $at, 0xa000 */ | ||
392 | |||
393 | dest = label(); | ||
394 | do { | ||
395 | build_store_reg(0); | ||
396 | build_store_reg(0); | ||
397 | build_store_reg(0); | ||
398 | build_store_reg(0); | ||
399 | } while (store_offset < half_scache_line_size()); | ||
400 | build_addiu_a0(2 * store_offset); | ||
401 | loop_start = store_offset; | ||
402 | do { | ||
403 | build_store_reg(0); | ||
404 | build_store_reg(0); | ||
405 | build_store_reg(0); | ||
406 | build_store_reg(0); | ||
407 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
408 | build_bne(dest); | ||
409 | |||
410 | if (cpu_has_prefetch && pref_offset_clear) { | ||
411 | build_addiu_a2_a0(pref_offset_clear); | ||
412 | dest = label(); | ||
413 | loop_start = store_offset; | ||
414 | do { | ||
415 | __build_store_reg(0); | ||
416 | __build_store_reg(0); | ||
417 | __build_store_reg(0); | ||
418 | __build_store_reg(0); | ||
419 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
420 | build_addiu_a0(2 * store_offset); | ||
421 | loop_start = store_offset; | ||
422 | do { | ||
423 | __build_store_reg(0); | ||
424 | __build_store_reg(0); | ||
425 | __build_store_reg(0); | ||
426 | __build_store_reg(0); | ||
427 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
428 | build_bne(dest); | ||
429 | } | ||
430 | |||
431 | build_jr_ra(); | ||
432 | |||
433 | BUG_ON(epc > clear_page_array + ARRAY_SIZE(clear_page_array)); | ||
434 | |||
435 | pr_info("Synthesized clear page handler (%u instructions).\n", | ||
436 | (unsigned int)(epc - clear_page_array)); | ||
437 | |||
438 | pr_debug("\t.set push\n"); | ||
439 | pr_debug("\t.set noreorder\n"); | ||
440 | for (i = 0; i < (epc - clear_page_array); i++) | ||
441 | pr_debug("\t.word 0x%08x\n", clear_page_array[i]); | ||
442 | pr_debug("\t.set pop\n"); | ||
443 | } | ||
444 | |||
445 | void __cpuinit build_copy_page(void) | ||
446 | { | ||
447 | unsigned int loop_start; | ||
448 | unsigned long off; | ||
449 | int i; | ||
450 | |||
451 | epc = (unsigned int *) ©_page_array; | ||
452 | store_offset = load_offset = 0; | ||
453 | instruction_pending = 0; | ||
454 | |||
455 | off = PAGE_SIZE - (cpu_has_prefetch ? pref_offset_copy : 0); | ||
456 | if (off > 0x7fff) { | ||
457 | build_addiu_a2_a0(off >> 1); | ||
458 | build_addiu_a2(off >> 1); | ||
459 | } else | ||
460 | build_addiu_a2_a0(off); | ||
461 | |||
462 | if (R4600_V2_HIT_CACHEOP_WAR && cpu_is_r4600_v2_x()) | ||
463 | build_insn_word(0x3c01a000); /* lui $at, 0xa000 */ | ||
464 | |||
465 | dest = label(); | ||
466 | loop_start = store_offset; | ||
467 | do { | ||
468 | build_load_reg( 8); | ||
469 | build_load_reg( 9); | ||
470 | build_load_reg(10); | ||
471 | build_load_reg(11); | ||
472 | build_store_reg( 8); | ||
473 | build_store_reg( 9); | ||
474 | build_store_reg(10); | ||
475 | build_store_reg(11); | ||
476 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
477 | build_addiu_a0(2 * store_offset); | ||
478 | build_addiu_a1(2 * load_offset); | ||
479 | loop_start = store_offset; | ||
480 | do { | ||
481 | build_load_reg( 8); | ||
482 | build_load_reg( 9); | ||
483 | build_load_reg(10); | ||
484 | build_load_reg(11); | ||
485 | build_store_reg( 8); | ||
486 | build_store_reg( 9); | ||
487 | build_store_reg(10); | ||
488 | build_store_reg(11); | ||
489 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
490 | build_bne(dest); | ||
491 | |||
492 | if (cpu_has_prefetch && pref_offset_copy) { | ||
493 | build_addiu_a2_a0(pref_offset_copy); | ||
494 | dest = label(); | ||
495 | loop_start = store_offset; | ||
496 | do { | ||
497 | __build_load_reg( 8); | ||
498 | __build_load_reg( 9); | ||
499 | __build_load_reg(10); | ||
500 | __build_load_reg(11); | ||
501 | __build_store_reg( 8); | ||
502 | __build_store_reg( 9); | ||
503 | __build_store_reg(10); | ||
504 | __build_store_reg(11); | ||
505 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
506 | build_addiu_a0(2 * store_offset); | ||
507 | build_addiu_a1(2 * load_offset); | ||
508 | loop_start = store_offset; | ||
509 | do { | ||
510 | __build_load_reg( 8); | ||
511 | __build_load_reg( 9); | ||
512 | __build_load_reg(10); | ||
513 | __build_load_reg(11); | ||
514 | __build_store_reg( 8); | ||
515 | __build_store_reg( 9); | ||
516 | __build_store_reg(10); | ||
517 | __build_store_reg(11); | ||
518 | } while ((store_offset - loop_start) < half_scache_line_size()); | ||
519 | build_bne(dest); | ||
520 | } | ||
521 | |||
522 | build_jr_ra(); | ||
523 | |||
524 | BUG_ON(epc > copy_page_array + ARRAY_SIZE(copy_page_array)); | ||
525 | |||
526 | pr_info("Synthesized copy page handler (%u instructions).\n", | ||
527 | (unsigned int)(epc - copy_page_array)); | ||
528 | |||
529 | pr_debug("\t.set push\n"); | ||
530 | pr_debug("\t.set noreorder\n"); | ||
531 | for (i = 0; i < (epc - copy_page_array); i++) | ||
532 | pr_debug("\t.word 0x%08x\n", copy_page_array[i]); | ||
533 | pr_debug("\t.set pop\n"); | ||
534 | } | ||