The short loop bug under certain conditions causes loops to execute only once or twice. GCC 2.95 that shipped with Sony PS2 Linux had a patch with the following note: On the R5900, we must ensure that the compiler never generates loops that satisfy all of the following conditions: - a loop consists of less than equal to six instructions (including the branch delay slot); - a loop contains only one conditional branch instruction at the end of the loop; - a loop does not contain any other branch or jump instructions; - a branch delay slot of the loop is not NOP (EE 2.9 or later). We need to do this because of a bug in the chip. Signed-off-by: Fredrik Noring <noring@xxxxxxxxxx> --- The exact NOP placements in this patch are provisional. Request for comment on the method to use. I believe there are at least three alternatives: 1. Add #ifdefs or macros in the source code (similar to this patch). 2. Modify the assembler to automatically insert NOPs as required. 3. Avoid assembly and use C versions of memcpy etc. instead. This change has been ported from v2.6 patches. diff --git a/arch/mips/include/asm/r4kcache.h b/arch/mips/include/asm/r4kcache.h index 7f12d7e27c94..9659fb55abd2 100644 --- a/arch/mips/include/asm/r4kcache.h +++ b/arch/mips/include/asm/r4kcache.h @@ -37,6 +37,12 @@ extern void (*r4k_blast_icache)(void); * without ifdefs we let the compiler do it by a type cast. */ #define INDEX_BASE CKSEG0 +#ifdef CONFIG_CPU_R5900 +/* Workaround for short loops on R5900. */ +#define R5900_LOOP_WAR() do { __asm__ __volatile__("nop;nop;\n"); } while(0) +#else +#define R5900_LOOP_WAR() do { } while(0) +#endif #define cache_op(op,addr) \ __asm__ __volatile__( \ @@ -689,6 +695,7 @@ static inline void prot##extra##blast_##pfx##cache##_range(unsigned long start, \ while (1) { \ prot##cache_op(hitop, addr); \ + R5900_LOOP_WAR(); /* FIXME: Is this needed in C? */ \ if (addr == aend) \ break; \ addr += lsize; \ diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index 03e3304d6ae5..713015f6faa2 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -339,6 +339,12 @@ STORE(t1, UNIT(-1)(dst), .Ls_exc_p1u\@) PREFS( 0, 8*32(src) ) PREFD( 1, 8*32(dst) ) +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne len, rem, 1b nop @@ -382,6 +388,12 @@ STORE(t0, 0(dst), .Ls_exc_p1u\@) .set reorder /* DADDI_WAR */ ADD dst, dst, NBYTES +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne rem, len, 1b .set noreorder @@ -467,6 +479,12 @@ PREFD( 1, 9*32(dst) ) # 1 is PREF_STORE (not streamed) .set reorder /* DADDI_WAR */ ADD dst, dst, 4*NBYTES +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne len, rem, 1b .set noreorder @@ -484,6 +502,12 @@ STORE(t0, 0(dst), .Ls_exc_p1u\@) .set reorder /* DADDI_WAR */ ADD dst, dst, NBYTES +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne len, rem, 1b .set noreorder @@ -528,6 +552,12 @@ COPY_BYTE(6) COPY_BYTE(7) ADD src, src, 8 +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif b 1b ADD dst, dst, 8 #endif /* CONFIG_CPU_MIPSR6 */ @@ -557,6 +587,12 @@ sb t1, 0(dst) # can't fault -- we're copy_from_user .set reorder /* DADDI_WAR */ ADD dst, dst, 1 +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne src, t0, 1b .set noreorder .Ll_exc\@: @@ -623,6 +659,12 @@ LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ SUB a1, a1, 0x1 .set reorder /* DADDI_WAR */ SUB a0, a0, 0x1 +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bnez a2, .Lr_end_bytes .set noreorder @@ -638,6 +680,12 @@ LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ ADD a1, a1, 0x1 .set reorder /* DADDI_WAR */ ADD a0, a0, 0x1 +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bnez a2, .Lr_end_bytes_up .set noreorder diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S index a1456664d6c2..489bc9cffcbd 100644 --- a/arch/mips/lib/memset.S +++ b/arch/mips/lib/memset.S @@ -156,6 +156,12 @@ 1: PTR_ADDIU a0, 64 R10KCBARRIER(0(ra)) f_fill64 a0, -64, FILL64RG, .Lfwd_fixup\@, \mode +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne t1, a0, 1b .set noreorder @@ -218,6 +224,12 @@ 1: PTR_ADDIU a0, 1 /* fill bytewise */ R10KCBARRIER(0(ra)) +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bne t1, a0, 1b sb a1, -1(a0) diff --git a/arch/mips/lib/strncpy_user.S b/arch/mips/lib/strncpy_user.S index acdff66bd5d2..44cc346fd400 100644 --- a/arch/mips/lib/strncpy_user.S +++ b/arch/mips/lib/strncpy_user.S @@ -48,6 +48,10 @@ LEAF(__strncpy_from_\func\()_asm) beqz v0, 2f PTR_ADDIU t0, 1 PTR_ADDIU a0, 1 +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop +#endif bne t0, a2, 1b 2: PTR_ADDU v0, a1, t0 xor v0, a1 diff --git a/arch/mips/lib/strnlen_user.S b/arch/mips/lib/strnlen_user.S index e1bacf5a3abe..474979641a8d 100644 --- a/arch/mips/lib/strnlen_user.S +++ b/arch/mips/lib/strnlen_user.S @@ -46,6 +46,12 @@ LEAF(__strnlen_\func\()_asm) EX(lbe, t0, (v0), .Lfault\@) .endif .set noreorder +#ifdef CONFIG_CPU_R5900 + /* No short loops. */ + nop + nop + nop +#endif bnez t0, 1b 1: #ifndef CONFIG_CPU_DADDI_WORKAROUNDS