1 /* 2 * Copyright (C) 2012 ARM Ltd. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 #ifndef __ASM_SPINLOCK_H 17 #define __ASM_SPINLOCK_H 18 19 #include <asm/lse.h> 20 #include <asm/spinlock_types.h> 21 #include <asm/processor.h> 22 23 /* 24 * Spinlock implementation. 25 * 26 * The memory barriers are implicit with the load-acquire and store-release 27 * instructions. 28 */ 29 30 #define arch_spin_unlock_wait(lock) \ 31 do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0) 32 33 #define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock) 34 35 static inline void arch_spin_lock(arch_spinlock_t *lock) 36 { 37 unsigned int tmp; 38 arch_spinlock_t lockval, newval; 39 40 asm volatile( 41 /* Atomically increment the next ticket. */ 42 ARM64_LSE_ATOMIC_INSN( 43 /* LL/SC */ 44 " prfm pstl1strm, %3\n" 45 "1: ldaxr %w0, %3\n" 46 " add %w1, %w0, %w5\n" 47 " stxr %w2, %w1, %3\n" 48 " cbnz %w2, 1b\n", 49 /* LSE atomics */ 50 " mov %w2, %w5\n" 51 " ldadda %w2, %w0, %3\n" 52 " nop\n" 53 " nop\n" 54 " nop\n" 55 ) 56 57 /* Did we get the lock? */ 58 " eor %w1, %w0, %w0, ror #16\n" 59 " cbz %w1, 3f\n" 60 /* 61 * No: spin on the owner. Send a local event to avoid missing an 62 * unlock before the exclusive load. 63 */ 64 " sevl\n" 65 "2: wfe\n" 66 " ldaxrh %w2, %4\n" 67 " eor %w1, %w2, %w0, lsr #16\n" 68 " cbnz %w1, 2b\n" 69 /* We got the lock. Critical section starts here. */ 70 "3:" 71 : "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock) 72 : "Q" (lock->owner), "I" (1 << TICKET_SHIFT) 73 : "memory"); 74 } 75 76 static inline int arch_spin_trylock(arch_spinlock_t *lock) 77 { 78 unsigned int tmp; 79 arch_spinlock_t lockval; 80 81 asm volatile(ARM64_LSE_ATOMIC_INSN( 82 /* LL/SC */ 83 " prfm pstl1strm, %2\n" 84 "1: ldaxr %w0, %2\n" 85 " eor %w1, %w0, %w0, ror #16\n" 86 " cbnz %w1, 2f\n" 87 " add %w0, %w0, %3\n" 88 " stxr %w1, %w0, %2\n" 89 " cbnz %w1, 1b\n" 90 "2:", 91 /* LSE atomics */ 92 " ldr %w0, %2\n" 93 " eor %w1, %w0, %w0, ror #16\n" 94 " cbnz %w1, 1f\n" 95 " add %w1, %w0, %3\n" 96 " casa %w0, %w1, %2\n" 97 " and %w1, %w1, #0xffff\n" 98 " eor %w1, %w1, %w0, lsr #16\n" 99 "1:") 100 : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock) 101 : "I" (1 << TICKET_SHIFT) 102 : "memory"); 103 104 return !tmp; 105 } 106 107 static inline void arch_spin_unlock(arch_spinlock_t *lock) 108 { 109 unsigned long tmp; 110 111 asm volatile(ARM64_LSE_ATOMIC_INSN( 112 /* LL/SC */ 113 " ldrh %w1, %0\n" 114 " add %w1, %w1, #1\n" 115 " stlrh %w1, %0", 116 /* LSE atomics */ 117 " mov %w1, #1\n" 118 " nop\n" 119 " staddlh %w1, %0") 120 : "=Q" (lock->owner), "=&r" (tmp) 121 : 122 : "memory"); 123 } 124 125 static inline int arch_spin_value_unlocked(arch_spinlock_t lock) 126 { 127 return lock.owner == lock.next; 128 } 129 130 static inline int arch_spin_is_locked(arch_spinlock_t *lock) 131 { 132 return !arch_spin_value_unlocked(READ_ONCE(*lock)); 133 } 134 135 static inline int arch_spin_is_contended(arch_spinlock_t *lock) 136 { 137 arch_spinlock_t lockval = READ_ONCE(*lock); 138 return (lockval.next - lockval.owner) > 1; 139 } 140 #define arch_spin_is_contended arch_spin_is_contended 141 142 /* 143 * Write lock implementation. 144 * 145 * Write locks set bit 31. Unlocking, is done by writing 0 since the lock is 146 * exclusively held. 147 * 148 * The memory barriers are implicit with the load-acquire and store-release 149 * instructions. 150 */ 151 152 static inline void arch_write_lock(arch_rwlock_t *rw) 153 { 154 unsigned int tmp; 155 156 asm volatile(ARM64_LSE_ATOMIC_INSN( 157 /* LL/SC */ 158 " sevl\n" 159 "1: wfe\n" 160 "2: ldaxr %w0, %1\n" 161 " cbnz %w0, 1b\n" 162 " stxr %w0, %w2, %1\n" 163 " cbnz %w0, 2b\n" 164 " nop", 165 /* LSE atomics */ 166 "1: mov %w0, wzr\n" 167 "2: casa %w0, %w2, %1\n" 168 " cbz %w0, 3f\n" 169 " ldxr %w0, %1\n" 170 " cbz %w0, 2b\n" 171 " wfe\n" 172 " b 1b\n" 173 "3:") 174 : "=&r" (tmp), "+Q" (rw->lock) 175 : "r" (0x80000000) 176 : "memory"); 177 } 178 179 static inline int arch_write_trylock(arch_rwlock_t *rw) 180 { 181 unsigned int tmp; 182 183 asm volatile(ARM64_LSE_ATOMIC_INSN( 184 /* LL/SC */ 185 "1: ldaxr %w0, %1\n" 186 " cbnz %w0, 2f\n" 187 " stxr %w0, %w2, %1\n" 188 " cbnz %w0, 1b\n" 189 "2:", 190 /* LSE atomics */ 191 " mov %w0, wzr\n" 192 " casa %w0, %w2, %1\n" 193 " nop\n" 194 " nop") 195 : "=&r" (tmp), "+Q" (rw->lock) 196 : "r" (0x80000000) 197 : "memory"); 198 199 return !tmp; 200 } 201 202 static inline void arch_write_unlock(arch_rwlock_t *rw) 203 { 204 asm volatile(ARM64_LSE_ATOMIC_INSN( 205 " stlr wzr, %0", 206 " swpl wzr, wzr, %0") 207 : "=Q" (rw->lock) :: "memory"); 208 } 209 210 /* write_can_lock - would write_trylock() succeed? */ 211 #define arch_write_can_lock(x) ((x)->lock == 0) 212 213 /* 214 * Read lock implementation. 215 * 216 * It exclusively loads the lock value, increments it and stores the new value 217 * back if positive and the CPU still exclusively owns the location. If the 218 * value is negative, the lock is already held. 219 * 220 * During unlocking there may be multiple active read locks but no write lock. 221 * 222 * The memory barriers are implicit with the load-acquire and store-release 223 * instructions. 224 * 225 * Note that in UNDEFINED cases, such as unlocking a lock twice, the LL/SC 226 * and LSE implementations may exhibit different behaviour (although this 227 * will have no effect on lockdep). 228 */ 229 static inline void arch_read_lock(arch_rwlock_t *rw) 230 { 231 unsigned int tmp, tmp2; 232 233 asm volatile( 234 " sevl\n" 235 ARM64_LSE_ATOMIC_INSN( 236 /* LL/SC */ 237 "1: wfe\n" 238 "2: ldaxr %w0, %2\n" 239 " add %w0, %w0, #1\n" 240 " tbnz %w0, #31, 1b\n" 241 " stxr %w1, %w0, %2\n" 242 " nop\n" 243 " cbnz %w1, 2b", 244 /* LSE atomics */ 245 "1: wfe\n" 246 "2: ldxr %w0, %2\n" 247 " adds %w1, %w0, #1\n" 248 " tbnz %w1, #31, 1b\n" 249 " casa %w0, %w1, %2\n" 250 " sbc %w0, %w1, %w0\n" 251 " cbnz %w0, 2b") 252 : "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock) 253 : 254 : "cc", "memory"); 255 } 256 257 static inline void arch_read_unlock(arch_rwlock_t *rw) 258 { 259 unsigned int tmp, tmp2; 260 261 asm volatile(ARM64_LSE_ATOMIC_INSN( 262 /* LL/SC */ 263 "1: ldxr %w0, %2\n" 264 " sub %w0, %w0, #1\n" 265 " stlxr %w1, %w0, %2\n" 266 " cbnz %w1, 1b", 267 /* LSE atomics */ 268 " movn %w0, #0\n" 269 " nop\n" 270 " nop\n" 271 " staddl %w0, %2") 272 : "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock) 273 : 274 : "memory"); 275 } 276 277 static inline int arch_read_trylock(arch_rwlock_t *rw) 278 { 279 unsigned int tmp, tmp2; 280 281 asm volatile(ARM64_LSE_ATOMIC_INSN( 282 /* LL/SC */ 283 " mov %w1, #1\n" 284 "1: ldaxr %w0, %2\n" 285 " add %w0, %w0, #1\n" 286 " tbnz %w0, #31, 2f\n" 287 " stxr %w1, %w0, %2\n" 288 " cbnz %w1, 1b\n" 289 "2:", 290 /* LSE atomics */ 291 " ldr %w0, %2\n" 292 " adds %w1, %w0, #1\n" 293 " tbnz %w1, #31, 1f\n" 294 " casa %w0, %w1, %2\n" 295 " sbc %w1, %w1, %w0\n" 296 " nop\n" 297 "1:") 298 : "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock) 299 : 300 : "cc", "memory"); 301 302 return !tmp2; 303 } 304 305 /* read_can_lock - would read_trylock() succeed? */ 306 #define arch_read_can_lock(x) ((x)->lock < 0x80000000) 307 308 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock) 309 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock) 310 311 #define arch_spin_relax(lock) cpu_relax() 312 #define arch_read_relax(lock) cpu_relax() 313 #define arch_write_relax(lock) cpu_relax() 314 315 #endif /* __ASM_SPINLOCK_H */ 316