1/*
2 * include/asm-xtensa/atomic.h
3 *
4 * Atomic operations that C can't guarantee us.  Useful for resource counting..
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License.  See the file "COPYING" in the main directory of this archive
8 * for more details.
9 *
10 * Copyright (C) 2001 - 2005 Tensilica Inc.
11 */
12
13#ifndef _XTENSA_ATOMIC_H
14#define _XTENSA_ATOMIC_H
15
16#include <linux/stringify.h>
17
18typedef struct { volatile int counter; } atomic_t;
19
20#ifdef __KERNEL__
21#include <asm/processor.h>
22#include <asm/system.h>
23
24#define ATOMIC_INIT(i)	{ (i) }
25
26/*
27 * This Xtensa implementation assumes that the right mechanism
28 * for exclusion is for locking interrupts to level 1.
29 *
30 * Locking interrupts looks like this:
31 *
32 *    rsil a15, 1
33 *    <code>
34 *    wsr  a15, PS
35 *    rsync
36 *
37 * Note that a15 is used here because the register allocation
38 * done by the compiler is not guaranteed and a window overflow
39 * may not occur between the rsil and wsr instructions. By using
40 * a15 in the rsil, the machine is guaranteed to be in a state
41 * where no register reference will cause an overflow.
42 */
43
44/**
45 * atomic_read - read atomic variable
46 * @v: pointer of type atomic_t
47 *
48 * Atomically reads the value of @v.
49 */
50#define atomic_read(v)		((v)->counter)
51
52/**
53 * atomic_set - set atomic variable
54 * @v: pointer of type atomic_t
55 * @i: required value
56 *
57 * Atomically sets the value of @v to @i.
58 */
59#define atomic_set(v,i)		((v)->counter = (i))
60
61/**
62 * atomic_add - add integer to atomic variable
63 * @i: integer value to add
64 * @v: pointer of type atomic_t
65 *
66 * Atomically adds @i to @v.
67 */
68static inline void atomic_add(int i, atomic_t * v)
69{
70    unsigned int vval;
71
72    __asm__ __volatile__(
73	"rsil    a15, "__stringify(LOCKLEVEL)"\n\t"
74	"l32i    %0, %2, 0              \n\t"
75	"add     %0, %0, %1             \n\t"
76	"s32i    %0, %2, 0              \n\t"
77	"wsr     a15, "__stringify(PS)"       \n\t"
78	"rsync                          \n"
79	: "=&a" (vval)
80	: "a" (i), "a" (v)
81	: "a15", "memory"
82	);
83}
84
85/**
86 * atomic_sub - subtract the atomic variable
87 * @i: integer value to subtract
88 * @v: pointer of type atomic_t
89 *
90 * Atomically subtracts @i from @v.
91 */
92static inline void atomic_sub(int i, atomic_t *v)
93{
94    unsigned int vval;
95
96    __asm__ __volatile__(
97	"rsil    a15, "__stringify(LOCKLEVEL)"\n\t"
98	"l32i    %0, %2, 0              \n\t"
99	"sub     %0, %0, %1             \n\t"
100	"s32i    %0, %2, 0              \n\t"
101	"wsr     a15, "__stringify(PS)"       \n\t"
102	"rsync                          \n"
103	: "=&a" (vval)
104	: "a" (i), "a" (v)
105	: "a15", "memory"
106	);
107}
108
109/*
110 * We use atomic_{add|sub}_return to define other functions.
111 */
112
113static inline int atomic_add_return(int i, atomic_t * v)
114{
115     unsigned int vval;
116
117    __asm__ __volatile__(
118	"rsil    a15,"__stringify(LOCKLEVEL)"\n\t"
119	"l32i    %0, %2, 0             \n\t"
120	"add     %0, %0, %1            \n\t"
121	"s32i    %0, %2, 0             \n\t"
122	"wsr     a15, "__stringify(PS)"      \n\t"
123	"rsync                         \n"
124	: "=&a" (vval)
125	: "a" (i), "a" (v)
126	: "a15", "memory"
127	);
128
129    return vval;
130}
131
132static inline int atomic_sub_return(int i, atomic_t * v)
133{
134    unsigned int vval;
135
136    __asm__ __volatile__(
137	"rsil    a15,"__stringify(LOCKLEVEL)"\n\t"
138	"l32i    %0, %2, 0             \n\t"
139	"sub     %0, %0, %1            \n\t"
140	"s32i    %0, %2, 0             \n\t"
141	"wsr     a15, "__stringify(PS)"       \n\t"
142	"rsync                         \n"
143	: "=&a" (vval)
144	: "a" (i), "a" (v)
145	: "a15", "memory"
146	);
147
148    return vval;
149}
150
151/**
152 * atomic_sub_and_test - subtract value from variable and test result
153 * @i: integer value to subtract
154 * @v: pointer of type atomic_t
155 *
156 * Atomically subtracts @i from @v and returns
157 * true if the result is zero, or false for all
158 * other cases.
159 */
160#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0)
161
162/**
163 * atomic_inc - increment atomic variable
164 * @v: pointer of type atomic_t
165 *
166 * Atomically increments @v by 1.
167 */
168#define atomic_inc(v) atomic_add(1,(v))
169
170/**
171 * atomic_inc - increment atomic variable
172 * @v: pointer of type atomic_t
173 *
174 * Atomically increments @v by 1.
175 */
176#define atomic_inc_return(v) atomic_add_return(1,(v))
177
178/**
179 * atomic_dec - decrement atomic variable
180 * @v: pointer of type atomic_t
181 *
182 * Atomically decrements @v by 1.
183 */
184#define atomic_dec(v) atomic_sub(1,(v))
185
186/**
187 * atomic_dec_return - decrement atomic variable
188 * @v: pointer of type atomic_t
189 *
190 * Atomically decrements @v by 1.
191 */
192#define atomic_dec_return(v) atomic_sub_return(1,(v))
193
194/**
195 * atomic_dec_and_test - decrement and test
196 * @v: pointer of type atomic_t
197 *
198 * Atomically decrements @v by 1 and
199 * returns true if the result is 0, or false for all other
200 * cases.
201 */
202#define atomic_dec_and_test(v) (atomic_sub_return(1,(v)) == 0)
203
204/**
205 * atomic_inc_and_test - increment and test
206 * @v: pointer of type atomic_t
207 *
208 * Atomically increments @v by 1
209 * and returns true if the result is zero, or false for all
210 * other cases.
211 */
212#define atomic_inc_and_test(v) (atomic_add_return(1,(v)) == 0)
213
214/**
215 * atomic_add_negative - add and test if negative
216 * @v: pointer of type atomic_t
217 * @i: integer value to add
218 *
219 * Atomically adds @i to @v and returns true
220 * if the result is negative, or false when
221 * result is greater than or equal to zero.
222 */
223#define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0)
224
225#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
226#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
227
228/**
229 * atomic_add_unless - add unless the number is a given value
230 * @v: pointer of type atomic_t
231 * @a: the amount to add to v...
232 * @u: ...unless v is equal to u.
233 *
234 * Atomically adds @a to @v, so long as it was not @u.
235 * Returns non-zero if @v was not @u, and zero otherwise.
236 */
237static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
238{
239	int c, old;
240	c = atomic_read(v);
241	for (;;) {
242		if (unlikely(c == (u)))
243			break;
244		old = atomic_cmpxchg((v), c, c + (a));
245		if (likely(old == c))
246			break;
247		c = old;
248	}
249	return c != (u);
250}
251
252#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
253
254static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
255{
256    unsigned int all_f = -1;
257    unsigned int vval;
258
259    __asm__ __volatile__(
260	"rsil    a15,"__stringify(LOCKLEVEL)"\n\t"
261	"l32i    %0, %2, 0             \n\t"
262	"xor     %1, %4, %3            \n\t"
263	"and     %0, %0, %4            \n\t"
264	"s32i    %0, %2, 0             \n\t"
265	"wsr     a15, "__stringify(PS)"      \n\t"
266	"rsync                         \n"
267	: "=&a" (vval), "=a" (mask)
268	: "a" (v), "a" (all_f), "1" (mask)
269	: "a15", "memory"
270	);
271}
272
273static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
274{
275    unsigned int vval;
276
277    __asm__ __volatile__(
278	"rsil    a15,"__stringify(LOCKLEVEL)"\n\t"
279	"l32i    %0, %2, 0             \n\t"
280	"or      %0, %0, %1            \n\t"
281	"s32i    %0, %2, 0             \n\t"
282	"wsr     a15, "__stringify(PS)"       \n\t"
283	"rsync                         \n"
284	: "=&a" (vval)
285	: "a" (mask), "a" (v)
286	: "a15", "memory"
287	);
288}
289
290/* Atomic operations are already serializing */
291#define smp_mb__before_atomic_dec()	barrier()
292#define smp_mb__after_atomic_dec()	barrier()
293#define smp_mb__before_atomic_inc()	barrier()
294#define smp_mb__after_atomic_inc()	barrier()
295
296#include <asm-generic/atomic.h>
297#endif /* __KERNEL__ */
298
299#endif /* _XTENSA_ATOMIC_H */
300