xref: /openbmc/linux/arch/s390/include/asm/cmpxchg.h (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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