1 /* spinlock.h: 32-bit Sparc spinlock support.
2  *
3  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
4  */
5 
6 #ifndef __SPARC_SPINLOCK_H
7 #define __SPARC_SPINLOCK_H
8 
9 #include <linux/threads.h>	/* For NR_CPUS */
10 
11 #ifndef __ASSEMBLY__
12 
13 #include <asm/psr.h>
14 
15 #define __raw_spin_is_locked(lock) (*((volatile unsigned char *)(lock)) != 0)
16 
17 #define __raw_spin_unlock_wait(lock) \
18 	do { while (__raw_spin_is_locked(lock)) cpu_relax(); } while (0)
19 
20 static inline void __raw_spin_lock(raw_spinlock_t *lock)
21 {
22 	__asm__ __volatile__(
23 	"\n1:\n\t"
24 	"ldstub	[%0], %%g2\n\t"
25 	"orcc	%%g2, 0x0, %%g0\n\t"
26 	"bne,a	2f\n\t"
27 	" ldub	[%0], %%g2\n\t"
28 	".subsection	2\n"
29 	"2:\n\t"
30 	"orcc	%%g2, 0x0, %%g0\n\t"
31 	"bne,a	2b\n\t"
32 	" ldub	[%0], %%g2\n\t"
33 	"b,a	1b\n\t"
34 	".previous\n"
35 	: /* no outputs */
36 	: "r" (lock)
37 	: "g2", "memory", "cc");
38 }
39 
40 static inline int __raw_spin_trylock(raw_spinlock_t *lock)
41 {
42 	unsigned int result;
43 	__asm__ __volatile__("ldstub [%1], %0"
44 			     : "=r" (result)
45 			     : "r" (lock)
46 			     : "memory");
47 	return (result == 0);
48 }
49 
50 static inline void __raw_spin_unlock(raw_spinlock_t *lock)
51 {
52 	__asm__ __volatile__("stb %%g0, [%0]" : : "r" (lock) : "memory");
53 }
54 
55 /* Read-write spinlocks, allowing multiple readers
56  * but only one writer.
57  *
58  * NOTE! it is quite common to have readers in interrupts
59  * but no interrupt writers. For those circumstances we
60  * can "mix" irq-safe locks - any writer needs to get a
61  * irq-safe write-lock, but readers can get non-irqsafe
62  * read-locks.
63  *
64  * XXX This might create some problems with my dual spinlock
65  * XXX scheme, deadlocks etc. -DaveM
66  *
67  * Sort of like atomic_t's on Sparc, but even more clever.
68  *
69  *	------------------------------------
70  *	| 24-bit counter           | wlock |  raw_rwlock_t
71  *	------------------------------------
72  *	 31                       8 7     0
73  *
74  * wlock signifies the one writer is in or somebody is updating
75  * counter. For a writer, if he successfully acquires the wlock,
76  * but counter is non-zero, he has to release the lock and wait,
77  * till both counter and wlock are zero.
78  *
79  * Unfortunately this scheme limits us to ~16,000,000 cpus.
80  */
81 static inline void __read_lock(raw_rwlock_t *rw)
82 {
83 	register raw_rwlock_t *lp asm("g1");
84 	lp = rw;
85 	__asm__ __volatile__(
86 	"mov	%%o7, %%g4\n\t"
87 	"call	___rw_read_enter\n\t"
88 	" ldstub	[%%g1 + 3], %%g2\n"
89 	: /* no outputs */
90 	: "r" (lp)
91 	: "g2", "g4", "memory", "cc");
92 }
93 
94 #define __raw_read_lock(lock) \
95 do {	unsigned long flags; \
96 	local_irq_save(flags); \
97 	__read_lock(lock); \
98 	local_irq_restore(flags); \
99 } while(0)
100 
101 static inline void __read_unlock(raw_rwlock_t *rw)
102 {
103 	register raw_rwlock_t *lp asm("g1");
104 	lp = rw;
105 	__asm__ __volatile__(
106 	"mov	%%o7, %%g4\n\t"
107 	"call	___rw_read_exit\n\t"
108 	" ldstub	[%%g1 + 3], %%g2\n"
109 	: /* no outputs */
110 	: "r" (lp)
111 	: "g2", "g4", "memory", "cc");
112 }
113 
114 #define __raw_read_unlock(lock) \
115 do {	unsigned long flags; \
116 	local_irq_save(flags); \
117 	__read_unlock(lock); \
118 	local_irq_restore(flags); \
119 } while(0)
120 
121 static inline void __raw_write_lock(raw_rwlock_t *rw)
122 {
123 	register raw_rwlock_t *lp asm("g1");
124 	lp = rw;
125 	__asm__ __volatile__(
126 	"mov	%%o7, %%g4\n\t"
127 	"call	___rw_write_enter\n\t"
128 	" ldstub	[%%g1 + 3], %%g2\n"
129 	: /* no outputs */
130 	: "r" (lp)
131 	: "g2", "g4", "memory", "cc");
132 	*(volatile __u32 *)&lp->lock = ~0U;
133 }
134 
135 static inline int __raw_write_trylock(raw_rwlock_t *rw)
136 {
137 	unsigned int val;
138 
139 	__asm__ __volatile__("ldstub [%1 + 3], %0"
140 			     : "=r" (val)
141 			     : "r" (&rw->lock)
142 			     : "memory");
143 
144 	if (val == 0) {
145 		val = rw->lock & ~0xff;
146 		if (val)
147 			((volatile u8*)&rw->lock)[3] = 0;
148 		else
149 			*(volatile u32*)&rw->lock = ~0U;
150 	}
151 
152 	return (val == 0);
153 }
154 
155 static inline int __read_trylock(raw_rwlock_t *rw)
156 {
157 	register raw_rwlock_t *lp asm("g1");
158 	register int res asm("o0");
159 	lp = rw;
160 	__asm__ __volatile__(
161 	"mov	%%o7, %%g4\n\t"
162 	"call	___rw_read_try\n\t"
163 	" ldstub	[%%g1 + 3], %%g2\n"
164 	: "=r" (res)
165 	: "r" (lp)
166 	: "g2", "g4", "memory", "cc");
167 	return res;
168 }
169 
170 #define __raw_read_trylock(lock) \
171 ({	unsigned long flags; \
172 	int res; \
173 	local_irq_save(flags); \
174 	res = __read_trylock(lock); \
175 	local_irq_restore(flags); \
176 	res; \
177 })
178 
179 #define __raw_write_unlock(rw)	do { (rw)->lock = 0; } while(0)
180 
181 #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
182 
183 #define _raw_spin_relax(lock)	cpu_relax()
184 #define _raw_read_relax(lock)	cpu_relax()
185 #define _raw_write_relax(lock)	cpu_relax()
186 
187 #define __raw_read_can_lock(rw) (!((rw)->lock & 0xff))
188 #define __raw_write_can_lock(rw) (!(rw)->lock)
189 
190 #endif /* !(__ASSEMBLY__) */
191 
192 #endif /* __SPARC_SPINLOCK_H */
193