forked from ~ljy/RK356X_SDK_RELEASE

hc
2024-05-10 9999e48639b3cecb08ffb37358bcba3b48161b29
kernel/arch/mips/include/asm/cmpxchg.h
....@@ -11,18 +11,9 @@
1111 #include <linux/bug.h>
1212 #include <linux/irqflags.h>
1313 #include <asm/compiler.h>
14
+#include <asm/llsc.h>
15
+#include <asm/sync.h>
1416 #include <asm/war.h>
15
-
16
-/*
17
- * Using a branch-likely instruction to check the result of an sc instruction
18
- * works around a bug present in R10000 CPUs prior to revision 3.0 that could
19
- * cause ll-sc sequences to execute non-atomically.
20
- */
21
-#if R10000_LLSC_WAR
22
-# define __scbeqz "beqzl"
23
-#else
24
-# define __scbeqz "beqz"
25
-#endif
2617
2718 /*
2819 * These functions doesn't exist, so if they are called you'll either:
....@@ -36,6 +27,8 @@
3627 */
3728 extern unsigned long __cmpxchg_called_with_bad_pointer(void)
3829 __compiletime_error("Bad argument size for cmpxchg");
30
+extern unsigned long __cmpxchg64_unsupported(void)
31
+ __compiletime_error("cmpxchg64 not available; cpu_has_64bits may be false");
3932 extern unsigned long __xchg_called_with_bad_pointer(void)
4033 __compiletime_error("Bad argument size for xchg");
4134
....@@ -47,17 +40,19 @@
4740 __asm__ __volatile__( \
4841 " .set push \n" \
4942 " .set noat \n" \
43
+ " .set push \n" \
5044 " .set " MIPS_ISA_ARCH_LEVEL " \n" \
45
+ " " __SYNC(full, loongson3_war) " \n" \
5146 "1: " ld " %0, %2 # __xchg_asm \n" \
52
- " .set mips0 \n" \
47
+ " .set pop \n" \
5348 " move $1, %z3 \n" \
5449 " .set " MIPS_ISA_ARCH_LEVEL " \n" \
5550 " " st " $1, %1 \n" \
56
- "\t" __scbeqz " $1, 1b \n" \
51
+ "\t" __SC_BEQZ "$1, 1b \n" \
5752 " .set pop \n" \
5853 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
5954 : GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \
60
- : "memory"); \
55
+ : __LLSC_CLOBBER); \
6156 } else { \
6257 unsigned long __flags; \
6358 \
....@@ -99,7 +94,13 @@
9994 ({ \
10095 __typeof__(*(ptr)) __res; \
10196 \
102
- smp_mb__before_llsc(); \
97
+ /* \
98
+ * In the Loongson3 workaround case __xchg_asm() already \
99
+ * contains a completion barrier prior to the LL, so we don't \
100
+ * need to emit an extra one here. \
101
+ */ \
102
+ if (__SYNC_loongson3_war == 0) \
103
+ smp_mb__before_llsc(); \
103104 \
104105 __res = (__typeof__(*(ptr))) \
105106 __xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
....@@ -117,19 +118,21 @@
117118 __asm__ __volatile__( \
118119 " .set push \n" \
119120 " .set noat \n" \
121
+ " .set push \n" \
120122 " .set "MIPS_ISA_ARCH_LEVEL" \n" \
123
+ " " __SYNC(full, loongson3_war) " \n" \
121124 "1: " ld " %0, %2 # __cmpxchg_asm \n" \
122125 " bne %0, %z3, 2f \n" \
123
- " .set mips0 \n" \
126
+ " .set pop \n" \
124127 " move $1, %z4 \n" \
125128 " .set "MIPS_ISA_ARCH_LEVEL" \n" \
126129 " " st " $1, %1 \n" \
127
- "\t" __scbeqz " $1, 1b \n" \
130
+ "\t" __SC_BEQZ "$1, 1b \n" \
128131 " .set pop \n" \
129
- "2: \n" \
132
+ "2: " __SYNC(full, loongson3_war) " \n" \
130133 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
131
- : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
132
- : "memory"); \
134
+ : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
135
+ : __LLSC_CLOBBER); \
133136 } else { \
134137 unsigned long __flags; \
135138 \
....@@ -183,9 +186,23 @@
183186 ({ \
184187 __typeof__(*(ptr)) __res; \
185188 \
186
- smp_mb__before_llsc(); \
189
+ /* \
190
+ * In the Loongson3 workaround case __cmpxchg_asm() already \
191
+ * contains a completion barrier prior to the LL, so we don't \
192
+ * need to emit an extra one here. \
193
+ */ \
194
+ if (__SYNC_loongson3_war == 0) \
195
+ smp_mb__before_llsc(); \
196
+ \
187197 __res = cmpxchg_local((ptr), (old), (new)); \
188
- smp_llsc_mb(); \
198
+ \
199
+ /* \
200
+ * In the Loongson3 workaround case __cmpxchg_asm() already \
201
+ * contains a completion barrier after the SC, so we don't \
202
+ * need to emit an extra one here. \
203
+ */ \
204
+ if (__SYNC_loongson3_war == 0) \
205
+ smp_llsc_mb(); \
189206 \
190207 __res; \
191208 })
....@@ -203,13 +220,108 @@
203220 cmpxchg((ptr), (o), (n)); \
204221 })
205222 #else
206
-#include <asm-generic/cmpxchg-local.h>
207
-#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
208
-#ifndef CONFIG_SMP
209
-#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
210
-#endif
211
-#endif
212223
213
-#undef __scbeqz
224
+# include <asm-generic/cmpxchg-local.h>
225
+# define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
226
+
227
+# ifdef CONFIG_SMP
228
+
229
+static inline unsigned long __cmpxchg64(volatile void *ptr,
230
+ unsigned long long old,
231
+ unsigned long long new)
232
+{
233
+ unsigned long long tmp, ret;
234
+ unsigned long flags;
235
+
236
+ /*
237
+ * The assembly below has to combine 32 bit values into a 64 bit
238
+ * register, and split 64 bit values from one register into two. If we
239
+ * were to take an interrupt in the middle of this we'd only save the
240
+ * least significant 32 bits of each register & probably clobber the
241
+ * most significant 32 bits of the 64 bit values we're using. In order
242
+ * to avoid this we must disable interrupts.
243
+ */
244
+ local_irq_save(flags);
245
+
246
+ asm volatile(
247
+ " .set push \n"
248
+ " .set " MIPS_ISA_ARCH_LEVEL " \n"
249
+ /* Load 64 bits from ptr */
250
+ " " __SYNC(full, loongson3_war) " \n"
251
+ "1: lld %L0, %3 # __cmpxchg64 \n"
252
+ " .set pop \n"
253
+ /*
254
+ * Split the 64 bit value we loaded into the 2 registers that hold the
255
+ * ret variable.
256
+ */
257
+ " dsra %M0, %L0, 32 \n"
258
+ " sll %L0, %L0, 0 \n"
259
+ /*
260
+ * Compare ret against old, breaking out of the loop if they don't
261
+ * match.
262
+ */
263
+ " bne %M0, %M4, 2f \n"
264
+ " bne %L0, %L4, 2f \n"
265
+ /*
266
+ * Combine the 32 bit halves from the 2 registers that hold the new
267
+ * variable into a single 64 bit register.
268
+ */
269
+# if MIPS_ISA_REV >= 2
270
+ " move %L1, %L5 \n"
271
+ " dins %L1, %M5, 32, 32 \n"
272
+# else
273
+ " dsll %L1, %L5, 32 \n"
274
+ " dsrl %L1, %L1, 32 \n"
275
+ " .set noat \n"
276
+ " dsll $at, %M5, 32 \n"
277
+ " or %L1, %L1, $at \n"
278
+ " .set at \n"
279
+# endif
280
+ " .set push \n"
281
+ " .set " MIPS_ISA_ARCH_LEVEL " \n"
282
+ /* Attempt to store new at ptr */
283
+ " scd %L1, %2 \n"
284
+ /* If we failed, loop! */
285
+ "\t" __SC_BEQZ "%L1, 1b \n"
286
+ "2: " __SYNC(full, loongson3_war) " \n"
287
+ " .set pop \n"
288
+ : "=&r"(ret),
289
+ "=&r"(tmp),
290
+ "=" GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr)
291
+ : GCC_OFF_SMALL_ASM() (*(unsigned long long *)ptr),
292
+ "r" (old),
293
+ "r" (new)
294
+ : "memory");
295
+
296
+ local_irq_restore(flags);
297
+ return ret;
298
+}
299
+
300
+# define cmpxchg64(ptr, o, n) ({ \
301
+ unsigned long long __old = (__typeof__(*(ptr)))(o); \
302
+ unsigned long long __new = (__typeof__(*(ptr)))(n); \
303
+ __typeof__(*(ptr)) __res; \
304
+ \
305
+ /* \
306
+ * We can only use cmpxchg64 if we know that the CPU supports \
307
+ * 64-bits, ie. lld & scd. Our call to __cmpxchg64_unsupported \
308
+ * will cause a build error unless cpu_has_64bits is a \
309
+ * compile-time constant 1. \
310
+ */ \
311
+ if (cpu_has_64bits && kernel_uses_llsc) { \
312
+ smp_mb__before_llsc(); \
313
+ __res = __cmpxchg64((ptr), __old, __new); \
314
+ smp_llsc_mb(); \
315
+ } else { \
316
+ __res = __cmpxchg64_unsupported(); \
317
+ } \
318
+ \
319
+ __res; \
320
+})
321
+
322
+# else /* !CONFIG_SMP */
323
+# define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
324
+# endif /* !CONFIG_SMP */
325
+#endif /* !CONFIG_64BIT */
214326
215327 #endif /* __ASM_CMPXCHG_H */