1 /***************************************************************************//**
2  * @file
3  * @brief RAM and peripheral bit-field set, clear, read and write API.
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2023 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #ifndef SL_HAL_BUS_H
32 #define SL_HAL_BUS_H
33 
34 #include "sl_assert.h"
35 #include "sl_core.h"
36 #include "em_device.h"
37 #include "sl_code_classification.h"
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 /***************************************************************************//**
44  * @addtogroup bus BUS - Bitfield Read/Write
45  * @brief BUS register and RAM bit-field read/write API
46  * @details
47  *  API to perform field set/clear/write/read access to RAM and peripheral's registers.
48  * @{
49  ******************************************************************************/
50 
51 /***************************************************************************//**
52  * @brief
53  *   Perform a single-bit write operation on a 32-bit word in RAM.
54  *
55  * @param[in] addr An address of a 32-bit word in RAM.
56  *
57  * @param[in] bit A bit position to write, 0-31.
58  *
59  * @param[in] val A value to set bit to, 0 or 1.
60  ******************************************************************************/
sl_hal_bus_ram_write_bit(volatile uint32_t * addr,uint32_t bit,uint32_t val)61 __STATIC_INLINE void sl_hal_bus_ram_write_bit(volatile uint32_t *addr,
62                                               uint32_t bit,
63                                               uint32_t val)
64 {
65   uint32_t tmp = *addr;
66 
67   /* Make sure val is not more than 1 because only one bit needs to be set. */
68   *addr = (tmp & ~(1UL << bit)) | ((val & 1UL) << bit);
69 }
70 
71 /***************************************************************************//**
72  * @brief
73  *   Perform a single-bit read operation on a 32-bit word in RAM.
74  *
75  * @param[in] addr RAM address.
76  *
77  * @param[in] bit A bit position to read, 0-31.
78  *
79  * @return
80  *     The requested bit shifted to bit position 0 in the return value.
81  ******************************************************************************/
sl_hal_bus_ram_read_bit(volatile const uint32_t * addr,uint32_t bit)82 __STATIC_INLINE unsigned int sl_hal_bus_ram_read_bit(volatile const uint32_t *addr,
83                                                      uint32_t bit)
84 {
85   return ((*addr) >> bit) & 1UL;
86 }
87 
88 /***************************************************************************//**
89  * @brief
90  *   Perform a single-bit atomic write operation on a peripheral register.
91  *
92  * @details
93  *   This function uses built-in hardware 4K-aliased addressing that allows to
94  *   perform an atomic read-modify-write operation on a single register bit.
95  *   See the reference manual for more details about alias addressing.
96  *
97  * @param[in] addr A peripheral register address.
98  *
99  * @param[in] bit A bit position to write, 0-31.
100  *
101  * @param[in] val A value to set bit to, 0 or 1.
102  ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_COMMON,SL_CODE_CLASS_TIME_CRITICAL)103 SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_COMMON, SL_CODE_CLASS_TIME_CRITICAL)
104 __STATIC_INLINE void sl_hal_bus_reg_write_bit(volatile uint32_t *addr,
105                                               uint32_t bit,
106                                               uint32_t val)
107 {
108   EFM_ASSERT(bit < 32U);
109 
110 #if defined(PER_REG_BLOCK_SET_OFFSET) && defined(PER_REG_BLOCK_CLR_OFFSET)
111   uint32_t aliasAddr;
112   if (val != 0U) {
113     aliasAddr = (uint32_t)addr + PER_REG_BLOCK_SET_OFFSET;
114   } else {
115     aliasAddr = (uint32_t)addr + PER_REG_BLOCK_CLR_OFFSET;
116   }
117   *(volatile uint32_t *)aliasAddr = 1UL << bit;
118 #else
119   uint32_t tmp = *addr;
120 
121   // Make sure val is not more than 1 because only one bit needs to be set.
122   *addr = (tmp & ~(1 << bit)) | ((val & 1) << bit);
123 #endif
124 }
125 
126 /***************************************************************************//**
127  * @brief
128  *   Perform a single-bit atomic read operation on a peripheral register.
129  *
130  * @param[in] addr A peripheral register address.
131  *
132  * @param[in] bit A bit position to read, 0-31.
133  *
134  * @return
135  *     The requested bit shifted to bit position 0 in the return value.
136  ******************************************************************************/
sl_hal_bus_reg_read_bit(volatile const uint32_t * addr,uint32_t bit)137 __STATIC_INLINE unsigned int sl_hal_bus_reg_read_bit(volatile const uint32_t *addr,
138                                                      uint32_t bit)
139 {
140   return ((*addr) >> bit) & 1UL;
141 }
142 
143 /***************************************************************************//**
144  * @brief
145  *   Perform an atomic masked set operation on a peripheral register address.
146  *
147  * @details
148  *   A peripheral register masked set provides a set operation of a bit-mask
149  *   in a peripheral register. All 1s in the mask are set to 1 in the register.
150  *   All 0s in the mask are not changed in the register.
151  *   RAMs and special peripherals are not supported.
152  *
153  * @note
154  *   This function uses built-in hardware 4K-aliased addressing that allows to
155  *   perform an atomic read-modify-write operation.
156  *   See the reference manual for more details about alias addressing.
157  *
158  * @param[in] addr A peripheral register address.
159  *
160  * @param[in] mask A mask to set.
161  ******************************************************************************/
sl_hal_bus_reg_set_mask(volatile uint32_t * addr,uint32_t mask)162 __STATIC_INLINE void sl_hal_bus_reg_set_mask(volatile uint32_t *addr,
163                                              uint32_t mask)
164 {
165 #if defined(PER_REG_BLOCK_SET_OFFSET)
166   uint32_t aliasAddr = (uint32_t)addr + PER_REG_BLOCK_SET_OFFSET;
167   *(volatile uint32_t *)aliasAddr = mask;
168 #else
169   CORE_DECLARE_IRQ_STATE;
170 
171   CORE_ENTER_CRITICAL();
172   *addr |= mask;
173   CORE_EXIT_CRITICAL();
174 #endif
175 }
176 
177 /***************************************************************************//**
178  * @brief
179  *   Perform an atomic masked clear operation on the peripheral register address.
180  *
181  * @details
182  *   A peripheral register masked clear provides a clear operation of a bit-mask
183  *   in a peripheral register. All 1s in the mask are set to 0 in the register.
184  *   All 0s in the mask are not changed in the register.
185  *   RAMs and special peripherals are not supported.
186  *
187  * @note
188  *   This function uses built-in hardware 4K-aliased addressing that allows to
189  *   perform an atomic read-modify-write operation.
190  *   See the reference manual for more details about alias addressing.
191  *
192  * @param[in] addr A peripheral register address.
193  *
194  * @param[in] mask A mask to clear.
195  ******************************************************************************/
sl_hal_bus_reg_clear_mask(volatile uint32_t * addr,uint32_t mask)196 __STATIC_INLINE void sl_hal_bus_reg_clear_mask(volatile uint32_t *addr,
197                                                uint32_t mask)
198 {
199 #if defined(PER_REG_BLOCK_CLR_OFFSET)
200   uint32_t aliasAddr = (uint32_t)addr + PER_REG_BLOCK_CLR_OFFSET;
201   *(volatile uint32_t *)aliasAddr = mask;
202 #else
203   CORE_DECLARE_IRQ_STATE;
204 
205   CORE_ENTER_CRITICAL();
206   *addr &= ~mask;
207   CORE_EXIT_CRITICAL();
208 #endif
209 }
210 
211 /***************************************************************************//**
212  * @brief
213  *   Perform peripheral register masked write.
214  *
215  * @details
216  *   This function first reads the peripheral register and updates only bits
217  *   that are set in the mask with content of val. Typically, the mask is a
218  *   bit-field in the register and the value val is within the mask.
219  *
220  * @note
221  *   The read-modify-write operation is executed in a critical section to
222  *   guarantee atomicity. Note that atomicity can only be guaranteed if register
223  *   is modified only by the core, and not by other peripherals (like DMA).
224  *
225  * @param[in] addr A peripheral register address.
226  *
227  * @param[in] mask A peripheral register mask.
228  *
229  * @param[in] val  A peripheral register value. The value must be shifted to the
230                   correct bit position in the register corresponding to the field
231                   defined by the mask parameter. The register value must be
232                   contained in the field defined by the mask parameter. The
233                   register value is masked to prevent involuntary spillage.
234  ******************************************************************************/
sl_hal_bus_reg_write_mask(volatile uint32_t * addr,uint32_t mask,uint32_t val)235 __STATIC_INLINE void sl_hal_bus_reg_write_mask(volatile uint32_t *addr,
236                                                uint32_t mask,
237                                                uint32_t val)
238 {
239   CORE_DECLARE_IRQ_STATE;
240   CORE_ENTER_CRITICAL();
241   *addr = (*addr & ~mask) | (val & mask);
242   CORE_EXIT_CRITICAL();
243 }
244 
245 /***************************************************************************//**
246  * @brief
247  *   Perform a peripheral register masked read.
248  *
249  * @details
250  *   Read an unshifted and masked value from a peripheral register.
251  *
252  * @note
253  *   This operation is not hardware accelerated.
254  *
255  * @param[in] addr A peripheral register address.
256  *
257  * @param[in] mask A peripheral register mask.
258  *
259  * @return
260  *   An unshifted and masked register value.
261  ******************************************************************************/
sl_hal_bus_reg_read_mask(volatile const uint32_t * addr,uint32_t mask)262 __STATIC_INLINE uint32_t sl_hal_bus_reg_read_mask(volatile const uint32_t *addr,
263                                                   uint32_t mask)
264 {
265   return *addr & mask;
266 }
267 
268 /** @} (end addtogroup bus) */
269 
270 #ifdef __cplusplus
271 }
272 #endif
273 
274 #endif /* SL_HAL_BUS_H */
275