Merge branch 'avr32-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemo...
[sfrench/cifs-2.6.git] / arch / mips / mm / pg-r4k.c
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 *) &copy_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 }