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, 06, 07 by Ralf Baechle (ralf@linux-mips.org) 7 */ 8 #ifndef __ASM_CMPXCHG_H 9 #define __ASM_CMPXCHG_H 10 11 #include <linux/bug.h> 12 #include <linux/irqflags.h> 13 #include <asm/compiler.h> 14 #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 27 /* 28 * These functions doesn't exist, so if they are called you'll either: 29 * 30 * - Get an error at compile-time due to __compiletime_error, if supported by 31 * your compiler. 32 * 33 * or: 34 * 35 * - Get an error at link-time due to the call to the missing function. 36 */ 37 extern unsigned long __cmpxchg_called_with_bad_pointer(void) 38 __compiletime_error("Bad argument size for cmpxchg"); 39 extern unsigned long __cmpxchg64_unsupported(void) 40 __compiletime_error("cmpxchg64 not available; cpu_has_64bits may be false"); 41 extern unsigned long __xchg_called_with_bad_pointer(void) 42 __compiletime_error("Bad argument size for xchg"); 43 44 #define __xchg_asm(ld, st, m, val) \ 45 ({ \ 46 __typeof(*(m)) __ret; \ 47 \ 48 if (kernel_uses_llsc) { \ 49 __asm__ __volatile__( \ 50 " .set push \n" \ 51 " .set noat \n" \ 52 " .set push \n" \ 53 " .set " MIPS_ISA_ARCH_LEVEL " \n" \ 54 "1: " ld " %0, %2 # __xchg_asm \n" \ 55 " .set pop \n" \ 56 " move $1, %z3 \n" \ 57 " .set " MIPS_ISA_ARCH_LEVEL " \n" \ 58 " " st " $1, %1 \n" \ 59 "\t" __scbeqz " $1, 1b \n" \ 60 " .set pop \n" \ 61 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \ 62 : GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \ 63 : "memory"); \ 64 } else { \ 65 unsigned long __flags; \ 66 \ 67 raw_local_irq_save(__flags); \ 68 __ret = *m; \ 69 *m = val; \ 70 raw_local_irq_restore(__flags); \ 71 } \ 72 \ 73 __ret; \ 74 }) 75 76 extern unsigned long __xchg_small(volatile void *ptr, unsigned long val, 77 unsigned int size); 78 79 static inline unsigned long __xchg(volatile void *ptr, unsigned long x, 80 int size) 81 { 82 switch (size) { 83 case 1: 84 case 2: 85 return __xchg_small(ptr, x, size); 86 87 case 4: 88 return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x); 89 90 case 8: 91 if (!IS_ENABLED(CONFIG_64BIT)) 92 return __xchg_called_with_bad_pointer(); 93 94 return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x); 95 96 default: 97 return __xchg_called_with_bad_pointer(); 98 } 99 } 100 101 #define xchg(ptr, x) \ 102 ({ \ 103 __typeof__(*(ptr)) __res; \ 104 \ 105 smp_mb__before_llsc(); \ 106 \ 107 __res = (__typeof__(*(ptr))) \ 108 __xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \ 109 \ 110 smp_llsc_mb(); \ 111 \ 112 __res; \ 113 }) 114 115 #define __cmpxchg_asm(ld, st, m, old, new) \ 116 ({ \ 117 __typeof(*(m)) __ret; \ 118 \ 119 if (kernel_uses_llsc) { \ 120 __asm__ __volatile__( \ 121 " .set push \n" \ 122 " .set noat \n" \ 123 " .set push \n" \ 124 " .set "MIPS_ISA_ARCH_LEVEL" \n" \ 125 "1: " ld " %0, %2 # __cmpxchg_asm \n" \ 126 " bne %0, %z3, 2f \n" \ 127 " .set pop \n" \ 128 " move $1, %z4 \n" \ 129 " .set "MIPS_ISA_ARCH_LEVEL" \n" \ 130 " " st " $1, %1 \n" \ 131 "\t" __scbeqz " $1, 1b \n" \ 132 " .set pop \n" \ 133 "2: \n" \ 134 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \ 135 : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \ 136 : "memory"); \ 137 } else { \ 138 unsigned long __flags; \ 139 \ 140 raw_local_irq_save(__flags); \ 141 __ret = *m; \ 142 if (__ret == old) \ 143 *m = new; \ 144 raw_local_irq_restore(__flags); \ 145 } \ 146 \ 147 __ret; \ 148 }) 149 150 extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old, 151 unsigned long new, unsigned int size); 152 153 static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, 154 unsigned long new, unsigned int size) 155 { 156 switch (size) { 157 case 1: 158 case 2: 159 return __cmpxchg_small(ptr, old, new, size); 160 161 case 4: 162 return __cmpxchg_asm("ll", "sc", (volatile u32 *)ptr, 163 (u32)old, new); 164 165 case 8: 166 /* lld/scd are only available for MIPS64 */ 167 if (!IS_ENABLED(CONFIG_64BIT)) 168 return __cmpxchg_called_with_bad_pointer(); 169 170 return __cmpxchg_asm("lld", "scd", (volatile u64 *)ptr, 171 (u64)old, new); 172 173 default: 174 return __cmpxchg_called_with_bad_pointer(); 175 } 176 } 177 178 #define cmpxchg_local(ptr, old, new) \ 179 ((__typeof__(*(ptr))) \ 180 __cmpxchg((ptr), \ 181 (unsigned long)(__typeof__(*(ptr)))(old), \ 182 (unsigned long)(__typeof__(*(ptr)))(new), \ 183 sizeof(*(ptr)))) 184 185 #define cmpxchg(ptr, old, new) \ 186 ({ \ 187 __typeof__(*(ptr)) __res; \ 188 \ 189 smp_mb__before_llsc(); \ 190 __res = cmpxchg_local((ptr), (old), (new)); \ 191 smp_llsc_mb(); \ 192 \ 193 __res; \ 194 }) 195 196 #ifdef CONFIG_64BIT 197 #define cmpxchg64_local(ptr, o, n) \ 198 ({ \ 199 BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ 200 cmpxchg_local((ptr), (o), (n)); \ 201 }) 202 203 #define cmpxchg64(ptr, o, n) \ 204 ({ \ 205 BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ 206 cmpxchg((ptr), (o), (n)); \ 207 }) 208 #else 209 210 # include <asm-generic/cmpxchg-local.h> 211 # define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) 212 213 # ifdef CONFIG_SMP 214 215 static inline unsigned long __cmpxchg64(volatile void *ptr, 216 unsigned long long old, 217 unsigned long long new) 218 { 219 unsigned long long tmp, ret; 220 unsigned long flags; 221 222 /* 223 * The assembly below has to combine 32 bit values into a 64 bit 224 * register, and split 64 bit values from one register into two. If we 225 * were to take an interrupt in the middle of this we'd only save the 226 * least significant 32 bits of each register & probably clobber the 227 * most significant 32 bits of the 64 bit values we're using. In order 228 * to avoid this we must disable interrupts. 229 */ 230 local_irq_save(flags); 231 232 asm volatile( 233 " .set push \n" 234 " .set " MIPS_ISA_ARCH_LEVEL " \n" 235 /* Load 64 bits from ptr */ 236 "1: lld %L0, %3 # __cmpxchg64 \n" 237 /* 238 * Split the 64 bit value we loaded into the 2 registers that hold the 239 * ret variable. 240 */ 241 " dsra %M0, %L0, 32 \n" 242 " sll %L0, %L0, 0 \n" 243 /* 244 * Compare ret against old, breaking out of the loop if they don't 245 * match. 246 */ 247 " bne %M0, %M4, 2f \n" 248 " bne %L0, %L4, 2f \n" 249 /* 250 * Combine the 32 bit halves from the 2 registers that hold the new 251 * variable into a single 64 bit register. 252 */ 253 # if MIPS_ISA_REV >= 2 254 " move %L1, %L5 \n" 255 " dins %L1, %M5, 32, 32 \n" 256 # else 257 " dsll %L1, %L5, 32 \n" 258 " dsrl %L1, %L1, 32 \n" 259 " .set noat \n" 260 " dsll $at, %M5, 32 \n" 261 " or %L1, %L1, $at \n" 262 " .set at \n" 263 # endif 264 /* Attempt to store new at ptr */ 265 " scd %L1, %2 \n" 266 /* If we failed, loop! */ 267 "\t" __scbeqz " %L1, 1b \n" 268 " .set pop \n" 269 "2: \n" 270 : "=&r"(ret), 271 "=&r"(tmp), 272 "=" GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr) 273 : GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr), 274 "r" (old), 275 "r" (new) 276 : "memory"); 277 278 local_irq_restore(flags); 279 return ret; 280 } 281 282 # define cmpxchg64(ptr, o, n) ({ \ 283 unsigned long long __old = (__typeof__(*(ptr)))(o); \ 284 unsigned long long __new = (__typeof__(*(ptr)))(n); \ 285 __typeof__(*(ptr)) __res; \ 286 \ 287 /* \ 288 * We can only use cmpxchg64 if we know that the CPU supports \ 289 * 64-bits, ie. lld & scd. Our call to __cmpxchg64_unsupported \ 290 * will cause a build error unless cpu_has_64bits is a \ 291 * compile-time constant 1. \ 292 */ \ 293 if (cpu_has_64bits && kernel_uses_llsc) \ 294 __res = __cmpxchg64((ptr), __old, __new); \ 295 else \ 296 __res = __cmpxchg64_unsupported(); \ 297 \ 298 __res; \ 299 }) 300 301 # else /* !CONFIG_SMP */ 302 # define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) 303 # endif /* !CONFIG_SMP */ 304 #endif /* !CONFIG_64BIT */ 305 306 #undef __scbeqz 307 308 #endif /* __ASM_CMPXCHG_H */ 309