blob: 3e7afff196cd533da3b124355f2dc872c0ad8635 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 *
Ralf Baechlef65e4fa2006-09-28 01:45:21 +01006 * Copyright (C) 1999, 2000, 06 Ralf Baechle (ralf@linux-mips.org)
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
8 */
9#ifndef _ASM_SPINLOCK_H
10#define _ASM_SPINLOCK_H
11
Ralf Baechle2a31b032008-08-28 15:17:49 +010012#include <linux/compiler.h>
13
Ralf Baechle0004a9d2006-10-31 03:45:07 +000014#include <asm/barrier.h>
Peter Zijlstra726328d2016-05-26 10:35:03 +020015#include <asm/processor.h>
Paul Burton25da4e92017-06-09 17:26:42 -070016#include <asm/qrwlock.h>
Maciej W. Rozyckib0984c42014-11-15 22:08:48 +000017#include <asm/compiler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <asm/war.h>
19
20/*
21 * Your basic SMP spinlocks, allowing only a single CPU anywhere
Ralf Baechle2a31b032008-08-28 15:17:49 +010022 *
Ralf Baechle70342282013-01-22 12:59:30 +010023 * Simple spin lock operations. There are two variants, one clears IRQ's
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 * on the local processor, one does not.
25 *
Ralf Baechle2a31b032008-08-28 15:17:49 +010026 * These are fair FIFO ticket locks
27 *
28 * (the type definitions are in asm/spinlock_types.h)
Linus Torvalds1da177e2005-04-16 15:20:36 -070029 */
30
Ralf Baechle2a31b032008-08-28 15:17:49 +010031
32/*
33 * Ticket locks are conceptually two parts, one indicating the current head of
34 * the queue, and the other indicating the current tail. The lock is acquired
35 * by atomically noting the tail and incrementing it by one (thus adding
36 * ourself to the queue and noting our position), then waiting until the head
37 * becomes equal to the the initial value of the tail.
38 */
39
Thomas Gleixner0199c4e2009-12-02 20:01:25 +010040static inline int arch_spin_is_locked(arch_spinlock_t *lock)
Ralf Baechle2a31b032008-08-28 15:17:49 +010041{
David Daney500c2e12010-02-04 11:31:49 -080042 u32 counters = ACCESS_ONCE(lock->lock);
Ralf Baechle2a31b032008-08-28 15:17:49 +010043
David Daney500c2e12010-02-04 11:31:49 -080044 return ((counters >> 16) ^ counters) & 0xffff;
Ralf Baechle2a31b032008-08-28 15:17:49 +010045}
46
Paul Burton5fac4f72015-07-30 08:16:10 -070047static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
48{
49 return lock.h.serving_now == lock.h.ticket;
50}
51
Thomas Gleixner0199c4e2009-12-02 20:01:25 +010052#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
Peter Zijlstra726328d2016-05-26 10:35:03 +020053
54static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
55{
56 u16 owner = READ_ONCE(lock->h.serving_now);
57 smp_rmb();
58 for (;;) {
59 arch_spinlock_t tmp = READ_ONCE(*lock);
60
61 if (tmp.h.serving_now == tmp.h.ticket ||
62 tmp.h.serving_now != owner)
63 break;
64
65 cpu_relax();
66 }
67 smp_acquire__after_ctrl_dep();
68}
Ralf Baechle2a31b032008-08-28 15:17:49 +010069
Thomas Gleixner0199c4e2009-12-02 20:01:25 +010070static inline int arch_spin_is_contended(arch_spinlock_t *lock)
Ralf Baechle2a31b032008-08-28 15:17:49 +010071{
David Daney500c2e12010-02-04 11:31:49 -080072 u32 counters = ACCESS_ONCE(lock->lock);
Ralf Baechle2a31b032008-08-28 15:17:49 +010073
David Daney500c2e12010-02-04 11:31:49 -080074 return (((counters >> 16) - counters) & 0xffff) > 1;
Ralf Baechle2a31b032008-08-28 15:17:49 +010075}
Thomas Gleixner0199c4e2009-12-02 20:01:25 +010076#define arch_spin_is_contended arch_spin_is_contended
Ralf Baechle2a31b032008-08-28 15:17:49 +010077
Thomas Gleixner0199c4e2009-12-02 20:01:25 +010078static inline void arch_spin_lock(arch_spinlock_t *lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079{
Ralf Baechle2a31b032008-08-28 15:17:49 +010080 int my_ticket;
81 int tmp;
David Daney500c2e12010-02-04 11:31:49 -080082 int inc = 0x10000;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84 if (R10000_LLSC_WAR) {
Ralf Baechle2a31b032008-08-28 15:17:49 +010085 __asm__ __volatile__ (
Thomas Gleixner0199c4e2009-12-02 20:01:25 +010086 " .set push # arch_spin_lock \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +010087 " .set noreorder \n"
88 " \n"
89 "1: ll %[ticket], %[ticket_ptr] \n"
David Daney500c2e12010-02-04 11:31:49 -080090 " addu %[my_ticket], %[ticket], %[inc] \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +010091 " sc %[my_ticket], %[ticket_ptr] \n"
92 " beqzl %[my_ticket], 1b \n"
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 " nop \n"
David Daney500c2e12010-02-04 11:31:49 -080094 " srl %[my_ticket], %[ticket], 16 \n"
95 " andi %[ticket], %[ticket], 0xffff \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +010096 " bne %[ticket], %[my_ticket], 4f \n"
97 " subu %[ticket], %[my_ticket], %[ticket] \n"
98 "2: \n"
Ralf Baechlef65e4fa2006-09-28 01:45:21 +010099 " .subsection 2 \n"
David Daney500c2e12010-02-04 11:31:49 -0800100 "4: andi %[ticket], %[ticket], 0xffff \n"
David Daney0e6826c2009-03-27 10:07:02 -0700101 " sll %[ticket], 5 \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100102 " \n"
103 "6: bnez %[ticket], 6b \n"
104 " subu %[ticket], 1 \n"
105 " \n"
David Daney500c2e12010-02-04 11:31:49 -0800106 " lhu %[ticket], %[serving_now_ptr] \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100107 " beq %[ticket], %[my_ticket], 2b \n"
108 " subu %[ticket], %[my_ticket], %[ticket] \n"
David Daney0e6826c2009-03-27 10:07:02 -0700109 " b 4b \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100110 " subu %[ticket], %[ticket], 1 \n"
Ralf Baechlef65e4fa2006-09-28 01:45:21 +0100111 " .previous \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100112 " .set pop \n"
Markos Chandras94bfb752015-01-26 12:44:11 +0000113 : [ticket_ptr] "+" GCC_OFF_SMALL_ASM() (lock->lock),
David Daney500c2e12010-02-04 11:31:49 -0800114 [serving_now_ptr] "+m" (lock->h.serving_now),
Ralf Baechle2a31b032008-08-28 15:17:49 +0100115 [ticket] "=&r" (tmp),
David Daney500c2e12010-02-04 11:31:49 -0800116 [my_ticket] "=&r" (my_ticket)
117 : [inc] "r" (inc));
Ralf Baechle2a31b032008-08-28 15:17:49 +0100118 } else {
119 __asm__ __volatile__ (
Thomas Gleixner0199c4e2009-12-02 20:01:25 +0100120 " .set push # arch_spin_lock \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100121 " .set noreorder \n"
122 " \n"
David Daney500c2e12010-02-04 11:31:49 -0800123 "1: ll %[ticket], %[ticket_ptr] \n"
124 " addu %[my_ticket], %[ticket], %[inc] \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100125 " sc %[my_ticket], %[ticket_ptr] \n"
David Daney500c2e12010-02-04 11:31:49 -0800126 " beqz %[my_ticket], 1b \n"
127 " srl %[my_ticket], %[ticket], 16 \n"
128 " andi %[ticket], %[ticket], 0xffff \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100129 " bne %[ticket], %[my_ticket], 4f \n"
130 " subu %[ticket], %[my_ticket], %[ticket] \n"
Paul Burton4b5347a2017-02-23 14:50:24 +0000131 "2: .insn \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100132 " .subsection 2 \n"
Markos Chandras9ff897c2015-04-20 10:54:34 +0100133 "4: andi %[ticket], %[ticket], 0xffff \n"
David Daney0e6826c2009-03-27 10:07:02 -0700134 " sll %[ticket], 5 \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100135 " \n"
136 "6: bnez %[ticket], 6b \n"
137 " subu %[ticket], 1 \n"
138 " \n"
David Daney500c2e12010-02-04 11:31:49 -0800139 " lhu %[ticket], %[serving_now_ptr] \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100140 " beq %[ticket], %[my_ticket], 2b \n"
141 " subu %[ticket], %[my_ticket], %[ticket] \n"
David Daney0e6826c2009-03-27 10:07:02 -0700142 " b 4b \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100143 " subu %[ticket], %[ticket], 1 \n"
144 " .previous \n"
145 " .set pop \n"
Markos Chandras94bfb752015-01-26 12:44:11 +0000146 : [ticket_ptr] "+" GCC_OFF_SMALL_ASM() (lock->lock),
David Daney500c2e12010-02-04 11:31:49 -0800147 [serving_now_ptr] "+m" (lock->h.serving_now),
Ralf Baechle2a31b032008-08-28 15:17:49 +0100148 [ticket] "=&r" (tmp),
David Daney500c2e12010-02-04 11:31:49 -0800149 [my_ticket] "=&r" (my_ticket)
150 : [inc] "r" (inc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 }
Ralf Baechle0004a9d2006-10-31 03:45:07 +0000152
Ralf Baechle17099b12007-07-14 13:24:05 +0100153 smp_llsc_mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154}
155
Thomas Gleixner0199c4e2009-12-02 20:01:25 +0100156static inline void arch_spin_unlock(arch_spinlock_t *lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
David Daney500c2e12010-02-04 11:31:49 -0800158 unsigned int serving_now = lock->h.serving_now + 1;
159 wmb();
160 lock->h.serving_now = (u16)serving_now;
161 nudge_writes();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
Thomas Gleixner0199c4e2009-12-02 20:01:25 +0100164static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
Ralf Baechle2a31b032008-08-28 15:17:49 +0100166 int tmp, tmp2, tmp3;
David Daney500c2e12010-02-04 11:31:49 -0800167 int inc = 0x10000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
169 if (R10000_LLSC_WAR) {
Ralf Baechle2a31b032008-08-28 15:17:49 +0100170 __asm__ __volatile__ (
Thomas Gleixner0199c4e2009-12-02 20:01:25 +0100171 " .set push # arch_spin_trylock \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100172 " .set noreorder \n"
173 " \n"
174 "1: ll %[ticket], %[ticket_ptr] \n"
David Daney500c2e12010-02-04 11:31:49 -0800175 " srl %[my_ticket], %[ticket], 16 \n"
David Daney500c2e12010-02-04 11:31:49 -0800176 " andi %[now_serving], %[ticket], 0xffff \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100177 " bne %[my_ticket], %[now_serving], 3f \n"
David Daney500c2e12010-02-04 11:31:49 -0800178 " addu %[ticket], %[ticket], %[inc] \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100179 " sc %[ticket], %[ticket_ptr] \n"
180 " beqzl %[ticket], 1b \n"
181 " li %[ticket], 1 \n"
182 "2: \n"
Ralf Baechlef65e4fa2006-09-28 01:45:21 +0100183 " .subsection 2 \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100184 "3: b 2b \n"
185 " li %[ticket], 0 \n"
Ralf Baechlef65e4fa2006-09-28 01:45:21 +0100186 " .previous \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100187 " .set pop \n"
Markos Chandras94bfb752015-01-26 12:44:11 +0000188 : [ticket_ptr] "+" GCC_OFF_SMALL_ASM() (lock->lock),
Ralf Baechle2a31b032008-08-28 15:17:49 +0100189 [ticket] "=&r" (tmp),
190 [my_ticket] "=&r" (tmp2),
David Daney500c2e12010-02-04 11:31:49 -0800191 [now_serving] "=&r" (tmp3)
192 : [inc] "r" (inc));
Ralf Baechle2a31b032008-08-28 15:17:49 +0100193 } else {
194 __asm__ __volatile__ (
Thomas Gleixner0199c4e2009-12-02 20:01:25 +0100195 " .set push # arch_spin_trylock \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100196 " .set noreorder \n"
197 " \n"
David Daney500c2e12010-02-04 11:31:49 -0800198 "1: ll %[ticket], %[ticket_ptr] \n"
199 " srl %[my_ticket], %[ticket], 16 \n"
David Daney500c2e12010-02-04 11:31:49 -0800200 " andi %[now_serving], %[ticket], 0xffff \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100201 " bne %[my_ticket], %[now_serving], 3f \n"
David Daney500c2e12010-02-04 11:31:49 -0800202 " addu %[ticket], %[ticket], %[inc] \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100203 " sc %[ticket], %[ticket_ptr] \n"
David Daney500c2e12010-02-04 11:31:49 -0800204 " beqz %[ticket], 1b \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100205 " li %[ticket], 1 \n"
Paul Burton4b5347a2017-02-23 14:50:24 +0000206 "2: .insn \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100207 " .subsection 2 \n"
208 "3: b 2b \n"
209 " li %[ticket], 0 \n"
Ralf Baechle2a31b032008-08-28 15:17:49 +0100210 " .previous \n"
211 " .set pop \n"
Markos Chandras94bfb752015-01-26 12:44:11 +0000212 : [ticket_ptr] "+" GCC_OFF_SMALL_ASM() (lock->lock),
Ralf Baechle2a31b032008-08-28 15:17:49 +0100213 [ticket] "=&r" (tmp),
214 [my_ticket] "=&r" (tmp2),
David Daney500c2e12010-02-04 11:31:49 -0800215 [now_serving] "=&r" (tmp3)
216 : [inc] "r" (inc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 }
218
Ralf Baechle17099b12007-07-14 13:24:05 +0100219 smp_llsc_mb();
Ralf Baechle0004a9d2006-10-31 03:45:07 +0000220
Ralf Baechle2a31b032008-08-28 15:17:49 +0100221 return tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222}
223
Thomas Gleixnere5931942009-12-03 20:08:46 +0100224#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
225#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
Ralf Baechle65316fd2006-08-31 14:16:06 +0100226
Thomas Gleixner0199c4e2009-12-02 20:01:25 +0100227#define arch_spin_relax(lock) cpu_relax()
228#define arch_read_relax(lock) cpu_relax()
229#define arch_write_relax(lock) cpu_relax()
Martin Schwidefskyef6edc92006-09-30 23:27:43 -0700230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231#endif /* _ASM_SPINLOCK_H */