xref: /openbmc/linux/arch/arm64/include/asm/spinlock.h (revision 28efb0046512e8a13ed9f9bdf0d68d10bbfbe9cf)
1 /*
2  * Copyright (C) 2012 ARM Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 #ifndef __ASM_SPINLOCK_H
17 #define __ASM_SPINLOCK_H
18 
19 #include <asm/lse.h>
20 #include <asm/spinlock_types.h>
21 #include <asm/processor.h>
22 
23 /*
24  * Spinlock implementation.
25  *
26  * The memory barriers are implicit with the load-acquire and store-release
27  * instructions.
28  */
29 
30 #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
31 
32 static inline void arch_spin_lock(arch_spinlock_t *lock)
33 {
34 	unsigned int tmp;
35 	arch_spinlock_t lockval, newval;
36 
37 	asm volatile(
38 	/* Atomically increment the next ticket. */
39 	ARM64_LSE_ATOMIC_INSN(
40 	/* LL/SC */
41 "	prfm	pstl1strm, %3\n"
42 "1:	ldaxr	%w0, %3\n"
43 "	add	%w1, %w0, %w5\n"
44 "	stxr	%w2, %w1, %3\n"
45 "	cbnz	%w2, 1b\n",
46 	/* LSE atomics */
47 "	mov	%w2, %w5\n"
48 "	ldadda	%w2, %w0, %3\n"
49 	__nops(3)
50 	)
51 
52 	/* Did we get the lock? */
53 "	eor	%w1, %w0, %w0, ror #16\n"
54 "	cbz	%w1, 3f\n"
55 	/*
56 	 * No: spin on the owner. Send a local event to avoid missing an
57 	 * unlock before the exclusive load.
58 	 */
59 "	sevl\n"
60 "2:	wfe\n"
61 "	ldaxrh	%w2, %4\n"
62 "	eor	%w1, %w2, %w0, lsr #16\n"
63 "	cbnz	%w1, 2b\n"
64 	/* We got the lock. Critical section starts here. */
65 "3:"
66 	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
67 	: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
68 	: "memory");
69 }
70 
71 static inline int arch_spin_trylock(arch_spinlock_t *lock)
72 {
73 	unsigned int tmp;
74 	arch_spinlock_t lockval;
75 
76 	asm volatile(ARM64_LSE_ATOMIC_INSN(
77 	/* LL/SC */
78 	"	prfm	pstl1strm, %2\n"
79 	"1:	ldaxr	%w0, %2\n"
80 	"	eor	%w1, %w0, %w0, ror #16\n"
81 	"	cbnz	%w1, 2f\n"
82 	"	add	%w0, %w0, %3\n"
83 	"	stxr	%w1, %w0, %2\n"
84 	"	cbnz	%w1, 1b\n"
85 	"2:",
86 	/* LSE atomics */
87 	"	ldr	%w0, %2\n"
88 	"	eor	%w1, %w0, %w0, ror #16\n"
89 	"	cbnz	%w1, 1f\n"
90 	"	add	%w1, %w0, %3\n"
91 	"	casa	%w0, %w1, %2\n"
92 	"	and	%w1, %w1, #0xffff\n"
93 	"	eor	%w1, %w1, %w0, lsr #16\n"
94 	"1:")
95 	: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
96 	: "I" (1 << TICKET_SHIFT)
97 	: "memory");
98 
99 	return !tmp;
100 }
101 
102 static inline void arch_spin_unlock(arch_spinlock_t *lock)
103 {
104 	unsigned long tmp;
105 
106 	asm volatile(ARM64_LSE_ATOMIC_INSN(
107 	/* LL/SC */
108 	"	ldrh	%w1, %0\n"
109 	"	add	%w1, %w1, #1\n"
110 	"	stlrh	%w1, %0",
111 	/* LSE atomics */
112 	"	mov	%w1, #1\n"
113 	"	staddlh	%w1, %0\n"
114 	__nops(1))
115 	: "=Q" (lock->owner), "=&r" (tmp)
116 	:
117 	: "memory");
118 }
119 
120 static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
121 {
122 	return lock.owner == lock.next;
123 }
124 
125 static inline int arch_spin_is_locked(arch_spinlock_t *lock)
126 {
127 	/*
128 	 * Ensure prior spin_lock operations to other locks have completed
129 	 * on this CPU before we test whether "lock" is locked.
130 	 */
131 	smp_mb(); /* ^^^ */
132 	return !arch_spin_value_unlocked(READ_ONCE(*lock));
133 }
134 
135 static inline int arch_spin_is_contended(arch_spinlock_t *lock)
136 {
137 	arch_spinlock_t lockval = READ_ONCE(*lock);
138 	return (lockval.next - lockval.owner) > 1;
139 }
140 #define arch_spin_is_contended	arch_spin_is_contended
141 
142 /*
143  * Write lock implementation.
144  *
145  * Write locks set bit 31. Unlocking, is done by writing 0 since the lock is
146  * exclusively held.
147  *
148  * The memory barriers are implicit with the load-acquire and store-release
149  * instructions.
150  */
151 
152 static inline void arch_write_lock(arch_rwlock_t *rw)
153 {
154 	unsigned int tmp;
155 
156 	asm volatile(ARM64_LSE_ATOMIC_INSN(
157 	/* LL/SC */
158 	"	sevl\n"
159 	"1:	wfe\n"
160 	"2:	ldaxr	%w0, %1\n"
161 	"	cbnz	%w0, 1b\n"
162 	"	stxr	%w0, %w2, %1\n"
163 	"	cbnz	%w0, 2b\n"
164 	__nops(1),
165 	/* LSE atomics */
166 	"1:	mov	%w0, wzr\n"
167 	"2:	casa	%w0, %w2, %1\n"
168 	"	cbz	%w0, 3f\n"
169 	"	ldxr	%w0, %1\n"
170 	"	cbz	%w0, 2b\n"
171 	"	wfe\n"
172 	"	b	1b\n"
173 	"3:")
174 	: "=&r" (tmp), "+Q" (rw->lock)
175 	: "r" (0x80000000)
176 	: "memory");
177 }
178 
179 static inline int arch_write_trylock(arch_rwlock_t *rw)
180 {
181 	unsigned int tmp;
182 
183 	asm volatile(ARM64_LSE_ATOMIC_INSN(
184 	/* LL/SC */
185 	"1:	ldaxr	%w0, %1\n"
186 	"	cbnz	%w0, 2f\n"
187 	"	stxr	%w0, %w2, %1\n"
188 	"	cbnz	%w0, 1b\n"
189 	"2:",
190 	/* LSE atomics */
191 	"	mov	%w0, wzr\n"
192 	"	casa	%w0, %w2, %1\n"
193 	__nops(2))
194 	: "=&r" (tmp), "+Q" (rw->lock)
195 	: "r" (0x80000000)
196 	: "memory");
197 
198 	return !tmp;
199 }
200 
201 static inline void arch_write_unlock(arch_rwlock_t *rw)
202 {
203 	asm volatile(ARM64_LSE_ATOMIC_INSN(
204 	"	stlr	wzr, %0",
205 	"	swpl	wzr, wzr, %0")
206 	: "=Q" (rw->lock) :: "memory");
207 }
208 
209 /* write_can_lock - would write_trylock() succeed? */
210 #define arch_write_can_lock(x)		((x)->lock == 0)
211 
212 /*
213  * Read lock implementation.
214  *
215  * It exclusively loads the lock value, increments it and stores the new value
216  * back if positive and the CPU still exclusively owns the location. If the
217  * value is negative, the lock is already held.
218  *
219  * During unlocking there may be multiple active read locks but no write lock.
220  *
221  * The memory barriers are implicit with the load-acquire and store-release
222  * instructions.
223  *
224  * Note that in UNDEFINED cases, such as unlocking a lock twice, the LL/SC
225  * and LSE implementations may exhibit different behaviour (although this
226  * will have no effect on lockdep).
227  */
228 static inline void arch_read_lock(arch_rwlock_t *rw)
229 {
230 	unsigned int tmp, tmp2;
231 
232 	asm volatile(
233 	"	sevl\n"
234 	ARM64_LSE_ATOMIC_INSN(
235 	/* LL/SC */
236 	"1:	wfe\n"
237 	"2:	ldaxr	%w0, %2\n"
238 	"	add	%w0, %w0, #1\n"
239 	"	tbnz	%w0, #31, 1b\n"
240 	"	stxr	%w1, %w0, %2\n"
241 	"	cbnz	%w1, 2b\n"
242 	__nops(1),
243 	/* LSE atomics */
244 	"1:	wfe\n"
245 	"2:	ldxr	%w0, %2\n"
246 	"	adds	%w1, %w0, #1\n"
247 	"	tbnz	%w1, #31, 1b\n"
248 	"	casa	%w0, %w1, %2\n"
249 	"	sbc	%w0, %w1, %w0\n"
250 	"	cbnz	%w0, 2b")
251 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
252 	:
253 	: "cc", "memory");
254 }
255 
256 static inline void arch_read_unlock(arch_rwlock_t *rw)
257 {
258 	unsigned int tmp, tmp2;
259 
260 	asm volatile(ARM64_LSE_ATOMIC_INSN(
261 	/* LL/SC */
262 	"1:	ldxr	%w0, %2\n"
263 	"	sub	%w0, %w0, #1\n"
264 	"	stlxr	%w1, %w0, %2\n"
265 	"	cbnz	%w1, 1b",
266 	/* LSE atomics */
267 	"	movn	%w0, #0\n"
268 	"	staddl	%w0, %2\n"
269 	__nops(2))
270 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
271 	:
272 	: "memory");
273 }
274 
275 static inline int arch_read_trylock(arch_rwlock_t *rw)
276 {
277 	unsigned int tmp, tmp2;
278 
279 	asm volatile(ARM64_LSE_ATOMIC_INSN(
280 	/* LL/SC */
281 	"	mov	%w1, #1\n"
282 	"1:	ldaxr	%w0, %2\n"
283 	"	add	%w0, %w0, #1\n"
284 	"	tbnz	%w0, #31, 2f\n"
285 	"	stxr	%w1, %w0, %2\n"
286 	"	cbnz	%w1, 1b\n"
287 	"2:",
288 	/* LSE atomics */
289 	"	ldr	%w0, %2\n"
290 	"	adds	%w1, %w0, #1\n"
291 	"	tbnz	%w1, #31, 1f\n"
292 	"	casa	%w0, %w1, %2\n"
293 	"	sbc	%w1, %w1, %w0\n"
294 	__nops(1)
295 	"1:")
296 	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
297 	:
298 	: "cc", "memory");
299 
300 	return !tmp2;
301 }
302 
303 /* read_can_lock - would read_trylock() succeed? */
304 #define arch_read_can_lock(x)		((x)->lock < 0x80000000)
305 
306 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
307 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
308 
309 #define arch_spin_relax(lock)	cpu_relax()
310 #define arch_read_relax(lock)	cpu_relax()
311 #define arch_write_relax(lock)	cpu_relax()
312 
313 /* See include/linux/spinlock.h */
314 #define smp_mb__after_spinlock()	smp_mb()
315 
316 #endif /* __ASM_SPINLOCK_H */
317