blob: aac64d55cb22eb9cb7a166e823ef1a4d27c3b4f1 [file] [log] [blame]
Catalin Marinas08e875c2012-03-05 11:49:30 +00001/*
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
Will Deacon81bb5c62015-02-10 03:03:15 +000019#include <asm/lse.h>
Catalin Marinas08e875c2012-03-05 11:49:30 +000020#include <asm/spinlock_types.h>
21#include <asm/processor.h>
22
23/*
24 * Spinlock implementation.
25 *
Catalin Marinas08e875c2012-03-05 11:49:30 +000026 * The memory barriers are implicit with the load-acquire and store-release
27 * instructions.
Catalin Marinas08e875c2012-03-05 11:49:30 +000028 */
Will Deacond86b8da2015-11-19 17:48:31 +000029static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
30{
31 unsigned int tmp;
32 arch_spinlock_t lockval;
Catalin Marinas08e875c2012-03-05 11:49:30 +000033
Will Deacon38b850a2016-06-02 15:27:04 +010034 /*
35 * Ensure prior spin_lock operations to other locks have completed
36 * on this CPU before we test whether "lock" is locked.
37 */
38 smp_mb();
39
Will Deacond86b8da2015-11-19 17:48:31 +000040 asm volatile(
41" sevl\n"
42"1: wfe\n"
43"2: ldaxr %w0, %2\n"
44" eor %w1, %w0, %w0, ror #16\n"
45" cbnz %w1, 1b\n"
46 ARM64_LSE_ATOMIC_INSN(
47 /* LL/SC */
48" stxr %w1, %w0, %2\n"
49" cbnz %w1, 2b\n", /* Serialise against any concurrent lockers */
50 /* LSE atomics */
51" nop\n"
52" nop\n")
53 : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
54 :
55 : "memory");
56}
Catalin Marinas08e875c2012-03-05 11:49:30 +000057
58#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
59
60static inline void arch_spin_lock(arch_spinlock_t *lock)
61{
62 unsigned int tmp;
Will Deacon52ea2a52013-10-09 15:54:26 +010063 arch_spinlock_t lockval, newval;
Catalin Marinas08e875c2012-03-05 11:49:30 +000064
65 asm volatile(
Will Deacon52ea2a52013-10-09 15:54:26 +010066 /* Atomically increment the next ticket. */
Will Deacon81bb5c62015-02-10 03:03:15 +000067 ARM64_LSE_ATOMIC_INSN(
68 /* LL/SC */
Will Deacon52ea2a52013-10-09 15:54:26 +010069" prfm pstl1strm, %3\n"
70"1: ldaxr %w0, %3\n"
71" add %w1, %w0, %w5\n"
72" stxr %w2, %w1, %3\n"
Will Deacon81bb5c62015-02-10 03:03:15 +000073" cbnz %w2, 1b\n",
74 /* LSE atomics */
75" mov %w2, %w5\n"
76" ldadda %w2, %w0, %3\n"
77" nop\n"
78" nop\n"
79" nop\n"
80 )
81
Will Deacon52ea2a52013-10-09 15:54:26 +010082 /* Did we get the lock? */
83" eor %w1, %w0, %w0, ror #16\n"
84" cbz %w1, 3f\n"
85 /*
86 * No: spin on the owner. Send a local event to avoid missing an
87 * unlock before the exclusive load.
88 */
89" sevl\n"
90"2: wfe\n"
91" ldaxrh %w2, %4\n"
92" eor %w1, %w2, %w0, lsr #16\n"
93" cbnz %w1, 2b\n"
94 /* We got the lock. Critical section starts here. */
95"3:"
96 : "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
97 : "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
98 : "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +000099}
100
101static inline int arch_spin_trylock(arch_spinlock_t *lock)
102{
103 unsigned int tmp;
Will Deacon52ea2a52013-10-09 15:54:26 +0100104 arch_spinlock_t lockval;
Catalin Marinas08e875c2012-03-05 11:49:30 +0000105
Will Deacon81bb5c62015-02-10 03:03:15 +0000106 asm volatile(ARM64_LSE_ATOMIC_INSN(
107 /* LL/SC */
108 " prfm pstl1strm, %2\n"
109 "1: ldaxr %w0, %2\n"
110 " eor %w1, %w0, %w0, ror #16\n"
111 " cbnz %w1, 2f\n"
112 " add %w0, %w0, %3\n"
113 " stxr %w1, %w0, %2\n"
114 " cbnz %w1, 1b\n"
115 "2:",
116 /* LSE atomics */
117 " ldr %w0, %2\n"
118 " eor %w1, %w0, %w0, ror #16\n"
119 " cbnz %w1, 1f\n"
120 " add %w1, %w0, %3\n"
121 " casa %w0, %w1, %2\n"
122 " and %w1, %w1, #0xffff\n"
123 " eor %w1, %w1, %w0, lsr #16\n"
124 "1:")
Will Deacon52ea2a52013-10-09 15:54:26 +0100125 : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
126 : "I" (1 << TICKET_SHIFT)
127 : "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000128
129 return !tmp;
130}
131
132static inline void arch_spin_unlock(arch_spinlock_t *lock)
133{
Will Deacon81bb5c62015-02-10 03:03:15 +0000134 unsigned long tmp;
135
136 asm volatile(ARM64_LSE_ATOMIC_INSN(
137 /* LL/SC */
Will Deaconc1d7cd22015-07-28 14:48:00 +0100138 " ldrh %w1, %0\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000139 " add %w1, %w1, #1\n"
140 " stlrh %w1, %0",
141 /* LSE atomics */
142 " mov %w1, #1\n"
143 " nop\n"
144 " staddlh %w1, %0")
145 : "=Q" (lock->owner), "=&r" (tmp)
146 :
Will Deacon52ea2a52013-10-09 15:54:26 +0100147 : "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000148}
149
Will Deacon5686b062013-10-09 15:54:27 +0100150static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
151{
152 return lock.owner == lock.next;
153}
154
Will Deacon52ea2a52013-10-09 15:54:26 +0100155static inline int arch_spin_is_locked(arch_spinlock_t *lock)
156{
Will Deacon38b850a2016-06-02 15:27:04 +0100157 smp_mb(); /* See arch_spin_unlock_wait */
Christian Borntraegeraf2e7aa2014-11-24 10:53:11 +0100158 return !arch_spin_value_unlocked(READ_ONCE(*lock));
Will Deacon52ea2a52013-10-09 15:54:26 +0100159}
160
161static inline int arch_spin_is_contended(arch_spinlock_t *lock)
162{
Christian Borntraegeraf2e7aa2014-11-24 10:53:11 +0100163 arch_spinlock_t lockval = READ_ONCE(*lock);
Will Deacon52ea2a52013-10-09 15:54:26 +0100164 return (lockval.next - lockval.owner) > 1;
165}
166#define arch_spin_is_contended arch_spin_is_contended
167
Catalin Marinas08e875c2012-03-05 11:49:30 +0000168/*
169 * Write lock implementation.
170 *
171 * Write locks set bit 31. Unlocking, is done by writing 0 since the lock is
172 * exclusively held.
173 *
174 * The memory barriers are implicit with the load-acquire and store-release
175 * instructions.
176 */
177
178static inline void arch_write_lock(arch_rwlock_t *rw)
179{
180 unsigned int tmp;
181
Will Deacon81bb5c62015-02-10 03:03:15 +0000182 asm volatile(ARM64_LSE_ATOMIC_INSN(
183 /* LL/SC */
Catalin Marinas08e875c2012-03-05 11:49:30 +0000184 " sevl\n"
185 "1: wfe\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000186 "2: ldaxr %w0, %1\n"
Catalin Marinas08e875c2012-03-05 11:49:30 +0000187 " cbnz %w0, 1b\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000188 " stxr %w0, %w2, %1\n"
Catalin Marinas08e875c2012-03-05 11:49:30 +0000189 " cbnz %w0, 2b\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000190 " nop",
191 /* LSE atomics */
192 "1: mov %w0, wzr\n"
193 "2: casa %w0, %w2, %1\n"
194 " cbz %w0, 3f\n"
195 " ldxr %w0, %1\n"
196 " cbz %w0, 2b\n"
197 " wfe\n"
198 " b 1b\n"
199 "3:")
Will Deacon3a0310e2013-02-04 12:12:33 +0000200 : "=&r" (tmp), "+Q" (rw->lock)
201 : "r" (0x80000000)
Will Deacon95c41892014-02-04 12:29:13 +0000202 : "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000203}
204
205static inline int arch_write_trylock(arch_rwlock_t *rw)
206{
207 unsigned int tmp;
208
Will Deacon81bb5c62015-02-10 03:03:15 +0000209 asm volatile(ARM64_LSE_ATOMIC_INSN(
210 /* LL/SC */
Will Deacon9511ca12015-07-22 18:25:52 +0100211 "1: ldaxr %w0, %1\n"
212 " cbnz %w0, 2f\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000213 " stxr %w0, %w2, %1\n"
Will Deacon9511ca12015-07-22 18:25:52 +0100214 " cbnz %w0, 1b\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000215 "2:",
216 /* LSE atomics */
217 " mov %w0, wzr\n"
218 " casa %w0, %w2, %1\n"
219 " nop\n"
220 " nop")
Will Deacon3a0310e2013-02-04 12:12:33 +0000221 : "=&r" (tmp), "+Q" (rw->lock)
222 : "r" (0x80000000)
Will Deacon95c41892014-02-04 12:29:13 +0000223 : "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000224
225 return !tmp;
226}
227
228static inline void arch_write_unlock(arch_rwlock_t *rw)
229{
Will Deacon81bb5c62015-02-10 03:03:15 +0000230 asm volatile(ARM64_LSE_ATOMIC_INSN(
231 " stlr wzr, %0",
232 " swpl wzr, wzr, %0")
233 : "=Q" (rw->lock) :: "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000234}
235
236/* write_can_lock - would write_trylock() succeed? */
237#define arch_write_can_lock(x) ((x)->lock == 0)
238
239/*
240 * Read lock implementation.
241 *
242 * It exclusively loads the lock value, increments it and stores the new value
243 * back if positive and the CPU still exclusively owns the location. If the
244 * value is negative, the lock is already held.
245 *
246 * During unlocking there may be multiple active read locks but no write lock.
247 *
248 * The memory barriers are implicit with the load-acquire and store-release
249 * instructions.
Will Deacon81bb5c62015-02-10 03:03:15 +0000250 *
251 * Note that in UNDEFINED cases, such as unlocking a lock twice, the LL/SC
252 * and LSE implementations may exhibit different behaviour (although this
253 * will have no effect on lockdep).
Catalin Marinas08e875c2012-03-05 11:49:30 +0000254 */
255static inline void arch_read_lock(arch_rwlock_t *rw)
256{
257 unsigned int tmp, tmp2;
258
259 asm volatile(
260 " sevl\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000261 ARM64_LSE_ATOMIC_INSN(
262 /* LL/SC */
Catalin Marinas08e875c2012-03-05 11:49:30 +0000263 "1: wfe\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000264 "2: ldaxr %w0, %2\n"
Catalin Marinas08e875c2012-03-05 11:49:30 +0000265 " add %w0, %w0, #1\n"
266 " tbnz %w0, #31, 1b\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000267 " stxr %w1, %w0, %2\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000268 " nop\n"
269 " cbnz %w1, 2b",
270 /* LSE atomics */
271 "1: wfe\n"
272 "2: ldxr %w0, %2\n"
273 " adds %w1, %w0, #1\n"
274 " tbnz %w1, #31, 1b\n"
275 " casa %w0, %w1, %2\n"
276 " sbc %w0, %w1, %w0\n"
277 " cbnz %w0, 2b")
Will Deacon3a0310e2013-02-04 12:12:33 +0000278 : "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
279 :
Will Deacon81bb5c62015-02-10 03:03:15 +0000280 : "cc", "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000281}
282
283static inline void arch_read_unlock(arch_rwlock_t *rw)
284{
285 unsigned int tmp, tmp2;
286
Will Deacon81bb5c62015-02-10 03:03:15 +0000287 asm volatile(ARM64_LSE_ATOMIC_INSN(
288 /* LL/SC */
Will Deacon3a0310e2013-02-04 12:12:33 +0000289 "1: ldxr %w0, %2\n"
Catalin Marinas08e875c2012-03-05 11:49:30 +0000290 " sub %w0, %w0, #1\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000291 " stlxr %w1, %w0, %2\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000292 " cbnz %w1, 1b",
293 /* LSE atomics */
294 " movn %w0, #0\n"
295 " nop\n"
296 " nop\n"
297 " staddl %w0, %2")
Will Deacon3a0310e2013-02-04 12:12:33 +0000298 : "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
299 :
Will Deacon95c41892014-02-04 12:29:13 +0000300 : "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000301}
302
303static inline int arch_read_trylock(arch_rwlock_t *rw)
304{
Will Deacon81bb5c62015-02-10 03:03:15 +0000305 unsigned int tmp, tmp2;
Catalin Marinas08e875c2012-03-05 11:49:30 +0000306
Will Deacon81bb5c62015-02-10 03:03:15 +0000307 asm volatile(ARM64_LSE_ATOMIC_INSN(
308 /* LL/SC */
309 " mov %w1, #1\n"
Will Deacon9511ca12015-07-22 18:25:52 +0100310 "1: ldaxr %w0, %2\n"
Catalin Marinas08e875c2012-03-05 11:49:30 +0000311 " add %w0, %w0, #1\n"
Will Deacon9511ca12015-07-22 18:25:52 +0100312 " tbnz %w0, #31, 2f\n"
Will Deacon3a0310e2013-02-04 12:12:33 +0000313 " stxr %w1, %w0, %2\n"
Will Deacon9511ca12015-07-22 18:25:52 +0100314 " cbnz %w1, 1b\n"
Will Deacon81bb5c62015-02-10 03:03:15 +0000315 "2:",
316 /* LSE atomics */
317 " ldr %w0, %2\n"
318 " adds %w1, %w0, #1\n"
319 " tbnz %w1, #31, 1f\n"
320 " casa %w0, %w1, %2\n"
321 " sbc %w1, %w1, %w0\n"
322 " nop\n"
323 "1:")
324 : "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
Will Deacon3a0310e2013-02-04 12:12:33 +0000325 :
Will Deacon81bb5c62015-02-10 03:03:15 +0000326 : "cc", "memory");
Catalin Marinas08e875c2012-03-05 11:49:30 +0000327
328 return !tmp2;
329}
330
331/* read_can_lock - would read_trylock() succeed? */
332#define arch_read_can_lock(x) ((x)->lock < 0x80000000)
333
334#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
335#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
336
337#define arch_spin_relax(lock) cpu_relax()
338#define arch_read_relax(lock) cpu_relax()
339#define arch_write_relax(lock) cpu_relax()
340
341#endif /* __ASM_SPINLOCK_H */