251 lines
6 KiB
C
251 lines
6 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef __ASM_SPINLOCK_LNKGET_H
|
|
#define __ASM_SPINLOCK_LNKGET_H
|
|
|
|
/*
|
|
* None of these asm statements clobber memory as LNKSET writes around
|
|
* the cache so the memory it modifies cannot safely be read by any means
|
|
* other than these accessors.
|
|
*/
|
|
|
|
static inline int arch_spin_is_locked(arch_spinlock_t *lock)
|
|
{
|
|
int ret;
|
|
|
|
asm volatile ("LNKGETD %0, [%1]\n"
|
|
"TST %0, #1\n"
|
|
"MOV %0, #1\n"
|
|
"XORZ %0, %0, %0\n"
|
|
: "=&d" (ret)
|
|
: "da" (&lock->lock)
|
|
: "cc");
|
|
return ret;
|
|
}
|
|
|
|
static inline void arch_spin_lock(arch_spinlock_t *lock)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile ("1: LNKGETD %0,[%1]\n"
|
|
" TST %0, #1\n"
|
|
" ADD %0, %0, #1\n"
|
|
" LNKSETDZ [%1], %0\n"
|
|
" BNZ 1b\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" BNZ 1b\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&lock->lock)
|
|
: "cc");
|
|
|
|
smp_mb();
|
|
}
|
|
|
|
/* Returns 0 if failed to acquire lock */
|
|
static inline int arch_spin_trylock(arch_spinlock_t *lock)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile (" LNKGETD %0,[%1]\n"
|
|
" TST %0, #1\n"
|
|
" ADD %0, %0, #1\n"
|
|
" LNKSETDZ [%1], %0\n"
|
|
" BNZ 1f\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" MOV %0, #1\n"
|
|
"1: XORNZ %0, %0, %0\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&lock->lock)
|
|
: "cc");
|
|
|
|
smp_mb();
|
|
|
|
return tmp;
|
|
}
|
|
|
|
static inline void arch_spin_unlock(arch_spinlock_t *lock)
|
|
{
|
|
smp_mb();
|
|
|
|
asm volatile (" SETD [%0], %1\n"
|
|
:
|
|
: "da" (&lock->lock), "da" (0)
|
|
: "memory");
|
|
}
|
|
|
|
/*
|
|
* RWLOCKS
|
|
*
|
|
*
|
|
* Write locks are easy - we just set bit 31. When unlocking, we can
|
|
* just write zero since the lock is exclusively held.
|
|
*/
|
|
|
|
static inline void arch_write_lock(arch_rwlock_t *rw)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile ("1: LNKGETD %0,[%1]\n"
|
|
" CMP %0, #0\n"
|
|
" ADD %0, %0, %2\n"
|
|
" LNKSETDZ [%1], %0\n"
|
|
" BNZ 1b\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" BNZ 1b\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&rw->lock), "bd" (0x80000000)
|
|
: "cc");
|
|
|
|
smp_mb();
|
|
}
|
|
|
|
static inline int arch_write_trylock(arch_rwlock_t *rw)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile (" LNKGETD %0,[%1]\n"
|
|
" CMP %0, #0\n"
|
|
" ADD %0, %0, %2\n"
|
|
" LNKSETDZ [%1], %0\n"
|
|
" BNZ 1f\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" MOV %0,#1\n"
|
|
"1: XORNZ %0, %0, %0\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&rw->lock), "bd" (0x80000000)
|
|
: "cc");
|
|
|
|
smp_mb();
|
|
|
|
return tmp;
|
|
}
|
|
|
|
static inline void arch_write_unlock(arch_rwlock_t *rw)
|
|
{
|
|
smp_mb();
|
|
|
|
asm volatile (" SETD [%0], %1\n"
|
|
:
|
|
: "da" (&rw->lock), "da" (0)
|
|
: "memory");
|
|
}
|
|
|
|
/* write_can_lock - would write_trylock() succeed? */
|
|
static inline int arch_write_can_lock(arch_rwlock_t *rw)
|
|
{
|
|
int ret;
|
|
|
|
asm volatile ("LNKGETD %0, [%1]\n"
|
|
"CMP %0, #0\n"
|
|
"MOV %0, #1\n"
|
|
"XORNZ %0, %0, %0\n"
|
|
: "=&d" (ret)
|
|
: "da" (&rw->lock)
|
|
: "cc");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Read locks are a bit more hairy:
|
|
* - Exclusively load the lock value.
|
|
* - Increment it.
|
|
* - Store new lock value if positive, and we still own this location.
|
|
* If the value is negative, we've already failed.
|
|
* - If we failed to store the value, we want a negative result.
|
|
* - If we failed, try again.
|
|
* Unlocking is similarly hairy. We may have multiple read locks
|
|
* currently active. However, we know we won't have any write
|
|
* locks.
|
|
*/
|
|
static inline void arch_read_lock(arch_rwlock_t *rw)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile ("1: LNKGETD %0,[%1]\n"
|
|
" ADDS %0, %0, #1\n"
|
|
" LNKSETDPL [%1], %0\n"
|
|
" BMI 1b\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" BNZ 1b\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&rw->lock)
|
|
: "cc");
|
|
|
|
smp_mb();
|
|
}
|
|
|
|
static inline void arch_read_unlock(arch_rwlock_t *rw)
|
|
{
|
|
int tmp;
|
|
|
|
smp_mb();
|
|
|
|
asm volatile ("1: LNKGETD %0,[%1]\n"
|
|
" SUB %0, %0, #1\n"
|
|
" LNKSETD [%1], %0\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" BNZ 1b\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&rw->lock)
|
|
: "cc", "memory");
|
|
}
|
|
|
|
static inline int arch_read_trylock(arch_rwlock_t *rw)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile (" LNKGETD %0,[%1]\n"
|
|
" ADDS %0, %0, #1\n"
|
|
" LNKSETDPL [%1], %0\n"
|
|
" BMI 1f\n"
|
|
" DEFR %0, TXSTAT\n"
|
|
" ANDT %0, %0, #HI(0x3f000000)\n"
|
|
" CMPT %0, #HI(0x02000000)\n"
|
|
" MOV %0,#1\n"
|
|
" BZ 2f\n"
|
|
"1: MOV %0,#0\n"
|
|
"2:\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&rw->lock)
|
|
: "cc");
|
|
|
|
smp_mb();
|
|
|
|
return tmp;
|
|
}
|
|
|
|
/* read_can_lock - would read_trylock() succeed? */
|
|
static inline int arch_read_can_lock(arch_rwlock_t *rw)
|
|
{
|
|
int tmp;
|
|
|
|
asm volatile ("LNKGETD %0, [%1]\n"
|
|
"CMP %0, %2\n"
|
|
"MOV %0, #1\n"
|
|
"XORZ %0, %0, %0\n"
|
|
: "=&d" (tmp)
|
|
: "da" (&rw->lock), "bd" (0x80000000)
|
|
: "cc");
|
|
return tmp;
|
|
}
|
|
|
|
#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
|
|
#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
|
|
|
|
#define arch_spin_relax(lock) cpu_relax()
|
|
#define arch_read_relax(lock) cpu_relax()
|
|
#define arch_write_relax(lock) cpu_relax()
|
|
|
|
#endif /* __ASM_SPINLOCK_LNKGET_H */
|