18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
28c2ecf20Sopenharmony_ci#ifndef _ASM_POWERPC_SIMPLE_SPINLOCK_H
38c2ecf20Sopenharmony_ci#define _ASM_POWERPC_SIMPLE_SPINLOCK_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci/*
68c2ecf20Sopenharmony_ci * Simple spin lock operations.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2001-2004 Paul Mackerras <paulus@au.ibm.com>, IBM
98c2ecf20Sopenharmony_ci * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM
108c2ecf20Sopenharmony_ci * Copyright (C) 2002 Dave Engebretsen <engebret@us.ibm.com>, IBM
118c2ecf20Sopenharmony_ci *	Rework to support virtual processors
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Type of int is used as a full 64b word is not necessary.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * (the type definitions are in asm/simple_spinlock_types.h)
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci#include <linux/irqflags.h>
188c2ecf20Sopenharmony_ci#include <asm/paravirt.h>
198c2ecf20Sopenharmony_ci#include <asm/paca.h>
208c2ecf20Sopenharmony_ci#include <asm/synch.h>
218c2ecf20Sopenharmony_ci#include <asm/ppc-opcode.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
248c2ecf20Sopenharmony_ci/* use 0x800000yy when locked, where yy == CPU number */
258c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN__
268c2ecf20Sopenharmony_ci#define LOCK_TOKEN	(*(u32 *)(&get_paca()->lock_token))
278c2ecf20Sopenharmony_ci#else
288c2ecf20Sopenharmony_ci#define LOCK_TOKEN	(*(u32 *)(&get_paca()->paca_index))
298c2ecf20Sopenharmony_ci#endif
308c2ecf20Sopenharmony_ci#else
318c2ecf20Sopenharmony_ci#define LOCK_TOKEN	1
328c2ecf20Sopenharmony_ci#endif
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	return lock.slock == 0;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic inline int arch_spin_is_locked(arch_spinlock_t *lock)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	smp_mb();
428c2ecf20Sopenharmony_ci	return !arch_spin_value_unlocked(*lock);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * This returns the old value in the lock, so we succeeded
478c2ecf20Sopenharmony_ci * in getting the lock if the return value is 0.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_cistatic inline unsigned long __arch_spin_trylock(arch_spinlock_t *lock)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	unsigned long tmp, token;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	token = LOCK_TOKEN;
548c2ecf20Sopenharmony_ci	__asm__ __volatile__(
558c2ecf20Sopenharmony_ci"1:	" PPC_LWARX(%0,0,%2,1) "\n\
568c2ecf20Sopenharmony_ci	cmpwi		0,%0,0\n\
578c2ecf20Sopenharmony_ci	bne-		2f\n\
588c2ecf20Sopenharmony_ci	stwcx.		%1,0,%2\n\
598c2ecf20Sopenharmony_ci	bne-		1b\n"
608c2ecf20Sopenharmony_ci	PPC_ACQUIRE_BARRIER
618c2ecf20Sopenharmony_ci"2:"
628c2ecf20Sopenharmony_ci	: "=&r" (tmp)
638c2ecf20Sopenharmony_ci	: "r" (token), "r" (&lock->slock)
648c2ecf20Sopenharmony_ci	: "cr0", "memory");
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return tmp;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic inline int arch_spin_trylock(arch_spinlock_t *lock)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	return __arch_spin_trylock(lock) == 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * On a system with shared processors (that is, where a physical
768c2ecf20Sopenharmony_ci * processor is multiplexed between several virtual processors),
778c2ecf20Sopenharmony_ci * there is no point spinning on a lock if the holder of the lock
788c2ecf20Sopenharmony_ci * isn't currently scheduled on a physical processor.  Instead
798c2ecf20Sopenharmony_ci * we detect this situation and ask the hypervisor to give the
808c2ecf20Sopenharmony_ci * rest of our timeslice to the lock holder.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * So that we can tell which virtual processor is holding a lock,
838c2ecf20Sopenharmony_ci * we put 0x80000000 | smp_processor_id() in the lock when it is
848c2ecf20Sopenharmony_ci * held.  Conveniently, we have a word in the paca that holds this
858c2ecf20Sopenharmony_ci * value.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#if defined(CONFIG_PPC_SPLPAR)
898c2ecf20Sopenharmony_ci/* We only yield to the hypervisor if we are in shared processor mode */
908c2ecf20Sopenharmony_civoid splpar_spin_yield(arch_spinlock_t *lock);
918c2ecf20Sopenharmony_civoid splpar_rw_yield(arch_rwlock_t *lock);
928c2ecf20Sopenharmony_ci#else /* SPLPAR */
938c2ecf20Sopenharmony_cistatic inline void splpar_spin_yield(arch_spinlock_t *lock) {};
948c2ecf20Sopenharmony_cistatic inline void splpar_rw_yield(arch_rwlock_t *lock) {};
958c2ecf20Sopenharmony_ci#endif
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline void spin_yield(arch_spinlock_t *lock)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	if (is_shared_processor())
1008c2ecf20Sopenharmony_ci		splpar_spin_yield(lock);
1018c2ecf20Sopenharmony_ci	else
1028c2ecf20Sopenharmony_ci		barrier();
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic inline void rw_yield(arch_rwlock_t *lock)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	if (is_shared_processor())
1088c2ecf20Sopenharmony_ci		splpar_rw_yield(lock);
1098c2ecf20Sopenharmony_ci	else
1108c2ecf20Sopenharmony_ci		barrier();
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic inline void arch_spin_lock(arch_spinlock_t *lock)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	while (1) {
1168c2ecf20Sopenharmony_ci		if (likely(__arch_spin_trylock(lock) == 0))
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci		do {
1198c2ecf20Sopenharmony_ci			HMT_low();
1208c2ecf20Sopenharmony_ci			if (is_shared_processor())
1218c2ecf20Sopenharmony_ci				splpar_spin_yield(lock);
1228c2ecf20Sopenharmony_ci		} while (unlikely(lock->slock != 0));
1238c2ecf20Sopenharmony_ci		HMT_medium();
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic inline
1288c2ecf20Sopenharmony_civoid arch_spin_lock_flags(arch_spinlock_t *lock, unsigned long flags)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	unsigned long flags_dis;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	while (1) {
1338c2ecf20Sopenharmony_ci		if (likely(__arch_spin_trylock(lock) == 0))
1348c2ecf20Sopenharmony_ci			break;
1358c2ecf20Sopenharmony_ci		local_save_flags(flags_dis);
1368c2ecf20Sopenharmony_ci		local_irq_restore(flags);
1378c2ecf20Sopenharmony_ci		do {
1388c2ecf20Sopenharmony_ci			HMT_low();
1398c2ecf20Sopenharmony_ci			if (is_shared_processor())
1408c2ecf20Sopenharmony_ci				splpar_spin_yield(lock);
1418c2ecf20Sopenharmony_ci		} while (unlikely(lock->slock != 0));
1428c2ecf20Sopenharmony_ci		HMT_medium();
1438c2ecf20Sopenharmony_ci		local_irq_restore(flags_dis);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci#define arch_spin_lock_flags arch_spin_lock_flags
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic inline void arch_spin_unlock(arch_spinlock_t *lock)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	__asm__ __volatile__("# arch_spin_unlock\n\t"
1518c2ecf20Sopenharmony_ci				PPC_RELEASE_BARRIER: : :"memory");
1528c2ecf20Sopenharmony_ci	lock->slock = 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * Read-write spinlocks, allowing multiple readers
1578c2ecf20Sopenharmony_ci * but only one writer.
1588c2ecf20Sopenharmony_ci *
1598c2ecf20Sopenharmony_ci * NOTE! it is quite common to have readers in interrupts
1608c2ecf20Sopenharmony_ci * but no interrupt writers. For those circumstances we
1618c2ecf20Sopenharmony_ci * can "mix" irq-safe locks - any writer needs to get a
1628c2ecf20Sopenharmony_ci * irq-safe write-lock, but readers can get non-irqsafe
1638c2ecf20Sopenharmony_ci * read-locks.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC64
1678c2ecf20Sopenharmony_ci#define __DO_SIGN_EXTEND	"extsw	%0,%0\n"
1688c2ecf20Sopenharmony_ci#define WRLOCK_TOKEN		LOCK_TOKEN	/* it's negative */
1698c2ecf20Sopenharmony_ci#else
1708c2ecf20Sopenharmony_ci#define __DO_SIGN_EXTEND
1718c2ecf20Sopenharmony_ci#define WRLOCK_TOKEN		(-1)
1728c2ecf20Sopenharmony_ci#endif
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci * This returns the old value in the lock + 1,
1768c2ecf20Sopenharmony_ci * so we got a read lock if the return value is > 0.
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_cistatic inline long __arch_read_trylock(arch_rwlock_t *rw)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	long tmp;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	__asm__ __volatile__(
1838c2ecf20Sopenharmony_ci"1:	" PPC_LWARX(%0,0,%1,1) "\n"
1848c2ecf20Sopenharmony_ci	__DO_SIGN_EXTEND
1858c2ecf20Sopenharmony_ci"	addic.		%0,%0,1\n\
1868c2ecf20Sopenharmony_ci	ble-		2f\n"
1878c2ecf20Sopenharmony_ci"	stwcx.		%0,0,%1\n\
1888c2ecf20Sopenharmony_ci	bne-		1b\n"
1898c2ecf20Sopenharmony_ci	PPC_ACQUIRE_BARRIER
1908c2ecf20Sopenharmony_ci"2:"	: "=&r" (tmp)
1918c2ecf20Sopenharmony_ci	: "r" (&rw->lock)
1928c2ecf20Sopenharmony_ci	: "cr0", "xer", "memory");
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return tmp;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * This returns the old value in the lock,
1998c2ecf20Sopenharmony_ci * so we got the write lock if the return value is 0.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cistatic inline long __arch_write_trylock(arch_rwlock_t *rw)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	long tmp, token;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	token = WRLOCK_TOKEN;
2068c2ecf20Sopenharmony_ci	__asm__ __volatile__(
2078c2ecf20Sopenharmony_ci"1:	" PPC_LWARX(%0,0,%2,1) "\n\
2088c2ecf20Sopenharmony_ci	cmpwi		0,%0,0\n\
2098c2ecf20Sopenharmony_ci	bne-		2f\n"
2108c2ecf20Sopenharmony_ci"	stwcx.		%1,0,%2\n\
2118c2ecf20Sopenharmony_ci	bne-		1b\n"
2128c2ecf20Sopenharmony_ci	PPC_ACQUIRE_BARRIER
2138c2ecf20Sopenharmony_ci"2:"	: "=&r" (tmp)
2148c2ecf20Sopenharmony_ci	: "r" (token), "r" (&rw->lock)
2158c2ecf20Sopenharmony_ci	: "cr0", "memory");
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return tmp;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic inline void arch_read_lock(arch_rwlock_t *rw)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	while (1) {
2238c2ecf20Sopenharmony_ci		if (likely(__arch_read_trylock(rw) > 0))
2248c2ecf20Sopenharmony_ci			break;
2258c2ecf20Sopenharmony_ci		do {
2268c2ecf20Sopenharmony_ci			HMT_low();
2278c2ecf20Sopenharmony_ci			if (is_shared_processor())
2288c2ecf20Sopenharmony_ci				splpar_rw_yield(rw);
2298c2ecf20Sopenharmony_ci		} while (unlikely(rw->lock < 0));
2308c2ecf20Sopenharmony_ci		HMT_medium();
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic inline void arch_write_lock(arch_rwlock_t *rw)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	while (1) {
2378c2ecf20Sopenharmony_ci		if (likely(__arch_write_trylock(rw) == 0))
2388c2ecf20Sopenharmony_ci			break;
2398c2ecf20Sopenharmony_ci		do {
2408c2ecf20Sopenharmony_ci			HMT_low();
2418c2ecf20Sopenharmony_ci			if (is_shared_processor())
2428c2ecf20Sopenharmony_ci				splpar_rw_yield(rw);
2438c2ecf20Sopenharmony_ci		} while (unlikely(rw->lock != 0));
2448c2ecf20Sopenharmony_ci		HMT_medium();
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic inline int arch_read_trylock(arch_rwlock_t *rw)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	return __arch_read_trylock(rw) > 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic inline int arch_write_trylock(arch_rwlock_t *rw)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	return __arch_write_trylock(rw) == 0;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic inline void arch_read_unlock(arch_rwlock_t *rw)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	long tmp;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	__asm__ __volatile__(
2638c2ecf20Sopenharmony_ci	"# read_unlock\n\t"
2648c2ecf20Sopenharmony_ci	PPC_RELEASE_BARRIER
2658c2ecf20Sopenharmony_ci"1:	lwarx		%0,0,%1\n\
2668c2ecf20Sopenharmony_ci	addic		%0,%0,-1\n"
2678c2ecf20Sopenharmony_ci"	stwcx.		%0,0,%1\n\
2688c2ecf20Sopenharmony_ci	bne-		1b"
2698c2ecf20Sopenharmony_ci	: "=&r"(tmp)
2708c2ecf20Sopenharmony_ci	: "r"(&rw->lock)
2718c2ecf20Sopenharmony_ci	: "cr0", "xer", "memory");
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic inline void arch_write_unlock(arch_rwlock_t *rw)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	__asm__ __volatile__("# write_unlock\n\t"
2778c2ecf20Sopenharmony_ci				PPC_RELEASE_BARRIER: : :"memory");
2788c2ecf20Sopenharmony_ci	rw->lock = 0;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci#define arch_spin_relax(lock)	spin_yield(lock)
2828c2ecf20Sopenharmony_ci#define arch_read_relax(lock)	rw_yield(lock)
2838c2ecf20Sopenharmony_ci#define arch_write_relax(lock)	rw_yield(lock)
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci/* See include/linux/spinlock.h */
2868c2ecf20Sopenharmony_ci#define smp_mb__after_spinlock()   smp_mb()
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci#endif /* _ASM_POWERPC_SIMPLE_SPINLOCK_H */
289