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