1#ifndef __ASM_SPINLOCK_H
2#define __ASM_SPINLOCK_H
3
4#if __LINUX_ARM_ARCH__ < 6
5#error SMP not supported on pre-ARMv6 CPUs
6#endif
7
8/*
9 * ARMv6 Spin-locking.
10 *
11 * We exclusively read the old value.  If it is zero, we may have
12 * won the lock, so we try exclusively storing it.  A memory barrier
13 * is required after we get a lock, and before we release it, because
14 * V6 CPUs are assumed to have weakly ordered memory.
15 *
16 * Unlocked value: 0
17 * Locked value: 1
18 */
19
20#define __raw_spin_is_locked(x)		((x)->lock != 0)
21#define __raw_spin_unlock_wait(lock) \
22	do { while (__raw_spin_is_locked(lock)) cpu_relax(); } while (0)
23
24#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
25
26static inline void __raw_spin_lock(raw_spinlock_t *lock)
27{
28	unsigned long tmp;
29
30	__asm__ __volatile__(
31"1:	ldrex	%0, [%1]\n"
32"	teq	%0, #0\n"
33#ifdef CONFIG_CPU_32v6K
34"	wfene\n"
35#endif
36"	strexeq	%0, %2, [%1]\n"
37"	teqeq	%0, #0\n"
38"	bne	1b"
39	: "=&r" (tmp)
40	: "r" (&lock->lock), "r" (1)
41	: "cc");
42
43	smp_mb();
44}
45
46static inline int __raw_spin_trylock(raw_spinlock_t *lock)
47{
48	unsigned long tmp;
49
50	__asm__ __volatile__(
51"	ldrex	%0, [%1]\n"
52"	teq	%0, #0\n"
53"	strexeq	%0, %2, [%1]"
54	: "=&r" (tmp)
55	: "r" (&lock->lock), "r" (1)
56	: "cc");
57
58	if (tmp == 0) {
59		smp_mb();
60		return 1;
61	} else {
62		return 0;
63	}
64}
65
66static inline void __raw_spin_unlock(raw_spinlock_t *lock)
67{
68	smp_mb();
69
70	__asm__ __volatile__(
71"	str	%1, [%0]\n"
72#ifdef CONFIG_CPU_32v6K
73"	mcr	p15, 0, %1, c7, c10, 4\n" /* DSB */
74"	sev"
75#endif
76	:
77	: "r" (&lock->lock), "r" (0)
78	: "cc");
79}
80
81/*
82 * RWLOCKS
83 *
84 *
85 * Write locks are easy - we just set bit 31.  When unlocking, we can
86 * just write zero since the lock is exclusively held.
87 */
88
89static inline void __raw_write_lock(raw_rwlock_t *rw)
90{
91	unsigned long tmp;
92
93	__asm__ __volatile__(
94"1:	ldrex	%0, [%1]\n"
95"	teq	%0, #0\n"
96#ifdef CONFIG_CPU_32v6K
97"	wfene\n"
98#endif
99"	strexeq	%0, %2, [%1]\n"
100"	teq	%0, #0\n"
101"	bne	1b"
102	: "=&r" (tmp)
103	: "r" (&rw->lock), "r" (0x80000000)
104	: "cc");
105
106	smp_mb();
107}
108
109static inline int __raw_write_trylock(raw_rwlock_t *rw)
110{
111	unsigned long tmp;
112
113	__asm__ __volatile__(
114"1:	ldrex	%0, [%1]\n"
115"	teq	%0, #0\n"
116"	strexeq	%0, %2, [%1]"
117	: "=&r" (tmp)
118	: "r" (&rw->lock), "r" (0x80000000)
119	: "cc");
120
121	if (tmp == 0) {
122		smp_mb();
123		return 1;
124	} else {
125		return 0;
126	}
127}
128
129static inline void __raw_write_unlock(raw_rwlock_t *rw)
130{
131	smp_mb();
132
133	__asm__ __volatile__(
134	"str	%1, [%0]\n"
135#ifdef CONFIG_CPU_32v6K
136"	mcr	p15, 0, %1, c7, c10, 4\n" /* DSB */
137"	sev\n"
138#endif
139	:
140	: "r" (&rw->lock), "r" (0)
141	: "cc");
142}
143
144/* write_can_lock - would write_trylock() succeed? */
145#define __raw_write_can_lock(x)		((x)->lock == 0x80000000)
146
147/*
148 * Read locks are a bit more hairy:
149 *  - Exclusively load the lock value.
150 *  - Increment it.
151 *  - Store new lock value if positive, and we still own this location.
152 *    If the value is negative, we've already failed.
153 *  - If we failed to store the value, we want a negative result.
154 *  - If we failed, try again.
155 * Unlocking is similarly hairy.  We may have multiple read locks
156 * currently active.  However, we know we won't have any write
157 * locks.
158 */
159static inline void __raw_read_lock(raw_rwlock_t *rw)
160{
161	unsigned long tmp, tmp2;
162
163	__asm__ __volatile__(
164"1:	ldrex	%0, [%2]\n"
165"	adds	%0, %0, #1\n"
166"	strexpl	%1, %0, [%2]\n"
167#ifdef CONFIG_CPU_32v6K
168"	wfemi\n"
169#endif
170"	rsbpls	%0, %1, #0\n"
171"	bmi	1b"
172	: "=&r" (tmp), "=&r" (tmp2)
173	: "r" (&rw->lock)
174	: "cc");
175
176	smp_mb();
177}
178
179static inline void __raw_read_unlock(raw_rwlock_t *rw)
180{
181	unsigned long tmp, tmp2;
182
183	smp_mb();
184
185	__asm__ __volatile__(
186"1:	ldrex	%0, [%2]\n"
187"	sub	%0, %0, #1\n"
188"	strex	%1, %0, [%2]\n"
189"	teq	%1, #0\n"
190"	bne	1b"
191#ifdef CONFIG_CPU_32v6K
192"\n	cmp	%0, #0\n"
193"	mcreq   p15, 0, %0, c7, c10, 4\n"
194"	seveq"
195#endif
196	: "=&r" (tmp), "=&r" (tmp2)
197	: "r" (&rw->lock)
198	: "cc");
199}
200
201static inline int __raw_read_trylock(raw_rwlock_t *rw)
202{
203	unsigned long tmp, tmp2 = 1;
204
205	__asm__ __volatile__(
206"1:	ldrex	%0, [%2]\n"
207"	adds	%0, %0, #1\n"
208"	strexpl	%1, %0, [%2]\n"
209	: "=&r" (tmp), "+r" (tmp2)
210	: "r" (&rw->lock)
211	: "cc");
212
213	smp_mb();
214	return tmp2 == 0;
215}
216
217/* read_can_lock - would read_trylock() succeed? */
218#define __raw_read_can_lock(x)		((x)->lock < 0x80000000)
219
220#define _raw_spin_relax(lock)	cpu_relax()
221#define _raw_read_relax(lock)	cpu_relax()
222#define _raw_write_relax(lock)	cpu_relax()
223
224#endif /* __ASM_SPINLOCK_H */
225