xref: /openbmc/linux/arch/mips/include/asm/cmpxchg.h (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1384740dcSRalf Baechle /*
2384740dcSRalf Baechle  * This file is subject to the terms and conditions of the GNU General Public
3384740dcSRalf Baechle  * License.  See the file "COPYING" in the main directory of this archive
4384740dcSRalf Baechle  * for more details.
5384740dcSRalf Baechle  *
6384740dcSRalf Baechle  * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
7384740dcSRalf Baechle  */
8384740dcSRalf Baechle #ifndef __ASM_CMPXCHG_H
9384740dcSRalf Baechle #define __ASM_CMPXCHG_H
10384740dcSRalf Baechle 
115520e426SAaro Koskinen #include <linux/bug.h>
12384740dcSRalf Baechle #include <linux/irqflags.h>
13f0b7ddbdSHuang Pei #include <asm/asm.h>
14b0984c43SMaciej W. Rozycki #include <asm/compiler.h>
156a57d2d1SPaul Burton #include <asm/sync.h>
16b81947c6SDavid Howells 
176b1e7629SPaul Burton /*
18d15dc68cSPaul Burton  * These functions doesn't exist, so if they are called you'll either:
19d15dc68cSPaul Burton  *
20d15dc68cSPaul Burton  * - Get an error at compile-time due to __compiletime_error, if supported by
21d15dc68cSPaul Burton  *   your compiler.
22d15dc68cSPaul Burton  *
23d15dc68cSPaul Burton  * or:
24d15dc68cSPaul Burton  *
25d15dc68cSPaul Burton  * - Get an error at link-time due to the call to the missing function.
26d15dc68cSPaul Burton  */
278263db4dSPaul Burton extern unsigned long __cmpxchg_called_with_bad_pointer(void)
28d15dc68cSPaul Burton 	__compiletime_error("Bad argument size for cmpxchg");
29c7e2d71dSPaul Burton extern unsigned long __cmpxchg64_unsupported(void)
30c7e2d71dSPaul Burton 	__compiletime_error("cmpxchg64 not available; cpu_has_64bits may be false");
31d15dc68cSPaul Burton extern unsigned long __xchg_called_with_bad_pointer(void)
32d15dc68cSPaul Burton 	__compiletime_error("Bad argument size for xchg");
33d15dc68cSPaul Burton 
345154f3b4SPaul Burton #define __xchg_asm(ld, st, m, val)					\
355154f3b4SPaul Burton ({									\
365154f3b4SPaul Burton 	__typeof(*(m)) __ret;						\
375154f3b4SPaul Burton 									\
385154f3b4SPaul Burton 	if (kernel_uses_llsc) {						\
395154f3b4SPaul Burton 		__asm__ __volatile__(					\
405154f3b4SPaul Burton 		"	.set	push				\n"	\
415154f3b4SPaul Burton 		"	.set	noat				\n"	\
42378ed6f0SPaul Burton 		"	.set	push				\n"	\
435154f3b4SPaul Burton 		"	.set	" MIPS_ISA_ARCH_LEVEL "		\n"	\
446a57d2d1SPaul Burton 		"	" __SYNC(full, loongson3_war) "		\n"	\
455154f3b4SPaul Burton 		"1:	" ld "	%0, %2		# __xchg_asm	\n"	\
46378ed6f0SPaul Burton 		"	.set	pop				\n"	\
475154f3b4SPaul Burton 		"	move	$1, %z3				\n"	\
485154f3b4SPaul Burton 		"	.set	" MIPS_ISA_ARCH_LEVEL "		\n"	\
495154f3b4SPaul Burton 		"	" st "	$1, %1				\n"	\
50f0b7ddbdSHuang Pei 		"\t" __stringify(SC_BEQZ)	"	$1, 1b	\n"	\
515154f3b4SPaul Burton 		"	.set	pop				\n"	\
525154f3b4SPaul Burton 		: "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m)		\
535154f3b4SPaul Burton 		: GCC_OFF_SMALL_ASM() (*m), "Jr" (val)			\
5442344113SPeter Zijlstra 		: __LLSC_CLOBBER);					\
555154f3b4SPaul Burton 	} else {							\
565154f3b4SPaul Burton 		unsigned long __flags;					\
575154f3b4SPaul Burton 									\
585154f3b4SPaul Burton 		raw_local_irq_save(__flags);				\
595154f3b4SPaul Burton 		__ret = *m;						\
605154f3b4SPaul Burton 		*m = val;						\
615154f3b4SPaul Burton 		raw_local_irq_restore(__flags);				\
625154f3b4SPaul Burton 	}								\
635154f3b4SPaul Burton 									\
645154f3b4SPaul Burton 	__ret;								\
655154f3b4SPaul Burton })
665154f3b4SPaul Burton 
67b70eb300SPaul Burton extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
68b70eb300SPaul Burton 				  unsigned int size);
69b70eb300SPaul Burton 
7046f16195SThomas Bogendoerfer static __always_inline
__arch_xchg(volatile void * ptr,unsigned long x,int size)71*06855063SAndrzej Hajda unsigned long __arch_xchg(volatile void *ptr, unsigned long x, int size)
72b81947c6SDavid Howells {
73b81947c6SDavid Howells 	switch (size) {
74b70eb300SPaul Burton 	case 1:
75b70eb300SPaul Burton 	case 2:
76b70eb300SPaul Burton 		return __xchg_small(ptr, x, size);
77b70eb300SPaul Burton 
78b81947c6SDavid Howells 	case 4:
7962c6081dSPaul Burton 		return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x);
8062c6081dSPaul Burton 
81b81947c6SDavid Howells 	case 8:
8262c6081dSPaul Burton 		if (!IS_ENABLED(CONFIG_64BIT))
8362c6081dSPaul Burton 			return __xchg_called_with_bad_pointer();
8462c6081dSPaul Burton 
8562c6081dSPaul Burton 		return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x);
8662c6081dSPaul Burton 
87d15dc68cSPaul Burton 	default:
88d15dc68cSPaul Burton 		return __xchg_called_with_bad_pointer();
89b81947c6SDavid Howells 	}
90b81947c6SDavid Howells }
91b81947c6SDavid Howells 
92c7b5fd6fSMark Rutland #define arch_xchg(ptr, x)						\
93b81947c6SDavid Howells ({									\
9462c6081dSPaul Burton 	__typeof__(*(ptr)) __res;					\
9562c6081dSPaul Burton 									\
96a91f2a1dSPaul Burton 	/*								\
97a91f2a1dSPaul Burton 	 * In the Loongson3 workaround case __xchg_asm() already	\
98a91f2a1dSPaul Burton 	 * contains a completion barrier prior to the LL, so we don't	\
99a91f2a1dSPaul Burton 	 * need to emit an extra one here.				\
100a91f2a1dSPaul Burton 	 */								\
1018790ccf8SNathan Chancellor 	if (__SYNC_loongson3_war == 0)					\
10262c6081dSPaul Burton 		smp_mb__before_llsc();					\
10362c6081dSPaul Burton 									\
10462c6081dSPaul Burton 	__res = (__typeof__(*(ptr)))					\
105*06855063SAndrzej Hajda 		__arch_xchg((ptr), (unsigned long)(x), sizeof(*(ptr)));	\
10662c6081dSPaul Burton 									\
10762c6081dSPaul Burton 	smp_llsc_mb();							\
10862c6081dSPaul Burton 									\
10962c6081dSPaul Burton 	__res;								\
110b81947c6SDavid Howells })
111384740dcSRalf Baechle 
112384740dcSRalf Baechle #define __cmpxchg_asm(ld, st, m, old, new)				\
113384740dcSRalf Baechle ({									\
114384740dcSRalf Baechle 	__typeof(*(m)) __ret;						\
115384740dcSRalf Baechle 									\
1166b1e7629SPaul Burton 	if (kernel_uses_llsc) {						\
117384740dcSRalf Baechle 		__asm__ __volatile__(					\
118384740dcSRalf Baechle 		"	.set	push				\n"	\
119384740dcSRalf Baechle 		"	.set	noat				\n"	\
120378ed6f0SPaul Burton 		"	.set	push				\n"	\
121fa998ebbSMarkos Chandras 		"	.set	"MIPS_ISA_ARCH_LEVEL"		\n"	\
1226a57d2d1SPaul Burton 		"	" __SYNC(full, loongson3_war) "		\n"	\
123384740dcSRalf Baechle 		"1:	" ld "	%0, %2		# __cmpxchg_asm \n"	\
124384740dcSRalf Baechle 		"	bne	%0, %z3, 2f			\n"	\
125378ed6f0SPaul Burton 		"	.set	pop				\n"	\
126384740dcSRalf Baechle 		"	move	$1, %z4				\n"	\
127fa998ebbSMarkos Chandras 		"	.set	"MIPS_ISA_ARCH_LEVEL"		\n"	\
128384740dcSRalf Baechle 		"	" st "	$1, %1				\n"	\
129f0b7ddbdSHuang Pei 		"\t" __stringify(SC_BEQZ)	"	$1, 1b	\n"	\
130384740dcSRalf Baechle 		"	.set	pop				\n"	\
1316a57d2d1SPaul Burton 		"2:	" __SYNC(full, loongson3_war) "		\n"	\
13294bfb75aSMarkos Chandras 		: "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m)		\
13394bfb75aSMarkos Chandras 		: GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new)	\
13442344113SPeter Zijlstra 		: __LLSC_CLOBBER);					\
135384740dcSRalf Baechle 	} else {							\
136384740dcSRalf Baechle 		unsigned long __flags;					\
137384740dcSRalf Baechle 									\
138384740dcSRalf Baechle 		raw_local_irq_save(__flags);				\
139384740dcSRalf Baechle 		__ret = *m;						\
140384740dcSRalf Baechle 		if (__ret == old)					\
141384740dcSRalf Baechle 			*m = new;					\
142384740dcSRalf Baechle 		raw_local_irq_restore(__flags);				\
143384740dcSRalf Baechle 	}								\
144384740dcSRalf Baechle 									\
145384740dcSRalf Baechle 	__ret;								\
146384740dcSRalf Baechle })
147384740dcSRalf Baechle 
1483ba7f44dSPaul Burton extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
1493ba7f44dSPaul Burton 				     unsigned long new, unsigned int size);
1503ba7f44dSPaul Burton 
15188356d09SThomas Bogendoerfer static __always_inline
__cmpxchg(volatile void * ptr,unsigned long old,unsigned long new,unsigned int size)15288356d09SThomas Bogendoerfer unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
1538263db4dSPaul Burton 			unsigned long new, unsigned int size)
1548263db4dSPaul Burton {
1558263db4dSPaul Burton 	switch (size) {
1563ba7f44dSPaul Burton 	case 1:
1573ba7f44dSPaul Burton 	case 2:
1583ba7f44dSPaul Burton 		return __cmpxchg_small(ptr, old, new, size);
1593ba7f44dSPaul Burton 
1608263db4dSPaul Burton 	case 4:
161133d68e0SPaul Burton 		return __cmpxchg_asm("ll", "sc", (volatile u32 *)ptr,
162133d68e0SPaul Burton 				     (u32)old, new);
1638263db4dSPaul Burton 
1648263db4dSPaul Burton 	case 8:
1658263db4dSPaul Burton 		/* lld/scd are only available for MIPS64 */
1668263db4dSPaul Burton 		if (!IS_ENABLED(CONFIG_64BIT))
1678263db4dSPaul Burton 			return __cmpxchg_called_with_bad_pointer();
1688263db4dSPaul Burton 
169133d68e0SPaul Burton 		return __cmpxchg_asm("lld", "scd", (volatile u64 *)ptr,
170133d68e0SPaul Burton 				     (u64)old, new);
1718263db4dSPaul Burton 
1728263db4dSPaul Burton 	default:
1738263db4dSPaul Burton 		return __cmpxchg_called_with_bad_pointer();
1748263db4dSPaul Burton 	}
1758263db4dSPaul Burton }
1768263db4dSPaul Burton 
177c7b5fd6fSMark Rutland #define arch_cmpxchg_local(ptr, old, new)				\
1788263db4dSPaul Burton 	((__typeof__(*(ptr)))						\
1798263db4dSPaul Burton 		__cmpxchg((ptr),					\
1808263db4dSPaul Burton 			  (unsigned long)(__typeof__(*(ptr)))(old),	\
1818263db4dSPaul Burton 			  (unsigned long)(__typeof__(*(ptr)))(new),	\
1828263db4dSPaul Burton 			  sizeof(*(ptr))))
1838263db4dSPaul Burton 
184c7b5fd6fSMark Rutland #define arch_cmpxchg(ptr, old, new)					\
185384740dcSRalf Baechle ({									\
1868263db4dSPaul Burton 	__typeof__(*(ptr)) __res;					\
187384740dcSRalf Baechle 									\
188a91f2a1dSPaul Burton 	/*								\
189a91f2a1dSPaul Burton 	 * In the Loongson3 workaround case __cmpxchg_asm() already	\
190a91f2a1dSPaul Burton 	 * contains a completion barrier prior to the LL, so we don't	\
191a91f2a1dSPaul Burton 	 * need to emit an extra one here.				\
192a91f2a1dSPaul Burton 	 */								\
1938790ccf8SNathan Chancellor 	if (__SYNC_loongson3_war == 0)					\
1948263db4dSPaul Burton 		smp_mb__before_llsc();					\
195a91f2a1dSPaul Burton 									\
196c7b5fd6fSMark Rutland 	__res = arch_cmpxchg_local((ptr), (old), (new));		\
197a91f2a1dSPaul Burton 									\
198a91f2a1dSPaul Burton 	/*								\
199a91f2a1dSPaul Burton 	 * In the Loongson3 workaround case __cmpxchg_asm() already	\
200a91f2a1dSPaul Burton 	 * contains a completion barrier after the SC, so we don't	\
201a91f2a1dSPaul Burton 	 * need to emit an extra one here.				\
202a91f2a1dSPaul Burton 	 */								\
2038790ccf8SNathan Chancellor 	if (__SYNC_loongson3_war == 0)					\
2048263db4dSPaul Burton 		smp_llsc_mb();						\
205384740dcSRalf Baechle 									\
206384740dcSRalf Baechle 	__res;								\
207384740dcSRalf Baechle })
208384740dcSRalf Baechle 
209384740dcSRalf Baechle #ifdef CONFIG_64BIT
210c7b5fd6fSMark Rutland #define arch_cmpxchg64_local(ptr, o, n)					\
211384740dcSRalf Baechle   ({									\
212384740dcSRalf Baechle 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
213c7b5fd6fSMark Rutland 	arch_cmpxchg_local((ptr), (o), (n));				\
214384740dcSRalf Baechle   })
215e2093c7bSDeng-Cheng Zhu 
216c7b5fd6fSMark Rutland #define arch_cmpxchg64(ptr, o, n)					\
217e2093c7bSDeng-Cheng Zhu   ({									\
218e2093c7bSDeng-Cheng Zhu 	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
219c7b5fd6fSMark Rutland 	arch_cmpxchg((ptr), (o), (n));					\
220e2093c7bSDeng-Cheng Zhu   })
221384740dcSRalf Baechle #else
222c7e2d71dSPaul Burton 
223384740dcSRalf Baechle # include <asm-generic/cmpxchg-local.h>
224c7b5fd6fSMark Rutland # define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
225c7e2d71dSPaul Burton 
226c7e2d71dSPaul Burton # ifdef CONFIG_SMP
227c7e2d71dSPaul Burton 
__cmpxchg64(volatile void * ptr,unsigned long long old,unsigned long long new)228c7e2d71dSPaul Burton static inline unsigned long __cmpxchg64(volatile void *ptr,
229c7e2d71dSPaul Burton 					unsigned long long old,
230c7e2d71dSPaul Burton 					unsigned long long new)
231c7e2d71dSPaul Burton {
232c7e2d71dSPaul Burton 	unsigned long long tmp, ret;
233c7e2d71dSPaul Burton 	unsigned long flags;
234c7e2d71dSPaul Burton 
235c7e2d71dSPaul Burton 	/*
236c7e2d71dSPaul Burton 	 * The assembly below has to combine 32 bit values into a 64 bit
237c7e2d71dSPaul Burton 	 * register, and split 64 bit values from one register into two. If we
238c7e2d71dSPaul Burton 	 * were to take an interrupt in the middle of this we'd only save the
239c7e2d71dSPaul Burton 	 * least significant 32 bits of each register & probably clobber the
240c7e2d71dSPaul Burton 	 * most significant 32 bits of the 64 bit values we're using. In order
241c7e2d71dSPaul Burton 	 * to avoid this we must disable interrupts.
242c7e2d71dSPaul Burton 	 */
243c7e2d71dSPaul Burton 	local_irq_save(flags);
244c7e2d71dSPaul Burton 
245c7e2d71dSPaul Burton 	asm volatile(
246c7e2d71dSPaul Burton 	"	.set	push				\n"
247c7e2d71dSPaul Burton 	"	.set	" MIPS_ISA_ARCH_LEVEL "		\n"
248c7e2d71dSPaul Burton 	/* Load 64 bits from ptr */
2496a57d2d1SPaul Burton 	"	" __SYNC(full, loongson3_war) "		\n"
250c7e2d71dSPaul Burton 	"1:	lld	%L0, %3		# __cmpxchg64	\n"
251a923a267SMaciej W. Rozycki 	"	.set	pop				\n"
252c7e2d71dSPaul Burton 	/*
253c7e2d71dSPaul Burton 	 * Split the 64 bit value we loaded into the 2 registers that hold the
254c7e2d71dSPaul Burton 	 * ret variable.
255c7e2d71dSPaul Burton 	 */
256c7e2d71dSPaul Burton 	"	dsra	%M0, %L0, 32			\n"
257c7e2d71dSPaul Burton 	"	sll	%L0, %L0, 0			\n"
258c7e2d71dSPaul Burton 	/*
259c7e2d71dSPaul Burton 	 * Compare ret against old, breaking out of the loop if they don't
260c7e2d71dSPaul Burton 	 * match.
261c7e2d71dSPaul Burton 	 */
262c7e2d71dSPaul Burton 	"	bne	%M0, %M4, 2f			\n"
263c7e2d71dSPaul Burton 	"	bne	%L0, %L4, 2f			\n"
264c7e2d71dSPaul Burton 	/*
265c7e2d71dSPaul Burton 	 * Combine the 32 bit halves from the 2 registers that hold the new
266c7e2d71dSPaul Burton 	 * variable into a single 64 bit register.
267c7e2d71dSPaul Burton 	 */
268c7e2d71dSPaul Burton #  if MIPS_ISA_REV >= 2
269c7e2d71dSPaul Burton 	"	move	%L1, %L5			\n"
270c7e2d71dSPaul Burton 	"	dins	%L1, %M5, 32, 32		\n"
271c7e2d71dSPaul Burton #  else
272c7e2d71dSPaul Burton 	"	dsll	%L1, %L5, 32			\n"
273c7e2d71dSPaul Burton 	"	dsrl	%L1, %L1, 32			\n"
274c7e2d71dSPaul Burton 	"	.set	noat				\n"
275c7e2d71dSPaul Burton 	"	dsll	$at, %M5, 32			\n"
276c7e2d71dSPaul Burton 	"	or	%L1, %L1, $at			\n"
277c7e2d71dSPaul Burton 	"	.set	at				\n"
278c7e2d71dSPaul Burton #  endif
279a923a267SMaciej W. Rozycki 	"	.set	push				\n"
280a923a267SMaciej W. Rozycki 	"	.set	" MIPS_ISA_ARCH_LEVEL "		\n"
281c7e2d71dSPaul Burton 	/* Attempt to store new at ptr */
282c7e2d71dSPaul Burton 	"	scd	%L1, %2				\n"
283c7e2d71dSPaul Burton 	/* If we failed, loop! */
284f0b7ddbdSHuang Pei 	"\t" __stringify(SC_BEQZ) "	%L1, 1b		\n"
2856a57d2d1SPaul Burton 	"2:	" __SYNC(full, loongson3_war) "		\n"
286a923a267SMaciej W. Rozycki 	"	.set	pop				\n"
287c7e2d71dSPaul Burton 	: "=&r"(ret),
288c7e2d71dSPaul Burton 	  "=&r"(tmp),
289c7e2d71dSPaul Burton 	  "=" GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr)
290c7e2d71dSPaul Burton 	: GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr),
291c7e2d71dSPaul Burton 	  "r" (old),
292c7e2d71dSPaul Burton 	  "r" (new)
293c7e2d71dSPaul Burton 	: "memory");
294c7e2d71dSPaul Burton 
295c7e2d71dSPaul Burton 	local_irq_restore(flags);
296c7e2d71dSPaul Burton 	return ret;
297c7e2d71dSPaul Burton }
298c7e2d71dSPaul Burton 
299c7b5fd6fSMark Rutland #  define arch_cmpxchg64(ptr, o, n) ({					\
300c7e2d71dSPaul Burton 	unsigned long long __old = (__typeof__(*(ptr)))(o);		\
301c7e2d71dSPaul Burton 	unsigned long long __new = (__typeof__(*(ptr)))(n);		\
302c7e2d71dSPaul Burton 	__typeof__(*(ptr)) __res;					\
303c7e2d71dSPaul Burton 									\
304c7e2d71dSPaul Burton 	/*								\
305c7e2d71dSPaul Burton 	 * We can only use cmpxchg64 if we know that the CPU supports	\
306c7e2d71dSPaul Burton 	 * 64-bits, ie. lld & scd. Our call to __cmpxchg64_unsupported	\
307c7e2d71dSPaul Burton 	 * will cause a build error unless cpu_has_64bits is a		\
308c7e2d71dSPaul Burton 	 * compile-time constant 1.					\
309c7e2d71dSPaul Burton 	 */								\
310dfc8d8deSPeter Zijlstra 	if (cpu_has_64bits && kernel_uses_llsc) {			\
311dfc8d8deSPeter Zijlstra 		smp_mb__before_llsc();					\
312c7e2d71dSPaul Burton 		__res = __cmpxchg64((ptr), __old, __new);		\
313dfc8d8deSPeter Zijlstra 		smp_llsc_mb();						\
314dfc8d8deSPeter Zijlstra 	} else {							\
315c7e2d71dSPaul Burton 		__res = __cmpxchg64_unsupported();			\
316dfc8d8deSPeter Zijlstra 	}								\
317c7e2d71dSPaul Burton 									\
318c7e2d71dSPaul Burton 	__res;								\
319c7e2d71dSPaul Burton })
320c7e2d71dSPaul Burton 
321c7e2d71dSPaul Burton # else /* !CONFIG_SMP */
322c7b5fd6fSMark Rutland #  define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n))
323c7e2d71dSPaul Burton # endif /* !CONFIG_SMP */
324c7e2d71dSPaul Burton #endif /* !CONFIG_64BIT */
325384740dcSRalf Baechle 
326384740dcSRalf Baechle #endif /* __ASM_CMPXCHG_H */
327