1 /*- 2 * Copyright (c) 2001 Benno Rice 3 * Copyright (c) 2001 David E. O'Brien 4 * Copyright (c) 1998 Doug Rabson 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #ifndef _MACHINE_ATOMIC_H_ 32 #define _MACHINE_ATOMIC_H_ 33 34 #include <machine/cpufunc.h> 35 36 #ifndef _SYS_CDEFS_H_ 37 #error this file needs sys/cdefs.h as a prerequisite 38 #endif 39 40 /* 41 * Various simple arithmetic on memory which is atomic in the presence 42 * of interrupts and SMP safe. 43 */ 44 45 void atomic_set_8(volatile uint8_t *, uint8_t); 46 void atomic_clear_8(volatile uint8_t *, uint8_t); 47 void atomic_add_8(volatile uint8_t *, uint8_t); 48 void atomic_subtract_8(volatile uint8_t *, uint8_t); 49 50 void atomic_set_16(volatile uint16_t *, uint16_t); 51 void atomic_clear_16(volatile uint16_t *, uint16_t); 52 void atomic_add_16(volatile uint16_t *, uint16_t); 53 void atomic_subtract_16(volatile uint16_t *, uint16_t); 54 55 static __inline void 56 atomic_set_32(volatile uint32_t *p, uint32_t v) 57 { 58 uint32_t temp; 59 60 #ifdef __GNUCLIKE_ASM 61 __asm __volatile ( 62 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 63 "or %0, %3, %0\n\t" /* calculate new value */ 64 "stwcx. %0, 0, %2\n\t" /* attempt to store */ 65 "bne- 1b\n\t" /* spin if failed */ 66 : "=&r" (temp), "+m" (*p) 67 : "r" (p), "r" (v) 68 : "cc", "memory"); 69 #endif 70 } 71 72 static __inline void 73 atomic_clear_32(volatile uint32_t *p, uint32_t v) 74 { 75 uint32_t temp; 76 77 #ifdef __GNUCLIKE_ASM 78 __asm __volatile ( 79 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 80 "andc %0, %0, %3\n\t" /* calculate new value */ 81 "stwcx. %0, 0, %2\n\t" /* attempt to store */ 82 "bne- 1b\n\t" /* spin if failed */ 83 : "=&r" (temp), "+m" (*p) 84 : "r" (p), "r" (v) 85 : "cc", "memory"); 86 #endif 87 } 88 89 static __inline void 90 atomic_add_32(volatile uint32_t *p, uint32_t v) 91 { 92 uint32_t temp; 93 94 #ifdef __GNUCLIKE_ASM 95 __asm __volatile ( 96 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 97 "add %0, %3, %0\n\t" /* calculate new value */ 98 "stwcx. %0, 0, %2\n\t" /* attempt to store */ 99 "bne- 1b\n\t" /* spin if failed */ 100 : "=&r" (temp), "+m" (*p) 101 : "r" (p), "r" (v) 102 : "cc", "memory"); 103 #endif 104 } 105 106 static __inline void 107 atomic_subtract_32(volatile uint32_t *p, uint32_t v) 108 { 109 uint32_t temp; 110 111 #ifdef __GNUCLIKE_ASM 112 __asm __volatile ( 113 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 114 "subf %0, %3, %0\n\t" /* calculate new value */ 115 "stwcx. %0, 0, %2\n\t" /* attempt to store */ 116 "bne- 1b\n\t" /* spin if failed */ 117 : "=&r" (temp), "+m" (*p) 118 : "r" (p), "r" (v) 119 : "cc", "memory"); 120 #endif 121 } 122 123 static __inline uint32_t 124 atomic_readandclear_32(volatile uint32_t *addr) 125 { 126 uint32_t result,temp; 127 128 #ifdef __GNUCLIKE_ASM 129 __asm __volatile ( 130 "\tsync\n" /* drain writes */ 131 "1:\tlwarx %0, 0, %3\n\t" /* load old value */ 132 "li %1, 0\n\t" /* load new value */ 133 "stwcx. %1, 0, %3\n\t" /* attempt to store */ 134 "bne- 1b\n\t" /* spin if failed */ 135 : "=&r"(result), "=&r"(temp), "+m" (*addr) 136 : "r" (addr) 137 : "cc", "memory"); 138 #endif 139 140 return (result); 141 } 142 143 #if 0 144 145 /* 146 * So far I haven't found a way to implement atomic 64-bit ops on the 147 * 32-bit PowerPC without involving major headaches. If anyone has 148 * any ideas, please let me know. =) 149 * - benno@FreeBSD.org 150 */ 151 152 static __inline void 153 atomic_set_64(volatile u_int64_t *p, u_int64_t v) 154 { 155 u_int64_t temp; 156 157 __asm __volatile ( 158 : "=&r" (temp), "=r" (*p) 159 : "r" (*p), "r" (v) 160 : "memory"); 161 } 162 163 static __inline void 164 atomic_clear_64(volatile u_int64_t *p, u_int64_t v) 165 { 166 u_int64_t temp; 167 168 __asm __volatile ( 169 : "=&r" (temp), "=r" (*p) 170 : "r" (*p), "r" (v) 171 : "memory"); 172 } 173 174 static __inline void 175 atomic_add_64(volatile u_int64_t *p, u_int64_t v) 176 { 177 u_int64_t temp; 178 179 __asm __volatile ( 180 : "=&r" (temp), "=r" (*p) 181 : "r" (*p), "r" (v) 182 : "memory"); 183 } 184 185 static __inline void 186 atomic_subtract_64(volatile u_int64_t *p, u_int64_t v) 187 { 188 u_int64_t temp; 189 190 __asm __volatile ( 191 : "=&r" (temp), "=r" (*p) 192 : "r" (*p), "r" (v) 193 : "memory"); 194 } 195 196 static __inline u_int64_t 197 atomic_readandclear_64(volatile u_int64_t *addr) 198 { 199 u_int64_t result,temp; 200 201 __asm __volatile ( 202 : "=&r"(result), "=&r"(temp), "=r" (*addr) 203 : "r"(*addr) 204 : "memory"); 205 206 return result; 207 } 208 209 #endif /* 0 */ 210 211 #define atomic_set_char atomic_set_8 212 #define atomic_clear_char atomic_clear_8 213 #define atomic_add_char atomic_add_8 214 #define atomic_subtract_char atomic_subtract_8 215 216 #define atomic_set_short atomic_set_16 217 #define atomic_clear_short atomic_clear_16 218 #define atomic_add_short atomic_add_16 219 #define atomic_subtract_short atomic_subtract_16 220 221 #define atomic_set_int atomic_set_32 222 #define atomic_clear_int atomic_clear_32 223 #define atomic_add_int atomic_add_32 224 #define atomic_subtract_int atomic_subtract_32 225 #define atomic_readandclear_int atomic_readandclear_32 226 227 #define atomic_set_long atomic_set_32 228 #define atomic_clear_long atomic_clear_32 229 #define atomic_add_long(p, v) atomic_add_32((uint32_t *)p, (uint32_t)v) 230 #define atomic_subtract_long(p, v) atomic_subtract_32((uint32_t *)p, (uint32_t)v) 231 #define atomic_readandclear_long atomic_readandclear_32 232 233 #if 0 234 235 /* See above. */ 236 237 #define atomic_set_long_long atomic_set_64 238 #define atomic_clear_long_long atomic_clear_64 239 #define atomic_add_long_long atomic_add_64 240 #define atomic_subtract_long_long atomic_subtract_64 241 #define atomic_readandclear_long_long atomic_readandclear_64 242 243 #endif /* 0 */ 244 245 #define ATOMIC_ACQ_REL(NAME, WIDTH, TYPE) \ 246 static __inline void \ 247 atomic_##NAME##_acq_##WIDTH(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \ 248 { \ 249 atomic_##NAME##_##WIDTH(p, v); \ 250 powerpc_mb(); \ 251 } \ 252 \ 253 static __inline void \ 254 atomic_##NAME##_rel_##WIDTH(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \ 255 { \ 256 powerpc_mb(); \ 257 atomic_##NAME##_##WIDTH(p, v); \ 258 } \ 259 \ 260 static __inline void \ 261 atomic_##NAME##_acq_##TYPE(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \ 262 { \ 263 atomic_##NAME##_##WIDTH(p, v); \ 264 powerpc_mb(); \ 265 } \ 266 \ 267 static __inline void \ 268 atomic_##NAME##_rel_##TYPE(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \ 269 { \ 270 powerpc_mb(); \ 271 atomic_##NAME##_##WIDTH(p, v); \ 272 } 273 274 ATOMIC_ACQ_REL(set, 8, char) 275 ATOMIC_ACQ_REL(clear, 8, char) 276 ATOMIC_ACQ_REL(add, 8, char) 277 ATOMIC_ACQ_REL(subtract, 8, char) 278 ATOMIC_ACQ_REL(set, 16, short) 279 ATOMIC_ACQ_REL(clear, 16, short) 280 ATOMIC_ACQ_REL(add, 16, short) 281 ATOMIC_ACQ_REL(subtract, 16, short) 282 ATOMIC_ACQ_REL(set, 32, int) 283 ATOMIC_ACQ_REL(clear, 32, int) 284 ATOMIC_ACQ_REL(add, 32, int) 285 ATOMIC_ACQ_REL(subtract, 32, int) 286 287 #define atomic_set_acq_long atomic_set_acq_32 288 #define atomic_set_rel_long atomic_set_rel_32 289 #define atomic_clear_acq_long atomic_clear_acq_32 290 #define atomic_clear_rel_long atomic_clear_rel_32 291 #define atomic_add_acq_long atomic_add_acq_32 292 #define atomic_add_rel_long atomic_add_rel_32 293 #define atomic_subtract_acq_long atomic_subtract_acq_32 294 #define atomic_subtract_rel_long atomic_subtract_rel_32 295 296 #undef ATOMIC_ACQ_REL 297 298 /* 299 * We assume that a = b will do atomic loads and stores. 300 */ 301 #define ATOMIC_STORE_LOAD(TYPE, WIDTH) \ 302 static __inline u_##TYPE \ 303 atomic_load_acq_##WIDTH(volatile u_##TYPE *p) \ 304 { \ 305 u_##TYPE v; \ 306 \ 307 v = *p; \ 308 powerpc_mb(); \ 309 return (v); \ 310 } \ 311 \ 312 static __inline void \ 313 atomic_store_rel_##WIDTH(volatile u_##TYPE *p, u_##TYPE v) \ 314 { \ 315 powerpc_mb(); \ 316 *p = v; \ 317 } \ 318 \ 319 static __inline u_##TYPE \ 320 atomic_load_acq_##TYPE(volatile u_##TYPE *p) \ 321 { \ 322 u_##TYPE v; \ 323 \ 324 v = *p; \ 325 powerpc_mb(); \ 326 return (v); \ 327 } \ 328 \ 329 static __inline void \ 330 atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v) \ 331 { \ 332 powerpc_mb(); \ 333 *p = v; \ 334 } 335 336 ATOMIC_STORE_LOAD(char, 8) 337 ATOMIC_STORE_LOAD(short, 16) 338 ATOMIC_STORE_LOAD(int, 32) 339 340 #define atomic_load_acq_long atomic_load_acq_32 341 #define atomic_store_rel_long atomic_store_rel_32 342 343 #undef ATOMIC_STORE_LOAD 344 345 /* 346 * Atomically compare the value stored at *p with cmpval and if the 347 * two values are equal, update the value of *p with newval. Returns 348 * zero if the compare failed, nonzero otherwise. 349 */ 350 static __inline uint32_t 351 atomic_cmpset_32(volatile uint32_t* p, uint32_t cmpval, uint32_t newval) 352 { 353 uint32_t ret; 354 355 #ifdef __GNUCLIKE_ASM 356 __asm __volatile ( 357 "1:\tlwarx %0, 0, %2\n\t" /* load old value */ 358 "cmplw %3, %0\n\t" /* compare */ 359 "bne 2f\n\t" /* exit if not equal */ 360 "stwcx. %4, 0, %2\n\t" /* attempt to store */ 361 "bne- 1b\n\t" /* spin if failed */ 362 "li %0, 1\n\t" /* success - retval = 1 */ 363 "b 3f\n\t" /* we've succeeded */ 364 "2:\n\t" 365 "stwcx. %0, 0, %2\n\t" /* clear reservation (74xx) */ 366 "li %0, 0\n\t" /* failure - retval = 0 */ 367 "3:\n\t" 368 : "=&r" (ret), "+m" (*p) 369 : "r" (p), "r" (cmpval), "r" (newval) 370 : "cc", "memory"); 371 #endif 372 373 return (ret); 374 } 375 376 #if 0 377 378 /* 379 * Atomically compare the value stored at *p with cmpval and if the 380 * two values are equal, update the value of *p with newval. Returns 381 * zero if the compare failed, nonzero otherwise. 382 */ 383 static __inline u_int64_t 384 atomic_cmpset_64(volatile u_int64_t* p, u_int64_t cmpval, u_int64_t newval) 385 { 386 u_int64_t ret; 387 388 __asm __volatile ( 389 : "=&r" (ret), "=r" (*p) 390 : "r" (cmpval), "r" (newval), "r" (*p) 391 : "memory"); 392 393 return ret; 394 } 395 396 #endif /* 0 */ 397 398 #define atomic_cmpset_int atomic_cmpset_32 399 #define atomic_cmpset_long atomic_cmpset_32 400 401 #if 0 402 #define atomic_cmpset_long_long atomic_cmpset_64 403 #endif /* 0 */ 404 405 static __inline int 406 atomic_cmpset_ptr(volatile void *dst, void *exp, void *src) 407 { 408 409 return (atomic_cmpset_32((volatile uint32_t *)dst, (uint32_t)exp, 410 (uint32_t)src)); 411 } 412 413 static __inline uint32_t 414 atomic_cmpset_acq_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval) 415 { 416 int retval; 417 418 retval = atomic_cmpset_32(p, cmpval, newval); 419 powerpc_mb(); 420 return (retval); 421 } 422 423 static __inline uint32_t 424 atomic_cmpset_rel_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval) 425 { 426 powerpc_mb(); 427 return (atomic_cmpset_32(p, cmpval, newval)); 428 } 429 430 #define atomic_cmpset_acq_int atomic_cmpset_acq_32 431 #define atomic_cmpset_rel_int atomic_cmpset_rel_32 432 #define atomic_cmpset_acq_long atomic_cmpset_acq_32 433 #define atomic_cmpset_rel_long atomic_cmpset_rel_32 434 435 static __inline int 436 atomic_cmpset_acq_ptr(volatile void *dst, void *exp, void *src) 437 { 438 439 return (atomic_cmpset_acq_32((volatile uint32_t *)dst, 440 (uint32_t)exp, (uint32_t)src)); 441 } 442 443 static __inline int 444 atomic_cmpset_rel_ptr(volatile void *dst, void *exp, void *src) 445 { 446 447 return (atomic_cmpset_rel_32((volatile uint32_t *)dst, 448 (uint32_t)exp, (uint32_t)src)); 449 } 450 451 static __inline void * 452 atomic_load_acq_ptr(volatile void *p) 453 { 454 455 return (void *)atomic_load_acq_32((volatile uint32_t *)p); 456 } 457 458 static __inline void 459 atomic_store_rel_ptr(volatile void *p, void *v) 460 { 461 462 atomic_store_rel_32((volatile uint32_t *)p, (uint32_t)v); 463 } 464 465 #define ATOMIC_PTR(NAME) \ 466 static __inline void \ 467 atomic_##NAME##_ptr(volatile void *p, uintptr_t v) \ 468 { \ 469 atomic_##NAME##_32((volatile uint32_t *)p, v); \ 470 } \ 471 \ 472 static __inline void \ 473 atomic_##NAME##_acq_ptr(volatile void *p, uintptr_t v) \ 474 { \ 475 atomic_##NAME##_acq_32((volatile uint32_t *)p, v); \ 476 } \ 477 \ 478 static __inline void \ 479 atomic_##NAME##_rel_ptr(volatile void *p, uintptr_t v) \ 480 { \ 481 atomic_##NAME##_rel_32((volatile uint32_t *)p, v); \ 482 } 483 484 ATOMIC_PTR(set) 485 ATOMIC_PTR(clear) 486 ATOMIC_PTR(add) 487 ATOMIC_PTR(subtract) 488 489 #undef ATOMIC_PTR 490 #endif /* ! _MACHINE_ATOMIC_H_ */ 491