xref: /openbmc/linux/arch/arc/include/asm/atomic.h (revision 0edbfea5)
1 /*
2  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 #ifndef _ASM_ARC_ATOMIC_H
10 #define _ASM_ARC_ATOMIC_H
11 
12 #ifndef __ASSEMBLY__
13 
14 #include <linux/types.h>
15 #include <linux/compiler.h>
16 #include <asm/cmpxchg.h>
17 #include <asm/barrier.h>
18 #include <asm/smp.h>
19 
20 #ifndef CONFIG_ARC_PLAT_EZNPS
21 
22 #define atomic_read(v)  READ_ONCE((v)->counter)
23 
24 #ifdef CONFIG_ARC_HAS_LLSC
25 
26 #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
27 
28 #ifdef CONFIG_ARC_STAR_9000923308
29 
30 #define SCOND_FAIL_RETRY_VAR_DEF						\
31 	unsigned int delay = 1, tmp;						\
32 
33 #define SCOND_FAIL_RETRY_ASM							\
34 	"	bz	4f			\n"				\
35 	"   ; --- scond fail delay ---		\n"				\
36 	"	mov	%[tmp], %[delay]	\n"	/* tmp = delay */	\
37 	"2: 	brne.d	%[tmp], 0, 2b		\n"	/* while (tmp != 0) */	\
38 	"	sub	%[tmp], %[tmp], 1	\n"	/* tmp-- */		\
39 	"	rol	%[delay], %[delay]	\n"	/* delay *= 2 */	\
40 	"	b	1b			\n"	/* start over */	\
41 	"4: ; --- success ---			\n"				\
42 
43 #define SCOND_FAIL_RETRY_VARS							\
44 	  ,[delay] "+&r" (delay),[tmp] "=&r"	(tmp)				\
45 
46 #else	/* !CONFIG_ARC_STAR_9000923308 */
47 
48 #define SCOND_FAIL_RETRY_VAR_DEF
49 
50 #define SCOND_FAIL_RETRY_ASM							\
51 	"	bnz     1b			\n"				\
52 
53 #define SCOND_FAIL_RETRY_VARS
54 
55 #endif
56 
57 #define ATOMIC_OP(op, c_op, asm_op)					\
58 static inline void atomic_##op(int i, atomic_t *v)			\
59 {									\
60 	unsigned int val;				                \
61 	SCOND_FAIL_RETRY_VAR_DEF                                        \
62 									\
63 	__asm__ __volatile__(						\
64 	"1:	llock   %[val], [%[ctr]]		\n"		\
65 	"	" #asm_op " %[val], %[val], %[i]	\n"		\
66 	"	scond   %[val], [%[ctr]]		\n"		\
67 	"						\n"		\
68 	SCOND_FAIL_RETRY_ASM						\
69 									\
70 	: [val]	"=&r"	(val) /* Early clobber to prevent reg reuse */	\
71 	  SCOND_FAIL_RETRY_VARS						\
72 	: [ctr]	"r"	(&v->counter), /* Not "m": llock only supports reg direct addr mode */	\
73 	  [i]	"ir"	(i)						\
74 	: "cc");							\
75 }									\
76 
77 #define ATOMIC_OP_RETURN(op, c_op, asm_op)				\
78 static inline int atomic_##op##_return(int i, atomic_t *v)		\
79 {									\
80 	unsigned int val;				                \
81 	SCOND_FAIL_RETRY_VAR_DEF                                        \
82 									\
83 	/*								\
84 	 * Explicit full memory barrier needed before/after as		\
85 	 * LLOCK/SCOND thmeselves don't provide any such semantics	\
86 	 */								\
87 	smp_mb();							\
88 									\
89 	__asm__ __volatile__(						\
90 	"1:	llock   %[val], [%[ctr]]		\n"		\
91 	"	" #asm_op " %[val], %[val], %[i]	\n"		\
92 	"	scond   %[val], [%[ctr]]		\n"		\
93 	"						\n"		\
94 	SCOND_FAIL_RETRY_ASM						\
95 									\
96 	: [val]	"=&r"	(val)						\
97 	  SCOND_FAIL_RETRY_VARS						\
98 	: [ctr]	"r"	(&v->counter),					\
99 	  [i]	"ir"	(i)						\
100 	: "cc");							\
101 									\
102 	smp_mb();							\
103 									\
104 	return val;							\
105 }
106 
107 #else	/* !CONFIG_ARC_HAS_LLSC */
108 
109 #ifndef CONFIG_SMP
110 
111  /* violating atomic_xxx API locking protocol in UP for optimization sake */
112 #define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))
113 
114 #else
115 
116 static inline void atomic_set(atomic_t *v, int i)
117 {
118 	/*
119 	 * Independent of hardware support, all of the atomic_xxx() APIs need
120 	 * to follow the same locking rules to make sure that a "hardware"
121 	 * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
122 	 * sequence
123 	 *
124 	 * Thus atomic_set() despite being 1 insn (and seemingly atomic)
125 	 * requires the locking.
126 	 */
127 	unsigned long flags;
128 
129 	atomic_ops_lock(flags);
130 	WRITE_ONCE(v->counter, i);
131 	atomic_ops_unlock(flags);
132 }
133 
134 #endif
135 
136 /*
137  * Non hardware assisted Atomic-R-M-W
138  * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
139  */
140 
141 #define ATOMIC_OP(op, c_op, asm_op)					\
142 static inline void atomic_##op(int i, atomic_t *v)			\
143 {									\
144 	unsigned long flags;						\
145 									\
146 	atomic_ops_lock(flags);						\
147 	v->counter c_op i;						\
148 	atomic_ops_unlock(flags);					\
149 }
150 
151 #define ATOMIC_OP_RETURN(op, c_op, asm_op)				\
152 static inline int atomic_##op##_return(int i, atomic_t *v)		\
153 {									\
154 	unsigned long flags;						\
155 	unsigned long temp;						\
156 									\
157 	/*								\
158 	 * spin lock/unlock provides the needed smp_mb() before/after	\
159 	 */								\
160 	atomic_ops_lock(flags);						\
161 	temp = v->counter;						\
162 	temp c_op i;							\
163 	v->counter = temp;						\
164 	atomic_ops_unlock(flags);					\
165 									\
166 	return temp;							\
167 }
168 
169 #endif /* !CONFIG_ARC_HAS_LLSC */
170 
171 #define ATOMIC_OPS(op, c_op, asm_op)					\
172 	ATOMIC_OP(op, c_op, asm_op)					\
173 	ATOMIC_OP_RETURN(op, c_op, asm_op)
174 
175 ATOMIC_OPS(add, +=, add)
176 ATOMIC_OPS(sub, -=, sub)
177 
178 #define atomic_andnot atomic_andnot
179 
180 ATOMIC_OP(and, &=, and)
181 ATOMIC_OP(andnot, &= ~, bic)
182 ATOMIC_OP(or, |=, or)
183 ATOMIC_OP(xor, ^=, xor)
184 
185 #undef SCOND_FAIL_RETRY_VAR_DEF
186 #undef SCOND_FAIL_RETRY_ASM
187 #undef SCOND_FAIL_RETRY_VARS
188 
189 #else /* CONFIG_ARC_PLAT_EZNPS */
190 
191 static inline int atomic_read(const atomic_t *v)
192 {
193 	int temp;
194 
195 	__asm__ __volatile__(
196 	"	ld.di %0, [%1]"
197 	: "=r"(temp)
198 	: "r"(&v->counter)
199 	: "memory");
200 	return temp;
201 }
202 
203 static inline void atomic_set(atomic_t *v, int i)
204 {
205 	__asm__ __volatile__(
206 	"	st.di %0,[%1]"
207 	:
208 	: "r"(i), "r"(&v->counter)
209 	: "memory");
210 }
211 
212 #define ATOMIC_OP(op, c_op, asm_op)					\
213 static inline void atomic_##op(int i, atomic_t *v)			\
214 {									\
215 	__asm__ __volatile__(						\
216 	"	mov r2, %0\n"						\
217 	"	mov r3, %1\n"						\
218 	"       .word %2\n"						\
219 	:								\
220 	: "r"(i), "r"(&v->counter), "i"(asm_op)				\
221 	: "r2", "r3", "memory");					\
222 }									\
223 
224 #define ATOMIC_OP_RETURN(op, c_op, asm_op)				\
225 static inline int atomic_##op##_return(int i, atomic_t *v)		\
226 {									\
227 	unsigned int temp = i;						\
228 									\
229 	/* Explicit full memory barrier needed before/after */		\
230 	smp_mb();							\
231 									\
232 	__asm__ __volatile__(						\
233 	"	mov r2, %0\n"						\
234 	"	mov r3, %1\n"						\
235 	"       .word %2\n"						\
236 	"	mov %0, r2"						\
237 	: "+r"(temp)							\
238 	: "r"(&v->counter), "i"(asm_op)					\
239 	: "r2", "r3", "memory");					\
240 									\
241 	smp_mb();							\
242 									\
243 	temp c_op i;							\
244 									\
245 	return temp;							\
246 }
247 
248 #define ATOMIC_OPS(op, c_op, asm_op)					\
249 	ATOMIC_OP(op, c_op, asm_op)					\
250 	ATOMIC_OP_RETURN(op, c_op, asm_op)
251 
252 ATOMIC_OPS(add, +=, CTOP_INST_AADD_DI_R2_R2_R3)
253 #define atomic_sub(i, v) atomic_add(-(i), (v))
254 #define atomic_sub_return(i, v) atomic_add_return(-(i), (v))
255 
256 ATOMIC_OP(and, &=, CTOP_INST_AAND_DI_R2_R2_R3)
257 #define atomic_andnot(mask, v) atomic_and(~(mask), (v))
258 ATOMIC_OP(or, |=, CTOP_INST_AOR_DI_R2_R2_R3)
259 ATOMIC_OP(xor, ^=, CTOP_INST_AXOR_DI_R2_R2_R3)
260 
261 #endif /* CONFIG_ARC_PLAT_EZNPS */
262 
263 #undef ATOMIC_OPS
264 #undef ATOMIC_OP_RETURN
265 #undef ATOMIC_OP
266 
267 /**
268  * __atomic_add_unless - add unless the number is a given value
269  * @v: pointer of type atomic_t
270  * @a: the amount to add to v...
271  * @u: ...unless v is equal to u.
272  *
273  * Atomically adds @a to @v, so long as it was not @u.
274  * Returns the old value of @v
275  */
276 #define __atomic_add_unless(v, a, u)					\
277 ({									\
278 	int c, old;							\
279 									\
280 	/*								\
281 	 * Explicit full memory barrier needed before/after as		\
282 	 * LLOCK/SCOND thmeselves don't provide any such semantics	\
283 	 */								\
284 	smp_mb();							\
285 									\
286 	c = atomic_read(v);						\
287 	while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c)\
288 		c = old;						\
289 									\
290 	smp_mb();							\
291 									\
292 	c;								\
293 })
294 
295 #define atomic_inc_not_zero(v)		atomic_add_unless((v), 1, 0)
296 
297 #define atomic_inc(v)			atomic_add(1, v)
298 #define atomic_dec(v)			atomic_sub(1, v)
299 
300 #define atomic_inc_and_test(v)		(atomic_add_return(1, v) == 0)
301 #define atomic_dec_and_test(v)		(atomic_sub_return(1, v) == 0)
302 #define atomic_inc_return(v)		atomic_add_return(1, (v))
303 #define atomic_dec_return(v)		atomic_sub_return(1, (v))
304 #define atomic_sub_and_test(i, v)	(atomic_sub_return(i, v) == 0)
305 
306 #define atomic_add_negative(i, v)	(atomic_add_return(i, v) < 0)
307 
308 #define ATOMIC_INIT(i)			{ (i) }
309 
310 #include <asm-generic/atomic64.h>
311 
312 #endif
313 
314 #endif
315