162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Based on arch/arm/include/asm/cmpxchg.h 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 ARM Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#ifndef __ASM_CMPXCHG_H 862306a36Sopenharmony_ci#define __ASM_CMPXCHG_H 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/build_bug.h> 1162306a36Sopenharmony_ci#include <linux/compiler.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/barrier.h> 1462306a36Sopenharmony_ci#include <asm/lse.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * We need separate acquire parameters for ll/sc and lse, since the full 1862306a36Sopenharmony_ci * barrier case is generated as release+dmb for the former and 1962306a36Sopenharmony_ci * acquire+release for the latter. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#define __XCHG_CASE(w, sfx, name, sz, mb, nop_lse, acq, acq_lse, rel, cl) \ 2262306a36Sopenharmony_cistatic inline u##sz __xchg_case_##name##sz(u##sz x, volatile void *ptr) \ 2362306a36Sopenharmony_ci{ \ 2462306a36Sopenharmony_ci u##sz ret; \ 2562306a36Sopenharmony_ci unsigned long tmp; \ 2662306a36Sopenharmony_ci \ 2762306a36Sopenharmony_ci asm volatile(ARM64_LSE_ATOMIC_INSN( \ 2862306a36Sopenharmony_ci /* LL/SC */ \ 2962306a36Sopenharmony_ci " prfm pstl1strm, %2\n" \ 3062306a36Sopenharmony_ci "1: ld" #acq "xr" #sfx "\t%" #w "0, %2\n" \ 3162306a36Sopenharmony_ci " st" #rel "xr" #sfx "\t%w1, %" #w "3, %2\n" \ 3262306a36Sopenharmony_ci " cbnz %w1, 1b\n" \ 3362306a36Sopenharmony_ci " " #mb, \ 3462306a36Sopenharmony_ci /* LSE atomics */ \ 3562306a36Sopenharmony_ci " swp" #acq_lse #rel #sfx "\t%" #w "3, %" #w "0, %2\n" \ 3662306a36Sopenharmony_ci __nops(3) \ 3762306a36Sopenharmony_ci " " #nop_lse) \ 3862306a36Sopenharmony_ci : "=&r" (ret), "=&r" (tmp), "+Q" (*(u##sz *)ptr) \ 3962306a36Sopenharmony_ci : "r" (x) \ 4062306a36Sopenharmony_ci : cl); \ 4162306a36Sopenharmony_ci \ 4262306a36Sopenharmony_ci return ret; \ 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci__XCHG_CASE(w, b, , 8, , , , , , ) 4662306a36Sopenharmony_ci__XCHG_CASE(w, h, , 16, , , , , , ) 4762306a36Sopenharmony_ci__XCHG_CASE(w, , , 32, , , , , , ) 4862306a36Sopenharmony_ci__XCHG_CASE( , , , 64, , , , , , ) 4962306a36Sopenharmony_ci__XCHG_CASE(w, b, acq_, 8, , , a, a, , "memory") 5062306a36Sopenharmony_ci__XCHG_CASE(w, h, acq_, 16, , , a, a, , "memory") 5162306a36Sopenharmony_ci__XCHG_CASE(w, , acq_, 32, , , a, a, , "memory") 5262306a36Sopenharmony_ci__XCHG_CASE( , , acq_, 64, , , a, a, , "memory") 5362306a36Sopenharmony_ci__XCHG_CASE(w, b, rel_, 8, , , , , l, "memory") 5462306a36Sopenharmony_ci__XCHG_CASE(w, h, rel_, 16, , , , , l, "memory") 5562306a36Sopenharmony_ci__XCHG_CASE(w, , rel_, 32, , , , , l, "memory") 5662306a36Sopenharmony_ci__XCHG_CASE( , , rel_, 64, , , , , l, "memory") 5762306a36Sopenharmony_ci__XCHG_CASE(w, b, mb_, 8, dmb ish, nop, , a, l, "memory") 5862306a36Sopenharmony_ci__XCHG_CASE(w, h, mb_, 16, dmb ish, nop, , a, l, "memory") 5962306a36Sopenharmony_ci__XCHG_CASE(w, , mb_, 32, dmb ish, nop, , a, l, "memory") 6062306a36Sopenharmony_ci__XCHG_CASE( , , mb_, 64, dmb ish, nop, , a, l, "memory") 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#undef __XCHG_CASE 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define __XCHG_GEN(sfx) \ 6562306a36Sopenharmony_cistatic __always_inline unsigned long \ 6662306a36Sopenharmony_ci__arch_xchg##sfx(unsigned long x, volatile void *ptr, int size) \ 6762306a36Sopenharmony_ci{ \ 6862306a36Sopenharmony_ci switch (size) { \ 6962306a36Sopenharmony_ci case 1: \ 7062306a36Sopenharmony_ci return __xchg_case##sfx##_8(x, ptr); \ 7162306a36Sopenharmony_ci case 2: \ 7262306a36Sopenharmony_ci return __xchg_case##sfx##_16(x, ptr); \ 7362306a36Sopenharmony_ci case 4: \ 7462306a36Sopenharmony_ci return __xchg_case##sfx##_32(x, ptr); \ 7562306a36Sopenharmony_ci case 8: \ 7662306a36Sopenharmony_ci return __xchg_case##sfx##_64(x, ptr); \ 7762306a36Sopenharmony_ci default: \ 7862306a36Sopenharmony_ci BUILD_BUG(); \ 7962306a36Sopenharmony_ci } \ 8062306a36Sopenharmony_ci \ 8162306a36Sopenharmony_ci unreachable(); \ 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci__XCHG_GEN() 8562306a36Sopenharmony_ci__XCHG_GEN(_acq) 8662306a36Sopenharmony_ci__XCHG_GEN(_rel) 8762306a36Sopenharmony_ci__XCHG_GEN(_mb) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#undef __XCHG_GEN 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define __xchg_wrapper(sfx, ptr, x) \ 9262306a36Sopenharmony_ci({ \ 9362306a36Sopenharmony_ci __typeof__(*(ptr)) __ret; \ 9462306a36Sopenharmony_ci __ret = (__typeof__(*(ptr))) \ 9562306a36Sopenharmony_ci __arch_xchg##sfx((unsigned long)(x), (ptr), sizeof(*(ptr))); \ 9662306a36Sopenharmony_ci __ret; \ 9762306a36Sopenharmony_ci}) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* xchg */ 10062306a36Sopenharmony_ci#define arch_xchg_relaxed(...) __xchg_wrapper( , __VA_ARGS__) 10162306a36Sopenharmony_ci#define arch_xchg_acquire(...) __xchg_wrapper(_acq, __VA_ARGS__) 10262306a36Sopenharmony_ci#define arch_xchg_release(...) __xchg_wrapper(_rel, __VA_ARGS__) 10362306a36Sopenharmony_ci#define arch_xchg(...) __xchg_wrapper( _mb, __VA_ARGS__) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define __CMPXCHG_CASE(name, sz) \ 10662306a36Sopenharmony_cistatic inline u##sz __cmpxchg_case_##name##sz(volatile void *ptr, \ 10762306a36Sopenharmony_ci u##sz old, \ 10862306a36Sopenharmony_ci u##sz new) \ 10962306a36Sopenharmony_ci{ \ 11062306a36Sopenharmony_ci return __lse_ll_sc_body(_cmpxchg_case_##name##sz, \ 11162306a36Sopenharmony_ci ptr, old, new); \ 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci__CMPXCHG_CASE( , 8) 11562306a36Sopenharmony_ci__CMPXCHG_CASE( , 16) 11662306a36Sopenharmony_ci__CMPXCHG_CASE( , 32) 11762306a36Sopenharmony_ci__CMPXCHG_CASE( , 64) 11862306a36Sopenharmony_ci__CMPXCHG_CASE(acq_, 8) 11962306a36Sopenharmony_ci__CMPXCHG_CASE(acq_, 16) 12062306a36Sopenharmony_ci__CMPXCHG_CASE(acq_, 32) 12162306a36Sopenharmony_ci__CMPXCHG_CASE(acq_, 64) 12262306a36Sopenharmony_ci__CMPXCHG_CASE(rel_, 8) 12362306a36Sopenharmony_ci__CMPXCHG_CASE(rel_, 16) 12462306a36Sopenharmony_ci__CMPXCHG_CASE(rel_, 32) 12562306a36Sopenharmony_ci__CMPXCHG_CASE(rel_, 64) 12662306a36Sopenharmony_ci__CMPXCHG_CASE(mb_, 8) 12762306a36Sopenharmony_ci__CMPXCHG_CASE(mb_, 16) 12862306a36Sopenharmony_ci__CMPXCHG_CASE(mb_, 32) 12962306a36Sopenharmony_ci__CMPXCHG_CASE(mb_, 64) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#undef __CMPXCHG_CASE 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define __CMPXCHG128(name) \ 13462306a36Sopenharmony_cistatic inline u128 __cmpxchg128##name(volatile u128 *ptr, \ 13562306a36Sopenharmony_ci u128 old, u128 new) \ 13662306a36Sopenharmony_ci{ \ 13762306a36Sopenharmony_ci return __lse_ll_sc_body(_cmpxchg128##name, \ 13862306a36Sopenharmony_ci ptr, old, new); \ 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci__CMPXCHG128( ) 14262306a36Sopenharmony_ci__CMPXCHG128(_mb) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#undef __CMPXCHG128 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define __CMPXCHG_GEN(sfx) \ 14762306a36Sopenharmony_cistatic __always_inline unsigned long __cmpxchg##sfx(volatile void *ptr, \ 14862306a36Sopenharmony_ci unsigned long old, \ 14962306a36Sopenharmony_ci unsigned long new, \ 15062306a36Sopenharmony_ci int size) \ 15162306a36Sopenharmony_ci{ \ 15262306a36Sopenharmony_ci switch (size) { \ 15362306a36Sopenharmony_ci case 1: \ 15462306a36Sopenharmony_ci return __cmpxchg_case##sfx##_8(ptr, old, new); \ 15562306a36Sopenharmony_ci case 2: \ 15662306a36Sopenharmony_ci return __cmpxchg_case##sfx##_16(ptr, old, new); \ 15762306a36Sopenharmony_ci case 4: \ 15862306a36Sopenharmony_ci return __cmpxchg_case##sfx##_32(ptr, old, new); \ 15962306a36Sopenharmony_ci case 8: \ 16062306a36Sopenharmony_ci return __cmpxchg_case##sfx##_64(ptr, old, new); \ 16162306a36Sopenharmony_ci default: \ 16262306a36Sopenharmony_ci BUILD_BUG(); \ 16362306a36Sopenharmony_ci } \ 16462306a36Sopenharmony_ci \ 16562306a36Sopenharmony_ci unreachable(); \ 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci__CMPXCHG_GEN() 16962306a36Sopenharmony_ci__CMPXCHG_GEN(_acq) 17062306a36Sopenharmony_ci__CMPXCHG_GEN(_rel) 17162306a36Sopenharmony_ci__CMPXCHG_GEN(_mb) 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#undef __CMPXCHG_GEN 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define __cmpxchg_wrapper(sfx, ptr, o, n) \ 17662306a36Sopenharmony_ci({ \ 17762306a36Sopenharmony_ci __typeof__(*(ptr)) __ret; \ 17862306a36Sopenharmony_ci __ret = (__typeof__(*(ptr))) \ 17962306a36Sopenharmony_ci __cmpxchg##sfx((ptr), (unsigned long)(o), \ 18062306a36Sopenharmony_ci (unsigned long)(n), sizeof(*(ptr))); \ 18162306a36Sopenharmony_ci __ret; \ 18262306a36Sopenharmony_ci}) 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* cmpxchg */ 18562306a36Sopenharmony_ci#define arch_cmpxchg_relaxed(...) __cmpxchg_wrapper( , __VA_ARGS__) 18662306a36Sopenharmony_ci#define arch_cmpxchg_acquire(...) __cmpxchg_wrapper(_acq, __VA_ARGS__) 18762306a36Sopenharmony_ci#define arch_cmpxchg_release(...) __cmpxchg_wrapper(_rel, __VA_ARGS__) 18862306a36Sopenharmony_ci#define arch_cmpxchg(...) __cmpxchg_wrapper( _mb, __VA_ARGS__) 18962306a36Sopenharmony_ci#define arch_cmpxchg_local arch_cmpxchg_relaxed 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* cmpxchg64 */ 19262306a36Sopenharmony_ci#define arch_cmpxchg64_relaxed arch_cmpxchg_relaxed 19362306a36Sopenharmony_ci#define arch_cmpxchg64_acquire arch_cmpxchg_acquire 19462306a36Sopenharmony_ci#define arch_cmpxchg64_release arch_cmpxchg_release 19562306a36Sopenharmony_ci#define arch_cmpxchg64 arch_cmpxchg 19662306a36Sopenharmony_ci#define arch_cmpxchg64_local arch_cmpxchg_local 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* cmpxchg128 */ 19962306a36Sopenharmony_ci#define system_has_cmpxchg128() 1 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define arch_cmpxchg128(ptr, o, n) \ 20262306a36Sopenharmony_ci({ \ 20362306a36Sopenharmony_ci __cmpxchg128_mb((ptr), (o), (n)); \ 20462306a36Sopenharmony_ci}) 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci#define arch_cmpxchg128_local(ptr, o, n) \ 20762306a36Sopenharmony_ci({ \ 20862306a36Sopenharmony_ci __cmpxchg128((ptr), (o), (n)); \ 20962306a36Sopenharmony_ci}) 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci#define __CMPWAIT_CASE(w, sfx, sz) \ 21262306a36Sopenharmony_cistatic inline void __cmpwait_case_##sz(volatile void *ptr, \ 21362306a36Sopenharmony_ci unsigned long val) \ 21462306a36Sopenharmony_ci{ \ 21562306a36Sopenharmony_ci unsigned long tmp; \ 21662306a36Sopenharmony_ci \ 21762306a36Sopenharmony_ci asm volatile( \ 21862306a36Sopenharmony_ci " sevl\n" \ 21962306a36Sopenharmony_ci " wfe\n" \ 22062306a36Sopenharmony_ci " ldxr" #sfx "\t%" #w "[tmp], %[v]\n" \ 22162306a36Sopenharmony_ci " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ 22262306a36Sopenharmony_ci " cbnz %" #w "[tmp], 1f\n" \ 22362306a36Sopenharmony_ci " wfe\n" \ 22462306a36Sopenharmony_ci "1:" \ 22562306a36Sopenharmony_ci : [tmp] "=&r" (tmp), [v] "+Q" (*(u##sz *)ptr) \ 22662306a36Sopenharmony_ci : [val] "r" (val)); \ 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci__CMPWAIT_CASE(w, b, 8); 23062306a36Sopenharmony_ci__CMPWAIT_CASE(w, h, 16); 23162306a36Sopenharmony_ci__CMPWAIT_CASE(w, , 32); 23262306a36Sopenharmony_ci__CMPWAIT_CASE( , , 64); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#undef __CMPWAIT_CASE 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#define __CMPWAIT_GEN(sfx) \ 23762306a36Sopenharmony_cistatic __always_inline void __cmpwait##sfx(volatile void *ptr, \ 23862306a36Sopenharmony_ci unsigned long val, \ 23962306a36Sopenharmony_ci int size) \ 24062306a36Sopenharmony_ci{ \ 24162306a36Sopenharmony_ci switch (size) { \ 24262306a36Sopenharmony_ci case 1: \ 24362306a36Sopenharmony_ci return __cmpwait_case##sfx##_8(ptr, (u8)val); \ 24462306a36Sopenharmony_ci case 2: \ 24562306a36Sopenharmony_ci return __cmpwait_case##sfx##_16(ptr, (u16)val); \ 24662306a36Sopenharmony_ci case 4: \ 24762306a36Sopenharmony_ci return __cmpwait_case##sfx##_32(ptr, val); \ 24862306a36Sopenharmony_ci case 8: \ 24962306a36Sopenharmony_ci return __cmpwait_case##sfx##_64(ptr, val); \ 25062306a36Sopenharmony_ci default: \ 25162306a36Sopenharmony_ci BUILD_BUG(); \ 25262306a36Sopenharmony_ci } \ 25362306a36Sopenharmony_ci \ 25462306a36Sopenharmony_ci unreachable(); \ 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci__CMPWAIT_GEN() 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci#undef __CMPWAIT_GEN 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci#define __cmpwait_relaxed(ptr, val) \ 26262306a36Sopenharmony_ci __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr))) 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci#endif /* __ASM_CMPXCHG_H */ 265