162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Atomic xchg and cmpxchg operations. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 562306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 662306a36Sopenharmony_ci * for more details. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2001 - 2005 Tensilica Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#ifndef _XTENSA_CMPXCHG_H 1262306a36Sopenharmony_ci#define _XTENSA_CMPXCHG_H 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/bits.h> 1762306a36Sopenharmony_ci#include <linux/stringify.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * cmpxchg 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic inline unsigned long 2462306a36Sopenharmony_ci__cmpxchg_u32(volatile int *p, int old, int new) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 2762306a36Sopenharmony_ci unsigned long tmp, result; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci __asm__ __volatile__( 3062306a36Sopenharmony_ci "1: l32ex %[result], %[addr]\n" 3162306a36Sopenharmony_ci " bne %[result], %[cmp], 2f\n" 3262306a36Sopenharmony_ci " mov %[tmp], %[new]\n" 3362306a36Sopenharmony_ci " s32ex %[tmp], %[addr]\n" 3462306a36Sopenharmony_ci " getex %[tmp]\n" 3562306a36Sopenharmony_ci " beqz %[tmp], 1b\n" 3662306a36Sopenharmony_ci "2:\n" 3762306a36Sopenharmony_ci : [result] "=&a" (result), [tmp] "=&a" (tmp) 3862306a36Sopenharmony_ci : [new] "a" (new), [addr] "a" (p), [cmp] "a" (old) 3962306a36Sopenharmony_ci : "memory" 4062306a36Sopenharmony_ci ); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return result; 4362306a36Sopenharmony_ci#elif XCHAL_HAVE_S32C1I 4462306a36Sopenharmony_ci __asm__ __volatile__( 4562306a36Sopenharmony_ci " wsr %[cmp], scompare1\n" 4662306a36Sopenharmony_ci " s32c1i %[new], %[mem]\n" 4762306a36Sopenharmony_ci : [new] "+a" (new), [mem] "+m" (*p) 4862306a36Sopenharmony_ci : [cmp] "a" (old) 4962306a36Sopenharmony_ci : "memory" 5062306a36Sopenharmony_ci ); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return new; 5362306a36Sopenharmony_ci#else 5462306a36Sopenharmony_ci __asm__ __volatile__( 5562306a36Sopenharmony_ci " rsil a14, "__stringify(TOPLEVEL)"\n" 5662306a36Sopenharmony_ci " l32i %[old], %[mem]\n" 5762306a36Sopenharmony_ci " bne %[old], %[cmp], 1f\n" 5862306a36Sopenharmony_ci " s32i %[new], %[mem]\n" 5962306a36Sopenharmony_ci "1:\n" 6062306a36Sopenharmony_ci " wsr a14, ps\n" 6162306a36Sopenharmony_ci " rsync\n" 6262306a36Sopenharmony_ci : [old] "=&a" (old), [mem] "+m" (*p) 6362306a36Sopenharmony_ci : [cmp] "a" (old), [new] "r" (new) 6462306a36Sopenharmony_ci : "a14", "memory"); 6562306a36Sopenharmony_ci return old; 6662306a36Sopenharmony_ci#endif 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci/* This function doesn't exist, so you'll get a linker error 6962306a36Sopenharmony_ci * if something tries to do an invalid cmpxchg(). */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciextern void __cmpxchg_called_with_bad_pointer(void); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic __inline__ unsigned long 7462306a36Sopenharmony_ci__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci switch (size) { 7762306a36Sopenharmony_ci case 4: return __cmpxchg_u32(ptr, old, new); 7862306a36Sopenharmony_ci default: __cmpxchg_called_with_bad_pointer(); 7962306a36Sopenharmony_ci return old; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define arch_cmpxchg(ptr,o,n) \ 8462306a36Sopenharmony_ci ({ __typeof__(*(ptr)) _o_ = (o); \ 8562306a36Sopenharmony_ci __typeof__(*(ptr)) _n_ = (n); \ 8662306a36Sopenharmony_ci (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ 8762306a36Sopenharmony_ci (unsigned long)_n_, sizeof (*(ptr))); \ 8862306a36Sopenharmony_ci }) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#include <asm-generic/cmpxchg-local.h> 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic inline unsigned long __cmpxchg_local(volatile void *ptr, 9362306a36Sopenharmony_ci unsigned long old, 9462306a36Sopenharmony_ci unsigned long new, int size) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci switch (size) { 9762306a36Sopenharmony_ci case 4: 9862306a36Sopenharmony_ci return __cmpxchg_u32(ptr, old, new); 9962306a36Sopenharmony_ci default: 10062306a36Sopenharmony_ci return __generic_cmpxchg_local(ptr, old, new, size); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return old; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make 10862306a36Sopenharmony_ci * them available. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci#define arch_cmpxchg_local(ptr, o, n) \ 11162306a36Sopenharmony_ci ((__typeof__(*(ptr)))__generic_cmpxchg_local((ptr), (unsigned long)(o),\ 11262306a36Sopenharmony_ci (unsigned long)(n), sizeof(*(ptr)))) 11362306a36Sopenharmony_ci#define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n)) 11462306a36Sopenharmony_ci#define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n)) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * xchg_u32 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Note that a14 is used here because the register allocation 12062306a36Sopenharmony_ci * done by the compiler is not guaranteed and a window overflow 12162306a36Sopenharmony_ci * may not occur between the rsil and wsr instructions. By using 12262306a36Sopenharmony_ci * a14 in the rsil, the machine is guaranteed to be in a state 12362306a36Sopenharmony_ci * where no register reference will cause an overflow. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic inline unsigned long xchg_u32(volatile int * m, unsigned long val) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 12962306a36Sopenharmony_ci unsigned long tmp, result; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci __asm__ __volatile__( 13262306a36Sopenharmony_ci "1: l32ex %[result], %[addr]\n" 13362306a36Sopenharmony_ci " mov %[tmp], %[val]\n" 13462306a36Sopenharmony_ci " s32ex %[tmp], %[addr]\n" 13562306a36Sopenharmony_ci " getex %[tmp]\n" 13662306a36Sopenharmony_ci " beqz %[tmp], 1b\n" 13762306a36Sopenharmony_ci : [result] "=&a" (result), [tmp] "=&a" (tmp) 13862306a36Sopenharmony_ci : [val] "a" (val), [addr] "a" (m) 13962306a36Sopenharmony_ci : "memory" 14062306a36Sopenharmony_ci ); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return result; 14362306a36Sopenharmony_ci#elif XCHAL_HAVE_S32C1I 14462306a36Sopenharmony_ci unsigned long tmp, result; 14562306a36Sopenharmony_ci __asm__ __volatile__( 14662306a36Sopenharmony_ci "1: l32i %[tmp], %[mem]\n" 14762306a36Sopenharmony_ci " mov %[result], %[val]\n" 14862306a36Sopenharmony_ci " wsr %[tmp], scompare1\n" 14962306a36Sopenharmony_ci " s32c1i %[result], %[mem]\n" 15062306a36Sopenharmony_ci " bne %[result], %[tmp], 1b\n" 15162306a36Sopenharmony_ci : [result] "=&a" (result), [tmp] "=&a" (tmp), 15262306a36Sopenharmony_ci [mem] "+m" (*m) 15362306a36Sopenharmony_ci : [val] "a" (val) 15462306a36Sopenharmony_ci : "memory" 15562306a36Sopenharmony_ci ); 15662306a36Sopenharmony_ci return result; 15762306a36Sopenharmony_ci#else 15862306a36Sopenharmony_ci unsigned long tmp; 15962306a36Sopenharmony_ci __asm__ __volatile__( 16062306a36Sopenharmony_ci " rsil a14, "__stringify(TOPLEVEL)"\n" 16162306a36Sopenharmony_ci " l32i %[tmp], %[mem]\n" 16262306a36Sopenharmony_ci " s32i %[val], %[mem]\n" 16362306a36Sopenharmony_ci " wsr a14, ps\n" 16462306a36Sopenharmony_ci " rsync\n" 16562306a36Sopenharmony_ci : [tmp] "=&a" (tmp), [mem] "+m" (*m) 16662306a36Sopenharmony_ci : [val] "a" (val) 16762306a36Sopenharmony_ci : "a14", "memory"); 16862306a36Sopenharmony_ci return tmp; 16962306a36Sopenharmony_ci#endif 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define arch_xchg(ptr,x) \ 17362306a36Sopenharmony_ci ((__typeof__(*(ptr)))__arch_xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic inline u32 xchg_small(volatile void *ptr, u32 x, int size) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int off = (unsigned long)ptr % sizeof(u32); 17862306a36Sopenharmony_ci volatile u32 *p = ptr - off; 17962306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 18062306a36Sopenharmony_ci int bitoff = (sizeof(u32) - size - off) * BITS_PER_BYTE; 18162306a36Sopenharmony_ci#else 18262306a36Sopenharmony_ci int bitoff = off * BITS_PER_BYTE; 18362306a36Sopenharmony_ci#endif 18462306a36Sopenharmony_ci u32 bitmask = ((0x1 << size * BITS_PER_BYTE) - 1) << bitoff; 18562306a36Sopenharmony_ci u32 oldv, newv; 18662306a36Sopenharmony_ci u32 ret; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci do { 18962306a36Sopenharmony_ci oldv = READ_ONCE(*p); 19062306a36Sopenharmony_ci ret = (oldv & bitmask) >> bitoff; 19162306a36Sopenharmony_ci newv = (oldv & ~bitmask) | (x << bitoff); 19262306a36Sopenharmony_ci } while (__cmpxchg_u32(p, oldv, newv) != oldv); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * This only works if the compiler isn't horribly bad at optimizing. 19962306a36Sopenharmony_ci * gcc-2.5.8 reportedly can't handle this, but I define that one to 20062306a36Sopenharmony_ci * be dead anyway. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciextern void __xchg_called_with_bad_pointer(void); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic __inline__ unsigned long 20662306a36Sopenharmony_ci__arch_xchg(unsigned long x, volatile void * ptr, int size) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci switch (size) { 20962306a36Sopenharmony_ci case 1: 21062306a36Sopenharmony_ci return xchg_small(ptr, x, 1); 21162306a36Sopenharmony_ci case 2: 21262306a36Sopenharmony_ci return xchg_small(ptr, x, 2); 21362306a36Sopenharmony_ci case 4: 21462306a36Sopenharmony_ci return xchg_u32(ptr, x); 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci __xchg_called_with_bad_pointer(); 21762306a36Sopenharmony_ci return x; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci#endif /* _XTENSA_CMPXCHG_H */ 224