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)
193 #define ATOMIC_FCMPSET_CODE(RET, TYPE, SUF) \
198 "1: ldrex" SUF " %[tmp], [%[ptr]] \n" \
199 " ldr" SUF " %[ret], [%[oldv]] \n" \
200 " teq %[tmp], %[ret] \n" \
202 " str" SUF "ne %[tmp], [%[oldv]] \n" \
203 " movne %[ret], #0 \n" \
204 " strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
205 " eorseq %[ret], #1 \n" \
207 : [ret] "=&r" (RET), \
209 : [ptr] "r" (_ptr), \
215 #define ATOMIC_FCMPSET_CODE64(RET) \
220 "1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
221 " ldrd %Q[cmp], %R[cmp], [%[oldv]] \n" \
222 " teq %Q[tmp], %Q[cmp] \n" \
224 " teqeq %R[tmp], %R[cmp] \n" \
226 " movne %[ret], #0 \n" \
227 " strdne %[cmp], [%[oldv]] \n" \
228 " strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
229 " eorseq %[ret], #1 \n" \
231 : [ret] "=&r" (RET), \
234 : [ptr] "r" (_ptr), \
241 atomic_fcmpset_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
245 ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
248 #define atomic_fcmpset_8 atomic_fcmpset_8
251 atomic_fcmpset_acq_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
255 ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
261 atomic_fcmpset_rel_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
266 ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
271 atomic_fcmpset_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
275 ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
278 #define atomic_fcmpset_16 atomic_fcmpset_16
281 atomic_fcmpset_acq_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
285 ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
291 atomic_fcmpset_rel_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
296 ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
301 atomic_fcmpset_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
305 ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
310 atomic_fcmpset_acq_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
314 ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
320 atomic_fcmpset_rel_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
325 ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
330 atomic_fcmpset_long(volatile u_long *_ptr, u_long *_old, u_long _new)
334 ATOMIC_FCMPSET_CODE(ret, u_long, "");
339 atomic_fcmpset_acq_long(volatile u_long *_ptr, u_long *_old, u_long _new)
343 ATOMIC_FCMPSET_CODE(ret, u_long, "");
349 atomic_fcmpset_rel_long(volatile u_long *_ptr, u_long *_old, u_long _new)
354 ATOMIC_FCMPSET_CODE(ret, u_long, "");
359 atomic_fcmpset_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
363 ATOMIC_FCMPSET_CODE64(ret);
368 atomic_fcmpset_acq_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
372 ATOMIC_FCMPSET_CODE64(ret);
378 atomic_fcmpset_rel_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
383 ATOMIC_FCMPSET_CODE64(ret);
387 #define ATOMIC_CMPSET_CODE(RET, SUF) \
390 "1: ldrex" SUF " %[ret], [%[ptr]] \n" \
391 " teq %[ret], %[oldv] \n" \
393 " movne %[ret], #0 \n" \
394 " strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
395 " eorseq %[ret], #1 \n" \
397 : [ret] "=&r" (RET) \
398 : [ptr] "r" (_ptr), \
404 #define ATOMIC_CMPSET_CODE64(RET) \
409 "1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
410 " teq %Q[tmp], %Q[oldv] \n" \
412 " teqeq %R[tmp], %R[oldv] \n" \
414 " movne %[ret], #0 \n" \
415 " strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
416 " eorseq %[ret], #1 \n" \
418 : [ret] "=&r" (RET), \
420 : [ptr] "r" (_ptr), \
427 atomic_cmpset_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
431 ATOMIC_CMPSET_CODE(ret, "b");
434 #define atomic_cmpset_8 atomic_cmpset_8
437 atomic_cmpset_acq_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
441 ATOMIC_CMPSET_CODE(ret, "b");
447 atomic_cmpset_rel_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
452 ATOMIC_CMPSET_CODE(ret, "b");
457 atomic_cmpset_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
461 ATOMIC_CMPSET_CODE(ret, "h");
464 #define atomic_cmpset_16 atomic_cmpset_16
467 atomic_cmpset_acq_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
471 ATOMIC_CMPSET_CODE(ret, "h");
477 atomic_cmpset_rel_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
482 ATOMIC_CMPSET_CODE(ret, "h");
487 atomic_cmpset_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
491 ATOMIC_CMPSET_CODE(ret, "");
496 atomic_cmpset_acq_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
500 ATOMIC_CMPSET_CODE(ret, "");
506 atomic_cmpset_rel_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
511 ATOMIC_CMPSET_CODE(ret, "");
516 atomic_cmpset_long(volatile u_long *_ptr, u_long _old, u_long _new)
520 ATOMIC_CMPSET_CODE(ret, "");
525 atomic_cmpset_acq_long(volatile u_long *_ptr, u_long _old, u_long _new)
529 ATOMIC_CMPSET_CODE(ret, "");
535 atomic_cmpset_rel_long(volatile u_long *_ptr, u_long _old, u_long _new)
540 ATOMIC_CMPSET_CODE(ret, "");
545 atomic_cmpset_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
549 ATOMIC_CMPSET_CODE64(ret);
554 atomic_cmpset_acq_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
558 ATOMIC_CMPSET_CODE64(ret);
564 atomic_cmpset_rel_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
569 ATOMIC_CMPSET_CODE64(ret);
573 static __inline uint32_t
574 atomic_fetchadd_32(volatile uint32_t *p, uint32_t val)
576 uint32_t tmp = 0, tmp2 = 0, ret = 0;
579 "1: ldrex %0, [%3] \n"
581 " strex %2, %1, [%3] \n"
585 : "+r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
590 static __inline uint64_t
591 atomic_fetchadd_64(volatile uint64_t *p, uint64_t val)
598 " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
599 " adds %Q[tmp], %Q[ret], %Q[val] \n"
600 " adc %R[tmp], %R[ret], %R[val] \n"
601 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
606 [exf] "=&r" (exflag),
614 static __inline u_long
615 atomic_fetchadd_long(volatile u_long *p, u_long val)
618 return (atomic_fetchadd_32((volatile uint32_t *)p, val));
621 static __inline uint32_t
622 atomic_load_acq_32(volatile uint32_t *p)
631 static __inline uint64_t
632 atomic_load_64(volatile uint64_t *p)
637 * The only way to atomically load 64 bits is with LDREXD which puts the
638 * exclusive monitor into the exclusive state, so reset it to open state
639 * with CLREX because we don't actually need to store anything.
642 "ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
650 static __inline uint64_t
651 atomic_load_acq_64(volatile uint64_t *p)
655 ret = atomic_load_64(p);
660 static __inline u_long
661 atomic_load_acq_long(volatile u_long *p)
670 static __inline uint32_t
671 atomic_readandclear_32(volatile uint32_t *p)
673 uint32_t ret, tmp = 0, tmp2 = 0;
676 "1: ldrex %0, [%3] \n"
678 " strex %2, %1, [%3] \n"
682 : "=r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p)
687 static __inline uint64_t
688 atomic_readandclear_64(volatile uint64_t *p)
695 " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
696 " mov %Q[tmp], #0 \n"
697 " mov %R[tmp], #0 \n"
698 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
703 [exf] "=&r" (exflag),
710 static __inline u_long
711 atomic_readandclear_long(volatile u_long *p)
714 return (atomic_readandclear_32((volatile uint32_t *)p));
718 atomic_set_32(volatile uint32_t *address, uint32_t setmask)
720 uint32_t tmp = 0, tmp2 = 0;
723 "1: ldrex %0, [%2] \n"
725 " strex %1, %0, [%2] \n"
729 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
734 atomic_set_64(volatile uint64_t *p, uint64_t val)
741 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
742 " orr %Q[tmp], %Q[val] \n"
743 " orr %R[tmp], %R[val] \n"
744 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
748 : [exf] "=&r" (exflag),
756 atomic_set_long(volatile u_long *address, u_long setmask)
759 atomic_set_32((volatile uint32_t *)address, setmask);
762 ATOMIC_ACQ_REL(set, 32)
763 ATOMIC_ACQ_REL(set, 64)
764 ATOMIC_ACQ_REL_LONG(set)
767 atomic_subtract_32(volatile uint32_t *p, uint32_t val)
769 uint32_t tmp = 0, tmp2 = 0;
772 "1: ldrex %0, [%2] \n"
774 " strex %1, %0, [%2] \n"
778 : "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
783 atomic_subtract_64(volatile uint64_t *p, uint64_t val)
790 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
791 " subs %Q[tmp], %Q[val] \n"
792 " sbc %R[tmp], %R[tmp], %R[val] \n"
793 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
797 : [exf] "=&r" (exflag),
805 atomic_subtract_long(volatile u_long *p, u_long val)
808 atomic_subtract_32((volatile uint32_t *)p, val);
811 ATOMIC_ACQ_REL(subtract, 32)
812 ATOMIC_ACQ_REL(subtract, 64)
813 ATOMIC_ACQ_REL_LONG(subtract)
816 atomic_store_64(volatile uint64_t *p, uint64_t val)
822 * The only way to atomically store 64 bits is with STREXD, which will
823 * succeed only if paired up with a preceeding LDREXD using the same
824 * address, so we read and discard the existing value before storing.
828 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
829 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
841 atomic_store_rel_32(volatile uint32_t *p, uint32_t v)
849 atomic_store_rel_64(volatile uint64_t *p, uint64_t val)
853 atomic_store_64(p, val);
857 atomic_store_rel_long(volatile u_long *p, u_long v)
865 atomic_testandclear_32(volatile uint32_t *ptr, u_int bit)
867 int newv, oldv, result;
871 " lsl ip, ip, %[bit] \n"
872 /* Done with %[bit] as input, reuse below as output. */
874 " ldrex %[oldv], [%[ptr]] \n"
875 " bic %[newv], %[oldv], ip \n"
876 " strex %[bit], %[newv], [%[ptr]] \n"
880 " ands %[bit], %[oldv], ip \n"
882 " movne %[bit], #1 \n"
883 : [bit] "=&r" (result),
888 : "cc", "ip", "memory");
894 atomic_testandclear_int(volatile u_int *p, u_int v)
897 return (atomic_testandclear_32((volatile uint32_t *)p, v));
901 atomic_testandclear_long(volatile u_long *p, u_int v)
904 return (atomic_testandclear_32((volatile uint32_t *)p, v));
906 #define atomic_testandclear_long atomic_testandclear_long
909 atomic_testandset_32(volatile uint32_t *ptr, u_int bit)
911 int newv, oldv, result;
915 " lsl ip, ip, %[bit] \n"
916 /* Done with %[bit] as input, reuse below as output. */
918 " ldrex %[oldv], [%[ptr]] \n"
919 " orr %[newv], %[oldv], ip \n"
920 " strex %[bit], %[newv], [%[ptr]] \n"
924 " ands %[bit], %[oldv], ip \n"
926 " movne %[bit], #1 \n"
927 : [bit] "=&r" (result),
932 : "cc", "ip", "memory");
938 atomic_testandset_int(volatile u_int *p, u_int v)
941 return (atomic_testandset_32((volatile uint32_t *)p, v));
945 atomic_testandset_long(volatile u_long *p, u_int v)
948 return (atomic_testandset_32((volatile uint32_t *)p, v));
950 #define atomic_testandset_long atomic_testandset_long
953 atomic_testandset_64(volatile uint64_t *p, u_int v)
955 volatile uint32_t *p32;
957 p32 = (volatile uint32_t *)p;
958 /* Assume little-endian */
963 return (atomic_testandset_32(p32, v));
966 static __inline uint32_t
967 atomic_swap_32(volatile uint32_t *p, uint32_t v)
969 uint32_t ret, exflag;
972 "1: ldrex %[ret], [%[ptr]] \n"
973 " strex %[exf], %[val], [%[ptr]] \n"
985 static __inline uint64_t
986 atomic_swap_64(volatile uint64_t *p, uint64_t v)
992 "1: ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
993 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
1005 #undef ATOMIC_ACQ_REL
1006 #undef ATOMIC_ACQ_REL_LONG
1008 static __inline void
1009 atomic_thread_fence_acq(void)
1015 static __inline void
1016 atomic_thread_fence_rel(void)
1022 static __inline void
1023 atomic_thread_fence_acq_rel(void)
1029 static __inline void
1030 atomic_thread_fence_seq_cst(void)
1036 #endif /* _MACHINE_ATOMIC_V6_H_ */