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