2005-06-24 09:01:26 +04:00
/*
* include / asm - xtensa / atomic . h
*
* Atomic operations that C can ' t guarantee us . Useful for resource counting . .
*
* 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 .
*
* Copyright ( C ) 2001 - 2005 Tensilica Inc .
*/
# ifndef _XTENSA_ATOMIC_H
# define _XTENSA_ATOMIC_H
# include <linux/stringify.h>
typedef struct { volatile int counter ; } atomic_t ;
# ifdef __KERNEL__
# include <asm/processor.h>
# include <asm/system.h>
2005-09-23 08:44:23 +04:00
# define ATOMIC_INIT(i) { (i) }
2005-06-24 09:01:26 +04:00
/*
* This Xtensa implementation assumes that the right mechanism
* for exclusion is for locking interrupts to level 1.
*
* Locking interrupts looks like this :
*
* rsil a15 , 1
* < code >
* wsr a15 , PS
* rsync
*
* Note that a15 is used here because the register allocation
* done by the compiler is not guaranteed and a window overflow
* may not occur between the rsil and wsr instructions . By using
* a15 in the rsil , the machine is guaranteed to be in a state
* where no register reference will cause an overflow .
*/
/**
* atomic_read - read atomic variable
* @ v : pointer of type atomic_t
*
* Atomically reads the value of @ v .
*/
# define atomic_read(v) ((v)->counter)
/**
* atomic_set - set atomic variable
* @ v : pointer of type atomic_t
* @ i : required value
*
* Atomically sets the value of @ v to @ i .
*/
# define atomic_set(v,i) ((v)->counter = (i))
/**
* atomic_add - add integer to atomic variable
* @ i : integer value to add
* @ v : pointer of type atomic_t
*
* Atomically adds @ i to @ v .
*/
2005-09-04 02:57:53 +04:00
static inline void atomic_add ( int i , atomic_t * v )
2005-06-24 09:01:26 +04:00
{
unsigned int vval ;
__asm__ __volatile__ (
" rsil a15, " __stringify ( LOCKLEVEL ) " \n \t "
" l32i %0, %2, 0 \n \t "
" add %0, %0, %1 \n \t "
" s32i %0, %2, 0 \n \t "
" wsr a15, " __stringify ( PS ) " \n \t "
" rsync \n "
: " =&a " ( vval )
: " a " ( i ) , " a " ( v )
: " a15 " , " memory "
) ;
}
/**
* atomic_sub - subtract the atomic variable
* @ i : integer value to subtract
* @ v : pointer of type atomic_t
*
* Atomically subtracts @ i from @ v .
*/
2005-09-04 02:57:53 +04:00
static inline void atomic_sub ( int i , atomic_t * v )
2005-06-24 09:01:26 +04:00
{
unsigned int vval ;
__asm__ __volatile__ (
" rsil a15, " __stringify ( LOCKLEVEL ) " \n \t "
" l32i %0, %2, 0 \n \t "
" sub %0, %0, %1 \n \t "
" s32i %0, %2, 0 \n \t "
" wsr a15, " __stringify ( PS ) " \n \t "
" rsync \n "
: " =&a " ( vval )
: " a " ( i ) , " a " ( v )
: " a15 " , " memory "
) ;
}
/*
* We use atomic_ { add | sub } _return to define other functions .
*/
2005-09-04 02:57:53 +04:00
static inline int atomic_add_return ( int i , atomic_t * v )
2005-06-24 09:01:26 +04:00
{
unsigned int vval ;
__asm__ __volatile__ (
" rsil a15, " __stringify ( LOCKLEVEL ) " \n \t "
" l32i %0, %2, 0 \n \t "
" add %0, %0, %1 \n \t "
" s32i %0, %2, 0 \n \t "
" wsr a15, " __stringify ( PS ) " \n \t "
" rsync \n "
: " =&a " ( vval )
: " a " ( i ) , " a " ( v )
: " a15 " , " memory "
) ;
return vval ;
}
2005-09-04 02:57:53 +04:00
static inline int atomic_sub_return ( int i , atomic_t * v )
2005-06-24 09:01:26 +04:00
{
unsigned int vval ;
__asm__ __volatile__ (
" rsil a15, " __stringify ( LOCKLEVEL ) " \n \t "
" l32i %0, %2, 0 \n \t "
" sub %0, %0, %1 \n \t "
" s32i %0, %2, 0 \n \t "
" wsr a15, " __stringify ( PS ) " \n \t "
" rsync \n "
: " =&a " ( vval )
: " a " ( i ) , " a " ( v )
: " a15 " , " memory "
) ;
return vval ;
}
/**
* atomic_sub_and_test - subtract value from variable and test result
* @ i : integer value to subtract
* @ v : pointer of type atomic_t
*
* Atomically subtracts @ i from @ v and returns
* true if the result is zero , or false for all
* other cases .
*/
# define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0)
/**
* atomic_inc - increment atomic variable
* @ v : pointer of type atomic_t
*
* Atomically increments @ v by 1.
*/
# define atomic_inc(v) atomic_add(1,(v))
/**
* atomic_inc - increment atomic variable
* @ v : pointer of type atomic_t
*
* Atomically increments @ v by 1.
*/
# define atomic_inc_return(v) atomic_add_return(1,(v))
/**
* atomic_dec - decrement atomic variable
* @ v : pointer of type atomic_t
*
* Atomically decrements @ v by 1.
*/
# define atomic_dec(v) atomic_sub(1,(v))
/**
* atomic_dec_return - decrement atomic variable
* @ v : pointer of type atomic_t
*
* Atomically decrements @ v by 1.
*/
# define atomic_dec_return(v) atomic_sub_return(1,(v))
/**
* atomic_dec_and_test - decrement and test
* @ v : pointer of type atomic_t
*
* Atomically decrements @ v by 1 and
* returns true if the result is 0 , or false for all other
* cases .
*/
# define atomic_dec_and_test(v) (atomic_sub_return(1,(v)) == 0)
/**
* 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_add_return(1,(v)) == 0)
/**
* atomic_add_negative - add and test if negative
* @ v : pointer of type atomic_t
* @ i : integer value to add
*
* Atomically adds @ i to @ v and returns true
* if the result is negative , or false when
* result is greater than or equal to zero .
*/
# define atomic_add_negative(i,v) (atomic_add_return((i),(v)) < 0)
2005-11-14 03:07:24 +03:00
# define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
2006-01-10 02:59:17 +03:00
# define atomic_xchg(v, new) (xchg(&((v)->counter), new))
2005-06-24 09:01:26 +04:00
2005-11-14 03:07:25 +03:00
/**
* atomic_add_unless - add unless the number is a given value
* @ v : pointer of type atomic_t
* @ a : the amount to add to v . . .
* @ u : . . . unless v is equal to u .
*
* Atomically adds @ a to @ v , so long as it was not @ u .
* Returns non - zero if @ v was not @ u , and zero otherwise .
*/
2007-05-08 11:34:38 +04:00
static __inline__ int atomic_add_unless ( atomic_t * v , int a , int u )
{
int c , old ;
c = atomic_read ( v ) ;
for ( ; ; ) {
if ( unlikely ( c = = ( u ) ) )
break ;
old = atomic_cmpxchg ( ( v ) , c , c + ( a ) ) ;
if ( likely ( old = = c ) )
break ;
c = old ;
}
return c ! = ( u ) ;
}
2005-11-14 03:07:25 +03:00
# define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
2005-09-04 02:57:53 +04:00
static inline void atomic_clear_mask ( unsigned int mask , atomic_t * v )
2005-06-24 09:01:26 +04:00
{
unsigned int all_f = - 1 ;
unsigned int vval ;
__asm__ __volatile__ (
" rsil a15, " __stringify ( LOCKLEVEL ) " \n \t "
" l32i %0, %2, 0 \n \t "
" xor %1, %4, %3 \n \t "
" and %0, %0, %4 \n \t "
" s32i %0, %2, 0 \n \t "
" wsr a15, " __stringify ( PS ) " \n \t "
" rsync \n "
: " =&a " ( vval ) , " =a " ( mask )
: " a " ( v ) , " a " ( all_f ) , " 1 " ( mask )
: " a15 " , " memory "
) ;
}
2005-09-04 02:57:53 +04:00
static inline void atomic_set_mask ( unsigned int mask , atomic_t * v )
2005-06-24 09:01:26 +04:00
{
unsigned int vval ;
__asm__ __volatile__ (
" rsil a15, " __stringify ( LOCKLEVEL ) " \n \t "
" l32i %0, %2, 0 \n \t "
" or %0, %0, %1 \n \t "
" s32i %0, %2, 0 \n \t "
" wsr a15, " __stringify ( PS ) " \n \t "
" rsync \n "
: " =&a " ( vval )
: " a " ( mask ) , " a " ( v )
: " a15 " , " memory "
) ;
}
/* Atomic operations are already serializing */
# 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()
2006-01-06 11:11:20 +03:00
# include <asm-generic/atomic.h>
2005-06-24 09:01:26 +04:00
# endif /* __KERNEL__ */
# endif /* _XTENSA_ATOMIC_H */