patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / include / asm-arm / atomic.h
1 /*
2  *  linux/include/asm-arm/atomic.h
3  *
4  *  Copyright (C) 1996 Russell King.
5  *  Copyright (C) 2002 Deep Blue Solutions Ltd.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 #ifndef __ASM_ARM_ATOMIC_H
12 #define __ASM_ARM_ATOMIC_H
13
14 #include <linux/config.h>
15
16 typedef struct { volatile int counter; } atomic_t;
17
18 #define ATOMIC_INIT(i)  { (i) }
19
20 #ifdef __KERNEL__
21
22 #define atomic_read(v)  ((v)->counter)
23
24 #if __LINUX_ARM_ARCH__ >= 6
25
26 /*
27  * ARMv6 UP and SMP safe atomic ops.  We use load exclusive and
28  * store exclusive to ensure that these are atomic.  We may loop
29  * to ensure that the update happens.  Writing to 'v->counter'
30  * without using the following operations WILL break the atomic
31  * nature of these ops.
32  */
33 static inline void atomic_set(atomic_t *v, int i)
34 {
35         unsigned long tmp;
36
37         __asm__ __volatile__("@ atomic_set\n"
38 "1:     ldrex   %0, [%1]\n"
39 "       strex   %0, %2, [%1]\n"
40 "       teq     %0, #0\n"
41 "       bne     1b"
42         : "=&r" (tmp)
43         : "r" (&v->counter), "r" (i)
44         : "cc");
45 }
46
47 static inline void atomic_add(int i, atomic_t *v)
48 {
49         unsigned long tmp, tmp2;
50
51         __asm__ __volatile__("@ atomic_add\n"
52 "1:     ldrex   %0, [%2]\n"
53 "       add     %0, %0, %3\n"
54 "       strex   %1, %0, [%2]\n"
55 "       teq     %1, #0\n"
56 "       bne     1b"
57         : "=&r" (tmp), "=&r" (tmp2)
58         : "r" (&v->counter), "Ir" (i)
59         : "cc");
60 }
61
62 static inline int atomic_add_return(int i, atomic_t *v)
63 {
64         unsigned long tmp;
65         int result;
66
67         __asm__ __volatile__("@ atomic_add_return\n"
68 "1:     ldrex   %0, [%2]\n"
69 "       add     %0, %0, %3\n"
70 "       strex   %1, %0, [%2]\n"
71 "       teq     %1, #0\n"
72 "       bne     1b"
73         : "=&r" (result), "=&r" (tmp)
74         : "r" (&v->counter), "Ir" (i)
75         : "cc");
76
77         return result;
78 }
79
80 static inline void atomic_sub(int i, atomic_t *v)
81 {
82         unsigned long tmp, tmp2;
83
84         __asm__ __volatile__("@ atomic_sub\n"
85 "1:     ldrex   %0, [%2]\n"
86 "       sub     %0, %0, %3\n"
87 "       strex   %1, %0, [%2]\n"
88 "       teq     %1, #0\n"
89 "       bne     1b"
90         : "=&r" (tmp), "=&r" (tmp2)
91         : "r" (&v->counter), "Ir" (i)
92         : "cc");
93 }
94
95 static inline int atomic_sub_return(int i, atomic_t *v)
96 {
97         unsigned long tmp;
98         int result;
99
100         __asm__ __volatile__("@ atomic_sub_return\n"
101 "1:     ldrex   %0, [%2]\n"
102 "       sub     %0, %0, %3\n"
103 "       strex   %1, %0, [%2]\n"
104 "       teq     %1, #0\n"
105 "       bne     1b"
106         : "=&r" (result), "=&r" (tmp)
107         : "r" (&v->counter), "Ir" (i)
108         : "cc");
109
110         return result;
111 }
112
113 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
114 {
115         unsigned long tmp, tmp2;
116
117         __asm__ __volatile__("@ atomic_clear_mask\n"
118 "1:     ldrex   %0, %2\n"
119 "       bic     %0, %0, %3\n"
120 "       strex   %1, %0, %2\n"
121 "       teq     %1, #0\n"
122 "       bne     1b"
123         : "=&r" (tmp), "=&r" (tmp2)
124         : "r" (addr), "Ir" (mask)
125         : "cc");
126 }
127
128 #else /* ARM_ARCH_6 */
129
130 #include <asm/system.h>
131
132 #ifdef CONFIG_SMP
133 #error SMP not supported on pre-ARMv6 CPUs
134 #endif
135
136 #define atomic_set(v,i) (((v)->counter) = (i))
137
138 static inline void atomic_add(int i, atomic_t *v)
139 {
140         unsigned long flags;
141
142         local_irq_save(flags);
143         v->counter += i;
144         local_irq_restore(flags);
145 }
146
147 static inline int atomic_add_return(int i, atomic_t *v)
148 {
149         unsigned long flags;
150         int val;
151
152         local_irq_save(flags);
153         val = v->counter;
154         v->counter = val += i;
155         local_irq_restore(flags);
156
157         return val;
158 }
159
160 static inline void atomic_sub(int i, atomic_t *v)
161 {
162         unsigned long flags;
163
164         local_irq_save(flags);
165         v->counter -= i;
166         local_irq_restore(flags);
167 }
168
169 static inline int atomic_sub_return(int i, atomic_t *v)
170 {
171         unsigned long flags;
172         int val;
173
174         local_irq_save(flags);
175         val = v->counter;
176         v->counter = val -= i;
177         local_irq_restore(flags);
178
179         return val;
180 }
181
182 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
183 {
184         unsigned long flags;
185
186         local_irq_save(flags);
187         *addr &= ~mask;
188         local_irq_restore(flags);
189 }
190
191 #endif /* __LINUX_ARM_ARCH__ */
192
193 #define atomic_inc(v)           atomic_add(1, v)
194 #define atomic_dec(v)           atomic_sub(1, v)
195
196 #define atomic_inc_and_test(v)  (atomic_add_return(1, v) == 0)
197 #define atomic_dec_and_test(v)  (atomic_sub_return(1, v) == 0)
198
199 #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
200
201 /* Atomic operations are already serializing on ARM */
202 #define smp_mb__before_atomic_dec()     barrier()
203 #define smp_mb__after_atomic_dec()      barrier()
204 #define smp_mb__before_atomic_inc()     barrier()
205 #define smp_mb__after_atomic_inc()      barrier()
206
207 #endif
208 #endif