Skip to content

Commit

Permalink
sh: add J2 atomics using the cas.l instruction
Browse files Browse the repository at this point in the history
Signed-off-by: Rich Felker <dalias@libc.org>
  • Loading branch information
Rich Felker committed Aug 5, 2016
1 parent 834da19 commit 2b47d54
Show file tree
Hide file tree
Showing 9 changed files with 481 additions and 216 deletions.
8 changes: 8 additions & 0 deletions arch/sh/include/asm/atomic.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#ifndef __ASM_SH_ATOMIC_H
#define __ASM_SH_ATOMIC_H

#if defined(CONFIG_CPU_J2)

#include <asm-generic/atomic.h>

#else

/*
* Atomic operations that C can't guarantee us. Useful for
* resource counting etc..
Expand Down Expand Up @@ -63,4 +69,6 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)
return c;
}

#endif /* CONFIG_CPU_J2 */

#endif /* __ASM_SH_ATOMIC_H */
5 changes: 5 additions & 0 deletions arch/sh/include/asm/barrier.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
#define wmb() mb()
#define ctrl_barrier() __icbi(PAGE_OFFSET)
#else
#if defined(CONFIG_CPU_J2) && defined(CONFIG_SMP)
#define __smp_mb() do { int tmp = 0; __asm__ __volatile__ ("cas.l %0,%0,@%1" : "+r"(tmp) : "z"(&tmp) : "memory", "t"); } while(0)
#define __smp_rmb() __smp_mb()
#define __smp_wmb() __smp_mb()
#endif
#define ctrl_barrier() __asm__ __volatile__ ("nop;nop;nop;nop;nop;nop;nop;nop")
#endif

Expand Down
93 changes: 93 additions & 0 deletions arch/sh/include/asm/bitops-cas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#ifndef __ASM_SH_BITOPS_CAS_H
#define __ASM_SH_BITOPS_CAS_H

static inline unsigned __bo_cas(volatile unsigned *p, unsigned old, unsigned new)
{
__asm__ __volatile__("cas.l %1,%0,@r0"
: "+r"(new)
: "r"(old), "z"(p)
: "t", "memory" );
return new;
}

static inline void set_bit(int nr, volatile void *addr)
{
unsigned mask, old;
volatile unsigned *a = addr;

a += nr >> 5;
mask = 1U << (nr & 0x1f);

do old = *a;
while (__bo_cas(a, old, old|mask) != old);
}

static inline void clear_bit(int nr, volatile void *addr)
{
unsigned mask, old;
volatile unsigned *a = addr;

a += nr >> 5;
mask = 1U << (nr & 0x1f);

do old = *a;
while (__bo_cas(a, old, old&~mask) != old);
}

static inline void change_bit(int nr, volatile void *addr)
{
unsigned mask, old;
volatile unsigned *a = addr;

a += nr >> 5;
mask = 1U << (nr & 0x1f);

do old = *a;
while (__bo_cas(a, old, old^mask) != old);
}

static inline int test_and_set_bit(int nr, volatile void *addr)
{
unsigned mask, old;
volatile unsigned *a = addr;

a += nr >> 5;
mask = 1U << (nr & 0x1f);

do old = *a;
while (__bo_cas(a, old, old|mask) != old);

return !!(old & mask);
}

static inline int test_and_clear_bit(int nr, volatile void *addr)
{
unsigned mask, old;
volatile unsigned *a = addr;

a += nr >> 5;
mask = 1U << (nr & 0x1f);

do old = *a;
while (__bo_cas(a, old, old&~mask) != old);

return !!(old & mask);
}

static inline int test_and_change_bit(int nr, volatile void *addr)
{
unsigned mask, old;
volatile unsigned *a = addr;

a += nr >> 5;
mask = 1U << (nr & 0x1f);

do old = *a;
while (__bo_cas(a, old, old^mask) != old);

return !!(old & mask);
}

#include <asm-generic/bitops/non-atomic.h>

#endif /* __ASM_SH_BITOPS_CAS_H */
2 changes: 2 additions & 0 deletions arch/sh/include/asm/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <asm/bitops-op32.h>
#elif defined(CONFIG_CPU_SH4A)
#include <asm/bitops-llsc.h>
#elif defined(CONFIG_CPU_J2) && defined(CONFIG_SMP)
#include <asm/bitops-cas.h>
#else
#include <asm-generic/bitops/atomic.h>
#include <asm-generic/bitops/non-atomic.h>
Expand Down
24 changes: 24 additions & 0 deletions arch/sh/include/asm/cmpxchg-cas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef __ASM_SH_CMPXCHG_CAS_H
#define __ASM_SH_CMPXCHG_CAS_H

static inline unsigned long
__cmpxchg_u32(volatile u32 *m, unsigned long old, unsigned long new)
{
__asm__ __volatile__("cas.l %1,%0,@r0"
: "+r"(new)
: "r"(old), "z"(m)
: "t", "memory" );
return new;
}

static inline unsigned long xchg_u32(volatile u32 *m, unsigned long val)
{
unsigned long old;
do old = *m;
while (__cmpxchg_u32(m, old, val) != old);
return old;
}

#include <asm/cmpxchg-xchg.h>

#endif /* __ASM_SH_CMPXCHG_CAS_H */
2 changes: 2 additions & 0 deletions arch/sh/include/asm/cmpxchg.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <asm/cmpxchg-grb.h>
#elif defined(CONFIG_CPU_SH4A)
#include <asm/cmpxchg-llsc.h>
#elif defined(CONFIG_CPU_J2) && defined(CONFIG_SMP)
#include <asm/cmpxchg-cas.h>
#else
#include <asm/cmpxchg-irq.h>
#endif
Expand Down
117 changes: 117 additions & 0 deletions arch/sh/include/asm/spinlock-cas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* include/asm-sh/spinlock-cas.h
*
* Copyright (C) 2015 SEI
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#ifndef __ASM_SH_SPINLOCK_CAS_H
#define __ASM_SH_SPINLOCK_CAS_H

#include <asm/barrier.h>
#include <asm/processor.h>

static inline unsigned __sl_cas(volatile unsigned *p, unsigned old, unsigned new)
{
__asm__ __volatile__("cas.l %1,%0,@r0"
: "+r"(new)
: "r"(old), "z"(p)
: "t", "memory" );
return new;
}

/*
* Your basic SMP spinlocks, allowing only a single CPU anywhere
*/

#define arch_spin_is_locked(x) ((x)->lock <= 0)
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)

static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
{
smp_cond_load_acquire(&lock->lock, VAL > 0);
}

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
while (!__sl_cas(&lock->lock, 1, 0));
}

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
__sl_cas(&lock->lock, 0, 1);
}

static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
return __sl_cas(&lock->lock, 1, 0);
}

/*
* Read-write spinlocks, allowing multiple readers but only one writer.
*
* NOTE! it is quite common to have readers in interrupts but no interrupt
* writers. For those circumstances we can "mix" irq-safe locks - any writer
* needs to get a irq-safe write-lock, but readers can get non-irqsafe
* read-locks.
*/

/**
* read_can_lock - would read_trylock() succeed?
* @lock: the rwlock in question.
*/
#define arch_read_can_lock(x) ((x)->lock > 0)

/**
* write_can_lock - would write_trylock() succeed?
* @lock: the rwlock in question.
*/
#define arch_write_can_lock(x) ((x)->lock == RW_LOCK_BIAS)

static inline void arch_read_lock(arch_rwlock_t *rw)
{
unsigned old;
do old = rw->lock;
while (!old || __sl_cas(&rw->lock, old, old-1) != old);
}

static inline void arch_read_unlock(arch_rwlock_t *rw)
{
unsigned old;
do old = rw->lock;
while (__sl_cas(&rw->lock, old, old+1) != old);
}

static inline void arch_write_lock(arch_rwlock_t *rw)
{
while (__sl_cas(&rw->lock, RW_LOCK_BIAS, 0) != RW_LOCK_BIAS);
}

static inline void arch_write_unlock(arch_rwlock_t *rw)
{
__sl_cas(&rw->lock, 0, RW_LOCK_BIAS);
}

static inline int arch_read_trylock(arch_rwlock_t *rw)
{
unsigned old;
do old = rw->lock;
while (old && __sl_cas(&rw->lock, old, old-1) != old);
return !!old;
}

static inline int arch_write_trylock(arch_rwlock_t *rw)
{
return __sl_cas(&rw->lock, RW_LOCK_BIAS, 0) == RW_LOCK_BIAS;
}

#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_SH_SPINLOCK_CAS_H */
Loading

0 comments on commit 2b47d54

Please sign in to comment.