xref: /openbmc/linux/arch/loongarch/include/asm/cmpxchg.h (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
15b0b14e5SHuacai Chen /* SPDX-License-Identifier: GPL-2.0 */
25b0b14e5SHuacai Chen /*
35b0b14e5SHuacai Chen  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
45b0b14e5SHuacai Chen  */
55b0b14e5SHuacai Chen #ifndef __ASM_CMPXCHG_H
65b0b14e5SHuacai Chen #define __ASM_CMPXCHG_H
75b0b14e5SHuacai Chen 
8720dc7abSHuacai Chen #include <linux/bits.h>
95b0b14e5SHuacai Chen #include <linux/build_bug.h>
10720dc7abSHuacai Chen #include <asm/barrier.h>
115b0b14e5SHuacai Chen 
125b0b14e5SHuacai Chen #define __xchg_asm(amswap_db, m, val)		\
135b0b14e5SHuacai Chen ({						\
145b0b14e5SHuacai Chen 		__typeof(val) __ret;		\
155b0b14e5SHuacai Chen 						\
165b0b14e5SHuacai Chen 		__asm__ __volatile__ (		\
175b0b14e5SHuacai Chen 		" "amswap_db" %1, %z2, %0 \n"	\
185b0b14e5SHuacai Chen 		: "+ZB" (*m), "=&r" (__ret)	\
195b0b14e5SHuacai Chen 		: "Jr" (val)			\
205b0b14e5SHuacai Chen 		: "memory");			\
215b0b14e5SHuacai Chen 						\
225b0b14e5SHuacai Chen 		__ret;				\
235b0b14e5SHuacai Chen })
245b0b14e5SHuacai Chen 
__xchg_small(volatile void * ptr,unsigned int val,unsigned int size)25720dc7abSHuacai Chen static inline unsigned int __xchg_small(volatile void *ptr, unsigned int val,
26720dc7abSHuacai Chen 					unsigned int size)
27720dc7abSHuacai Chen {
28720dc7abSHuacai Chen 	unsigned int shift;
29720dc7abSHuacai Chen 	u32 old32, mask, temp;
30720dc7abSHuacai Chen 	volatile u32 *ptr32;
31720dc7abSHuacai Chen 
32720dc7abSHuacai Chen 	/* Mask value to the correct size. */
33720dc7abSHuacai Chen 	mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
34720dc7abSHuacai Chen 	val &= mask;
35720dc7abSHuacai Chen 
36720dc7abSHuacai Chen 	/*
37720dc7abSHuacai Chen 	 * Calculate a shift & mask that correspond to the value we wish to
38720dc7abSHuacai Chen 	 * exchange within the naturally aligned 4 byte integerthat includes
39720dc7abSHuacai Chen 	 * it.
40720dc7abSHuacai Chen 	 */
41720dc7abSHuacai Chen 	shift = (unsigned long)ptr & 0x3;
42720dc7abSHuacai Chen 	shift *= BITS_PER_BYTE;
43720dc7abSHuacai Chen 	mask <<= shift;
44720dc7abSHuacai Chen 
45720dc7abSHuacai Chen 	/*
46720dc7abSHuacai Chen 	 * Calculate a pointer to the naturally aligned 4 byte integer that
47720dc7abSHuacai Chen 	 * includes our byte of interest, and load its value.
48720dc7abSHuacai Chen 	 */
49720dc7abSHuacai Chen 	ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
50720dc7abSHuacai Chen 
51720dc7abSHuacai Chen 	asm volatile (
52720dc7abSHuacai Chen 	"1:	ll.w		%0, %3		\n"
53720dc7abSHuacai Chen 	"	andn		%1, %0, %z4	\n"
54720dc7abSHuacai Chen 	"	or		%1, %1, %z5	\n"
55720dc7abSHuacai Chen 	"	sc.w		%1, %2		\n"
56720dc7abSHuacai Chen 	"	beqz		%1, 1b		\n"
57720dc7abSHuacai Chen 	: "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
58720dc7abSHuacai Chen 	: "ZC" (*ptr32), "Jr" (mask), "Jr" (val << shift)
59720dc7abSHuacai Chen 	: "memory");
60720dc7abSHuacai Chen 
61720dc7abSHuacai Chen 	return (old32 & mask) >> shift;
62720dc7abSHuacai Chen }
63720dc7abSHuacai Chen 
64ddf50271SHuacai Chen static __always_inline unsigned long
__arch_xchg(volatile void * ptr,unsigned long x,int size)65*06855063SAndrzej Hajda __arch_xchg(volatile void *ptr, unsigned long x, int size)
665b0b14e5SHuacai Chen {
675b0b14e5SHuacai Chen 	switch (size) {
68720dc7abSHuacai Chen 	case 1:
69720dc7abSHuacai Chen 	case 2:
70720dc7abSHuacai Chen 		return __xchg_small(ptr, x, size);
71720dc7abSHuacai Chen 
725b0b14e5SHuacai Chen 	case 4:
735b0b14e5SHuacai Chen 		return __xchg_asm("amswap_db.w", (volatile u32 *)ptr, (u32)x);
745b0b14e5SHuacai Chen 
755b0b14e5SHuacai Chen 	case 8:
765b0b14e5SHuacai Chen 		return __xchg_asm("amswap_db.d", (volatile u64 *)ptr, (u64)x);
775b0b14e5SHuacai Chen 
785b0b14e5SHuacai Chen 	default:
795b0b14e5SHuacai Chen 		BUILD_BUG();
805b0b14e5SHuacai Chen 	}
815b0b14e5SHuacai Chen 
825b0b14e5SHuacai Chen 	return 0;
835b0b14e5SHuacai Chen }
845b0b14e5SHuacai Chen 
855b0b14e5SHuacai Chen #define arch_xchg(ptr, x)						\
865b0b14e5SHuacai Chen ({									\
875b0b14e5SHuacai Chen 	__typeof__(*(ptr)) __res;					\
885b0b14e5SHuacai Chen 									\
895b0b14e5SHuacai Chen 	__res = (__typeof__(*(ptr)))					\
90*06855063SAndrzej Hajda 		__arch_xchg((ptr), (unsigned long)(x), sizeof(*(ptr)));	\
915b0b14e5SHuacai Chen 									\
925b0b14e5SHuacai Chen 	__res;								\
935b0b14e5SHuacai Chen })
945b0b14e5SHuacai Chen 
955b0b14e5SHuacai Chen #define __cmpxchg_asm(ld, st, m, old, new)				\
965b0b14e5SHuacai Chen ({									\
975b0b14e5SHuacai Chen 	__typeof(old) __ret;						\
985b0b14e5SHuacai Chen 									\
995b0b14e5SHuacai Chen 	__asm__ __volatile__(						\
1005b0b14e5SHuacai Chen 	"1:	" ld "	%0, %2		# __cmpxchg_asm \n"		\
1015b0b14e5SHuacai Chen 	"	bne	%0, %z3, 2f			\n"		\
10257ce5d3eSWANG Xuerui 	"	move	$t0, %z4			\n"		\
1035b0b14e5SHuacai Chen 	"	" st "	$t0, %1				\n"		\
104d47b2dc8SWANG Xuerui 	"	beqz	$t0, 1b				\n"		\
1055b0b14e5SHuacai Chen 	"2:						\n"		\
10646859ac8SHuacai Chen 	__WEAK_LLSC_MB							\
1075b0b14e5SHuacai Chen 	: "=&r" (__ret), "=ZB"(*m)					\
1085b0b14e5SHuacai Chen 	: "ZB"(*m), "Jr" (old), "Jr" (new)				\
1095b0b14e5SHuacai Chen 	: "t0", "memory");						\
1105b0b14e5SHuacai Chen 									\
1115b0b14e5SHuacai Chen 	__ret;								\
1125b0b14e5SHuacai Chen })
1135b0b14e5SHuacai Chen 
__cmpxchg_small(volatile void * ptr,unsigned int old,unsigned int new,unsigned int size)114720dc7abSHuacai Chen static inline unsigned int __cmpxchg_small(volatile void *ptr, unsigned int old,
115720dc7abSHuacai Chen 					   unsigned int new, unsigned int size)
116720dc7abSHuacai Chen {
117720dc7abSHuacai Chen 	unsigned int shift;
118720dc7abSHuacai Chen 	u32 old32, mask, temp;
119720dc7abSHuacai Chen 	volatile u32 *ptr32;
120720dc7abSHuacai Chen 
121720dc7abSHuacai Chen 	/* Mask inputs to the correct size. */
122720dc7abSHuacai Chen 	mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
123720dc7abSHuacai Chen 	old &= mask;
124720dc7abSHuacai Chen 	new &= mask;
125720dc7abSHuacai Chen 
126720dc7abSHuacai Chen 	/*
127720dc7abSHuacai Chen 	 * Calculate a shift & mask that correspond to the value we wish to
128720dc7abSHuacai Chen 	 * compare & exchange within the naturally aligned 4 byte integer
129720dc7abSHuacai Chen 	 * that includes it.
130720dc7abSHuacai Chen 	 */
131720dc7abSHuacai Chen 	shift = (unsigned long)ptr & 0x3;
132720dc7abSHuacai Chen 	shift *= BITS_PER_BYTE;
133720dc7abSHuacai Chen 	old <<= shift;
134720dc7abSHuacai Chen 	new <<= shift;
135720dc7abSHuacai Chen 	mask <<= shift;
136720dc7abSHuacai Chen 
137720dc7abSHuacai Chen 	/*
138720dc7abSHuacai Chen 	 * Calculate a pointer to the naturally aligned 4 byte integer that
139720dc7abSHuacai Chen 	 * includes our byte of interest, and load its value.
140720dc7abSHuacai Chen 	 */
141720dc7abSHuacai Chen 	ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
142720dc7abSHuacai Chen 
143720dc7abSHuacai Chen 	asm volatile (
144720dc7abSHuacai Chen 	"1:	ll.w		%0, %3		\n"
145720dc7abSHuacai Chen 	"	and		%1, %0, %z4	\n"
146720dc7abSHuacai Chen 	"	bne		%1, %z5, 2f	\n"
147720dc7abSHuacai Chen 	"	andn		%1, %0, %z4	\n"
148720dc7abSHuacai Chen 	"	or		%1, %1, %z6	\n"
149720dc7abSHuacai Chen 	"	sc.w		%1, %2		\n"
150720dc7abSHuacai Chen 	"	beqz		%1, 1b		\n"
151720dc7abSHuacai Chen 	"	b		3f		\n"
152720dc7abSHuacai Chen 	"2:					\n"
153720dc7abSHuacai Chen 	__WEAK_LLSC_MB
154720dc7abSHuacai Chen 	"3:					\n"
155720dc7abSHuacai Chen 	: "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
156720dc7abSHuacai Chen 	: "ZC" (*ptr32), "Jr" (mask), "Jr" (old), "Jr" (new)
157720dc7abSHuacai Chen 	: "memory");
158720dc7abSHuacai Chen 
159720dc7abSHuacai Chen 	return (old32 & mask) >> shift;
160720dc7abSHuacai Chen }
161720dc7abSHuacai Chen 
162ddf50271SHuacai Chen static __always_inline unsigned long
__cmpxchg(volatile void * ptr,unsigned long old,unsigned long new,unsigned int size)163ddf50271SHuacai Chen __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size)
1645b0b14e5SHuacai Chen {
1655b0b14e5SHuacai Chen 	switch (size) {
166720dc7abSHuacai Chen 	case 1:
167720dc7abSHuacai Chen 	case 2:
168720dc7abSHuacai Chen 		return __cmpxchg_small(ptr, old, new, size);
169720dc7abSHuacai Chen 
1705b0b14e5SHuacai Chen 	case 4:
1715b0b14e5SHuacai Chen 		return __cmpxchg_asm("ll.w", "sc.w", (volatile u32 *)ptr,
1725b0b14e5SHuacai Chen 				     (u32)old, new);
1735b0b14e5SHuacai Chen 
1745b0b14e5SHuacai Chen 	case 8:
1755b0b14e5SHuacai Chen 		return __cmpxchg_asm("ll.d", "sc.d", (volatile u64 *)ptr,
1765b0b14e5SHuacai Chen 				     (u64)old, new);
1775b0b14e5SHuacai Chen 
1785b0b14e5SHuacai Chen 	default:
1795b0b14e5SHuacai Chen 		BUILD_BUG();
1805b0b14e5SHuacai Chen 	}
1815b0b14e5SHuacai Chen 
1825b0b14e5SHuacai Chen 	return 0;
1835b0b14e5SHuacai Chen }
1845b0b14e5SHuacai Chen 
1855b0b14e5SHuacai Chen #define arch_cmpxchg_local(ptr, old, new)				\
1865b0b14e5SHuacai Chen 	((__typeof__(*(ptr)))						\
1875b0b14e5SHuacai Chen 		__cmpxchg((ptr),					\
1885b0b14e5SHuacai Chen 			  (unsigned long)(__typeof__(*(ptr)))(old),	\
1895b0b14e5SHuacai Chen 			  (unsigned long)(__typeof__(*(ptr)))(new),	\
1905b0b14e5SHuacai Chen 			  sizeof(*(ptr))))
1915b0b14e5SHuacai Chen 
1925b0b14e5SHuacai Chen #define arch_cmpxchg(ptr, old, new)					\
1935b0b14e5SHuacai Chen ({									\
1945b0b14e5SHuacai Chen 	__typeof__(*(ptr)) __res;					\
1955b0b14e5SHuacai Chen 									\
1965b0b14e5SHuacai Chen 	__res = arch_cmpxchg_local((ptr), (old), (new));		\
1975b0b14e5SHuacai Chen 									\
1985b0b14e5SHuacai Chen 	__res;								\
1995b0b14e5SHuacai Chen })
2005b0b14e5SHuacai Chen 
2015b0b14e5SHuacai Chen #ifdef CONFIG_64BIT
2025b0b14e5SHuacai Chen #define arch_cmpxchg64_local(ptr, o, n)					\
2035b0b14e5SHuacai Chen   ({									\
2045b0b14e5SHuacai Chen 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
2055b0b14e5SHuacai Chen 	arch_cmpxchg_local((ptr), (o), (n));				\
2065b0b14e5SHuacai Chen   })
2075b0b14e5SHuacai Chen 
2085b0b14e5SHuacai Chen #define arch_cmpxchg64(ptr, o, n)					\
2095b0b14e5SHuacai Chen   ({									\
2105b0b14e5SHuacai Chen 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
2115b0b14e5SHuacai Chen 	arch_cmpxchg((ptr), (o), (n));					\
2125b0b14e5SHuacai Chen   })
2135b0b14e5SHuacai Chen #else
2145b0b14e5SHuacai Chen #include <asm-generic/cmpxchg-local.h>
2155b0b14e5SHuacai Chen #define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
2165b0b14e5SHuacai Chen #define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n))
2175b0b14e5SHuacai Chen #endif
2185b0b14e5SHuacai Chen 
2195b0b14e5SHuacai Chen #endif /* __ASM_CMPXCHG_H */
220