xref: /rk3399_ARM-atf/lib/locks/exclusive/aarch64/spinlock.c (revision fdf3f697f22728a9150aefe3fe79c48b66e5e043)
1*38e580e6SBoyan Karatotev /*
2*38e580e6SBoyan Karatotev  * Copyright (c) 2025, Arm Limited. All rights reserved.
3*38e580e6SBoyan Karatotev  *
4*38e580e6SBoyan Karatotev  * SPDX-License-Identifier: BSD-3-Clause
5*38e580e6SBoyan Karatotev  */
6*38e580e6SBoyan Karatotev 
7*38e580e6SBoyan Karatotev #include <arch_features.h>
8*38e580e6SBoyan Karatotev #include <lib/spinlock.h>
9*38e580e6SBoyan Karatotev 
10*38e580e6SBoyan Karatotev /*
11*38e580e6SBoyan Karatotev  * Performs a compare-and-swap of 0 -> 1. If the lock is already held, uses
12*38e580e6SBoyan Karatotev  * LDAXR/WFE to efficiently wait.
13*38e580e6SBoyan Karatotev  */
spin_lock_atomic(volatile uint32_t * dst)14*38e580e6SBoyan Karatotev static void spin_lock_atomic(volatile uint32_t *dst)
15*38e580e6SBoyan Karatotev {
16*38e580e6SBoyan Karatotev 	uint32_t src = 1;
17*38e580e6SBoyan Karatotev 	uint32_t tmp;
18*38e580e6SBoyan Karatotev 
19*38e580e6SBoyan Karatotev 	__asm__ volatile (
20*38e580e6SBoyan Karatotev 	".arch_extension lse\n"
21*38e580e6SBoyan Karatotev 	"1:	mov	%w[tmp], wzr\n"
22*38e580e6SBoyan Karatotev 	"2:	casa	%w[tmp], %w[src], [%[dst]]\n"
23*38e580e6SBoyan Karatotev 	"	cbz	%w[tmp], 3f\n"
24*38e580e6SBoyan Karatotev 	"	ldxr	%w[tmp], [%[dst]]\n"
25*38e580e6SBoyan Karatotev 	"	cbz	%w[tmp], 2b\n"
26*38e580e6SBoyan Karatotev 	"	wfe\n"
27*38e580e6SBoyan Karatotev 	"	b	1b\n"
28*38e580e6SBoyan Karatotev 	"3:\n"
29*38e580e6SBoyan Karatotev 	: "+m" (*dst), [tmp] "=&r" (tmp), [src] "+r" (src)
30*38e580e6SBoyan Karatotev 	: [dst] "r" (dst));
31*38e580e6SBoyan Karatotev }
32*38e580e6SBoyan Karatotev 
33*38e580e6SBoyan Karatotev /*
34*38e580e6SBoyan Karatotev  * Uses the load-acquire (LDAXR) and store-exclusive (STXR) instruction pair.
35*38e580e6SBoyan Karatotev  */
spin_lock_excl(volatile uint32_t * dst)36*38e580e6SBoyan Karatotev static void spin_lock_excl(volatile uint32_t *dst)
37*38e580e6SBoyan Karatotev {
38*38e580e6SBoyan Karatotev 	uint32_t src = 1;
39*38e580e6SBoyan Karatotev 	uint32_t tmp;
40*38e580e6SBoyan Karatotev 
41*38e580e6SBoyan Karatotev 	__asm__ volatile (
42*38e580e6SBoyan Karatotev 	"	sevl\n"
43*38e580e6SBoyan Karatotev 	"1:	wfe\n"
44*38e580e6SBoyan Karatotev 	"2:	ldaxr	%w[tmp], [%[dst]]\n"
45*38e580e6SBoyan Karatotev 	"	cbnz	%w[tmp], 1b\n"
46*38e580e6SBoyan Karatotev 	"	stxr	%w[tmp], %w[src], [%[dst]]\n"
47*38e580e6SBoyan Karatotev 	"	cbnz	%w[tmp], 2b\n"
48*38e580e6SBoyan Karatotev 	: "+m" (*dst), [tmp] "=&r" (tmp), [src] "+r" (src)
49*38e580e6SBoyan Karatotev 	: [dst] "r" (dst));
50*38e580e6SBoyan Karatotev }
51*38e580e6SBoyan Karatotev 
spin_lock(spinlock_t * lock)52*38e580e6SBoyan Karatotev void spin_lock(spinlock_t *lock)
53*38e580e6SBoyan Karatotev {
54*38e580e6SBoyan Karatotev 	volatile uint32_t *dst = &(lock->lock);
55*38e580e6SBoyan Karatotev 
56*38e580e6SBoyan Karatotev 	if (is_feat_lse_supported()) {
57*38e580e6SBoyan Karatotev 		spin_lock_atomic(dst);
58*38e580e6SBoyan Karatotev 	} else {
59*38e580e6SBoyan Karatotev 		spin_lock_excl(dst);
60*38e580e6SBoyan Karatotev 	}
61*38e580e6SBoyan Karatotev }
62*38e580e6SBoyan Karatotev 
63*38e580e6SBoyan Karatotev /*
64*38e580e6SBoyan Karatotev  * Use store-release to unconditionally clear the spinlock variable. Store
65*38e580e6SBoyan Karatotev  * operation generates an event to all cores waiting in WFE when address is
66*38e580e6SBoyan Karatotev  * monitored by the global monitor.
67*38e580e6SBoyan Karatotev  */
spin_unlock(spinlock_t * lock)68*38e580e6SBoyan Karatotev void spin_unlock(spinlock_t *lock)
69*38e580e6SBoyan Karatotev {
70*38e580e6SBoyan Karatotev 	volatile uint32_t *dst = &(lock->lock);
71*38e580e6SBoyan Karatotev 
72*38e580e6SBoyan Karatotev 	__asm__ volatile (
73*38e580e6SBoyan Karatotev 	"stlr	wzr, [%[dst]]"
74*38e580e6SBoyan Karatotev 	: "=m" (dst)
75*38e580e6SBoyan Karatotev 	: [dst] "r" (dst));
76*38e580e6SBoyan Karatotev }
77*38e580e6SBoyan Karatotev 
spin_trylock_atomic(volatile uint32_t * dst)78*38e580e6SBoyan Karatotev static bool spin_trylock_atomic(volatile uint32_t *dst)
79*38e580e6SBoyan Karatotev {
80*38e580e6SBoyan Karatotev 	uint32_t src = 1;
81*38e580e6SBoyan Karatotev 	uint32_t tmp = 0;
82*38e580e6SBoyan Karatotev 	bool out;
83*38e580e6SBoyan Karatotev 
84*38e580e6SBoyan Karatotev 	__asm__ volatile (
85*38e580e6SBoyan Karatotev 	".arch_extension lse\n"
86*38e580e6SBoyan Karatotev 	"casa	%w[tmp], %w[src], [%[dst]]\n"
87*38e580e6SBoyan Karatotev 	"eor	%w[out], %w[tmp], #1\n" /* convert the result to bool */
88*38e580e6SBoyan Karatotev 	: "+m" (*dst), [tmp] "+r" (tmp), [out] "=r" (out)
89*38e580e6SBoyan Karatotev 	: [src] "r" (src), [dst] "r" (dst));
90*38e580e6SBoyan Karatotev 
91*38e580e6SBoyan Karatotev 	return out;
92*38e580e6SBoyan Karatotev }
93*38e580e6SBoyan Karatotev 
spin_trylock_excl(volatile uint32_t * dst)94*38e580e6SBoyan Karatotev static bool spin_trylock_excl(volatile uint32_t *dst)
95*38e580e6SBoyan Karatotev {
96*38e580e6SBoyan Karatotev 	uint32_t src = 1;
97*38e580e6SBoyan Karatotev 	uint32_t tmp;
98*38e580e6SBoyan Karatotev 	bool out;
99*38e580e6SBoyan Karatotev 
100*38e580e6SBoyan Karatotev 	__asm__ volatile (
101*38e580e6SBoyan Karatotev 	"ldaxr	%w[tmp], [%[dst]]\n"
102*38e580e6SBoyan Karatotev 	"cbnz	%w[tmp], 1f\n"
103*38e580e6SBoyan Karatotev 	"stxr	%w[tmp], %w[src], [%[dst]]\n"
104*38e580e6SBoyan Karatotev 	"cbnz	%w[tmp], 1f\n"
105*38e580e6SBoyan Karatotev 	"mov	%w[out], #1\n"
106*38e580e6SBoyan Karatotev 	"1:\n" /* fail */
107*38e580e6SBoyan Karatotev 	"mov	%w[out], #0\n"
108*38e580e6SBoyan Karatotev 	: "+m" (*dst), [tmp] "=&r" (tmp), [out] "=r" (out)
109*38e580e6SBoyan Karatotev 	:  [src] "r" (src), [dst] "r" (dst));
110*38e580e6SBoyan Karatotev 
111*38e580e6SBoyan Karatotev 	return out;
112*38e580e6SBoyan Karatotev }
113*38e580e6SBoyan Karatotev 
114*38e580e6SBoyan Karatotev /*
115*38e580e6SBoyan Karatotev  * Attempts to acquire the spinlock once without spinning. If unlocked (0),
116*38e580e6SBoyan Karatotev  * attempts to store 1 to acquire it.
117*38e580e6SBoyan Karatotev  */
spin_trylock(spinlock_t * lock)118*38e580e6SBoyan Karatotev bool spin_trylock(spinlock_t *lock)
119*38e580e6SBoyan Karatotev {
120*38e580e6SBoyan Karatotev 	volatile uint32_t *dst = &(lock->lock);
121*38e580e6SBoyan Karatotev 
122*38e580e6SBoyan Karatotev 	if (is_feat_lse_supported()) {
123*38e580e6SBoyan Karatotev 		return spin_trylock_atomic(dst);
124*38e580e6SBoyan Karatotev 	} else {
125*38e580e6SBoyan Karatotev 		return spin_trylock_excl(dst);
126*38e580e6SBoyan Karatotev 	}
127*38e580e6SBoyan Karatotev }
128*38e580e6SBoyan Karatotev 
129*38e580e6SBoyan Karatotev #if USE_SPINLOCK_CAS
130*38e580e6SBoyan Karatotev /*
131*38e580e6SBoyan Karatotev  * Acquire bitlock using atomic bit set on byte. If the original read value
132*38e580e6SBoyan Karatotev  * has the bit set, use load exclusive semantics to monitor the address and
133*38e580e6SBoyan Karatotev  * enter WFE.
134*38e580e6SBoyan Karatotev  */
bit_lock(bitlock_t * lock,uint8_t mask)135*38e580e6SBoyan Karatotev void bit_lock(bitlock_t *lock, uint8_t mask)
136*38e580e6SBoyan Karatotev {
137*38e580e6SBoyan Karatotev 	volatile uint8_t *dst = &(lock->lock);
138*38e580e6SBoyan Karatotev 	uint32_t tmp;
139*38e580e6SBoyan Karatotev 
140*38e580e6SBoyan Karatotev 	/* there is no exclusive fallback */
141*38e580e6SBoyan Karatotev 	assert(is_feat_lse_supported());
142*38e580e6SBoyan Karatotev 
143*38e580e6SBoyan Karatotev 	__asm__ volatile (
144*38e580e6SBoyan Karatotev 	"1:	ldsetab	%w[mask], %w[tmp], [%[dst]]\n"
145*38e580e6SBoyan Karatotev 	"	tst	%w[tmp], %w[mask]\n"
146*38e580e6SBoyan Karatotev 	"	b.eq	2f\n"
147*38e580e6SBoyan Karatotev 	"	ldxrb	%w[tmp], [%[dst]]\n"
148*38e580e6SBoyan Karatotev 	"	tst	%w[tmp], %w[mask]\n"
149*38e580e6SBoyan Karatotev 	"	b.eq	1b\n"
150*38e580e6SBoyan Karatotev 	"	wfe\n"
151*38e580e6SBoyan Karatotev 	"	b	1b\n"
152*38e580e6SBoyan Karatotev 	"2:\n"
153*38e580e6SBoyan Karatotev 	: "+m" (*dst), [tmp] "=&r" (tmp)
154*38e580e6SBoyan Karatotev 	: [mask] "r" (mask), [dst] "r" (dst));
155*38e580e6SBoyan Karatotev }
156*38e580e6SBoyan Karatotev 
157*38e580e6SBoyan Karatotev /*
158*38e580e6SBoyan Karatotev  * Use atomic bit clear store-release to unconditionally clear bitlock variable.
159*38e580e6SBoyan Karatotev  * Store operation generates an event to all cores waiting in WFE when address
160*38e580e6SBoyan Karatotev  * is monitored by the global monitor.
161*38e580e6SBoyan Karatotev  */
bit_unlock(bitlock_t * lock,uint8_t mask)162*38e580e6SBoyan Karatotev void bit_unlock(bitlock_t *lock, uint8_t mask)
163*38e580e6SBoyan Karatotev {
164*38e580e6SBoyan Karatotev 	volatile uint8_t *dst = &(lock->lock);
165*38e580e6SBoyan Karatotev 
166*38e580e6SBoyan Karatotev 	/* there is no exclusive fallback */
167*38e580e6SBoyan Karatotev 	assert(is_feat_lse_supported());
168*38e580e6SBoyan Karatotev 
169*38e580e6SBoyan Karatotev 	__asm__ volatile (
170*38e580e6SBoyan Karatotev 	"stclrlb	%w[mask], [%[dst]]"
171*38e580e6SBoyan Karatotev 	: "=m" (dst)
172*38e580e6SBoyan Karatotev 	: [mask] "r" (mask), [dst] "r" (dst));
173*38e580e6SBoyan Karatotev }
174*38e580e6SBoyan Karatotev #endif
175