aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/mm/pg-r4k.c
diff options
context:
space:
mode:
authorMaciej W. Rozycki <macro@linux-mips.org>2007-10-23 07:43:25 -0400
committerRalf Baechle <ralf@linux-mips.org>2008-01-29 05:14:55 -0500
commit619b6e18fce20e4b2d0082cde989f37e1be7b3e1 (patch)
treeb56120c0de584f6672aa7e4533163435613f5203 /arch/mips/mm/pg-r4k.c
parent20d60d9973c3b441902b0a3f4f6f7e7ade08f77d (diff)
[MIPS] R4000/R4400 daddiu erratum workaround
This complements the generic R4000/R4400 errata workaround code and adds bits for the daddiu problem. In most places it just modifies handwritten assembly code so that the assembler is allowed to use a temporary register as daddiu may now be treated as a macro that expands to a sequence of li and daddu. It is the AT register or, where AT is unavailable or used explicitly for another purpose, an explicitly-named register is selected, using the .set at=<reg> feature added recently to gas. This feature is only used if CONFIG_CPU_DADDI_WORKAROUNDS has been set, so if the workaround remains disabled, the required version of binutils stays unchanged. Similarly, daddiu instructions put in branch delay slots in noreorder fragments are now taken out of them and the assembler is allowed to reorder them itself as possible (which it does making the whole idea of scheduling them into delay slots manually questionable). Also in the very few places where such a simple conversion was not possible, a handcoded longer sequence is implemented. Other than that there are changes to code responsible for building the TLB fault and page clear/copy handlers to avoid daddiu as appropriate. These are only effective if the erratum is verified to be present at the run time. Finally there is a trivial update to __delay(), because it uses daddiu in a branch delay slot. Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm/pg-r4k.c')
-rw-r--r--arch/mips/mm/pg-r4k.c66
1 files changed, 31 insertions, 35 deletions
diff --git a/arch/mips/mm/pg-r4k.c b/arch/mips/mm/pg-r4k.c
index 4f770ac885c..9185fbf37c0 100644
--- a/arch/mips/mm/pg-r4k.c
+++ b/arch/mips/mm/pg-r4k.c
@@ -4,6 +4,7 @@
4 * for more details. 4 * for more details.
5 * 5 *
6 * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org) 6 * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org)
7 * Copyright (C) 2007 Maciej W. Rozycki
7 */ 8 */
8#include <linux/init.h> 9#include <linux/init.h>
9#include <linux/kernel.h> 10#include <linux/kernel.h>
@@ -12,6 +13,7 @@
12#include <linux/module.h> 13#include <linux/module.h>
13#include <linux/proc_fs.h> 14#include <linux/proc_fs.h>
14 15
16#include <asm/bugs.h>
15#include <asm/cacheops.h> 17#include <asm/cacheops.h>
16#include <asm/inst.h> 18#include <asm/inst.h>
17#include <asm/io.h> 19#include <asm/io.h>
@@ -255,64 +257,58 @@ static inline void build_store_reg(int reg)
255 __build_store_reg(reg); 257 __build_store_reg(reg);
256} 258}
257 259
258static inline void build_addiu_a2_a0(unsigned long offset) 260static inline void build_addiu_rt_rs(unsigned int rt, unsigned int rs,
261 unsigned long offset)
259{ 262{
260 union mips_instruction mi; 263 union mips_instruction mi;
261 264
262 BUG_ON(offset > 0x7fff); 265 BUG_ON(offset > 0x7fff);
263 266
264 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op; 267 if (cpu_has_64bit_gp_regs && DADDI_WAR && r4k_daddiu_bug()) {
265 mi.i_format.rs = 4; /* $a0 */ 268 mi.i_format.opcode = addiu_op;
266 mi.i_format.rt = 6; /* $a2 */ 269 mi.i_format.rs = 0; /* $zero */
267 mi.i_format.simmediate = offset; 270 mi.i_format.rt = 25; /* $t9 */
271 mi.i_format.simmediate = offset;
272 emit_instruction(mi);
268 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 }
269 emit_instruction(mi); 287 emit_instruction(mi);
270} 288}
271 289
272static inline void build_addiu_a2(unsigned long offset) 290static inline void build_addiu_a2_a0(unsigned long offset)
273{ 291{
274 union mips_instruction mi; 292 build_addiu_rt_rs(6, 4, offset); /* $a2, $a0, offset */
275 293}
276 BUG_ON(offset > 0x7fff);
277
278 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op;
279 mi.i_format.rs = 6; /* $a2 */
280 mi.i_format.rt = 6; /* $a2 */
281 mi.i_format.simmediate = offset;
282 294
283 emit_instruction(mi); 295static inline void build_addiu_a2(unsigned long offset)
296{
297 build_addiu_rt_rs(6, 6, offset); /* $a2, $a2, offset */
284} 298}
285 299
286static inline void build_addiu_a1(unsigned long offset) 300static inline void build_addiu_a1(unsigned long offset)
287{ 301{
288 union mips_instruction mi; 302 build_addiu_rt_rs(5, 5, offset); /* $a1, $a1, offset */
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 = 5; /* $a1 */
294 mi.i_format.rt = 5; /* $a1 */
295 mi.i_format.simmediate = offset;
296 303
297 load_offset -= offset; 304 load_offset -= offset;
298
299 emit_instruction(mi);
300} 305}
301 306
302static inline void build_addiu_a0(unsigned long offset) 307static inline void build_addiu_a0(unsigned long offset)
303{ 308{
304 union mips_instruction mi; 309 build_addiu_rt_rs(4, 4, offset); /* $a0, $a0, offset */
305
306 BUG_ON(offset > 0x7fff);
307
308 mi.i_format.opcode = cpu_has_64bit_gp_regs ? daddiu_op : addiu_op;
309 mi.i_format.rs = 4; /* $a0 */
310 mi.i_format.rt = 4; /* $a0 */
311 mi.i_format.simmediate = offset;
312 310
313 store_offset -= offset; 311 store_offset -= offset;
314
315 emit_instruction(mi);
316} 312}
317 313
318static inline void build_bne(unsigned int *dest) 314static inline void build_bne(unsigned int *dest)