18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Based on arch/arm/include/asm/cmpxchg.h
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 ARM Ltd.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#ifndef __ASM_CMPXCHG_H
88c2ecf20Sopenharmony_ci#define __ASM_CMPXCHG_H
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/build_bug.h>
118c2ecf20Sopenharmony_ci#include <linux/compiler.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/barrier.h>
148c2ecf20Sopenharmony_ci#include <asm/lse.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * We need separate acquire parameters for ll/sc and lse, since the full
188c2ecf20Sopenharmony_ci * barrier case is generated as release+dmb for the former and
198c2ecf20Sopenharmony_ci * acquire+release for the latter.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci#define __XCHG_CASE(w, sfx, name, sz, mb, nop_lse, acq, acq_lse, rel, cl)	\
228c2ecf20Sopenharmony_cistatic inline u##sz __xchg_case_##name##sz(u##sz x, volatile void *ptr)		\
238c2ecf20Sopenharmony_ci{										\
248c2ecf20Sopenharmony_ci	u##sz ret;								\
258c2ecf20Sopenharmony_ci	unsigned long tmp;							\
268c2ecf20Sopenharmony_ci										\
278c2ecf20Sopenharmony_ci	asm volatile(ARM64_LSE_ATOMIC_INSN(					\
288c2ecf20Sopenharmony_ci	/* LL/SC */								\
298c2ecf20Sopenharmony_ci	"	prfm	pstl1strm, %2\n"					\
308c2ecf20Sopenharmony_ci	"1:	ld" #acq "xr" #sfx "\t%" #w "0, %2\n"				\
318c2ecf20Sopenharmony_ci	"	st" #rel "xr" #sfx "\t%w1, %" #w "3, %2\n"			\
328c2ecf20Sopenharmony_ci	"	cbnz	%w1, 1b\n"						\
338c2ecf20Sopenharmony_ci	"	" #mb,								\
348c2ecf20Sopenharmony_ci	/* LSE atomics */							\
358c2ecf20Sopenharmony_ci	"	swp" #acq_lse #rel #sfx "\t%" #w "3, %" #w "0, %2\n"		\
368c2ecf20Sopenharmony_ci		__nops(3)							\
378c2ecf20Sopenharmony_ci	"	" #nop_lse)							\
388c2ecf20Sopenharmony_ci	: "=&r" (ret), "=&r" (tmp), "+Q" (*(u##sz *)ptr)			\
398c2ecf20Sopenharmony_ci	: "r" (x)								\
408c2ecf20Sopenharmony_ci	: cl);									\
418c2ecf20Sopenharmony_ci										\
428c2ecf20Sopenharmony_ci	return ret;								\
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci__XCHG_CASE(w, b,     ,  8,        ,    ,  ,  ,  ,         )
468c2ecf20Sopenharmony_ci__XCHG_CASE(w, h,     , 16,        ,    ,  ,  ,  ,         )
478c2ecf20Sopenharmony_ci__XCHG_CASE(w,  ,     , 32,        ,    ,  ,  ,  ,         )
488c2ecf20Sopenharmony_ci__XCHG_CASE( ,  ,     , 64,        ,    ,  ,  ,  ,         )
498c2ecf20Sopenharmony_ci__XCHG_CASE(w, b, acq_,  8,        ,    , a, a,  , "memory")
508c2ecf20Sopenharmony_ci__XCHG_CASE(w, h, acq_, 16,        ,    , a, a,  , "memory")
518c2ecf20Sopenharmony_ci__XCHG_CASE(w,  , acq_, 32,        ,    , a, a,  , "memory")
528c2ecf20Sopenharmony_ci__XCHG_CASE( ,  , acq_, 64,        ,    , a, a,  , "memory")
538c2ecf20Sopenharmony_ci__XCHG_CASE(w, b, rel_,  8,        ,    ,  ,  , l, "memory")
548c2ecf20Sopenharmony_ci__XCHG_CASE(w, h, rel_, 16,        ,    ,  ,  , l, "memory")
558c2ecf20Sopenharmony_ci__XCHG_CASE(w,  , rel_, 32,        ,    ,  ,  , l, "memory")
568c2ecf20Sopenharmony_ci__XCHG_CASE( ,  , rel_, 64,        ,    ,  ,  , l, "memory")
578c2ecf20Sopenharmony_ci__XCHG_CASE(w, b,  mb_,  8, dmb ish, nop,  , a, l, "memory")
588c2ecf20Sopenharmony_ci__XCHG_CASE(w, h,  mb_, 16, dmb ish, nop,  , a, l, "memory")
598c2ecf20Sopenharmony_ci__XCHG_CASE(w,  ,  mb_, 32, dmb ish, nop,  , a, l, "memory")
608c2ecf20Sopenharmony_ci__XCHG_CASE( ,  ,  mb_, 64, dmb ish, nop,  , a, l, "memory")
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#undef __XCHG_CASE
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define __XCHG_GEN(sfx)							\
658c2ecf20Sopenharmony_cistatic __always_inline  unsigned long __xchg##sfx(unsigned long x,	\
668c2ecf20Sopenharmony_ci					volatile void *ptr,		\
678c2ecf20Sopenharmony_ci					int size)			\
688c2ecf20Sopenharmony_ci{									\
698c2ecf20Sopenharmony_ci	switch (size) {							\
708c2ecf20Sopenharmony_ci	case 1:								\
718c2ecf20Sopenharmony_ci		return __xchg_case##sfx##_8(x, ptr);			\
728c2ecf20Sopenharmony_ci	case 2:								\
738c2ecf20Sopenharmony_ci		return __xchg_case##sfx##_16(x, ptr);			\
748c2ecf20Sopenharmony_ci	case 4:								\
758c2ecf20Sopenharmony_ci		return __xchg_case##sfx##_32(x, ptr);			\
768c2ecf20Sopenharmony_ci	case 8:								\
778c2ecf20Sopenharmony_ci		return __xchg_case##sfx##_64(x, ptr);			\
788c2ecf20Sopenharmony_ci	default:							\
798c2ecf20Sopenharmony_ci		BUILD_BUG();						\
808c2ecf20Sopenharmony_ci	}								\
818c2ecf20Sopenharmony_ci									\
828c2ecf20Sopenharmony_ci	unreachable();							\
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci__XCHG_GEN()
868c2ecf20Sopenharmony_ci__XCHG_GEN(_acq)
878c2ecf20Sopenharmony_ci__XCHG_GEN(_rel)
888c2ecf20Sopenharmony_ci__XCHG_GEN(_mb)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#undef __XCHG_GEN
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define __xchg_wrapper(sfx, ptr, x)					\
938c2ecf20Sopenharmony_ci({									\
948c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __ret;					\
958c2ecf20Sopenharmony_ci	__ret = (__typeof__(*(ptr)))					\
968c2ecf20Sopenharmony_ci		__xchg##sfx((unsigned long)(x), (ptr), sizeof(*(ptr))); \
978c2ecf20Sopenharmony_ci	__ret;								\
988c2ecf20Sopenharmony_ci})
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* xchg */
1018c2ecf20Sopenharmony_ci#define arch_xchg_relaxed(...)	__xchg_wrapper(    , __VA_ARGS__)
1028c2ecf20Sopenharmony_ci#define arch_xchg_acquire(...)	__xchg_wrapper(_acq, __VA_ARGS__)
1038c2ecf20Sopenharmony_ci#define arch_xchg_release(...)	__xchg_wrapper(_rel, __VA_ARGS__)
1048c2ecf20Sopenharmony_ci#define arch_xchg(...)		__xchg_wrapper( _mb, __VA_ARGS__)
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define __CMPXCHG_CASE(name, sz)			\
1078c2ecf20Sopenharmony_cistatic inline u##sz __cmpxchg_case_##name##sz(volatile void *ptr,	\
1088c2ecf20Sopenharmony_ci					      u##sz old,		\
1098c2ecf20Sopenharmony_ci					      u##sz new)		\
1108c2ecf20Sopenharmony_ci{									\
1118c2ecf20Sopenharmony_ci	return __lse_ll_sc_body(_cmpxchg_case_##name##sz,		\
1128c2ecf20Sopenharmony_ci				ptr, old, new);				\
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci__CMPXCHG_CASE(    ,  8)
1168c2ecf20Sopenharmony_ci__CMPXCHG_CASE(    , 16)
1178c2ecf20Sopenharmony_ci__CMPXCHG_CASE(    , 32)
1188c2ecf20Sopenharmony_ci__CMPXCHG_CASE(    , 64)
1198c2ecf20Sopenharmony_ci__CMPXCHG_CASE(acq_,  8)
1208c2ecf20Sopenharmony_ci__CMPXCHG_CASE(acq_, 16)
1218c2ecf20Sopenharmony_ci__CMPXCHG_CASE(acq_, 32)
1228c2ecf20Sopenharmony_ci__CMPXCHG_CASE(acq_, 64)
1238c2ecf20Sopenharmony_ci__CMPXCHG_CASE(rel_,  8)
1248c2ecf20Sopenharmony_ci__CMPXCHG_CASE(rel_, 16)
1258c2ecf20Sopenharmony_ci__CMPXCHG_CASE(rel_, 32)
1268c2ecf20Sopenharmony_ci__CMPXCHG_CASE(rel_, 64)
1278c2ecf20Sopenharmony_ci__CMPXCHG_CASE(mb_,  8)
1288c2ecf20Sopenharmony_ci__CMPXCHG_CASE(mb_, 16)
1298c2ecf20Sopenharmony_ci__CMPXCHG_CASE(mb_, 32)
1308c2ecf20Sopenharmony_ci__CMPXCHG_CASE(mb_, 64)
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#undef __CMPXCHG_CASE
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#define __CMPXCHG_DBL(name)						\
1358c2ecf20Sopenharmony_cistatic inline long __cmpxchg_double##name(unsigned long old1,		\
1368c2ecf20Sopenharmony_ci					 unsigned long old2,		\
1378c2ecf20Sopenharmony_ci					 unsigned long new1,		\
1388c2ecf20Sopenharmony_ci					 unsigned long new2,		\
1398c2ecf20Sopenharmony_ci					 volatile void *ptr)		\
1408c2ecf20Sopenharmony_ci{									\
1418c2ecf20Sopenharmony_ci	return __lse_ll_sc_body(_cmpxchg_double##name, 			\
1428c2ecf20Sopenharmony_ci				old1, old2, new1, new2, ptr);		\
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci__CMPXCHG_DBL(   )
1468c2ecf20Sopenharmony_ci__CMPXCHG_DBL(_mb)
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci#undef __CMPXCHG_DBL
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#define __CMPXCHG_GEN(sfx)						\
1518c2ecf20Sopenharmony_cistatic __always_inline unsigned long __cmpxchg##sfx(volatile void *ptr,	\
1528c2ecf20Sopenharmony_ci					   unsigned long old,		\
1538c2ecf20Sopenharmony_ci					   unsigned long new,		\
1548c2ecf20Sopenharmony_ci					   int size)			\
1558c2ecf20Sopenharmony_ci{									\
1568c2ecf20Sopenharmony_ci	switch (size) {							\
1578c2ecf20Sopenharmony_ci	case 1:								\
1588c2ecf20Sopenharmony_ci		return __cmpxchg_case##sfx##_8(ptr, old, new);		\
1598c2ecf20Sopenharmony_ci	case 2:								\
1608c2ecf20Sopenharmony_ci		return __cmpxchg_case##sfx##_16(ptr, old, new);		\
1618c2ecf20Sopenharmony_ci	case 4:								\
1628c2ecf20Sopenharmony_ci		return __cmpxchg_case##sfx##_32(ptr, old, new);		\
1638c2ecf20Sopenharmony_ci	case 8:								\
1648c2ecf20Sopenharmony_ci		return __cmpxchg_case##sfx##_64(ptr, old, new);		\
1658c2ecf20Sopenharmony_ci	default:							\
1668c2ecf20Sopenharmony_ci		BUILD_BUG();						\
1678c2ecf20Sopenharmony_ci	}								\
1688c2ecf20Sopenharmony_ci									\
1698c2ecf20Sopenharmony_ci	unreachable();							\
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci__CMPXCHG_GEN()
1738c2ecf20Sopenharmony_ci__CMPXCHG_GEN(_acq)
1748c2ecf20Sopenharmony_ci__CMPXCHG_GEN(_rel)
1758c2ecf20Sopenharmony_ci__CMPXCHG_GEN(_mb)
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci#undef __CMPXCHG_GEN
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci#define __cmpxchg_wrapper(sfx, ptr, o, n)				\
1808c2ecf20Sopenharmony_ci({									\
1818c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __ret;					\
1828c2ecf20Sopenharmony_ci	__ret = (__typeof__(*(ptr)))					\
1838c2ecf20Sopenharmony_ci		__cmpxchg##sfx((ptr), (unsigned long)(o),		\
1848c2ecf20Sopenharmony_ci				(unsigned long)(n), sizeof(*(ptr)));	\
1858c2ecf20Sopenharmony_ci	__ret;								\
1868c2ecf20Sopenharmony_ci})
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* cmpxchg */
1898c2ecf20Sopenharmony_ci#define arch_cmpxchg_relaxed(...)	__cmpxchg_wrapper(    , __VA_ARGS__)
1908c2ecf20Sopenharmony_ci#define arch_cmpxchg_acquire(...)	__cmpxchg_wrapper(_acq, __VA_ARGS__)
1918c2ecf20Sopenharmony_ci#define arch_cmpxchg_release(...)	__cmpxchg_wrapper(_rel, __VA_ARGS__)
1928c2ecf20Sopenharmony_ci#define arch_cmpxchg(...)		__cmpxchg_wrapper( _mb, __VA_ARGS__)
1938c2ecf20Sopenharmony_ci#define arch_cmpxchg_local		arch_cmpxchg_relaxed
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* cmpxchg64 */
1968c2ecf20Sopenharmony_ci#define arch_cmpxchg64_relaxed		arch_cmpxchg_relaxed
1978c2ecf20Sopenharmony_ci#define arch_cmpxchg64_acquire		arch_cmpxchg_acquire
1988c2ecf20Sopenharmony_ci#define arch_cmpxchg64_release		arch_cmpxchg_release
1998c2ecf20Sopenharmony_ci#define arch_cmpxchg64			arch_cmpxchg
2008c2ecf20Sopenharmony_ci#define arch_cmpxchg64_local		arch_cmpxchg_local
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* cmpxchg_double */
2038c2ecf20Sopenharmony_ci#define system_has_cmpxchg_double()     1
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci#define __cmpxchg_double_check(ptr1, ptr2)					\
2068c2ecf20Sopenharmony_ci({										\
2078c2ecf20Sopenharmony_ci	if (sizeof(*(ptr1)) != 8)						\
2088c2ecf20Sopenharmony_ci		BUILD_BUG();							\
2098c2ecf20Sopenharmony_ci	VM_BUG_ON((unsigned long *)(ptr2) - (unsigned long *)(ptr1) != 1);	\
2108c2ecf20Sopenharmony_ci})
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci#define arch_cmpxchg_double(ptr1, ptr2, o1, o2, n1, n2)				\
2138c2ecf20Sopenharmony_ci({										\
2148c2ecf20Sopenharmony_ci	int __ret;								\
2158c2ecf20Sopenharmony_ci	__cmpxchg_double_check(ptr1, ptr2);					\
2168c2ecf20Sopenharmony_ci	__ret = !__cmpxchg_double_mb((unsigned long)(o1), (unsigned long)(o2),	\
2178c2ecf20Sopenharmony_ci				     (unsigned long)(n1), (unsigned long)(n2),	\
2188c2ecf20Sopenharmony_ci				     ptr1);					\
2198c2ecf20Sopenharmony_ci	__ret;									\
2208c2ecf20Sopenharmony_ci})
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci#define arch_cmpxchg_double_local(ptr1, ptr2, o1, o2, n1, n2)			\
2238c2ecf20Sopenharmony_ci({										\
2248c2ecf20Sopenharmony_ci	int __ret;								\
2258c2ecf20Sopenharmony_ci	__cmpxchg_double_check(ptr1, ptr2);					\
2268c2ecf20Sopenharmony_ci	__ret = !__cmpxchg_double((unsigned long)(o1), (unsigned long)(o2),	\
2278c2ecf20Sopenharmony_ci				  (unsigned long)(n1), (unsigned long)(n2),	\
2288c2ecf20Sopenharmony_ci				  ptr1);					\
2298c2ecf20Sopenharmony_ci	__ret;									\
2308c2ecf20Sopenharmony_ci})
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci#define __CMPWAIT_CASE(w, sfx, sz)					\
2338c2ecf20Sopenharmony_cistatic inline void __cmpwait_case_##sz(volatile void *ptr,		\
2348c2ecf20Sopenharmony_ci				       unsigned long val)		\
2358c2ecf20Sopenharmony_ci{									\
2368c2ecf20Sopenharmony_ci	unsigned long tmp;						\
2378c2ecf20Sopenharmony_ci									\
2388c2ecf20Sopenharmony_ci	asm volatile(							\
2398c2ecf20Sopenharmony_ci	"	sevl\n"							\
2408c2ecf20Sopenharmony_ci	"	wfe\n"							\
2418c2ecf20Sopenharmony_ci	"	ldxr" #sfx "\t%" #w "[tmp], %[v]\n"			\
2428c2ecf20Sopenharmony_ci	"	eor	%" #w "[tmp], %" #w "[tmp], %" #w "[val]\n"	\
2438c2ecf20Sopenharmony_ci	"	cbnz	%" #w "[tmp], 1f\n"				\
2448c2ecf20Sopenharmony_ci	"	wfe\n"							\
2458c2ecf20Sopenharmony_ci	"1:"								\
2468c2ecf20Sopenharmony_ci	: [tmp] "=&r" (tmp), [v] "+Q" (*(unsigned long *)ptr)		\
2478c2ecf20Sopenharmony_ci	: [val] "r" (val));						\
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci__CMPWAIT_CASE(w, b, 8);
2518c2ecf20Sopenharmony_ci__CMPWAIT_CASE(w, h, 16);
2528c2ecf20Sopenharmony_ci__CMPWAIT_CASE(w,  , 32);
2538c2ecf20Sopenharmony_ci__CMPWAIT_CASE( ,  , 64);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci#undef __CMPWAIT_CASE
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci#define __CMPWAIT_GEN(sfx)						\
2588c2ecf20Sopenharmony_cistatic __always_inline void __cmpwait##sfx(volatile void *ptr,		\
2598c2ecf20Sopenharmony_ci				  unsigned long val,			\
2608c2ecf20Sopenharmony_ci				  int size)				\
2618c2ecf20Sopenharmony_ci{									\
2628c2ecf20Sopenharmony_ci	switch (size) {							\
2638c2ecf20Sopenharmony_ci	case 1:								\
2648c2ecf20Sopenharmony_ci		return __cmpwait_case##sfx##_8(ptr, (u8)val);		\
2658c2ecf20Sopenharmony_ci	case 2:								\
2668c2ecf20Sopenharmony_ci		return __cmpwait_case##sfx##_16(ptr, (u16)val);		\
2678c2ecf20Sopenharmony_ci	case 4:								\
2688c2ecf20Sopenharmony_ci		return __cmpwait_case##sfx##_32(ptr, val);		\
2698c2ecf20Sopenharmony_ci	case 8:								\
2708c2ecf20Sopenharmony_ci		return __cmpwait_case##sfx##_64(ptr, val);		\
2718c2ecf20Sopenharmony_ci	default:							\
2728c2ecf20Sopenharmony_ci		BUILD_BUG();						\
2738c2ecf20Sopenharmony_ci	}								\
2748c2ecf20Sopenharmony_ci									\
2758c2ecf20Sopenharmony_ci	unreachable();							\
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci__CMPWAIT_GEN()
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci#undef __CMPWAIT_GEN
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci#define __cmpwait_relaxed(ptr, val) \
2838c2ecf20Sopenharmony_ci	__cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci#endif	/* __ASM_CMPXCHG_H */
286