Skip to content

Commit

Permalink
metag: Atomics, locks and bitops
Browse files Browse the repository at this point in the history
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: James Hogan <james.hogan@imgtec.com>
  • Loading branch information
James Hogan committed Mar 2, 2013
1 parent 9b802d1 commit 6006c0d
Show file tree
Hide file tree
Showing 13 changed files with 1,395 additions and 0 deletions.
53 changes: 53 additions & 0 deletions arch/metag/include/asm/atomic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#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 */
234 changes: 234 additions & 0 deletions arch/metag/include/asm/atomic_lnkget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#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 */
Loading

0 comments on commit 6006c0d

Please sign in to comment.