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