2007-10-01 07:15:00 +04:00
/*
* 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 ) 2003 , 06 , 07 by Ralf Baechle ( ralf @ linux - mips . org )
*/
# ifndef __ASM_CMPXCHG_H
# define __ASM_CMPXCHG_H
# include <linux/irqflags.h>
# define __HAVE_ARCH_CMPXCHG 1
# define __cmpxchg_asm(ld, st, m, old, new) \
( { \
__typeof ( * ( m ) ) __ret ; \
\
if ( cpu_has_llsc & & R10000_LLSC_WAR ) { \
__asm__ __volatile__ ( \
" .set push \n " \
" .set noat \n " \
" .set mips3 \n " \
" 1: " ld " %0, %2 # __cmpxchg_asm \n " \
" bne %0, %z3, 2f \n " \
" .set mips0 \n " \
" move $1, %z4 \n " \
" .set mips3 \n " \
" " st " $1, %1 \n " \
" beqzl $1, 1b \n " \
" 2: \n " \
" .set pop \n " \
: " =&r " ( __ret ) , " =R " ( * m ) \
: " R " ( * m ) , " Jr " ( old ) , " Jr " ( new ) \
: " memory " ) ; \
} else if ( cpu_has_llsc ) { \
__asm__ __volatile__ ( \
" .set push \n " \
" .set noat \n " \
" .set mips3 \n " \
" 1: " ld " %0, %2 # __cmpxchg_asm \n " \
" bne %0, %z3, 2f \n " \
" .set mips0 \n " \
" move $1, %z4 \n " \
" .set mips3 \n " \
" " st " $1, %1 \n " \
" beqz $1, 3f \n " \
" 2: \n " \
" .subsection 2 \n " \
" 3: b 1b \n " \
" .previous \n " \
" .set pop \n " \
: " =&r " ( __ret ) , " =R " ( * m ) \
: " R " ( * m ) , " Jr " ( old ) , " Jr " ( new ) \
: " memory " ) ; \
} else { \
unsigned long __flags ; \
\
raw_local_irq_save ( __flags ) ; \
__ret = * m ; \
if ( __ret = = old ) \
* m = new ; \
raw_local_irq_restore ( __flags ) ; \
} \
\
__ret ; \
} )
/*
* 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 ) ;
2007-10-12 02:46:15 +04:00
# define __cmpxchg(ptr, old, new, barrier) \
2007-10-01 07:15:00 +04:00
( { \
__typeof__ ( ptr ) __ptr = ( ptr ) ; \
__typeof__ ( * ( ptr ) ) __old = ( old ) ; \
__typeof__ ( * ( ptr ) ) __new = ( new ) ; \
__typeof__ ( * ( ptr ) ) __res = 0 ; \
\
barrier ; \
\
switch ( sizeof ( * ( __ptr ) ) ) { \
case 4 : \
__res = __cmpxchg_asm ( " ll " , " sc " , __ptr , __old , __new ) ; \
break ; \
case 8 : \
if ( sizeof ( long ) = = 8 ) { \
__res = __cmpxchg_asm ( " lld " , " scd " , __ptr , \
__old , __new ) ; \
break ; \
} \
default : \
__cmpxchg_called_with_bad_pointer ( ) ; \
break ; \
} \
\
barrier ; \
\
__res ; \
} )
# define cmpxchg(ptr, old, new) __cmpxchg(ptr, old, new, smp_llsc_mb())
2007-10-12 02:46:15 +04:00
# define cmpxchg_local(ptr, old, new) __cmpxchg(ptr, old, new, )
2007-10-01 07:15:00 +04:00
2008-02-07 11:16:09 +03:00
# define cmpxchg64(ptr, o, n) \
( { \
BUILD_BUG_ON ( sizeof ( * ( ptr ) ) ! = 8 ) ; \
cmpxchg ( ( ptr ) , ( o ) , ( n ) ) ; \
} )
# ifdef CONFIG_64BIT
# define cmpxchg64_local(ptr, o, n) \
( { \
BUILD_BUG_ON ( sizeof ( * ( ptr ) ) ! = 8 ) ; \
cmpxchg_local ( ( ptr ) , ( o ) , ( n ) ) ; \
} )
# else
# include <asm-generic/cmpxchg-local.h>
# define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
# endif
2007-10-01 07:15:00 +04:00
# endif /* __ASM_CMPXCHG_H */