1 /*
2 * Copyright (C) 2015-2017 Alibaba Group Holding Limited
3 */
4
5 /*
6 * DESCRIPTION
7 * This library is used to provide the atomic operators for CPU
8 * which do not support native atomic operations.
9 *
10 * The design principle is disable the interrupt when execute the
11 * atomic operations and enable the interrupt after finish the
12 * operation.
13 */
14
15 #include "k_api.h"
16 #include "k_atomic.h"
17
18 /**
19 * This routine atomically adds <*target> and <value>, placing the result in
20 * <*target>. The operation is done using unsigned integer arithmetic.
21 *
22 * This routine can be used from both task and interrupt context.
23 *
24 * @param target memory location to add to
25 * @param value the value to add
26 *
27 * @return The previous value from <target>
28 */
29
rhino_atomic_add(atomic_t * target,atomic_val_t value)30 atomic_val_t rhino_atomic_add(atomic_t *target, atomic_val_t value)
31 {
32 CPSR_ALLOC();
33 atomic_val_t old_value;
34
35 RHINO_CPU_INTRPT_DISABLE();
36
37 old_value = *target;
38 *target += value;
39
40 RHINO_CPU_INTRPT_ENABLE();
41
42 return old_value;
43 }
44
45 /**
46 * This routine atomically subtracts <value> from <*target>, result is placed
47 * in <*target>. The operation is done using unsigned integer arithmetic.
48 *
49 * This routine can be used from both task and interrupt context.
50 *
51 * @param target the memory location to subtract from
52 * @param value the value to subtract
53 *
54 * @return The previous value from <target>
55 */
56
rhino_atomic_sub(atomic_t * target,atomic_val_t value)57 atomic_val_t rhino_atomic_sub(atomic_t *target, atomic_val_t value)
58 {
59 CPSR_ALLOC();
60 atomic_val_t old_value;
61
62 RHINO_CPU_INTRPT_DISABLE();
63
64 old_value = *target;
65 *target -= value;
66
67 RHINO_CPU_INTRPT_ENABLE();
68
69 return old_value;
70 }
71
72 /**
73 * This routine atomically increments the value in <*target>. The operation is
74 * done using unsigned integer arithmetic.
75 *
76 * This routine can be used from both task and interrupt context.
77 *
78 * @return The value from <target> before the increment
79 */
80
rhino_atomic_inc(atomic_t * target)81 atomic_val_t rhino_atomic_inc(atomic_t *target)
82 {
83 CPSR_ALLOC();
84 atomic_val_t old_value;
85
86 RHINO_CPU_INTRPT_DISABLE();
87
88 old_value = *target;
89 (*target)++;
90
91 RHINO_CPU_INTRPT_ENABLE();
92
93 return old_value;
94 }
95
96 /**
97 * This routine atomically decrement the value in <*target>. The operation is
98 * done using unsigned integer arithmetic.
99 *
100 * This routine can be used from both task and interrupt context.
101 *
102 * @return The value from <target> before the increment
103 */
104
rhino_atomic_dec(atomic_t * target)105 atomic_val_t rhino_atomic_dec(atomic_t *target)
106 {
107 CPSR_ALLOC();
108 atomic_val_t old_value;
109
110 RHINO_CPU_INTRPT_DISABLE();
111
112 old_value = *target;
113 (*target)--;
114
115 RHINO_CPU_INTRPT_ENABLE();
116
117 return old_value;
118 }
119
120 /**
121 * This routine atomically sets <*target> to <value> and returns the old value
122 * that was in <*target>. Normally all CPU architectures can atomically write
123 * to a variable of size atomic_t without the help of this routine.
124 * This routine is intended for software that needs to atomically fetch and
125 * replace the value of a memory location.
126 *
127 * This routine can be used from both task and interrupt context.
128 *
129 * @param target the memory location to write to
130 * @param value the value to write
131 *
132 * @return The previous value from <target>
133 */
134
rhino_atomic_set(atomic_t * target,atomic_val_t value)135 atomic_val_t rhino_atomic_set(atomic_t *target, atomic_val_t value)
136 {
137 CPSR_ALLOC();
138 atomic_val_t old_value;
139
140 RHINO_CPU_INTRPT_DISABLE();
141
142 old_value = *target;
143 *target = value;
144
145 RHINO_CPU_INTRPT_ENABLE();
146
147 return old_value;
148 }
149
150 /**
151 * This routine atomically read a value from <target>.
152 * This routine can be used from both task and interrupt context.
153 *
154 * @return The value read from <target>
155 */
156
rhino_atomic_get(const atomic_t * target)157 atomic_val_t rhino_atomic_get(const atomic_t *target)
158 {
159 return *target;
160 }
161
162 /**
163 * This routine atomically performs a bitwise OR operation of <*target>
164 * and <value>, placing the result in <*target>.
165 *
166 * This routine can be used from both task and interrupt context.
167 *
168 * @param target the memory location to be modified
169 * @param value the value to OR
170 *
171 * @return The previous value from <target>
172 */
173
rhino_atomic_or(atomic_t * target,atomic_val_t value)174 atomic_val_t rhino_atomic_or(atomic_t *target, atomic_val_t value)
175 {
176 CPSR_ALLOC();
177 atomic_val_t old_value;
178
179 RHINO_CPU_INTRPT_DISABLE();
180
181 old_value = *target;
182 *target |= value;
183
184 RHINO_CPU_INTRPT_ENABLE();
185
186 return old_value;
187 }
188
189 /**
190 * This routine atomically performs a bitwise XOR operation of <*target> and
191 * <value>, placing the result in <*target>.
192 *
193 * This routine can be used from both task and interrupt context.
194 *
195 * @param target the memory location to be modified
196 * @param value the value to XOR
197 *
198 * @return The previous value from <target>
199 */
200
rhino_atomic_xor(atomic_t * target,atomic_val_t value)201 atomic_val_t rhino_atomic_xor(atomic_t *target, atomic_val_t value)
202 {
203 CPSR_ALLOC();
204 atomic_val_t old_value;
205
206 RHINO_CPU_INTRPT_DISABLE();
207
208 old_value = *target;
209 *target ^= value;
210
211 RHINO_CPU_INTRPT_ENABLE();
212
213 return old_value;
214 }
215
216 /**
217 * This routine atomically performs a bitwise AND operation of <*target> and
218 * <value>, placing the result in <*target>.
219 *
220 * This routine can be used from both task and interrupt context.
221 *
222 * @param target the memory location to be modified
223 * @param value the value to AND
224 *
225 * @return The previous value from <target>
226 */
227
rhino_atomic_and(atomic_t * target,atomic_val_t value)228 atomic_val_t rhino_atomic_and(atomic_t *target, atomic_val_t value)
229 {
230 CPSR_ALLOC();
231 atomic_val_t old_value;
232
233 RHINO_CPU_INTRPT_DISABLE();
234
235 old_value = *target;
236 *target &= value;
237
238 RHINO_CPU_INTRPT_ENABLE();
239
240 return old_value;
241 }
242
243 /**
244 * This routine atomically performs a bitwise NAND operation of <*target> and
245 * <value>, placing the result in <*target>.
246 *
247 * This routine can be used from both task and interrupt context.
248 *
249 * @param target the memory location to be modified
250 * @param value the value to NAND
251 *
252 * @return The previous value from <target>
253 */
254
rhino_atomic_nand(atomic_t * target,atomic_val_t value)255 atomic_val_t rhino_atomic_nand(atomic_t *target, atomic_val_t value)
256 {
257 CPSR_ALLOC();
258 atomic_val_t old_value;
259
260 RHINO_CPU_INTRPT_DISABLE();
261
262 old_value = *target;
263 *target = ~(*target & value);
264
265 RHINO_CPU_INTRPT_ENABLE();
266
267 return old_value;
268 }
269
270 /**
271 * This routine provides the atomic clear operator. The value of 0 is atomically
272 * written at <target> and the previous value at <target> is returned.
273 *
274 * This routine can be used from both task and interrupt context.
275 *
276 * @param target the memory location to write
277 *
278 * @return The previous value from <target>
279 */
280
rhino_atomic_clear(atomic_t * target)281 atomic_val_t rhino_atomic_clear(atomic_t *target)
282 {
283 CPSR_ALLOC();
284 atomic_val_t old_value;
285
286 RHINO_CPU_INTRPT_DISABLE();
287
288 old_value = *target;
289 *target = 0;
290
291 RHINO_CPU_INTRPT_ENABLE();
292
293 return old_value;
294 }
295
296 /**
297 * This routine performs an atomic compare-and-swap, it test that whether
298 * <*target> equal to <oldValue>, and if TRUE, setting the value of <*target>
299 * to <newValue> and return 1.
300 *
301 * If the original value at <target> does not equal <oldValue>, then the target
302 * will not be updated and return 0.
303 *
304 * @param target address to be tested
305 * @param old_value value to compare against
306 * @param new_value value to compare against
307 * @return Returns 1 if <new_value> is written, 0 otherwise.
308 */
309
rhino_atomic_cas(atomic_t * target,atomic_val_t old_value,atomic_val_t new_value)310 int rhino_atomic_cas(atomic_t *target, atomic_val_t old_value,
311 atomic_val_t new_value)
312 {
313 CPSR_ALLOC();
314 int ret = 0;
315
316 RHINO_CPU_INTRPT_DISABLE();
317
318 if (*target == old_value){
319 *target = new_value;
320 ret = 1;
321 }
322
323 RHINO_CPU_INTRPT_ENABLE();
324
325 return ret;
326 }
327