1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Copyright (C) 2020 Loongson Technology Corporation Limited
4 */
5#ifndef __ASM_CMPXCHG_H
6#define __ASM_CMPXCHG_H
7
8#include <asm/barrier.h>
9#include <linux/build_bug.h>
10
11#define __xchg_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
24extern unsigned long __xchg_small(volatile void *ptr, unsigned long x,
25				  unsigned int size);
26
27static __always_inline unsigned long
28__xchg(volatile void *ptr, unsigned long x, int size)
29{
30	switch (size) {
31	case 1:
32	case 2:
33		return __xchg_small(ptr, x, size);
34
35	case 4:
36		return __xchg_asm("amswap_db.w", (volatile u32 *)ptr, (u32)x);
37
38	case 8:
39		return __xchg_asm("amswap_db.d", (volatile u64 *)ptr, (u64)x);
40
41	default:
42		BUILD_BUG();
43	}
44
45	return 0;
46}
47
48#define xchg(ptr, x)							\
49({									\
50	__typeof__(*(ptr)) __res;					\
51									\
52	__res = (__typeof__(*(ptr)))					\
53		__xchg((ptr), (unsigned long)(x), sizeof(*(ptr)));	\
54									\
55	__res;								\
56})
57
58#define __cmpxchg_asm(ld, st, m, old, new)				\
59({									\
60	__typeof(old) __ret;						\
61									\
62	__asm__ __volatile__(						\
63	"1:	" ld "	%0, %2		# __cmpxchg_asm \n"		\
64	"	bne	%0, %z3, 2f			\n"		\
65	"	or	$t0, %z4, $zero			\n"		\
66	"	" st "	$t0, %1				\n"		\
67	"	beq	$zero, $t0, 1b			\n"		\
68	"2:						\n"		\
69	__WEAK_LLSC_MB							\
70	: "=&r" (__ret), "=ZB"(*m)					\
71	: "ZB"(*m), "Jr" (old), "Jr" (new)				\
72	: "t0", "memory");						\
73									\
74	__ret;								\
75})
76
77extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
78				     unsigned long new, unsigned int size);
79
80static __always_inline unsigned long
81__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size)
82{
83	switch (size) {
84	case 1:
85	case 2:
86		return __cmpxchg_small(ptr, old, new, size);
87
88	case 4:
89		return __cmpxchg_asm("ll.w", "sc.w", (volatile u32 *)ptr,
90				     (u32)old, new);
91
92	case 8:
93		return __cmpxchg_asm("ll.d", "sc.d", (volatile u64 *)ptr,
94				     (u64)old, new);
95
96	default:
97		BUILD_BUG();
98	}
99
100	return 0;
101}
102
103#define cmpxchg_local(ptr, old, new)					\
104	((__typeof__(*(ptr)))						\
105		__cmpxchg((ptr),					\
106			  (unsigned long)(__typeof__(*(ptr)))(old),	\
107			  (unsigned long)(__typeof__(*(ptr)))(new),	\
108			  sizeof(*(ptr))))
109
110#define cmpxchg(ptr, old, new)						\
111({									\
112	__typeof__(*(ptr)) __res;					\
113									\
114	__res = cmpxchg_local((ptr), (old), (new));			\
115									\
116	__res;								\
117})
118
119#ifdef CONFIG_64BIT
120#define cmpxchg64_local(ptr, o, n)					\
121  ({									\
122	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
123	cmpxchg_local((ptr), (o), (n));					\
124  })
125
126#define cmpxchg64(ptr, o, n)						\
127  ({									\
128	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
129	cmpxchg((ptr), (o), (n));					\
130  })
131#else
132#include <asm-generic/cmpxchg-local.h>
133#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
134#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n))
135#endif
136
137#endif /* __ASM_CMPXCHG_H */
138