1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 */ 5 #ifndef _ARCH_LOONGARCH_LOCAL_H 6 #define _ARCH_LOONGARCH_LOCAL_H 7 8 #include <linux/percpu.h> 9 #include <linux/bitops.h> 10 #include <linux/atomic.h> 11 #include <asm/asm.h> 12 #include <asm/cmpxchg.h> 13 14 typedef struct { 15 atomic_long_t a; 16 } local_t; 17 18 #define LOCAL_INIT(i) { ATOMIC_LONG_INIT(i) } 19 20 #define local_read(l) atomic_long_read(&(l)->a) 21 #define local_set(l, i) atomic_long_set(&(l)->a, (i)) 22 23 #define local_add(i, l) atomic_long_add((i), (&(l)->a)) 24 #define local_sub(i, l) atomic_long_sub((i), (&(l)->a)) 25 #define local_inc(l) atomic_long_inc(&(l)->a) 26 #define local_dec(l) atomic_long_dec(&(l)->a) 27 28 /* 29 * Same as above, but return the result value 30 */ 31 #ifdef CONFIG_CPU_HAS_AMO 32 static inline long local_add_return(long i, local_t *l) 33 { 34 unsigned long result; 35 36 __asm__ __volatile__( 37 " " __AMADD " %1, %2, %0 \n" 38 : "+ZB" (l->a.counter), "=&r" (result) 39 : "r" (i) 40 : "memory"); 41 result = result + i; 42 43 return result; 44 } 45 46 static inline long local_sub_return(long i, local_t *l) 47 { 48 unsigned long result; 49 50 __asm__ __volatile__( 51 " " __AMADD "%1, %2, %0 \n" 52 : "+ZB" (l->a.counter), "=&r" (result) 53 : "r" (-i) 54 : "memory"); 55 56 result = result - i; 57 58 return result; 59 } 60 #else 61 static inline long local_add_return(long i, local_t *l) 62 { 63 unsigned long result, temp; 64 65 __asm__ __volatile__( 66 "1:" __LL "%1, %2 # local_add_return \n" 67 __stringify(LONG_ADD) " %0, %1, %3 \n" 68 __SC "%0, %2 \n" 69 " beq %0, $r0, 1b \n" 70 __stringify(LONG_ADD) " %0, %1, %3 \n" 71 : "=&r" (result), "=&r" (temp), "=ZC" (l->a.counter) 72 : "r" (i), "ZC" (l->a.counter) 73 : "memory"); 74 75 return result; 76 } 77 78 static inline long local_sub_return(long i, local_t *l) 79 { 80 unsigned long result, temp; 81 82 __asm__ __volatile__( 83 "1:" __LL "%1, %2 # local_sub_return \n" 84 __stringify(LONG_SUB) " %0, %1, %3 \n" 85 __SC "%0, %2 \n" 86 " beq %0, $r0, 1b \n" 87 __stringify(LONG_SUB) " %0, %1, %3 \n" 88 : "=&r" (result), "=&r" (temp), "=ZC" (l->a.counter) 89 : "r" (i), "ZC" (l->a.counter) 90 : "memory"); 91 92 return result; 93 } 94 #endif 95 96 static inline long local_cmpxchg(local_t *l, long old, long new) 97 { 98 return cmpxchg_local(&l->a.counter, old, new); 99 } 100 101 static inline bool local_try_cmpxchg(local_t *l, long *old, long new) 102 { 103 return try_cmpxchg_local(&l->a.counter, 104 (typeof(l->a.counter) *) old, new); 105 } 106 107 #define local_xchg(l, n) (atomic_long_xchg((&(l)->a), (n))) 108 109 /** 110 * local_add_unless - add unless the number is already a given value 111 * @l: pointer of type local_t 112 * @a: the amount to add to l... 113 * @u: ...unless l is equal to u. 114 * 115 * Atomically adds @a to @l, if @v was not already @u. 116 * Returns true if the addition was done. 117 */ 118 static inline bool 119 local_add_unless(local_t *l, long a, long u) 120 { 121 long c = local_read(l); 122 123 do { 124 if (unlikely(c == u)) 125 return false; 126 } while (!local_try_cmpxchg(l, &c, c + a)); 127 128 return true; 129 } 130 131 #define local_inc_not_zero(l) local_add_unless((l), 1, 0) 132 133 #define local_dec_return(l) local_sub_return(1, (l)) 134 #define local_inc_return(l) local_add_return(1, (l)) 135 136 /* 137 * local_sub_and_test - subtract value from variable and test result 138 * @i: integer value to subtract 139 * @l: pointer of type local_t 140 * 141 * Atomically subtracts @i from @l and returns 142 * true if the result is zero, or false for all 143 * other cases. 144 */ 145 #define local_sub_and_test(i, l) (local_sub_return((i), (l)) == 0) 146 147 /* 148 * local_inc_and_test - increment and test 149 * @l: pointer of type local_t 150 * 151 * Atomically increments @l by 1 152 * and returns true if the result is zero, or false for all 153 * other cases. 154 */ 155 #define local_inc_and_test(l) (local_inc_return(l) == 0) 156 157 /* 158 * local_dec_and_test - decrement by 1 and test 159 * @l: pointer of type local_t 160 * 161 * Atomically decrements @l by 1 and 162 * returns true if the result is 0, or false for all other 163 * cases. 164 */ 165 #define local_dec_and_test(l) (local_sub_return(1, (l)) == 0) 166 167 /* 168 * local_add_negative - add and test if negative 169 * @l: pointer of type local_t 170 * @i: integer value to add 171 * 172 * Atomically adds @i to @l and returns true 173 * if the result is negative, or false when 174 * result is greater than or equal to zero. 175 */ 176 #define local_add_negative(i, l) (local_add_return(i, (l)) < 0) 177 178 /* Use these for per-cpu local_t variables: on some archs they are 179 * much more efficient than these naive implementations. Note they take 180 * a variable, not an address. 181 */ 182 183 #define __local_inc(l) ((l)->a.counter++) 184 #define __local_dec(l) ((l)->a.counter++) 185 #define __local_add(i, l) ((l)->a.counter += (i)) 186 #define __local_sub(i, l) ((l)->a.counter -= (i)) 187 188 #endif /* _ARCH_LOONGARCH_LOCAL_H */ 189