xref: /openbmc/linux/arch/xtensa/include/asm/atomic.h (revision d4608dd5)
1367b8112SChris Zankel /*
2367b8112SChris Zankel  * include/asm-xtensa/atomic.h
3367b8112SChris Zankel  *
4367b8112SChris Zankel  * Atomic operations that C can't guarantee us.  Useful for resource counting..
5367b8112SChris Zankel  *
6367b8112SChris Zankel  * This file is subject to the terms and conditions of the GNU General Public
7367b8112SChris Zankel  * License.  See the file "COPYING" in the main directory of this archive
8367b8112SChris Zankel  * for more details.
9367b8112SChris Zankel  *
102d1c645cSMarc Gauthier  * Copyright (C) 2001 - 2008 Tensilica Inc.
11367b8112SChris Zankel  */
12367b8112SChris Zankel 
13367b8112SChris Zankel #ifndef _XTENSA_ATOMIC_H
14367b8112SChris Zankel #define _XTENSA_ATOMIC_H
15367b8112SChris Zankel 
16367b8112SChris Zankel #include <linux/stringify.h>
1752fefcecSLinus Torvalds #include <linux/types.h>
18367b8112SChris Zankel 
19367b8112SChris Zankel #ifdef __KERNEL__
20367b8112SChris Zankel #include <asm/processor.h>
21f9aa7e18SDavid Howells #include <asm/cmpxchg.h>
2209a01c0cSPeter Zijlstra #include <asm/barrier.h>
23367b8112SChris Zankel 
24367b8112SChris Zankel #define ATOMIC_INIT(i)	{ (i) }
25367b8112SChris Zankel 
26367b8112SChris Zankel /*
27367b8112SChris Zankel  * This Xtensa implementation assumes that the right mechanism
282d1c645cSMarc Gauthier  * for exclusion is for locking interrupts to level EXCM_LEVEL.
29367b8112SChris Zankel  *
30367b8112SChris Zankel  * Locking interrupts looks like this:
31367b8112SChris Zankel  *
322d1c645cSMarc Gauthier  *    rsil a15, LOCKLEVEL
33367b8112SChris Zankel  *    <code>
34367b8112SChris Zankel  *    wsr  a15, PS
35367b8112SChris Zankel  *    rsync
36367b8112SChris Zankel  *
37367b8112SChris Zankel  * Note that a15 is used here because the register allocation
38367b8112SChris Zankel  * done by the compiler is not guaranteed and a window overflow
39367b8112SChris Zankel  * may not occur between the rsil and wsr instructions. By using
40367b8112SChris Zankel  * a15 in the rsil, the machine is guaranteed to be in a state
41367b8112SChris Zankel  * where no register reference will cause an overflow.
42367b8112SChris Zankel  */
43367b8112SChris Zankel 
44367b8112SChris Zankel /**
45367b8112SChris Zankel  * atomic_read - read atomic variable
46367b8112SChris Zankel  * @v: pointer of type atomic_t
47367b8112SChris Zankel  *
48367b8112SChris Zankel  * Atomically reads the value of @v.
49367b8112SChris Zankel  */
50f3d46f9dSAnton Blanchard #define atomic_read(v)		(*(volatile int *)&(v)->counter)
51367b8112SChris Zankel 
52367b8112SChris Zankel /**
53367b8112SChris Zankel  * atomic_set - set atomic variable
54367b8112SChris Zankel  * @v: pointer of type atomic_t
55367b8112SChris Zankel  * @i: required value
56367b8112SChris Zankel  *
57367b8112SChris Zankel  * Atomically sets the value of @v to @i.
58367b8112SChris Zankel  */
59367b8112SChris Zankel #define atomic_set(v,i)		((v)->counter = (i))
60367b8112SChris Zankel 
61219b1e4cSMax Filippov #if XCHAL_HAVE_S32C1I
62d4608dd5SPeter Zijlstra #define ATOMIC_OP(op)							\
63d4608dd5SPeter Zijlstra static inline void atomic_##op(int i, atomic_t * v)			\
64d4608dd5SPeter Zijlstra {									\
65d4608dd5SPeter Zijlstra 	unsigned long tmp;						\
66d4608dd5SPeter Zijlstra 	int result;							\
67d4608dd5SPeter Zijlstra 									\
68d4608dd5SPeter Zijlstra 	__asm__ __volatile__(						\
69d4608dd5SPeter Zijlstra 			"1:     l32i    %1, %3, 0\n"			\
70d4608dd5SPeter Zijlstra 			"       wsr     %1, scompare1\n"		\
71d4608dd5SPeter Zijlstra 			"       " #op " %0, %1, %2\n"			\
72d4608dd5SPeter Zijlstra 			"       s32c1i  %0, %3, 0\n"			\
73d4608dd5SPeter Zijlstra 			"       bne     %0, %1, 1b\n"			\
74d4608dd5SPeter Zijlstra 			: "=&a" (result), "=&a" (tmp)			\
75d4608dd5SPeter Zijlstra 			: "a" (i), "a" (v)				\
76d4608dd5SPeter Zijlstra 			: "memory"					\
77d4608dd5SPeter Zijlstra 			);						\
78d4608dd5SPeter Zijlstra }									\
79219b1e4cSMax Filippov 
80d4608dd5SPeter Zijlstra #define ATOMIC_OP_RETURN(op)						\
81d4608dd5SPeter Zijlstra static inline int atomic_##op##_return(int i, atomic_t * v)		\
82d4608dd5SPeter Zijlstra {									\
83d4608dd5SPeter Zijlstra 	unsigned long tmp;						\
84d4608dd5SPeter Zijlstra 	int result;							\
85d4608dd5SPeter Zijlstra 									\
86d4608dd5SPeter Zijlstra 	__asm__ __volatile__(						\
87d4608dd5SPeter Zijlstra 			"1:     l32i    %1, %3, 0\n"			\
88d4608dd5SPeter Zijlstra 			"       wsr     %1, scompare1\n"		\
89d4608dd5SPeter Zijlstra 			"       " #op " %0, %1, %2\n"			\
90d4608dd5SPeter Zijlstra 			"       s32c1i  %0, %3, 0\n"			\
91d4608dd5SPeter Zijlstra 			"       bne     %0, %1, 1b\n"			\
92d4608dd5SPeter Zijlstra 			"       " #op " %0, %0, %2\n"			\
93d4608dd5SPeter Zijlstra 			: "=&a" (result), "=&a" (tmp)			\
94d4608dd5SPeter Zijlstra 			: "a" (i), "a" (v)				\
95d4608dd5SPeter Zijlstra 			: "memory"					\
96d4608dd5SPeter Zijlstra 			);						\
97d4608dd5SPeter Zijlstra 									\
98d4608dd5SPeter Zijlstra 	return result;							\
99367b8112SChris Zankel }
100367b8112SChris Zankel 
101d4608dd5SPeter Zijlstra #else /* XCHAL_HAVE_S32C1I */
102219b1e4cSMax Filippov 
103d4608dd5SPeter Zijlstra #define ATOMIC_OP(op)							\
104d4608dd5SPeter Zijlstra static inline void atomic_##op(int i, atomic_t * v)			\
105d4608dd5SPeter Zijlstra {									\
106d4608dd5SPeter Zijlstra 	unsigned int vval;						\
107d4608dd5SPeter Zijlstra 									\
108d4608dd5SPeter Zijlstra 	__asm__ __volatile__(						\
109d4608dd5SPeter Zijlstra 			"       rsil    a15, "__stringify(LOCKLEVEL)"\n"\
110d4608dd5SPeter Zijlstra 			"       l32i    %0, %2, 0\n"			\
111d4608dd5SPeter Zijlstra 			"       " #op " %0, %0, %1\n"			\
112d4608dd5SPeter Zijlstra 			"       s32i    %0, %2, 0\n"			\
113d4608dd5SPeter Zijlstra 			"       wsr     a15, ps\n"			\
114d4608dd5SPeter Zijlstra 			"       rsync\n"				\
115d4608dd5SPeter Zijlstra 			: "=&a" (vval)					\
116d4608dd5SPeter Zijlstra 			: "a" (i), "a" (v)				\
117d4608dd5SPeter Zijlstra 			: "a15", "memory"				\
118d4608dd5SPeter Zijlstra 			);						\
119d4608dd5SPeter Zijlstra }									\
120367b8112SChris Zankel 
121d4608dd5SPeter Zijlstra #define ATOMIC_OP_RETURN(op)						\
122d4608dd5SPeter Zijlstra static inline int atomic_##op##_return(int i, atomic_t * v)		\
123d4608dd5SPeter Zijlstra {									\
124d4608dd5SPeter Zijlstra 	unsigned int vval;						\
125d4608dd5SPeter Zijlstra 									\
126d4608dd5SPeter Zijlstra 	__asm__ __volatile__(						\
127d4608dd5SPeter Zijlstra 			"       rsil    a15,"__stringify(LOCKLEVEL)"\n"	\
128d4608dd5SPeter Zijlstra 			"       l32i    %0, %2, 0\n"			\
129d4608dd5SPeter Zijlstra 			"       " #op " %0, %0, %1\n"			\
130d4608dd5SPeter Zijlstra 			"       s32i    %0, %2, 0\n"			\
131d4608dd5SPeter Zijlstra 			"       wsr     a15, ps\n"			\
132d4608dd5SPeter Zijlstra 			"       rsync\n"				\
133d4608dd5SPeter Zijlstra 			: "=&a" (vval)					\
134d4608dd5SPeter Zijlstra 			: "a" (i), "a" (v)				\
135d4608dd5SPeter Zijlstra 			: "a15", "memory"				\
136d4608dd5SPeter Zijlstra 			);						\
137d4608dd5SPeter Zijlstra 									\
138d4608dd5SPeter Zijlstra 	return vval;							\
139367b8112SChris Zankel }
140367b8112SChris Zankel 
141d4608dd5SPeter Zijlstra #endif /* XCHAL_HAVE_S32C1I */
142367b8112SChris Zankel 
143d4608dd5SPeter Zijlstra #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op)
144219b1e4cSMax Filippov 
145d4608dd5SPeter Zijlstra ATOMIC_OPS(add)
146d4608dd5SPeter Zijlstra ATOMIC_OPS(sub)
147219b1e4cSMax Filippov 
148d4608dd5SPeter Zijlstra #undef ATOMIC_OPS
149d4608dd5SPeter Zijlstra #undef ATOMIC_OP_RETURN
150d4608dd5SPeter Zijlstra #undef ATOMIC_OP
151367b8112SChris Zankel 
152367b8112SChris Zankel /**
153367b8112SChris Zankel  * atomic_sub_and_test - subtract value from variable and test result
154367b8112SChris Zankel  * @i: integer value to subtract
155367b8112SChris Zankel  * @v: pointer of type atomic_t
156367b8112SChris Zankel  *
157367b8112SChris Zankel  * Atomically subtracts @i from @v and returns
158367b8112SChris Zankel  * true if the result is zero, or false for all
159367b8112SChris Zankel  * other cases.
160367b8112SChris Zankel  */
161367b8112SChris Zankel #define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0)
162367b8112SChris Zankel 
163367b8112SChris Zankel /**
164367b8112SChris Zankel  * atomic_inc - increment atomic variable
165367b8112SChris Zankel  * @v: pointer of type atomic_t
166367b8112SChris Zankel  *
167367b8112SChris Zankel  * Atomically increments @v by 1.
168367b8112SChris Zankel  */
169367b8112SChris Zankel #define atomic_inc(v) atomic_add(1,(v))
170367b8112SChris Zankel 
171367b8112SChris Zankel /**
172367b8112SChris Zankel  * atomic_inc - increment atomic variable
173367b8112SChris Zankel  * @v: pointer of type atomic_t
174367b8112SChris Zankel  *
175367b8112SChris Zankel  * Atomically increments @v by 1.
176367b8112SChris Zankel  */
177367b8112SChris Zankel #define atomic_inc_return(v) atomic_add_return(1,(v))
178367b8112SChris Zankel 
179367b8112SChris Zankel /**
180367b8112SChris Zankel  * atomic_dec - decrement atomic variable
181367b8112SChris Zankel  * @v: pointer of type atomic_t
182367b8112SChris Zankel  *
183367b8112SChris Zankel  * Atomically decrements @v by 1.
184367b8112SChris Zankel  */
185367b8112SChris Zankel #define atomic_dec(v) atomic_sub(1,(v))
186367b8112SChris Zankel 
187367b8112SChris Zankel /**
188367b8112SChris Zankel  * atomic_dec_return - decrement atomic variable
189367b8112SChris Zankel  * @v: pointer of type atomic_t
190367b8112SChris Zankel  *
191367b8112SChris Zankel  * Atomically decrements @v by 1.
192367b8112SChris Zankel  */
193367b8112SChris Zankel #define atomic_dec_return(v) atomic_sub_return(1,(v))
194367b8112SChris Zankel 
195367b8112SChris Zankel /**
196367b8112SChris Zankel  * atomic_dec_and_test - decrement and test
197367b8112SChris Zankel  * @v: pointer of type atomic_t
198367b8112SChris Zankel  *
199367b8112SChris Zankel  * Atomically decrements @v by 1 and
200367b8112SChris Zankel  * returns true if the result is 0, or false for all other
201367b8112SChris Zankel  * cases.
202367b8112SChris Zankel  */
203367b8112SChris Zankel #define atomic_dec_and_test(v) (atomic_sub_return(1,(v)) == 0)
204367b8112SChris Zankel 
205367b8112SChris Zankel /**
206367b8112SChris Zankel  * atomic_inc_and_test - increment and test
207367b8112SChris Zankel  * @v: pointer of type atomic_t
208367b8112SChris Zankel  *
209367b8112SChris Zankel  * Atomically increments @v by 1
210367b8112SChris Zankel  * and returns true if the result is zero, or false for all
211367b8112SChris Zankel  * other cases.
212367b8112SChris Zankel  */
213367b8112SChris Zankel #define atomic_inc_and_test(v) (atomic_add_return(1,(v)) == 0)
214367b8112SChris Zankel 
215367b8112SChris Zankel /**
216367b8112SChris Zankel  * atomic_add_negative - add and test if negative
217367b8112SChris Zankel  * @v: pointer of type atomic_t
218367b8112SChris Zankel  * @i: integer value to add
219367b8112SChris Zankel  *
220367b8112SChris Zankel  * Atomically adds @i to @v and returns true
221367b8112SChris Zankel  * if the result is negative, or false when
222367b8112SChris Zankel  * result is greater than or equal to zero.
223367b8112SChris Zankel  */
224367b8112SChris Zankel #define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0)
225367b8112SChris Zankel 
226367b8112SChris Zankel #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
227367b8112SChris Zankel #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
228367b8112SChris Zankel 
229367b8112SChris Zankel /**
230f24219b4SArun Sharma  * __atomic_add_unless - add unless the number is a given value
231367b8112SChris Zankel  * @v: pointer of type atomic_t
232367b8112SChris Zankel  * @a: the amount to add to v...
233367b8112SChris Zankel  * @u: ...unless v is equal to u.
234367b8112SChris Zankel  *
235367b8112SChris Zankel  * Atomically adds @a to @v, so long as it was not @u.
236f24219b4SArun Sharma  * Returns the old value of @v.
237367b8112SChris Zankel  */
238f24219b4SArun Sharma static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
239367b8112SChris Zankel {
240367b8112SChris Zankel 	int c, old;
241367b8112SChris Zankel 	c = atomic_read(v);
242367b8112SChris Zankel 	for (;;) {
243367b8112SChris Zankel 		if (unlikely(c == (u)))
244367b8112SChris Zankel 			break;
245367b8112SChris Zankel 		old = atomic_cmpxchg((v), c, c + (a));
246367b8112SChris Zankel 		if (likely(old == c))
247367b8112SChris Zankel 			break;
248367b8112SChris Zankel 		c = old;
249367b8112SChris Zankel 	}
250f24219b4SArun Sharma 	return c;
251367b8112SChris Zankel }
252367b8112SChris Zankel 
253367b8112SChris Zankel 
254367b8112SChris Zankel static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
255367b8112SChris Zankel {
256219b1e4cSMax Filippov #if XCHAL_HAVE_S32C1I
257219b1e4cSMax Filippov 	unsigned long tmp;
258219b1e4cSMax Filippov 	int result;
259219b1e4cSMax Filippov 
260219b1e4cSMax Filippov 	__asm__ __volatile__(
261219b1e4cSMax Filippov 			"1:     l32i    %1, %3, 0\n"
262219b1e4cSMax Filippov 			"       wsr     %1, scompare1\n"
263219b1e4cSMax Filippov 			"       and     %0, %1, %2\n"
264219b1e4cSMax Filippov 			"       s32c1i  %0, %3, 0\n"
265219b1e4cSMax Filippov 			"       bne     %0, %1, 1b\n"
266219b1e4cSMax Filippov 			: "=&a" (result), "=&a" (tmp)
267219b1e4cSMax Filippov 			: "a" (~mask), "a" (v)
268219b1e4cSMax Filippov 			: "memory"
269219b1e4cSMax Filippov 			);
270219b1e4cSMax Filippov #else
271367b8112SChris Zankel 	unsigned int all_f = -1;
272367b8112SChris Zankel 	unsigned int vval;
273367b8112SChris Zankel 
274367b8112SChris Zankel 	__asm__ __volatile__(
275219b1e4cSMax Filippov 			"       rsil    a15,"__stringify(LOCKLEVEL)"\n"
276219b1e4cSMax Filippov 			"       l32i    %0, %2, 0\n"
277219b1e4cSMax Filippov 			"       xor     %1, %4, %3\n"
278219b1e4cSMax Filippov 			"       and     %0, %0, %4\n"
279219b1e4cSMax Filippov 			"       s32i    %0, %2, 0\n"
280219b1e4cSMax Filippov 			"       wsr     a15, ps\n"
281367b8112SChris Zankel 			"       rsync\n"
282367b8112SChris Zankel 			: "=&a" (vval), "=a" (mask)
283367b8112SChris Zankel 			: "a" (v), "a" (all_f), "1" (mask)
284367b8112SChris Zankel 			: "a15", "memory"
285367b8112SChris Zankel 			);
286219b1e4cSMax Filippov #endif
287367b8112SChris Zankel }
288367b8112SChris Zankel 
289367b8112SChris Zankel static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
290367b8112SChris Zankel {
291219b1e4cSMax Filippov #if XCHAL_HAVE_S32C1I
292219b1e4cSMax Filippov 	unsigned long tmp;
293219b1e4cSMax Filippov 	int result;
294219b1e4cSMax Filippov 
295219b1e4cSMax Filippov 	__asm__ __volatile__(
296219b1e4cSMax Filippov 			"1:     l32i    %1, %3, 0\n"
297219b1e4cSMax Filippov 			"       wsr     %1, scompare1\n"
298219b1e4cSMax Filippov 			"       or      %0, %1, %2\n"
299219b1e4cSMax Filippov 			"       s32c1i  %0, %3, 0\n"
300219b1e4cSMax Filippov 			"       bne     %0, %1, 1b\n"
301219b1e4cSMax Filippov 			: "=&a" (result), "=&a" (tmp)
302219b1e4cSMax Filippov 			: "a" (mask), "a" (v)
303219b1e4cSMax Filippov 			: "memory"
304219b1e4cSMax Filippov 			);
305219b1e4cSMax Filippov #else
306367b8112SChris Zankel 	unsigned int vval;
307367b8112SChris Zankel 
308367b8112SChris Zankel 	__asm__ __volatile__(
309219b1e4cSMax Filippov 			"       rsil    a15,"__stringify(LOCKLEVEL)"\n"
310219b1e4cSMax Filippov 			"       l32i    %0, %2, 0\n"
311219b1e4cSMax Filippov 			"       or      %0, %0, %1\n"
312219b1e4cSMax Filippov 			"       s32i    %0, %2, 0\n"
313219b1e4cSMax Filippov 			"       wsr     a15, ps\n"
314367b8112SChris Zankel 			"       rsync\n"
315367b8112SChris Zankel 			: "=&a" (vval)
316367b8112SChris Zankel 			: "a" (mask), "a" (v)
317367b8112SChris Zankel 			: "a15", "memory"
318367b8112SChris Zankel 			);
319219b1e4cSMax Filippov #endif
320367b8112SChris Zankel }
321367b8112SChris Zankel 
322367b8112SChris Zankel #endif /* __KERNEL__ */
323367b8112SChris Zankel 
324367b8112SChris Zankel #endif /* _XTENSA_ATOMIC_H */
325