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