xref: /OK3568_Linux_fs/kernel/arch/powerpc/include/asm/simple_spinlock.h (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-or-later */
2*4882a593Smuzhiyun #ifndef _ASM_POWERPC_SIMPLE_SPINLOCK_H
3*4882a593Smuzhiyun #define _ASM_POWERPC_SIMPLE_SPINLOCK_H
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun /*
6*4882a593Smuzhiyun  * Simple spin lock operations.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Copyright (C) 2001-2004 Paul Mackerras <paulus@au.ibm.com>, IBM
9*4882a593Smuzhiyun  * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
10*4882a593Smuzhiyun  * Copyright (C) 2002 Dave Engebretsen <engebret@us.ibm.com>, IBM
11*4882a593Smuzhiyun  *	Rework to support virtual processors
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Type of int is used as a full 64b word is not necessary.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * (the type definitions are in asm/simple_spinlock_types.h)
16*4882a593Smuzhiyun  */
17*4882a593Smuzhiyun #include <linux/irqflags.h>
18*4882a593Smuzhiyun #include <asm/paravirt.h>
19*4882a593Smuzhiyun #include <asm/paca.h>
20*4882a593Smuzhiyun #include <asm/synch.h>
21*4882a593Smuzhiyun #include <asm/ppc-opcode.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #ifdef CONFIG_PPC64
24*4882a593Smuzhiyun /* use 0x800000yy when locked, where yy == CPU number */
25*4882a593Smuzhiyun #ifdef __BIG_ENDIAN__
26*4882a593Smuzhiyun #define LOCK_TOKEN	(*(u32 *)(&get_paca()->lock_token))
27*4882a593Smuzhiyun #else
28*4882a593Smuzhiyun #define LOCK_TOKEN	(*(u32 *)(&get_paca()->paca_index))
29*4882a593Smuzhiyun #endif
30*4882a593Smuzhiyun #else
31*4882a593Smuzhiyun #define LOCK_TOKEN	1
32*4882a593Smuzhiyun #endif
33*4882a593Smuzhiyun 
arch_spin_value_unlocked(arch_spinlock_t lock)34*4882a593Smuzhiyun static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	return lock.slock == 0;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun 
arch_spin_is_locked(arch_spinlock_t * lock)39*4882a593Smuzhiyun static inline int arch_spin_is_locked(arch_spinlock_t *lock)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	smp_mb();
42*4882a593Smuzhiyun 	return !arch_spin_value_unlocked(*lock);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /*
46*4882a593Smuzhiyun  * This returns the old value in the lock, so we succeeded
47*4882a593Smuzhiyun  * in getting the lock if the return value is 0.
48*4882a593Smuzhiyun  */
__arch_spin_trylock(arch_spinlock_t * lock)49*4882a593Smuzhiyun static inline unsigned long __arch_spin_trylock(arch_spinlock_t *lock)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	unsigned long tmp, token;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	token = LOCK_TOKEN;
54*4882a593Smuzhiyun 	__asm__ __volatile__(
55*4882a593Smuzhiyun "1:	" PPC_LWARX(%0,0,%2,1) "\n\
56*4882a593Smuzhiyun 	cmpwi		0,%0,0\n\
57*4882a593Smuzhiyun 	bne-		2f\n\
58*4882a593Smuzhiyun 	stwcx.		%1,0,%2\n\
59*4882a593Smuzhiyun 	bne-		1b\n"
60*4882a593Smuzhiyun 	PPC_ACQUIRE_BARRIER
61*4882a593Smuzhiyun "2:"
62*4882a593Smuzhiyun 	: "=&r" (tmp)
63*4882a593Smuzhiyun 	: "r" (token), "r" (&lock->slock)
64*4882a593Smuzhiyun 	: "cr0", "memory");
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	return tmp;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun 
arch_spin_trylock(arch_spinlock_t * lock)69*4882a593Smuzhiyun static inline int arch_spin_trylock(arch_spinlock_t *lock)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	return __arch_spin_trylock(lock) == 0;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun /*
75*4882a593Smuzhiyun  * On a system with shared processors (that is, where a physical
76*4882a593Smuzhiyun  * processor is multiplexed between several virtual processors),
77*4882a593Smuzhiyun  * there is no point spinning on a lock if the holder of the lock
78*4882a593Smuzhiyun  * isn't currently scheduled on a physical processor.  Instead
79*4882a593Smuzhiyun  * we detect this situation and ask the hypervisor to give the
80*4882a593Smuzhiyun  * rest of our timeslice to the lock holder.
81*4882a593Smuzhiyun  *
82*4882a593Smuzhiyun  * So that we can tell which virtual processor is holding a lock,
83*4882a593Smuzhiyun  * we put 0x80000000 | smp_processor_id() in the lock when it is
84*4882a593Smuzhiyun  * held.  Conveniently, we have a word in the paca that holds this
85*4882a593Smuzhiyun  * value.
86*4882a593Smuzhiyun  */
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun #if defined(CONFIG_PPC_SPLPAR)
89*4882a593Smuzhiyun /* We only yield to the hypervisor if we are in shared processor mode */
90*4882a593Smuzhiyun void splpar_spin_yield(arch_spinlock_t *lock);
91*4882a593Smuzhiyun void splpar_rw_yield(arch_rwlock_t *lock);
92*4882a593Smuzhiyun #else /* SPLPAR */
splpar_spin_yield(arch_spinlock_t * lock)93*4882a593Smuzhiyun static inline void splpar_spin_yield(arch_spinlock_t *lock) {};
splpar_rw_yield(arch_rwlock_t * lock)94*4882a593Smuzhiyun static inline void splpar_rw_yield(arch_rwlock_t *lock) {};
95*4882a593Smuzhiyun #endif
96*4882a593Smuzhiyun 
spin_yield(arch_spinlock_t * lock)97*4882a593Smuzhiyun static inline void spin_yield(arch_spinlock_t *lock)
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 	if (is_shared_processor())
100*4882a593Smuzhiyun 		splpar_spin_yield(lock);
101*4882a593Smuzhiyun 	else
102*4882a593Smuzhiyun 		barrier();
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
rw_yield(arch_rwlock_t * lock)105*4882a593Smuzhiyun static inline void rw_yield(arch_rwlock_t *lock)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	if (is_shared_processor())
108*4882a593Smuzhiyun 		splpar_rw_yield(lock);
109*4882a593Smuzhiyun 	else
110*4882a593Smuzhiyun 		barrier();
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
arch_spin_lock(arch_spinlock_t * lock)113*4882a593Smuzhiyun static inline void arch_spin_lock(arch_spinlock_t *lock)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	while (1) {
116*4882a593Smuzhiyun 		if (likely(__arch_spin_trylock(lock) == 0))
117*4882a593Smuzhiyun 			break;
118*4882a593Smuzhiyun 		do {
119*4882a593Smuzhiyun 			HMT_low();
120*4882a593Smuzhiyun 			if (is_shared_processor())
121*4882a593Smuzhiyun 				splpar_spin_yield(lock);
122*4882a593Smuzhiyun 		} while (unlikely(lock->slock != 0));
123*4882a593Smuzhiyun 		HMT_medium();
124*4882a593Smuzhiyun 	}
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun static inline
arch_spin_lock_flags(arch_spinlock_t * lock,unsigned long flags)128*4882a593Smuzhiyun void arch_spin_lock_flags(arch_spinlock_t *lock, unsigned long flags)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	unsigned long flags_dis;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	while (1) {
133*4882a593Smuzhiyun 		if (likely(__arch_spin_trylock(lock) == 0))
134*4882a593Smuzhiyun 			break;
135*4882a593Smuzhiyun 		local_save_flags(flags_dis);
136*4882a593Smuzhiyun 		local_irq_restore(flags);
137*4882a593Smuzhiyun 		do {
138*4882a593Smuzhiyun 			HMT_low();
139*4882a593Smuzhiyun 			if (is_shared_processor())
140*4882a593Smuzhiyun 				splpar_spin_yield(lock);
141*4882a593Smuzhiyun 		} while (unlikely(lock->slock != 0));
142*4882a593Smuzhiyun 		HMT_medium();
143*4882a593Smuzhiyun 		local_irq_restore(flags_dis);
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun #define arch_spin_lock_flags arch_spin_lock_flags
147*4882a593Smuzhiyun 
arch_spin_unlock(arch_spinlock_t * lock)148*4882a593Smuzhiyun static inline void arch_spin_unlock(arch_spinlock_t *lock)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	__asm__ __volatile__("# arch_spin_unlock\n\t"
151*4882a593Smuzhiyun 				PPC_RELEASE_BARRIER: : :"memory");
152*4882a593Smuzhiyun 	lock->slock = 0;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun /*
156*4882a593Smuzhiyun  * Read-write spinlocks, allowing multiple readers
157*4882a593Smuzhiyun  * but only one writer.
158*4882a593Smuzhiyun  *
159*4882a593Smuzhiyun  * NOTE! it is quite common to have readers in interrupts
160*4882a593Smuzhiyun  * but no interrupt writers. For those circumstances we
161*4882a593Smuzhiyun  * can "mix" irq-safe locks - any writer needs to get a
162*4882a593Smuzhiyun  * irq-safe write-lock, but readers can get non-irqsafe
163*4882a593Smuzhiyun  * read-locks.
164*4882a593Smuzhiyun  */
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun #ifdef CONFIG_PPC64
167*4882a593Smuzhiyun #define __DO_SIGN_EXTEND	"extsw	%0,%0\n"
168*4882a593Smuzhiyun #define WRLOCK_TOKEN		LOCK_TOKEN	/* it's negative */
169*4882a593Smuzhiyun #else
170*4882a593Smuzhiyun #define __DO_SIGN_EXTEND
171*4882a593Smuzhiyun #define WRLOCK_TOKEN		(-1)
172*4882a593Smuzhiyun #endif
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /*
175*4882a593Smuzhiyun  * This returns the old value in the lock + 1,
176*4882a593Smuzhiyun  * so we got a read lock if the return value is > 0.
177*4882a593Smuzhiyun  */
__arch_read_trylock(arch_rwlock_t * rw)178*4882a593Smuzhiyun static inline long __arch_read_trylock(arch_rwlock_t *rw)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	long tmp;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	__asm__ __volatile__(
183*4882a593Smuzhiyun "1:	" PPC_LWARX(%0,0,%1,1) "\n"
184*4882a593Smuzhiyun 	__DO_SIGN_EXTEND
185*4882a593Smuzhiyun "	addic.		%0,%0,1\n\
186*4882a593Smuzhiyun 	ble-		2f\n"
187*4882a593Smuzhiyun "	stwcx.		%0,0,%1\n\
188*4882a593Smuzhiyun 	bne-		1b\n"
189*4882a593Smuzhiyun 	PPC_ACQUIRE_BARRIER
190*4882a593Smuzhiyun "2:"	: "=&r" (tmp)
191*4882a593Smuzhiyun 	: "r" (&rw->lock)
192*4882a593Smuzhiyun 	: "cr0", "xer", "memory");
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return tmp;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /*
198*4882a593Smuzhiyun  * This returns the old value in the lock,
199*4882a593Smuzhiyun  * so we got the write lock if the return value is 0.
200*4882a593Smuzhiyun  */
__arch_write_trylock(arch_rwlock_t * rw)201*4882a593Smuzhiyun static inline long __arch_write_trylock(arch_rwlock_t *rw)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun 	long tmp, token;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	token = WRLOCK_TOKEN;
206*4882a593Smuzhiyun 	__asm__ __volatile__(
207*4882a593Smuzhiyun "1:	" PPC_LWARX(%0,0,%2,1) "\n\
208*4882a593Smuzhiyun 	cmpwi		0,%0,0\n\
209*4882a593Smuzhiyun 	bne-		2f\n"
210*4882a593Smuzhiyun "	stwcx.		%1,0,%2\n\
211*4882a593Smuzhiyun 	bne-		1b\n"
212*4882a593Smuzhiyun 	PPC_ACQUIRE_BARRIER
213*4882a593Smuzhiyun "2:"	: "=&r" (tmp)
214*4882a593Smuzhiyun 	: "r" (token), "r" (&rw->lock)
215*4882a593Smuzhiyun 	: "cr0", "memory");
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	return tmp;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
arch_read_lock(arch_rwlock_t * rw)220*4882a593Smuzhiyun static inline void arch_read_lock(arch_rwlock_t *rw)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	while (1) {
223*4882a593Smuzhiyun 		if (likely(__arch_read_trylock(rw) > 0))
224*4882a593Smuzhiyun 			break;
225*4882a593Smuzhiyun 		do {
226*4882a593Smuzhiyun 			HMT_low();
227*4882a593Smuzhiyun 			if (is_shared_processor())
228*4882a593Smuzhiyun 				splpar_rw_yield(rw);
229*4882a593Smuzhiyun 		} while (unlikely(rw->lock < 0));
230*4882a593Smuzhiyun 		HMT_medium();
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
arch_write_lock(arch_rwlock_t * rw)234*4882a593Smuzhiyun static inline void arch_write_lock(arch_rwlock_t *rw)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	while (1) {
237*4882a593Smuzhiyun 		if (likely(__arch_write_trylock(rw) == 0))
238*4882a593Smuzhiyun 			break;
239*4882a593Smuzhiyun 		do {
240*4882a593Smuzhiyun 			HMT_low();
241*4882a593Smuzhiyun 			if (is_shared_processor())
242*4882a593Smuzhiyun 				splpar_rw_yield(rw);
243*4882a593Smuzhiyun 		} while (unlikely(rw->lock != 0));
244*4882a593Smuzhiyun 		HMT_medium();
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun 
arch_read_trylock(arch_rwlock_t * rw)248*4882a593Smuzhiyun static inline int arch_read_trylock(arch_rwlock_t *rw)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun 	return __arch_read_trylock(rw) > 0;
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
arch_write_trylock(arch_rwlock_t * rw)253*4882a593Smuzhiyun static inline int arch_write_trylock(arch_rwlock_t *rw)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	return __arch_write_trylock(rw) == 0;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
arch_read_unlock(arch_rwlock_t * rw)258*4882a593Smuzhiyun static inline void arch_read_unlock(arch_rwlock_t *rw)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	long tmp;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	__asm__ __volatile__(
263*4882a593Smuzhiyun 	"# read_unlock\n\t"
264*4882a593Smuzhiyun 	PPC_RELEASE_BARRIER
265*4882a593Smuzhiyun "1:	lwarx		%0,0,%1\n\
266*4882a593Smuzhiyun 	addic		%0,%0,-1\n"
267*4882a593Smuzhiyun "	stwcx.		%0,0,%1\n\
268*4882a593Smuzhiyun 	bne-		1b"
269*4882a593Smuzhiyun 	: "=&r"(tmp)
270*4882a593Smuzhiyun 	: "r"(&rw->lock)
271*4882a593Smuzhiyun 	: "cr0", "xer", "memory");
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
arch_write_unlock(arch_rwlock_t * rw)274*4882a593Smuzhiyun static inline void arch_write_unlock(arch_rwlock_t *rw)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	__asm__ __volatile__("# write_unlock\n\t"
277*4882a593Smuzhiyun 				PPC_RELEASE_BARRIER: : :"memory");
278*4882a593Smuzhiyun 	rw->lock = 0;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun #define arch_spin_relax(lock)	spin_yield(lock)
282*4882a593Smuzhiyun #define arch_read_relax(lock)	rw_yield(lock)
283*4882a593Smuzhiyun #define arch_write_relax(lock)	rw_yield(lock)
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun /* See include/linux/spinlock.h */
286*4882a593Smuzhiyun #define smp_mb__after_spinlock()   smp_mb()
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun #endif /* _ASM_POWERPC_SIMPLE_SPINLOCK_H */
289