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