Commit 6006c0d8 authored by James Hogan's avatar James Hogan

metag: Atomics, locks and bitops

Add header files to implement Meta hardware thread locks (used by some
other atomic operations), atomics, spinlocks, and bitops.

There are 2 main types of atomic primitives for metag (in addition to
IRQs off on UP):
 - LOCK instructions provide locking between hardware threads.
 - LNKGET/LNKSET instructions provide load-linked/store-conditional
   operations allowing for lighter weight atomics on Meta2

LOCK instructions allow for hardware threads to acquire voluntary or
exclusive hardware thread locks:
 - LOCK0 releases exclusive and voluntary lock from the running hardware
   thread.
 - LOCK1 acquires the voluntary hardware lock, blocking until it becomes
   available.
 - LOCK2 implies LOCK1, and additionally acquires the exclusive hardware
   lock, blocking all other hardware threads from executing.
Signed-off-by: default avatarJames Hogan <james.hogan@imgtec.com>
parent 9b802d1f
#ifndef __ASM_METAG_ATOMIC_H
#define __ASM_METAG_ATOMIC_H
#include <linux/compiler.h>
#include <linux/types.h>
#include <asm/cmpxchg.h>
#if defined(CONFIG_METAG_ATOMICITY_IRQSOFF)
/* The simple UP case. */
#include <asm-generic/atomic.h>
#else
#if defined(CONFIG_METAG_ATOMICITY_LOCK1)
#include <asm/atomic_lock1.h>
#else
#include <asm/atomic_lnkget.h>
#endif
#define atomic_add_negative(a, v) (atomic_add_return((a), (v)) < 0)
#define atomic_dec_return(v) atomic_sub_return(1, (v))
#define atomic_inc_return(v) atomic_add_return(1, (v))
/*
* atomic_inc_and_test - increment and test
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases.
*/
#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0)
#define atomic_sub_and_test(i, v) (atomic_sub_return((i), (v)) == 0)
#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
#define atomic_inc(v) atomic_add(1, (v))
#define atomic_dec(v) atomic_sub(1, (v))
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
#define smp_mb__before_atomic_dec() barrier()
#define smp_mb__after_atomic_dec() barrier()
#define smp_mb__before_atomic_inc() barrier()
#define smp_mb__after_atomic_inc() barrier()
#endif
#define atomic_dec_if_positive(v) atomic_sub_if_positive(1, v)
#include <asm-generic/atomic64.h>
#endif /* __ASM_METAG_ATOMIC_H */
#ifndef __ASM_METAG_ATOMIC_LNKGET_H
#define __ASM_METAG_ATOMIC_LNKGET_H
#define ATOMIC_INIT(i) { (i) }
#define atomic_set(v, i) ((v)->counter = (i))
#include <linux/compiler.h>
#include <asm/barrier.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 atomic_read(const atomic_t *v)
{
int temp;
asm volatile (
"LNKGETD %0, [%1]\n"
: "=da" (temp)
: "da" (&v->counter));
return temp;
}
static inline void atomic_add(int i, atomic_t *v)
{
int temp;
asm volatile (
"1: LNKGETD %0, [%1]\n"
" ADD %0, %0, %2\n"
" LNKSETD [%1], %0\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp)
: "da" (&v->counter), "bd" (i)
: "cc");
}
static inline void atomic_sub(int i, atomic_t *v)
{
int temp;
asm volatile (
"1: LNKGETD %0, [%1]\n"
" SUB %0, %0, %2\n"
" LNKSETD [%1], %0\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp)
: "da" (&v->counter), "bd" (i)
: "cc");
}
static inline int atomic_add_return(int i, atomic_t *v)
{
int result, temp;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" ADD %1, %1, %3\n"
" LNKSETD [%2], %1\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp), "=&da" (result)
: "da" (&v->counter), "bd" (i)
: "cc");
smp_mb();
return result;
}
static inline int atomic_sub_return(int i, atomic_t *v)
{
int result, temp;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" SUB %1, %1, %3\n"
" LNKSETD [%2], %1\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp), "=&da" (result)
: "da" (&v->counter), "bd" (i)
: "cc");
smp_mb();
return result;
}
static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
{
int temp;
asm volatile (
"1: LNKGETD %0, [%1]\n"
" AND %0, %0, %2\n"
" LNKSETD [%1] %0\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp)
: "da" (&v->counter), "bd" (~mask)
: "cc");
}
static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
{
int temp;
asm volatile (
"1: LNKGETD %0, [%1]\n"
" OR %0, %0, %2\n"
" LNKSETD [%1], %0\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp)
: "da" (&v->counter), "bd" (mask)
: "cc");
}
static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
{
int result, temp;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" CMP %1, %3\n"
" LNKSETDEQ [%2], %4\n"
" BNE 2f\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
"2:\n"
: "=&d" (temp), "=&d" (result)
: "da" (&v->counter), "bd" (old), "da" (new)
: "cc");
smp_mb();
return result;
}
static inline int atomic_xchg(atomic_t *v, int new)
{
int temp, old;
asm volatile (
"1: LNKGETD %1, [%2]\n"
" LNKSETD [%2], %3\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
: "=&d" (temp), "=&d" (old)
: "da" (&v->counter), "da" (new)
: "cc");
return old;
}
static inline int __atomic_add_unless(atomic_t *v, int a, int u)
{
int result, temp;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" CMP %1, %3\n"
" ADD %0, %1, %4\n"
" LNKSETDNE [%2], %0\n"
" BEQ 2f\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
"2:\n"
: "=&d" (temp), "=&d" (result)
: "da" (&v->counter), "bd" (u), "bd" (a)
: "cc");
smp_mb();
return result;
}
static inline int atomic_sub_if_positive(int i, atomic_t *v)
{
int result, temp;
asm volatile (
"1: LNKGETD %1, [%2]\n"
" SUBS %1, %1, %3\n"
" LNKSETDGE [%2], %1\n"
" BLT 2f\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
"2:\n"
: "=&d" (temp), "=&da" (result)
: "da" (&v->counter), "bd" (i)
: "cc");
return result;
}
#endif /* __ASM_METAG_ATOMIC_LNKGET_H */
#ifndef __ASM_METAG_ATOMIC_LOCK1_H
#define __ASM_METAG_ATOMIC_LOCK1_H
#define ATOMIC_INIT(i) { (i) }
#include <linux/compiler.h>
#include <asm/barrier.h>
#include <asm/global_lock.h>
static inline int atomic_read(const atomic_t *v)
{
return (v)->counter;
}
/*
* atomic_set needs to be take the lock to protect atomic_add_unless from a
* possible race, as it reads the counter twice:
*
* CPU0 CPU1
* atomic_add_unless(1, 0)
* ret = v->counter (non-zero)
* if (ret != u) v->counter = 0
* v->counter += 1 (counter set to 1)
*
* Making atomic_set take the lock ensures that ordering and logical
* consistency is preserved.
*/
static inline int atomic_set(atomic_t *v, int i)
{
unsigned long flags;
__global_lock1(flags);
fence();
v->counter = i;
__global_unlock1(flags);
return i;
}
static inline void atomic_add(int i, atomic_t *v)
{
unsigned long flags;
__global_lock1(flags);
fence();
v->counter += i;
__global_unlock1(flags);
}
static inline void atomic_sub(int i, atomic_t *v)
{
unsigned long flags;
__global_lock1(flags);
fence();
v->counter -= i;
__global_unlock1(flags);
}
static inline int atomic_add_return(int i, atomic_t *v)
{
unsigned long result;
unsigned long flags;
__global_lock1(flags);
result = v->counter;
result += i;
fence();
v->counter = result;
__global_unlock1(flags);
return result;
}
static inline int atomic_sub_return(int i, atomic_t *v)
{
unsigned long result;
unsigned long flags;
__global_lock1(flags);
result = v->counter;
result -= i;
fence();
v->counter = result;
__global_unlock1(flags);
return result;
}
static inline void atomic_clear_mask(unsigned int mask, atomic_t *v)
{
unsigned long flags;
__global_lock1(flags);
fence();
v->counter &= ~mask;
__global_unlock1(flags);
}
static inline void atomic_set_mask(unsigned int mask, atomic_t *v)
{
unsigned long flags;
__global_lock1(flags);
fence();
v->counter |= mask;
__global_unlock1(flags);
}
static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
{
int ret;
unsigned long flags;
__global_lock1(flags);
ret = v->counter;
if (ret == old) {
fence();
v->counter = new;
}
__global_unlock1(flags);
return ret;
}
#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
static inline int __atomic_add_unless(atomic_t *v, int a, int u)
{
int ret;
unsigned long flags;
__global_lock1(flags);
ret = v->counter;
if (ret != u) {
fence();
v->counter += a;
}
__global_unlock1(flags);
return ret;
}
static inline int atomic_sub_if_positive(int i, atomic_t *v)
{
int ret;
unsigned long flags;
__global_lock1(flags);
ret = v->counter - 1;
if (ret >= 0) {
fence();
v->counter = ret;
}
__global_unlock1(flags);
return ret;
}
#endif /* __ASM_METAG_ATOMIC_LOCK1_H */
#ifndef __ASM_METAG_BITOPS_H
#define __ASM_METAG_BITOPS_H
#include <linux/compiler.h>
#include <asm/barrier.h>
#include <asm/global_lock.h>
/*
* clear_bit() doesn't provide any barrier for the compiler.
*/
#define smp_mb__before_clear_bit() barrier()
#define smp_mb__after_clear_bit() barrier()
#ifdef CONFIG_SMP
/*
* These functions are the basis of our bit ops.
*/
static inline void set_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
__global_lock1(flags);
fence();
*p |= mask;
__global_unlock1(flags);
}
static inline void clear_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
__global_lock1(flags);
fence();
*p &= ~mask;
__global_unlock1(flags);
}
static inline void change_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
__global_lock1(flags);
fence();
*p ^= mask;
__global_unlock1(flags);
}
static inline int test_and_set_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long old;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
__global_lock1(flags);
old = *p;
if (!(old & mask)) {
fence();
*p = old | mask;
}
__global_unlock1(flags);
return (old & mask) != 0;
}
static inline int test_and_clear_bit(unsigned int bit,
volatile unsigned long *p)
{
unsigned long flags;
unsigned long old;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
__global_lock1(flags);
old = *p;
if (old & mask) {
fence();
*p = old & ~mask;
}
__global_unlock1(flags);
return (old & mask) != 0;
}
static inline int test_and_change_bit(unsigned int bit,
volatile unsigned long *p)
{
unsigned long flags;
unsigned long old;
unsigned long mask = 1UL << (bit & 31);
p += bit >> 5;
__global_lock1(flags);
fence();
old = *p;
*p = old ^ mask;
__global_unlock1(flags);
return (old & mask) != 0;
}
#else
#include <asm-generic/bitops/atomic.h>
#endif /* CONFIG_SMP */
#include <asm-generic/bitops/non-atomic.h>
#include <asm-generic/bitops/find.h>
#include <asm-generic/bitops/ffs.h>
#include <asm-generic/bitops/__ffs.h>
#include <asm-generic/bitops/ffz.h>
#include <asm-generic/bitops/fls.h>
#include <asm-generic/bitops/__fls.h>
#include <asm-generic/bitops/fls64.h>
#include <asm-generic/bitops/hweight.h>
#include <asm-generic/bitops/lock.h>
#include <asm-generic/bitops/sched.h>
#include <asm-generic/bitops/le.h>
#include <asm-generic/bitops/ext2-atomic.h>
#endif /* __ASM_METAG_BITOPS_H */
#ifndef __ASM_METAG_CMPXCHG_H
#define __ASM_METAG_CMPXCHG_H
#include <asm/barrier.h>
#if defined(CONFIG_METAG_ATOMICITY_IRQSOFF)
#include <asm/cmpxchg_irq.h>
#elif defined(CONFIG_METAG_ATOMICITY_LOCK1)
#include <asm/cmpxchg_lock1.h>
#elif defined(CONFIG_METAG_ATOMICITY_LNKGET)
#include <asm/cmpxchg_lnkget.h>
#endif
extern void __xchg_called_with_bad_pointer(void);
#define __xchg(ptr, x, size) \
({ \
unsigned long __xchg__res; \
volatile void *__xchg_ptr = (ptr); \
switch (size) { \
case 4: \
__xchg__res = xchg_u32(__xchg_ptr, x); \
break; \
case 1: \
__xchg__res = xchg_u8(__xchg_ptr, x); \
break; \
default: \
__xchg_called_with_bad_pointer(); \
__xchg__res = x; \
break; \
} \
\
__xchg__res; \
})
#define xchg(ptr, x) \
((__typeof__(*(ptr)))__xchg((ptr), (unsigned long)(x), sizeof(*(ptr))))
/* This function doesn't exist, so you'll get a linker error
* if something tries to do an invalid cmpxchg(). */
extern void __cmpxchg_called_with_bad_pointer(void);
static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
unsigned long new, int size)
{
switch (size) {
case 4:
return __cmpxchg_u32(ptr, old, new);
}
__cmpxchg_called_with_bad_pointer();
return old;
}
#define __HAVE_ARCH_CMPXCHG 1
#define cmpxchg(ptr, o, n) \
({ \
__typeof__(*(ptr)) _o_ = (o); \
__typeof__(*(ptr)) _n_ = (n); \
(__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \
(unsigned long)_n_, \
sizeof(*(ptr))); \
})
#endif /* __ASM_METAG_CMPXCHG_H */
#ifndef __ASM_METAG_CMPXCHG_IRQ_H
#define __ASM_METAG_CMPXCHG_IRQ_H
#include <linux/irqflags.h>
static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
{
unsigned long flags, retval;
local_irq_save(flags);
retval = *m;
*m = val;
local_irq_restore(flags);
return retval;
}
static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val)
{
unsigned long flags, retval;
local_irq_save(flags);
retval = *m;
*m = val & 0xff;
local_irq_restore(flags);
return retval;
}
static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
unsigned long new)
{
__u32 retval;
unsigned long flags;
local_irq_save(flags);
retval = *m;
if (retval == old)
*m = new;
local_irq_restore(flags); /* implies memory barrier */
return retval;
}
#endif /* __ASM_METAG_CMPXCHG_IRQ_H */
#ifndef __ASM_METAG_CMPXCHG_LNKGET_H
#define __ASM_METAG_CMPXCHG_LNKGET_H
static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
{
int temp, old;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" LNKSETD [%2], %3\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
#ifdef CONFIG_METAG_LNKGET_AROUND_CACHE
" DCACHE [%2], %0\n"
#endif
: "=&d" (temp), "=&d" (old)
: "da" (m), "da" (val)
: "cc"
);
smp_mb();
return old;
}
static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val)
{
int temp, old;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" LNKSETD [%2], %3\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
#ifdef CONFIG_METAG_LNKGET_AROUND_CACHE
" DCACHE [%2], %0\n"
#endif
: "=&d" (temp), "=&d" (old)
: "da" (m), "da" (val & 0xff)
: "cc"
);
smp_mb();
return old;
}
static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
unsigned long new)
{
__u32 retval, temp;
smp_mb();
asm volatile (
"1: LNKGETD %1, [%2]\n"
" CMP %1, %3\n"
" LNKSETDEQ [%2], %4\n"
" BNE 2f\n"
" DEFR %0, TXSTAT\n"
" ANDT %0, %0, #HI(0x3f000000)\n"
" CMPT %0, #HI(0x02000000)\n"
" BNZ 1b\n"
#ifdef CONFIG_METAG_LNKGET_AROUND_CACHE
" DCACHE [%2], %0\n"
#endif
"2:\n"
: "=&d" (temp), "=&da" (retval)
: "da" (m), "bd" (old), "da" (new)
: "cc"
);
smp_mb();
return retval;
}
#endif /* __ASM_METAG_CMPXCHG_LNKGET_H */
#ifndef __ASM_METAG_CMPXCHG_LOCK1_H
#define __ASM_METAG_CMPXCHG_LOCK1_H
#include <asm/global_lock.h>
/* Use LOCK2 as these have to be atomic w.r.t. ordinary accesses. */
static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
{
unsigned long flags, retval;
__global_lock2(flags);
fence();
retval = *m;
*m = val;
__global_unlock2(flags);
return retval;
}
static inline unsigned long xchg_u8(volatile u8 *m, unsigned long val)
{
unsigned long flags, retval;
__global_lock2(flags);
fence();
retval = *m;
*m = val & 0xff;
__global_unlock2(flags);
return retval;
}
static inline unsigned long __cmpxchg_u32(volatile int *m, unsigned long old,
unsigned long new)
{
__u32 retval;
unsigned long flags;
__global_lock2(flags);
retval = *m;
if (retval == old) {
fence();
*m = new;
}
__global_unlock2(flags);
return retval;
}
#endif /* __ASM_METAG_CMPXCHG_LOCK1_H */
#ifndef __ASM_METAG_GLOBAL_LOCK_H
#define __ASM_METAG_GLOBAL_LOCK_H
#include <asm/metag_mem.h>
/**
* __global_lock1() - Acquire global voluntary lock (LOCK1).
* @flags: Variable to store flags into.
*
* Acquires the Meta global voluntary lock (LOCK1), also taking care to disable
* all triggers so we cannot be interrupted, and to enforce a compiler barrier
* so that the compiler cannot reorder memory accesses across the lock.
*
* No other hardware thread will be able to acquire the voluntary or exclusive
* locks until the voluntary lock is released with @__global_unlock1, but they
* may continue to execute as long as they aren't trying to acquire either of
* the locks.
*/
#define __global_lock1(flags) do { \
unsigned int __trval; \
asm volatile("MOV %0,#0\n\t" \
"SWAP %0,TXMASKI\n\t" \
"LOCK1" \
: "=r" (__trval) \
: \
: "memory"); \
(flags) = __trval; \
} while (0)
/**
* __global_unlock1() - Release global voluntary lock (LOCK1).
* @flags: Variable to restore flags from.
*
* Releases the Meta global voluntary lock (LOCK1) acquired with
* @__global_lock1, also taking care to re-enable triggers, and to enforce a
* compiler barrier so that the compiler cannot reorder memory accesses across
* the unlock.
*
* This immediately allows another hardware thread to acquire the voluntary or
* exclusive locks.
*/
#define __global_unlock1(flags) do { \
unsigned int __trval = (flags); \
asm volatile("LOCK0\n\t" \
"MOV TXMASKI,%0" \
: \
: "r" (__trval) \
: "memory"); \
} while (0)
/**
* __global_lock2() - Acquire global exclusive lock (LOCK2).
* @flags: Variable to store flags into.
*
* Acquires the Meta global voluntary lock and global exclusive lock (LOCK2),
* also taking care to disable all triggers so we cannot be interrupted, to take
* the atomic lock (system event) and to enforce a compiler barrier so that the
* compiler cannot reorder memory accesses across the lock.
*
* No other hardware thread will be able to execute code until the locks are
* released with @__global_unlock2.
*/
#define __global_lock2(flags) do { \
unsigned int __trval; \
unsigned int __aloc_hi = LINSYSEVENT_WR_ATOMIC_LOCK & 0xFFFF0000; \
asm volatile("MOV %0,#0\n\t" \
"SWAP %0,TXMASKI\n\t" \
"LOCK2\n\t" \
"SETD [%1+#0x40],D1RtP" \
: "=r&" (__trval) \
: "u" (__aloc_hi) \
: "memory"); \
(flags) = __trval; \
} while (0)
/**
* __global_unlock2() - Release global exclusive lock (LOCK2).
* @flags: Variable to restore flags from.
*
* Releases the Meta global exclusive lock (LOCK2) and global voluntary lock
* acquired with @__global_lock2, also taking care to release the atomic lock
* (system event), re-enable triggers, and to enforce a compiler barrier so that
* the compiler cannot reorder memory accesses across the unlock.
*
* This immediately allows other hardware threads to continue executing and one
* of them to acquire locks.
*/
#define __global_unlock2(flags) do { \
unsigned int __trval = (flags); \
unsigned int __alock_hi = LINSYSEVENT_WR_ATOMIC_LOCK & 0xFFFF0000; \
asm volatile("SETD [%1+#0x00],D1RtP\n\t" \
"LOCK0\n\t" \
"MOV TXMASKI,%0" \
: \
: "r" (__trval), \
"u" (__alock_hi) \
: "memory"); \
} while (0)
#endif /* __ASM_METAG_GLOBAL_LOCK_H */
#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
#ifdef CONFIG_METAG_ATOMICITY_LOCK1
#include <asm/spinlock_lock1.h>
#else
#include <asm/spinlock_lnkget.h>
#endif
#define arch_spin_unlock_wait(lock) \
do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
#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_H */
#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 */
#ifndef __ASM_SPINLOCK_LOCK1_H
#define __ASM_SPINLOCK_LOCK1_H
#include <asm/bug.h>
#include <asm/global_lock.h>
static inline int arch_spin_is_locked(arch_spinlock_t *lock)
{
int ret;
barrier();
ret = lock->lock;
WARN_ON(ret != 0 && ret != 1);
return ret;
}
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned int we_won = 0;
unsigned long flags;
again:
__global_lock1(flags);
if (lock->lock == 0) {
fence();
lock->lock = 1;
we_won = 1;
}
__global_unlock1(flags);
if (we_won == 0)
goto again;
WARN_ON(lock->lock != 1);
}
/* Returns 0 if failed to acquire lock */
static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
unsigned long flags;
unsigned int ret;
__global_lock1(flags);
ret = lock->lock;
if (ret == 0) {
fence();
lock->lock = 1;
}
__global_unlock1(flags);
return (ret == 0);
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
barrier();
WARN_ON(!lock->lock);
lock->lock = 0;
}
/*
* 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)
{
unsigned long flags;
unsigned int we_won = 0;
again:
__global_lock1(flags);
if (rw->lock == 0) {
fence();
rw->lock = 0x80000000;
we_won = 1;
}
__global_unlock1(flags);
if (we_won == 0)
goto again;
WARN_ON(rw->lock != 0x80000000);
}
static inline int arch_write_trylock(arch_rwlock_t *rw)
{
unsigned long flags;
unsigned int ret;
__global_lock1(flags);
ret = rw->lock;
if (ret == 0) {
fence();
rw->lock = 0x80000000;
}
__global_unlock1(flags);
return (ret == 0);
}
static inline void arch_write_unlock(arch_rwlock_t *rw)
{
barrier();
WARN_ON(rw->lock != 0x80000000);
rw->lock = 0;
}
/* write_can_lock - would write_trylock() succeed? */
static inline int arch_write_can_lock(arch_rwlock_t *rw)
{
unsigned int ret;
barrier();
ret = rw->lock;
return (ret == 0);
}
/*
* 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)
{
unsigned long flags;
unsigned int we_won = 0, ret;
again:
__global_lock1(flags);
ret = rw->lock;
if (ret < 0x80000000) {
fence();
rw->lock = ret + 1;
we_won = 1;
}
__global_unlock1(flags);
if (!we_won)
goto again;
}
static inline void arch_read_unlock(arch_rwlock_t *rw)
{
unsigned long flags;
unsigned int ret;
__global_lock1(flags);
fence();
ret = rw->lock--;
__global_unlock1(flags);
WARN_ON(ret == 0);
}
static inline int arch_read_trylock(arch_rwlock_t *rw)
{
unsigned long flags;
unsigned int ret;
__global_lock1(flags);
ret = rw->lock;
if (ret < 0x80000000) {
fence();
rw->lock = ret + 1;
}
__global_unlock1(flags);
return (ret < 0x80000000);
}
/* read_can_lock - would read_trylock() succeed? */
static inline int arch_read_can_lock(arch_rwlock_t *rw)
{
unsigned int ret;
barrier();
ret = rw->lock;
return (ret < 0x80000000);
}
#endif /* __ASM_SPINLOCK_LOCK1_H */
#ifndef _ASM_METAG_SPINLOCK_TYPES_H
#define _ASM_METAG_SPINLOCK_TYPES_H
#ifndef __LINUX_SPINLOCK_TYPES_H
# error "please don't include this file directly"
#endif
typedef struct {
volatile unsigned int lock;
} arch_spinlock_t;
#define __ARCH_SPIN_LOCK_UNLOCKED { 0 }
typedef struct {
volatile unsigned int lock;
} arch_rwlock_t;
#define __ARCH_RW_LOCK_UNLOCKED { 0 }
#endif /* _ASM_METAG_SPINLOCK_TYPES_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment