15b0b14e5SHuacai Chen /* SPDX-License-Identifier: GPL-2.0 */
25b0b14e5SHuacai Chen /*
35b0b14e5SHuacai Chen * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
45b0b14e5SHuacai Chen */
55b0b14e5SHuacai Chen #ifndef __ASM_CMPXCHG_H
65b0b14e5SHuacai Chen #define __ASM_CMPXCHG_H
75b0b14e5SHuacai Chen
8720dc7abSHuacai Chen #include <linux/bits.h>
95b0b14e5SHuacai Chen #include <linux/build_bug.h>
10720dc7abSHuacai Chen #include <asm/barrier.h>
115b0b14e5SHuacai Chen
125b0b14e5SHuacai Chen #define __xchg_asm(amswap_db, m, val) \
135b0b14e5SHuacai Chen ({ \
145b0b14e5SHuacai Chen __typeof(val) __ret; \
155b0b14e5SHuacai Chen \
165b0b14e5SHuacai Chen __asm__ __volatile__ ( \
175b0b14e5SHuacai Chen " "amswap_db" %1, %z2, %0 \n" \
185b0b14e5SHuacai Chen : "+ZB" (*m), "=&r" (__ret) \
195b0b14e5SHuacai Chen : "Jr" (val) \
205b0b14e5SHuacai Chen : "memory"); \
215b0b14e5SHuacai Chen \
225b0b14e5SHuacai Chen __ret; \
235b0b14e5SHuacai Chen })
245b0b14e5SHuacai Chen
__xchg_small(volatile void * ptr,unsigned int val,unsigned int size)25720dc7abSHuacai Chen static inline unsigned int __xchg_small(volatile void *ptr, unsigned int val,
26720dc7abSHuacai Chen unsigned int size)
27720dc7abSHuacai Chen {
28720dc7abSHuacai Chen unsigned int shift;
29720dc7abSHuacai Chen u32 old32, mask, temp;
30720dc7abSHuacai Chen volatile u32 *ptr32;
31720dc7abSHuacai Chen
32720dc7abSHuacai Chen /* Mask value to the correct size. */
33720dc7abSHuacai Chen mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
34720dc7abSHuacai Chen val &= mask;
35720dc7abSHuacai Chen
36720dc7abSHuacai Chen /*
37720dc7abSHuacai Chen * Calculate a shift & mask that correspond to the value we wish to
38720dc7abSHuacai Chen * exchange within the naturally aligned 4 byte integerthat includes
39720dc7abSHuacai Chen * it.
40720dc7abSHuacai Chen */
41720dc7abSHuacai Chen shift = (unsigned long)ptr & 0x3;
42720dc7abSHuacai Chen shift *= BITS_PER_BYTE;
43720dc7abSHuacai Chen mask <<= shift;
44720dc7abSHuacai Chen
45720dc7abSHuacai Chen /*
46720dc7abSHuacai Chen * Calculate a pointer to the naturally aligned 4 byte integer that
47720dc7abSHuacai Chen * includes our byte of interest, and load its value.
48720dc7abSHuacai Chen */
49720dc7abSHuacai Chen ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
50720dc7abSHuacai Chen
51720dc7abSHuacai Chen asm volatile (
52720dc7abSHuacai Chen "1: ll.w %0, %3 \n"
53720dc7abSHuacai Chen " andn %1, %0, %z4 \n"
54720dc7abSHuacai Chen " or %1, %1, %z5 \n"
55720dc7abSHuacai Chen " sc.w %1, %2 \n"
56720dc7abSHuacai Chen " beqz %1, 1b \n"
57720dc7abSHuacai Chen : "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
58720dc7abSHuacai Chen : "ZC" (*ptr32), "Jr" (mask), "Jr" (val << shift)
59720dc7abSHuacai Chen : "memory");
60720dc7abSHuacai Chen
61720dc7abSHuacai Chen return (old32 & mask) >> shift;
62720dc7abSHuacai Chen }
63720dc7abSHuacai Chen
64ddf50271SHuacai Chen static __always_inline unsigned long
__arch_xchg(volatile void * ptr,unsigned long x,int size)65*06855063SAndrzej Hajda __arch_xchg(volatile void *ptr, unsigned long x, int size)
665b0b14e5SHuacai Chen {
675b0b14e5SHuacai Chen switch (size) {
68720dc7abSHuacai Chen case 1:
69720dc7abSHuacai Chen case 2:
70720dc7abSHuacai Chen return __xchg_small(ptr, x, size);
71720dc7abSHuacai Chen
725b0b14e5SHuacai Chen case 4:
735b0b14e5SHuacai Chen return __xchg_asm("amswap_db.w", (volatile u32 *)ptr, (u32)x);
745b0b14e5SHuacai Chen
755b0b14e5SHuacai Chen case 8:
765b0b14e5SHuacai Chen return __xchg_asm("amswap_db.d", (volatile u64 *)ptr, (u64)x);
775b0b14e5SHuacai Chen
785b0b14e5SHuacai Chen default:
795b0b14e5SHuacai Chen BUILD_BUG();
805b0b14e5SHuacai Chen }
815b0b14e5SHuacai Chen
825b0b14e5SHuacai Chen return 0;
835b0b14e5SHuacai Chen }
845b0b14e5SHuacai Chen
855b0b14e5SHuacai Chen #define arch_xchg(ptr, x) \
865b0b14e5SHuacai Chen ({ \
875b0b14e5SHuacai Chen __typeof__(*(ptr)) __res; \
885b0b14e5SHuacai Chen \
895b0b14e5SHuacai Chen __res = (__typeof__(*(ptr))) \
90*06855063SAndrzej Hajda __arch_xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
915b0b14e5SHuacai Chen \
925b0b14e5SHuacai Chen __res; \
935b0b14e5SHuacai Chen })
945b0b14e5SHuacai Chen
955b0b14e5SHuacai Chen #define __cmpxchg_asm(ld, st, m, old, new) \
965b0b14e5SHuacai Chen ({ \
975b0b14e5SHuacai Chen __typeof(old) __ret; \
985b0b14e5SHuacai Chen \
995b0b14e5SHuacai Chen __asm__ __volatile__( \
1005b0b14e5SHuacai Chen "1: " ld " %0, %2 # __cmpxchg_asm \n" \
1015b0b14e5SHuacai Chen " bne %0, %z3, 2f \n" \
10257ce5d3eSWANG Xuerui " move $t0, %z4 \n" \
1035b0b14e5SHuacai Chen " " st " $t0, %1 \n" \
104d47b2dc8SWANG Xuerui " beqz $t0, 1b \n" \
1055b0b14e5SHuacai Chen "2: \n" \
10646859ac8SHuacai Chen __WEAK_LLSC_MB \
1075b0b14e5SHuacai Chen : "=&r" (__ret), "=ZB"(*m) \
1085b0b14e5SHuacai Chen : "ZB"(*m), "Jr" (old), "Jr" (new) \
1095b0b14e5SHuacai Chen : "t0", "memory"); \
1105b0b14e5SHuacai Chen \
1115b0b14e5SHuacai Chen __ret; \
1125b0b14e5SHuacai Chen })
1135b0b14e5SHuacai Chen
__cmpxchg_small(volatile void * ptr,unsigned int old,unsigned int new,unsigned int size)114720dc7abSHuacai Chen static inline unsigned int __cmpxchg_small(volatile void *ptr, unsigned int old,
115720dc7abSHuacai Chen unsigned int new, unsigned int size)
116720dc7abSHuacai Chen {
117720dc7abSHuacai Chen unsigned int shift;
118720dc7abSHuacai Chen u32 old32, mask, temp;
119720dc7abSHuacai Chen volatile u32 *ptr32;
120720dc7abSHuacai Chen
121720dc7abSHuacai Chen /* Mask inputs to the correct size. */
122720dc7abSHuacai Chen mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
123720dc7abSHuacai Chen old &= mask;
124720dc7abSHuacai Chen new &= mask;
125720dc7abSHuacai Chen
126720dc7abSHuacai Chen /*
127720dc7abSHuacai Chen * Calculate a shift & mask that correspond to the value we wish to
128720dc7abSHuacai Chen * compare & exchange within the naturally aligned 4 byte integer
129720dc7abSHuacai Chen * that includes it.
130720dc7abSHuacai Chen */
131720dc7abSHuacai Chen shift = (unsigned long)ptr & 0x3;
132720dc7abSHuacai Chen shift *= BITS_PER_BYTE;
133720dc7abSHuacai Chen old <<= shift;
134720dc7abSHuacai Chen new <<= shift;
135720dc7abSHuacai Chen mask <<= shift;
136720dc7abSHuacai Chen
137720dc7abSHuacai Chen /*
138720dc7abSHuacai Chen * Calculate a pointer to the naturally aligned 4 byte integer that
139720dc7abSHuacai Chen * includes our byte of interest, and load its value.
140720dc7abSHuacai Chen */
141720dc7abSHuacai Chen ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
142720dc7abSHuacai Chen
143720dc7abSHuacai Chen asm volatile (
144720dc7abSHuacai Chen "1: ll.w %0, %3 \n"
145720dc7abSHuacai Chen " and %1, %0, %z4 \n"
146720dc7abSHuacai Chen " bne %1, %z5, 2f \n"
147720dc7abSHuacai Chen " andn %1, %0, %z4 \n"
148720dc7abSHuacai Chen " or %1, %1, %z6 \n"
149720dc7abSHuacai Chen " sc.w %1, %2 \n"
150720dc7abSHuacai Chen " beqz %1, 1b \n"
151720dc7abSHuacai Chen " b 3f \n"
152720dc7abSHuacai Chen "2: \n"
153720dc7abSHuacai Chen __WEAK_LLSC_MB
154720dc7abSHuacai Chen "3: \n"
155720dc7abSHuacai Chen : "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
156720dc7abSHuacai Chen : "ZC" (*ptr32), "Jr" (mask), "Jr" (old), "Jr" (new)
157720dc7abSHuacai Chen : "memory");
158720dc7abSHuacai Chen
159720dc7abSHuacai Chen return (old32 & mask) >> shift;
160720dc7abSHuacai Chen }
161720dc7abSHuacai Chen
162ddf50271SHuacai Chen static __always_inline unsigned long
__cmpxchg(volatile void * ptr,unsigned long old,unsigned long new,unsigned int size)163ddf50271SHuacai Chen __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size)
1645b0b14e5SHuacai Chen {
1655b0b14e5SHuacai Chen switch (size) {
166720dc7abSHuacai Chen case 1:
167720dc7abSHuacai Chen case 2:
168720dc7abSHuacai Chen return __cmpxchg_small(ptr, old, new, size);
169720dc7abSHuacai Chen
1705b0b14e5SHuacai Chen case 4:
1715b0b14e5SHuacai Chen return __cmpxchg_asm("ll.w", "sc.w", (volatile u32 *)ptr,
1725b0b14e5SHuacai Chen (u32)old, new);
1735b0b14e5SHuacai Chen
1745b0b14e5SHuacai Chen case 8:
1755b0b14e5SHuacai Chen return __cmpxchg_asm("ll.d", "sc.d", (volatile u64 *)ptr,
1765b0b14e5SHuacai Chen (u64)old, new);
1775b0b14e5SHuacai Chen
1785b0b14e5SHuacai Chen default:
1795b0b14e5SHuacai Chen BUILD_BUG();
1805b0b14e5SHuacai Chen }
1815b0b14e5SHuacai Chen
1825b0b14e5SHuacai Chen return 0;
1835b0b14e5SHuacai Chen }
1845b0b14e5SHuacai Chen
1855b0b14e5SHuacai Chen #define arch_cmpxchg_local(ptr, old, new) \
1865b0b14e5SHuacai Chen ((__typeof__(*(ptr))) \
1875b0b14e5SHuacai Chen __cmpxchg((ptr), \
1885b0b14e5SHuacai Chen (unsigned long)(__typeof__(*(ptr)))(old), \
1895b0b14e5SHuacai Chen (unsigned long)(__typeof__(*(ptr)))(new), \
1905b0b14e5SHuacai Chen sizeof(*(ptr))))
1915b0b14e5SHuacai Chen
1925b0b14e5SHuacai Chen #define arch_cmpxchg(ptr, old, new) \
1935b0b14e5SHuacai Chen ({ \
1945b0b14e5SHuacai Chen __typeof__(*(ptr)) __res; \
1955b0b14e5SHuacai Chen \
1965b0b14e5SHuacai Chen __res = arch_cmpxchg_local((ptr), (old), (new)); \
1975b0b14e5SHuacai Chen \
1985b0b14e5SHuacai Chen __res; \
1995b0b14e5SHuacai Chen })
2005b0b14e5SHuacai Chen
2015b0b14e5SHuacai Chen #ifdef CONFIG_64BIT
2025b0b14e5SHuacai Chen #define arch_cmpxchg64_local(ptr, o, n) \
2035b0b14e5SHuacai Chen ({ \
2045b0b14e5SHuacai Chen BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
2055b0b14e5SHuacai Chen arch_cmpxchg_local((ptr), (o), (n)); \
2065b0b14e5SHuacai Chen })
2075b0b14e5SHuacai Chen
2085b0b14e5SHuacai Chen #define arch_cmpxchg64(ptr, o, n) \
2095b0b14e5SHuacai Chen ({ \
2105b0b14e5SHuacai Chen BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
2115b0b14e5SHuacai Chen arch_cmpxchg((ptr), (o), (n)); \
2125b0b14e5SHuacai Chen })
2135b0b14e5SHuacai Chen #else
2145b0b14e5SHuacai Chen #include <asm-generic/cmpxchg-local.h>
2155b0b14e5SHuacai Chen #define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
2165b0b14e5SHuacai Chen #define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n))
2175b0b14e5SHuacai Chen #endif
2185b0b14e5SHuacai Chen
2195b0b14e5SHuacai Chen #endif /* __ASM_CMPXCHG_H */
220