1 /* $NetBSD: atomic.h,v 1.1 2002/10/19 12:22:34 bsh Exp $ */
4 * Copyright (C) 2003-2004 Olivier Houchard
5 * Copyright (C) 1994-1997 Mark Brinicombe
6 * Copyright (C) 1994 Brini
9 * This code is derived from software written for Brini by Mark Brinicombe
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
39 #ifndef _MACHINE_ATOMIC_V6_H_
40 #define _MACHINE_ATOMIC_V6_H_
42 #ifndef _MACHINE_ATOMIC_H_
43 #error Do not include this file directly, use <machine/atomic.h>
47 #define isb() __asm __volatile("isb" : : : "memory")
48 #define dsb() __asm __volatile("dsb" : : : "memory")
49 #define dmb() __asm __volatile("dmb" : : : "memory")
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")
55 #error Only use this file with ARMv6 and later
62 #define ARM_HAVE_ATOMIC64
64 #define ATOMIC_ACQ_REL_LONG(NAME) \
65 static __inline void \
66 atomic_##NAME##_acq_long(__volatile u_long *p, u_long v) \
68 atomic_##NAME##_long(p, v); \
72 static __inline void \
73 atomic_##NAME##_rel_long(__volatile u_long *p, u_long v) \
76 atomic_##NAME##_long(p, v); \
79 #define ATOMIC_ACQ_REL(NAME, WIDTH) \
80 static __inline void \
81 atomic_##NAME##_acq_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
83 atomic_##NAME##_##WIDTH(p, v); \
87 static __inline void \
88 atomic_##NAME##_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
91 atomic_##NAME##_##WIDTH(p, v); \
96 atomic_add_32(volatile uint32_t *p, uint32_t val)
98 uint32_t tmp = 0, tmp2 = 0;
101 "1: ldrex %0, [%2] \n"
103 " strex %1, %0, [%2] \n"
107 : "=&r" (tmp), "+r" (tmp2)
108 ,"+r" (p), "+r" (val) : : "cc", "memory");
112 atomic_add_64(volatile uint64_t *p, uint64_t val)
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"
126 : [exf] "=&r" (exflag),
134 atomic_add_long(volatile u_long *p, u_long val)
137 atomic_add_32((volatile uint32_t *)p, val);
140 ATOMIC_ACQ_REL(add, 32)
141 ATOMIC_ACQ_REL(add, 64)
142 ATOMIC_ACQ_REL_LONG(add)
145 atomic_clear_32(volatile uint32_t *address, uint32_t setmask)
147 uint32_t tmp = 0, tmp2 = 0;
150 "1: ldrex %0, [%2] \n"
152 " strex %1, %0, [%2] \n"
156 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
161 atomic_clear_64(volatile uint64_t *p, uint64_t val)
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"
175 : [exf] "=&r" (exflag),
183 atomic_clear_long(volatile u_long *address, u_long setmask)
186 atomic_clear_32((volatile uint32_t *)address, setmask);
189 ATOMIC_ACQ_REL(clear, 32)
190 ATOMIC_ACQ_REL(clear, 64)
191 ATOMIC_ACQ_REL_LONG(clear)
194 atomic_fcmpset_32(volatile uint32_t *p, uint32_t *cmpval, uint32_t newval)
197 uint32_t _cmpval = *cmpval;
205 " strexeq %0, %4, [%2] \n"
206 : "=&r" (ret), "=&r" (tmp), "+r" (p), "+r" (_cmpval), "+r" (newval)
212 static __inline uint64_t
213 atomic_fcmpset_64(volatile uint64_t *p, uint64_t *cmpval, uint64_t newval)
216 uint64_t _cmpval = *cmpval;
220 "1: mov %[ret], #1 \n"
221 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
222 " teq %Q[tmp], %Q[_cmpval] \n"
224 " teqeq %R[tmp], %R[_cmpval] \n"
226 " strexd %[ret], %Q[newval], %R[newval], [%[ptr]]\n"
231 [_cmpval] "r" (_cmpval),
232 [newval] "r" (newval)
238 static __inline u_long
239 atomic_fcmpset_long(volatile u_long *p, u_long *cmpval, u_long newval)
242 return (atomic_fcmpset_32((volatile uint32_t *)p,
243 (uint32_t *)cmpval, newval));
246 static __inline uint64_t
247 atomic_fcmpset_acq_64(volatile uint64_t *p, uint64_t *cmpval, uint64_t newval)
251 ret = atomic_fcmpset_64(p, cmpval, newval);
256 static __inline u_long
257 atomic_fcmpset_acq_long(volatile u_long *p, u_long *cmpval, u_long newval)
261 ret = atomic_fcmpset_long(p, cmpval, newval);
266 static __inline uint32_t
267 atomic_fcmpset_acq_32(volatile uint32_t *p, uint32_t *cmpval, uint32_t newval)
272 ret = atomic_fcmpset_32(p, cmpval, newval);
277 static __inline uint32_t
278 atomic_fcmpset_rel_32(volatile uint32_t *p, uint32_t *cmpval, uint32_t newval)
282 return (atomic_fcmpset_32(p, cmpval, newval));
285 static __inline uint64_t
286 atomic_fcmpset_rel_64(volatile uint64_t *p, uint64_t *cmpval, uint64_t newval)
290 return (atomic_fcmpset_64(p, cmpval, newval));
293 static __inline u_long
294 atomic_fcmpset_rel_long(volatile u_long *p, u_long *cmpval, u_long newval)
298 return (atomic_fcmpset_long(p, cmpval, newval));
301 static __inline uint32_t
302 atomic_cmpset_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
307 "1: ldrex %0, [%1] \n"
312 " strex %0, %3, [%1] \n"
318 : "=&r" (ret), "+r" (p), "+r" (cmpval), "+r" (newval)
324 atomic_cmpset_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval)
331 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
332 " teq %Q[tmp], %Q[cmpval] \n"
334 " teqeq %R[tmp], %R[cmpval] \n"
335 " movne %[ret], #0 \n"
337 " strexd %[ret], %Q[newval], %R[newval], [%[ptr]]\n"
346 [cmpval] "r" (cmpval),
347 [newval] "r" (newval)
352 static __inline u_long
353 atomic_cmpset_long(volatile u_long *p, u_long cmpval, u_long newval)
356 return (atomic_cmpset_32((volatile uint32_t *)p, cmpval, newval));
359 static __inline uint32_t
360 atomic_cmpset_acq_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
364 ret = atomic_cmpset_32(p, cmpval, newval);
369 static __inline uint64_t
370 atomic_cmpset_acq_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval)
374 ret = atomic_cmpset_64(p, cmpval, newval);
379 static __inline u_long
380 atomic_cmpset_acq_long(volatile u_long *p, u_long cmpval, u_long newval)
384 ret = atomic_cmpset_long(p, cmpval, newval);
389 static __inline uint32_t
390 atomic_cmpset_rel_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
394 return (atomic_cmpset_32(p, cmpval, newval));
397 static __inline uint64_t
398 atomic_cmpset_rel_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval)
402 return (atomic_cmpset_64(p, cmpval, newval));
405 static __inline u_long
406 atomic_cmpset_rel_long(volatile u_long *p, u_long cmpval, u_long newval)
410 return (atomic_cmpset_long(p, cmpval, newval));
413 static __inline uint32_t
414 atomic_fetchadd_32(volatile uint32_t *p, uint32_t val)
416 uint32_t tmp = 0, tmp2 = 0, ret = 0;
419 "1: ldrex %0, [%3] \n"
421 " strex %2, %1, [%3] \n"
425 : "+r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
430 static __inline uint64_t
431 atomic_fetchadd_64(volatile uint64_t *p, uint64_t val)
438 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
439 " adds %Q[tmp], %Q[ret], %Q[val] \n"
440 " adc %R[tmp], %R[ret], %R[val] \n"
441 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
446 [exf] "=&r" (exflag),
454 static __inline u_long
455 atomic_fetchadd_long(volatile u_long *p, u_long val)
458 return (atomic_fetchadd_32((volatile uint32_t *)p, val));
461 static __inline uint32_t
462 atomic_load_acq_32(volatile uint32_t *p)
471 static __inline uint64_t
472 atomic_load_64(volatile uint64_t *p)
477 * The only way to atomically load 64 bits is with LDREXD which puts the
478 * exclusive monitor into the exclusive state, so reset it to open state
479 * with CLREX because we don't actually need to store anything.
482 "ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
490 static __inline uint64_t
491 atomic_load_acq_64(volatile uint64_t *p)
495 ret = atomic_load_64(p);
500 static __inline u_long
501 atomic_load_acq_long(volatile u_long *p)
510 static __inline uint32_t
511 atomic_readandclear_32(volatile uint32_t *p)
513 uint32_t ret, tmp = 0, tmp2 = 0;
516 "1: ldrex %0, [%3] \n"
518 " strex %2, %1, [%3] \n"
522 : "=r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p)
527 static __inline uint64_t
528 atomic_readandclear_64(volatile uint64_t *p)
535 " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
536 " mov %Q[tmp], #0 \n"
537 " mov %R[tmp], #0 \n"
538 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
543 [exf] "=&r" (exflag),
550 static __inline u_long
551 atomic_readandclear_long(volatile u_long *p)
554 return (atomic_readandclear_32((volatile uint32_t *)p));
558 atomic_set_32(volatile uint32_t *address, uint32_t setmask)
560 uint32_t tmp = 0, tmp2 = 0;
563 "1: ldrex %0, [%2] \n"
565 " strex %1, %0, [%2] \n"
569 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
574 atomic_set_64(volatile uint64_t *p, uint64_t val)
581 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
582 " orr %Q[tmp], %Q[val] \n"
583 " orr %R[tmp], %R[val] \n"
584 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
588 : [exf] "=&r" (exflag),
596 atomic_set_long(volatile u_long *address, u_long setmask)
599 atomic_set_32((volatile uint32_t *)address, setmask);
602 ATOMIC_ACQ_REL(set, 32)
603 ATOMIC_ACQ_REL(set, 64)
604 ATOMIC_ACQ_REL_LONG(set)
607 atomic_subtract_32(volatile uint32_t *p, uint32_t val)
609 uint32_t tmp = 0, tmp2 = 0;
612 "1: ldrex %0, [%2] \n"
614 " strex %1, %0, [%2] \n"
618 : "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
623 atomic_subtract_64(volatile uint64_t *p, uint64_t val)
630 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
631 " subs %Q[tmp], %Q[val] \n"
632 " sbc %R[tmp], %R[tmp], %R[val] \n"
633 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
637 : [exf] "=&r" (exflag),
645 atomic_subtract_long(volatile u_long *p, u_long val)
648 atomic_subtract_32((volatile uint32_t *)p, val);
651 ATOMIC_ACQ_REL(subtract, 32)
652 ATOMIC_ACQ_REL(subtract, 64)
653 ATOMIC_ACQ_REL_LONG(subtract)
656 atomic_store_64(volatile uint64_t *p, uint64_t val)
662 * The only way to atomically store 64 bits is with STREXD, which will
663 * succeed only if paired up with a preceeding LDREXD using the same
664 * address, so we read and discard the existing value before storing.
668 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
669 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
681 atomic_store_rel_32(volatile uint32_t *p, uint32_t v)
689 atomic_store_rel_64(volatile uint64_t *p, uint64_t val)
693 atomic_store_64(p, val);
697 atomic_store_rel_long(volatile u_long *p, u_long v)
705 atomic_testandset_32(volatile uint32_t *p, u_int v)
707 uint32_t tmp, tmp2, res, mask;
709 mask = 1u << (v & 0x1f);
712 "1: ldrex %0, [%4] \n"
714 " strex %2, %1, [%4] \n"
718 : "=&r" (res), "=&r" (tmp), "=&r" (tmp2)
719 : "r" (mask), "r" (p)
721 return ((res & mask) != 0);
725 atomic_testandset_int(volatile u_int *p, u_int v)
728 return (atomic_testandset_32((volatile uint32_t *)p, v));
732 atomic_testandset_long(volatile u_long *p, u_int v)
735 return (atomic_testandset_32((volatile uint32_t *)p, v));
739 atomic_testandset_64(volatile uint64_t *p, u_int v)
741 volatile uint32_t *p32;
743 p32 = (volatile uint32_t *)p;
744 /* Assume little-endian */
749 return (atomic_testandset_32(p32, v));
752 static __inline uint32_t
753 atomic_swap_32(volatile uint32_t *p, uint32_t v)
755 uint32_t ret, exflag;
758 "1: ldrex %[ret], [%[ptr]] \n"
759 " strex %[exf], %[val], [%[ptr]] \n"
771 static __inline uint64_t
772 atomic_swap_64(volatile uint64_t *p, uint64_t v)
778 "1: ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
779 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
791 #undef ATOMIC_ACQ_REL
792 #undef ATOMIC_ACQ_REL_LONG
795 atomic_thread_fence_acq(void)
802 atomic_thread_fence_rel(void)
809 atomic_thread_fence_acq_rel(void)
816 atomic_thread_fence_seq_cst(void)
822 #endif /* _MACHINE_ATOMIC_V6_H_ */