| /* | |
| * Atomic xchg and cmpxchg operations. | |
| * | |
| * 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_CMPXCHG_H | |
| #define _XTENSA_CMPXCHG_H | |
| #ifndef __ASSEMBLY__ | |
| #include <linux/stringify.h> | |
| /* | |
| * cmpxchg | |
| */ | |
| static inline unsigned long | |
| __cmpxchg_u32(volatile int *p, int old, int new) | |
| { | |
| #if XCHAL_HAVE_S32C1I | |
| __asm__ __volatile__( | |
| " wsr %2, scompare1\n" | |
| " s32c1i %0, %1, 0\n" | |
| : "+a" (new) | |
| : "a" (p), "a" (old) | |
| : "memory" | |
| ); | |
| return new; | |
| #else | |
| __asm__ __volatile__( | |
| " rsil a15, "__stringify(TOPLEVEL)"\n" | |
| " l32i %0, %1, 0\n" | |
| " bne %0, %2, 1f\n" | |
| " s32i %3, %1, 0\n" | |
| "1:\n" | |
| " wsr a15, ps\n" | |
| " rsync\n" | |
| : "=&a" (old) | |
| : "a" (p), "a" (old), "r" (new) | |
| : "a15", "memory"); | |
| return old; | |
| #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 __inline__ unsigned long | |
| __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) | |
| { | |
| switch (size) { | |
| case 4: return __cmpxchg_u32(ptr, old, new); | |
| default: __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))); \ | |
| }) | |
| #include <asm-generic/cmpxchg-local.h> | |
| static inline unsigned long __cmpxchg_local(volatile void *ptr, | |
| unsigned long old, | |
| unsigned long new, int size) | |
| { | |
| switch (size) { | |
| case 4: | |
| return __cmpxchg_u32(ptr, old, new); | |
| default: | |
| return __cmpxchg_local_generic(ptr, old, new, size); | |
| } | |
| return old; | |
| } | |
| /* | |
| * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make | |
| * them available. | |
| */ | |
| #define cmpxchg_local(ptr, o, n) \ | |
| ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ | |
| (unsigned long)(n), sizeof(*(ptr)))) | |
| #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) | |
| #define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) | |
| /* | |
| * xchg_u32 | |
| * | |
| * 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. | |
| */ | |
| static inline unsigned long xchg_u32(volatile int * m, unsigned long val) | |
| { | |
| #if XCHAL_HAVE_S32C1I | |
| unsigned long tmp, result; | |
| __asm__ __volatile__( | |
| "1: l32i %1, %2, 0\n" | |
| " mov %0, %3\n" | |
| " wsr %1, scompare1\n" | |
| " s32c1i %0, %2, 0\n" | |
| " bne %0, %1, 1b\n" | |
| : "=&a" (result), "=&a" (tmp) | |
| : "a" (m), "a" (val) | |
| : "memory" | |
| ); | |
| return result; | |
| #else | |
| unsigned long tmp; | |
| __asm__ __volatile__( | |
| " rsil a15, "__stringify(TOPLEVEL)"\n" | |
| " l32i %0, %1, 0\n" | |
| " s32i %2, %1, 0\n" | |
| " wsr a15, ps\n" | |
| " rsync\n" | |
| : "=&a" (tmp) | |
| : "a" (m), "a" (val) | |
| : "a15", "memory"); | |
| return tmp; | |
| #endif | |
| } | |
| #define xchg(ptr,x) \ | |
| ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) | |
| /* | |
| * This only works if the compiler isn't horribly bad at optimizing. | |
| * gcc-2.5.8 reportedly can't handle this, but I define that one to | |
| * be dead anyway. | |
| */ | |
| extern void __xchg_called_with_bad_pointer(void); | |
| static __inline__ unsigned long | |
| __xchg(unsigned long x, volatile void * ptr, int size) | |
| { | |
| switch (size) { | |
| case 4: | |
| return xchg_u32(ptr, x); | |
| } | |
| __xchg_called_with_bad_pointer(); | |
| return x; | |
| } | |
| #endif /* __ASSEMBLY__ */ | |
| #endif /* _XTENSA_CMPXCHG_H */ |