162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci#ifndef _ASM_POWERPC_SIMPLE_SPINLOCK_H
362306a36Sopenharmony_ci#define _ASM_POWERPC_SIMPLE_SPINLOCK_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci/*
662306a36Sopenharmony_ci * Simple spin lock operations.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2001-2004 Paul Mackerras <paulus@au.ibm.com>, IBM
962306a36Sopenharmony_ci * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
1062306a36Sopenharmony_ci * Copyright (C) 2002 Dave Engebretsen <engebret@us.ibm.com>, IBM
1162306a36Sopenharmony_ci *	Rework to support virtual processors
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Type of int is used as a full 64b word is not necessary.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * (the type definitions are in asm/simple_spinlock_types.h)
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci#include <linux/irqflags.h>
1862306a36Sopenharmony_ci#include <linux/kcsan-checks.h>
1962306a36Sopenharmony_ci#include <asm/paravirt.h>
2062306a36Sopenharmony_ci#include <asm/paca.h>
2162306a36Sopenharmony_ci#include <asm/synch.h>
2262306a36Sopenharmony_ci#include <asm/ppc-opcode.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#ifdef CONFIG_PPC64
2562306a36Sopenharmony_ci/* use 0x800000yy when locked, where yy == CPU number */
2662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN__
2762306a36Sopenharmony_ci#define LOCK_TOKEN	(*(u32 *)(&get_paca()->lock_token))
2862306a36Sopenharmony_ci#else
2962306a36Sopenharmony_ci#define LOCK_TOKEN	(*(u32 *)(&get_paca()->paca_index))
3062306a36Sopenharmony_ci#endif
3162306a36Sopenharmony_ci#else
3262306a36Sopenharmony_ci#define LOCK_TOKEN	1
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	return lock.slock == 0;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic inline int arch_spin_is_locked(arch_spinlock_t *lock)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	return !arch_spin_value_unlocked(READ_ONCE(*lock));
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * This returns the old value in the lock, so we succeeded
4762306a36Sopenharmony_ci * in getting the lock if the return value is 0.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic inline unsigned long __arch_spin_trylock(arch_spinlock_t *lock)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	unsigned long tmp, token;
5262306a36Sopenharmony_ci	unsigned int eh = IS_ENABLED(CONFIG_PPC64);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	token = LOCK_TOKEN;
5562306a36Sopenharmony_ci	__asm__ __volatile__(
5662306a36Sopenharmony_ci"1:	lwarx		%0,0,%2,%[eh]\n\
5762306a36Sopenharmony_ci	cmpwi		0,%0,0\n\
5862306a36Sopenharmony_ci	bne-		2f\n\
5962306a36Sopenharmony_ci	stwcx.		%1,0,%2\n\
6062306a36Sopenharmony_ci	bne-		1b\n"
6162306a36Sopenharmony_ci	PPC_ACQUIRE_BARRIER
6262306a36Sopenharmony_ci"2:"
6362306a36Sopenharmony_ci	: "=&r" (tmp)
6462306a36Sopenharmony_ci	: "r" (token), "r" (&lock->slock), [eh] "n" (eh)
6562306a36Sopenharmony_ci	: "cr0", "memory");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return tmp;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic inline int arch_spin_trylock(arch_spinlock_t *lock)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	return __arch_spin_trylock(lock) == 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * On a system with shared processors (that is, where a physical
7762306a36Sopenharmony_ci * processor is multiplexed between several virtual processors),
7862306a36Sopenharmony_ci * there is no point spinning on a lock if the holder of the lock
7962306a36Sopenharmony_ci * isn't currently scheduled on a physical processor.  Instead
8062306a36Sopenharmony_ci * we detect this situation and ask the hypervisor to give the
8162306a36Sopenharmony_ci * rest of our timeslice to the lock holder.
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * So that we can tell which virtual processor is holding a lock,
8462306a36Sopenharmony_ci * we put 0x80000000 | smp_processor_id() in the lock when it is
8562306a36Sopenharmony_ci * held.  Conveniently, we have a word in the paca that holds this
8662306a36Sopenharmony_ci * value.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#if defined(CONFIG_PPC_SPLPAR)
9062306a36Sopenharmony_ci/* We only yield to the hypervisor if we are in shared processor mode */
9162306a36Sopenharmony_civoid splpar_spin_yield(arch_spinlock_t *lock);
9262306a36Sopenharmony_civoid splpar_rw_yield(arch_rwlock_t *lock);
9362306a36Sopenharmony_ci#else /* SPLPAR */
9462306a36Sopenharmony_cistatic inline void splpar_spin_yield(arch_spinlock_t *lock) {}
9562306a36Sopenharmony_cistatic inline void splpar_rw_yield(arch_rwlock_t *lock) {}
9662306a36Sopenharmony_ci#endif
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic inline void spin_yield(arch_spinlock_t *lock)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	if (is_shared_processor())
10162306a36Sopenharmony_ci		splpar_spin_yield(lock);
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		barrier();
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline void rw_yield(arch_rwlock_t *lock)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	if (is_shared_processor())
10962306a36Sopenharmony_ci		splpar_rw_yield(lock);
11062306a36Sopenharmony_ci	else
11162306a36Sopenharmony_ci		barrier();
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic inline void arch_spin_lock(arch_spinlock_t *lock)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	while (1) {
11762306a36Sopenharmony_ci		if (likely(__arch_spin_trylock(lock) == 0))
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci		do {
12062306a36Sopenharmony_ci			HMT_low();
12162306a36Sopenharmony_ci			if (is_shared_processor())
12262306a36Sopenharmony_ci				splpar_spin_yield(lock);
12362306a36Sopenharmony_ci		} while (unlikely(lock->slock != 0));
12462306a36Sopenharmony_ci		HMT_medium();
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic inline void arch_spin_unlock(arch_spinlock_t *lock)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	kcsan_mb();
13162306a36Sopenharmony_ci	__asm__ __volatile__("# arch_spin_unlock\n\t"
13262306a36Sopenharmony_ci				PPC_RELEASE_BARRIER: : :"memory");
13362306a36Sopenharmony_ci	lock->slock = 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * Read-write spinlocks, allowing multiple readers
13862306a36Sopenharmony_ci * but only one writer.
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * NOTE! it is quite common to have readers in interrupts
14162306a36Sopenharmony_ci * but no interrupt writers. For those circumstances we
14262306a36Sopenharmony_ci * can "mix" irq-safe locks - any writer needs to get a
14362306a36Sopenharmony_ci * irq-safe write-lock, but readers can get non-irqsafe
14462306a36Sopenharmony_ci * read-locks.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#ifdef CONFIG_PPC64
14862306a36Sopenharmony_ci#define __DO_SIGN_EXTEND	"extsw	%0,%0\n"
14962306a36Sopenharmony_ci#define WRLOCK_TOKEN		LOCK_TOKEN	/* it's negative */
15062306a36Sopenharmony_ci#else
15162306a36Sopenharmony_ci#define __DO_SIGN_EXTEND
15262306a36Sopenharmony_ci#define WRLOCK_TOKEN		(-1)
15362306a36Sopenharmony_ci#endif
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * This returns the old value in the lock + 1,
15762306a36Sopenharmony_ci * so we got a read lock if the return value is > 0.
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_cistatic inline long __arch_read_trylock(arch_rwlock_t *rw)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	long tmp;
16262306a36Sopenharmony_ci	unsigned int eh = IS_ENABLED(CONFIG_PPC64);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	__asm__ __volatile__(
16562306a36Sopenharmony_ci"1:	lwarx		%0,0,%1,%[eh]\n"
16662306a36Sopenharmony_ci	__DO_SIGN_EXTEND
16762306a36Sopenharmony_ci"	addic.		%0,%0,1\n\
16862306a36Sopenharmony_ci	ble-		2f\n"
16962306a36Sopenharmony_ci"	stwcx.		%0,0,%1\n\
17062306a36Sopenharmony_ci	bne-		1b\n"
17162306a36Sopenharmony_ci	PPC_ACQUIRE_BARRIER
17262306a36Sopenharmony_ci"2:"	: "=&r" (tmp)
17362306a36Sopenharmony_ci	: "r" (&rw->lock), [eh] "n" (eh)
17462306a36Sopenharmony_ci	: "cr0", "xer", "memory");
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return tmp;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*
18062306a36Sopenharmony_ci * This returns the old value in the lock,
18162306a36Sopenharmony_ci * so we got the write lock if the return value is 0.
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_cistatic inline long __arch_write_trylock(arch_rwlock_t *rw)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	long tmp, token;
18662306a36Sopenharmony_ci	unsigned int eh = IS_ENABLED(CONFIG_PPC64);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	token = WRLOCK_TOKEN;
18962306a36Sopenharmony_ci	__asm__ __volatile__(
19062306a36Sopenharmony_ci"1:	lwarx		%0,0,%2,%[eh]\n\
19162306a36Sopenharmony_ci	cmpwi		0,%0,0\n\
19262306a36Sopenharmony_ci	bne-		2f\n"
19362306a36Sopenharmony_ci"	stwcx.		%1,0,%2\n\
19462306a36Sopenharmony_ci	bne-		1b\n"
19562306a36Sopenharmony_ci	PPC_ACQUIRE_BARRIER
19662306a36Sopenharmony_ci"2:"	: "=&r" (tmp)
19762306a36Sopenharmony_ci	: "r" (token), "r" (&rw->lock), [eh] "n" (eh)
19862306a36Sopenharmony_ci	: "cr0", "memory");
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return tmp;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic inline void arch_read_lock(arch_rwlock_t *rw)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	while (1) {
20662306a36Sopenharmony_ci		if (likely(__arch_read_trylock(rw) > 0))
20762306a36Sopenharmony_ci			break;
20862306a36Sopenharmony_ci		do {
20962306a36Sopenharmony_ci			HMT_low();
21062306a36Sopenharmony_ci			if (is_shared_processor())
21162306a36Sopenharmony_ci				splpar_rw_yield(rw);
21262306a36Sopenharmony_ci		} while (unlikely(rw->lock < 0));
21362306a36Sopenharmony_ci		HMT_medium();
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic inline void arch_write_lock(arch_rwlock_t *rw)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	while (1) {
22062306a36Sopenharmony_ci		if (likely(__arch_write_trylock(rw) == 0))
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci		do {
22362306a36Sopenharmony_ci			HMT_low();
22462306a36Sopenharmony_ci			if (is_shared_processor())
22562306a36Sopenharmony_ci				splpar_rw_yield(rw);
22662306a36Sopenharmony_ci		} while (unlikely(rw->lock != 0));
22762306a36Sopenharmony_ci		HMT_medium();
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic inline int arch_read_trylock(arch_rwlock_t *rw)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	return __arch_read_trylock(rw) > 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic inline int arch_write_trylock(arch_rwlock_t *rw)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	return __arch_write_trylock(rw) == 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic inline void arch_read_unlock(arch_rwlock_t *rw)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	long tmp;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	__asm__ __volatile__(
24662306a36Sopenharmony_ci	"# read_unlock\n\t"
24762306a36Sopenharmony_ci	PPC_RELEASE_BARRIER
24862306a36Sopenharmony_ci"1:	lwarx		%0,0,%1\n\
24962306a36Sopenharmony_ci	addic		%0,%0,-1\n"
25062306a36Sopenharmony_ci"	stwcx.		%0,0,%1\n\
25162306a36Sopenharmony_ci	bne-		1b"
25262306a36Sopenharmony_ci	: "=&r"(tmp)
25362306a36Sopenharmony_ci	: "r"(&rw->lock)
25462306a36Sopenharmony_ci	: "cr0", "xer", "memory");
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic inline void arch_write_unlock(arch_rwlock_t *rw)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	__asm__ __volatile__("# write_unlock\n\t"
26062306a36Sopenharmony_ci				PPC_RELEASE_BARRIER: : :"memory");
26162306a36Sopenharmony_ci	rw->lock = 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci#define arch_spin_relax(lock)	spin_yield(lock)
26562306a36Sopenharmony_ci#define arch_read_relax(lock)	rw_yield(lock)
26662306a36Sopenharmony_ci#define arch_write_relax(lock)	rw_yield(lock)
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci#endif /* _ASM_POWERPC_SIMPLE_SPINLOCK_H */
269