xref: /linux/arch/riscv/include/asm/spinlock.h (revision 841b86f3289dbe858daeceec36423d4ea286fac2)
1 /*
2  * Copyright (C) 2015 Regents of the University of California
3  * Copyright (C) 2017 SiFive
4  *
5  *   This program is free software; you can redistribute it and/or
6  *   modify it under the terms of the GNU General Public License
7  *   as published by the Free Software Foundation, version 2.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  */
14 
15 #ifndef _ASM_RISCV_SPINLOCK_H
16 #define _ASM_RISCV_SPINLOCK_H
17 
18 #include <linux/kernel.h>
19 #include <asm/current.h>
20 
21 /*
22  * Simple spin lock operations.  These provide no fairness guarantees.
23  */
24 
25 /* FIXME: Replace this with a ticket lock, like MIPS. */
26 
27 #define arch_spin_is_locked(x)	((x)->lock != 0)
28 
29 static inline void arch_spin_unlock(arch_spinlock_t *lock)
30 {
31 	__asm__ __volatile__ (
32 		"amoswap.w.rl x0, x0, %0"
33 		: "=A" (lock->lock)
34 		:: "memory");
35 }
36 
37 static inline int arch_spin_trylock(arch_spinlock_t *lock)
38 {
39 	int tmp = 1, busy;
40 
41 	__asm__ __volatile__ (
42 		"amoswap.w.aq %0, %2, %1"
43 		: "=r" (busy), "+A" (lock->lock)
44 		: "r" (tmp)
45 		: "memory");
46 
47 	return !busy;
48 }
49 
50 static inline void arch_spin_lock(arch_spinlock_t *lock)
51 {
52 	while (1) {
53 		if (arch_spin_is_locked(lock))
54 			continue;
55 
56 		if (arch_spin_trylock(lock))
57 			break;
58 	}
59 }
60 
61 static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
62 {
63 	smp_rmb();
64 	do {
65 		cpu_relax();
66 	} while (arch_spin_is_locked(lock));
67 	smp_acquire__after_ctrl_dep();
68 }
69 
70 /***********************************************************/
71 
72 static inline void arch_read_lock(arch_rwlock_t *lock)
73 {
74 	int tmp;
75 
76 	__asm__ __volatile__(
77 		"1:	lr.w	%1, %0\n"
78 		"	bltz	%1, 1b\n"
79 		"	addi	%1, %1, 1\n"
80 		"	sc.w.aq	%1, %1, %0\n"
81 		"	bnez	%1, 1b\n"
82 		: "+A" (lock->lock), "=&r" (tmp)
83 		:: "memory");
84 }
85 
86 static inline void arch_write_lock(arch_rwlock_t *lock)
87 {
88 	int tmp;
89 
90 	__asm__ __volatile__(
91 		"1:	lr.w	%1, %0\n"
92 		"	bnez	%1, 1b\n"
93 		"	li	%1, -1\n"
94 		"	sc.w.aq	%1, %1, %0\n"
95 		"	bnez	%1, 1b\n"
96 		: "+A" (lock->lock), "=&r" (tmp)
97 		:: "memory");
98 }
99 
100 static inline int arch_read_trylock(arch_rwlock_t *lock)
101 {
102 	int busy;
103 
104 	__asm__ __volatile__(
105 		"1:	lr.w	%1, %0\n"
106 		"	bltz	%1, 1f\n"
107 		"	addi	%1, %1, 1\n"
108 		"	sc.w.aq	%1, %1, %0\n"
109 		"	bnez	%1, 1b\n"
110 		"1:\n"
111 		: "+A" (lock->lock), "=&r" (busy)
112 		:: "memory");
113 
114 	return !busy;
115 }
116 
117 static inline int arch_write_trylock(arch_rwlock_t *lock)
118 {
119 	int busy;
120 
121 	__asm__ __volatile__(
122 		"1:	lr.w	%1, %0\n"
123 		"	bnez	%1, 1f\n"
124 		"	li	%1, -1\n"
125 		"	sc.w.aq	%1, %1, %0\n"
126 		"	bnez	%1, 1b\n"
127 		"1:\n"
128 		: "+A" (lock->lock), "=&r" (busy)
129 		:: "memory");
130 
131 	return !busy;
132 }
133 
134 static inline void arch_read_unlock(arch_rwlock_t *lock)
135 {
136 	__asm__ __volatile__(
137 		"amoadd.w.rl x0, %1, %0"
138 		: "+A" (lock->lock)
139 		: "r" (-1)
140 		: "memory");
141 }
142 
143 static inline void arch_write_unlock(arch_rwlock_t *lock)
144 {
145 	__asm__ __volatile__ (
146 		"amoswap.w.rl x0, x0, %0"
147 		: "=A" (lock->lock)
148 		:: "memory");
149 }
150 
151 #endif /* _ASM_RISCV_SPINLOCK_H */
152