1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
3*4882a593Smuzhiyun * License. See the file "COPYING" in the main directory of this archive
4*4882a593Smuzhiyun * for more details.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #ifndef __ASM_CMPXCHG_H
9*4882a593Smuzhiyun #define __ASM_CMPXCHG_H
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/bug.h>
12*4882a593Smuzhiyun #include <linux/irqflags.h>
13*4882a593Smuzhiyun #include <asm/compiler.h>
14*4882a593Smuzhiyun #include <asm/llsc.h>
15*4882a593Smuzhiyun #include <asm/sync.h>
16*4882a593Smuzhiyun #include <asm/war.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun /*
19*4882a593Smuzhiyun * These functions doesn't exist, so if they are called you'll either:
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * - Get an error at compile-time due to __compiletime_error, if supported by
22*4882a593Smuzhiyun * your compiler.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun * or:
25*4882a593Smuzhiyun *
26*4882a593Smuzhiyun * - Get an error at link-time due to the call to the missing function.
27*4882a593Smuzhiyun */
28*4882a593Smuzhiyun extern unsigned long __cmpxchg_called_with_bad_pointer(void)
29*4882a593Smuzhiyun __compiletime_error("Bad argument size for cmpxchg");
30*4882a593Smuzhiyun extern unsigned long __cmpxchg64_unsupported(void)
31*4882a593Smuzhiyun __compiletime_error("cmpxchg64 not available; cpu_has_64bits may be false");
32*4882a593Smuzhiyun extern unsigned long __xchg_called_with_bad_pointer(void)
33*4882a593Smuzhiyun __compiletime_error("Bad argument size for xchg");
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #define __xchg_asm(ld, st, m, val) \
36*4882a593Smuzhiyun ({ \
37*4882a593Smuzhiyun __typeof(*(m)) __ret; \
38*4882a593Smuzhiyun \
39*4882a593Smuzhiyun if (kernel_uses_llsc) { \
40*4882a593Smuzhiyun __asm__ __volatile__( \
41*4882a593Smuzhiyun " .set push \n" \
42*4882a593Smuzhiyun " .set noat \n" \
43*4882a593Smuzhiyun " .set push \n" \
44*4882a593Smuzhiyun " .set " MIPS_ISA_ARCH_LEVEL " \n" \
45*4882a593Smuzhiyun " " __SYNC(full, loongson3_war) " \n" \
46*4882a593Smuzhiyun "1: " ld " %0, %2 # __xchg_asm \n" \
47*4882a593Smuzhiyun " .set pop \n" \
48*4882a593Smuzhiyun " move $1, %z3 \n" \
49*4882a593Smuzhiyun " .set " MIPS_ISA_ARCH_LEVEL " \n" \
50*4882a593Smuzhiyun " " st " $1, %1 \n" \
51*4882a593Smuzhiyun "\t" __SC_BEQZ "$1, 1b \n" \
52*4882a593Smuzhiyun " .set pop \n" \
53*4882a593Smuzhiyun : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
54*4882a593Smuzhiyun : GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \
55*4882a593Smuzhiyun : __LLSC_CLOBBER); \
56*4882a593Smuzhiyun } else { \
57*4882a593Smuzhiyun unsigned long __flags; \
58*4882a593Smuzhiyun \
59*4882a593Smuzhiyun raw_local_irq_save(__flags); \
60*4882a593Smuzhiyun __ret = *m; \
61*4882a593Smuzhiyun *m = val; \
62*4882a593Smuzhiyun raw_local_irq_restore(__flags); \
63*4882a593Smuzhiyun } \
64*4882a593Smuzhiyun \
65*4882a593Smuzhiyun __ret; \
66*4882a593Smuzhiyun })
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
69*4882a593Smuzhiyun unsigned int size);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun static __always_inline
__xchg(volatile void * ptr,unsigned long x,int size)72*4882a593Smuzhiyun unsigned long __xchg(volatile void *ptr, unsigned long x, int size)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun switch (size) {
75*4882a593Smuzhiyun case 1:
76*4882a593Smuzhiyun case 2:
77*4882a593Smuzhiyun return __xchg_small(ptr, x, size);
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun case 4:
80*4882a593Smuzhiyun return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun case 8:
83*4882a593Smuzhiyun if (!IS_ENABLED(CONFIG_64BIT))
84*4882a593Smuzhiyun return __xchg_called_with_bad_pointer();
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun default:
89*4882a593Smuzhiyun return __xchg_called_with_bad_pointer();
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun #define xchg(ptr, x) \
94*4882a593Smuzhiyun ({ \
95*4882a593Smuzhiyun __typeof__(*(ptr)) __res; \
96*4882a593Smuzhiyun \
97*4882a593Smuzhiyun /* \
98*4882a593Smuzhiyun * In the Loongson3 workaround case __xchg_asm() already \
99*4882a593Smuzhiyun * contains a completion barrier prior to the LL, so we don't \
100*4882a593Smuzhiyun * need to emit an extra one here. \
101*4882a593Smuzhiyun */ \
102*4882a593Smuzhiyun if (__SYNC_loongson3_war == 0) \
103*4882a593Smuzhiyun smp_mb__before_llsc(); \
104*4882a593Smuzhiyun \
105*4882a593Smuzhiyun __res = (__typeof__(*(ptr))) \
106*4882a593Smuzhiyun __xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
107*4882a593Smuzhiyun \
108*4882a593Smuzhiyun smp_llsc_mb(); \
109*4882a593Smuzhiyun \
110*4882a593Smuzhiyun __res; \
111*4882a593Smuzhiyun })
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun #define __cmpxchg_asm(ld, st, m, old, new) \
114*4882a593Smuzhiyun ({ \
115*4882a593Smuzhiyun __typeof(*(m)) __ret; \
116*4882a593Smuzhiyun \
117*4882a593Smuzhiyun if (kernel_uses_llsc) { \
118*4882a593Smuzhiyun __asm__ __volatile__( \
119*4882a593Smuzhiyun " .set push \n" \
120*4882a593Smuzhiyun " .set noat \n" \
121*4882a593Smuzhiyun " .set push \n" \
122*4882a593Smuzhiyun " .set "MIPS_ISA_ARCH_LEVEL" \n" \
123*4882a593Smuzhiyun " " __SYNC(full, loongson3_war) " \n" \
124*4882a593Smuzhiyun "1: " ld " %0, %2 # __cmpxchg_asm \n" \
125*4882a593Smuzhiyun " bne %0, %z3, 2f \n" \
126*4882a593Smuzhiyun " .set pop \n" \
127*4882a593Smuzhiyun " move $1, %z4 \n" \
128*4882a593Smuzhiyun " .set "MIPS_ISA_ARCH_LEVEL" \n" \
129*4882a593Smuzhiyun " " st " $1, %1 \n" \
130*4882a593Smuzhiyun "\t" __SC_BEQZ "$1, 1b \n" \
131*4882a593Smuzhiyun " .set pop \n" \
132*4882a593Smuzhiyun "2: " __SYNC(full, loongson3_war) " \n" \
133*4882a593Smuzhiyun : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
134*4882a593Smuzhiyun : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
135*4882a593Smuzhiyun : __LLSC_CLOBBER); \
136*4882a593Smuzhiyun } else { \
137*4882a593Smuzhiyun unsigned long __flags; \
138*4882a593Smuzhiyun \
139*4882a593Smuzhiyun raw_local_irq_save(__flags); \
140*4882a593Smuzhiyun __ret = *m; \
141*4882a593Smuzhiyun if (__ret == old) \
142*4882a593Smuzhiyun *m = new; \
143*4882a593Smuzhiyun raw_local_irq_restore(__flags); \
144*4882a593Smuzhiyun } \
145*4882a593Smuzhiyun \
146*4882a593Smuzhiyun __ret; \
147*4882a593Smuzhiyun })
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
150*4882a593Smuzhiyun unsigned long new, unsigned int size);
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun static __always_inline
__cmpxchg(volatile void * ptr,unsigned long old,unsigned long new,unsigned int size)153*4882a593Smuzhiyun unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
154*4882a593Smuzhiyun unsigned long new, unsigned int size)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun switch (size) {
157*4882a593Smuzhiyun case 1:
158*4882a593Smuzhiyun case 2:
159*4882a593Smuzhiyun return __cmpxchg_small(ptr, old, new, size);
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun case 4:
162*4882a593Smuzhiyun return __cmpxchg_asm("ll", "sc", (volatile u32 *)ptr,
163*4882a593Smuzhiyun (u32)old, new);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun case 8:
166*4882a593Smuzhiyun /* lld/scd are only available for MIPS64 */
167*4882a593Smuzhiyun if (!IS_ENABLED(CONFIG_64BIT))
168*4882a593Smuzhiyun return __cmpxchg_called_with_bad_pointer();
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun return __cmpxchg_asm("lld", "scd", (volatile u64 *)ptr,
171*4882a593Smuzhiyun (u64)old, new);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun default:
174*4882a593Smuzhiyun return __cmpxchg_called_with_bad_pointer();
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun #define cmpxchg_local(ptr, old, new) \
179*4882a593Smuzhiyun ((__typeof__(*(ptr))) \
180*4882a593Smuzhiyun __cmpxchg((ptr), \
181*4882a593Smuzhiyun (unsigned long)(__typeof__(*(ptr)))(old), \
182*4882a593Smuzhiyun (unsigned long)(__typeof__(*(ptr)))(new), \
183*4882a593Smuzhiyun sizeof(*(ptr))))
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun #define cmpxchg(ptr, old, new) \
186*4882a593Smuzhiyun ({ \
187*4882a593Smuzhiyun __typeof__(*(ptr)) __res; \
188*4882a593Smuzhiyun \
189*4882a593Smuzhiyun /* \
190*4882a593Smuzhiyun * In the Loongson3 workaround case __cmpxchg_asm() already \
191*4882a593Smuzhiyun * contains a completion barrier prior to the LL, so we don't \
192*4882a593Smuzhiyun * need to emit an extra one here. \
193*4882a593Smuzhiyun */ \
194*4882a593Smuzhiyun if (__SYNC_loongson3_war == 0) \
195*4882a593Smuzhiyun smp_mb__before_llsc(); \
196*4882a593Smuzhiyun \
197*4882a593Smuzhiyun __res = cmpxchg_local((ptr), (old), (new)); \
198*4882a593Smuzhiyun \
199*4882a593Smuzhiyun /* \
200*4882a593Smuzhiyun * In the Loongson3 workaround case __cmpxchg_asm() already \
201*4882a593Smuzhiyun * contains a completion barrier after the SC, so we don't \
202*4882a593Smuzhiyun * need to emit an extra one here. \
203*4882a593Smuzhiyun */ \
204*4882a593Smuzhiyun if (__SYNC_loongson3_war == 0) \
205*4882a593Smuzhiyun smp_llsc_mb(); \
206*4882a593Smuzhiyun \
207*4882a593Smuzhiyun __res; \
208*4882a593Smuzhiyun })
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun #ifdef CONFIG_64BIT
211*4882a593Smuzhiyun #define cmpxchg64_local(ptr, o, n) \
212*4882a593Smuzhiyun ({ \
213*4882a593Smuzhiyun BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
214*4882a593Smuzhiyun cmpxchg_local((ptr), (o), (n)); \
215*4882a593Smuzhiyun })
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun #define cmpxchg64(ptr, o, n) \
218*4882a593Smuzhiyun ({ \
219*4882a593Smuzhiyun BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
220*4882a593Smuzhiyun cmpxchg((ptr), (o), (n)); \
221*4882a593Smuzhiyun })
222*4882a593Smuzhiyun #else
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun # include <asm-generic/cmpxchg-local.h>
225*4882a593Smuzhiyun # define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun # ifdef CONFIG_SMP
228*4882a593Smuzhiyun
__cmpxchg64(volatile void * ptr,unsigned long long old,unsigned long long new)229*4882a593Smuzhiyun static inline unsigned long __cmpxchg64(volatile void *ptr,
230*4882a593Smuzhiyun unsigned long long old,
231*4882a593Smuzhiyun unsigned long long new)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun unsigned long long tmp, ret;
234*4882a593Smuzhiyun unsigned long flags;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun /*
237*4882a593Smuzhiyun * The assembly below has to combine 32 bit values into a 64 bit
238*4882a593Smuzhiyun * register, and split 64 bit values from one register into two. If we
239*4882a593Smuzhiyun * were to take an interrupt in the middle of this we'd only save the
240*4882a593Smuzhiyun * least significant 32 bits of each register & probably clobber the
241*4882a593Smuzhiyun * most significant 32 bits of the 64 bit values we're using. In order
242*4882a593Smuzhiyun * to avoid this we must disable interrupts.
243*4882a593Smuzhiyun */
244*4882a593Smuzhiyun local_irq_save(flags);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun asm volatile(
247*4882a593Smuzhiyun " .set push \n"
248*4882a593Smuzhiyun " .set " MIPS_ISA_ARCH_LEVEL " \n"
249*4882a593Smuzhiyun /* Load 64 bits from ptr */
250*4882a593Smuzhiyun " " __SYNC(full, loongson3_war) " \n"
251*4882a593Smuzhiyun "1: lld %L0, %3 # __cmpxchg64 \n"
252*4882a593Smuzhiyun " .set pop \n"
253*4882a593Smuzhiyun /*
254*4882a593Smuzhiyun * Split the 64 bit value we loaded into the 2 registers that hold the
255*4882a593Smuzhiyun * ret variable.
256*4882a593Smuzhiyun */
257*4882a593Smuzhiyun " dsra %M0, %L0, 32 \n"
258*4882a593Smuzhiyun " sll %L0, %L0, 0 \n"
259*4882a593Smuzhiyun /*
260*4882a593Smuzhiyun * Compare ret against old, breaking out of the loop if they don't
261*4882a593Smuzhiyun * match.
262*4882a593Smuzhiyun */
263*4882a593Smuzhiyun " bne %M0, %M4, 2f \n"
264*4882a593Smuzhiyun " bne %L0, %L4, 2f \n"
265*4882a593Smuzhiyun /*
266*4882a593Smuzhiyun * Combine the 32 bit halves from the 2 registers that hold the new
267*4882a593Smuzhiyun * variable into a single 64 bit register.
268*4882a593Smuzhiyun */
269*4882a593Smuzhiyun # if MIPS_ISA_REV >= 2
270*4882a593Smuzhiyun " move %L1, %L5 \n"
271*4882a593Smuzhiyun " dins %L1, %M5, 32, 32 \n"
272*4882a593Smuzhiyun # else
273*4882a593Smuzhiyun " dsll %L1, %L5, 32 \n"
274*4882a593Smuzhiyun " dsrl %L1, %L1, 32 \n"
275*4882a593Smuzhiyun " .set noat \n"
276*4882a593Smuzhiyun " dsll $at, %M5, 32 \n"
277*4882a593Smuzhiyun " or %L1, %L1, $at \n"
278*4882a593Smuzhiyun " .set at \n"
279*4882a593Smuzhiyun # endif
280*4882a593Smuzhiyun " .set push \n"
281*4882a593Smuzhiyun " .set " MIPS_ISA_ARCH_LEVEL " \n"
282*4882a593Smuzhiyun /* Attempt to store new at ptr */
283*4882a593Smuzhiyun " scd %L1, %2 \n"
284*4882a593Smuzhiyun /* If we failed, loop! */
285*4882a593Smuzhiyun "\t" __SC_BEQZ "%L1, 1b \n"
286*4882a593Smuzhiyun "2: " __SYNC(full, loongson3_war) " \n"
287*4882a593Smuzhiyun " .set pop \n"
288*4882a593Smuzhiyun : "=&r"(ret),
289*4882a593Smuzhiyun "=&r"(tmp),
290*4882a593Smuzhiyun "=" GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr)
291*4882a593Smuzhiyun : GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr),
292*4882a593Smuzhiyun "r" (old),
293*4882a593Smuzhiyun "r" (new)
294*4882a593Smuzhiyun : "memory");
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun local_irq_restore(flags);
297*4882a593Smuzhiyun return ret;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun # define cmpxchg64(ptr, o, n) ({ \
301*4882a593Smuzhiyun unsigned long long __old = (__typeof__(*(ptr)))(o); \
302*4882a593Smuzhiyun unsigned long long __new = (__typeof__(*(ptr)))(n); \
303*4882a593Smuzhiyun __typeof__(*(ptr)) __res; \
304*4882a593Smuzhiyun \
305*4882a593Smuzhiyun /* \
306*4882a593Smuzhiyun * We can only use cmpxchg64 if we know that the CPU supports \
307*4882a593Smuzhiyun * 64-bits, ie. lld & scd. Our call to __cmpxchg64_unsupported \
308*4882a593Smuzhiyun * will cause a build error unless cpu_has_64bits is a \
309*4882a593Smuzhiyun * compile-time constant 1. \
310*4882a593Smuzhiyun */ \
311*4882a593Smuzhiyun if (cpu_has_64bits && kernel_uses_llsc) { \
312*4882a593Smuzhiyun smp_mb__before_llsc(); \
313*4882a593Smuzhiyun __res = __cmpxchg64((ptr), __old, __new); \
314*4882a593Smuzhiyun smp_llsc_mb(); \
315*4882a593Smuzhiyun } else { \
316*4882a593Smuzhiyun __res = __cmpxchg64_unsupported(); \
317*4882a593Smuzhiyun } \
318*4882a593Smuzhiyun \
319*4882a593Smuzhiyun __res; \
320*4882a593Smuzhiyun })
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun # else /* !CONFIG_SMP */
323*4882a593Smuzhiyun # define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
324*4882a593Smuzhiyun # endif /* !CONFIG_SMP */
325*4882a593Smuzhiyun #endif /* !CONFIG_64BIT */
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun #endif /* __ASM_CMPXCHG_H */
328