xref: /openbmc/linux/arch/xtensa/include/asm/atomic.h (revision 7b73a9c8)
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 - 2008 Tensilica Inc.
11  */
12 
13 #ifndef _XTENSA_ATOMIC_H
14 #define _XTENSA_ATOMIC_H
15 
16 #include <linux/stringify.h>
17 #include <linux/types.h>
18 #include <asm/processor.h>
19 #include <asm/cmpxchg.h>
20 #include <asm/barrier.h>
21 
22 #define ATOMIC_INIT(i)	{ (i) }
23 
24 /*
25  * This Xtensa implementation assumes that the right mechanism
26  * for exclusion is for locking interrupts to level EXCM_LEVEL.
27  *
28  * Locking interrupts looks like this:
29  *
30  *    rsil a15, TOPLEVEL
31  *    <code>
32  *    wsr  a15, PS
33  *    rsync
34  *
35  * Note that a15 is used here because the register allocation
36  * done by the compiler is not guaranteed and a window overflow
37  * may not occur between the rsil and wsr instructions. By using
38  * a15 in the rsil, the machine is guaranteed to be in a state
39  * where no register reference will cause an overflow.
40  */
41 
42 /**
43  * atomic_read - read atomic variable
44  * @v: pointer of type atomic_t
45  *
46  * Atomically reads the value of @v.
47  */
48 #define atomic_read(v)		READ_ONCE((v)->counter)
49 
50 /**
51  * atomic_set - set atomic variable
52  * @v: pointer of type atomic_t
53  * @i: required value
54  *
55  * Atomically sets the value of @v to @i.
56  */
57 #define atomic_set(v,i)		WRITE_ONCE((v)->counter, (i))
58 
59 #if XCHAL_HAVE_EXCLUSIVE
60 #define ATOMIC_OP(op)							\
61 static inline void atomic_##op(int i, atomic_t *v)			\
62 {									\
63 	unsigned long tmp;						\
64 	int result;							\
65 									\
66 	__asm__ __volatile__(						\
67 			"1:     l32ex   %[tmp], %[addr]\n"		\
68 			"       " #op " %[result], %[tmp], %[i]\n"	\
69 			"       s32ex   %[result], %[addr]\n"		\
70 			"       getex   %[result]\n"			\
71 			"       beqz    %[result], 1b\n"		\
72 			: [result] "=&a" (result), [tmp] "=&a" (tmp)	\
73 			: [i] "a" (i), [addr] "a" (v)			\
74 			: "memory"					\
75 			);						\
76 }									\
77 
78 #define ATOMIC_OP_RETURN(op)						\
79 static inline int atomic_##op##_return(int i, atomic_t *v)		\
80 {									\
81 	unsigned long tmp;						\
82 	int result;							\
83 									\
84 	__asm__ __volatile__(						\
85 			"1:     l32ex   %[tmp], %[addr]\n"		\
86 			"       " #op " %[result], %[tmp], %[i]\n"	\
87 			"       s32ex   %[result], %[addr]\n"		\
88 			"       getex   %[result]\n"			\
89 			"       beqz    %[result], 1b\n"		\
90 			"       " #op " %[result], %[tmp], %[i]\n"	\
91 			: [result] "=&a" (result), [tmp] "=&a" (tmp)	\
92 			: [i] "a" (i), [addr] "a" (v)			\
93 			: "memory"					\
94 			);						\
95 									\
96 	return result;							\
97 }
98 
99 #define ATOMIC_FETCH_OP(op)						\
100 static inline int atomic_fetch_##op(int i, atomic_t *v)			\
101 {									\
102 	unsigned long tmp;						\
103 	int result;							\
104 									\
105 	__asm__ __volatile__(						\
106 			"1:     l32ex   %[tmp], %[addr]\n"		\
107 			"       " #op " %[result], %[tmp], %[i]\n"	\
108 			"       s32ex   %[result], %[addr]\n"		\
109 			"       getex   %[result]\n"			\
110 			"       beqz    %[result], 1b\n"		\
111 			: [result] "=&a" (result), [tmp] "=&a" (tmp)	\
112 			: [i] "a" (i), [addr] "a" (v)			\
113 			: "memory"					\
114 			);						\
115 									\
116 	return tmp;							\
117 }
118 
119 #elif XCHAL_HAVE_S32C1I
120 #define ATOMIC_OP(op)							\
121 static inline void atomic_##op(int i, atomic_t * v)			\
122 {									\
123 	unsigned long tmp;						\
124 	int result;							\
125 									\
126 	__asm__ __volatile__(						\
127 			"1:     l32i    %[tmp], %[mem]\n"		\
128 			"       wsr     %[tmp], scompare1\n"		\
129 			"       " #op " %[result], %[tmp], %[i]\n"	\
130 			"       s32c1i  %[result], %[mem]\n"		\
131 			"       bne     %[result], %[tmp], 1b\n"	\
132 			: [result] "=&a" (result), [tmp] "=&a" (tmp),	\
133 			  [mem] "+m" (*v)				\
134 			: [i] "a" (i)					\
135 			: "memory"					\
136 			);						\
137 }									\
138 
139 #define ATOMIC_OP_RETURN(op)						\
140 static inline int atomic_##op##_return(int i, atomic_t * v)		\
141 {									\
142 	unsigned long tmp;						\
143 	int result;							\
144 									\
145 	__asm__ __volatile__(						\
146 			"1:     l32i    %[tmp], %[mem]\n"		\
147 			"       wsr     %[tmp], scompare1\n"		\
148 			"       " #op " %[result], %[tmp], %[i]\n"	\
149 			"       s32c1i  %[result], %[mem]\n"		\
150 			"       bne     %[result], %[tmp], 1b\n"	\
151 			"       " #op " %[result], %[result], %[i]\n"	\
152 			: [result] "=&a" (result), [tmp] "=&a" (tmp),	\
153 			  [mem] "+m" (*v)				\
154 			: [i] "a" (i)					\
155 			: "memory"					\
156 			);						\
157 									\
158 	return result;							\
159 }
160 
161 #define ATOMIC_FETCH_OP(op)						\
162 static inline int atomic_fetch_##op(int i, atomic_t * v)		\
163 {									\
164 	unsigned long tmp;						\
165 	int result;							\
166 									\
167 	__asm__ __volatile__(						\
168 			"1:     l32i    %[tmp], %[mem]\n"		\
169 			"       wsr     %[tmp], scompare1\n"		\
170 			"       " #op " %[result], %[tmp], %[i]\n"	\
171 			"       s32c1i  %[result], %[mem]\n"		\
172 			"       bne     %[result], %[tmp], 1b\n"	\
173 			: [result] "=&a" (result), [tmp] "=&a" (tmp),	\
174 			  [mem] "+m" (*v)				\
175 			: [i] "a" (i)					\
176 			: "memory"					\
177 			);						\
178 									\
179 	return result;							\
180 }
181 
182 #else /* XCHAL_HAVE_S32C1I */
183 
184 #define ATOMIC_OP(op)							\
185 static inline void atomic_##op(int i, atomic_t * v)			\
186 {									\
187 	unsigned int vval;						\
188 									\
189 	__asm__ __volatile__(						\
190 			"       rsil    a15, "__stringify(TOPLEVEL)"\n"	\
191 			"       l32i    %[result], %[mem]\n"		\
192 			"       " #op " %[result], %[result], %[i]\n"	\
193 			"       s32i    %[result], %[mem]\n"		\
194 			"       wsr     a15, ps\n"			\
195 			"       rsync\n"				\
196 			: [result] "=&a" (vval), [mem] "+m" (*v)	\
197 			: [i] "a" (i)					\
198 			: "a15", "memory"				\
199 			);						\
200 }									\
201 
202 #define ATOMIC_OP_RETURN(op)						\
203 static inline int atomic_##op##_return(int i, atomic_t * v)		\
204 {									\
205 	unsigned int vval;						\
206 									\
207 	__asm__ __volatile__(						\
208 			"       rsil    a15,"__stringify(TOPLEVEL)"\n"	\
209 			"       l32i    %[result], %[mem]\n"		\
210 			"       " #op " %[result], %[result], %[i]\n"	\
211 			"       s32i    %[result], %[mem]\n"		\
212 			"       wsr     a15, ps\n"			\
213 			"       rsync\n"				\
214 			: [result] "=&a" (vval), [mem] "+m" (*v)	\
215 			: [i] "a" (i)					\
216 			: "a15", "memory"				\
217 			);						\
218 									\
219 	return vval;							\
220 }
221 
222 #define ATOMIC_FETCH_OP(op)						\
223 static inline int atomic_fetch_##op(int i, atomic_t * v)		\
224 {									\
225 	unsigned int tmp, vval;						\
226 									\
227 	__asm__ __volatile__(						\
228 			"       rsil    a15,"__stringify(TOPLEVEL)"\n"	\
229 			"       l32i    %[result], %[mem]\n"		\
230 			"       " #op " %[tmp], %[result], %[i]\n"	\
231 			"       s32i    %[tmp], %[mem]\n"		\
232 			"       wsr     a15, ps\n"			\
233 			"       rsync\n"				\
234 			: [result] "=&a" (vval), [tmp] "=&a" (tmp),	\
235 			  [mem] "+m" (*v)				\
236 			: [i] "a" (i)					\
237 			: "a15", "memory"				\
238 			);						\
239 									\
240 	return vval;							\
241 }
242 
243 #endif /* XCHAL_HAVE_S32C1I */
244 
245 #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op) ATOMIC_OP_RETURN(op)
246 
247 ATOMIC_OPS(add)
248 ATOMIC_OPS(sub)
249 
250 #undef ATOMIC_OPS
251 #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op)
252 
253 ATOMIC_OPS(and)
254 ATOMIC_OPS(or)
255 ATOMIC_OPS(xor)
256 
257 #undef ATOMIC_OPS
258 #undef ATOMIC_FETCH_OP
259 #undef ATOMIC_OP_RETURN
260 #undef ATOMIC_OP
261 
262 #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
263 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
264 
265 #endif /* _XTENSA_ATOMIC_H */
266