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