xref: /aosp_15_r20/system/chre/platform/arm/include/chre/target_platform/atomic_base.h (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef CHRE_PLATFORM_ARM_ATOMIC_BASE_H_
18 #define CHRE_PLATFORM_ARM_ATOMIC_BASE_H_
19 
20 namespace chre {
21 
22 namespace atomic {
23 
24 /**
25  * Atomically swap the value of a byte with a new value.
26  *
27  * @param byte Pointer to a byte whose value is to be swapped out.
28  * @param newValue The value replacing the old value in the byte.
29  * @return The bytes pre-swap value.
30  */
swapByte(volatile uint8_t * byte,uint32_t newValue)31 inline bool swapByte(volatile uint8_t *byte, uint32_t newValue) {
32   uint32_t prevValue;
33   uint32_t storeFailed;
34 
35   do {
36     asm volatile(
37         "ldrexb %0,     [%3] \n"
38         "strexb %1, %2, [%3] \n"
39         : "=r"(prevValue), "=r"(storeFailed), "=r"(newValue), "=r"(byte)
40         : "2"(newValue), "3"(byte)
41         : "memory");
42   } while (storeFailed);
43 
44   return prevValue;
45 }
46 
47 /**
48  * Atomically swap the value of a 32-bit word with a new value.
49  *
50  * @param word Pointer to a 32-bit word whose value is to be swapped out.
51  * @param newValue The value replacing the old value in the 32-bit word.
52  * @return The 32-bit word's pre-swap value.
53  */
swapWord(volatile uint32_t * word,uint32_t newValue)54 inline uint32_t swapWord(volatile uint32_t *word, uint32_t newValue) {
55   uint32_t prevValue;
56   uint32_t storeFailed;
57 
58   do {
59     asm volatile(
60         "ldrex %0,     [%3] \n"
61         "strex %1, %2, [%3] \n"
62         : "=r"(prevValue), "=r"(storeFailed), "=r"(newValue), "=r"(word)
63         : "2"(newValue), "3"(word)
64         : "memory");
65   } while (storeFailed);
66 
67   return prevValue;
68 }
69 
70 /**
71  * Atomically add a value to a 32-bit word.
72  *
73  * @param word Pointer to a 32-bit word whose value is to be incremented.
74  * @param addend Value that is to be added to the 32-bit word.
75  * @return The 32-bit word's value before the addition was performed.
76  */
addToWord(volatile uint32_t * word,uint32_t addend)77 inline uint32_t addToWord(volatile uint32_t *word, uint32_t addend) {
78   uint32_t prevValue;
79   uint32_t storeFailed;
80   uint32_t tmp;
81 
82   do {
83     asm volatile(
84         "ldrex  %0,     [%4] \n"
85         "add    %2, %0, %3   \n"
86         "strex  %1, %2, [%4] \n"
87         : "=r"(prevValue), "=r"(storeFailed), "=r"(tmp), "=r"(addend),
88           "=r"(word)
89         : "3"(addend), "4"(word)
90         : "memory");
91   } while (storeFailed);
92 
93   return prevValue;
94 }
95 
96 /**
97  * Atomically subtract a value from a 32-bit word.
98  *
99  * @param word Pointer to a 32-bit word which is to be decremented.
100  * @param arg Value to subtract from the 32-bit word.
101  * @return Value of the 32-bit word before the subtraction was performed.
102  */
subFromWord(volatile uint32_t * word,uint32_t arg)103 inline uint32_t subFromWord(volatile uint32_t *word, uint32_t arg) {
104   uint32_t prevValue;
105   uint32_t storeFailed;
106   uint32_t tmp;
107 
108   do {
109     asm volatile(
110         "ldrex  %0,     [%4] \n"
111         "sub    %2, %0, %3   \n"
112         "strex  %1, %2, [%4] \n"
113         : "=r"(prevValue), "=r"(storeFailed), "=r"(tmp), "=r"(arg), "=r"(word)
114         : "3"(arg), "4"(word)
115         : "memory");
116   } while (storeFailed);
117 
118   return prevValue;
119 }
120 
121 }  // namespace atomic
122 
123 /**
124  * Base class implementation for the Atomic Bool and Uint32 types.
125  */
126 template <typename T>
127 class AtomicBase {
128  public:
129   /**
130    * Generic atomic load of a value implemented via a compiler level memory
131    * barrier.
132    *
133    * @return The current value of the data stored.
134    */
get()135   inline bool get() const {
136     barrier();
137     return mValue;
138   }
139 
140   /**
141    * Generic atomic store of a value implemented via a compiler level memory
142    * barrier.
143    *
144    * @param value The new value that the data stored is to change to.
145    */
set(T value)146   inline void set(T value) {
147     mValue = value;
148     barrier();
149   }
150 
151  protected:
152   volatile T mValue;
153 
154   /**
155    * Forces the compiler to not optimize/re-order memory accesses around the
156    * barrier.
157    */
barrier()158   inline void barrier() const {
159     asm volatile("" ::: "memory");
160   }
161 };
162 
163 /**
164  * Base class implementation for the Atomic Bool type.
165  */
166 class AtomicBoolBase : public AtomicBase<bool> {
167  public:
168   /**
169    * Atomically swap the stored boolean with a new value.
170    *
171    * @param desired New value to be assigned to the stored boolean.
172    * @return Previous value of the stored boolean.
173    */
swap(bool desired)174   bool swap(bool desired) {
175     return atomic::swapByte(reinterpret_cast<volatile uint8_t *>(&mValue),
176                             desired);
177   }
178 };
179 
180 /**
181  * Base class implementation for the Atomic Uint32 type.
182  */
183 class AtomicUint32Base : public AtomicBase<uint32_t> {
184  public:
185   /**
186    * Atomically swap the stored 32-bit word with a new value.
187    *
188    * @param desired New value to be assigned to the stored 32-bit word.
189    * @return Previous value of the stored 32-bit word.
190    */
swap(uint32_t desired)191   uint32_t swap(uint32_t desired) {
192     return atomic::swapWord(&mValue, desired);
193   }
194 
195   /**
196    * Atomically add a new value to the stored 32-bit word.
197    *
198    * @param arg Value to be added to the stored word.
199    * @return Pre-addition value of the stored word.
200    */
add(uint32_t arg)201   uint32_t add(uint32_t arg) {
202     return atomic::addToWord(&mValue, arg);
203   }
204 
205   /**
206    * Atomically subtract a value from the stored 32-bit word.
207    *
208    * @param arg Value to be subtracted from the stored word.
209    * @return Pre-subtraction value of the stored word.
210    */
sub(uint32_t arg)211   uint32_t sub(uint32_t arg) {
212     return atomic::subFromWord(&mValue, arg);
213   }
214 };
215 
216 }  // namespace chre
217 
218 #endif  // CHRE_PLATFORM_ARM_ATOMIC_BASE_H_
219