cmpxchg.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
  4. */
  5. #ifndef __ASM_CMPXCHG_H
  6. #define __ASM_CMPXCHG_H
  7. #include <linux/bits.h>
  8. #include <linux/build_bug.h>
  9. #include <asm/barrier.h>
  10. #include <asm/cpu-features.h>
  11. #define __xchg_amo_asm(amswap_db, m, val) \
  12. ({ \
  13. __typeof(val) __ret; \
  14. \
  15. __asm__ __volatile__ ( \
  16. " "amswap_db" %1, %z2, %0 \n" \
  17. : "+ZB" (*m), "=&r" (__ret) \
  18. : "Jr" (val) \
  19. : "memory"); \
  20. \
  21. __ret; \
  22. })
  23. #define __xchg_llsc_asm(ld, st, m, val) \
  24. ({ \
  25. __typeof(val) __ret, __tmp; \
  26. \
  27. asm volatile ( \
  28. "1: ll.w %0, %3 \n" \
  29. " move %1, %z4 \n" \
  30. " sc.w %1, %2 \n" \
  31. " beqz %1, 1b \n" \
  32. : "=&r" (__ret), "=&r" (__tmp), "=ZC" (*m) \
  33. : "ZC" (*m), "Jr" (val) \
  34. : "memory"); \
  35. \
  36. __ret; \
  37. })
  38. static inline unsigned int __xchg_small(volatile void *ptr, unsigned int val,
  39. unsigned int size)
  40. {
  41. unsigned int shift;
  42. u32 old32, mask, temp;
  43. volatile u32 *ptr32;
  44. /* Mask value to the correct size. */
  45. mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
  46. val &= mask;
  47. /*
  48. * Calculate a shift & mask that correspond to the value we wish to
  49. * exchange within the naturally aligned 4 byte integerthat includes
  50. * it.
  51. */
  52. shift = (unsigned long)ptr & 0x3;
  53. shift *= BITS_PER_BYTE;
  54. mask <<= shift;
  55. /*
  56. * Calculate a pointer to the naturally aligned 4 byte integer that
  57. * includes our byte of interest, and load its value.
  58. */
  59. ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
  60. asm volatile (
  61. "1: ll.w %0, %3 \n"
  62. " andn %1, %0, %z4 \n"
  63. " or %1, %1, %z5 \n"
  64. " sc.w %1, %2 \n"
  65. " beqz %1, 1b \n"
  66. : "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
  67. : "ZC" (*ptr32), "Jr" (mask), "Jr" (val << shift)
  68. : "memory");
  69. return (old32 & mask) >> shift;
  70. }
  71. static __always_inline unsigned long
  72. __arch_xchg(volatile void *ptr, unsigned long x, int size)
  73. {
  74. switch (size) {
  75. case 1:
  76. case 2:
  77. return __xchg_small((volatile void *)ptr, x, size);
  78. case 4:
  79. #ifdef CONFIG_CPU_HAS_AMO
  80. return __xchg_amo_asm("amswap_db.w", (volatile u32 *)ptr, (u32)x);
  81. #else
  82. return __xchg_llsc_asm("ll.w", "sc.w", (volatile u32 *)ptr, (u32)x);
  83. #endif /* CONFIG_CPU_HAS_AMO */
  84. #ifdef CONFIG_64BIT
  85. case 8:
  86. #ifdef CONFIG_CPU_HAS_AMO
  87. return __xchg_amo_asm("amswap_db.d", (volatile u64 *)ptr, (u64)x);
  88. #else
  89. return __xchg_llsc_asm("ll.d", "sc.d", (volatile u64 *)ptr, (u64)x);
  90. #endif /* CONFIG_CPU_HAS_AMO */
  91. #endif /* CONFIG_64BIT */
  92. default:
  93. BUILD_BUG();
  94. }
  95. return 0;
  96. }
  97. #define arch_xchg(ptr, x) \
  98. ({ \
  99. __typeof__(*(ptr)) __res; \
  100. \
  101. __res = (__typeof__(*(ptr))) \
  102. __arch_xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
  103. \
  104. __res; \
  105. })
  106. #define __cmpxchg_asm(ld, st, m, old, new) \
  107. ({ \
  108. __typeof(old) __ret; \
  109. \
  110. __asm__ __volatile__( \
  111. "1: " ld " %0, %2 # __cmpxchg_asm \n" \
  112. " bne %0, %z3, 2f \n" \
  113. " move $t0, %z4 \n" \
  114. " " st " $t0, %1 \n" \
  115. " beqz $t0, 1b \n" \
  116. "2: \n" \
  117. __WEAK_LLSC_MB \
  118. : "=&r" (__ret), "=ZB"(*m) \
  119. : "ZB"(*m), "Jr" (old), "Jr" (new) \
  120. : "t0", "memory"); \
  121. \
  122. __ret; \
  123. })
  124. static inline unsigned int __cmpxchg_small(volatile void *ptr, unsigned int old,
  125. unsigned int new, unsigned int size)
  126. {
  127. unsigned int shift;
  128. u32 old32, mask, temp;
  129. volatile u32 *ptr32;
  130. /* Mask inputs to the correct size. */
  131. mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
  132. old &= mask;
  133. new &= mask;
  134. /*
  135. * Calculate a shift & mask that correspond to the value we wish to
  136. * compare & exchange within the naturally aligned 4 byte integer
  137. * that includes it.
  138. */
  139. shift = (unsigned long)ptr & 0x3;
  140. shift *= BITS_PER_BYTE;
  141. old <<= shift;
  142. new <<= shift;
  143. mask <<= shift;
  144. /*
  145. * Calculate a pointer to the naturally aligned 4 byte integer that
  146. * includes our byte of interest, and load its value.
  147. */
  148. ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
  149. asm volatile (
  150. "1: ll.w %0, %3 \n"
  151. " and %1, %0, %z4 \n"
  152. " bne %1, %z5, 2f \n"
  153. " andn %1, %0, %z4 \n"
  154. " or %1, %1, %z6 \n"
  155. " sc.w %1, %2 \n"
  156. " beqz %1, 1b \n"
  157. " b 3f \n"
  158. "2: \n"
  159. __WEAK_LLSC_MB
  160. "3: \n"
  161. : "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
  162. : "ZC" (*ptr32), "Jr" (mask), "Jr" (old), "Jr" (new)
  163. : "memory");
  164. return (old32 & mask) >> shift;
  165. }
  166. static __always_inline unsigned long
  167. __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size)
  168. {
  169. switch (size) {
  170. case 1:
  171. case 2:
  172. return __cmpxchg_small(ptr, old, new, size);
  173. case 4:
  174. return __cmpxchg_asm("ll.w", "sc.w", (volatile u32 *)ptr,
  175. (u32)old, new);
  176. case 8:
  177. return __cmpxchg_asm("ll.d", "sc.d", (volatile u64 *)ptr,
  178. (u64)old, new);
  179. default:
  180. BUILD_BUG();
  181. }
  182. return 0;
  183. }
  184. #define arch_cmpxchg_local(ptr, old, new) \
  185. ((__typeof__(*(ptr))) \
  186. __cmpxchg((ptr), \
  187. (unsigned long)(__typeof__(*(ptr)))(old), \
  188. (unsigned long)(__typeof__(*(ptr)))(new), \
  189. sizeof(*(ptr))))
  190. #define arch_cmpxchg(ptr, old, new) \
  191. ({ \
  192. __typeof__(*(ptr)) __res; \
  193. \
  194. __res = arch_cmpxchg_local((ptr), (old), (new)); \
  195. \
  196. __res; \
  197. })
  198. #ifdef CONFIG_64BIT
  199. #define arch_cmpxchg64_local(ptr, o, n) \
  200. ({ \
  201. BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
  202. arch_cmpxchg_local((ptr), (o), (n)); \
  203. })
  204. #define arch_cmpxchg64(ptr, o, n) \
  205. ({ \
  206. BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
  207. arch_cmpxchg((ptr), (o), (n)); \
  208. })
  209. #ifdef CONFIG_AS_HAS_SCQ_EXTENSION
  210. union __u128_halves {
  211. u128 full;
  212. struct {
  213. u64 low;
  214. u64 high;
  215. };
  216. };
  217. #define system_has_cmpxchg128() cpu_opt(LOONGARCH_CPU_SCQ)
  218. #define __arch_cmpxchg128(ptr, old, new, llsc_mb) \
  219. ({ \
  220. union __u128_halves __old, __new, __ret; \
  221. volatile u64 *__ptr = (volatile u64 *)(ptr); \
  222. \
  223. __old.full = (old); \
  224. __new.full = (new); \
  225. \
  226. __asm__ __volatile__( \
  227. "1: ll.d %0, %3 # 128-bit cmpxchg low \n" \
  228. llsc_mb \
  229. " ld.d %1, %4 # 128-bit cmpxchg high \n" \
  230. " move $t0, %0 \n" \
  231. " move $t1, %1 \n" \
  232. " bne %0, %z5, 2f \n" \
  233. " bne %1, %z6, 2f \n" \
  234. " move $t0, %z7 \n" \
  235. " move $t1, %z8 \n" \
  236. "2: sc.q $t0, $t1, %2 \n" \
  237. " beqz $t0, 1b \n" \
  238. llsc_mb \
  239. : "=&r" (__ret.low), "=&r" (__ret.high) \
  240. : "r" (__ptr), \
  241. "ZC" (__ptr[0]), "m" (__ptr[1]), \
  242. "Jr" (__old.low), "Jr" (__old.high), \
  243. "Jr" (__new.low), "Jr" (__new.high) \
  244. : "t0", "t1", "memory"); \
  245. \
  246. __ret.full; \
  247. })
  248. #define arch_cmpxchg128(ptr, o, n) \
  249. ({ \
  250. BUILD_BUG_ON(sizeof(*(ptr)) != 16); \
  251. __arch_cmpxchg128(ptr, o, n, __WEAK_LLSC_MB); \
  252. })
  253. #define arch_cmpxchg128_local(ptr, o, n) \
  254. ({ \
  255. BUILD_BUG_ON(sizeof(*(ptr)) != 16); \
  256. __arch_cmpxchg128(ptr, o, n, ""); \
  257. })
  258. #endif /* CONFIG_AS_HAS_SCQ_EXTENSION */
  259. #else
  260. #include <asm-generic/cmpxchg-local.h>
  261. #define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
  262. #define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n))
  263. #endif
  264. #endif /* __ASM_CMPXCHG_H */