xref: /openbmc/linux/arch/powerpc/include/asm/cmpxchg.h (revision b4a6aaea)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _ASM_POWERPC_CMPXCHG_H_
3 #define _ASM_POWERPC_CMPXCHG_H_
4 
5 #ifdef __KERNEL__
6 #include <linux/compiler.h>
7 #include <asm/synch.h>
8 #include <linux/bug.h>
9 
10 #ifdef __BIG_ENDIAN
11 #define BITOFF_CAL(size, off)	((sizeof(u32) - size - off) * BITS_PER_BYTE)
12 #else
13 #define BITOFF_CAL(size, off)	(off * BITS_PER_BYTE)
14 #endif
15 
16 #define XCHG_GEN(type, sfx, cl)				\
17 static inline u32 __xchg_##type##sfx(volatile void *p, u32 val)	\
18 {								\
19 	unsigned int prev, prev_mask, tmp, bitoff, off;		\
20 								\
21 	off = (unsigned long)p % sizeof(u32);			\
22 	bitoff = BITOFF_CAL(sizeof(type), off);			\
23 	p -= off;						\
24 	val <<= bitoff;						\
25 	prev_mask = (u32)(type)-1 << bitoff;			\
26 								\
27 	__asm__ __volatile__(					\
28 "1:	lwarx   %0,0,%3\n"					\
29 "	andc	%1,%0,%5\n"					\
30 "	or	%1,%1,%4\n"					\
31 "	stwcx.	%1,0,%3\n"					\
32 "	bne-	1b\n"						\
33 	: "=&r" (prev), "=&r" (tmp), "+m" (*(u32*)p)		\
34 	: "r" (p), "r" (val), "r" (prev_mask)			\
35 	: "cc", cl);						\
36 								\
37 	return prev >> bitoff;					\
38 }
39 
40 #define CMPXCHG_GEN(type, sfx, br, br2, cl)			\
41 static inline							\
42 u32 __cmpxchg_##type##sfx(volatile void *p, u32 old, u32 new)	\
43 {								\
44 	unsigned int prev, prev_mask, tmp, bitoff, off;		\
45 								\
46 	off = (unsigned long)p % sizeof(u32);			\
47 	bitoff = BITOFF_CAL(sizeof(type), off);			\
48 	p -= off;						\
49 	old <<= bitoff;						\
50 	new <<= bitoff;						\
51 	prev_mask = (u32)(type)-1 << bitoff;			\
52 								\
53 	__asm__ __volatile__(					\
54 	br							\
55 "1:	lwarx   %0,0,%3\n"					\
56 "	and	%1,%0,%6\n"					\
57 "	cmpw	0,%1,%4\n"					\
58 "	bne-	2f\n"						\
59 "	andc	%1,%0,%6\n"					\
60 "	or	%1,%1,%5\n"					\
61 "	stwcx.  %1,0,%3\n"					\
62 "	bne-    1b\n"						\
63 	br2							\
64 	"\n"							\
65 "2:"								\
66 	: "=&r" (prev), "=&r" (tmp), "+m" (*(u32*)p)		\
67 	: "r" (p), "r" (old), "r" (new), "r" (prev_mask)	\
68 	: "cc", cl);						\
69 								\
70 	return prev >> bitoff;					\
71 }
72 
73 /*
74  * Atomic exchange
75  *
76  * Changes the memory location '*p' to be val and returns
77  * the previous value stored there.
78  */
79 
80 XCHG_GEN(u8, _local, "memory");
81 XCHG_GEN(u8, _relaxed, "cc");
82 XCHG_GEN(u16, _local, "memory");
83 XCHG_GEN(u16, _relaxed, "cc");
84 
85 static __always_inline unsigned long
86 __xchg_u32_local(volatile void *p, unsigned long val)
87 {
88 	unsigned long prev;
89 
90 	__asm__ __volatile__(
91 "1:	lwarx	%0,0,%2 \n"
92 "	stwcx.	%3,0,%2 \n\
93 	bne-	1b"
94 	: "=&r" (prev), "+m" (*(volatile unsigned int *)p)
95 	: "r" (p), "r" (val)
96 	: "cc", "memory");
97 
98 	return prev;
99 }
100 
101 static __always_inline unsigned long
102 __xchg_u32_relaxed(u32 *p, unsigned long val)
103 {
104 	unsigned long prev;
105 
106 	__asm__ __volatile__(
107 "1:	lwarx	%0,0,%2\n"
108 "	stwcx.	%3,0,%2\n"
109 "	bne-	1b"
110 	: "=&r" (prev), "+m" (*p)
111 	: "r" (p), "r" (val)
112 	: "cc");
113 
114 	return prev;
115 }
116 
117 #ifdef CONFIG_PPC64
118 static __always_inline unsigned long
119 __xchg_u64_local(volatile void *p, unsigned long val)
120 {
121 	unsigned long prev;
122 
123 	__asm__ __volatile__(
124 "1:	ldarx	%0,0,%2 \n"
125 "	stdcx.	%3,0,%2 \n\
126 	bne-	1b"
127 	: "=&r" (prev), "+m" (*(volatile unsigned long *)p)
128 	: "r" (p), "r" (val)
129 	: "cc", "memory");
130 
131 	return prev;
132 }
133 
134 static __always_inline unsigned long
135 __xchg_u64_relaxed(u64 *p, unsigned long val)
136 {
137 	unsigned long prev;
138 
139 	__asm__ __volatile__(
140 "1:	ldarx	%0,0,%2\n"
141 "	stdcx.	%3,0,%2\n"
142 "	bne-	1b"
143 	: "=&r" (prev), "+m" (*p)
144 	: "r" (p), "r" (val)
145 	: "cc");
146 
147 	return prev;
148 }
149 #endif
150 
151 static __always_inline unsigned long
152 __xchg_local(void *ptr, unsigned long x, unsigned int size)
153 {
154 	switch (size) {
155 	case 1:
156 		return __xchg_u8_local(ptr, x);
157 	case 2:
158 		return __xchg_u16_local(ptr, x);
159 	case 4:
160 		return __xchg_u32_local(ptr, x);
161 #ifdef CONFIG_PPC64
162 	case 8:
163 		return __xchg_u64_local(ptr, x);
164 #endif
165 	}
166 	BUILD_BUG_ON_MSG(1, "Unsupported size for __xchg");
167 	return x;
168 }
169 
170 static __always_inline unsigned long
171 __xchg_relaxed(void *ptr, unsigned long x, unsigned int size)
172 {
173 	switch (size) {
174 	case 1:
175 		return __xchg_u8_relaxed(ptr, x);
176 	case 2:
177 		return __xchg_u16_relaxed(ptr, x);
178 	case 4:
179 		return __xchg_u32_relaxed(ptr, x);
180 #ifdef CONFIG_PPC64
181 	case 8:
182 		return __xchg_u64_relaxed(ptr, x);
183 #endif
184 	}
185 	BUILD_BUG_ON_MSG(1, "Unsupported size for __xchg_local");
186 	return x;
187 }
188 #define arch_xchg_local(ptr,x)						     \
189   ({									     \
190      __typeof__(*(ptr)) _x_ = (x);					     \
191      (__typeof__(*(ptr))) __xchg_local((ptr),				     \
192      		(unsigned long)_x_, sizeof(*(ptr))); 			     \
193   })
194 
195 #define arch_xchg_relaxed(ptr, x)					\
196 ({									\
197 	__typeof__(*(ptr)) _x_ = (x);					\
198 	(__typeof__(*(ptr))) __xchg_relaxed((ptr),			\
199 			(unsigned long)_x_, sizeof(*(ptr)));		\
200 })
201 /*
202  * Compare and exchange - if *p == old, set it to new,
203  * and return the old value of *p.
204  */
205 
206 CMPXCHG_GEN(u8, , PPC_ATOMIC_ENTRY_BARRIER, PPC_ATOMIC_EXIT_BARRIER, "memory");
207 CMPXCHG_GEN(u8, _local, , , "memory");
208 CMPXCHG_GEN(u8, _acquire, , PPC_ACQUIRE_BARRIER, "memory");
209 CMPXCHG_GEN(u8, _relaxed, , , "cc");
210 CMPXCHG_GEN(u16, , PPC_ATOMIC_ENTRY_BARRIER, PPC_ATOMIC_EXIT_BARRIER, "memory");
211 CMPXCHG_GEN(u16, _local, , , "memory");
212 CMPXCHG_GEN(u16, _acquire, , PPC_ACQUIRE_BARRIER, "memory");
213 CMPXCHG_GEN(u16, _relaxed, , , "cc");
214 
215 static __always_inline unsigned long
216 __cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new)
217 {
218 	unsigned int prev;
219 
220 	__asm__ __volatile__ (
221 	PPC_ATOMIC_ENTRY_BARRIER
222 "1:	lwarx	%0,0,%2		# __cmpxchg_u32\n\
223 	cmpw	0,%0,%3\n\
224 	bne-	2f\n"
225 "	stwcx.	%4,0,%2\n\
226 	bne-	1b"
227 	PPC_ATOMIC_EXIT_BARRIER
228 	"\n\
229 2:"
230 	: "=&r" (prev), "+m" (*p)
231 	: "r" (p), "r" (old), "r" (new)
232 	: "cc", "memory");
233 
234 	return prev;
235 }
236 
237 static __always_inline unsigned long
238 __cmpxchg_u32_local(volatile unsigned int *p, unsigned long old,
239 			unsigned long new)
240 {
241 	unsigned int prev;
242 
243 	__asm__ __volatile__ (
244 "1:	lwarx	%0,0,%2		# __cmpxchg_u32\n\
245 	cmpw	0,%0,%3\n\
246 	bne-	2f\n"
247 "	stwcx.	%4,0,%2\n\
248 	bne-	1b"
249 	"\n\
250 2:"
251 	: "=&r" (prev), "+m" (*p)
252 	: "r" (p), "r" (old), "r" (new)
253 	: "cc", "memory");
254 
255 	return prev;
256 }
257 
258 static __always_inline unsigned long
259 __cmpxchg_u32_relaxed(u32 *p, unsigned long old, unsigned long new)
260 {
261 	unsigned long prev;
262 
263 	__asm__ __volatile__ (
264 "1:	lwarx	%0,0,%2		# __cmpxchg_u32_relaxed\n"
265 "	cmpw	0,%0,%3\n"
266 "	bne-	2f\n"
267 "	stwcx.	%4,0,%2\n"
268 "	bne-	1b\n"
269 "2:"
270 	: "=&r" (prev), "+m" (*p)
271 	: "r" (p), "r" (old), "r" (new)
272 	: "cc");
273 
274 	return prev;
275 }
276 
277 /*
278  * cmpxchg family don't have order guarantee if cmp part fails, therefore we
279  * can avoid superfluous barriers if we use assembly code to implement
280  * cmpxchg() and cmpxchg_acquire(), however we don't do the similar for
281  * cmpxchg_release() because that will result in putting a barrier in the
282  * middle of a ll/sc loop, which is probably a bad idea. For example, this
283  * might cause the conditional store more likely to fail.
284  */
285 static __always_inline unsigned long
286 __cmpxchg_u32_acquire(u32 *p, unsigned long old, unsigned long new)
287 {
288 	unsigned long prev;
289 
290 	__asm__ __volatile__ (
291 "1:	lwarx	%0,0,%2		# __cmpxchg_u32_acquire\n"
292 "	cmpw	0,%0,%3\n"
293 "	bne-	2f\n"
294 "	stwcx.	%4,0,%2\n"
295 "	bne-	1b\n"
296 	PPC_ACQUIRE_BARRIER
297 	"\n"
298 "2:"
299 	: "=&r" (prev), "+m" (*p)
300 	: "r" (p), "r" (old), "r" (new)
301 	: "cc", "memory");
302 
303 	return prev;
304 }
305 
306 #ifdef CONFIG_PPC64
307 static __always_inline unsigned long
308 __cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new)
309 {
310 	unsigned long prev;
311 
312 	__asm__ __volatile__ (
313 	PPC_ATOMIC_ENTRY_BARRIER
314 "1:	ldarx	%0,0,%2		# __cmpxchg_u64\n\
315 	cmpd	0,%0,%3\n\
316 	bne-	2f\n\
317 	stdcx.	%4,0,%2\n\
318 	bne-	1b"
319 	PPC_ATOMIC_EXIT_BARRIER
320 	"\n\
321 2:"
322 	: "=&r" (prev), "+m" (*p)
323 	: "r" (p), "r" (old), "r" (new)
324 	: "cc", "memory");
325 
326 	return prev;
327 }
328 
329 static __always_inline unsigned long
330 __cmpxchg_u64_local(volatile unsigned long *p, unsigned long old,
331 			unsigned long new)
332 {
333 	unsigned long prev;
334 
335 	__asm__ __volatile__ (
336 "1:	ldarx	%0,0,%2		# __cmpxchg_u64\n\
337 	cmpd	0,%0,%3\n\
338 	bne-	2f\n\
339 	stdcx.	%4,0,%2\n\
340 	bne-	1b"
341 	"\n\
342 2:"
343 	: "=&r" (prev), "+m" (*p)
344 	: "r" (p), "r" (old), "r" (new)
345 	: "cc", "memory");
346 
347 	return prev;
348 }
349 
350 static __always_inline unsigned long
351 __cmpxchg_u64_relaxed(u64 *p, unsigned long old, unsigned long new)
352 {
353 	unsigned long prev;
354 
355 	__asm__ __volatile__ (
356 "1:	ldarx	%0,0,%2		# __cmpxchg_u64_relaxed\n"
357 "	cmpd	0,%0,%3\n"
358 "	bne-	2f\n"
359 "	stdcx.	%4,0,%2\n"
360 "	bne-	1b\n"
361 "2:"
362 	: "=&r" (prev), "+m" (*p)
363 	: "r" (p), "r" (old), "r" (new)
364 	: "cc");
365 
366 	return prev;
367 }
368 
369 static __always_inline unsigned long
370 __cmpxchg_u64_acquire(u64 *p, unsigned long old, unsigned long new)
371 {
372 	unsigned long prev;
373 
374 	__asm__ __volatile__ (
375 "1:	ldarx	%0,0,%2		# __cmpxchg_u64_acquire\n"
376 "	cmpd	0,%0,%3\n"
377 "	bne-	2f\n"
378 "	stdcx.	%4,0,%2\n"
379 "	bne-	1b\n"
380 	PPC_ACQUIRE_BARRIER
381 	"\n"
382 "2:"
383 	: "=&r" (prev), "+m" (*p)
384 	: "r" (p), "r" (old), "r" (new)
385 	: "cc", "memory");
386 
387 	return prev;
388 }
389 #endif
390 
391 static __always_inline unsigned long
392 __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new,
393 	  unsigned int size)
394 {
395 	switch (size) {
396 	case 1:
397 		return __cmpxchg_u8(ptr, old, new);
398 	case 2:
399 		return __cmpxchg_u16(ptr, old, new);
400 	case 4:
401 		return __cmpxchg_u32(ptr, old, new);
402 #ifdef CONFIG_PPC64
403 	case 8:
404 		return __cmpxchg_u64(ptr, old, new);
405 #endif
406 	}
407 	BUILD_BUG_ON_MSG(1, "Unsupported size for __cmpxchg");
408 	return old;
409 }
410 
411 static __always_inline unsigned long
412 __cmpxchg_local(void *ptr, unsigned long old, unsigned long new,
413 	  unsigned int size)
414 {
415 	switch (size) {
416 	case 1:
417 		return __cmpxchg_u8_local(ptr, old, new);
418 	case 2:
419 		return __cmpxchg_u16_local(ptr, old, new);
420 	case 4:
421 		return __cmpxchg_u32_local(ptr, old, new);
422 #ifdef CONFIG_PPC64
423 	case 8:
424 		return __cmpxchg_u64_local(ptr, old, new);
425 #endif
426 	}
427 	BUILD_BUG_ON_MSG(1, "Unsupported size for __cmpxchg_local");
428 	return old;
429 }
430 
431 static __always_inline unsigned long
432 __cmpxchg_relaxed(void *ptr, unsigned long old, unsigned long new,
433 		  unsigned int size)
434 {
435 	switch (size) {
436 	case 1:
437 		return __cmpxchg_u8_relaxed(ptr, old, new);
438 	case 2:
439 		return __cmpxchg_u16_relaxed(ptr, old, new);
440 	case 4:
441 		return __cmpxchg_u32_relaxed(ptr, old, new);
442 #ifdef CONFIG_PPC64
443 	case 8:
444 		return __cmpxchg_u64_relaxed(ptr, old, new);
445 #endif
446 	}
447 	BUILD_BUG_ON_MSG(1, "Unsupported size for __cmpxchg_relaxed");
448 	return old;
449 }
450 
451 static __always_inline unsigned long
452 __cmpxchg_acquire(void *ptr, unsigned long old, unsigned long new,
453 		  unsigned int size)
454 {
455 	switch (size) {
456 	case 1:
457 		return __cmpxchg_u8_acquire(ptr, old, new);
458 	case 2:
459 		return __cmpxchg_u16_acquire(ptr, old, new);
460 	case 4:
461 		return __cmpxchg_u32_acquire(ptr, old, new);
462 #ifdef CONFIG_PPC64
463 	case 8:
464 		return __cmpxchg_u64_acquire(ptr, old, new);
465 #endif
466 	}
467 	BUILD_BUG_ON_MSG(1, "Unsupported size for __cmpxchg_acquire");
468 	return old;
469 }
470 #define arch_cmpxchg(ptr, o, n)						 \
471   ({									 \
472      __typeof__(*(ptr)) _o_ = (o);					 \
473      __typeof__(*(ptr)) _n_ = (n);					 \
474      (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_,		 \
475 				    (unsigned long)_n_, sizeof(*(ptr))); \
476   })
477 
478 
479 #define arch_cmpxchg_local(ptr, o, n)					 \
480   ({									 \
481      __typeof__(*(ptr)) _o_ = (o);					 \
482      __typeof__(*(ptr)) _n_ = (n);					 \
483      (__typeof__(*(ptr))) __cmpxchg_local((ptr), (unsigned long)_o_,	 \
484 				    (unsigned long)_n_, sizeof(*(ptr))); \
485   })
486 
487 #define arch_cmpxchg_relaxed(ptr, o, n)					\
488 ({									\
489 	__typeof__(*(ptr)) _o_ = (o);					\
490 	__typeof__(*(ptr)) _n_ = (n);					\
491 	(__typeof__(*(ptr))) __cmpxchg_relaxed((ptr),			\
492 			(unsigned long)_o_, (unsigned long)_n_,		\
493 			sizeof(*(ptr)));				\
494 })
495 
496 #define arch_cmpxchg_acquire(ptr, o, n)					\
497 ({									\
498 	__typeof__(*(ptr)) _o_ = (o);					\
499 	__typeof__(*(ptr)) _n_ = (n);					\
500 	(__typeof__(*(ptr))) __cmpxchg_acquire((ptr),			\
501 			(unsigned long)_o_, (unsigned long)_n_,		\
502 			sizeof(*(ptr)));				\
503 })
504 #ifdef CONFIG_PPC64
505 #define arch_cmpxchg64(ptr, o, n)					\
506   ({									\
507 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
508 	arch_cmpxchg((ptr), (o), (n));					\
509   })
510 #define arch_cmpxchg64_local(ptr, o, n)					\
511   ({									\
512 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
513 	arch_cmpxchg_local((ptr), (o), (n));				\
514   })
515 #define arch_cmpxchg64_relaxed(ptr, o, n)				\
516 ({									\
517 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
518 	arch_cmpxchg_relaxed((ptr), (o), (n));				\
519 })
520 #define arch_cmpxchg64_acquire(ptr, o, n)				\
521 ({									\
522 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
523 	arch_cmpxchg_acquire((ptr), (o), (n));				\
524 })
525 #else
526 #include <asm-generic/cmpxchg-local.h>
527 #define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
528 #endif
529 
530 #endif /* __KERNEL__ */
531 #endif /* _ASM_POWERPC_CMPXCHG_H_ */
532