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 #include <asm/fence.h>
21
22 /*
23 * Simple spin lock operations. These provide no fairness guarantees.
24 */
25
26 /* FIXME: Replace this with a ticket lock, like MIPS. */
27
28 #define arch_spin_is_locked(x) (READ_ONCE((x)->lock) != 0)
29
arch_spin_unlock(arch_spinlock_t * lock)30 static inline void arch_spin_unlock(arch_spinlock_t *lock)
31 {
32 smp_store_release(&lock->lock, 0);
33 }
34
arch_spin_trylock(arch_spinlock_t * lock)35 static inline int arch_spin_trylock(arch_spinlock_t *lock)
36 {
37 int tmp = 1, busy;
38
39 __asm__ __volatile__ (
40 " amoswap.w %0, %2, %1\n"
41 RISCV_ACQUIRE_BARRIER
42 : "=r" (busy), "+A" (lock->lock)
43 : "r" (tmp)
44 : "memory");
45
46 return !busy;
47 }
48
arch_spin_lock(arch_spinlock_t * lock)49 static inline void arch_spin_lock(arch_spinlock_t *lock)
50 {
51 while (1) {
52 if (arch_spin_is_locked(lock))
53 continue;
54
55 if (arch_spin_trylock(lock))
56 break;
57 }
58 }
59
60 /***********************************************************/
61
arch_read_lock(arch_rwlock_t * lock)62 static inline void arch_read_lock(arch_rwlock_t *lock)
63 {
64 int tmp;
65
66 __asm__ __volatile__(
67 "1: lr.w %1, %0\n"
68 " bltz %1, 1b\n"
69 " addi %1, %1, 1\n"
70 " sc.w %1, %1, %0\n"
71 " bnez %1, 1b\n"
72 RISCV_ACQUIRE_BARRIER
73 : "+A" (lock->lock), "=&r" (tmp)
74 :: "memory");
75 }
76
arch_write_lock(arch_rwlock_t * lock)77 static inline void arch_write_lock(arch_rwlock_t *lock)
78 {
79 int tmp;
80
81 __asm__ __volatile__(
82 "1: lr.w %1, %0\n"
83 " bnez %1, 1b\n"
84 " li %1, -1\n"
85 " sc.w %1, %1, %0\n"
86 " bnez %1, 1b\n"
87 RISCV_ACQUIRE_BARRIER
88 : "+A" (lock->lock), "=&r" (tmp)
89 :: "memory");
90 }
91
arch_read_trylock(arch_rwlock_t * lock)92 static inline int arch_read_trylock(arch_rwlock_t *lock)
93 {
94 int busy;
95
96 __asm__ __volatile__(
97 "1: lr.w %1, %0\n"
98 " bltz %1, 1f\n"
99 " addi %1, %1, 1\n"
100 " sc.w %1, %1, %0\n"
101 " bnez %1, 1b\n"
102 RISCV_ACQUIRE_BARRIER
103 "1:\n"
104 : "+A" (lock->lock), "=&r" (busy)
105 :: "memory");
106
107 return !busy;
108 }
109
arch_write_trylock(arch_rwlock_t * lock)110 static inline int arch_write_trylock(arch_rwlock_t *lock)
111 {
112 int busy;
113
114 __asm__ __volatile__(
115 "1: lr.w %1, %0\n"
116 " bnez %1, 1f\n"
117 " li %1, -1\n"
118 " sc.w %1, %1, %0\n"
119 " bnez %1, 1b\n"
120 RISCV_ACQUIRE_BARRIER
121 "1:\n"
122 : "+A" (lock->lock), "=&r" (busy)
123 :: "memory");
124
125 return !busy;
126 }
127
arch_read_unlock(arch_rwlock_t * lock)128 static inline void arch_read_unlock(arch_rwlock_t *lock)
129 {
130 __asm__ __volatile__(
131 RISCV_RELEASE_BARRIER
132 " amoadd.w x0, %1, %0\n"
133 : "+A" (lock->lock)
134 : "r" (-1)
135 : "memory");
136 }
137
arch_write_unlock(arch_rwlock_t * lock)138 static inline void arch_write_unlock(arch_rwlock_t *lock)
139 {
140 smp_store_release(&lock->lock, 0);
141 }
142
143 #endif /* _ASM_RISCV_SPINLOCK_H */
144