blob: e9c1e97bc29da63ff50099b08e021dcd6c30c04f [file] [log] [blame]
Ralf Baechlefef74702007-10-01 04:15:00 +01001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
7 */
8#ifndef __ASM_CMPXCHG_H
9#define __ASM_CMPXCHG_H
10
Aaro Koskinen5520e422012-07-19 09:11:15 +020011#include <linux/bug.h>
Ralf Baechlefef74702007-10-01 04:15:00 +010012#include <linux/irqflags.h>
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +000013#include <asm/compiler.h>
David Howellsb81947c2012-03-28 18:30:02 +010014#include <asm/war.h>
15
Paul Burton6b1e7622017-06-09 17:26:33 -070016/*
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
26
Paul Burtond15dc682017-06-09 17:26:36 -070027/*
28 * These functions doesn't exist, so if they are called you'll either:
29 *
30 * - Get an error at compile-time due to __compiletime_error, if supported by
31 * your compiler.
32 *
33 * or:
34 *
35 * - Get an error at link-time due to the call to the missing function.
36 */
37extern void __cmpxchg_called_with_bad_pointer(void)
38 __compiletime_error("Bad argument size for cmpxchg");
39extern unsigned long __xchg_called_with_bad_pointer(void)
40 __compiletime_error("Bad argument size for xchg");
41
Paul Burton5154f3b2017-06-09 17:26:34 -070042#define __xchg_asm(ld, st, m, val) \
43({ \
44 __typeof(*(m)) __ret; \
45 \
46 if (kernel_uses_llsc) { \
47 __asm__ __volatile__( \
48 " .set push \n" \
49 " .set noat \n" \
50 " .set " MIPS_ISA_ARCH_LEVEL " \n" \
51 "1: " ld " %0, %2 # __xchg_asm \n" \
52 " .set mips0 \n" \
53 " move $1, %z3 \n" \
54 " .set " MIPS_ISA_ARCH_LEVEL " \n" \
55 " " st " $1, %1 \n" \
56 "\t" __scbeqz " $1, 1b \n" \
57 " .set pop \n" \
58 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
59 : GCC_OFF_SMALL_ASM() (*m), "Jr" (val) \
60 : "memory"); \
61 } else { \
62 unsigned long __flags; \
63 \
64 raw_local_irq_save(__flags); \
65 __ret = *m; \
66 *m = val; \
67 raw_local_irq_restore(__flags); \
68 } \
69 \
70 __ret; \
71})
72
David Howellsb81947c2012-03-28 18:30:02 +010073static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
74{
75 switch (size) {
76 case 4:
Paul Burton62c60812017-06-09 17:26:37 -070077 return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x);
78
David Howellsb81947c2012-03-28 18:30:02 +010079 case 8:
Paul Burton62c60812017-06-09 17:26:37 -070080 if (!IS_ENABLED(CONFIG_64BIT))
81 return __xchg_called_with_bad_pointer();
82
83 return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x);
84
Paul Burtond15dc682017-06-09 17:26:36 -070085 default:
86 return __xchg_called_with_bad_pointer();
David Howellsb81947c2012-03-28 18:30:02 +010087 }
David Howellsb81947c2012-03-28 18:30:02 +010088}
89
90#define xchg(ptr, x) \
91({ \
Paul Burton62c60812017-06-09 17:26:37 -070092 __typeof__(*(ptr)) __res; \
93 \
David Howellsb81947c2012-03-28 18:30:02 +010094 BUILD_BUG_ON(sizeof(*(ptr)) & ~0xc); \
95 \
Paul Burton62c60812017-06-09 17:26:37 -070096 smp_mb__before_llsc(); \
97 \
98 __res = (__typeof__(*(ptr))) \
99 __xchg((unsigned long)(x), (ptr), sizeof(*(ptr))); \
100 \
101 smp_llsc_mb(); \
102 \
103 __res; \
David Howellsb81947c2012-03-28 18:30:02 +0100104})
Ralf Baechlefef74702007-10-01 04:15:00 +0100105
Ralf Baechlefef74702007-10-01 04:15:00 +0100106#define __cmpxchg_asm(ld, st, m, old, new) \
107({ \
108 __typeof(*(m)) __ret; \
109 \
Paul Burton6b1e7622017-06-09 17:26:33 -0700110 if (kernel_uses_llsc) { \
Ralf Baechlefef74702007-10-01 04:15:00 +0100111 __asm__ __volatile__( \
112 " .set push \n" \
113 " .set noat \n" \
Markos Chandrasfa998eb2014-11-20 13:31:48 +0000114 " .set "MIPS_ISA_ARCH_LEVEL" \n" \
Ralf Baechle70342282013-01-22 12:59:30 +0100115 "1: " ld " %0, %2 # __cmpxchg_asm \n" \
Ralf Baechlefef74702007-10-01 04:15:00 +0100116 " bne %0, %z3, 2f \n" \
117 " .set mips0 \n" \
118 " move $1, %z4 \n" \
Markos Chandrasfa998eb2014-11-20 13:31:48 +0000119 " .set "MIPS_ISA_ARCH_LEVEL" \n" \
Ralf Baechlefef74702007-10-01 04:15:00 +0100120 " " st " $1, %1 \n" \
Paul Burton6b1e7622017-06-09 17:26:33 -0700121 "\t" __scbeqz " $1, 1b \n" \
Ralf Baechlefef74702007-10-01 04:15:00 +0100122 " .set pop \n" \
Ralf Baechle78373142010-10-29 19:08:24 +0100123 "2: \n" \
Markos Chandras94bfb752015-01-26 12:44:11 +0000124 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
125 : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
Ralf Baechlefef74702007-10-01 04:15:00 +0100126 : "memory"); \
127 } else { \
128 unsigned long __flags; \
129 \
130 raw_local_irq_save(__flags); \
131 __ret = *m; \
132 if (__ret == old) \
133 *m = new; \
134 raw_local_irq_restore(__flags); \
135 } \
136 \
137 __ret; \
138})
139
David Daneyf252ffd2010-01-08 17:17:43 -0800140#define __cmpxchg(ptr, old, new, pre_barrier, post_barrier) \
Ralf Baechlefef74702007-10-01 04:15:00 +0100141({ \
142 __typeof__(ptr) __ptr = (ptr); \
143 __typeof__(*(ptr)) __old = (old); \
144 __typeof__(*(ptr)) __new = (new); \
145 __typeof__(*(ptr)) __res = 0; \
146 \
David Daneyf252ffd2010-01-08 17:17:43 -0800147 pre_barrier; \
Ralf Baechlefef74702007-10-01 04:15:00 +0100148 \
149 switch (sizeof(*(__ptr))) { \
150 case 4: \
Ralf Baechle70342282013-01-22 12:59:30 +0100151 __res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new); \
Ralf Baechlefef74702007-10-01 04:15:00 +0100152 break; \
153 case 8: \
154 if (sizeof(long) == 8) { \
155 __res = __cmpxchg_asm("lld", "scd", __ptr, \
156 __old, __new); \
157 break; \
158 } \
159 default: \
160 __cmpxchg_called_with_bad_pointer(); \
161 break; \
162 } \
163 \
David Daneyf252ffd2010-01-08 17:17:43 -0800164 post_barrier; \
Ralf Baechlefef74702007-10-01 04:15:00 +0100165 \
166 __res; \
167})
168
David Daneyf252ffd2010-01-08 17:17:43 -0800169#define cmpxchg(ptr, old, new) __cmpxchg(ptr, old, new, smp_mb__before_llsc(), smp_llsc_mb())
170#define cmpxchg_local(ptr, old, new) __cmpxchg(ptr, old, new, , )
Ralf Baechlefef74702007-10-01 04:15:00 +0100171
Mathieu Desnoyers3b96a562008-02-07 00:16:09 -0800172#ifdef CONFIG_64BIT
173#define cmpxchg64_local(ptr, o, n) \
174 ({ \
175 BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
176 cmpxchg_local((ptr), (o), (n)); \
177 })
Deng-Cheng Zhue2093c72015-03-07 10:30:20 -0800178
179#define cmpxchg64(ptr, o, n) \
180 ({ \
181 BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
182 cmpxchg((ptr), (o), (n)); \
183 })
Mathieu Desnoyers3b96a562008-02-07 00:16:09 -0800184#else
185#include <asm-generic/cmpxchg-local.h>
186#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
Deng-Cheng Zhue2093c72015-03-07 10:30:20 -0800187#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
Mathieu Desnoyers3b96a562008-02-07 00:16:09 -0800188#endif
189
Paul Burton6b1e7622017-06-09 17:26:33 -0700190#undef __scbeqz
191
Ralf Baechlefef74702007-10-01 04:15:00 +0100192#endif /* __ASM_CMPXCHG_H */