Line data Source code
1 1 : /*
2 : * Copyright (c) 2025 Sequans Communications
3 : *
4 : * SPDX-License-Identifier: Apache-2.0
5 : */
6 :
7 : /**
8 : * @file
9 : * @ingroup hwspinlock_interface
10 : * @brief Main header file for hardware spinlock driver API.
11 : */
12 :
13 : #ifndef ZEPHYR_INCLUDE_DRIVERS_HWSPINLOCK_H_
14 : #define ZEPHYR_INCLUDE_DRIVERS_HWSPINLOCK_H_
15 :
16 : /**
17 : * @brief Interfaces for hardware spinlocks.
18 : * @defgroup hwspinlock_interface Hardware Spinlock
19 : * @ingroup io_interfaces
20 : * @{
21 : */
22 :
23 : #include <zephyr/types.h>
24 : #include <zephyr/sys/util.h>
25 : #include <zephyr/sys/__assert.h>
26 : #include <zephyr/device.h>
27 : #include <zephyr/spinlock.h>
28 : #include <zephyr/devicetree.h>
29 :
30 : #ifdef __cplusplus
31 : extern "C" {
32 : #endif
33 :
34 : /**
35 : * @brief HW spinlock controller runtime context
36 : */
37 1 : struct hwspinlock_context {
38 : /**
39 : * @internal
40 : * Per HW spinlock lock
41 : * @note HW spinlock protects resources across clusters, but we need to protect the
42 : * access to HW spinlock inside of the same cluster, so a single thread may claim the
43 : * lock at a time.
44 : */
45 0 : struct k_spinlock lock;
46 : };
47 :
48 : /**
49 : * @brief Opaque type to represent a hwspinlock runtime context.
50 : *
51 : * This type is not meant to be inspected by application code.
52 : */
53 1 : typedef struct hwspinlock_context hwspinlock_ctx_t;
54 :
55 : /**
56 : * @brief Complete hardware spinlock DT information
57 : */
58 1 : struct hwspinlock_dt_spec {
59 : /** HW spinlock device */
60 1 : const struct device *dev;
61 : /** HW spinlock id */
62 1 : uint32_t id;
63 : /** Runtime context */
64 1 : hwspinlock_ctx_t ctx;
65 : };
66 :
67 : /**
68 : * @brief Initializer for a hwspinlock_ctx_t
69 : *
70 : * @note We must declare each field individually because a struct k_spinlock might have no field
71 : * depending on Kconfig options, and gcc requires struct without any field to be initialized
72 : * explicitly, instead of just being able to do `{0}`.
73 : */
74 1 : #define HWSPINLOCK_CTX_INITIALIZER \
75 : { \
76 : .lock = {0}, \
77 : }
78 :
79 : /**
80 : * @brief Structure initializer for struct hwspinlock_dt_spec from devicetree by index
81 : *
82 : * This helper macro expands to a static initializer for a struct hwspinlock_dt_spec
83 : * by reading the relevant device controller and id number from the devicetree.
84 : *
85 : * Example devicetree fragment:
86 : *
87 : * @code{.devicetree}
88 : * n: node {
89 : * hwlocks = <&hwlock 8>,
90 : * <&hwlock 9>;
91 : * };
92 : * @endcode
93 : *
94 : * Example usage:
95 : *
96 : * @code{.c}
97 : * const struct hwspinlock_dt_spec spec = HWSPINLOCK_DT_SPEC_GET_BY_IDX(DT_NODELABEL(n), 0);
98 : * @endcode
99 : *
100 : * @param node_id Devicetree node identifier for the HWSPINLOCK device
101 : * @param idx Index of hwlocks element
102 : *
103 : * @return static initializer for a struct hwspinlock_dt_spec
104 : */
105 1 : #define HWSPINLOCK_DT_SPEC_GET_BY_IDX(node_id, idx) \
106 : { \
107 : .dev = DEVICE_DT_GET(DT_HWSPINLOCK_CTRL_BY_IDX(node_id, idx)), \
108 : .id = DT_HWSPINLOCK_ID_BY_IDX(node_id, idx), \
109 : .ctx = HWSPINLOCK_CTX_INITIALIZER, \
110 : }
111 :
112 : /**
113 : * @brief Structure initializer for struct hwspinlock_dt_spec from devicetree by name
114 : *
115 : * This helper macro expands to a static initializer for a struct hwspinlock_dt_spec
116 : * by reading the relevant device controller and id number from the devicetree.
117 : *
118 : * Example devicetree fragment:
119 : *
120 : * @code{.devicetree}
121 : * n: node {
122 : * hwlocks = <&hwlock 8>,
123 : * <&hwlock 9>;
124 : * hwlock-names = "rd", "wr";
125 : * };
126 : * @endcode
127 : *
128 : * Example usage:
129 : *
130 : * @code{.c}
131 : * const struct hwspinlock_dt_spec spec = HWSPINLOCK_DT_SPEC_GET_BY_NAME(DT_NODELABEL(n), wr);
132 : * @endcode
133 : *
134 : * @param node_id Devicetree node identifier for the HWSPINLOCK device
135 : * @param name lowercase-and-underscores name of the hwlocks element
136 : *
137 : * @return static initializer for a struct hwspinlock_dt_spec
138 : */
139 1 : #define HWSPINLOCK_DT_SPEC_GET_BY_NAME(node_id, name) \
140 : { \
141 : .dev = DEVICE_DT_GET(DT_HWSPINLOCK_CTRL_BY_NAME(node_id, name)), \
142 : .id = DT_HWSPINLOCK_ID_BY_NAME(node_id, name), \
143 : .ctx = HWSPINLOCK_CTX_INITIALIZER, \
144 : }
145 :
146 : /**
147 : * @brief Structure initializer for struct hwspinlock_dt_spec from devicetree.
148 : *
149 : * This is equivalent to HWSPINLOCK_DT_SPEC_GET_BY_IDX(node_id, 0)
150 : *
151 : * @param node_id Devicetree node identifier for the HWSPINLOCK device
152 : * @see HWSPINLOCK_DT_SPEC_GET_BY_IDX()
153 : */
154 1 : #define HWSPINLOCK_DT_SPEC_GET(node_id) \
155 : HWSPINLOCK_DT_SPEC_GET_BY_IDX(node_id, 0)
156 :
157 : /**
158 : * @brief Instance version of HWSPINLOCK_DT_SPEC_GET_BY_IDX()
159 : *
160 : * @param inst DT_DRV_COMPAT instance number
161 : * @param idx Index of the hwlocks element
162 : *
163 : * @see HWSPINLOCK_DT_SPEC_GET_BY_IDX()
164 : */
165 1 : #define HWSPINLOCK_DT_SPEC_INST_GET_BY_IDX(inst, idx) \
166 : HWSPINLOCK_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), idx)
167 :
168 : /**
169 : * @brief Instance version of HWSPINLOCK_DT_SPEC_GET_BY_NAME()
170 : *
171 : * @param inst DT_DRV_COMPAT instance number
172 : * @param name lowercase-and-underscores name of the hwlocks element
173 : *
174 : * @see HWSPINLOCK_DT_SPEC_GET_BY_NAME()
175 : */
176 1 : #define HWSPINLOCK_DT_SPEC_INST_GET_BY_NAME(inst, name) \
177 : HWSPINLOCK_DT_SPEC_GET_BY_NAME(DT_DRV_INST(inst), name)
178 : /**
179 : * @brief Instance version of HWSPINLOCK_DT_SPEC_GET()
180 : *
181 : * @param inst DT_DRV_COMPAT instance number
182 : *
183 : * @see HWSPINLOCK_DT_SPEC_GET()
184 : */
185 1 : #define HWSPINLOCK_DT_SPEC_INST_GET(inst) \
186 : HWSPINLOCK_DT_SPEC_GET(DT_DRV_INST(inst))
187 :
188 : /** @cond INTERNAL_HIDDEN */
189 :
190 : /**
191 : * @brief Callback API for trying to lock HW spinlock
192 : *
193 : * This callback is optional. If not implemented, hw_spin_trylock() will return -ENOSYS.
194 : *
195 : * @see hw_spin_trylock
196 : */
197 : typedef int (*hwspinlock_api_trylock)(const struct device *dev, uint32_t id);
198 :
199 : /**
200 : * @brief Callback API to lock HW spinlock
201 : *
202 : * This callback must be implemented.
203 : *
204 : * @see hw_spin_lock
205 : */
206 : typedef void (*hwspinlock_api_lock)(const struct device *dev, uint32_t id);
207 :
208 : /**
209 : * @brief Callback API to unlock HW spinlock
210 : *
211 : * This callback must be implemented.
212 : *
213 : * @see hw_spin_unlock
214 : */
215 : typedef void (*hwspinlock_api_unlock)(const struct device *dev, uint32_t id);
216 :
217 : /**
218 : * @brief Callback API to get HW spinlock max ID
219 : *
220 : * This callback must be implemented.
221 : *
222 : * @see hw_spinlock_get_max_id
223 : */
224 : typedef uint32_t (*hwspinlock_api_get_max_id)(const struct device *dev);
225 :
226 : __subsystem struct hwspinlock_driver_api {
227 : hwspinlock_api_trylock trylock;
228 : hwspinlock_api_lock lock;
229 : hwspinlock_api_unlock unlock;
230 : hwspinlock_api_get_max_id get_max_id;
231 : };
232 :
233 : /**
234 : * @endcond
235 : */
236 :
237 : /**
238 : * @brief Try to lock HW spinlock
239 : *
240 : * This function is used when trying to lock an HW spinlock. If the spinlock is already locked by
241 : * another cluster, exits with -EBUSY.
242 : *
243 : * @see hw_spin_lock
244 : *
245 : * @param dev HW spinlock device instance.
246 : * @param ctx HW spinlock runtime context.
247 : * @param id Spinlock identifier.
248 : * @param[out] key A pointer to the spinlock key.
249 : *
250 : * @retval 0 On success.
251 : * @retval -ENOSYS If the operation is not implemented.
252 : * @retval -EINVAL If HW spinlock id is invalid.
253 : * @retval -EBUSY If HW spinlock is already locked by someone else.
254 : */
255 1 : static inline int hw_spin_trylock(const struct device *dev, hwspinlock_ctx_t *ctx, uint32_t id,
256 : k_spinlock_key_t *key)
257 : {
258 : const struct hwspinlock_driver_api *api = (const struct hwspinlock_driver_api *)dev->api;
259 : int ret;
260 :
261 : if (api->trylock == NULL) {
262 : return -ENOSYS;
263 : }
264 :
265 : ret = k_spin_trylock(&ctx->lock, key);
266 : if (ret) {
267 : return ret;
268 : }
269 : return api->trylock(dev, id);
270 : }
271 :
272 : /**
273 : * @brief Lock HW spinlock
274 : *
275 : * This function is used when locking an HW spinlock. If the spinlock is already locked by
276 : * another party, waits for it to be released.
277 : * This is useful when protecting a resource which is shared between multiple clusters.
278 : *
279 : * @note The goal is not to replace regular spinlocks, but to protect shared resources between
280 : * clusters. However, because we don't want another thread to take the same HW spinlock twice, the
281 : * locking mechanism acts as a regular spinlock as well.
282 : *
283 : * Because this uses a regular zephyr spinlock in conjunction with a hwspinlock, the same rules
284 : * applies. Separate hwspinlock may be nested. It is legal to lock another (unlocked) hwspinlock
285 : * while holding a lock. However, an attempt to acquire a hwspinlock that the CPU already holds
286 : * will deadlock.
287 : * @see k_spin_lock
288 : *
289 : * @param dev HW spinlock device instance.
290 : * @param ctx HW spinlock runtime context.
291 : * @param id Spinlock identifier.
292 : *
293 : * @return A key value that must be passed to hw_spin_unlock() when the
294 : * lock is released.
295 : */
296 1 : static inline k_spinlock_key_t hw_spin_lock(const struct device *dev, hwspinlock_ctx_t *ctx,
297 : uint32_t id)
298 : {
299 : const struct hwspinlock_driver_api *api = (const struct hwspinlock_driver_api *)dev->api;
300 : k_spinlock_key_t k;
301 :
302 : __ASSERT(api->lock != NULL, "hwspinlock lock callback must be implemented");
303 :
304 : k = k_spin_lock(&ctx->lock);
305 : api->lock(dev, id);
306 :
307 : return k;
308 : }
309 :
310 : /**
311 : * @brief Unlock HW spinlock
312 : *
313 : * This function to unlock an HW spinlock
314 : *
315 : * @param dev HW spinlock device instance.
316 : * @param ctx HW spinlock runtime context.
317 : * @param id Spinlock identifier.
318 : * @param key The value returned from hw_spin_lock() when this lock was
319 : * acquired
320 : */
321 1 : static inline void hw_spin_unlock(const struct device *dev, hwspinlock_ctx_t *ctx, uint32_t id,
322 : k_spinlock_key_t key)
323 : {
324 : const struct hwspinlock_driver_api *api = (const struct hwspinlock_driver_api *)dev->api;
325 :
326 : __ASSERT(api->unlock != NULL, "hwspinlock unlock callback must be implemented");
327 :
328 : api->unlock(dev, id);
329 : k_spin_unlock(&ctx->lock, key);
330 : }
331 :
332 : /**
333 : * @brief Get HW spinlock max ID
334 : *
335 : * This function is used to get the HW spinlock maximum ID. It should
336 : * be called before attempting to lock/unlock a specific HW spinlock.
337 : *
338 : * @param dev HW spinlock device instance.
339 : *
340 : * @retval HW spinlock max ID.
341 : */
342 1 : static inline uint32_t hw_spinlock_get_max_id(const struct device *dev)
343 : {
344 : const struct hwspinlock_driver_api *api = (const struct hwspinlock_driver_api *)dev->api;
345 :
346 : __ASSERT(api->get_max_id != NULL, "hwspinlock get_max_id callback must be implemented");
347 :
348 : return api->get_max_id(dev);
349 : }
350 :
351 : /**
352 : * @brief Try to lock HW spinlock from a struct hwspinlock_dt_spec
353 : *
354 : * This is the dt_spec equivalent of hw_spin_trylock()
355 : * @see hw_spin_trylock
356 : *
357 : * @param spec HWSPINLOCK specification from devicetree
358 : * @param[out] key A pointer to the spinlock key.
359 : *
360 : * @returns See return values from hw_spin_trylock()
361 : */
362 1 : static inline int hw_spin_trylock_dt(struct hwspinlock_dt_spec *spec, k_spinlock_key_t *key)
363 : {
364 : return hw_spin_trylock(spec->dev, &spec->ctx, spec->id, key);
365 : }
366 :
367 : /**
368 : * @brief Lock HW spinlock from a struct hwspinlock_dt_spec
369 : *
370 : * This is the dt_spec equivalent of hw_spin_lock()
371 : * @see hw_spin_lock
372 : *
373 : * @param spec HWSPINLOCK specification from devicetree
374 : *
375 : * @returns See return values from hw_spin_lock()
376 : */
377 1 : static inline k_spinlock_key_t hw_spin_lock_dt(struct hwspinlock_dt_spec *spec)
378 : {
379 : return hw_spin_lock(spec->dev, &spec->ctx, spec->id);
380 : }
381 :
382 : /**
383 : * @brief Unlock HW spinlock from a struct hwspinlock_dt_spec
384 : *
385 : * This is the dt_spec equivalent of hw_spin_unlock()
386 : * @see hw_spin_unlock
387 : *
388 : * @param spec HWSPINLOCK specification from devicetree
389 : * @param key The value returned from hw_spin_lock() when this lock was
390 : * acquired
391 : */
392 1 : static inline void hw_spin_unlock_dt(struct hwspinlock_dt_spec *spec, k_spinlock_key_t key)
393 : {
394 : hw_spin_unlock(spec->dev, &spec->ctx, spec->id, key);
395 : }
396 :
397 : /**
398 : * @brief Get HW spinlock max ID from a struct hwspinlock_dt_spec
399 : *
400 : * This is the dt_spec equivalent of hw_spinlock_get_max_id()
401 : * @see hw_spinlock_get_max_id
402 : *
403 : * @param spec HWSPINLOCK specification from devicetree
404 : *
405 : * @returns See return values from hw_spinlock_get_max_id()
406 : */
407 1 : static inline uint32_t hw_spinlock_get_max_id_dt(struct hwspinlock_dt_spec *spec)
408 : {
409 : return hw_spinlock_get_max_id(spec->dev);
410 : }
411 :
412 : #ifdef __cplusplus
413 : }
414 : #endif
415 :
416 : /** @} */
417 :
418 : #endif /* ZEPHYR_INCLUDE_DRIVERS_HWSPINLOCK_H_ */
|