diff options
Diffstat (limited to 'arch/powerpc/include/asm/cmpxchg.h')
| -rw-r--r-- | arch/powerpc/include/asm/cmpxchg.h | 310 | 
1 files changed, 310 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/cmpxchg.h b/arch/powerpc/include/asm/cmpxchg.h new file mode 100644 index 00000000000..d463c68fe7f --- /dev/null +++ b/arch/powerpc/include/asm/cmpxchg.h @@ -0,0 +1,310 @@ +#ifndef _ASM_POWERPC_CMPXCHG_H_ +#define _ASM_POWERPC_CMPXCHG_H_ + +#ifdef __KERNEL__ +#include <linux/compiler.h> +#include <asm/synch.h> +#include <asm/asm-compat.h> + +/* + * Atomic exchange + * + * Changes the memory location '*ptr' to be val and returns + * the previous value stored there. + */ +static __always_inline unsigned long +__xchg_u32(volatile void *p, unsigned long val) +{ +	unsigned long prev; + +	__asm__ __volatile__( +	PPC_RELEASE_BARRIER +"1:	lwarx	%0,0,%2 \n" +	PPC405_ERR77(0,%2) +"	stwcx.	%3,0,%2 \n\ +	bne-	1b" +	PPC_ACQUIRE_BARRIER +	: "=&r" (prev), "+m" (*(volatile unsigned int *)p) +	: "r" (p), "r" (val) +	: "cc", "memory"); + +	return prev; +} + +/* + * Atomic exchange + * + * Changes the memory location '*ptr' to be val and returns + * the previous value stored there. + */ +static __always_inline unsigned long +__xchg_u32_local(volatile void *p, unsigned long val) +{ +	unsigned long prev; + +	__asm__ __volatile__( +"1:	lwarx	%0,0,%2 \n" +	PPC405_ERR77(0,%2) +"	stwcx.	%3,0,%2 \n\ +	bne-	1b" +	: "=&r" (prev), "+m" (*(volatile unsigned int *)p) +	: "r" (p), "r" (val) +	: "cc", "memory"); + +	return prev; +} + +#ifdef CONFIG_PPC64 +static __always_inline unsigned long +__xchg_u64(volatile void *p, unsigned long val) +{ +	unsigned long prev; + +	__asm__ __volatile__( +	PPC_RELEASE_BARRIER +"1:	ldarx	%0,0,%2 \n" +	PPC405_ERR77(0,%2) +"	stdcx.	%3,0,%2 \n\ +	bne-	1b" +	PPC_ACQUIRE_BARRIER +	: "=&r" (prev), "+m" (*(volatile unsigned long *)p) +	: "r" (p), "r" (val) +	: "cc", "memory"); + +	return prev; +} + +static __always_inline unsigned long +__xchg_u64_local(volatile void *p, unsigned long val) +{ +	unsigned long prev; + +	__asm__ __volatile__( +"1:	ldarx	%0,0,%2 \n" +	PPC405_ERR77(0,%2) +"	stdcx.	%3,0,%2 \n\ +	bne-	1b" +	: "=&r" (prev), "+m" (*(volatile unsigned long *)p) +	: "r" (p), "r" (val) +	: "cc", "memory"); + +	return prev; +} +#endif + +/* + * This function doesn't exist, so you'll get a linker error + * if something tries to do an invalid xchg(). + */ +extern void __xchg_called_with_bad_pointer(void); + +static __always_inline unsigned long +__xchg(volatile void *ptr, unsigned long x, unsigned int size) +{ +	switch (size) { +	case 4: +		return __xchg_u32(ptr, x); +#ifdef CONFIG_PPC64 +	case 8: +		return __xchg_u64(ptr, x); +#endif +	} +	__xchg_called_with_bad_pointer(); +	return x; +} + +static __always_inline unsigned long +__xchg_local(volatile void *ptr, unsigned long x, unsigned int size) +{ +	switch (size) { +	case 4: +		return __xchg_u32_local(ptr, x); +#ifdef CONFIG_PPC64 +	case 8: +		return __xchg_u64_local(ptr, x); +#endif +	} +	__xchg_called_with_bad_pointer(); +	return x; +} +#define xchg(ptr,x)							     \ +  ({									     \ +     __typeof__(*(ptr)) _x_ = (x);					     \ +     (__typeof__(*(ptr))) __xchg((ptr), (unsigned long)_x_, sizeof(*(ptr))); \ +  }) + +#define xchg_local(ptr,x)						     \ +  ({									     \ +     __typeof__(*(ptr)) _x_ = (x);					     \ +     (__typeof__(*(ptr))) __xchg_local((ptr),				     \ +     		(unsigned long)_x_, sizeof(*(ptr))); 			     \ +  }) + +/* + * Compare and exchange - if *p == old, set it to new, + * and return the old value of *p. + */ +#define __HAVE_ARCH_CMPXCHG	1 + +static __always_inline unsigned long +__cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new) +{ +	unsigned int prev; + +	__asm__ __volatile__ ( +	PPC_RELEASE_BARRIER +"1:	lwarx	%0,0,%2		# __cmpxchg_u32\n\ +	cmpw	0,%0,%3\n\ +	bne-	2f\n" +	PPC405_ERR77(0,%2) +"	stwcx.	%4,0,%2\n\ +	bne-	1b" +	PPC_ACQUIRE_BARRIER +	"\n\ +2:" +	: "=&r" (prev), "+m" (*p) +	: "r" (p), "r" (old), "r" (new) +	: "cc", "memory"); + +	return prev; +} + +static __always_inline unsigned long +__cmpxchg_u32_local(volatile unsigned int *p, unsigned long old, +			unsigned long new) +{ +	unsigned int prev; + +	__asm__ __volatile__ ( +"1:	lwarx	%0,0,%2		# __cmpxchg_u32\n\ +	cmpw	0,%0,%3\n\ +	bne-	2f\n" +	PPC405_ERR77(0,%2) +"	stwcx.	%4,0,%2\n\ +	bne-	1b" +	"\n\ +2:" +	: "=&r" (prev), "+m" (*p) +	: "r" (p), "r" (old), "r" (new) +	: "cc", "memory"); + +	return prev; +} + +#ifdef CONFIG_PPC64 +static __always_inline unsigned long +__cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new) +{ +	unsigned long prev; + +	__asm__ __volatile__ ( +	PPC_RELEASE_BARRIER +"1:	ldarx	%0,0,%2		# __cmpxchg_u64\n\ +	cmpd	0,%0,%3\n\ +	bne-	2f\n\ +	stdcx.	%4,0,%2\n\ +	bne-	1b" +	PPC_ACQUIRE_BARRIER +	"\n\ +2:" +	: "=&r" (prev), "+m" (*p) +	: "r" (p), "r" (old), "r" (new) +	: "cc", "memory"); + +	return prev; +} + +static __always_inline unsigned long +__cmpxchg_u64_local(volatile unsigned long *p, unsigned long old, +			unsigned long new) +{ +	unsigned long prev; + +	__asm__ __volatile__ ( +"1:	ldarx	%0,0,%2		# __cmpxchg_u64\n\ +	cmpd	0,%0,%3\n\ +	bne-	2f\n\ +	stdcx.	%4,0,%2\n\ +	bne-	1b" +	"\n\ +2:" +	: "=&r" (prev), "+m" (*p) +	: "r" (p), "r" (old), "r" (new) +	: "cc", "memory"); + +	return prev; +} +#endif + +/* 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); + +static __always_inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, +	  unsigned int size) +{ +	switch (size) { +	case 4: +		return __cmpxchg_u32(ptr, old, new); +#ifdef CONFIG_PPC64 +	case 8: +		return __cmpxchg_u64(ptr, old, new); +#endif +	} +	__cmpxchg_called_with_bad_pointer(); +	return old; +} + +static __always_inline unsigned long +__cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, +	  unsigned int size) +{ +	switch (size) { +	case 4: +		return __cmpxchg_u32_local(ptr, old, new); +#ifdef CONFIG_PPC64 +	case 8: +		return __cmpxchg_u64_local(ptr, old, new); +#endif +	} +	__cmpxchg_called_with_bad_pointer(); +	return old; +} + +#define cmpxchg(ptr, o, n)						 \ +  ({									 \ +     __typeof__(*(ptr)) _o_ = (o);					 \ +     __typeof__(*(ptr)) _n_ = (n);					 \ +     (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_,		 \ +				    (unsigned long)_n_, sizeof(*(ptr))); \ +  }) + + +#define cmpxchg_local(ptr, o, n)					 \ +  ({									 \ +     __typeof__(*(ptr)) _o_ = (o);					 \ +     __typeof__(*(ptr)) _n_ = (n);					 \ +     (__typeof__(*(ptr))) __cmpxchg_local((ptr), (unsigned long)_o_,	 \ +				    (unsigned long)_n_, sizeof(*(ptr))); \ +  }) + +#ifdef CONFIG_PPC64 +#define cmpxchg64(ptr, o, n)						\ +  ({									\ +	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\ +	cmpxchg((ptr), (o), (n));					\ +  }) +#define cmpxchg64_local(ptr, o, n)					\ +  ({									\ +	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\ +	cmpxchg_local((ptr), (o), (n));					\ +  }) +#define cmpxchg64_relaxed	cmpxchg64_local +#else +#include <asm-generic/cmpxchg-local.h> +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#endif + +#endif /* __KERNEL__ */ +#endif /* _ASM_POWERPC_CMPXCHG_H_ */  | 
