1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2a2c9dbe8SHeiko Carstens /*
3a2c9dbe8SHeiko Carstens * Copyright IBM Corp. 1999, 2011
4a2c9dbe8SHeiko Carstens *
5a2c9dbe8SHeiko Carstens * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
6a2c9dbe8SHeiko Carstens */
7a2c9dbe8SHeiko Carstens
8a2c9dbe8SHeiko Carstens #ifndef __ASM_CMPXCHG_H
9a2c9dbe8SHeiko Carstens #define __ASM_CMPXCHG_H
10a2c9dbe8SHeiko Carstens
11b1d6b40cSHeiko Carstens #include <linux/mmdebug.h>
12a2c9dbe8SHeiko Carstens #include <linux/types.h>
13b1d6b40cSHeiko Carstens #include <linux/bug.h>
14a2c9dbe8SHeiko Carstens
15d2b1f6d2SHeiko Carstens void __xchg_called_with_bad_pointer(void);
16d2b1f6d2SHeiko Carstens
17*06855063SAndrzej Hajda static __always_inline unsigned long
__arch_xchg(unsigned long x,unsigned long address,int size)18*06855063SAndrzej Hajda __arch_xchg(unsigned long x, unsigned long address, int size)
19d2b1f6d2SHeiko Carstens {
2013525f0aSHeiko Carstens unsigned long old;
21d2b1f6d2SHeiko Carstens int shift;
22d2b1f6d2SHeiko Carstens
23d2b1f6d2SHeiko Carstens switch (size) {
24d2b1f6d2SHeiko Carstens case 1:
2513525f0aSHeiko Carstens shift = (3 ^ (address & 3)) << 3;
2613525f0aSHeiko Carstens address ^= address & 3;
27d2b1f6d2SHeiko Carstens asm volatile(
28d2b1f6d2SHeiko Carstens " l %0,%1\n"
29d2b1f6d2SHeiko Carstens "0: lr 0,%0\n"
30d2b1f6d2SHeiko Carstens " nr 0,%3\n"
31d2b1f6d2SHeiko Carstens " or 0,%2\n"
32d2b1f6d2SHeiko Carstens " cs %0,0,%1\n"
33d2b1f6d2SHeiko Carstens " jl 0b\n"
3413525f0aSHeiko Carstens : "=&d" (old), "+Q" (*(int *) address)
35d2b1f6d2SHeiko Carstens : "d" ((x & 0xff) << shift), "d" (~(0xff << shift))
36d2b1f6d2SHeiko Carstens : "memory", "cc", "0");
37d2b1f6d2SHeiko Carstens return old >> shift;
38d2b1f6d2SHeiko Carstens case 2:
3913525f0aSHeiko Carstens shift = (2 ^ (address & 2)) << 3;
4013525f0aSHeiko Carstens address ^= address & 2;
41d2b1f6d2SHeiko Carstens asm volatile(
42d2b1f6d2SHeiko Carstens " l %0,%1\n"
43d2b1f6d2SHeiko Carstens "0: lr 0,%0\n"
44d2b1f6d2SHeiko Carstens " nr 0,%3\n"
45d2b1f6d2SHeiko Carstens " or 0,%2\n"
46d2b1f6d2SHeiko Carstens " cs %0,0,%1\n"
47d2b1f6d2SHeiko Carstens " jl 0b\n"
4813525f0aSHeiko Carstens : "=&d" (old), "+Q" (*(int *) address)
49d2b1f6d2SHeiko Carstens : "d" ((x & 0xffff) << shift), "d" (~(0xffff << shift))
50d2b1f6d2SHeiko Carstens : "memory", "cc", "0");
51d2b1f6d2SHeiko Carstens return old >> shift;
52d2b1f6d2SHeiko Carstens case 4:
53d2b1f6d2SHeiko Carstens asm volatile(
54d2b1f6d2SHeiko Carstens " l %0,%1\n"
55d2b1f6d2SHeiko Carstens "0: cs %0,%2,%1\n"
56d2b1f6d2SHeiko Carstens " jl 0b\n"
5713525f0aSHeiko Carstens : "=&d" (old), "+Q" (*(int *) address)
58d2b1f6d2SHeiko Carstens : "d" (x)
59d2b1f6d2SHeiko Carstens : "memory", "cc");
60d2b1f6d2SHeiko Carstens return old;
61d2b1f6d2SHeiko Carstens case 8:
62d2b1f6d2SHeiko Carstens asm volatile(
63d2b1f6d2SHeiko Carstens " lg %0,%1\n"
64d2b1f6d2SHeiko Carstens "0: csg %0,%2,%1\n"
65d2b1f6d2SHeiko Carstens " jl 0b\n"
663e5ee323SHeiko Carstens : "=&d" (old), "+QS" (*(long *) address)
67d2b1f6d2SHeiko Carstens : "d" (x)
68d2b1f6d2SHeiko Carstens : "memory", "cc");
69d2b1f6d2SHeiko Carstens return old;
70d2b1f6d2SHeiko Carstens }
71d2b1f6d2SHeiko Carstens __xchg_called_with_bad_pointer();
72d2b1f6d2SHeiko Carstens return x;
73d2b1f6d2SHeiko Carstens }
74d2b1f6d2SHeiko Carstens
7500017423SHeiko Carstens #define arch_xchg(ptr, x) \
76d2b1f6d2SHeiko Carstens ({ \
77d2b1f6d2SHeiko Carstens __typeof__(*(ptr)) __ret; \
78d2b1f6d2SHeiko Carstens \
79d2b1f6d2SHeiko Carstens __ret = (__typeof__(*(ptr))) \
80*06855063SAndrzej Hajda __arch_xchg((unsigned long)(x), (unsigned long)(ptr), \
8113525f0aSHeiko Carstens sizeof(*(ptr))); \
82d2b1f6d2SHeiko Carstens __ret; \
83d2b1f6d2SHeiko Carstens })
84d2b1f6d2SHeiko Carstens
85d2b1f6d2SHeiko Carstens void __cmpxchg_called_with_bad_pointer(void);
86d2b1f6d2SHeiko Carstens
__cmpxchg(unsigned long address,unsigned long old,unsigned long new,int size)87a637b3bfSHeiko Carstens static __always_inline unsigned long __cmpxchg(unsigned long address,
88a637b3bfSHeiko Carstens unsigned long old,
89d2b1f6d2SHeiko Carstens unsigned long new, int size)
90d2b1f6d2SHeiko Carstens {
91d2b1f6d2SHeiko Carstens switch (size) {
92ce968f65SHeiko Carstens case 1: {
9351098f0eSJanis Schoetterl-Glausch unsigned int prev, shift, mask;
94ce968f65SHeiko Carstens
9513525f0aSHeiko Carstens shift = (3 ^ (address & 3)) << 3;
9613525f0aSHeiko Carstens address ^= address & 3;
9751098f0eSJanis Schoetterl-Glausch old = (old & 0xff) << shift;
9851098f0eSJanis Schoetterl-Glausch new = (new & 0xff) << shift;
9951098f0eSJanis Schoetterl-Glausch mask = ~(0xff << shift);
100d2b1f6d2SHeiko Carstens asm volatile(
10113f62e84SHeiko Carstens " l %[prev],%[address]\n"
10251098f0eSJanis Schoetterl-Glausch " nr %[prev],%[mask]\n"
10351098f0eSJanis Schoetterl-Glausch " xilf %[mask],0xffffffff\n"
10451098f0eSJanis Schoetterl-Glausch " or %[new],%[prev]\n"
10551098f0eSJanis Schoetterl-Glausch " or %[prev],%[tmp]\n"
10651098f0eSJanis Schoetterl-Glausch "0: lr %[tmp],%[prev]\n"
10751098f0eSJanis Schoetterl-Glausch " cs %[prev],%[new],%[address]\n"
108d2b1f6d2SHeiko Carstens " jnl 1f\n"
10913f62e84SHeiko Carstens " xr %[tmp],%[prev]\n"
11051098f0eSJanis Schoetterl-Glausch " xr %[new],%[tmp]\n"
11113f62e84SHeiko Carstens " nr %[tmp],%[mask]\n"
11251098f0eSJanis Schoetterl-Glausch " jz 0b\n"
113d2b1f6d2SHeiko Carstens "1:"
11413f62e84SHeiko Carstens : [prev] "=&d" (prev),
11551098f0eSJanis Schoetterl-Glausch [address] "+Q" (*(int *)address),
11651098f0eSJanis Schoetterl-Glausch [tmp] "+&d" (old),
11751098f0eSJanis Schoetterl-Glausch [new] "+&d" (new),
11851098f0eSJanis Schoetterl-Glausch [mask] "+&d" (mask)
11951098f0eSJanis Schoetterl-Glausch :: "memory", "cc");
120d2b1f6d2SHeiko Carstens return prev >> shift;
121ce968f65SHeiko Carstens }
122ce968f65SHeiko Carstens case 2: {
12351098f0eSJanis Schoetterl-Glausch unsigned int prev, shift, mask;
124ce968f65SHeiko Carstens
12513525f0aSHeiko Carstens shift = (2 ^ (address & 2)) << 3;
12613525f0aSHeiko Carstens address ^= address & 2;
12751098f0eSJanis Schoetterl-Glausch old = (old & 0xffff) << shift;
12851098f0eSJanis Schoetterl-Glausch new = (new & 0xffff) << shift;
12951098f0eSJanis Schoetterl-Glausch mask = ~(0xffff << shift);
130d2b1f6d2SHeiko Carstens asm volatile(
13113f62e84SHeiko Carstens " l %[prev],%[address]\n"
13251098f0eSJanis Schoetterl-Glausch " nr %[prev],%[mask]\n"
13351098f0eSJanis Schoetterl-Glausch " xilf %[mask],0xffffffff\n"
13451098f0eSJanis Schoetterl-Glausch " or %[new],%[prev]\n"
13551098f0eSJanis Schoetterl-Glausch " or %[prev],%[tmp]\n"
13651098f0eSJanis Schoetterl-Glausch "0: lr %[tmp],%[prev]\n"
13751098f0eSJanis Schoetterl-Glausch " cs %[prev],%[new],%[address]\n"
138d2b1f6d2SHeiko Carstens " jnl 1f\n"
13913f62e84SHeiko Carstens " xr %[tmp],%[prev]\n"
14051098f0eSJanis Schoetterl-Glausch " xr %[new],%[tmp]\n"
14113f62e84SHeiko Carstens " nr %[tmp],%[mask]\n"
14251098f0eSJanis Schoetterl-Glausch " jz 0b\n"
143d2b1f6d2SHeiko Carstens "1:"
14413f62e84SHeiko Carstens : [prev] "=&d" (prev),
14551098f0eSJanis Schoetterl-Glausch [address] "+Q" (*(int *)address),
14651098f0eSJanis Schoetterl-Glausch [tmp] "+&d" (old),
14751098f0eSJanis Schoetterl-Glausch [new] "+&d" (new),
14851098f0eSJanis Schoetterl-Glausch [mask] "+&d" (mask)
14951098f0eSJanis Schoetterl-Glausch :: "memory", "cc");
150d2b1f6d2SHeiko Carstens return prev >> shift;
151ce968f65SHeiko Carstens }
152ce968f65SHeiko Carstens case 4: {
153e388d66fSHeiko Carstens unsigned int prev = old;
154ce968f65SHeiko Carstens
155d2b1f6d2SHeiko Carstens asm volatile(
15613f62e84SHeiko Carstens " cs %[prev],%[new],%[address]\n"
157e388d66fSHeiko Carstens : [prev] "+&d" (prev),
15813f62e84SHeiko Carstens [address] "+Q" (*(int *)address)
159e388d66fSHeiko Carstens : [new] "d" (new)
160d2b1f6d2SHeiko Carstens : "memory", "cc");
161d2b1f6d2SHeiko Carstens return prev;
162ce968f65SHeiko Carstens }
163ce968f65SHeiko Carstens case 8: {
164e388d66fSHeiko Carstens unsigned long prev = old;
165ce968f65SHeiko Carstens
166d2b1f6d2SHeiko Carstens asm volatile(
16713f62e84SHeiko Carstens " csg %[prev],%[new],%[address]\n"
168e388d66fSHeiko Carstens : [prev] "+&d" (prev),
16913f62e84SHeiko Carstens [address] "+QS" (*(long *)address)
170e388d66fSHeiko Carstens : [new] "d" (new)
171d2b1f6d2SHeiko Carstens : "memory", "cc");
172d2b1f6d2SHeiko Carstens return prev;
173d2b1f6d2SHeiko Carstens }
174ce968f65SHeiko Carstens }
175d2b1f6d2SHeiko Carstens __cmpxchg_called_with_bad_pointer();
176d2b1f6d2SHeiko Carstens return old;
177d2b1f6d2SHeiko Carstens }
178d2b1f6d2SHeiko Carstens
17900017423SHeiko Carstens #define arch_cmpxchg(ptr, o, n) \
180f318a122SMartin Schwidefsky ({ \
181d2b1f6d2SHeiko Carstens __typeof__(*(ptr)) __ret; \
182d2b1f6d2SHeiko Carstens \
183d2b1f6d2SHeiko Carstens __ret = (__typeof__(*(ptr))) \
18413525f0aSHeiko Carstens __cmpxchg((unsigned long)(ptr), (unsigned long)(o), \
185d2b1f6d2SHeiko Carstens (unsigned long)(n), sizeof(*(ptr))); \
186d2b1f6d2SHeiko Carstens __ret; \
187f318a122SMartin Schwidefsky })
188a2c9dbe8SHeiko Carstens
18900017423SHeiko Carstens #define arch_cmpxchg64 arch_cmpxchg
19000017423SHeiko Carstens #define arch_cmpxchg_local arch_cmpxchg
19100017423SHeiko Carstens #define arch_cmpxchg64_local arch_cmpxchg
192a2c9dbe8SHeiko Carstens
193d2b1f6d2SHeiko Carstens #define system_has_cmpxchg128() 1
194a2c9dbe8SHeiko Carstens
arch_cmpxchg128(volatile u128 * ptr,u128 old,u128 new)19579ee201eSHeiko Carstens static __always_inline u128 arch_cmpxchg128(volatile u128 *ptr, u128 old, u128 new)
19679ee201eSHeiko Carstens {
19779ee201eSHeiko Carstens asm volatile(
19879ee201eSHeiko Carstens " cdsg %[old],%[new],%[ptr]\n"
19979ee201eSHeiko Carstens : [old] "+d" (old), [ptr] "+QS" (*ptr)
20079ee201eSHeiko Carstens : [new] "d" (new)
20179ee201eSHeiko Carstens : "memory", "cc");
20279ee201eSHeiko Carstens return old;
20379ee201eSHeiko Carstens }
20479ee201eSHeiko Carstens
20579ee201eSHeiko Carstens #define arch_cmpxchg128 arch_cmpxchg128
20679ee201eSHeiko Carstens
20779ee201eSHeiko Carstens #endif /* __ASM_CMPXCHG_H */
20879ee201eSHeiko Carstens