LCOV - code coverage report
Current view: top level - zephyr/drivers - hwspinlock.h Coverage Total Hit
Test: new.info Lines: 95.7 % 23 22
Test Date: 2025-10-20 12:20:01

            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_ */
        

Generated by: LCOV version 2.0-1