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