From 5c105d9f3fd086aff195d3849dcf847d6b0bd927 Mon Sep 17 00:00:00 2001 From: blogic Date: Fri, 5 Oct 2012 10:12:53 +0000 Subject: branch Attitude Adjustment git-svn-id: svn://svn.openwrt.org/openwrt/branches/attitude_adjustment@33625 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../files/arch/ubicom32/include/asm/atomic.h | 353 +++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h (limited to 'target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h') diff --git a/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h b/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h new file mode 100644 index 000000000..aaf772602 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h @@ -0,0 +1,353 @@ +/* + * arch/ubicom32/include/asm/atomic.h + * Atomic operations definitions for Ubicom32 architecture. + * + * (C) Copyright 2009, Ubicom, Inc. + * + * This file is part of the Ubicom32 Linux Kernel Port. + * + * The Ubicom32 Linux Kernel Port is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * The Ubicom32 Linux Kernel Port is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Ubicom32 Linux Kernel Port. If not, + * see . + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + */ +#ifndef _ASM_UBICOM32_ATOMIC_H +#define _ASM_UBICOM32_ATOMIC_H + +#include +#include +#include + +/* + * Most instructions on the Ubicom32 processor are atomic in that they + * execute in one clock cycle. However, Linux has several operations + * (e.g. compare and swap) which will require more than a single instruction + * to perform. To achieve this, the Ubicom32 processor uses a single + * global bit in a scratchpad register as a critical section lock. All + * atomic operations acquire this lock. + * + * NOTE: To AVOID DEADLOCK(s), the atomic lock must only be used for atomic + * operations or by the ldsr to avoid disabling a thread performing an atomic + * operation. + * + * Do not attempt to disable interrupts while holding the atomic operations + * lock or you will DEADLOCK the system. + */ + +#define ATOMIC_INIT(i) { (i) } + +/* + * __atomic_add() + * Add i to v and return the result. + */ +static inline void __atomic_add(int i, atomic_t *v) +{ + atomic_t *vt = v; + + __atomic_lock_acquire(); + vt->counter += i; + __atomic_lock_release(); +} + +/* + * __atomic_sub() + * Subtract i from v and return the result. + */ +static inline void __atomic_sub(int i, atomic_t *v) +{ + atomic_t *vt = v; + + __atomic_lock_acquire(); + vt->counter -= i; + __atomic_lock_release(); +} + +/* + * __atomic_add_return() + * Add i to v and return the result. + * + * The implementation here looks rather odd because we appear to be doing + * the addition twice. In fact that's exactly what we're doing but with + * the ubicom32 instruction set we can do the inner load and add with two + * instructions whereas generating both the atomic result and the "ret" + * result requires three instructions. The second add is generally only as + * costly as a move instruction and in cases where we compare the result + * with a constant the compiler can fold two constant values and do a + * single instruction, thus saving an instruction overall! + * + * At the worst we save one instruction inside the atomic lock. + */ +static inline int __atomic_add_return(int i, atomic_t *v) +{ + int ret; + atomic_t *vt = v; + + __atomic_lock_acquire(); + ret = vt->counter; + vt->counter = ret + i; + __atomic_lock_release(); + + return ret + i; +} + +/* + * __atomic_sub_return() + * Subtract i from v and return the result. + * + * The implementation here looks rather odd because we appear to be doing + * the subtraction twice. In fact that's exactly what we're doing but with + * the ubicom32 instruction set we can do the inner load and sub with two + * instructions whereas generating both the atomic result and the "ret" + * result requires three instructions. The second sub is generally only as + * costly as a move instruction and in cases where we compare the result + * with a constant the compiler can fold two constant values and do a + * single instruction, thus saving an instruction overall! + * + * At the worst we save one instruction inside the atomic lock. + */ +static inline int __atomic_sub_return(int i, atomic_t *v) +{ + int ret; + atomic_t *vt = v; + + __atomic_lock_acquire(); + ret = vt->counter; + vt->counter = ret - i; + __atomic_lock_release(); + + return ret - i; +} + +/* + * PUBLIC API FOR ATOMIC! + */ +#define atomic_add(i,v) (__atomic_add( ((int)i),(v))) +#define atomic_sub(i,v) (__atomic_sub( ((int)i),(v))) +#define atomic_inc(v) (__atomic_add( 1,(v))) +#define atomic_dec(v) (__atomic_sub( 1,(v))) +#define atomic_add_return(i,v) (__atomic_add_return( ((int)i),(v))) +#define atomic_sub_return(i,v) (__atomic_sub_return( ((int)i),(v))) +#define atomic_inc_return(v) (__atomic_add_return( 1,(v))) +#define atomic_dec_return(v) (__atomic_sub_return( 1,(v))) +#define atomic_inc_and_test(v) (atomic_inc_return(v) == 0) +#define atomic_dec_and_test(v) (atomic_dec_return(v) == 0) +#define atomic_add_negative(a, v) (atomic_add_return((a), (v)) < 0) +#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0) + +/* + * atomic_read() + * Acquire the atomic lock and read the variable. + */ +static inline int atomic_read(const atomic_t *v) +{ + int ret; + const atomic_t *vt = v; + + __atomic_lock_acquire(); + ret = vt->counter; + __atomic_lock_release(); + + return ret; +} + +/* + * atomic_set() + * Acquire the atomic lock and set the variable. + */ +static inline void atomic_set(atomic_t *v, int i) +{ + atomic_t *vt = v; + + __atomic_lock_acquire(); + vt->counter = i; + __atomic_lock_release(); +} + +/* + * atomic_cmpxchg + * Acquire the atomic lock and exchange if current == old. + */ +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int prev; + atomic_t *vt = v; + + __atomic_lock_acquire(); + prev = vt->counter; + if (prev == old) { + vt->counter = new; + } + __atomic_lock_release(); + + return prev; +} + +/* + * atomic_xchg() + * Acquire the atomic lock and exchange values. + */ +static inline int atomic_xchg(atomic_t *v, int new) +{ + int prev; + atomic_t *vt = v; + + __atomic_lock_acquire(); + prev = vt->counter; + vt->counter = new; + __atomic_lock_release(); + + return prev; +} + +/* + * atomic_add_unless() + * Acquire the atomic lock and add a unless the value is u. + */ +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int prev; + atomic_t *vt = v; + + __atomic_lock_acquire(); + prev = vt->counter; + if (prev != u) { + vt->counter += a; + __atomic_lock_release(); + return 1; + } + + __atomic_lock_release(); + return 0; +} + +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) +#include +#else +#include +#endif + +/* + * The following is not a real function. The compiler should remove the function + * call as long as the user does not pass in a size that __xchg and __cmpxchg + * are not prepared for. If the user does pass in an unknown size, the user + * will get a link time error. + * + * The no return is to prevent a compiler error that can occur when dealing with + * uninitialized variables. Given that the function doesn't exist there is no + * net effect (and if it did it would not return). + */ +extern void __xchg_called_with_bad_pointer(void) __attribute__((noreturn)); + +/* + * __xchg() + * Xchange *ptr for x atomically. + * + * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an + * atomic exchange instruction so we use the global atomic_lock. + */ +static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size) +{ + unsigned long ret; + + __atomic_lock_acquire(); + + switch (size) { + case 1: + ret = *(volatile unsigned char *)ptr; + *(volatile unsigned char *)ptr = x; + break; + + case 2: + ret = *(volatile unsigned short *)ptr; + *(volatile unsigned short *)ptr = x; + break; + + case 4: + ret = *(volatile unsigned int *)ptr; + *(volatile unsigned int *)ptr = x; + break; + + default: + __xchg_called_with_bad_pointer(); + break; + } + __atomic_lock_release(); + return ret; +} + +#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) + +/* + * __cmpxchg() + * Compare and Xchange *ptr for x atomically. + * + * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an + * atomic exchange instruction so we use the global atomic_lock. + */ +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long next, int size) +{ + unsigned long prev; + + __atomic_lock_acquire(); + switch (size) { + case 1: + prev = *(u8 *)ptr; + if (prev == old) { + *(u8 *)ptr = (u8)next; + } + break; + + case 2: + prev = *(u16 *)ptr; + if (prev == old) { + *(u16 *)ptr = (u16)next; + } + break; + + case 4: + prev = *(u32 *)ptr; + if (prev == old) { + *(u32 *)ptr = (u32)next; + } + break; + + default: + __xchg_called_with_bad_pointer(); + break; + } + __atomic_lock_release(); + return prev; +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), (unsigned long)(n), sizeof(*(ptr)))) + +#define cmpxchg(ptr, o, n) __cmpxchg((ptr), (o), (n), sizeof(*(ptr))) + +#define smp_mb__before_atomic_inc() asm volatile ("" : : : "memory") +#define smp_mb__after_atomic_inc() asm volatile ("" : : : "memory") +#define smp_mb__before_atomic_dec() asm volatile ("" : : : "memory") +#define smp_mb__after_atomic_dec() asm volatile ("" : : : "memory") + +#endif /* _ASM_UBICOM32_ATOMIC_H */ -- cgit v1.2.3