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