]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/include/atomic-v6.h
Update tcsh to 6.21.00.
[FreeBSD/FreeBSD.git] / sys / arm / include / atomic-v6.h
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$
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)                                       \
65 static __inline void                                                    \
66 atomic_##NAME##_acq_long(__volatile u_long *p, u_long v)                \
67 {                                                                       \
68         atomic_##NAME##_long(p, v);                                     \
69         dmb();                                                          \
70 }                                                                       \
71                                                                         \
72 static __inline  void                                                   \
73 atomic_##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)                                     \
80 static __inline  void                                                   \
81 atomic_##NAME##_acq_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
82 {                                                                       \
83         atomic_##NAME##_##WIDTH(p, v);                                  \
84         dmb();                                                          \
85 }                                                                       \
86                                                                         \
87 static __inline  void                                                   \
88 atomic_##NAME##_rel_##WIDTH(__volatile uint##WIDTH##_t *p, uint##WIDTH##_t v)\
89 {                                                                       \
90         dmb();                                                          \
91         atomic_##NAME##_##WIDTH(p, v);                                  \
92 }
93
94
95 static __inline void
96 atomic_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
111 static __inline void
112 atomic_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
133 static __inline void
134 atomic_add_long(volatile u_long *p, u_long val)
135 {
136
137         atomic_add_32((volatile uint32_t *)p, val);
138 }
139
140 ATOMIC_ACQ_REL(add, 32)
141 ATOMIC_ACQ_REL(add, 64)
142 ATOMIC_ACQ_REL_LONG(add)
143
144 static __inline void
145 atomic_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
160 static __inline void
161 atomic_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
182 static __inline void
183 atomic_clear_long(volatile u_long *address, u_long setmask)
184 {
185
186         atomic_clear_32((volatile uint32_t *)address, setmask);
187 }
188
189 ATOMIC_ACQ_REL(clear, 32)
190 ATOMIC_ACQ_REL(clear, 64)
191 ATOMIC_ACQ_REL_LONG(clear)
192
193 #define ATOMIC_FCMPSET_CODE(RET, TYPE, SUF)                   \
194     {                                                         \
195         TYPE tmp;                                             \
196                                                               \
197         __asm __volatile(                                     \
198             "1: ldrex" SUF "   %[tmp], [%[ptr]]          \n"  \
199             "   ldr            %[ret], [%[oldv]]         \n"  \
200             "   teq            %[tmp], %[ret]            \n"  \
201             "   ittee          ne                        \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"  \
206             "   beq            1b                        \n"  \
207             : [ret] "=&r" (RET),                              \
208               [tmp] "=&r" (tmp)                               \
209             : [ptr] "r"   (_ptr),                             \
210               [oldv] "r"  (_old),                             \
211               [newv] "r"  (_new)                              \
212             : "cc", "memory");                                \
213     }
214
215 #define ATOMIC_FCMPSET_CODE64(RET)                                 \
216     {                                                              \
217         uint64_t cmp, tmp;                                         \
218                                                                    \
219         __asm __volatile(                                          \
220             "1: ldrexd   %Q[tmp], %R[tmp], [%[ptr]]           \n"  \
221             "   ldrd     %Q[cmp], %R[cmp], [%[oldv]]          \n"  \
222             "   teq      %Q[tmp], %Q[cmp]                     \n"  \
223             "   it       eq                                   \n"  \
224             "   teqeq    %R[tmp], %R[cmp]                     \n"  \
225             "   ittee    ne                                   \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"  \
230             "   beq      1b                                   \n"  \
231             : [ret] "=&r" (RET),                                   \
232               [cmp] "=&r" (cmp),                                   \
233               [tmp] "=&r" (tmp)                                    \
234             : [ptr] "r"   (_ptr),                                  \
235               [oldv] "r"  (_old),                                  \
236               [newv] "r"  (_new)                                   \
237             : "cc", "memory");                                     \
238     }
239
240 static __inline int
241 atomic_fcmpset_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
242 {
243         int ret;
244
245         ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
246         return (ret);
247 }
248
249 static __inline int
250 atomic_fcmpset_acq_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
251 {
252         int ret;
253
254         ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
255         dmb();
256         return (ret);
257 }
258
259 static __inline int
260 atomic_fcmpset_rel_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
261 {
262         int ret;
263
264         dmb();
265         ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
266         return (ret);
267 }
268
269 static __inline int
270 atomic_fcmpset_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
271 {
272         int ret;
273
274         ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
275         return (ret);
276 }
277
278 static __inline int
279 atomic_fcmpset_acq_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
280 {
281         int ret;
282
283         ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
284         dmb();
285         return (ret);
286 }
287
288 static __inline int
289 atomic_fcmpset_rel_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
290 {
291         int ret;
292
293         dmb();
294         ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
295         return (ret);
296 }
297
298 static __inline int
299 atomic_fcmpset_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
300 {
301         int ret;
302
303         ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
304         return (ret);
305 }
306
307 static __inline int
308 atomic_fcmpset_acq_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
309 {
310         int ret;
311
312         ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
313         dmb();
314         return (ret);
315 }
316
317 static __inline int
318 atomic_fcmpset_rel_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
319 {
320         int ret;
321
322         dmb();
323         ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
324         return (ret);
325 }
326
327 static __inline int
328 atomic_fcmpset_long(volatile long *_ptr, long *_old, long _new)
329 {
330         int ret;
331
332         ATOMIC_FCMPSET_CODE(ret, long, "");
333         return (ret);
334 }
335
336 static __inline int
337 atomic_fcmpset_acq_long(volatile long *_ptr, long *_old, long _new)
338 {
339         int ret;
340
341         ATOMIC_FCMPSET_CODE(ret, long, "");
342         dmb();
343         return (ret);
344 }
345
346 static __inline int
347 atomic_fcmpset_rel_long(volatile long *_ptr, long *_old, long _new)
348 {
349         int ret;
350
351         dmb();
352         ATOMIC_FCMPSET_CODE(ret, long, "");
353         return (ret);
354 }
355
356 static __inline int
357 atomic_fcmpset_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
358 {
359         int ret;
360
361         ATOMIC_FCMPSET_CODE64(ret);
362         return (ret);
363 }
364
365 static __inline int
366 atomic_fcmpset_acq_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
367 {
368         int ret;
369
370         ATOMIC_FCMPSET_CODE64(ret);
371         dmb();
372         return (ret);
373 }
374
375 static __inline int
376 atomic_fcmpset_rel_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
377 {
378         int ret;
379
380         dmb();
381         ATOMIC_FCMPSET_CODE64(ret);
382         return (ret);
383 }
384
385 #define ATOMIC_CMPSET_CODE(RET, SUF)                         \
386     {                                                        \
387         __asm __volatile(                                    \
388             "1: ldrex" SUF "   %[ret], [%[ptr]]          \n" \
389             "   teq            %[ret], %[oldv]           \n" \
390             "   itee           ne                        \n" \
391             "   movne          %[ret], #0                \n" \
392             "   strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
393             "   eorseq         %[ret], #1                \n" \
394             "   beq            1b                        \n" \
395             : [ret] "=&r" (RET)                              \
396             : [ptr] "r"   (_ptr),                            \
397               [oldv] "r"  (_old),                            \
398               [newv] "r"  (_new)                             \
399             : "cc", "memory");                               \
400     }
401
402 #define ATOMIC_CMPSET_CODE64(RET)                                 \
403     {                                                             \
404         uint64_t tmp;                                             \
405                                                                   \
406         __asm __volatile(                                         \
407             "1: ldrexd   %Q[tmp], %R[tmp], [%[ptr]]           \n" \
408             "   teq      %Q[tmp], %Q[oldv]                    \n" \
409             "   it       eq                                   \n" \
410             "   teqeq    %R[tmp], %R[oldv]                    \n" \
411             "   itee     ne                                   \n" \
412             "   movne    %[ret], #0                           \n" \
413             "   strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
414             "   eorseq   %[ret], #1                           \n" \
415             "   beq      1b                                   \n" \
416             : [ret] "=&r" (RET),                                  \
417               [tmp] "=&r" (tmp)                                   \
418             : [ptr] "r"   (_ptr),                                 \
419               [oldv] "r"  (_old),                                 \
420               [newv] "r"  (_new)                                  \
421             : "cc", "memory");                                    \
422     }
423
424 static __inline int
425 atomic_cmpset_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
426 {
427         int ret;
428
429         ATOMIC_CMPSET_CODE(ret, "b");
430         return (ret);
431 }
432
433 static __inline int
434 atomic_cmpset_acq_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
435 {
436         int ret;
437
438         ATOMIC_CMPSET_CODE(ret, "b");
439         dmb();
440         return (ret);
441 }
442
443 static __inline int
444 atomic_cmpset_rel_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
445 {
446         int ret;
447
448         dmb();
449         ATOMIC_CMPSET_CODE(ret, "b");
450         return (ret);
451 }
452
453 static __inline int
454 atomic_cmpset_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
455 {
456         int ret;
457
458         ATOMIC_CMPSET_CODE(ret, "h");
459         return (ret);
460 }
461
462 static __inline int
463 atomic_cmpset_acq_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
464 {
465         int ret;
466
467         ATOMIC_CMPSET_CODE(ret, "h");
468         dmb();
469         return (ret);
470 }
471
472 static __inline int
473 atomic_cmpset_rel_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
474 {
475         int ret;
476
477         dmb();
478         ATOMIC_CMPSET_CODE(ret, "h");
479         return (ret);
480 }
481
482 static __inline int
483 atomic_cmpset_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
484 {
485         int ret;
486
487         ATOMIC_CMPSET_CODE(ret, "");
488         return (ret);
489 }
490
491 static __inline int
492 atomic_cmpset_acq_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
493 {
494         int ret;
495
496         ATOMIC_CMPSET_CODE(ret, "");
497         dmb();
498         return (ret);
499 }
500
501 static __inline int
502 atomic_cmpset_rel_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
503 {
504         int ret;
505
506         dmb();
507         ATOMIC_CMPSET_CODE(ret, "");
508         return (ret);
509 }
510
511 static __inline int
512 atomic_cmpset_long(volatile long *_ptr, long _old, long _new)
513 {
514         int ret;
515
516         ATOMIC_CMPSET_CODE(ret, "");
517         return (ret);
518 }
519
520 static __inline int
521 atomic_cmpset_acq_long(volatile long *_ptr, long _old, long _new)
522 {
523         int ret;
524
525         ATOMIC_CMPSET_CODE(ret, "");
526         dmb();
527         return (ret);
528 }
529
530 static __inline int
531 atomic_cmpset_rel_long(volatile long *_ptr, long _old, long _new)
532 {
533         int ret;
534
535         dmb();
536         ATOMIC_CMPSET_CODE(ret, "");
537         return (ret);
538 }
539
540 static __inline int
541 atomic_cmpset_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
542 {
543         int ret;
544
545         ATOMIC_CMPSET_CODE64(ret);
546         return (ret);
547 }
548
549 static __inline int
550 atomic_cmpset_acq_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
551 {
552         int ret;
553
554         ATOMIC_CMPSET_CODE64(ret);
555         dmb();
556         return (ret);
557 }
558
559 static __inline int
560 atomic_cmpset_rel_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
561 {
562         int ret;
563
564         dmb();
565         ATOMIC_CMPSET_CODE64(ret);
566         return (ret);
567 }
568
569 static __inline uint32_t
570 atomic_fetchadd_32(volatile uint32_t *p, uint32_t val)
571 {
572         uint32_t tmp = 0, tmp2 = 0, ret = 0;
573
574         __asm __volatile(
575             "1: ldrex   %0, [%3]        \n"
576             "   add     %1, %0, %4      \n"
577             "   strex   %2, %1, [%3]    \n"
578             "   cmp     %2, #0          \n"
579             "   it      ne              \n"
580             "   bne     1b              \n"
581             : "+r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
582             : : "cc", "memory");
583         return (ret);
584 }
585
586 static __inline uint64_t
587 atomic_fetchadd_64(volatile uint64_t *p, uint64_t val)
588 {
589         uint64_t ret, tmp;
590         uint32_t exflag;
591
592         __asm __volatile(
593             "1:                                                 \n"
594             "   ldrexd  %Q[ret], %R[ret], [%[ptr]]              \n"
595             "   adds    %Q[tmp], %Q[ret], %Q[val]               \n"
596             "   adc     %R[tmp], %R[ret], %R[val]               \n"
597             "   strexd  %[exf], %Q[tmp], %R[tmp], [%[ptr]]      \n"
598             "   teq     %[exf], #0                              \n"
599             "   it      ne                                      \n"
600             "   bne     1b                                      \n"
601             : [ret] "=&r" (ret),
602               [exf] "=&r" (exflag),
603               [tmp] "=&r" (tmp)
604             : [ptr] "r"   (p),
605               [val] "r"   (val)
606             : "cc", "memory");
607         return (ret);
608 }
609
610 static __inline u_long
611 atomic_fetchadd_long(volatile u_long *p, u_long val)
612 {
613
614         return (atomic_fetchadd_32((volatile uint32_t *)p, val));
615 }
616
617 static __inline uint32_t
618 atomic_load_acq_32(volatile uint32_t *p)
619 {
620         uint32_t v;
621
622         v = *p;
623         dmb();
624         return (v);
625 }
626
627 static __inline uint64_t
628 atomic_load_64(volatile uint64_t *p)
629 {
630         uint64_t ret;
631
632         /*
633          * The only way to atomically load 64 bits is with LDREXD which puts the
634          * exclusive monitor into the exclusive state, so reset it to open state
635          * with CLREX because we don't actually need to store anything.
636          */
637         __asm __volatile(
638             "ldrexd     %Q[ret], %R[ret], [%[ptr]]      \n"
639             "clrex                                      \n"
640             : [ret] "=&r" (ret)
641             : [ptr] "r"   (p)
642             : "cc", "memory");
643         return (ret);
644 }
645
646 static __inline uint64_t
647 atomic_load_acq_64(volatile uint64_t *p)
648 {
649         uint64_t ret;
650
651         ret = atomic_load_64(p);
652         dmb();
653         return (ret);
654 }
655
656 static __inline u_long
657 atomic_load_acq_long(volatile u_long *p)
658 {
659         u_long v;
660
661         v = *p;
662         dmb();
663         return (v);
664 }
665
666 static __inline uint32_t
667 atomic_readandclear_32(volatile uint32_t *p)
668 {
669         uint32_t ret, tmp = 0, tmp2 = 0;
670
671         __asm __volatile(
672             "1: ldrex   %0, [%3]        \n"
673             "   mov     %1, #0          \n"
674             "   strex   %2, %1, [%3]    \n"
675             "   cmp     %2, #0          \n"
676             "   it      ne              \n"
677             "   bne     1b              \n"
678             : "=r" (ret), "=&r" (tmp), "+r" (tmp2), "+r" (p)
679             : : "cc", "memory");
680         return (ret);
681 }
682
683 static __inline uint64_t
684 atomic_readandclear_64(volatile uint64_t *p)
685 {
686         uint64_t ret, tmp;
687         uint32_t exflag;
688
689         __asm __volatile(
690             "1:                                                 \n"
691             "   ldrexd  %Q[ret], %R[ret], [%[ptr]]              \n"
692             "   mov     %Q[tmp], #0                             \n"
693             "   mov     %R[tmp], #0                             \n"
694             "   strexd  %[exf], %Q[tmp], %R[tmp], [%[ptr]]      \n"
695             "   teq     %[exf], #0                              \n"
696             "   it      ne                                      \n"
697             "   bne     1b                                      \n"
698             : [ret] "=&r" (ret),
699               [exf] "=&r" (exflag),
700               [tmp] "=&r" (tmp)
701             : [ptr] "r"   (p)
702             : "cc", "memory");
703         return (ret);
704 }
705
706 static __inline u_long
707 atomic_readandclear_long(volatile u_long *p)
708 {
709
710         return (atomic_readandclear_32((volatile uint32_t *)p));
711 }
712
713 static __inline void
714 atomic_set_32(volatile uint32_t *address, uint32_t setmask)
715 {
716         uint32_t tmp = 0, tmp2 = 0;
717
718         __asm __volatile(
719             "1: ldrex   %0, [%2]        \n"
720             "   orr     %0, %0, %3      \n"
721             "   strex   %1, %0, [%2]    \n"
722             "   cmp     %1, #0          \n"
723             "   it      ne              \n"
724             "   bne     1b              \n"
725             : "=&r" (tmp), "+r" (tmp2), "+r" (address), "+r" (setmask)
726             : : "cc", "memory");
727 }
728
729 static __inline void
730 atomic_set_64(volatile uint64_t *p, uint64_t val)
731 {
732         uint64_t tmp;
733         uint32_t exflag;
734
735         __asm __volatile(
736             "1:                                                 \n"
737             "   ldrexd  %Q[tmp], %R[tmp], [%[ptr]]              \n"
738             "   orr     %Q[tmp], %Q[val]                        \n"
739             "   orr     %R[tmp], %R[val]                        \n"
740             "   strexd  %[exf], %Q[tmp], %R[tmp], [%[ptr]]      \n"
741             "   teq     %[exf], #0                              \n"
742             "   it      ne                                      \n"
743             "   bne     1b                                      \n"
744             : [exf] "=&r" (exflag),
745               [tmp] "=&r" (tmp)
746             : [ptr] "r"   (p),
747               [val] "r"   (val)
748             : "cc", "memory");
749 }
750
751 static __inline void
752 atomic_set_long(volatile u_long *address, u_long setmask)
753 {
754
755         atomic_set_32((volatile uint32_t *)address, setmask);
756 }
757
758 ATOMIC_ACQ_REL(set, 32)
759 ATOMIC_ACQ_REL(set, 64)
760 ATOMIC_ACQ_REL_LONG(set)
761
762 static __inline void
763 atomic_subtract_32(volatile uint32_t *p, uint32_t val)
764 {
765         uint32_t tmp = 0, tmp2 = 0;
766
767         __asm __volatile(
768             "1: ldrex   %0, [%2]        \n"
769             "   sub     %0, %0, %3      \n"
770             "   strex   %1, %0, [%2]    \n"
771             "   cmp     %1, #0          \n"
772             "   it      ne              \n"
773             "   bne     1b              \n"
774             : "=&r" (tmp), "+r" (tmp2), "+r" (p), "+r" (val)
775             : : "cc", "memory");
776 }
777
778 static __inline void
779 atomic_subtract_64(volatile uint64_t *p, uint64_t val)
780 {
781         uint64_t tmp;
782         uint32_t exflag;
783
784         __asm __volatile(
785             "1:                                                 \n"
786             "   ldrexd  %Q[tmp], %R[tmp], [%[ptr]]              \n"
787             "   subs    %Q[tmp], %Q[val]                        \n"
788             "   sbc     %R[tmp], %R[tmp], %R[val]               \n"
789             "   strexd  %[exf], %Q[tmp], %R[tmp], [%[ptr]]      \n"
790             "   teq     %[exf], #0                              \n"
791             "   it      ne                                      \n"
792             "   bne     1b                                      \n"
793             : [exf] "=&r" (exflag),
794               [tmp] "=&r" (tmp)
795             : [ptr] "r"   (p),
796               [val] "r"   (val)
797             : "cc", "memory");
798 }
799
800 static __inline void
801 atomic_subtract_long(volatile u_long *p, u_long val)
802 {
803
804         atomic_subtract_32((volatile uint32_t *)p, val);
805 }
806
807 ATOMIC_ACQ_REL(subtract, 32)
808 ATOMIC_ACQ_REL(subtract, 64)
809 ATOMIC_ACQ_REL_LONG(subtract)
810
811 static __inline void
812 atomic_store_64(volatile uint64_t *p, uint64_t val)
813 {
814         uint64_t tmp;
815         uint32_t exflag;
816
817         /*
818          * The only way to atomically store 64 bits is with STREXD, which will
819          * succeed only if paired up with a preceeding LDREXD using the same
820          * address, so we read and discard the existing value before storing.
821          */
822         __asm __volatile(
823             "1:                                                 \n"
824             "   ldrexd  %Q[tmp], %R[tmp], [%[ptr]]              \n"
825             "   strexd  %[exf], %Q[val], %R[val], [%[ptr]]      \n"
826             "   teq     %[exf], #0                              \n"
827             "   it      ne                                      \n"
828             "   bne     1b                                      \n"
829             : [tmp] "=&r" (tmp),
830               [exf] "=&r" (exflag)
831             : [ptr] "r"   (p),
832               [val] "r"   (val)
833             : "cc", "memory");
834 }
835
836 static __inline void
837 atomic_store_rel_32(volatile uint32_t *p, uint32_t v)
838 {
839
840         dmb();
841         *p = v;
842 }
843
844 static __inline void
845 atomic_store_rel_64(volatile uint64_t *p, uint64_t val)
846 {
847
848         dmb();
849         atomic_store_64(p, val);
850 }
851
852 static __inline void
853 atomic_store_rel_long(volatile u_long *p, u_long v)
854 {
855
856         dmb();
857         *p = v;
858 }
859
860 static __inline int
861 atomic_testandset_32(volatile uint32_t *p, u_int v)
862 {
863         uint32_t tmp, tmp2, res, mask;
864
865         mask = 1u << (v & 0x1f);
866         tmp = tmp2 = 0;
867         __asm __volatile(
868         "1:     ldrex   %0, [%4]        \n"
869         "       orr     %1, %0, %3      \n"
870         "       strex   %2, %1, [%4]    \n"
871         "       cmp     %2, #0          \n"
872         "       it      ne              \n"
873         "       bne     1b              \n"
874         : "=&r" (res), "=&r" (tmp), "=&r" (tmp2)
875         : "r" (mask), "r" (p)
876         : "cc", "memory");
877         return ((res & mask) != 0);
878 }
879
880 static __inline int
881 atomic_testandset_int(volatile u_int *p, u_int v)
882 {
883
884         return (atomic_testandset_32((volatile uint32_t *)p, v));
885 }
886
887 static __inline int
888 atomic_testandset_long(volatile u_long *p, u_int v)
889 {
890
891         return (atomic_testandset_32((volatile uint32_t *)p, v));
892 }
893
894 static __inline int
895 atomic_testandset_64(volatile uint64_t *p, u_int v)
896 {
897         volatile uint32_t *p32;
898
899         p32 = (volatile uint32_t *)p;
900         /* Assume little-endian */
901         if (v >= 32) {
902                 v &= 0x1f;
903                 p32++;
904         }
905         return (atomic_testandset_32(p32, v));
906 }
907
908 static __inline uint32_t
909 atomic_swap_32(volatile uint32_t *p, uint32_t v)
910 {
911         uint32_t ret, exflag;
912
913         __asm __volatile(
914             "1: ldrex   %[ret], [%[ptr]]                \n"
915             "   strex   %[exf], %[val], [%[ptr]]        \n"
916             "   teq     %[exf], #0                      \n"
917             "   it      ne                              \n"
918             "   bne     1b                              \n"
919             : [ret] "=&r"  (ret),
920               [exf] "=&r" (exflag)
921             : [val] "r"  (v),
922               [ptr] "r"  (p)
923             : "cc", "memory");
924         return (ret);
925 }
926
927 static __inline uint64_t
928 atomic_swap_64(volatile uint64_t *p, uint64_t v)
929 {
930         uint64_t ret;
931         uint32_t exflag;
932
933         __asm __volatile(
934             "1: ldrexd  %Q[ret], %R[ret], [%[ptr]]              \n"
935             "   strexd  %[exf], %Q[val], %R[val], [%[ptr]]      \n"
936             "   teq     %[exf], #0                              \n"
937             "   it      ne                                      \n"
938             "   bne     1b                                      \n"
939             : [ret] "=&r" (ret),
940               [exf] "=&r" (exflag)
941             : [val] "r"   (v),
942               [ptr] "r"   (p)
943             : "cc", "memory");
944         return (ret);
945 }
946
947 #undef ATOMIC_ACQ_REL
948 #undef ATOMIC_ACQ_REL_LONG
949
950 static __inline void
951 atomic_thread_fence_acq(void)
952 {
953
954         dmb();
955 }
956
957 static __inline void
958 atomic_thread_fence_rel(void)
959 {
960
961         dmb();
962 }
963
964 static __inline void
965 atomic_thread_fence_acq_rel(void)
966 {
967
968         dmb();
969 }
970
971 static __inline void
972 atomic_thread_fence_seq_cst(void)
973 {
974
975         dmb();
976 }
977
978 #endif /* _MACHINE_ATOMIC_V6_H_ */