ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / include / asm-mips / atomic.h
1 /*
2  * Atomic operations that C can't guarantee us.  Useful for
3  * resource counting etc..
4  *
5  * But use these as seldom as possible since they are much more slower
6  * than regular operations.
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License.  See the file "COPYING" in the main directory of this archive
10  * for more details.
11  *
12  * Copyright (C) 1996, 97, 99, 2000, 03 by Ralf Baechle
13  */
14
15 /*
16  * As workaround for the ATOMIC_DEC_AND_LOCK / atomic_dec_and_lock mess in
17  * <linux/spinlock.h> we have to include <linux/spinlock.h> outside the
18  * main big wrapper ...
19  */
20 #include <linux/config.h>
21 #include <linux/spinlock.h>
22
23 #ifndef _ASM_ATOMIC_H
24 #define _ASM_ATOMIC_H
25
26 extern spinlock_t atomic_lock;
27
28 typedef struct { volatile int counter; } atomic_t;
29
30 #define ATOMIC_INIT(i)    { (i) }
31
32 /*
33  * atomic_read - read atomic variable
34  * @v: pointer of type atomic_t
35  *
36  * Atomically reads the value of @v.
37  */
38 #define atomic_read(v)          ((v)->counter)
39
40 /*
41  * atomic_set - set atomic variable
42  * @v: pointer of type atomic_t
43  * @i: required value
44  *
45  * Atomically sets the value of @v to @i.
46  */
47 #define atomic_set(v,i)         ((v)->counter = (i))
48
49 #ifdef CONFIG_CPU_HAS_LLSC
50
51 /*
52  * atomic_add - add integer to atomic variable
53  * @i: integer value to add
54  * @v: pointer of type atomic_t
55  *
56  * Atomically adds @i to @v.
57  */
58 static __inline__ void atomic_add(int i, atomic_t * v)
59 {
60         unsigned long temp;
61
62         __asm__ __volatile__(
63         "1:     ll      %0, %1          # atomic_add            \n"
64         "       addu    %0, %2                                  \n"
65         "       sc      %0, %1                                  \n"
66         "       beqz    %0, 1b                                  \n"
67         : "=&r" (temp), "=m" (v->counter)
68         : "Ir" (i), "m" (v->counter));
69 }
70
71 /*
72  * atomic_sub - subtract the atomic variable
73  * @i: integer value to subtract
74  * @v: pointer of type atomic_t
75  *
76  * Atomically subtracts @i from @v.
77  */
78 static __inline__ void atomic_sub(int i, atomic_t * v)
79 {
80         unsigned long temp;
81
82         __asm__ __volatile__(
83         "1:     ll      %0, %1          # atomic_sub            \n"
84         "       subu    %0, %2                                  \n"
85         "       sc      %0, %1                                  \n"
86         "       beqz    %0, 1b                                  \n"
87         : "=&r" (temp), "=m" (v->counter)
88         : "Ir" (i), "m" (v->counter));
89 }
90
91 /*
92  * Same as above, but return the result value
93  */
94 static __inline__ int atomic_add_return(int i, atomic_t * v)
95 {
96         unsigned long temp, result;
97
98         __asm__ __volatile__(
99         "1:     ll      %1, %2          # atomic_add_return     \n"
100         "       addu    %0, %1, %3                              \n"
101         "       sc      %0, %2                                  \n"
102         "       beqz    %0, 1b                                  \n"
103         "       addu    %0, %1, %3                              \n"
104         "       sync                                            \n"
105         : "=&r" (result), "=&r" (temp), "=m" (v->counter)
106         : "Ir" (i), "m" (v->counter)
107         : "memory");
108
109         return result;
110 }
111
112 static __inline__ int atomic_sub_return(int i, atomic_t * v)
113 {
114         unsigned long temp, result;
115
116         __asm__ __volatile__(
117         "1:     ll      %1, %2          # atomic_sub_return     \n"
118         "       subu    %0, %1, %3                              \n"
119         "       sc      %0, %2                                  \n"
120         "       beqz    %0, 1b                                  \n"
121         "       subu    %0, %1, %3                              \n"
122         "       sync                                            \n"
123         : "=&r" (result), "=&r" (temp), "=m" (v->counter)
124         : "Ir" (i), "m" (v->counter)
125         : "memory");
126
127         return result;
128 }
129
130 #else
131
132 /*
133  * The MIPS I implementation is only atomic with respect to
134  * interrupts.  R3000 based multiprocessor machines are rare anyway ...
135  *
136  * atomic_add - add integer to atomic variable
137  * @i: integer value to add
138  * @v: pointer of type atomic_t
139  *
140  * Atomically adds @i to @v.
141  */
142 static __inline__ void atomic_add(int i, atomic_t * v)
143 {
144         unsigned long flags;
145
146         spin_lock_irqsave(&atomic_lock, flags);
147         v->counter += i;
148         spin_unlock_irqrestore(&atomic_lock, flags);
149 }
150
151 /*
152  * atomic_sub - subtract the atomic variable
153  * @i: integer value to subtract
154  * @v: pointer of type atomic_t
155  *
156  * Atomically subtracts @i from @v.
157  */
158 static __inline__ void atomic_sub(int i, atomic_t * v)
159 {
160         unsigned long flags;
161
162         spin_lock_irqsave(&atomic_lock, flags);
163         v->counter -= i;
164         spin_unlock_irqrestore(&atomic_lock, flags);
165 }
166
167 static __inline__ int atomic_add_return(int i, atomic_t * v)
168 {
169         unsigned long flags;
170         int temp;
171
172         spin_lock_irqsave(&atomic_lock, flags);
173         temp = v->counter;
174         temp += i;
175         v->counter = temp;
176         spin_unlock_irqrestore(&atomic_lock, flags);
177
178         return temp;
179 }
180
181 static __inline__ int atomic_sub_return(int i, atomic_t * v)
182 {
183         unsigned long flags;
184         int temp;
185
186         spin_lock_irqsave(&atomic_lock, flags);
187         temp = v->counter;
188         temp -= i;
189         v->counter = temp;
190         spin_unlock_irqrestore(&atomic_lock, flags);
191
192         return temp;
193 }
194
195 #endif /* CONFIG_CPU_HAS_LLSC */
196
197 #define atomic_dec_return(v) atomic_sub_return(1,(v))
198 #define atomic_inc_return(v) atomic_add_return(1,(v))
199
200 /*
201  * atomic_sub_and_test - subtract value from variable and test result
202  * @i: integer value to subtract
203  * @v: pointer of type atomic_t
204  *
205  * Atomically subtracts @i from @v and returns
206  * true if the result is zero, or false for all
207  * other cases.
208  */
209 #define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
210
211 /*
212  * atomic_inc_and_test - increment and test
213  * @v: pointer of type atomic_t
214  *
215  * Atomically increments @v by 1
216  * and returns true if the result is zero, or false for all
217  * other cases.
218  */
219 #define atomic_inc_and_test(v) (atomic_inc_return(v) == 0)
220
221 /*
222  * atomic_dec_and_test - decrement by 1 and test
223  * @v: pointer of type atomic_t
224  *
225  * Atomically decrements @v by 1 and
226  * returns true if the result is 0, or false for all other
227  * cases.
228  */
229 #define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
230
231 /*
232  * atomic_inc - increment atomic variable
233  * @v: pointer of type atomic_t
234  *
235  * Atomically increments @v by 1.
236  */
237 #define atomic_inc(v) atomic_add(1,(v))
238
239 /*
240  * atomic_dec - decrement and test
241  * @v: pointer of type atomic_t
242  *
243  * Atomically decrements @v by 1.
244  */
245 #define atomic_dec(v) atomic_sub(1,(v))
246
247 /*
248  * atomic_add_negative - add and test if negative
249  * @v: pointer of type atomic_t
250  * @i: integer value to add
251  *
252  * Atomically adds @i to @v and returns true
253  * if the result is negative, or false when
254  * result is greater than or equal to zero.
255  */
256 #define atomic_add_negative(i,v) (atomic_add_return(i, (v)) < 0)
257
258 #ifdef CONFIG_MIPS64
259
260 typedef struct { volatile __s64 counter; } atomic64_t;
261
262 #define ATOMIC64_INIT(i)    { (i) }
263
264 /*
265  * atomic64_read - read atomic variable
266  * @v: pointer of type atomic64_t
267  *
268  */
269 #define atomic64_read(v)        ((v)->counter)
270
271 /*
272  * atomic64_set - set atomic variable
273  * @v: pointer of type atomic64_t
274  * @i: required value
275  */
276 #define atomic64_set(v,i)       ((v)->counter = (i))
277
278 #ifdef CONFIG_CPU_HAS_LLDSCD
279
280 /*
281  * atomic64_add - add integer to atomic variable
282  * @i: integer value to add
283  * @v: pointer of type atomic64_t
284  *
285  * Atomically adds @i to @v.
286  */
287 static __inline__ void atomic64_add(int i, atomic64_t * v)
288 {
289         unsigned long temp;
290
291         __asm__ __volatile__(
292         "1:     lld     %0, %1          # atomic64_add          \n"
293         "       addu    %0, %2                                  \n"
294         "       scd     %0, %1                                  \n"
295         "       beqz    %0, 1b                                  \n"
296         : "=&r" (temp), "=m" (v->counter)
297         : "Ir" (i), "m" (v->counter));
298 }
299
300 /*
301  * atomic64_sub - subtract the atomic variable
302  * @i: integer value to subtract
303  * @v: pointer of type atomic64_t
304  *
305  * Atomically subtracts @i from @v.
306  */
307 static __inline__ void atomic64_sub(int i, atomic64_t * v)
308 {
309         unsigned long temp;
310
311         __asm__ __volatile__(
312         "1:     lld     %0, %1          # atomic64_sub          \n"
313         "       subu    %0, %2                                  \n"
314         "       scd     %0, %1                                  \n"
315         "       beqz    %0, 1b                                  \n"
316         : "=&r" (temp), "=m" (v->counter)
317         : "Ir" (i), "m" (v->counter));
318 }
319
320 /*
321  * Same as above, but return the result value
322  */
323 static __inline__ int atomic64_add_return(int i, atomic64_t * v)
324 {
325         unsigned long temp, result;
326
327         __asm__ __volatile__(
328         "1:     lld     %1, %2          # atomic64_add_return   \n"
329         "       addu    %0, %1, %3                              \n"
330         "       scd     %0, %2                                  \n"
331         "       beqz    %0, 1b                                  \n"
332         "       addu    %0, %1, %3                              \n"
333         "       sync                                            \n"
334         : "=&r" (result), "=&r" (temp), "=m" (v->counter)
335         : "Ir" (i), "m" (v->counter)
336         : "memory");
337
338         return result;
339 }
340
341 static __inline__ int atomic64_sub_return(int i, atomic64_t * v)
342 {
343         unsigned long temp, result;
344
345         __asm__ __volatile__(
346         "1:     lld     %1, %2          # atomic64_sub_return   \n"
347         "       subu    %0, %1, %3                              \n"
348         "       scd     %0, %2                                  \n"
349         "       beqz    %0, 1b                                  \n"
350         "       subu    %0, %1, %3                              \n"
351         "       sync                                            \n"
352         : "=&r" (result), "=&r" (temp), "=m" (v->counter)
353         : "Ir" (i), "m" (v->counter)
354         : "memory");
355
356         return result;
357 }
358
359 #else
360
361 /*
362  * This implementation is only atomic with respect to interrupts.  It can't
363  * be used on SMP
364  *
365  * atomic64_add - add integer to atomic variable
366  * @i: integer value to add
367  * @v: pointer of type atomic64_t
368  *
369  * Atomically adds @i to @v.
370  */
371 static __inline__ void atomic64_add(int i, atomic64_t * v)
372 {
373         unsigned long flags;
374
375         spin_lock_irqsave(&atomic_lock, flags);
376         v->counter += i;
377         spin_unlock_irqrestore(&atomic_lock, flags);
378 }
379
380 /*
381  * atomic64_sub - subtract the atomic variable
382  * @i: integer value to subtract
383  * @v: pointer of type atomic64_t
384  *
385  * Atomically subtracts @i from @v.
386  */
387 static __inline__ void atomic64_sub(int i, atomic64_t * v)
388 {
389         unsigned long flags;
390
391         spin_lock_irqsave(&atomic_lock, flags);
392         v->counter -= i;
393         spin_unlock_irqrestore(&atomic_lock, flags);
394 }
395
396 static __inline__ int atomic64_add_return(int i, atomic64_t * v)
397 {
398         unsigned long flags;
399         int temp;
400
401         spin_lock_irqsave(&atomic_lock, flags);
402         temp = v->counter;
403         temp += i;
404         v->counter = temp;
405         spin_unlock_irqrestore(&atomic_lock, flags);
406
407         return temp;
408 }
409
410 static __inline__ int atomic64_sub_return(int i, atomic64_t * v)
411 {
412         unsigned long flags;
413         int temp;
414
415         spin_lock_irqsave(&atomic_lock, flags);
416         temp = v->counter;
417         temp -= i;
418         v->counter = temp;
419         spin_unlock_irqrestore(&atomic_lock, flags);
420
421         return temp;
422 }
423
424 #endif /* CONFIG_CPU_HAS_LLDSCD */
425
426 #define atomic64_dec_return(v) atomic64_sub_return(1,(v))
427 #define atomic64_inc_return(v) atomic64_add_return(1,(v))
428
429 /*
430  * atomic64_sub_and_test - subtract value from variable and test result
431  * @i: integer value to subtract
432  * @v: pointer of type atomic64_t
433  *
434  * Atomically subtracts @i from @v and returns
435  * true if the result is zero, or false for all
436  * other cases.
437  */
438 #define atomic64_sub_and_test(i,v) (atomic64_sub_return((i), (v)) == 0)
439
440 /*
441  * atomic64_inc_and_test - increment and test
442  * @v: pointer of type atomic64_t
443  *
444  * Atomically increments @v by 1
445  * and returns true if the result is zero, or false for all
446  * other cases.
447  */
448 #define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
449
450 /*
451  * atomic64_dec_and_test - decrement by 1 and test
452  * @v: pointer of type atomic64_t
453  *
454  * Atomically decrements @v by 1 and
455  * returns true if the result is 0, or false for all other
456  * cases.
457  */
458 #define atomic64_dec_and_test(v) (atomic64_sub_return(1, (v)) == 0)
459
460 /*
461  * atomic64_inc - increment atomic variable
462  * @v: pointer of type atomic64_t
463  *
464  * Atomically increments @v by 1.
465  */
466 #define atomic64_inc(v) atomic64_add(1,(v))
467
468 /*
469  * atomic64_dec - decrement and test
470  * @v: pointer of type atomic64_t
471  *
472  * Atomically decrements @v by 1.
473  */
474 #define atomic64_dec(v) atomic64_sub(1,(v))
475
476 /*
477  * atomic64_add_negative - add and test if negative
478  * @v: pointer of type atomic64_t
479  * @i: integer value to add
480  *
481  * Atomically adds @i to @v and returns true
482  * if the result is negative, or false when
483  * result is greater than or equal to zero.
484  */
485 #define atomic64_add_negative(i,v) (atomic64_add_return(i, (v)) < 0)
486
487 #endif /* CONFIG_MIPS64 */
488
489 /*
490  * atomic*_return operations are serializing but not the non-*_return
491  * versions.
492  */
493 #define smp_mb__before_atomic_dec()     smp_mb()
494 #define smp_mb__after_atomic_dec()      smp_mb()
495 #define smp_mb__before_atomic_inc()     smp_mb()
496 #define smp_mb__after_atomic_inc()      smp_mb()
497
498 #endif /* _ASM_ATOMIC_H */