xref: /openbmc/linux/arch/parisc/include/asm/spinlock.h (revision 55fd7e02)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef __ASM_SPINLOCK_H
3 #define __ASM_SPINLOCK_H
4 
5 #include <asm/barrier.h>
6 #include <asm/ldcw.h>
7 #include <asm/processor.h>
8 #include <asm/spinlock_types.h>
9 
10 static inline int arch_spin_is_locked(arch_spinlock_t *x)
11 {
12 	volatile unsigned int *a = __ldcw_align(x);
13 	smp_mb();
14 	return *a == 0;
15 }
16 
17 static inline void arch_spin_lock(arch_spinlock_t *x)
18 {
19 	volatile unsigned int *a;
20 
21 	a = __ldcw_align(x);
22 	while (__ldcw(a) == 0)
23 		while (*a == 0)
24 			cpu_relax();
25 }
26 
27 static inline void arch_spin_lock_flags(arch_spinlock_t *x,
28 					 unsigned long flags)
29 {
30 	volatile unsigned int *a;
31 	unsigned long flags_dis;
32 
33 	a = __ldcw_align(x);
34 	while (__ldcw(a) == 0) {
35 		local_save_flags(flags_dis);
36 		local_irq_restore(flags);
37 		while (*a == 0)
38 			cpu_relax();
39 		local_irq_restore(flags_dis);
40 	}
41 }
42 #define arch_spin_lock_flags arch_spin_lock_flags
43 
44 static inline void arch_spin_unlock(arch_spinlock_t *x)
45 {
46 	volatile unsigned int *a;
47 
48 	a = __ldcw_align(x);
49 #ifdef CONFIG_SMP
50 	(void) __ldcw(a);
51 #else
52 	mb();
53 #endif
54 	*a = 1;
55 }
56 
57 static inline int arch_spin_trylock(arch_spinlock_t *x)
58 {
59 	volatile unsigned int *a;
60 	int ret;
61 
62 	a = __ldcw_align(x);
63         ret = __ldcw(a) != 0;
64 
65 	return ret;
66 }
67 
68 /*
69  * Read-write spinlocks, allowing multiple readers but only one writer.
70  * Unfair locking as Writers could be starved indefinitely by Reader(s)
71  *
72  * The spinlock itself is contained in @counter and access to it is
73  * serialized with @lock_mutex.
74  */
75 
76 /* 1 - lock taken successfully */
77 static inline int arch_read_trylock(arch_rwlock_t *rw)
78 {
79 	int ret = 0;
80 	unsigned long flags;
81 
82 	local_irq_save(flags);
83 	arch_spin_lock(&(rw->lock_mutex));
84 
85 	/*
86 	 * zero means writer holds the lock exclusively, deny Reader.
87 	 * Otherwise grant lock to first/subseq reader
88 	 */
89 	if (rw->counter > 0) {
90 		rw->counter--;
91 		ret = 1;
92 	}
93 
94 	arch_spin_unlock(&(rw->lock_mutex));
95 	local_irq_restore(flags);
96 
97 	return ret;
98 }
99 
100 /* 1 - lock taken successfully */
101 static inline int arch_write_trylock(arch_rwlock_t *rw)
102 {
103 	int ret = 0;
104 	unsigned long flags;
105 
106 	local_irq_save(flags);
107 	arch_spin_lock(&(rw->lock_mutex));
108 
109 	/*
110 	 * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__),
111 	 * deny writer. Otherwise if unlocked grant to writer
112 	 * Hence the claim that Linux rwlocks are unfair to writers.
113 	 * (can be starved for an indefinite time by readers).
114 	 */
115 	if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
116 		rw->counter = 0;
117 		ret = 1;
118 	}
119 	arch_spin_unlock(&(rw->lock_mutex));
120 	local_irq_restore(flags);
121 
122 	return ret;
123 }
124 
125 static inline void arch_read_lock(arch_rwlock_t *rw)
126 {
127 	while (!arch_read_trylock(rw))
128 		cpu_relax();
129 }
130 
131 static inline void arch_write_lock(arch_rwlock_t *rw)
132 {
133 	while (!arch_write_trylock(rw))
134 		cpu_relax();
135 }
136 
137 static inline void arch_read_unlock(arch_rwlock_t *rw)
138 {
139 	unsigned long flags;
140 
141 	local_irq_save(flags);
142 	arch_spin_lock(&(rw->lock_mutex));
143 	rw->counter++;
144 	arch_spin_unlock(&(rw->lock_mutex));
145 	local_irq_restore(flags);
146 }
147 
148 static inline void arch_write_unlock(arch_rwlock_t *rw)
149 {
150 	unsigned long flags;
151 
152 	local_irq_save(flags);
153 	arch_spin_lock(&(rw->lock_mutex));
154 	rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
155 	arch_spin_unlock(&(rw->lock_mutex));
156 	local_irq_restore(flags);
157 }
158 
159 #endif /* __ASM_SPINLOCK_H */
160