1/* SPDX-License-Identifier: GPL-2.0 */
2
3#ifndef __ASM_CSKY_SPINLOCK_H
4#define __ASM_CSKY_SPINLOCK_H
5
6#include <linux/spinlock_types.h>
7#include <asm/barrier.h>
8
9#ifdef CONFIG_QUEUED_RWLOCKS
10
11/*
12 * Ticket-based spin-locking.
13 */
14static inline void arch_spin_lock(arch_spinlock_t *lock)
15{
16	arch_spinlock_t lockval;
17	u32 ticket_next = 1 << TICKET_NEXT;
18	u32 *p = &lock->lock;
19	u32 tmp;
20
21	asm volatile (
22		"1:	ldex.w		%0, (%2) \n"
23		"	mov		%1, %0	 \n"
24		"	add		%0, %3	 \n"
25		"	stex.w		%0, (%2) \n"
26		"	bez		%0, 1b   \n"
27		: "=&r" (tmp), "=&r" (lockval)
28		: "r"(p), "r"(ticket_next)
29		: "cc");
30
31	while (lockval.tickets.next != lockval.tickets.owner)
32		lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
33
34	smp_mb();
35}
36
37static inline int arch_spin_trylock(arch_spinlock_t *lock)
38{
39	u32 tmp, contended, res;
40	u32 ticket_next = 1 << TICKET_NEXT;
41	u32 *p = &lock->lock;
42
43	do {
44		asm volatile (
45		"	ldex.w		%0, (%3)   \n"
46		"	movi		%2, 1	   \n"
47		"	rotli		%1, %0, 16 \n"
48		"	cmpne		%1, %0     \n"
49		"	bt		1f         \n"
50		"	movi		%2, 0	   \n"
51		"	add		%0, %0, %4 \n"
52		"	stex.w		%0, (%3)   \n"
53		"1:				   \n"
54		: "=&r" (res), "=&r" (tmp), "=&r" (contended)
55		: "r"(p), "r"(ticket_next)
56		: "cc");
57	} while (!res);
58
59	if (!contended)
60		smp_mb();
61
62	return !contended;
63}
64
65static inline void arch_spin_unlock(arch_spinlock_t *lock)
66{
67	smp_mb();
68	WRITE_ONCE(lock->tickets.owner, lock->tickets.owner + 1);
69}
70
71static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
72{
73	return lock.tickets.owner == lock.tickets.next;
74}
75
76static inline int arch_spin_is_locked(arch_spinlock_t *lock)
77{
78	return !arch_spin_value_unlocked(READ_ONCE(*lock));
79}
80
81static inline int arch_spin_is_contended(arch_spinlock_t *lock)
82{
83	struct __raw_tickets tickets = READ_ONCE(lock->tickets);
84
85	return (tickets.next - tickets.owner) > 1;
86}
87#define arch_spin_is_contended	arch_spin_is_contended
88
89#include <asm/qrwlock.h>
90
91/* See include/linux/spinlock.h */
92#define smp_mb__after_spinlock()	smp_mb()
93
94#else /* CONFIG_QUEUED_RWLOCKS */
95
96/*
97 * Test-and-set spin-locking.
98 */
99static inline void arch_spin_lock(arch_spinlock_t *lock)
100{
101	u32 *p = &lock->lock;
102	u32 tmp;
103
104	asm volatile (
105		"1:	ldex.w		%0, (%1) \n"
106		"	bnez		%0, 1b   \n"
107		"	movi		%0, 1    \n"
108		"	stex.w		%0, (%1) \n"
109		"	bez		%0, 1b   \n"
110		: "=&r" (tmp)
111		: "r"(p)
112		: "cc");
113	smp_mb();
114}
115
116static inline void arch_spin_unlock(arch_spinlock_t *lock)
117{
118	smp_mb();
119	WRITE_ONCE(lock->lock, 0);
120}
121
122static inline int arch_spin_trylock(arch_spinlock_t *lock)
123{
124	u32 *p = &lock->lock;
125	u32 tmp;
126
127	asm volatile (
128		"1:	ldex.w		%0, (%1) \n"
129		"	bnez		%0, 2f   \n"
130		"	movi		%0, 1    \n"
131		"	stex.w		%0, (%1) \n"
132		"	bez		%0, 1b   \n"
133		"	movi		%0, 0    \n"
134		"2:				 \n"
135		: "=&r" (tmp)
136		: "r"(p)
137		: "cc");
138
139	if (!tmp)
140		smp_mb();
141
142	return !tmp;
143}
144
145#define arch_spin_is_locked(x)	(READ_ONCE((x)->lock) != 0)
146
147/*
148 * read lock/unlock/trylock
149 */
150static inline void arch_read_lock(arch_rwlock_t *lock)
151{
152	u32 *p = &lock->lock;
153	u32 tmp;
154
155	asm volatile (
156		"1:	ldex.w		%0, (%1) \n"
157		"	blz		%0, 1b   \n"
158		"	addi		%0, 1    \n"
159		"	stex.w		%0, (%1) \n"
160		"	bez		%0, 1b   \n"
161		: "=&r" (tmp)
162		: "r"(p)
163		: "cc");
164	smp_mb();
165}
166
167static inline void arch_read_unlock(arch_rwlock_t *lock)
168{
169	u32 *p = &lock->lock;
170	u32 tmp;
171
172	smp_mb();
173	asm volatile (
174		"1:	ldex.w		%0, (%1) \n"
175		"	subi		%0, 1    \n"
176		"	stex.w		%0, (%1) \n"
177		"	bez		%0, 1b   \n"
178		: "=&r" (tmp)
179		: "r"(p)
180		: "cc");
181}
182
183static inline int arch_read_trylock(arch_rwlock_t *lock)
184{
185	u32 *p = &lock->lock;
186	u32 tmp;
187
188	asm volatile (
189		"1:	ldex.w		%0, (%1) \n"
190		"	blz		%0, 2f   \n"
191		"	addi		%0, 1    \n"
192		"	stex.w		%0, (%1) \n"
193		"	bez		%0, 1b   \n"
194		"	movi		%0, 0    \n"
195		"2:				 \n"
196		: "=&r" (tmp)
197		: "r"(p)
198		: "cc");
199
200	if (!tmp)
201		smp_mb();
202
203	return !tmp;
204}
205
206/*
207 * write lock/unlock/trylock
208 */
209static inline void arch_write_lock(arch_rwlock_t *lock)
210{
211	u32 *p = &lock->lock;
212	u32 tmp;
213
214	asm volatile (
215		"1:	ldex.w		%0, (%1) \n"
216		"	bnez		%0, 1b   \n"
217		"	subi		%0, 1    \n"
218		"	stex.w		%0, (%1) \n"
219		"	bez		%0, 1b   \n"
220		: "=&r" (tmp)
221		: "r"(p)
222		: "cc");
223	smp_mb();
224}
225
226static inline void arch_write_unlock(arch_rwlock_t *lock)
227{
228	smp_mb();
229	WRITE_ONCE(lock->lock, 0);
230}
231
232static inline int arch_write_trylock(arch_rwlock_t *lock)
233{
234	u32 *p = &lock->lock;
235	u32 tmp;
236
237	asm volatile (
238		"1:	ldex.w		%0, (%1) \n"
239		"	bnez		%0, 2f   \n"
240		"	subi		%0, 1    \n"
241		"	stex.w		%0, (%1) \n"
242		"	bez		%0, 1b   \n"
243		"	movi		%0, 0    \n"
244		"2:				 \n"
245		: "=&r" (tmp)
246		: "r"(p)
247		: "cc");
248
249	if (!tmp)
250		smp_mb();
251
252	return !tmp;
253}
254
255#endif /* CONFIG_QUEUED_RWLOCKS */
256#endif /* __ASM_CSKY_SPINLOCK_H */
257