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")
60 #define ARM_HAVE_ATOMIC64
62 #define ATOMIC_ACQ_REL_LONG(NAME) \
63 static __inline void \
64 atomic_##NAME##_acq_long(__volatile u_long *p, u_long v) \
66 atomic_##NAME##_long(p, v); \
70 static __inline void \
71 atomic_##NAME##_rel_long(__volatile u_long *p, u_long v) \
74 atomic_##NAME##_long(p, v); \
77 #define ATOMIC_ACQ_REL(NAME, WIDTH) \
78 static __inline void \
79 atomic_##NAME##_acq_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
81 atomic_##NAME##_##WIDTH(p, v); \
85 static __inline void \
86 atomic_##NAME##_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
89 atomic_##NAME##_##WIDTH(p, v); \
93 atomic_add_32(volatile uint32_t *p, uint32_t val)
95 uint32_t tmp = 0, tmp2 = 0;
98 "1: ldrex %0, [%2] \n"
100 " strex %1, %0, [%2] \n"
104 : "=&r" (tmp), "+r" (tmp2)
105 ,"+r" (p), "+r" (val) : : "cc", "memory");
109 atomic_add_64(volatile uint64_t *p, uint64_t val)
116 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
117 " adds %Q[tmp], %Q[val] \n"
118 " adc %R[tmp], %R[tmp], %R[val] \n"
119 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
123 : [exf] "=&r" (exflag),
131 atomic_add_long(volatile u_long *p, u_long val)
134 atomic_add_32((volatile uint32_t *)p, val);
137 ATOMIC_ACQ_REL(add, 32)
138 ATOMIC_ACQ_REL(add, 64)
139 ATOMIC_ACQ_REL_LONG(add)
142 atomic_clear_32(volatile uint32_t *address, uint32_t setmask)
144 uint32_t tmp = 0, tmp2 = 0;
147 "1: ldrex %0, [%2] \n"
149 " strex %1, %0, [%2] \n"
153 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
158 atomic_clear_64(volatile uint64_t *p, uint64_t val)
165 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
166 " bic %Q[tmp], %Q[val] \n"
167 " bic %R[tmp], %R[val] \n"
168 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
172 : [exf] "=&r" (exflag),
180 atomic_clear_long(volatile u_long *address, u_long setmask)
183 atomic_clear_32((volatile uint32_t *)address, setmask);
186 ATOMIC_ACQ_REL(clear, 32)
187 ATOMIC_ACQ_REL(clear, 64)
188 ATOMIC_ACQ_REL_LONG(clear)
190 #define ATOMIC_FCMPSET_CODE(RET, TYPE, SUF) \
195 "1: ldrex" SUF " %[tmp], [%[ptr]] \n" \
196 " ldr" SUF " %[ret], [%[oldv]] \n" \
197 " teq %[tmp], %[ret] \n" \
199 " str" SUF "ne %[tmp], [%[oldv]] \n" \
200 " movne %[ret], #0 \n" \
201 " strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
202 " eorseq %[ret], #1 \n" \
204 : [ret] "=&r" (RET), \
206 : [ptr] "r" (_ptr), \
212 #define ATOMIC_FCMPSET_CODE64(RET) \
217 "1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
218 " ldrd %Q[cmp], %R[cmp], [%[oldv]] \n" \
219 " teq %Q[tmp], %Q[cmp] \n" \
221 " teqeq %R[tmp], %R[cmp] \n" \
223 " movne %[ret], #0 \n" \
224 " strdne %[cmp], [%[oldv]] \n" \
225 " strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
226 " eorseq %[ret], #1 \n" \
228 : [ret] "=&r" (RET), \
231 : [ptr] "r" (_ptr), \
238 atomic_fcmpset_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
242 ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
245 #define atomic_fcmpset_8 atomic_fcmpset_8
248 atomic_fcmpset_acq_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
252 ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
258 atomic_fcmpset_rel_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
263 ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
268 atomic_fcmpset_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
272 ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
275 #define atomic_fcmpset_16 atomic_fcmpset_16
278 atomic_fcmpset_acq_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
282 ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
288 atomic_fcmpset_rel_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
293 ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
298 atomic_fcmpset_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
302 ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
307 atomic_fcmpset_acq_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
311 ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
317 atomic_fcmpset_rel_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
322 ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
327 atomic_fcmpset_long(volatile u_long *_ptr, u_long *_old, u_long _new)
331 ATOMIC_FCMPSET_CODE(ret, u_long, "");
336 atomic_fcmpset_acq_long(volatile u_long *_ptr, u_long *_old, u_long _new)
340 ATOMIC_FCMPSET_CODE(ret, u_long, "");
346 atomic_fcmpset_rel_long(volatile u_long *_ptr, u_long *_old, u_long _new)
351 ATOMIC_FCMPSET_CODE(ret, u_long, "");
356 atomic_fcmpset_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
360 ATOMIC_FCMPSET_CODE64(ret);
365 atomic_fcmpset_acq_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
369 ATOMIC_FCMPSET_CODE64(ret);
375 atomic_fcmpset_rel_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
380 ATOMIC_FCMPSET_CODE64(ret);
384 #define ATOMIC_CMPSET_CODE(RET, SUF) \
387 "1: ldrex" SUF " %[ret], [%[ptr]] \n" \
388 " teq %[ret], %[oldv] \n" \
390 " movne %[ret], #0 \n" \
391 " strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
392 " eorseq %[ret], #1 \n" \
394 : [ret] "=&r" (RET) \
395 : [ptr] "r" (_ptr), \
401 #define ATOMIC_CMPSET_CODE64(RET) \
406 "1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
407 " teq %Q[tmp], %Q[oldv] \n" \
409 " teqeq %R[tmp], %R[oldv] \n" \
411 " movne %[ret], #0 \n" \
412 " strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
413 " eorseq %[ret], #1 \n" \
415 : [ret] "=&r" (RET), \
417 : [ptr] "r" (_ptr), \
424 atomic_cmpset_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
428 ATOMIC_CMPSET_CODE(ret, "b");
431 #define atomic_cmpset_8 atomic_cmpset_8
434 atomic_cmpset_acq_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
438 ATOMIC_CMPSET_CODE(ret, "b");
444 atomic_cmpset_rel_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
449 ATOMIC_CMPSET_CODE(ret, "b");
454 atomic_cmpset_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
458 ATOMIC_CMPSET_CODE(ret, "h");
461 #define atomic_cmpset_16 atomic_cmpset_16
464 atomic_cmpset_acq_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
468 ATOMIC_CMPSET_CODE(ret, "h");
474 atomic_cmpset_rel_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
479 ATOMIC_CMPSET_CODE(ret, "h");
484 atomic_cmpset_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
488 ATOMIC_CMPSET_CODE(ret, "");
493 atomic_cmpset_acq_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
497 ATOMIC_CMPSET_CODE(ret, "");
503 atomic_cmpset_rel_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
508 ATOMIC_CMPSET_CODE(ret, "");
513 atomic_cmpset_long(volatile u_long *_ptr, u_long _old, u_long _new)
517 ATOMIC_CMPSET_CODE(ret, "");
522 atomic_cmpset_acq_long(volatile u_long *_ptr, u_long _old, u_long _new)
526 ATOMIC_CMPSET_CODE(ret, "");
532 atomic_cmpset_rel_long(volatile u_long *_ptr, u_long _old, u_long _new)
537 ATOMIC_CMPSET_CODE(ret, "");
542 atomic_cmpset_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
546 ATOMIC_CMPSET_CODE64(ret);
551 atomic_cmpset_acq_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
555 ATOMIC_CMPSET_CODE64(ret);
561 atomic_cmpset_rel_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
566 ATOMIC_CMPSET_CODE64(ret);
570 static __inline uint32_t
571 atomic_fetchadd_32(volatile uint32_t *p, uint32_t val)
573 uint32_t tmp = 0, tmp2 = 0, ret = 0;
576 "1: ldrex %0, [%3] \n"
578 " strex %2, %1, [%3] \n"
582 : "+r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
587 static __inline uint64_t
588 atomic_fetchadd_64(volatile uint64_t *p, uint64_t val)
595 " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
596 " adds %Q[tmp], %Q[ret], %Q[val] \n"
597 " adc %R[tmp], %R[ret], %R[val] \n"
598 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
603 [exf] "=&r" (exflag),
611 static __inline u_long
612 atomic_fetchadd_long(volatile u_long *p, u_long val)
615 return (atomic_fetchadd_32((volatile uint32_t *)p, val));
618 static __inline uint32_t
619 atomic_load_acq_32(volatile uint32_t *p)
628 static __inline uint64_t
629 atomic_load_64(volatile uint64_t *p)
634 * The only way to atomically load 64 bits is with LDREXD which puts the
635 * exclusive monitor into the exclusive state, so reset it to open state
636 * with CLREX because we don't actually need to store anything.
639 "ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
647 static __inline uint64_t
648 atomic_load_acq_64(volatile uint64_t *p)
652 ret = atomic_load_64(p);
657 static __inline u_long
658 atomic_load_acq_long(volatile u_long *p)
667 static __inline uint32_t
668 atomic_readandclear_32(volatile uint32_t *p)
670 uint32_t ret, tmp = 0, tmp2 = 0;
673 "1: ldrex %0, [%3] \n"
675 " strex %2, %1, [%3] \n"
679 : "=r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p)
684 static __inline uint64_t
685 atomic_readandclear_64(volatile uint64_t *p)
692 " ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
693 " mov %Q[tmp], #0 \n"
694 " mov %R[tmp], #0 \n"
695 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
700 [exf] "=&r" (exflag),
707 static __inline u_long
708 atomic_readandclear_long(volatile u_long *p)
711 return (atomic_readandclear_32((volatile uint32_t *)p));
715 atomic_set_32(volatile uint32_t *address, uint32_t setmask)
717 uint32_t tmp = 0, tmp2 = 0;
720 "1: ldrex %0, [%2] \n"
722 " strex %1, %0, [%2] \n"
726 : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
731 atomic_set_64(volatile uint64_t *p, uint64_t val)
738 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
739 " orr %Q[tmp], %Q[val] \n"
740 " orr %R[tmp], %R[val] \n"
741 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
745 : [exf] "=&r" (exflag),
753 atomic_set_long(volatile u_long *address, u_long setmask)
756 atomic_set_32((volatile uint32_t *)address, setmask);
759 ATOMIC_ACQ_REL(set, 32)
760 ATOMIC_ACQ_REL(set, 64)
761 ATOMIC_ACQ_REL_LONG(set)
764 atomic_subtract_32(volatile uint32_t *p, uint32_t val)
766 uint32_t tmp = 0, tmp2 = 0;
769 "1: ldrex %0, [%2] \n"
771 " strex %1, %0, [%2] \n"
775 : "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
780 atomic_subtract_64(volatile uint64_t *p, uint64_t val)
787 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
788 " subs %Q[tmp], %Q[val] \n"
789 " sbc %R[tmp], %R[tmp], %R[val] \n"
790 " strexd %[exf], %Q[tmp], %R[tmp], [%[ptr]] \n"
794 : [exf] "=&r" (exflag),
802 atomic_subtract_long(volatile u_long *p, u_long val)
805 atomic_subtract_32((volatile uint32_t *)p, val);
808 ATOMIC_ACQ_REL(subtract, 32)
809 ATOMIC_ACQ_REL(subtract, 64)
810 ATOMIC_ACQ_REL_LONG(subtract)
813 atomic_store_64(volatile uint64_t *p, uint64_t val)
819 * The only way to atomically store 64 bits is with STREXD, which will
820 * succeed only if paired up with a preceeding LDREXD using the same
821 * address, so we read and discard the existing value before storing.
825 " ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
826 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
838 atomic_store_rel_32(volatile uint32_t *p, uint32_t v)
846 atomic_store_rel_64(volatile uint64_t *p, uint64_t val)
850 atomic_store_64(p, val);
854 atomic_store_rel_long(volatile u_long *p, u_long v)
862 atomic_testandclear_32(volatile uint32_t *ptr, u_int bit)
864 int newv, oldv, result;
868 " lsl ip, ip, %[bit] \n"
869 /* Done with %[bit] as input, reuse below as output. */
871 " ldrex %[oldv], [%[ptr]] \n"
872 " bic %[newv], %[oldv], ip \n"
873 " strex %[bit], %[newv], [%[ptr]] \n"
877 " ands %[bit], %[oldv], ip \n"
879 " movne %[bit], #1 \n"
880 : [bit] "=&r" (result),
885 : "cc", "ip", "memory");
891 atomic_testandclear_int(volatile u_int *p, u_int v)
894 return (atomic_testandclear_32((volatile uint32_t *)p, v));
898 atomic_testandclear_long(volatile u_long *p, u_int v)
901 return (atomic_testandclear_32((volatile uint32_t *)p, v));
903 #define atomic_testandclear_long atomic_testandclear_long
906 atomic_testandset_32(volatile uint32_t *ptr, u_int bit)
908 int newv, oldv, result;
912 " lsl ip, ip, %[bit] \n"
913 /* Done with %[bit] as input, reuse below as output. */
915 " ldrex %[oldv], [%[ptr]] \n"
916 " orr %[newv], %[oldv], ip \n"
917 " strex %[bit], %[newv], [%[ptr]] \n"
921 " ands %[bit], %[oldv], ip \n"
923 " movne %[bit], #1 \n"
924 : [bit] "=&r" (result),
929 : "cc", "ip", "memory");
935 atomic_testandset_int(volatile u_int *p, u_int v)
938 return (atomic_testandset_32((volatile uint32_t *)p, v));
942 atomic_testandset_long(volatile u_long *p, u_int v)
945 return (atomic_testandset_32((volatile uint32_t *)p, v));
947 #define atomic_testandset_long atomic_testandset_long
950 atomic_testandset_64(volatile uint64_t *p, u_int v)
952 volatile uint32_t *p32;
954 p32 = (volatile uint32_t *)p;
955 /* Assume little-endian */
960 return (atomic_testandset_32(p32, v));
963 static __inline uint32_t
964 atomic_swap_32(volatile uint32_t *p, uint32_t v)
966 uint32_t ret, exflag;
969 "1: ldrex %[ret], [%[ptr]] \n"
970 " strex %[exf], %[val], [%[ptr]] \n"
982 static __inline uint64_t
983 atomic_swap_64(volatile uint64_t *p, uint64_t v)
989 "1: ldrexd %Q[ret], %R[ret], [%[ptr]] \n"
990 " strexd %[exf], %Q[val], %R[val], [%[ptr]] \n"
1002 #undef ATOMIC_ACQ_REL
1003 #undef ATOMIC_ACQ_REL_LONG
1005 static __inline void
1006 atomic_thread_fence_acq(void)
1012 static __inline void
1013 atomic_thread_fence_rel(void)
1019 static __inline void
1020 atomic_thread_fence_acq_rel(void)
1026 static __inline void
1027 atomic_thread_fence_seq_cst(void)
1033 #endif /* _MACHINE_ATOMIC_V6_H_ */