blob: fe652c3e5d8cba5496c00531bea0db45b5a3f184 [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_u32(volatile int * m, unsigned int val)
74{
75 __u32 retval;
76
77 smp_mb__before_llsc();
Paul Burton5154f3b2017-06-09 17:26:34 -070078 retval = __xchg_asm("ll", "sc", m, val);
David Howellsb81947c2012-03-28 18:30:02 +010079 smp_llsc_mb();
80
81 return retval;
82}
83
84#ifdef CONFIG_64BIT
85static inline __u64 __xchg_u64(volatile __u64 * m, __u64 val)
86{
87 __u64 retval;
88
89 smp_mb__before_llsc();
Paul Burton5154f3b2017-06-09 17:26:34 -070090 retval = __xchg_asm("lld", "scd", m, val);
David Howellsb81947c2012-03-28 18:30:02 +010091 smp_llsc_mb();
92
93 return retval;
94}
95#else
96extern __u64 __xchg_u64_unsupported_on_32bit_kernels(volatile __u64 * m, __u64 val);
97#define __xchg_u64 __xchg_u64_unsupported_on_32bit_kernels
98#endif
99
100static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
101{
102 switch (size) {
103 case 4:
104 return __xchg_u32(ptr, x);
105 case 8:
106 return __xchg_u64(ptr, x);
Paul Burtond15dc682017-06-09 17:26:36 -0700107 default:
108 return __xchg_called_with_bad_pointer();
David Howellsb81947c2012-03-28 18:30:02 +0100109 }
David Howellsb81947c2012-03-28 18:30:02 +0100110}
111
112#define xchg(ptr, x) \
113({ \
114 BUILD_BUG_ON(sizeof(*(ptr)) & ~0xc); \
115 \
116 ((__typeof__(*(ptr))) \
117 __xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))); \
118})
Ralf Baechlefef74702007-10-01 04:15:00 +0100119
Ralf Baechlefef74702007-10-01 04:15:00 +0100120#define __cmpxchg_asm(ld, st, m, old, new) \
121({ \
122 __typeof(*(m)) __ret; \
123 \
Paul Burton6b1e7622017-06-09 17:26:33 -0700124 if (kernel_uses_llsc) { \
Ralf Baechlefef74702007-10-01 04:15:00 +0100125 __asm__ __volatile__( \
126 " .set push \n" \
127 " .set noat \n" \
Markos Chandrasfa998eb2014-11-20 13:31:48 +0000128 " .set "MIPS_ISA_ARCH_LEVEL" \n" \
Ralf Baechle70342282013-01-22 12:59:30 +0100129 "1: " ld " %0, %2 # __cmpxchg_asm \n" \
Ralf Baechlefef74702007-10-01 04:15:00 +0100130 " bne %0, %z3, 2f \n" \
131 " .set mips0 \n" \
132 " move $1, %z4 \n" \
Markos Chandrasfa998eb2014-11-20 13:31:48 +0000133 " .set "MIPS_ISA_ARCH_LEVEL" \n" \
Ralf Baechlefef74702007-10-01 04:15:00 +0100134 " " st " $1, %1 \n" \
Paul Burton6b1e7622017-06-09 17:26:33 -0700135 "\t" __scbeqz " $1, 1b \n" \
Ralf Baechlefef74702007-10-01 04:15:00 +0100136 " .set pop \n" \
Ralf Baechle78373142010-10-29 19:08:24 +0100137 "2: \n" \
Markos Chandras94bfb752015-01-26 12:44:11 +0000138 : "=&r" (__ret), "=" GCC_OFF_SMALL_ASM() (*m) \
139 : GCC_OFF_SMALL_ASM() (*m), "Jr" (old), "Jr" (new) \
Ralf Baechlefef74702007-10-01 04:15:00 +0100140 : "memory"); \
141 } else { \
142 unsigned long __flags; \
143 \
144 raw_local_irq_save(__flags); \
145 __ret = *m; \
146 if (__ret == old) \
147 *m = new; \
148 raw_local_irq_restore(__flags); \
149 } \
150 \
151 __ret; \
152})
153
David Daneyf252ffd2010-01-08 17:17:43 -0800154#define __cmpxchg(ptr, old, new, pre_barrier, post_barrier) \
Ralf Baechlefef74702007-10-01 04:15:00 +0100155({ \
156 __typeof__(ptr) __ptr = (ptr); \
157 __typeof__(*(ptr)) __old = (old); \
158 __typeof__(*(ptr)) __new = (new); \
159 __typeof__(*(ptr)) __res = 0; \
160 \
David Daneyf252ffd2010-01-08 17:17:43 -0800161 pre_barrier; \
Ralf Baechlefef74702007-10-01 04:15:00 +0100162 \
163 switch (sizeof(*(__ptr))) { \
164 case 4: \
Ralf Baechle70342282013-01-22 12:59:30 +0100165 __res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new); \
Ralf Baechlefef74702007-10-01 04:15:00 +0100166 break; \
167 case 8: \
168 if (sizeof(long) == 8) { \
169 __res = __cmpxchg_asm("lld", "scd", __ptr, \
170 __old, __new); \
171 break; \
172 } \
173 default: \
174 __cmpxchg_called_with_bad_pointer(); \
175 break; \
176 } \
177 \
David Daneyf252ffd2010-01-08 17:17:43 -0800178 post_barrier; \
Ralf Baechlefef74702007-10-01 04:15:00 +0100179 \
180 __res; \
181})
182
David Daneyf252ffd2010-01-08 17:17:43 -0800183#define cmpxchg(ptr, old, new) __cmpxchg(ptr, old, new, smp_mb__before_llsc(), smp_llsc_mb())
184#define cmpxchg_local(ptr, old, new) __cmpxchg(ptr, old, new, , )
Ralf Baechlefef74702007-10-01 04:15:00 +0100185
Mathieu Desnoyers3b96a562008-02-07 00:16:09 -0800186#ifdef CONFIG_64BIT
187#define cmpxchg64_local(ptr, o, n) \
188 ({ \
189 BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
190 cmpxchg_local((ptr), (o), (n)); \
191 })
Deng-Cheng Zhue2093c72015-03-07 10:30:20 -0800192
193#define cmpxchg64(ptr, o, n) \
194 ({ \
195 BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
196 cmpxchg((ptr), (o), (n)); \
197 })
Mathieu Desnoyers3b96a562008-02-07 00:16:09 -0800198#else
199#include <asm-generic/cmpxchg-local.h>
200#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
Deng-Cheng Zhue2093c72015-03-07 10:30:20 -0800201#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
Mathieu Desnoyers3b96a562008-02-07 00:16:09 -0800202#endif
203
Paul Burton6b1e7622017-06-09 17:26:33 -0700204#undef __scbeqz
205
Ralf Baechlefef74702007-10-01 04:15:00 +0100206#endif /* __ASM_CMPXCHG_H */