xref: /linux/arch/sh/include/asm/spinlock.h (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
1 /*
2  * include/asm-sh/spinlock.h
3  *
4  * Copyright (C) 2002, 2003 Paul Mundt
5  * Copyright (C) 2006, 2007 Akio Idehara
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  */
11 #ifndef __ASM_SH_SPINLOCK_H
12 #define __ASM_SH_SPINLOCK_H
13 
14 /*
15  * The only locking implemented here uses SH-4A opcodes. For others,
16  * split this out as per atomic-*.h.
17  */
18 #ifndef CONFIG_CPU_SH4A
19 #error "Need movli.l/movco.l for spinlocks"
20 #endif
21 
22 /*
23  * Your basic SMP spinlocks, allowing only a single CPU anywhere
24  */
25 
26 #define arch_spin_is_locked(x)		((x)->lock <= 0)
27 #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
28 #define arch_spin_unlock_wait(x) \
29 	do { while (arch_spin_is_locked(x)) cpu_relax(); } while (0)
30 
31 /*
32  * Simple spin lock operations.  There are two variants, one clears IRQ's
33  * on the local processor, one does not.
34  *
35  * We make no fairness assumptions.  They have a cost.
36  */
37 static inline void arch_spin_lock(arch_spinlock_t *lock)
38 {
39 	unsigned long tmp;
40 	unsigned long oldval;
41 
42 	__asm__ __volatile__ (
43 		"1:						\n\t"
44 		"movli.l	@%2, %0	! arch_spin_lock	\n\t"
45 		"mov		%0, %1				\n\t"
46 		"mov		#0, %0				\n\t"
47 		"movco.l	%0, @%2				\n\t"
48 		"bf		1b				\n\t"
49 		"cmp/pl		%1				\n\t"
50 		"bf		1b				\n\t"
51 		: "=&z" (tmp), "=&r" (oldval)
52 		: "r" (&lock->lock)
53 		: "t", "memory"
54 	);
55 }
56 
57 static inline void arch_spin_unlock(arch_spinlock_t *lock)
58 {
59 	unsigned long tmp;
60 
61 	__asm__ __volatile__ (
62 		"mov		#1, %0 ! arch_spin_unlock	\n\t"
63 		"mov.l		%0, @%1				\n\t"
64 		: "=&z" (tmp)
65 		: "r" (&lock->lock)
66 		: "t", "memory"
67 	);
68 }
69 
70 static inline int arch_spin_trylock(arch_spinlock_t *lock)
71 {
72 	unsigned long tmp, oldval;
73 
74 	__asm__ __volatile__ (
75 		"1:						\n\t"
76 		"movli.l	@%2, %0	! arch_spin_trylock	\n\t"
77 		"mov		%0, %1				\n\t"
78 		"mov		#0, %0				\n\t"
79 		"movco.l	%0, @%2				\n\t"
80 		"bf		1b				\n\t"
81 		"synco						\n\t"
82 		: "=&z" (tmp), "=&r" (oldval)
83 		: "r" (&lock->lock)
84 		: "t", "memory"
85 	);
86 
87 	return oldval;
88 }
89 
90 /*
91  * Read-write spinlocks, allowing multiple readers but only one writer.
92  *
93  * NOTE! it is quite common to have readers in interrupts but no interrupt
94  * writers. For those circumstances we can "mix" irq-safe locks - any writer
95  * needs to get a irq-safe write-lock, but readers can get non-irqsafe
96  * read-locks.
97  */
98 
99 /**
100  * read_can_lock - would read_trylock() succeed?
101  * @lock: the rwlock in question.
102  */
103 #define arch_read_can_lock(x)	((x)->lock > 0)
104 
105 /**
106  * write_can_lock - would write_trylock() succeed?
107  * @lock: the rwlock in question.
108  */
109 #define arch_write_can_lock(x)	((x)->lock == RW_LOCK_BIAS)
110 
111 static inline void arch_read_lock(arch_rwlock_t *rw)
112 {
113 	unsigned long tmp;
114 
115 	__asm__ __volatile__ (
116 		"1:						\n\t"
117 		"movli.l	@%1, %0	! arch_read_lock	\n\t"
118 		"cmp/pl		%0				\n\t"
119 		"bf		1b				\n\t"
120 		"add		#-1, %0				\n\t"
121 		"movco.l	%0, @%1				\n\t"
122 		"bf		1b				\n\t"
123 		: "=&z" (tmp)
124 		: "r" (&rw->lock)
125 		: "t", "memory"
126 	);
127 }
128 
129 static inline void arch_read_unlock(arch_rwlock_t *rw)
130 {
131 	unsigned long tmp;
132 
133 	__asm__ __volatile__ (
134 		"1:						\n\t"
135 		"movli.l	@%1, %0	! arch_read_unlock	\n\t"
136 		"add		#1, %0				\n\t"
137 		"movco.l	%0, @%1				\n\t"
138 		"bf		1b				\n\t"
139 		: "=&z" (tmp)
140 		: "r" (&rw->lock)
141 		: "t", "memory"
142 	);
143 }
144 
145 static inline void arch_write_lock(arch_rwlock_t *rw)
146 {
147 	unsigned long tmp;
148 
149 	__asm__ __volatile__ (
150 		"1:						\n\t"
151 		"movli.l	@%1, %0	! arch_write_lock	\n\t"
152 		"cmp/hs		%2, %0				\n\t"
153 		"bf		1b				\n\t"
154 		"sub		%2, %0				\n\t"
155 		"movco.l	%0, @%1				\n\t"
156 		"bf		1b				\n\t"
157 		: "=&z" (tmp)
158 		: "r" (&rw->lock), "r" (RW_LOCK_BIAS)
159 		: "t", "memory"
160 	);
161 }
162 
163 static inline void arch_write_unlock(arch_rwlock_t *rw)
164 {
165 	__asm__ __volatile__ (
166 		"mov.l		%1, @%0 ! arch_write_unlock	\n\t"
167 		:
168 		: "r" (&rw->lock), "r" (RW_LOCK_BIAS)
169 		: "t", "memory"
170 	);
171 }
172 
173 static inline int arch_read_trylock(arch_rwlock_t *rw)
174 {
175 	unsigned long tmp, oldval;
176 
177 	__asm__ __volatile__ (
178 		"1:						\n\t"
179 		"movli.l	@%2, %0	! arch_read_trylock	\n\t"
180 		"mov		%0, %1				\n\t"
181 		"cmp/pl		%0				\n\t"
182 		"bf		2f				\n\t"
183 		"add		#-1, %0				\n\t"
184 		"movco.l	%0, @%2				\n\t"
185 		"bf		1b				\n\t"
186 		"2:						\n\t"
187 		"synco						\n\t"
188 		: "=&z" (tmp), "=&r" (oldval)
189 		: "r" (&rw->lock)
190 		: "t", "memory"
191 	);
192 
193 	return (oldval > 0);
194 }
195 
196 static inline int arch_write_trylock(arch_rwlock_t *rw)
197 {
198 	unsigned long tmp, oldval;
199 
200 	__asm__ __volatile__ (
201 		"1:						\n\t"
202 		"movli.l	@%2, %0	! arch_write_trylock	\n\t"
203 		"mov		%0, %1				\n\t"
204 		"cmp/hs		%3, %0				\n\t"
205 		"bf		2f				\n\t"
206 		"sub		%3, %0				\n\t"
207 		"2:						\n\t"
208 		"movco.l	%0, @%2				\n\t"
209 		"bf		1b				\n\t"
210 		"synco						\n\t"
211 		: "=&z" (tmp), "=&r" (oldval)
212 		: "r" (&rw->lock), "r" (RW_LOCK_BIAS)
213 		: "t", "memory"
214 	);
215 
216 	return (oldval > (RW_LOCK_BIAS - 1));
217 }
218 
219 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
220 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
221 
222 #define arch_spin_relax(lock)	cpu_relax()
223 #define arch_read_relax(lock)	cpu_relax()
224 #define arch_write_relax(lock)	cpu_relax()
225 
226 #endif /* __ASM_SH_SPINLOCK_H */
227