atomic-v6.h revision 308327
1/* $NetBSD: atomic.h,v 1.1 2002/10/19 12:22:34 bsh Exp $ */ 2 3/*- 4 * Copyright (C) 2003-2004 Olivier Houchard 5 * Copyright (C) 1994-1997 Mark Brinicombe 6 * Copyright (C) 1994 Brini 7 * All rights reserved. 8 * 9 * This code is derived from software written for Brini by Mark Brinicombe 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Brini. 22 * 4. The name of Brini may not be used to endorse or promote products 23 * derived from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL BRINI BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 31 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 33 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 34 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 * 36 * $FreeBSD: stable/11/sys/arm/include/atomic-v6.h 308327 2016-11-05 04:36:12Z mmel $ 37 */ 38 39#ifndef _MACHINE_ATOMIC_V6_H_ 40#define _MACHINE_ATOMIC_V6_H_ 41 42#ifndef _MACHINE_ATOMIC_H_ 43#error Do not include this file directly, use <machine/atomic.h> 44#endif 45 46#if __ARM_ARCH >= 7 47#define isb() __asm __volatile("isb" : : : "memory") 48#define dsb() __asm __volatile("dsb" : : : "memory") 49#define dmb() __asm __volatile("dmb" : : : "memory") 50#elif __ARM_ARCH >= 6 51#define isb() __asm __volatile("mcr p15, 0, %0, c7, c5, 4" : : "r" (0) : "memory") 52#define dsb() __asm __volatile("mcr p15, 0, %0, c7, c10, 4" : : "r" (0) : "memory") 53#define dmb() __asm __volatile("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory") 54#else 55#error Only use this file with ARMv6 and later 56#endif 57 58#define mb() dmb() 59#define wmb() dmb() 60#define rmb() dmb() 61 62#define ARM_HAVE_ATOMIC64 63 64#define ATOMIC_ACQ_REL_LONG(NAME) \ 65static __inline void \ 66atomic_##NAME##_acq_long(__volatile u_long *p, u_long v) \ 67{ \ 68 atomic_##NAME##_long(p, v); \ 69 dmb(); \ 70} \ 71 \ 72static __inline void \ 73atomic_##NAME##_rel_long(__volatile u_long *p, u_long v) \ 74{ \ 75 dmb(); \ 76 atomic_##NAME##_long(p, v); \ 77} 78 79#define ATOMIC_ACQ_REL(NAME, WIDTH) \ 80static __inline void \ 81atomic_##NAME##_acq_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\ 82{ \ 83 atomic_##NAME##_##WIDTH(p, v); \ 84 dmb(); \ 85} \ 86 \ 87static __inline void \ 88atomic_##NAME##_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\ 89{ \ 90 dmb(); \ 91 atomic_##NAME##_##WIDTH(p, v); \ 92} 93 94 95static __inline void 96atomic_add_32(volatile uint32_t *p, uint32_t val) 97{ 98 uint32_t tmp = 0, tmp2 = 0; 99 100 __asm __volatile( 101 "1: ldrex %0, [%2] \n" 102 " add %0, %0, %3 \n" 103 " strex %1, %0, [%2] \n" 104 " cmp %1, #0 \n" 105 " it ne \n" 106 " bne 1b \n" 107 : "=&r" (tmp), "+r" (tmp2) 108 ,"+r" (p), "+r" (val) : : "cc", "memory"); 109} 110 111static __inline void 112atomic_add_64(volatile uint64_t *p, uint64_t val) 113{ 114 uint64_t tmp; 115 uint32_t exflag; 116 117 __asm __volatile( 118 "1: \n" 119 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 120 " adds %Q[tmp], %Q[val] \n" 121 " adc %R[tmp], %R[tmp], %R[val] \n" 122 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n" 123 " teq %[exf], #0 \n" 124 " it ne \n" 125 " bne 1b \n" 126 : [exf] "=&r" (exflag), 127 [tmp] "=&r" (tmp) 128 : [ptr] "r" (p), 129 [val] "r" (val) 130 : "cc", "memory"); 131} 132 133static __inline void 134atomic_add_long(volatile u_long *p, u_long val) 135{ 136 137 atomic_add_32((volatile uint32_t *)p, val); 138} 139 140ATOMIC_ACQ_REL(add, 32) 141ATOMIC_ACQ_REL(add, 64) 142ATOMIC_ACQ_REL_LONG(add) 143 144static __inline void 145atomic_clear_32(volatile uint32_t *address, uint32_t setmask) 146{ 147 uint32_t tmp = 0, tmp2 = 0; 148 149 __asm __volatile( 150 "1: ldrex %0, [%2] \n" 151 " bic %0, %0, %3 \n" 152 " strex %1, %0, [%2] \n" 153 " cmp %1, #0 \n" 154 " it ne \n" 155 " bne 1b \n" 156 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask) 157 : : "cc", "memory"); 158} 159 160static __inline void 161atomic_clear_64(volatile uint64_t *p, uint64_t val) 162{ 163 uint64_t tmp; 164 uint32_t exflag; 165 166 __asm __volatile( 167 "1: \n" 168 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 169 " bic %Q[tmp], %Q[val] \n" 170 " bic %R[tmp], %R[val] \n" 171 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n" 172 " teq %[exf], #0 \n" 173 " it ne \n" 174 " bne 1b \n" 175 : [exf] "=&r" (exflag), 176 [tmp] "=&r" (tmp) 177 : [ptr] "r" (p), 178 [val] "r" (val) 179 : "cc", "memory"); 180} 181 182static __inline void 183atomic_clear_long(volatile u_long *address, u_long setmask) 184{ 185 186 atomic_clear_32((volatile uint32_t *)address, setmask); 187} 188 189ATOMIC_ACQ_REL(clear, 32) 190ATOMIC_ACQ_REL(clear, 64) 191ATOMIC_ACQ_REL_LONG(clear) 192 193static __inline uint32_t 194atomic_cmpset_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval) 195{ 196 uint32_t ret; 197 198 __asm __volatile( 199 "1: ldrex %0, [%1] \n" 200 " cmp %0, %2 \n" 201 " itt ne \n" 202 " movne %0, #0 \n" 203 " bne 2f \n" 204 " strex %0, %3, [%1] \n" 205 " cmp %0, #0 \n" 206 " ite eq \n" 207 " moveq %0, #1 \n" 208 " bne 1b \n" 209 "2:" 210 : "=&r" (ret), "+r" (p), "+r" (cmpval), "+r" (newval) 211 : : "cc", "memory"); 212 return (ret); 213} 214 215static __inline int 216atomic_cmpset_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval) 217{ 218 uint64_t tmp; 219 uint32_t ret; 220 221 __asm __volatile( 222 "1: \n" 223 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 224 " teq %Q[tmp], %Q[cmpval] \n" 225 " itee eq \n" 226 " teqeq %R[tmp], %R[cmpval] \n" 227 " movne %[ret], #0 \n" 228 " bne 2f \n" 229 " strexd %[ret], %Q[newval], %R[newval], [%[ptr]]\n" 230 " teq %[ret], #0 \n" 231 " it ne \n" 232 " bne 1b \n" 233 " mov %[ret], #1 \n" 234 "2: \n" 235 : [ret] "=&r" (ret), 236 [tmp] "=&r" (tmp) 237 : [ptr] "r" (p), 238 [cmpval] "r" (cmpval), 239 [newval] "r" (newval) 240 : "cc", "memory"); 241 return (ret); 242} 243 244static __inline u_long 245atomic_cmpset_long(volatile u_long *p, u_long cmpval, u_long newval) 246{ 247 248 return (atomic_cmpset_32((volatile uint32_t *)p, cmpval, newval)); 249} 250 251static __inline uint32_t 252atomic_cmpset_acq_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval) 253{ 254 uint32_t ret; 255 256 ret = atomic_cmpset_32(p, cmpval, newval); 257 dmb(); 258 return (ret); 259} 260 261static __inline uint64_t 262atomic_cmpset_acq_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval) 263{ 264 uint64_t ret; 265 266 ret = atomic_cmpset_64(p, cmpval, newval); 267 dmb(); 268 return (ret); 269} 270 271static __inline u_long 272atomic_cmpset_acq_long(volatile u_long *p, u_long cmpval, u_long newval) 273{ 274 u_long ret; 275 276 ret = atomic_cmpset_long(p, cmpval, newval); 277 dmb(); 278 return (ret); 279} 280 281static __inline uint32_t 282atomic_cmpset_rel_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval) 283{ 284 285 dmb(); 286 return (atomic_cmpset_32(p, cmpval, newval)); 287} 288 289static __inline uint64_t 290atomic_cmpset_rel_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval) 291{ 292 293 dmb(); 294 return (atomic_cmpset_64(p, cmpval, newval)); 295} 296 297static __inline u_long 298atomic_cmpset_rel_long(volatile u_long *p, u_long cmpval, u_long newval) 299{ 300 301 dmb(); 302 return (atomic_cmpset_long(p, cmpval, newval)); 303} 304 305static __inline uint32_t 306atomic_fetchadd_32(volatile uint32_t *p, uint32_t val) 307{ 308 uint32_t tmp = 0, tmp2 = 0, ret = 0; 309 310 __asm __volatile( 311 "1: ldrex %0, [%3] \n" 312 " add %1, %0, %4 \n" 313 " strex %2, %1, [%3] \n" 314 " cmp %2, #0 \n" 315 " it ne \n" 316 " bne 1b \n" 317 : "+r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val) 318 : : "cc", "memory"); 319 return (ret); 320} 321 322static __inline uint64_t 323atomic_fetchadd_64(volatile uint64_t *p, uint64_t val) 324{ 325 uint64_t ret, tmp; 326 uint32_t exflag; 327 328 __asm __volatile( 329 "1: \n" 330 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 331 " adds %Q[tmp], %Q[ret], %Q[val] \n" 332 " adc %R[tmp], %R[ret], %R[val] \n" 333 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n" 334 " teq %[exf], #0 \n" 335 " it ne \n" 336 " bne 1b \n" 337 : [ret] "=&r" (ret), 338 [exf] "=&r" (exflag), 339 [tmp] "=&r" (tmp) 340 : [ptr] "r" (p), 341 [val] "r" (val) 342 : "cc", "memory"); 343 return (ret); 344} 345 346static __inline u_long 347atomic_fetchadd_long(volatile u_long *p, u_long val) 348{ 349 350 return (atomic_fetchadd_32((volatile uint32_t *)p, val)); 351} 352 353static __inline uint32_t 354atomic_load_acq_32(volatile uint32_t *p) 355{ 356 uint32_t v; 357 358 v = *p; 359 dmb(); 360 return (v); 361} 362 363static __inline uint64_t 364atomic_load_64(volatile uint64_t *p) 365{ 366 uint64_t ret; 367 368 /* 369 * The only way to atomically load 64 bits is with LDREXD which puts the 370 * exclusive monitor into the exclusive state, so reset it to open state 371 * with CLREX because we don't actually need to store anything. 372 */ 373 __asm __volatile( 374 "ldrexd %Q[ret], %R[ret], [%[ptr]] \n" 375 "clrex \n" 376 : [ret] "=&r" (ret) 377 : [ptr] "r" (p) 378 : "cc", "memory"); 379 return (ret); 380} 381 382static __inline uint64_t 383atomic_load_acq_64(volatile uint64_t *p) 384{ 385 uint64_t ret; 386 387 ret = atomic_load_64(p); 388 dmb(); 389 return (ret); 390} 391 392static __inline u_long 393atomic_load_acq_long(volatile u_long *p) 394{ 395 u_long v; 396 397 v = *p; 398 dmb(); 399 return (v); 400} 401 402static __inline uint32_t 403atomic_readandclear_32(volatile uint32_t *p) 404{ 405 uint32_t ret, tmp = 0, tmp2 = 0; 406 407 __asm __volatile( 408 "1: ldrex %0, [%3] \n" 409 " mov %1, #0 \n" 410 " strex %2, %1, [%3] \n" 411 " cmp %2, #0 \n" 412 " it ne \n" 413 " bne 1b \n" 414 : "=r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p) 415 : : "cc", "memory"); 416 return (ret); 417} 418 419static __inline uint64_t 420atomic_readandclear_64(volatile uint64_t *p) 421{ 422 uint64_t ret, tmp; 423 uint32_t exflag; 424 425 __asm __volatile( 426 "1: \n" 427 " ldrexd %Q[ret], %R[ret], [%[ptr]] \n" 428 " mov %Q[tmp], #0 \n" 429 " mov %R[tmp], #0 \n" 430 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n" 431 " teq %[exf], #0 \n" 432 " it ne \n" 433 " bne 1b \n" 434 : [ret] "=&r" (ret), 435 [exf] "=&r" (exflag), 436 [tmp] "=&r" (tmp) 437 : [ptr] "r" (p) 438 : "cc", "memory"); 439 return (ret); 440} 441 442static __inline u_long 443atomic_readandclear_long(volatile u_long *p) 444{ 445 446 return (atomic_readandclear_32((volatile uint32_t *)p)); 447} 448 449static __inline void 450atomic_set_32(volatile uint32_t *address, uint32_t setmask) 451{ 452 uint32_t tmp = 0, tmp2 = 0; 453 454 __asm __volatile( 455 "1: ldrex %0, [%2] \n" 456 " orr %0, %0, %3 \n" 457 " strex %1, %0, [%2] \n" 458 " cmp %1, #0 \n" 459 " it ne \n" 460 " bne 1b \n" 461 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask) 462 : : "cc", "memory"); 463} 464 465static __inline void 466atomic_set_64(volatile uint64_t *p, uint64_t val) 467{ 468 uint64_t tmp; 469 uint32_t exflag; 470 471 __asm __volatile( 472 "1: \n" 473 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 474 " orr %Q[tmp], %Q[val] \n" 475 " orr %R[tmp], %R[val] \n" 476 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n" 477 " teq %[exf], #0 \n" 478 " it ne \n" 479 " bne 1b \n" 480 : [exf] "=&r" (exflag), 481 [tmp] "=&r" (tmp) 482 : [ptr] "r" (p), 483 [val] "r" (val) 484 : "cc", "memory"); 485} 486 487static __inline void 488atomic_set_long(volatile u_long *address, u_long setmask) 489{ 490 491 atomic_set_32((volatile uint32_t *)address, setmask); 492} 493 494ATOMIC_ACQ_REL(set, 32) 495ATOMIC_ACQ_REL(set, 64) 496ATOMIC_ACQ_REL_LONG(set) 497 498static __inline void 499atomic_subtract_32(volatile uint32_t *p, uint32_t val) 500{ 501 uint32_t tmp = 0, tmp2 = 0; 502 503 __asm __volatile( 504 "1: ldrex %0, [%2] \n" 505 " sub %0, %0, %3 \n" 506 " strex %1, %0, [%2] \n" 507 " cmp %1, #0 \n" 508 " it ne \n" 509 " bne 1b \n" 510 : "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val) 511 : : "cc", "memory"); 512} 513 514static __inline void 515atomic_subtract_64(volatile uint64_t *p, uint64_t val) 516{ 517 uint64_t tmp; 518 uint32_t exflag; 519 520 __asm __volatile( 521 "1: \n" 522 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 523 " subs %Q[tmp], %Q[val] \n" 524 " sbc %R[tmp], %R[tmp], %R[val] \n" 525 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n" 526 " teq %[exf], #0 \n" 527 " it ne \n" 528 " bne 1b \n" 529 : [exf] "=&r" (exflag), 530 [tmp] "=&r" (tmp) 531 : [ptr] "r" (p), 532 [val] "r" (val) 533 : "cc", "memory"); 534} 535 536static __inline void 537atomic_subtract_long(volatile u_long *p, u_long val) 538{ 539 540 atomic_subtract_32((volatile uint32_t *)p, val); 541} 542 543ATOMIC_ACQ_REL(subtract, 32) 544ATOMIC_ACQ_REL(subtract, 64) 545ATOMIC_ACQ_REL_LONG(subtract) 546 547static __inline void 548atomic_store_64(volatile uint64_t *p, uint64_t val) 549{ 550 uint64_t tmp; 551 uint32_t exflag; 552 553 /* 554 * The only way to atomically store 64 bits is with STREXD, which will 555 * succeed only if paired up with a preceeding LDREXD using the same 556 * address, so we read and discard the existing value before storing. 557 */ 558 __asm __volatile( 559 "1: \n" 560 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" 561 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n" 562 " teq %[exf], #0 \n" 563 " it ne \n" 564 " bne 1b \n" 565 : [tmp] "=&r" (tmp), 566 [exf] "=&r" (exflag) 567 : [ptr] "r" (p), 568 [val] "r" (val) 569 : "cc", "memory"); 570} 571 572static __inline void 573atomic_store_rel_32(volatile uint32_t *p, uint32_t v) 574{ 575 576 dmb(); 577 *p = v; 578} 579 580static __inline void 581atomic_store_rel_64(volatile uint64_t *p, uint64_t val) 582{ 583 584 dmb(); 585 atomic_store_64(p, val); 586} 587 588static __inline void 589atomic_store_rel_long(volatile u_long *p, u_long v) 590{ 591 592 dmb(); 593 *p = v; 594} 595 596static __inline int 597atomic_testandset_32(volatile uint32_t *p, u_int v) 598{ 599 uint32_t tmp, tmp2, res, mask; 600 601 mask = 1u << (v & 0x1f); 602 tmp = tmp2 = 0; 603 __asm __volatile( 604 "1: ldrex %0, [%4] \n" 605 " orr %1, %0, %3 \n" 606 " strex %2, %1, [%4] \n" 607 " cmp %2, #0 \n" 608 " it ne \n" 609 " bne 1b \n" 610 : "=&r" (res), "=&r" (tmp), "=&r" (tmp2) 611 : "r" (mask), "r" (p) 612 : "cc", "memory"); 613 return ((res & mask) != 0); 614} 615 616static __inline int 617atomic_testandset_int(volatile u_int *p, u_int v) 618{ 619 620 return (atomic_testandset_32((volatile uint32_t *)p, v)); 621} 622 623static __inline int 624atomic_testandset_long(volatile u_long *p, u_int v) 625{ 626 627 return (atomic_testandset_32((volatile uint32_t *)p, v)); 628} 629 630static __inline int 631atomic_testandset_64(volatile uint64_t *p, u_int v) 632{ 633 volatile uint32_t *p32; 634 635 p32 = (volatile uint32_t *)p; 636 /* Assume little-endian */ 637 if (v >= 32) { 638 v &= 0x1f; 639 p32++; 640 } 641 return (atomic_testandset_32(p32, v)); 642} 643 644static __inline uint32_t 645atomic_swap_32(volatile uint32_t *p, uint32_t v) 646{ 647 uint32_t ret, exflag; 648 649 __asm __volatile( 650 "1: ldrex %[ret], [%[ptr]] \n" 651 " strex %[exf], %[val], [%[ptr]] \n" 652 " teq %[exf], #0 \n" 653 " it ne \n" 654 " bne 1b \n" 655 : [ret] "=&r" (ret), 656 [exf] "=&r" (exflag) 657 : [val] "r" (v), 658 [ptr] "r" (p) 659 : "cc", "memory"); 660 return (ret); 661} 662 663static __inline uint64_t 664atomic_swap_64(volatile uint64_t *p, uint64_t v) 665{ 666 uint64_t ret; 667 uint32_t exflag; 668 669 __asm __volatile( 670 "1: ldrexd %Q[ret], %R[ret], [%[ptr]] \n" 671 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n" 672 " teq %[exf], #0 \n" 673 " it ne \n" 674 " bne 1b \n" 675 : [ret] "=&r" (ret), 676 [exf] "=&r" (exflag) 677 : [val] "r" (v), 678 [ptr] "r" (p) 679 : "cc", "memory"); 680 return (ret); 681} 682 683#undef ATOMIC_ACQ_REL 684#undef ATOMIC_ACQ_REL_LONG 685 686static __inline void 687atomic_thread_fence_acq(void) 688{ 689 690 dmb(); 691} 692 693static __inline void 694atomic_thread_fence_rel(void) 695{ 696 697 dmb(); 698} 699 700static __inline void 701atomic_thread_fence_acq_rel(void) 702{ 703 704 dmb(); 705} 706 707static __inline void 708atomic_thread_fence_seq_cst(void) 709{ 710 711 dmb(); 712} 713 714#endif /* _MACHINE_ATOMIC_V6_H_ */ 715