1 /* 2 * Copyright (C) 2015 Regents of the University of California 3 * Copyright (C) 2017 SiFive 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation, version 2. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #ifndef _ASM_RISCV_SPINLOCK_H 16 #define _ASM_RISCV_SPINLOCK_H 17 18 #include <linux/kernel.h> 19 #include <asm/current.h> 20 21 /* 22 * Simple spin lock operations. These provide no fairness guarantees. 23 */ 24 25 /* FIXME: Replace this with a ticket lock, like MIPS. */ 26 27 #define arch_spin_is_locked(x) (READ_ONCE((x)->lock) != 0) 28 29 static inline void arch_spin_unlock(arch_spinlock_t *lock) 30 { 31 __asm__ __volatile__ ( 32 "amoswap.w.rl x0, x0, %0" 33 : "=A" (lock->lock) 34 :: "memory"); 35 } 36 37 static inline int arch_spin_trylock(arch_spinlock_t *lock) 38 { 39 int tmp = 1, busy; 40 41 __asm__ __volatile__ ( 42 "amoswap.w.aq %0, %2, %1" 43 : "=r" (busy), "+A" (lock->lock) 44 : "r" (tmp) 45 : "memory"); 46 47 return !busy; 48 } 49 50 static inline void arch_spin_lock(arch_spinlock_t *lock) 51 { 52 while (1) { 53 if (arch_spin_is_locked(lock)) 54 continue; 55 56 if (arch_spin_trylock(lock)) 57 break; 58 } 59 } 60 61 /***********************************************************/ 62 63 static inline void arch_read_lock(arch_rwlock_t *lock) 64 { 65 int tmp; 66 67 __asm__ __volatile__( 68 "1: lr.w %1, %0\n" 69 " bltz %1, 1b\n" 70 " addi %1, %1, 1\n" 71 " sc.w.aq %1, %1, %0\n" 72 " bnez %1, 1b\n" 73 : "+A" (lock->lock), "=&r" (tmp) 74 :: "memory"); 75 } 76 77 static inline void arch_write_lock(arch_rwlock_t *lock) 78 { 79 int tmp; 80 81 __asm__ __volatile__( 82 "1: lr.w %1, %0\n" 83 " bnez %1, 1b\n" 84 " li %1, -1\n" 85 " sc.w.aq %1, %1, %0\n" 86 " bnez %1, 1b\n" 87 : "+A" (lock->lock), "=&r" (tmp) 88 :: "memory"); 89 } 90 91 static inline int arch_read_trylock(arch_rwlock_t *lock) 92 { 93 int busy; 94 95 __asm__ __volatile__( 96 "1: lr.w %1, %0\n" 97 " bltz %1, 1f\n" 98 " addi %1, %1, 1\n" 99 " sc.w.aq %1, %1, %0\n" 100 " bnez %1, 1b\n" 101 "1:\n" 102 : "+A" (lock->lock), "=&r" (busy) 103 :: "memory"); 104 105 return !busy; 106 } 107 108 static inline int arch_write_trylock(arch_rwlock_t *lock) 109 { 110 int busy; 111 112 __asm__ __volatile__( 113 "1: lr.w %1, %0\n" 114 " bnez %1, 1f\n" 115 " li %1, -1\n" 116 " sc.w.aq %1, %1, %0\n" 117 " bnez %1, 1b\n" 118 "1:\n" 119 : "+A" (lock->lock), "=&r" (busy) 120 :: "memory"); 121 122 return !busy; 123 } 124 125 static inline void arch_read_unlock(arch_rwlock_t *lock) 126 { 127 __asm__ __volatile__( 128 "amoadd.w.rl x0, %1, %0" 129 : "+A" (lock->lock) 130 : "r" (-1) 131 : "memory"); 132 } 133 134 static inline void arch_write_unlock(arch_rwlock_t *lock) 135 { 136 __asm__ __volatile__ ( 137 "amoswap.w.rl x0, x0, %0" 138 : "=A" (lock->lock) 139 :: "memory"); 140 } 141 142 #endif /* _ASM_RISCV_SPINLOCK_H */ 143