GNU Linux-libre 4.19.245-gnu1
[releases.git] / arch / arc / include / asm / atomic.h
1 /*
2  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #ifndef _ASM_ARC_ATOMIC_H
10 #define _ASM_ARC_ATOMIC_H
11
12 #ifndef __ASSEMBLY__
13
14 #include <linux/types.h>
15 #include <linux/compiler.h>
16 #include <asm/cmpxchg.h>
17 #include <asm/barrier.h>
18 #include <asm/smp.h>
19
20 #define ATOMIC_INIT(i)  { (i) }
21
22 #ifndef CONFIG_ARC_PLAT_EZNPS
23
24 #define atomic_read(v)  READ_ONCE((v)->counter)
25
26 #ifdef CONFIG_ARC_HAS_LLSC
27
28 #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
29
30 #define ATOMIC_OP(op, c_op, asm_op)                                     \
31 static inline void atomic_##op(int i, atomic_t *v)                      \
32 {                                                                       \
33         unsigned int val;                                               \
34                                                                         \
35         __asm__ __volatile__(                                           \
36         "1:     llock   %[val], [%[ctr]]                \n"             \
37         "       " #asm_op " %[val], %[val], %[i]        \n"             \
38         "       scond   %[val], [%[ctr]]                \n"             \
39         "       bnz     1b                              \n"             \
40         : [val] "=&r"   (val) /* Early clobber to prevent reg reuse */  \
41         : [ctr] "r"     (&v->counter), /* Not "m": llock only supports reg direct addr mode */  \
42           [i]   "ir"    (i)                                             \
43         : "cc");                                                        \
44 }                                                                       \
45
46 #define ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
47 static inline int atomic_##op##_return(int i, atomic_t *v)              \
48 {                                                                       \
49         unsigned int val;                                               \
50                                                                         \
51         /*                                                              \
52          * Explicit full memory barrier needed before/after as          \
53          * LLOCK/SCOND thmeselves don't provide any such semantics      \
54          */                                                             \
55         smp_mb();                                                       \
56                                                                         \
57         __asm__ __volatile__(                                           \
58         "1:     llock   %[val], [%[ctr]]                \n"             \
59         "       " #asm_op " %[val], %[val], %[i]        \n"             \
60         "       scond   %[val], [%[ctr]]                \n"             \
61         "       bnz     1b                              \n"             \
62         : [val] "=&r"   (val)                                           \
63         : [ctr] "r"     (&v->counter),                                  \
64           [i]   "ir"    (i)                                             \
65         : "cc");                                                        \
66                                                                         \
67         smp_mb();                                                       \
68                                                                         \
69         return val;                                                     \
70 }
71
72 #define ATOMIC_FETCH_OP(op, c_op, asm_op)                               \
73 static inline int atomic_fetch_##op(int i, atomic_t *v)                 \
74 {                                                                       \
75         unsigned int val, orig;                                         \
76                                                                         \
77         /*                                                              \
78          * Explicit full memory barrier needed before/after as          \
79          * LLOCK/SCOND thmeselves don't provide any such semantics      \
80          */                                                             \
81         smp_mb();                                                       \
82                                                                         \
83         __asm__ __volatile__(                                           \
84         "1:     llock   %[orig], [%[ctr]]               \n"             \
85         "       " #asm_op " %[val], %[orig], %[i]       \n"             \
86         "       scond   %[val], [%[ctr]]                \n"             \
87         "       bnz     1b                              \n"             \
88         : [val] "=&r"   (val),                                          \
89           [orig] "=&r" (orig)                                           \
90         : [ctr] "r"     (&v->counter),                                  \
91           [i]   "ir"    (i)                                             \
92         : "cc");                                                        \
93                                                                         \
94         smp_mb();                                                       \
95                                                                         \
96         return orig;                                                    \
97 }
98
99 #else   /* !CONFIG_ARC_HAS_LLSC */
100
101 #ifndef CONFIG_SMP
102
103  /* violating atomic_xxx API locking protocol in UP for optimization sake */
104 #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
105
106 #else
107
108 static inline void atomic_set(atomic_t *v, int i)
109 {
110         /*
111          * Independent of hardware support, all of the atomic_xxx() APIs need
112          * to follow the same locking rules to make sure that a "hardware"
113          * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
114          * sequence
115          *
116          * Thus atomic_set() despite being 1 insn (and seemingly atomic)
117          * requires the locking.
118          */
119         unsigned long flags;
120
121         atomic_ops_lock(flags);
122         WRITE_ONCE(v->counter, i);
123         atomic_ops_unlock(flags);
124 }
125
126 #define atomic_set_release(v, i)        atomic_set((v), (i))
127
128 #endif
129
130 /*
131  * Non hardware assisted Atomic-R-M-W
132  * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
133  */
134
135 #define ATOMIC_OP(op, c_op, asm_op)                                     \
136 static inline void atomic_##op(int i, atomic_t *v)                      \
137 {                                                                       \
138         unsigned long flags;                                            \
139                                                                         \
140         atomic_ops_lock(flags);                                         \
141         v->counter c_op i;                                              \
142         atomic_ops_unlock(flags);                                       \
143 }
144
145 #define ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
146 static inline int atomic_##op##_return(int i, atomic_t *v)              \
147 {                                                                       \
148         unsigned long flags;                                            \
149         unsigned long temp;                                             \
150                                                                         \
151         /*                                                              \
152          * spin lock/unlock provides the needed smp_mb() before/after   \
153          */                                                             \
154         atomic_ops_lock(flags);                                         \
155         temp = v->counter;                                              \
156         temp c_op i;                                                    \
157         v->counter = temp;                                              \
158         atomic_ops_unlock(flags);                                       \
159                                                                         \
160         return temp;                                                    \
161 }
162
163 #define ATOMIC_FETCH_OP(op, c_op, asm_op)                               \
164 static inline int atomic_fetch_##op(int i, atomic_t *v)                 \
165 {                                                                       \
166         unsigned long flags;                                            \
167         unsigned long orig;                                             \
168                                                                         \
169         /*                                                              \
170          * spin lock/unlock provides the needed smp_mb() before/after   \
171          */                                                             \
172         atomic_ops_lock(flags);                                         \
173         orig = v->counter;                                              \
174         v->counter c_op i;                                              \
175         atomic_ops_unlock(flags);                                       \
176                                                                         \
177         return orig;                                                    \
178 }
179
180 #endif /* !CONFIG_ARC_HAS_LLSC */
181
182 #define ATOMIC_OPS(op, c_op, asm_op)                                    \
183         ATOMIC_OP(op, c_op, asm_op)                                     \
184         ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
185         ATOMIC_FETCH_OP(op, c_op, asm_op)
186
187 ATOMIC_OPS(add, +=, add)
188 ATOMIC_OPS(sub, -=, sub)
189
190 #define atomic_andnot           atomic_andnot
191 #define atomic_fetch_andnot     atomic_fetch_andnot
192
193 #undef ATOMIC_OPS
194 #define ATOMIC_OPS(op, c_op, asm_op)                                    \
195         ATOMIC_OP(op, c_op, asm_op)                                     \
196         ATOMIC_FETCH_OP(op, c_op, asm_op)
197
198 ATOMIC_OPS(and, &=, and)
199 ATOMIC_OPS(andnot, &= ~, bic)
200 ATOMIC_OPS(or, |=, or)
201 ATOMIC_OPS(xor, ^=, xor)
202
203 #else /* CONFIG_ARC_PLAT_EZNPS */
204
205 static inline int atomic_read(const atomic_t *v)
206 {
207         int temp;
208
209         __asm__ __volatile__(
210         "       ld.di %0, [%1]"
211         : "=r"(temp)
212         : "r"(&v->counter)
213         : "memory");
214         return temp;
215 }
216
217 static inline void atomic_set(atomic_t *v, int i)
218 {
219         __asm__ __volatile__(
220         "       st.di %0,[%1]"
221         :
222         : "r"(i), "r"(&v->counter)
223         : "memory");
224 }
225
226 #define ATOMIC_OP(op, c_op, asm_op)                                     \
227 static inline void atomic_##op(int i, atomic_t *v)                      \
228 {                                                                       \
229         __asm__ __volatile__(                                           \
230         "       mov r2, %0\n"                                           \
231         "       mov r3, %1\n"                                           \
232         "       .word %2\n"                                             \
233         :                                                               \
234         : "r"(i), "r"(&v->counter), "i"(asm_op)                         \
235         : "r2", "r3", "memory");                                        \
236 }                                                                       \
237
238 #define ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
239 static inline int atomic_##op##_return(int i, atomic_t *v)              \
240 {                                                                       \
241         unsigned int temp = i;                                          \
242                                                                         \
243         /* Explicit full memory barrier needed before/after */          \
244         smp_mb();                                                       \
245                                                                         \
246         __asm__ __volatile__(                                           \
247         "       mov r2, %0\n"                                           \
248         "       mov r3, %1\n"                                           \
249         "       .word %2\n"                                             \
250         "       mov %0, r2"                                             \
251         : "+r"(temp)                                                    \
252         : "r"(&v->counter), "i"(asm_op)                                 \
253         : "r2", "r3", "memory");                                        \
254                                                                         \
255         smp_mb();                                                       \
256                                                                         \
257         temp c_op i;                                                    \
258                                                                         \
259         return temp;                                                    \
260 }
261
262 #define ATOMIC_FETCH_OP(op, c_op, asm_op)                               \
263 static inline int atomic_fetch_##op(int i, atomic_t *v)                 \
264 {                                                                       \
265         unsigned int temp = i;                                          \
266                                                                         \
267         /* Explicit full memory barrier needed before/after */          \
268         smp_mb();                                                       \
269                                                                         \
270         __asm__ __volatile__(                                           \
271         "       mov r2, %0\n"                                           \
272         "       mov r3, %1\n"                                           \
273         "       .word %2\n"                                             \
274         "       mov %0, r2"                                             \
275         : "+r"(temp)                                                    \
276         : "r"(&v->counter), "i"(asm_op)                                 \
277         : "r2", "r3", "memory");                                        \
278                                                                         \
279         smp_mb();                                                       \
280                                                                         \
281         return temp;                                                    \
282 }
283
284 #define ATOMIC_OPS(op, c_op, asm_op)                                    \
285         ATOMIC_OP(op, c_op, asm_op)                                     \
286         ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
287         ATOMIC_FETCH_OP(op, c_op, asm_op)
288
289 ATOMIC_OPS(add, +=, CTOP_INST_AADD_DI_R2_R2_R3)
290 #define atomic_sub(i, v) atomic_add(-(i), (v))
291 #define atomic_sub_return(i, v) atomic_add_return(-(i), (v))
292 #define atomic_fetch_sub(i, v) atomic_fetch_add(-(i), (v))
293
294 #undef ATOMIC_OPS
295 #define ATOMIC_OPS(op, c_op, asm_op)                                    \
296         ATOMIC_OP(op, c_op, asm_op)                                     \
297         ATOMIC_FETCH_OP(op, c_op, asm_op)
298
299 ATOMIC_OPS(and, &=, CTOP_INST_AAND_DI_R2_R2_R3)
300 ATOMIC_OPS(or, |=, CTOP_INST_AOR_DI_R2_R2_R3)
301 ATOMIC_OPS(xor, ^=, CTOP_INST_AXOR_DI_R2_R2_R3)
302
303 #endif /* CONFIG_ARC_PLAT_EZNPS */
304
305 #undef ATOMIC_OPS
306 #undef ATOMIC_FETCH_OP
307 #undef ATOMIC_OP_RETURN
308 #undef ATOMIC_OP
309
310 #ifdef CONFIG_GENERIC_ATOMIC64
311
312 #include <asm-generic/atomic64.h>
313
314 #else   /* Kconfig ensures this is only enabled with needed h/w assist */
315
316 /*
317  * ARCv2 supports 64-bit exclusive load (LLOCKD) / store (SCONDD)
318  *  - The address HAS to be 64-bit aligned
319  *  - There are 2 semantics involved here:
320  *    = exclusive implies no interim update between load/store to same addr
321  *    = both words are observed/updated together: this is guaranteed even
322  *      for regular 64-bit load (LDD) / store (STD). Thus atomic64_set()
323  *      is NOT required to use LLOCKD+SCONDD, STD suffices
324  */
325
326 typedef struct {
327         aligned_u64 counter;
328 } atomic64_t;
329
330 #define ATOMIC64_INIT(a) { (a) }
331
332 static inline long long atomic64_read(const atomic64_t *v)
333 {
334         unsigned long long val;
335
336         __asm__ __volatile__(
337         "       ldd   %0, [%1]  \n"
338         : "=r"(val)
339         : "r"(&v->counter));
340
341         return val;
342 }
343
344 static inline void atomic64_set(atomic64_t *v, long long a)
345 {
346         /*
347          * This could have been a simple assignment in "C" but would need
348          * explicit volatile. Otherwise gcc optimizers could elide the store
349          * which borked atomic64 self-test
350          * In the inline asm version, memory clobber needed for exact same
351          * reason, to tell gcc about the store.
352          *
353          * This however is not needed for sibling atomic64_add() etc since both
354          * load/store are explicitly done in inline asm. As long as API is used
355          * for each access, gcc has no way to optimize away any load/store
356          */
357         __asm__ __volatile__(
358         "       std   %0, [%1]  \n"
359         :
360         : "r"(a), "r"(&v->counter)
361         : "memory");
362 }
363
364 #define ATOMIC64_OP(op, op1, op2)                                       \
365 static inline void atomic64_##op(long long a, atomic64_t *v)            \
366 {                                                                       \
367         unsigned long long val;                                         \
368                                                                         \
369         __asm__ __volatile__(                                           \
370         "1:                             \n"                             \
371         "       llockd  %0, [%1]        \n"                             \
372         "       " #op1 " %L0, %L0, %L2  \n"                             \
373         "       " #op2 " %H0, %H0, %H2  \n"                             \
374         "       scondd   %0, [%1]       \n"                             \
375         "       bnz     1b              \n"                             \
376         : "=&r"(val)                                                    \
377         : "r"(&v->counter), "ir"(a)                                     \
378         : "cc");                                                \
379 }                                                                       \
380
381 #define ATOMIC64_OP_RETURN(op, op1, op2)                                \
382 static inline long long atomic64_##op##_return(long long a, atomic64_t *v)      \
383 {                                                                       \
384         unsigned long long val;                                         \
385                                                                         \
386         smp_mb();                                                       \
387                                                                         \
388         __asm__ __volatile__(                                           \
389         "1:                             \n"                             \
390         "       llockd   %0, [%1]       \n"                             \
391         "       " #op1 " %L0, %L0, %L2  \n"                             \
392         "       " #op2 " %H0, %H0, %H2  \n"                             \
393         "       scondd   %0, [%1]       \n"                             \
394         "       bnz     1b              \n"                             \
395         : [val] "=&r"(val)                                              \
396         : "r"(&v->counter), "ir"(a)                                     \
397         : "cc");        /* memory clobber comes from smp_mb() */        \
398                                                                         \
399         smp_mb();                                                       \
400                                                                         \
401         return val;                                                     \
402 }
403
404 #define ATOMIC64_FETCH_OP(op, op1, op2)                                 \
405 static inline long long atomic64_fetch_##op(long long a, atomic64_t *v) \
406 {                                                                       \
407         unsigned long long val, orig;                                   \
408                                                                         \
409         smp_mb();                                                       \
410                                                                         \
411         __asm__ __volatile__(                                           \
412         "1:                             \n"                             \
413         "       llockd   %0, [%2]       \n"                             \
414         "       " #op1 " %L1, %L0, %L3  \n"                             \
415         "       " #op2 " %H1, %H0, %H3  \n"                             \
416         "       scondd   %1, [%2]       \n"                             \
417         "       bnz     1b              \n"                             \
418         : "=&r"(orig), "=&r"(val)                                       \
419         : "r"(&v->counter), "ir"(a)                                     \
420         : "cc");        /* memory clobber comes from smp_mb() */        \
421                                                                         \
422         smp_mb();                                                       \
423                                                                         \
424         return orig;                                                    \
425 }
426
427 #define ATOMIC64_OPS(op, op1, op2)                                      \
428         ATOMIC64_OP(op, op1, op2)                                       \
429         ATOMIC64_OP_RETURN(op, op1, op2)                                \
430         ATOMIC64_FETCH_OP(op, op1, op2)
431
432 #define atomic64_andnot         atomic64_andnot
433 #define atomic64_fetch_andnot   atomic64_fetch_andnot
434
435 ATOMIC64_OPS(add, add.f, adc)
436 ATOMIC64_OPS(sub, sub.f, sbc)
437 ATOMIC64_OPS(and, and, and)
438 ATOMIC64_OPS(andnot, bic, bic)
439 ATOMIC64_OPS(or, or, or)
440 ATOMIC64_OPS(xor, xor, xor)
441
442 #undef ATOMIC64_OPS
443 #undef ATOMIC64_FETCH_OP
444 #undef ATOMIC64_OP_RETURN
445 #undef ATOMIC64_OP
446
447 static inline long long
448 atomic64_cmpxchg(atomic64_t *ptr, long long expected, long long new)
449 {
450         long long prev;
451
452         smp_mb();
453
454         __asm__ __volatile__(
455         "1:     llockd  %0, [%1]        \n"
456         "       brne    %L0, %L2, 2f    \n"
457         "       brne    %H0, %H2, 2f    \n"
458         "       scondd  %3, [%1]        \n"
459         "       bnz     1b              \n"
460         "2:                             \n"
461         : "=&r"(prev)
462         : "r"(ptr), "ir"(expected), "r"(new)
463         : "cc");        /* memory clobber comes from smp_mb() */
464
465         smp_mb();
466
467         return prev;
468 }
469
470 static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
471 {
472         long long prev;
473
474         smp_mb();
475
476         __asm__ __volatile__(
477         "1:     llockd  %0, [%1]        \n"
478         "       scondd  %2, [%1]        \n"
479         "       bnz     1b              \n"
480         "2:                             \n"
481         : "=&r"(prev)
482         : "r"(ptr), "r"(new)
483         : "cc");        /* memory clobber comes from smp_mb() */
484
485         smp_mb();
486
487         return prev;
488 }
489
490 /**
491  * atomic64_dec_if_positive - decrement by 1 if old value positive
492  * @v: pointer of type atomic64_t
493  *
494  * The function returns the old value of *v minus 1, even if
495  * the atomic variable, v, was not decremented.
496  */
497
498 static inline long long atomic64_dec_if_positive(atomic64_t *v)
499 {
500         long long val;
501
502         smp_mb();
503
504         __asm__ __volatile__(
505         "1:     llockd  %0, [%1]        \n"
506         "       sub.f   %L0, %L0, 1     # w0 - 1, set C on borrow\n"
507         "       sub.c   %H0, %H0, 1     # if C set, w1 - 1\n"
508         "       brlt    %H0, 0, 2f      \n"
509         "       scondd  %0, [%1]        \n"
510         "       bnz     1b              \n"
511         "2:                             \n"
512         : "=&r"(val)
513         : "r"(&v->counter)
514         : "cc");        /* memory clobber comes from smp_mb() */
515
516         smp_mb();
517
518         return val;
519 }
520 #define atomic64_dec_if_positive atomic64_dec_if_positive
521
522 /**
523  * atomic64_fetch_add_unless - add unless the number is a given value
524  * @v: pointer of type atomic64_t
525  * @a: the amount to add to v...
526  * @u: ...unless v is equal to u.
527  *
528  * Atomically adds @a to @v, if it was not @u.
529  * Returns the old value of @v
530  */
531 static inline long long atomic64_fetch_add_unless(atomic64_t *v, long long a,
532                                                   long long u)
533 {
534         long long old, temp;
535
536         smp_mb();
537
538         __asm__ __volatile__(
539         "1:     llockd  %0, [%2]        \n"
540         "       brne    %L0, %L4, 2f    # continue to add since v != u \n"
541         "       breq.d  %H0, %H4, 3f    # return since v == u \n"
542         "2:                             \n"
543         "       add.f   %L1, %L0, %L3   \n"
544         "       adc     %H1, %H0, %H3   \n"
545         "       scondd  %1, [%2]        \n"
546         "       bnz     1b              \n"
547         "3:                             \n"
548         : "=&r"(old), "=&r" (temp)
549         : "r"(&v->counter), "r"(a), "r"(u)
550         : "cc");        /* memory clobber comes from smp_mb() */
551
552         smp_mb();
553
554         return old;
555 }
556 #define atomic64_fetch_add_unless atomic64_fetch_add_unless
557
558 #endif  /* !CONFIG_GENERIC_ATOMIC64 */
559
560 #endif  /* !__ASSEMBLY__ */
561
562 #endif