1/* SPDX-License-Identifier: GPL-2.0
2 *
3 * include/asm-sh/spinlock-llsc.h
4 *
5 * Copyright (C) 2002, 2003 Paul Mundt
6 * Copyright (C) 2006, 2007 Akio Idehara
7 */
8#ifndef __ASM_SH_SPINLOCK_LLSC_H
9#define __ASM_SH_SPINLOCK_LLSC_H
10
11#include <asm/barrier.h>
12#include <asm/processor.h>
13
14/*
15 * Your basic SMP spinlocks, allowing only a single CPU anywhere
16 */
17
18#define arch_spin_is_locked(x)		((x)->lock <= 0)
19
20/*
21 * Simple spin lock operations.  There are two variants, one clears IRQ's
22 * on the local processor, one does not.
23 *
24 * We make no fairness assumptions.  They have a cost.
25 */
26static inline void arch_spin_lock(arch_spinlock_t *lock)
27{
28	unsigned long tmp;
29	unsigned long oldval;
30
31	__asm__ __volatile__ (
32		"1:						\n\t"
33		"movli.l	@%2, %0	! arch_spin_lock	\n\t"
34		"mov		%0, %1				\n\t"
35		"mov		#0, %0				\n\t"
36		"movco.l	%0, @%2				\n\t"
37		"bf		1b				\n\t"
38		"cmp/pl		%1				\n\t"
39		"bf		1b				\n\t"
40		: "=&z" (tmp), "=&r" (oldval)
41		: "r" (&lock->lock)
42		: "t", "memory"
43	);
44}
45
46static inline void arch_spin_unlock(arch_spinlock_t *lock)
47{
48	unsigned long tmp;
49
50	/* This could be optimised with ARCH_HAS_MMIOWB */
51	mmiowb();
52	__asm__ __volatile__ (
53		"mov		#1, %0 ! arch_spin_unlock	\n\t"
54		"mov.l		%0, @%1				\n\t"
55		: "=&z" (tmp)
56		: "r" (&lock->lock)
57		: "t", "memory"
58	);
59}
60
61static inline int arch_spin_trylock(arch_spinlock_t *lock)
62{
63	unsigned long tmp, oldval;
64
65	__asm__ __volatile__ (
66		"1:						\n\t"
67		"movli.l	@%2, %0	! arch_spin_trylock	\n\t"
68		"mov		%0, %1				\n\t"
69		"mov		#0, %0				\n\t"
70		"movco.l	%0, @%2				\n\t"
71		"bf		1b				\n\t"
72		"synco						\n\t"
73		: "=&z" (tmp), "=&r" (oldval)
74		: "r" (&lock->lock)
75		: "t", "memory"
76	);
77
78	return oldval;
79}
80
81/*
82 * Read-write spinlocks, allowing multiple readers but only one writer.
83 *
84 * NOTE! it is quite common to have readers in interrupts but no interrupt
85 * writers. For those circumstances we can "mix" irq-safe locks - any writer
86 * needs to get a irq-safe write-lock, but readers can get non-irqsafe
87 * read-locks.
88 */
89
90static inline void arch_read_lock(arch_rwlock_t *rw)
91{
92	unsigned long tmp;
93
94	__asm__ __volatile__ (
95		"1:						\n\t"
96		"movli.l	@%1, %0	! arch_read_lock	\n\t"
97		"cmp/pl		%0				\n\t"
98		"bf		1b				\n\t"
99		"add		#-1, %0				\n\t"
100		"movco.l	%0, @%1				\n\t"
101		"bf		1b				\n\t"
102		: "=&z" (tmp)
103		: "r" (&rw->lock)
104		: "t", "memory"
105	);
106}
107
108static inline void arch_read_unlock(arch_rwlock_t *rw)
109{
110	unsigned long tmp;
111
112	__asm__ __volatile__ (
113		"1:						\n\t"
114		"movli.l	@%1, %0	! arch_read_unlock	\n\t"
115		"add		#1, %0				\n\t"
116		"movco.l	%0, @%1				\n\t"
117		"bf		1b				\n\t"
118		: "=&z" (tmp)
119		: "r" (&rw->lock)
120		: "t", "memory"
121	);
122}
123
124static inline void arch_write_lock(arch_rwlock_t *rw)
125{
126	unsigned long tmp;
127
128	__asm__ __volatile__ (
129		"1:						\n\t"
130		"movli.l	@%1, %0	! arch_write_lock	\n\t"
131		"cmp/hs		%2, %0				\n\t"
132		"bf		1b				\n\t"
133		"sub		%2, %0				\n\t"
134		"movco.l	%0, @%1				\n\t"
135		"bf		1b				\n\t"
136		: "=&z" (tmp)
137		: "r" (&rw->lock), "r" (RW_LOCK_BIAS)
138		: "t", "memory"
139	);
140}
141
142static inline void arch_write_unlock(arch_rwlock_t *rw)
143{
144	__asm__ __volatile__ (
145		"mov.l		%1, @%0 ! arch_write_unlock	\n\t"
146		:
147		: "r" (&rw->lock), "r" (RW_LOCK_BIAS)
148		: "t", "memory"
149	);
150}
151
152static inline int arch_read_trylock(arch_rwlock_t *rw)
153{
154	unsigned long tmp, oldval;
155
156	__asm__ __volatile__ (
157		"1:						\n\t"
158		"movli.l	@%2, %0	! arch_read_trylock	\n\t"
159		"mov		%0, %1				\n\t"
160		"cmp/pl		%0				\n\t"
161		"bf		2f				\n\t"
162		"add		#-1, %0				\n\t"
163		"movco.l	%0, @%2				\n\t"
164		"bf		1b				\n\t"
165		"2:						\n\t"
166		"synco						\n\t"
167		: "=&z" (tmp), "=&r" (oldval)
168		: "r" (&rw->lock)
169		: "t", "memory"
170	);
171
172	return (oldval > 0);
173}
174
175static inline int arch_write_trylock(arch_rwlock_t *rw)
176{
177	unsigned long tmp, oldval;
178
179	__asm__ __volatile__ (
180		"1:						\n\t"
181		"movli.l	@%2, %0	! arch_write_trylock	\n\t"
182		"mov		%0, %1				\n\t"
183		"cmp/hs		%3, %0				\n\t"
184		"bf		2f				\n\t"
185		"sub		%3, %0				\n\t"
186		"2:						\n\t"
187		"movco.l	%0, @%2				\n\t"
188		"bf		1b				\n\t"
189		"synco						\n\t"
190		: "=&z" (tmp), "=&r" (oldval)
191		: "r" (&rw->lock), "r" (RW_LOCK_BIAS)
192		: "t", "memory"
193	);
194
195	return (oldval > (RW_LOCK_BIAS - 1));
196}
197
198#endif /* __ASM_SH_SPINLOCK_LLSC_H */
199