18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Atomic xchg and cmpxchg operations. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 58c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 68c2ecf20Sopenharmony_ci * for more details. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2005 Tensilica Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#ifndef _XTENSA_CMPXCHG_H 128c2ecf20Sopenharmony_ci#define _XTENSA_CMPXCHG_H 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/bits.h> 178c2ecf20Sopenharmony_ci#include <linux/stringify.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * cmpxchg 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic inline unsigned long 248c2ecf20Sopenharmony_ci__cmpxchg_u32(volatile int *p, int old, int new) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 278c2ecf20Sopenharmony_ci unsigned long tmp, result; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci __asm__ __volatile__( 308c2ecf20Sopenharmony_ci "1: l32ex %[result], %[addr]\n" 318c2ecf20Sopenharmony_ci " bne %[result], %[cmp], 2f\n" 328c2ecf20Sopenharmony_ci " mov %[tmp], %[new]\n" 338c2ecf20Sopenharmony_ci " s32ex %[tmp], %[addr]\n" 348c2ecf20Sopenharmony_ci " getex %[tmp]\n" 358c2ecf20Sopenharmony_ci " beqz %[tmp], 1b\n" 368c2ecf20Sopenharmony_ci "2:\n" 378c2ecf20Sopenharmony_ci : [result] "=&a" (result), [tmp] "=&a" (tmp) 388c2ecf20Sopenharmony_ci : [new] "a" (new), [addr] "a" (p), [cmp] "a" (old) 398c2ecf20Sopenharmony_ci : "memory" 408c2ecf20Sopenharmony_ci ); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return result; 438c2ecf20Sopenharmony_ci#elif XCHAL_HAVE_S32C1I 448c2ecf20Sopenharmony_ci __asm__ __volatile__( 458c2ecf20Sopenharmony_ci " wsr %[cmp], scompare1\n" 468c2ecf20Sopenharmony_ci " s32c1i %[new], %[mem]\n" 478c2ecf20Sopenharmony_ci : [new] "+a" (new), [mem] "+m" (*p) 488c2ecf20Sopenharmony_ci : [cmp] "a" (old) 498c2ecf20Sopenharmony_ci : "memory" 508c2ecf20Sopenharmony_ci ); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return new; 538c2ecf20Sopenharmony_ci#else 548c2ecf20Sopenharmony_ci __asm__ __volatile__( 558c2ecf20Sopenharmony_ci " rsil a15, "__stringify(TOPLEVEL)"\n" 568c2ecf20Sopenharmony_ci " l32i %[old], %[mem]\n" 578c2ecf20Sopenharmony_ci " bne %[old], %[cmp], 1f\n" 588c2ecf20Sopenharmony_ci " s32i %[new], %[mem]\n" 598c2ecf20Sopenharmony_ci "1:\n" 608c2ecf20Sopenharmony_ci " wsr a15, ps\n" 618c2ecf20Sopenharmony_ci " rsync\n" 628c2ecf20Sopenharmony_ci : [old] "=&a" (old), [mem] "+m" (*p) 638c2ecf20Sopenharmony_ci : [cmp] "a" (old), [new] "r" (new) 648c2ecf20Sopenharmony_ci : "a15", "memory"); 658c2ecf20Sopenharmony_ci return old; 668c2ecf20Sopenharmony_ci#endif 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci/* This function doesn't exist, so you'll get a linker error 698c2ecf20Sopenharmony_ci * if something tries to do an invalid cmpxchg(). */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ciextern void __cmpxchg_called_with_bad_pointer(void); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic __inline__ unsigned long 748c2ecf20Sopenharmony_ci__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci switch (size) { 778c2ecf20Sopenharmony_ci case 4: return __cmpxchg_u32(ptr, old, new); 788c2ecf20Sopenharmony_ci default: __cmpxchg_called_with_bad_pointer(); 798c2ecf20Sopenharmony_ci return old; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define cmpxchg(ptr,o,n) \ 848c2ecf20Sopenharmony_ci ({ __typeof__(*(ptr)) _o_ = (o); \ 858c2ecf20Sopenharmony_ci __typeof__(*(ptr)) _n_ = (n); \ 868c2ecf20Sopenharmony_ci (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ 878c2ecf20Sopenharmony_ci (unsigned long)_n_, sizeof (*(ptr))); \ 888c2ecf20Sopenharmony_ci }) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#include <asm-generic/cmpxchg-local.h> 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline unsigned long __cmpxchg_local(volatile void *ptr, 938c2ecf20Sopenharmony_ci unsigned long old, 948c2ecf20Sopenharmony_ci unsigned long new, int size) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci switch (size) { 978c2ecf20Sopenharmony_ci case 4: 988c2ecf20Sopenharmony_ci return __cmpxchg_u32(ptr, old, new); 998c2ecf20Sopenharmony_ci default: 1008c2ecf20Sopenharmony_ci return __cmpxchg_local_generic(ptr, old, new, size); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return old; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make 1088c2ecf20Sopenharmony_ci * them available. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci#define cmpxchg_local(ptr, o, n) \ 1118c2ecf20Sopenharmony_ci ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ 1128c2ecf20Sopenharmony_ci (unsigned long)(n), sizeof(*(ptr)))) 1138c2ecf20Sopenharmony_ci#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) 1148c2ecf20Sopenharmony_ci#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * xchg_u32 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Note that a15 is used here because the register allocation 1208c2ecf20Sopenharmony_ci * done by the compiler is not guaranteed and a window overflow 1218c2ecf20Sopenharmony_ci * may not occur between the rsil and wsr instructions. By using 1228c2ecf20Sopenharmony_ci * a15 in the rsil, the machine is guaranteed to be in a state 1238c2ecf20Sopenharmony_ci * where no register reference will cause an overflow. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic inline unsigned long xchg_u32(volatile int * m, unsigned long val) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci#if XCHAL_HAVE_EXCLUSIVE 1298c2ecf20Sopenharmony_ci unsigned long tmp, result; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci __asm__ __volatile__( 1328c2ecf20Sopenharmony_ci "1: l32ex %[result], %[addr]\n" 1338c2ecf20Sopenharmony_ci " mov %[tmp], %[val]\n" 1348c2ecf20Sopenharmony_ci " s32ex %[tmp], %[addr]\n" 1358c2ecf20Sopenharmony_ci " getex %[tmp]\n" 1368c2ecf20Sopenharmony_ci " beqz %[tmp], 1b\n" 1378c2ecf20Sopenharmony_ci : [result] "=&a" (result), [tmp] "=&a" (tmp) 1388c2ecf20Sopenharmony_ci : [val] "a" (val), [addr] "a" (m) 1398c2ecf20Sopenharmony_ci : "memory" 1408c2ecf20Sopenharmony_ci ); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return result; 1438c2ecf20Sopenharmony_ci#elif XCHAL_HAVE_S32C1I 1448c2ecf20Sopenharmony_ci unsigned long tmp, result; 1458c2ecf20Sopenharmony_ci __asm__ __volatile__( 1468c2ecf20Sopenharmony_ci "1: l32i %[tmp], %[mem]\n" 1478c2ecf20Sopenharmony_ci " mov %[result], %[val]\n" 1488c2ecf20Sopenharmony_ci " wsr %[tmp], scompare1\n" 1498c2ecf20Sopenharmony_ci " s32c1i %[result], %[mem]\n" 1508c2ecf20Sopenharmony_ci " bne %[result], %[tmp], 1b\n" 1518c2ecf20Sopenharmony_ci : [result] "=&a" (result), [tmp] "=&a" (tmp), 1528c2ecf20Sopenharmony_ci [mem] "+m" (*m) 1538c2ecf20Sopenharmony_ci : [val] "a" (val) 1548c2ecf20Sopenharmony_ci : "memory" 1558c2ecf20Sopenharmony_ci ); 1568c2ecf20Sopenharmony_ci return result; 1578c2ecf20Sopenharmony_ci#else 1588c2ecf20Sopenharmony_ci unsigned long tmp; 1598c2ecf20Sopenharmony_ci __asm__ __volatile__( 1608c2ecf20Sopenharmony_ci " rsil a15, "__stringify(TOPLEVEL)"\n" 1618c2ecf20Sopenharmony_ci " l32i %[tmp], %[mem]\n" 1628c2ecf20Sopenharmony_ci " s32i %[val], %[mem]\n" 1638c2ecf20Sopenharmony_ci " wsr a15, ps\n" 1648c2ecf20Sopenharmony_ci " rsync\n" 1658c2ecf20Sopenharmony_ci : [tmp] "=&a" (tmp), [mem] "+m" (*m) 1668c2ecf20Sopenharmony_ci : [val] "a" (val) 1678c2ecf20Sopenharmony_ci : "a15", "memory"); 1688c2ecf20Sopenharmony_ci return tmp; 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define xchg(ptr,x) \ 1738c2ecf20Sopenharmony_ci ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic inline u32 xchg_small(volatile void *ptr, u32 x, int size) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int off = (unsigned long)ptr % sizeof(u32); 1788c2ecf20Sopenharmony_ci volatile u32 *p = ptr - off; 1798c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 1808c2ecf20Sopenharmony_ci int bitoff = (sizeof(u32) - size - off) * BITS_PER_BYTE; 1818c2ecf20Sopenharmony_ci#else 1828c2ecf20Sopenharmony_ci int bitoff = off * BITS_PER_BYTE; 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci u32 bitmask = ((0x1 << size * BITS_PER_BYTE) - 1) << bitoff; 1858c2ecf20Sopenharmony_ci u32 oldv, newv; 1868c2ecf20Sopenharmony_ci u32 ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci do { 1898c2ecf20Sopenharmony_ci oldv = READ_ONCE(*p); 1908c2ecf20Sopenharmony_ci ret = (oldv & bitmask) >> bitoff; 1918c2ecf20Sopenharmony_ci newv = (oldv & ~bitmask) | (x << bitoff); 1928c2ecf20Sopenharmony_ci } while (__cmpxchg_u32(p, oldv, newv) != oldv); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * This only works if the compiler isn't horribly bad at optimizing. 1998c2ecf20Sopenharmony_ci * gcc-2.5.8 reportedly can't handle this, but I define that one to 2008c2ecf20Sopenharmony_ci * be dead anyway. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ciextern void __xchg_called_with_bad_pointer(void); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic __inline__ unsigned long 2068c2ecf20Sopenharmony_ci__xchg(unsigned long x, volatile void * ptr, int size) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci switch (size) { 2098c2ecf20Sopenharmony_ci case 1: 2108c2ecf20Sopenharmony_ci return xchg_small(ptr, x, 1); 2118c2ecf20Sopenharmony_ci case 2: 2128c2ecf20Sopenharmony_ci return xchg_small(ptr, x, 2); 2138c2ecf20Sopenharmony_ci case 4: 2148c2ecf20Sopenharmony_ci return xchg_u32(ptr, x); 2158c2ecf20Sopenharmony_ci default: 2168c2ecf20Sopenharmony_ci __xchg_called_with_bad_pointer(); 2178c2ecf20Sopenharmony_ci return x; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#endif /* __ASSEMBLY__ */ 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#endif /* _XTENSA_CMPXCHG_H */ 224