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