diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 6005b4589b6..a9caf9f0320 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -79,3 +79,4 @@ add_subdirectory_ifdef(CONFIG_WATCHDOG watchdog) add_subdirectory_ifdef(CONFIG_WIFI wifi) add_subdirectory_ifdef(CONFIG_RTC rtc) add_subdirectory_ifdef(CONFIG_ARM_SIP_SVC_DRIVER sip_svc) +add_subdirectory_ifdef(CONFIG_HWSPINLOCK hwspinlock) diff --git a/drivers/Kconfig b/drivers/Kconfig index d615249727c..0a369875f6f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -82,5 +82,6 @@ source "drivers/watchdog/Kconfig" source "drivers/wifi/Kconfig" source "drivers/xen/Kconfig" source "drivers/sip_svc/Kconfig" +source "drivers/hwspinlock/Kconfig" endmenu diff --git a/drivers/hwspinlock/CMakeLists.txt b/drivers/hwspinlock/CMakeLists.txt new file mode 100644 index 00000000000..458f545f75d --- /dev/null +++ b/drivers/hwspinlock/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_USERSPACE hwspinlock_handlers.c) diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig new file mode 100644 index 00000000000..6b12ca50d59 --- /dev/null +++ b/drivers/hwspinlock/Kconfig @@ -0,0 +1,19 @@ +# HW spinlock configuration options + +# Copyright (c) 2023 Sequans Communications. +# SPDX-License-Identifier: Apache-2.0 + +menuconfig HWSPINLOCK + bool "HW spinlock Support" + help + Include support for HW spinlock. + +if HWSPINLOCK + +config HWSPINLOCK_INIT_PRIORITY + int "HW spinlock init priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + HW spinlock driver device initialization priority. + +endif diff --git a/drivers/hwspinlock/hwspinlock_handlers.c b/drivers/hwspinlock/hwspinlock_handlers.c new file mode 100644 index 00000000000..a8e6c2a132e --- /dev/null +++ b/drivers/hwspinlock/hwspinlock_handlers.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Sequans Communications + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static inline int z_vrfy_hwspinlock_trylock(const struct device *dev, uint32_t id) +{ + Z_OOPS(Z_SYSCALL_DRIVER_HWSPINLOCK(dev, trylock)); + return z_impl_hwspinlock_trylock(dev, id); +} + +#include + +static inline void z_vrfy_hwspinlock_lock(const struct device *dev, uint32_t id) +{ + Z_OOPS(Z_SYSCALL_DRIVER_HWSPINLOCK(dev, lock)); + z_impl_hwspinlock_lock(dev, id); +} + +#include + +static inline void z_vrfy_hwspinlock_unlock(const struct device *dev, uint32_t id) +{ + Z_OOPS(Z_SYSCALL_DRIVER_HWSPINLOCK(dev, unlock)); + z_impl_hwspinlock_unlock(dev, id); +} + +#include + +static inline uint32_t z_vrfy_hwspinlock_get_max_id(const struct device *dev) +{ + Z_OOPS(Z_SYSCALL_DRIVER_HWSPINLOCK(dev, get_max_id)); + return z_impl_hwspinlock_get_max_id(dev); +} + +#include diff --git a/include/zephyr/drivers/hwspinlock.h b/include/zephyr/drivers/hwspinlock.h new file mode 100644 index 00000000000..9f9fd994ea2 --- /dev/null +++ b/include/zephyr/drivers/hwspinlock.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2023 Sequans Communications + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_HWSPINLOCK_H_ +#define ZEPHYR_INCLUDE_DRIVERS_HWSPINLOCK_H_ + +/** + * @brief HW spinlock Interface + * @defgroup hwspinlock_interface HW spinlock Interface + * @ingroup io_interfaces + * @{ + */ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Callback API for trying to lock HW spinlock + * @see hwspinlock_trylock(). + */ +typedef int (*hwspinlock_api_trylock)(const struct device *dev, uint32_t id); + +/** + * @brief Callback API to lock HW spinlock + * @see hwspinlock_lock(). + */ +typedef void (*hwspinlock_api_lock)(const struct device *dev, uint32_t id); + +/** + * @brief Callback API to unlock HW spinlock + * @see hwspinlock_unlock(). + */ +typedef void (*hwspinlock_api_unlock)(const struct device *dev, uint32_t id); + +/** + * @brief Callback API to get HW spinlock max ID + * @see hwspinlock_get_max_id(). + */ +typedef uint32_t (*hwspinlock_api_get_max_id)(const struct device *dev); + +__subsystem struct hwspinlock_driver_api { + hwspinlock_api_trylock trylock; + hwspinlock_api_lock lock; + hwspinlock_api_unlock unlock; + hwspinlock_api_get_max_id get_max_id; +}; +/** + * @endcond + */ + +/** + * @brief Try to lock HW spinlock + * + * This function is used for try to lock specific HW spinlock. It should + * be called before a critical section that we want to protect. + * + * @param dev HW spinlock device instance. + * @param id Spinlock identifier. + * + * @retval 0 If successful. + * @retval -errno In case of any failure. + */ +__syscall int hwspinlock_trylock(const struct device *dev, uint32_t id); + +static inline int z_impl_hwspinlock_trylock(const struct device *dev, uint32_t id) +{ + const struct hwspinlock_driver_api *api = + (const struct hwspinlock_driver_api *)dev->api; + + if (api->trylock == NULL) + return -ENOSYS; + + return api->trylock(dev, id); +} + +/** + * @brief Lock HW spinlock + * + * This function is used to lock specific HW spinlock. It should be + * called before a critical section that we want to protect. + * + * @param dev HW spinlock device instance. + * @param id Spinlock identifier. + */ +__syscall void hwspinlock_lock(const struct device *dev, uint32_t id); + +static inline void z_impl_hwspinlock_lock(const struct device *dev, uint32_t id) +{ + const struct hwspinlock_driver_api *api = + (const struct hwspinlock_driver_api *)dev->api; + + if (api->lock != NULL) + api->lock(dev, id); +} + +/** + * @brief Try to unlock HW spinlock + * + * This function is used for try to unlock specific HW spinlock. It should + * be called after a critical section that we want to protect. + * + * @param dev HW spinlock device instance. + * @param id Spinlock identifier. + */ +__syscall void hwspinlock_unlock(const struct device *dev, uint32_t id); + +static inline void z_impl_hwspinlock_unlock(const struct device *dev, uint32_t id) +{ + const struct hwspinlock_driver_api *api = + (const struct hwspinlock_driver_api *)dev->api; + + if (api->unlock != NULL) + api->unlock(dev, id); +} + +/** + * @brief Get HW spinlock max ID + * + * This function is used to get the HW spinlock maximum ID. It should + * be called before attempting to lock/unlock a specific HW spinlock. + * + * @param dev HW spinlock device instance. + * + * @retval HW spinlock max ID. + * @retval 0 if the function is not implemented by the driver. + */ +__syscall uint32_t hwspinlock_get_max_id(const struct device *dev); + +static inline uint32_t z_impl_hwspinlock_get_max_id(const struct device *dev) +{ + const struct hwspinlock_driver_api *api = + (const struct hwspinlock_driver_api *)dev->api; + + if (api->get_max_id == NULL) + return 0; + + return api->get_max_id(dev); +} + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_HWSPINLOCK_H_ */