.. | .. |
---|
11 | 11 | #include <linux/bug.h> |
---|
12 | 12 | #include <linux/irqflags.h> |
---|
13 | 13 | #include <asm/compiler.h> |
---|
| 14 | +#include <asm/llsc.h> |
---|
| 15 | +#include <asm/sync.h> |
---|
14 | 16 | #include <asm/war.h> |
---|
15 | | - |
---|
16 | | -/* |
---|
17 | | - * Using a branch-likely instruction to check the result of an sc instruction |
---|
18 | | - * works around a bug present in R10000 CPUs prior to revision 3.0 that could |
---|
19 | | - * cause ll-sc sequences to execute non-atomically. |
---|
20 | | - */ |
---|
21 | | -#if R10000_LLSC_WAR |
---|
22 | | -# define __scbeqz "beqzl" |
---|
23 | | -#else |
---|
24 | | -# define __scbeqz "beqz" |
---|
25 | | -#endif |
---|
26 | 17 | |
---|
27 | 18 | /* |
---|
28 | 19 | * These functions doesn't exist, so if they are called you'll either: |
---|
.. | .. |
---|
36 | 27 | */ |
---|
37 | 28 | extern unsigned long __cmpxchg_called_with_bad_pointer(void) |
---|
38 | 29 | __compiletime_error("Bad argument size for cmpxchg"); |
---|
| 30 | +extern unsigned long __cmpxchg64_unsupported(void) |
---|
| 31 | + __compiletime_error("cmpxchg64 not available; cpu_has_64bits may be false"); |
---|
39 | 32 | extern unsigned long __xchg_called_with_bad_pointer(void) |
---|
40 | 33 | __compiletime_error("Bad argument size for xchg"); |
---|
41 | 34 | |
---|
.. | .. |
---|
47 | 40 | __asm__ __volatile__( \ |
---|
48 | 41 | " .set push \n" \ |
---|
49 | 42 | " .set noat \n" \ |
---|
| 43 | + " .set push \n" \ |
---|
50 | 44 | " .set " MIPS_ISA_ARCH_LEVEL " \n" \ |
---|
| 45 | + " " __SYNC(full, loongson3_war) " \n" \ |
---|
51 | 46 | "1: " ld " %0, %2 # __xchg_asm \n" \ |
---|
52 | | - " .set mips0 \n" \ |
---|
| 47 | + " .set pop \n" \ |
---|
53 | 48 | " move $1, %z3 \n" \ |
---|
54 | 49 | " .set " MIPS_ISA_ARCH_LEVEL " \n" \ |
---|
55 | 50 | " " st " $1, %1 \n" \ |
---|
56 | | - "\t" __scbeqz " $1, 1b \n" \ |
---|
| 51 | + "\t" __SC_BEQZ "$1, 1b \n" \ |
---|
57 | 52 | " .set pop \n" \ |
---|
58 | 53 | : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \ |
---|
59 | 54 | : GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \ |
---|
60 | | - : "memory"); \ |
---|
| 55 | + : __LLSC_CLOBBER); \ |
---|
61 | 56 | } else { \ |
---|
62 | 57 | unsigned long __flags; \ |
---|
63 | 58 | \ |
---|
.. | .. |
---|
99 | 94 | ({ \ |
---|
100 | 95 | __typeof__(*(ptr)) __res; \ |
---|
101 | 96 | \ |
---|
102 | | - smp_mb__before_llsc(); \ |
---|
| 97 | + /* \ |
---|
| 98 | + * In the Loongson3 workaround case __xchg_asm() already \ |
---|
| 99 | + * contains a completion barrier prior to the LL, so we don't \ |
---|
| 100 | + * need to emit an extra one here. \ |
---|
| 101 | + */ \ |
---|
| 102 | + if (__SYNC_loongson3_war == 0) \ |
---|
| 103 | + smp_mb__before_llsc(); \ |
---|
103 | 104 | \ |
---|
104 | 105 | __res = (__typeof__(*(ptr))) \ |
---|
105 | 106 | __xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \ |
---|
.. | .. |
---|
117 | 118 | __asm__ __volatile__( \ |
---|
118 | 119 | " .set push \n" \ |
---|
119 | 120 | " .set noat \n" \ |
---|
| 121 | + " .set push \n" \ |
---|
120 | 122 | " .set "MIPS_ISA_ARCH_LEVEL" \n" \ |
---|
| 123 | + " " __SYNC(full, loongson3_war) " \n" \ |
---|
121 | 124 | "1: " ld " %0, %2 # __cmpxchg_asm \n" \ |
---|
122 | 125 | " bne %0, %z3, 2f \n" \ |
---|
123 | | - " .set mips0 \n" \ |
---|
| 126 | + " .set pop \n" \ |
---|
124 | 127 | " move $1, %z4 \n" \ |
---|
125 | 128 | " .set "MIPS_ISA_ARCH_LEVEL" \n" \ |
---|
126 | 129 | " " st " $1, %1 \n" \ |
---|
127 | | - "\t" __scbeqz " $1, 1b \n" \ |
---|
| 130 | + "\t" __SC_BEQZ "$1, 1b \n" \ |
---|
128 | 131 | " .set pop \n" \ |
---|
129 | | - "2: \n" \ |
---|
| 132 | + "2: " __SYNC(full, loongson3_war) " \n" \ |
---|
130 | 133 | : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \ |
---|
131 | | - : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \ |
---|
132 | | - : "memory"); \ |
---|
| 134 | + : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \ |
---|
| 135 | + : __LLSC_CLOBBER); \ |
---|
133 | 136 | } else { \ |
---|
134 | 137 | unsigned long __flags; \ |
---|
135 | 138 | \ |
---|
.. | .. |
---|
183 | 186 | ({ \ |
---|
184 | 187 | __typeof__(*(ptr)) __res; \ |
---|
185 | 188 | \ |
---|
186 | | - smp_mb__before_llsc(); \ |
---|
| 189 | + /* \ |
---|
| 190 | + * In the Loongson3 workaround case __cmpxchg_asm() already \ |
---|
| 191 | + * contains a completion barrier prior to the LL, so we don't \ |
---|
| 192 | + * need to emit an extra one here. \ |
---|
| 193 | + */ \ |
---|
| 194 | + if (__SYNC_loongson3_war == 0) \ |
---|
| 195 | + smp_mb__before_llsc(); \ |
---|
| 196 | + \ |
---|
187 | 197 | __res = cmpxchg_local((ptr), (old), (new)); \ |
---|
188 | | - smp_llsc_mb(); \ |
---|
| 198 | + \ |
---|
| 199 | + /* \ |
---|
| 200 | + * In the Loongson3 workaround case __cmpxchg_asm() already \ |
---|
| 201 | + * contains a completion barrier after the SC, so we don't \ |
---|
| 202 | + * need to emit an extra one here. \ |
---|
| 203 | + */ \ |
---|
| 204 | + if (__SYNC_loongson3_war == 0) \ |
---|
| 205 | + smp_llsc_mb(); \ |
---|
189 | 206 | \ |
---|
190 | 207 | __res; \ |
---|
191 | 208 | }) |
---|
.. | .. |
---|
203 | 220 | cmpxchg((ptr), (o), (n)); \ |
---|
204 | 221 | }) |
---|
205 | 222 | #else |
---|
206 | | -#include <asm-generic/cmpxchg-local.h> |
---|
207 | | -#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) |
---|
208 | | -#ifndef CONFIG_SMP |
---|
209 | | -#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) |
---|
210 | | -#endif |
---|
211 | | -#endif |
---|
212 | 223 | |
---|
213 | | -#undef __scbeqz |
---|
| 224 | +# include <asm-generic/cmpxchg-local.h> |
---|
| 225 | +# define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) |
---|
| 226 | + |
---|
| 227 | +# ifdef CONFIG_SMP |
---|
| 228 | + |
---|
| 229 | +static inline unsigned long __cmpxchg64(volatile void *ptr, |
---|
| 230 | + unsigned long long old, |
---|
| 231 | + unsigned long long new) |
---|
| 232 | +{ |
---|
| 233 | + unsigned long long tmp, ret; |
---|
| 234 | + unsigned long flags; |
---|
| 235 | + |
---|
| 236 | + /* |
---|
| 237 | + * The assembly below has to combine 32 bit values into a 64 bit |
---|
| 238 | + * register, and split 64 bit values from one register into two. If we |
---|
| 239 | + * were to take an interrupt in the middle of this we'd only save the |
---|
| 240 | + * least significant 32 bits of each register & probably clobber the |
---|
| 241 | + * most significant 32 bits of the 64 bit values we're using. In order |
---|
| 242 | + * to avoid this we must disable interrupts. |
---|
| 243 | + */ |
---|
| 244 | + local_irq_save(flags); |
---|
| 245 | + |
---|
| 246 | + asm volatile( |
---|
| 247 | + " .set push \n" |
---|
| 248 | + " .set " MIPS_ISA_ARCH_LEVEL " \n" |
---|
| 249 | + /* Load 64 bits from ptr */ |
---|
| 250 | + " " __SYNC(full, loongson3_war) " \n" |
---|
| 251 | + "1: lld %L0, %3 # __cmpxchg64 \n" |
---|
| 252 | + " .set pop \n" |
---|
| 253 | + /* |
---|
| 254 | + * Split the 64 bit value we loaded into the 2 registers that hold the |
---|
| 255 | + * ret variable. |
---|
| 256 | + */ |
---|
| 257 | + " dsra %M0, %L0, 32 \n" |
---|
| 258 | + " sll %L0, %L0, 0 \n" |
---|
| 259 | + /* |
---|
| 260 | + * Compare ret against old, breaking out of the loop if they don't |
---|
| 261 | + * match. |
---|
| 262 | + */ |
---|
| 263 | + " bne %M0, %M4, 2f \n" |
---|
| 264 | + " bne %L0, %L4, 2f \n" |
---|
| 265 | + /* |
---|
| 266 | + * Combine the 32 bit halves from the 2 registers that hold the new |
---|
| 267 | + * variable into a single 64 bit register. |
---|
| 268 | + */ |
---|
| 269 | +# if MIPS_ISA_REV >= 2 |
---|
| 270 | + " move %L1, %L5 \n" |
---|
| 271 | + " dins %L1, %M5, 32, 32 \n" |
---|
| 272 | +# else |
---|
| 273 | + " dsll %L1, %L5, 32 \n" |
---|
| 274 | + " dsrl %L1, %L1, 32 \n" |
---|
| 275 | + " .set noat \n" |
---|
| 276 | + " dsll $at, %M5, 32 \n" |
---|
| 277 | + " or %L1, %L1, $at \n" |
---|
| 278 | + " .set at \n" |
---|
| 279 | +# endif |
---|
| 280 | + " .set push \n" |
---|
| 281 | + " .set " MIPS_ISA_ARCH_LEVEL " \n" |
---|
| 282 | + /* Attempt to store new at ptr */ |
---|
| 283 | + " scd %L1, %2 \n" |
---|
| 284 | + /* If we failed, loop! */ |
---|
| 285 | + "\t" __SC_BEQZ "%L1, 1b \n" |
---|
| 286 | + "2: " __SYNC(full, loongson3_war) " \n" |
---|
| 287 | + " .set pop \n" |
---|
| 288 | + : "=&r"(ret), |
---|
| 289 | + "=&r"(tmp), |
---|
| 290 | + "=" GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr) |
---|
| 291 | + : GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr), |
---|
| 292 | + "r" (old), |
---|
| 293 | + "r" (new) |
---|
| 294 | + : "memory"); |
---|
| 295 | + |
---|
| 296 | + local_irq_restore(flags); |
---|
| 297 | + return ret; |
---|
| 298 | +} |
---|
| 299 | + |
---|
| 300 | +# define cmpxchg64(ptr, o, n) ({ \ |
---|
| 301 | + unsigned long long __old = (__typeof__(*(ptr)))(o); \ |
---|
| 302 | + unsigned long long __new = (__typeof__(*(ptr)))(n); \ |
---|
| 303 | + __typeof__(*(ptr)) __res; \ |
---|
| 304 | + \ |
---|
| 305 | + /* \ |
---|
| 306 | + * We can only use cmpxchg64 if we know that the CPU supports \ |
---|
| 307 | + * 64-bits, ie. lld & scd. Our call to __cmpxchg64_unsupported \ |
---|
| 308 | + * will cause a build error unless cpu_has_64bits is a \ |
---|
| 309 | + * compile-time constant 1. \ |
---|
| 310 | + */ \ |
---|
| 311 | + if (cpu_has_64bits && kernel_uses_llsc) { \ |
---|
| 312 | + smp_mb__before_llsc(); \ |
---|
| 313 | + __res = __cmpxchg64((ptr), __old, __new); \ |
---|
| 314 | + smp_llsc_mb(); \ |
---|
| 315 | + } else { \ |
---|
| 316 | + __res = __cmpxchg64_unsupported(); \ |
---|
| 317 | + } \ |
---|
| 318 | + \ |
---|
| 319 | + __res; \ |
---|
| 320 | +}) |
---|
| 321 | + |
---|
| 322 | +# else /* !CONFIG_SMP */ |
---|
| 323 | +# define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) |
---|
| 324 | +# endif /* !CONFIG_SMP */ |
---|
| 325 | +#endif /* !CONFIG_64BIT */ |
---|
214 | 326 | |
---|
215 | 327 | #endif /* __ASM_CMPXCHG_H */ |
---|