6638a0392f4e345b215b504dee1e608ee2b15ae2
[sfrench/cifs-2.6.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_read(v)  ((v)->counter)
21
22 #ifdef CONFIG_ARC_HAS_LLSC
23
24 #define atomic_set(v, i) (((v)->counter) = (i))
25
26 static inline void atomic_add(int i, atomic_t *v)
27 {
28         unsigned int temp;
29
30         __asm__ __volatile__(
31         "1:     llock   %0, [%1]        \n"
32         "       add     %0, %0, %2      \n"
33         "       scond   %0, [%1]        \n"
34         "       bnz     1b              \n"
35         : "=&r"(temp)   /* Early clobber, to prevent reg reuse */
36         : "r"(&v->counter), "ir"(i)
37         : "cc");
38 }
39
40 static inline void atomic_sub(int i, atomic_t *v)
41 {
42         unsigned int temp;
43
44         __asm__ __volatile__(
45         "1:     llock   %0, [%1]        \n"
46         "       sub     %0, %0, %2      \n"
47         "       scond   %0, [%1]        \n"
48         "       bnz     1b              \n"
49         : "=&r"(temp)
50         : "r"(&v->counter), "ir"(i)
51         : "cc");
52 }
53
54 /* add and also return the new value */
55 static inline int atomic_add_return(int i, atomic_t *v)
56 {
57         unsigned int temp;
58
59         __asm__ __volatile__(
60         "1:     llock   %0, [%1]        \n"
61         "       add     %0, %0, %2      \n"
62         "       scond   %0, [%1]        \n"
63         "       bnz     1b              \n"
64         : "=&r"(temp)
65         : "r"(&v->counter), "ir"(i)
66         : "cc");
67
68         return temp;
69 }
70
71 static inline int atomic_sub_return(int i, atomic_t *v)
72 {
73         unsigned int temp;
74
75         __asm__ __volatile__(
76         "1:     llock   %0, [%1]        \n"
77         "       sub     %0, %0, %2      \n"
78         "       scond   %0, [%1]        \n"
79         "       bnz     1b              \n"
80         : "=&r"(temp)
81         : "r"(&v->counter), "ir"(i)
82         : "cc");
83
84         return temp;
85 }
86
87 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
88 {
89         unsigned int temp;
90
91         __asm__ __volatile__(
92         "1:     llock   %0, [%1]        \n"
93         "       bic     %0, %0, %2      \n"
94         "       scond   %0, [%1]        \n"
95         "       bnz     1b              \n"
96         : "=&r"(temp)
97         : "r"(addr), "ir"(mask)
98         : "cc");
99 }
100
101 #else   /* !CONFIG_ARC_HAS_LLSC */
102
103 #ifndef CONFIG_SMP
104
105  /* violating atomic_xxx API locking protocol in UP for optimization sake */
106 #define atomic_set(v, i) (((v)->counter) = (i))
107
108 #else
109
110 static inline void atomic_set(atomic_t *v, int i)
111 {
112         /*
113          * Independent of hardware support, all of the atomic_xxx() APIs need
114          * to follow the same locking rules to make sure that a "hardware"
115          * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
116          * sequence
117          *
118          * Thus atomic_set() despite being 1 insn (and seemingly atomic)
119          * requires the locking.
120          */
121         unsigned long flags;
122
123         atomic_ops_lock(flags);
124         v->counter = i;
125         atomic_ops_unlock(flags);
126 }
127 #endif
128
129 /*
130  * Non hardware assisted Atomic-R-M-W
131  * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
132  */
133
134 static inline void atomic_add(int i, atomic_t *v)
135 {
136         unsigned long flags;
137
138         atomic_ops_lock(flags);
139         v->counter += i;
140         atomic_ops_unlock(flags);
141 }
142
143 static inline void atomic_sub(int i, atomic_t *v)
144 {
145         unsigned long flags;
146
147         atomic_ops_lock(flags);
148         v->counter -= i;
149         atomic_ops_unlock(flags);
150 }
151
152 static inline int atomic_add_return(int i, atomic_t *v)
153 {
154         unsigned long flags;
155         unsigned long temp;
156
157         atomic_ops_lock(flags);
158         temp = v->counter;
159         temp += i;
160         v->counter = temp;
161         atomic_ops_unlock(flags);
162
163         return temp;
164 }
165
166 static inline int atomic_sub_return(int i, atomic_t *v)
167 {
168         unsigned long flags;
169         unsigned long temp;
170
171         atomic_ops_lock(flags);
172         temp = v->counter;
173         temp -= i;
174         v->counter = temp;
175         atomic_ops_unlock(flags);
176
177         return temp;
178 }
179
180 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
181 {
182         unsigned long flags;
183
184         atomic_ops_lock(flags);
185         *addr &= ~mask;
186         atomic_ops_unlock(flags);
187 }
188
189 #endif /* !CONFIG_ARC_HAS_LLSC */
190
191 /**
192  * __atomic_add_unless - add unless the number is a given value
193  * @v: pointer of type atomic_t
194  * @a: the amount to add to v...
195  * @u: ...unless v is equal to u.
196  *
197  * Atomically adds @a to @v, so long as it was not @u.
198  * Returns the old value of @v
199  */
200 #define __atomic_add_unless(v, a, u)                                    \
201 ({                                                                      \
202         int c, old;                                                     \
203         c = atomic_read(v);                                             \
204         while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c)\
205                 c = old;                                                \
206         c;                                                              \
207 })
208
209 #define atomic_inc_not_zero(v)          atomic_add_unless((v), 1, 0)
210
211 #define atomic_inc(v)                   atomic_add(1, v)
212 #define atomic_dec(v)                   atomic_sub(1, v)
213
214 #define atomic_inc_and_test(v)          (atomic_add_return(1, v) == 0)
215 #define atomic_dec_and_test(v)          (atomic_sub_return(1, v) == 0)
216 #define atomic_inc_return(v)            atomic_add_return(1, (v))
217 #define atomic_dec_return(v)            atomic_sub_return(1, (v))
218 #define atomic_sub_and_test(i, v)       (atomic_sub_return(i, v) == 0)
219
220 #define atomic_add_negative(i, v)       (atomic_add_return(i, v) < 0)
221
222 #define ATOMIC_INIT(i)                  { (i) }
223
224 #include <asm-generic/atomic64.h>
225
226 #endif
227
228 #endif