Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[POWERPC] Cell timebase bug workaround

The Cell CPU timebase has an erratum. When reading the entire 64 bits
of the timebase with one mftb instruction, there is a handful of cycles
window during which one might read a value with the low order 32 bits
already reset to 0x00000000 but the high order bits not yet incremeted
by one. This fixes it by reading the timebase again until the low order
32 bits is no longer 0. That might introduce occasional latencies if
hitting mftb just at the wrong time, but no more than 70ns on a cell
blade, and that was considered acceptable.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Paul Mackerras <paulus@samba.org>

authored by

Benjamin Herrenschmidt and committed by
Paul Mackerras
859deea9 21c4ff80

+68 -27
+4 -2
arch/powerpc/kernel/vdso64/gettimeofday.S
··· 229 229 xor r0,r8,r8 /* create dependency */ 230 230 add r3,r3,r0 231 231 232 - /* Get TB & offset it */ 233 - mftb r7 232 + /* Get TB & offset it. We use the MFTB macro which will generate 233 + * workaround code for Cell. 234 + */ 235 + MFTB(r7) 234 236 ld r9,CFG_TB_ORIG_STAMP(r3) 235 237 subf r7,r9,r7 236 238
+2 -1
include/asm-powerpc/cputable.h
··· 147 147 #define CPU_FTR_CI_LARGE_PAGE LONG_ASM_CONST(0x0000100000000000) 148 148 #define CPU_FTR_PAUSE_ZERO LONG_ASM_CONST(0x0000200000000000) 149 149 #define CPU_FTR_PURR LONG_ASM_CONST(0x0000400000000000) 150 + #define CPU_FTR_CELL_TB_BUG LONG_ASM_CONST(0x0000800000000000) 150 151 151 152 #ifndef __ASSEMBLY__ 152 153 ··· 336 335 #define CPU_FTRS_CELL (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ 337 336 CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ 338 337 CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ 339 - CPU_FTR_PAUSE_ZERO | CPU_FTR_CI_LARGE_PAGE) 338 + CPU_FTR_PAUSE_ZERO | CPU_FTR_CI_LARGE_PAGE | CPU_FTR_CELL_TB_BUG) 340 339 #define CPU_FTRS_PA6T (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ 341 340 CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ 342 341 CPU_FTR_ALTIVEC_COMP | CPU_FTR_CI_LARGE_PAGE | \
+14 -4
include/asm-powerpc/ppc_asm.h
··· 30 30 mfspr ra,SPRN_PURR; /* get processor util. reg */ \ 31 31 END_FTR_SECTION_IFSET(CPU_FTR_PURR); \ 32 32 BEGIN_FTR_SECTION; \ 33 - mftb ra; /* or get TB if no PURR */ \ 33 + MFTB(ra); /* or get TB if no PURR */ \ 34 34 END_FTR_SECTION_IFCLR(CPU_FTR_PURR); \ 35 - ld rb,PACA_STARTPURR(r13); \ 35 + ld rb,PACA_STARTPURR(r13); \ 36 36 std ra,PACA_STARTPURR(r13); \ 37 37 subf rb,rb,ra; /* subtract start value */ \ 38 38 ld ra,PACA_USER_TIME(r13); \ ··· 45 45 mfspr ra,SPRN_PURR; /* get processor util. reg */ \ 46 46 END_FTR_SECTION_IFSET(CPU_FTR_PURR); \ 47 47 BEGIN_FTR_SECTION; \ 48 - mftb ra; /* or get TB if no PURR */ \ 48 + MFTB(ra); /* or get TB if no PURR */ \ 49 49 END_FTR_SECTION_IFCLR(CPU_FTR_PURR); \ 50 - ld rb,PACA_STARTPURR(r13); \ 50 + ld rb,PACA_STARTPURR(r13); \ 51 51 std ra,PACA_STARTPURR(r13); \ 52 52 subf rb,rb,ra; /* subtract start value */ \ 53 53 ld ra,PACA_SYSTEM_TIME(r13); \ ··· 274 274 #define ISYNC_601 275 275 #endif 276 276 277 + #ifdef CONFIG_PPC_CELL 278 + #define MFTB(dest) \ 279 + 90: mftb dest; \ 280 + BEGIN_FTR_SECTION_NESTED(96); \ 281 + cmpwi dest,0; \ 282 + beq- 90b; \ 283 + END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96) 284 + #else 285 + #define MFTB(dest) mftb dest 286 + #endif 277 287 278 288 #ifndef CONFIG_SMP 279 289 #define TLBSYNC
+25
include/asm-powerpc/reg.h
··· 619 619 : "=r" (rval)); rval;}) 620 620 #define mtspr(rn, v) asm volatile("mtspr " __stringify(rn) ",%0" : : "r" (v)) 621 621 622 + #ifdef __powerpc64__ 623 + #ifdef CONFIG_PPC_CELL 624 + #define mftb() ({unsigned long rval; \ 625 + asm volatile( \ 626 + "90: mftb %0;\n" \ 627 + "97: cmpwi %0,0;\n" \ 628 + " beq- 90b;\n" \ 629 + "99:\n" \ 630 + ".section __ftr_fixup,\"a\"\n" \ 631 + ".align 3\n" \ 632 + "98:\n" \ 633 + " .llong %1\n" \ 634 + " .llong %1\n" \ 635 + " .llong 97b-98b\n" \ 636 + " .llong 99b-98b\n" \ 637 + ".previous" \ 638 + : "=r" (rval) : "i" (CPU_FTR_CELL_TB_BUG)); rval;}) 639 + #else 622 640 #define mftb() ({unsigned long rval; \ 623 641 asm volatile("mftb %0" : "=r" (rval)); rval;}) 642 + #endif /* !CONFIG_PPC_CELL */ 643 + 644 + #else /* __powerpc64__ */ 645 + 624 646 #define mftbl() ({unsigned long rval; \ 625 647 asm volatile("mftbl %0" : "=r" (rval)); rval;}) 648 + #define mftbu() ({unsigned long rval; \ 649 + asm volatile("mftbu %0" : "=r" (rval)); rval;}) 650 + #endif /* !__powerpc64__ */ 626 651 627 652 #define mttbl(v) asm volatile("mttbl %0":: "r"(v)) 628 653 #define mttbu(v) asm volatile("mttbu %0":: "r"(v))
+18 -13
include/asm-powerpc/time.h
··· 82 82 #define __USE_RTC() 0 83 83 #endif 84 84 85 - /* On ppc64 this gets us the whole timebase; on ppc32 just the lower half */ 85 + #ifdef CONFIG_PPC64 86 + 87 + /* For compatibility, get_tbl() is defined as get_tb() on ppc64 */ 88 + #define get_tbl get_tb 89 + 90 + #else 91 + 86 92 static inline unsigned long get_tbl(void) 87 93 { 88 - unsigned long tbl; 89 - 90 94 #if defined(CONFIG_403GCX) 95 + unsigned long tbl; 91 96 asm volatile("mfspr %0, 0x3dd" : "=r" (tbl)); 92 - #else 93 - asm volatile("mftb %0" : "=r" (tbl)); 94 - #endif 95 97 return tbl; 98 + #else 99 + return mftbl(); 100 + #endif 96 101 } 97 102 98 103 static inline unsigned int get_tbu(void) 99 104 { 105 + #ifdef CONFIG_403GCX 100 106 unsigned int tbu; 101 - 102 - #if defined(CONFIG_403GCX) 103 107 asm volatile("mfspr %0, 0x3dc" : "=r" (tbu)); 104 - #else 105 - asm volatile("mftbu %0" : "=r" (tbu)); 106 - #endif 107 108 return tbu; 109 + #else 110 + return mftbu(); 111 + #endif 108 112 } 113 + #endif /* !CONFIG_PPC64 */ 109 114 110 115 static inline unsigned int get_rtcl(void) 111 116 { ··· 136 131 { 137 132 return mftb(); 138 133 } 139 - #else 134 + #else /* CONFIG_PPC64 */ 140 135 static inline u64 get_tb(void) 141 136 { 142 137 unsigned int tbhi, tblo, tbhi2; ··· 149 144 150 145 return ((u64)tbhi << 32) | tblo; 151 146 } 152 - #endif 147 + #endif /* !CONFIG_PPC64 */ 153 148 154 149 static inline void set_tb(unsigned int upper, unsigned int lower) 155 150 {
+5 -7
include/asm-powerpc/timex.h
··· 8 8 */ 9 9 10 10 #include <asm/cputable.h> 11 + #include <asm/reg.h> 11 12 12 13 #define CLOCK_TICK_RATE 1024000 /* Underlying HZ */ 13 14 ··· 16 15 17 16 static inline cycles_t get_cycles(void) 18 17 { 18 + #ifdef __powerpc64__ 19 + return mftb(); 20 + #else 19 21 cycles_t ret; 20 22 21 - #ifdef __powerpc64__ 22 - 23 - __asm__ __volatile__("mftb %0" : "=r" (ret) : ); 24 - 25 - #else 26 23 /* 27 24 * For the "cycle" counter we use the timebase lower half. 28 25 * Currently only used on SMP. ··· 40 41 " .long 99b-98b\n" 41 42 ".previous" 42 43 : "=r" (ret) : "i" (CPU_FTR_601)); 43 - #endif 44 - 45 44 return ret; 45 + #endif 46 46 } 47 47 48 48 #endif /* __KERNEL__ */