| /* |
| * Copyright (c) 2018 Nordic Semiconductor ASA |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Public API for counter and timer drivers |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_DRIVERS_COUNTER_H_ |
| #define ZEPHYR_INCLUDE_DRIVERS_COUNTER_H_ |
| |
| /** |
| * @brief Counter Interface |
| * @defgroup counter_interface Counter Interface |
| * @since 1.14 |
| * @version 0.8.0 |
| * @ingroup io_interfaces |
| * @{ |
| */ |
| |
| #include <errno.h> |
| |
| #include <zephyr/types.h> |
| #include <stddef.h> |
| #include <zephyr/device.h> |
| #include <zephyr/sys_clock.h> |
| #include <stdbool.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** |
| * @anchor COUNTER_FLAGS |
| * @name Counter device capabilities |
| * @{ |
| */ |
| |
| /** |
| * @brief Counter count up flag. |
| */ |
| #define COUNTER_CONFIG_INFO_COUNT_UP BIT(0) |
| |
| /**@} */ |
| |
| /** |
| * @anchor COUNTER_TOP_FLAGS |
| * @name Flags used by counter_top_cfg. |
| * @{ |
| */ |
| |
| /** |
| * @brief Flag preventing counter reset when top value is changed. |
| * |
| * If flags is set then counter is free running while top value is updated, |
| * otherwise counter is reset (see @ref counter_set_top_value()). |
| */ |
| #define COUNTER_TOP_CFG_DONT_RESET BIT(0) |
| |
| /** |
| * @brief Flag instructing counter to reset itself if changing top value |
| * results in counter going out of new top value bound. |
| * |
| * See @ref COUNTER_TOP_CFG_DONT_RESET. |
| */ |
| #define COUNTER_TOP_CFG_RESET_WHEN_LATE BIT(1) |
| |
| /**@} */ |
| |
| /** |
| * @anchor COUNTER_ALARM_FLAGS |
| * @name Alarm configuration flags |
| * |
| * @brief Used in alarm configuration structure (@ref counter_alarm_cfg). |
| * @{ */ |
| |
| /** |
| * @brief Counter alarm absolute value flag. |
| * |
| * Ticks relation to counter value. If set ticks are treated as absolute value, |
| * else it is relative to the counter reading performed during the call. |
| */ |
| #define COUNTER_ALARM_CFG_ABSOLUTE BIT(0) |
| |
| /** |
| * @brief Alarm flag enabling immediate expiration when driver detects that |
| * absolute alarm was set too late. |
| * |
| * Alarm callback must be called from the same context as if it was set on time. |
| */ |
| #define COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE BIT(1) |
| |
| /**@} */ |
| |
| /** |
| * @anchor COUNTER_GUARD_PERIOD_FLAGS |
| * @name Counter guard period flags |
| * |
| * @brief Used by @ref counter_set_guard_period and |
| * @ref counter_get_guard_period. |
| * @{ */ |
| |
| /** |
| * @brief Identifies guard period needed for detection of late setting of |
| * absolute alarm (see @ref counter_set_channel_alarm). |
| */ |
| #define COUNTER_GUARD_PERIOD_LATE_TO_SET BIT(0) |
| |
| /**@} */ |
| |
| /** @brief Alarm callback |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param chan_id Channel ID. |
| * @param ticks Counter value that triggered the alarm. |
| * @param user_data User data. |
| */ |
| typedef void (*counter_alarm_callback_t)(const struct device *dev, |
| uint8_t chan_id, uint32_t ticks, |
| void *user_data); |
| |
| /** @brief Alarm callback structure. |
| */ |
| struct counter_alarm_cfg { |
| /** |
| * Callback called on alarm (cannot be NULL). |
| */ |
| counter_alarm_callback_t callback; |
| /** |
| * Number of ticks that triggers the alarm. |
| * |
| * It can be relative (to now) or an absolute value (see @ref |
| * COUNTER_ALARM_CFG_ABSOLUTE). Both, relative and absolute, alarm |
| * values can be any value between zero and the current top value (see |
| * @ref counter_get_top_value). When setting an absolute alarm value |
| * close to the current counter value there is a risk that the counter |
| * will have counted past the given absolute value before the driver |
| * manages to activate the alarm. Therefore a guard period can be |
| * defined that lets the driver decide unambiguously whether it is late |
| * or not (see @ref counter_set_guard_period). If the counter is clock |
| * driven then ticks can be converted to microseconds (see @ref |
| * counter_ticks_to_us). Alternatively, the counter implementation may |
| * count asynchronous events. |
| */ |
| uint32_t ticks; |
| /** |
| * User data returned in callback. |
| */ |
| void *user_data; |
| /** |
| * Alarm flags (see @ref COUNTER_ALARM_FLAGS). |
| */ |
| uint32_t flags; |
| }; |
| |
| /** @brief Callback called when counter turns around. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param user_data User data provided in @ref counter_set_top_value. |
| */ |
| typedef void (*counter_top_callback_t)(const struct device *dev, |
| void *user_data); |
| |
| /** @brief Top value configuration structure. |
| */ |
| struct counter_top_cfg { |
| /** |
| * Top value. |
| */ |
| uint32_t ticks; |
| /** |
| * Callback function (can be NULL). |
| */ |
| counter_top_callback_t callback; |
| /** |
| * User data passed to callback function (not valid if callback is NULL). |
| */ |
| void *user_data; |
| /** |
| * Flags (see @ref COUNTER_TOP_FLAGS). |
| */ |
| uint32_t flags; |
| }; |
| |
| /** @brief Structure with generic counter features. |
| */ |
| struct counter_config_info { |
| /** |
| * Maximal (default) top value on which counter is reset (cleared or reloaded). |
| */ |
| uint32_t max_top_value; |
| /** |
| * Frequency of the source clock if synchronous events are counted. |
| */ |
| uint32_t freq; |
| /** |
| * Flags (see @ref COUNTER_FLAGS). |
| */ |
| uint8_t flags; |
| /** |
| * Number of channels that can be used for setting alarm. |
| * |
| * @see counter_set_channel_alarm |
| */ |
| uint8_t channels; |
| }; |
| |
| typedef int (*counter_api_start)(const struct device *dev); |
| typedef int (*counter_api_stop)(const struct device *dev); |
| typedef int (*counter_api_get_value)(const struct device *dev, |
| uint32_t *ticks); |
| typedef int (*counter_api_get_value_64)(const struct device *dev, |
| uint64_t *ticks); |
| typedef int (*counter_api_set_alarm)(const struct device *dev, |
| uint8_t chan_id, |
| const struct counter_alarm_cfg *alarm_cfg); |
| typedef int (*counter_api_cancel_alarm)(const struct device *dev, |
| uint8_t chan_id); |
| typedef int (*counter_api_set_top_value)(const struct device *dev, |
| const struct counter_top_cfg *cfg); |
| typedef uint32_t (*counter_api_get_pending_int)(const struct device *dev); |
| typedef uint32_t (*counter_api_get_top_value)(const struct device *dev); |
| typedef uint32_t (*counter_api_get_guard_period)(const struct device *dev, |
| uint32_t flags); |
| typedef int (*counter_api_set_guard_period)(const struct device *dev, |
| uint32_t ticks, |
| uint32_t flags); |
| typedef uint32_t (*counter_api_get_freq)(const struct device *dev); |
| |
| __subsystem struct counter_driver_api { |
| counter_api_start start; |
| counter_api_stop stop; |
| counter_api_get_value get_value; |
| counter_api_get_value_64 get_value_64; |
| counter_api_set_alarm set_alarm; |
| counter_api_cancel_alarm cancel_alarm; |
| counter_api_set_top_value set_top_value; |
| counter_api_get_pending_int get_pending_int; |
| counter_api_get_top_value get_top_value; |
| counter_api_get_guard_period get_guard_period; |
| counter_api_set_guard_period set_guard_period; |
| counter_api_get_freq get_freq; |
| }; |
| |
| /** |
| * @brief Function to check if counter is counting up. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * |
| * @retval true if counter is counting up. |
| * @retval false if counter is counting down. |
| */ |
| __syscall bool counter_is_counting_up(const struct device *dev); |
| |
| static inline bool z_impl_counter_is_counting_up(const struct device *dev) |
| { |
| const struct counter_config_info *config = |
| (const struct counter_config_info *)dev->config; |
| |
| return config->flags & COUNTER_CONFIG_INFO_COUNT_UP; |
| } |
| |
| /** |
| * @brief Function to get number of alarm channels. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * |
| * @return Number of alarm channels. |
| */ |
| __syscall uint8_t counter_get_num_of_channels(const struct device *dev); |
| |
| static inline uint8_t z_impl_counter_get_num_of_channels(const struct device *dev) |
| { |
| const struct counter_config_info *config = |
| (const struct counter_config_info *)dev->config; |
| |
| return config->channels; |
| } |
| |
| /** |
| * @brief Function to get counter frequency. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * |
| * @return Frequency of the counter in Hz, or zero if the counter does |
| * not have a fixed frequency. |
| */ |
| __syscall uint32_t counter_get_frequency(const struct device *dev); |
| |
| static inline uint32_t z_impl_counter_get_frequency(const struct device *dev) |
| { |
| const struct counter_config_info *config = |
| (const struct counter_config_info *)dev->config; |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return api->get_freq ? api->get_freq(dev) : config->freq; |
| } |
| |
| /** |
| * @brief Function to convert microseconds to ticks. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * @param[in] us Microseconds. |
| * |
| * @return Converted ticks. Ticks will be saturated if exceed 32 bits. |
| */ |
| __syscall uint32_t counter_us_to_ticks(const struct device *dev, uint64_t us); |
| |
| static inline uint32_t z_impl_counter_us_to_ticks(const struct device *dev, |
| uint64_t us) |
| { |
| uint64_t ticks = (us * z_impl_counter_get_frequency(dev)) / USEC_PER_SEC; |
| |
| return (ticks > (uint64_t)UINT32_MAX) ? UINT32_MAX : ticks; |
| } |
| |
| /** |
| * @brief Function to convert ticks to microseconds. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * @param[in] ticks Ticks. |
| * |
| * @return Converted microseconds. |
| */ |
| __syscall uint64_t counter_ticks_to_us(const struct device *dev, uint32_t ticks); |
| |
| static inline uint64_t z_impl_counter_ticks_to_us(const struct device *dev, |
| uint32_t ticks) |
| { |
| return ((uint64_t)ticks * USEC_PER_SEC) / z_impl_counter_get_frequency(dev); |
| } |
| |
| /** |
| * @brief Function to retrieve maximum top value that can be set. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * |
| * @return Max top value. |
| */ |
| __syscall uint32_t counter_get_max_top_value(const struct device *dev); |
| |
| static inline uint32_t z_impl_counter_get_max_top_value(const struct device *dev) |
| { |
| const struct counter_config_info *config = |
| (const struct counter_config_info *)dev->config; |
| |
| return config->max_top_value; |
| } |
| |
| /** |
| * @brief Start counter device in free running mode. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * |
| * @retval 0 If successful. |
| * @retval Negative errno code if failure. |
| */ |
| __syscall int counter_start(const struct device *dev); |
| |
| static inline int z_impl_counter_start(const struct device *dev) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return api->start(dev); |
| } |
| |
| /** |
| * @brief Stop counter device. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * |
| * @retval 0 If successful. |
| * @retval -ENOTSUP if the device doesn't support stopping the |
| * counter. |
| */ |
| __syscall int counter_stop(const struct device *dev); |
| |
| static inline int z_impl_counter_stop(const struct device *dev) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return api->stop(dev); |
| } |
| |
| /** |
| * @brief Get current counter value. |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param ticks Pointer to where to store the current counter value |
| * |
| * @retval 0 If successful. |
| * @retval Negative error code on failure getting the counter value |
| */ |
| __syscall int counter_get_value(const struct device *dev, uint32_t *ticks); |
| |
| static inline int z_impl_counter_get_value(const struct device *dev, |
| uint32_t *ticks) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return api->get_value(dev, ticks); |
| } |
| |
| /** |
| * @brief Get current counter 64-bit value. |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param ticks Pointer to where to store the current counter value |
| * |
| * @retval 0 If successful. |
| * @retval Negative error code on failure getting the counter value |
| */ |
| __syscall int counter_get_value_64(const struct device *dev, uint64_t *ticks); |
| |
| static inline int z_impl_counter_get_value_64(const struct device *dev, |
| uint64_t *ticks) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| if (!api->get_value_64) { |
| return -ENOTSUP; |
| } |
| |
| return api->get_value_64(dev, ticks); |
| } |
| |
| /** |
| * @brief Set a single shot alarm on a channel. |
| * |
| * After expiration alarm can be set again, disabling is not needed. When alarm |
| * expiration handler is called, channel is considered available and can be |
| * set again in that context. |
| * |
| * @note API is not thread safe. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param chan_id Channel ID. |
| * @param alarm_cfg Alarm configuration. |
| * |
| * @retval 0 If successful. |
| * @retval -ENOTSUP if request is not supported (device does not support |
| * interrupts or requested channel). |
| * @retval -EINVAL if alarm settings are invalid. |
| * @retval -ETIME if absolute alarm was set too late. |
| * @retval -EBUSY if alarm is already active. |
| */ |
| __syscall int counter_set_channel_alarm(const struct device *dev, |
| uint8_t chan_id, |
| const struct counter_alarm_cfg *alarm_cfg); |
| |
| static inline int z_impl_counter_set_channel_alarm(const struct device *dev, |
| uint8_t chan_id, |
| const struct counter_alarm_cfg *alarm_cfg) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| if (chan_id >= counter_get_num_of_channels(dev)) { |
| return -ENOTSUP; |
| } |
| |
| return api->set_alarm(dev, chan_id, alarm_cfg); |
| } |
| |
| /** |
| * @brief Cancel an alarm on a channel. |
| * |
| * @note API is not thread safe. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param chan_id Channel ID. |
| * |
| * @retval 0 If successful. |
| * @retval -ENOTSUP if request is not supported or the counter was not started |
| * yet. |
| */ |
| __syscall int counter_cancel_channel_alarm(const struct device *dev, |
| uint8_t chan_id); |
| |
| static inline int z_impl_counter_cancel_channel_alarm(const struct device *dev, |
| uint8_t chan_id) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| if (chan_id >= counter_get_num_of_channels(dev)) { |
| return -ENOTSUP; |
| } |
| |
| return api->cancel_alarm(dev, chan_id); |
| } |
| |
| /** |
| * @brief Set counter top value. |
| * |
| * Function sets top value and optionally resets the counter to 0 or top value |
| * depending on counter direction. On turnaround, counter can be reset and |
| * optional callback is periodically called. Top value can only be changed when |
| * there is no active channel alarm. |
| * |
| * @ref COUNTER_TOP_CFG_DONT_RESET prevents counter reset. When counter is |
| * running while top value is updated, it is possible that counter progresses |
| * outside the new top value. In that case, error is returned and optionally |
| * driver can reset the counter (see @ref COUNTER_TOP_CFG_RESET_WHEN_LATE). |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param cfg Configuration. Cannot be NULL. |
| * |
| * @retval 0 If successful. |
| * @retval -ENOTSUP if request is not supported (e.g. top value cannot be |
| * changed or counter cannot/must be reset during top value |
| update). |
| * @retval -EBUSY if any alarm is active. |
| * @retval -ETIME if @ref COUNTER_TOP_CFG_DONT_RESET was set and new top value |
| * is smaller than current counter value (counter counting up). |
| */ |
| __syscall int counter_set_top_value(const struct device *dev, |
| const struct counter_top_cfg *cfg); |
| |
| static inline int z_impl_counter_set_top_value(const struct device *dev, |
| const struct counter_top_cfg |
| *cfg) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| if (cfg->ticks > counter_get_max_top_value(dev)) { |
| return -EINVAL; |
| } |
| |
| return api->set_top_value(dev, cfg); |
| } |
| |
| /** |
| * @brief Function to get pending interrupts |
| * |
| * The purpose of this function is to return the interrupt |
| * status register for the device. |
| * This is especially useful when waking up from |
| * low power states to check the wake up source. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * |
| * @retval 1 if any counter interrupt is pending. |
| * @retval 0 if no counter interrupt is pending. |
| */ |
| __syscall int counter_get_pending_int(const struct device *dev); |
| |
| static inline int z_impl_counter_get_pending_int(const struct device *dev) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return api->get_pending_int(dev); |
| } |
| |
| /** |
| * @brief Function to retrieve current top value. |
| * |
| * @param[in] dev Pointer to the device structure for the driver instance. |
| * |
| * @return Top value. |
| */ |
| __syscall uint32_t counter_get_top_value(const struct device *dev); |
| |
| static inline uint32_t z_impl_counter_get_top_value(const struct device *dev) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return api->get_top_value(dev); |
| } |
| |
| /** |
| * @brief Set guard period in counter ticks. |
| * |
| * When setting an absolute alarm value close to the current counter value there |
| * is a risk that the counter will have counted past the given absolute value |
| * before the driver manages to activate the alarm. If this would go unnoticed |
| * then the alarm would only expire after the timer has wrapped and reached the |
| * given absolute value again after a full timer period. This could take a long |
| * time in case of a 32 bit timer. Setting a sufficiently large guard period will |
| * help the driver detect unambiguously whether it is late or not. |
| * |
| * The guard period should be as many counter ticks as the driver will need at |
| * most to actually activate the alarm after the driver API has been called. If |
| * the driver finds that the counter has just passed beyond the given absolute |
| * tick value but is still close enough to fall within the guard period, it will |
| * assume that it is "late", i.e. that the intended expiry time has already passed. |
| * Depending on the @ref COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE flag the driver will |
| * either ignore the alarm or expire it immediately in such a case. |
| * |
| * If, however, the counter is past the given absolute tick value but outside |
| * the guard period, then the driver will assume that this is intentional and |
| * let the counter wrap around to/from zero before it expires. |
| * |
| * More precisely: |
| * |
| * - When counting upwards (see @ref COUNTER_CONFIG_INFO_COUNT_UP) the given |
| * absolute tick value must be above (now + guard_period) % top_value to be |
| * accepted by the driver. |
| * - When counting downwards, the given absolute tick value must be less than |
| * (now + top_value - guard_period) % top_value to be accepted. |
| * |
| * Examples: |
| * |
| * - counting upwards, now = 4950, top value = 5000, guard period = 100: |
| * absolute tick value >= (4950 + 100) % 5000 = 50 |
| * - counting downwards, now = 50, top value = 5000, guard period = 100: |
| * absolute tick value <= (50 + 5000 - * 100) % 5000 = 4950 |
| * |
| * If you need only short alarm periods, you can set the guard period very high |
| * (e.g. half of the counter top value) which will make it highly unlikely that |
| * the counter will ever unintentionally wrap. |
| * |
| * The guard period is set to 0 on initialization (no protection). |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param ticks Guard period in counter ticks. |
| * @param flags See @ref COUNTER_GUARD_PERIOD_FLAGS. |
| * |
| * @retval 0 if successful. |
| * @retval -ENOTSUP if function or flags are not supported. |
| * @retval -EINVAL if ticks value is invalid. |
| */ |
| __syscall int counter_set_guard_period(const struct device *dev, |
| uint32_t ticks, |
| uint32_t flags); |
| |
| static inline int z_impl_counter_set_guard_period(const struct device *dev, |
| uint32_t ticks, uint32_t flags) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| if (!api->set_guard_period) { |
| return -ENOTSUP; |
| } |
| |
| return api->set_guard_period(dev, ticks, flags); |
| } |
| |
| /** |
| * @brief Return guard period. |
| * |
| * @see counter_set_guard_period. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param flags See @ref COUNTER_GUARD_PERIOD_FLAGS. |
| * |
| * @return Guard period given in counter ticks or 0 if function or flags are |
| * not supported. |
| */ |
| __syscall uint32_t counter_get_guard_period(const struct device *dev, |
| uint32_t flags); |
| |
| static inline uint32_t z_impl_counter_get_guard_period(const struct device *dev, |
| uint32_t flags) |
| { |
| const struct counter_driver_api *api = |
| (struct counter_driver_api *)dev->api; |
| |
| return (api->get_guard_period) ? api->get_guard_period(dev, flags) : 0; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /** |
| * @} |
| */ |
| |
| #include <zephyr/syscalls/counter.h> |
| |
| #endif /* ZEPHYR_INCLUDE_DRIVERS_COUNTER_H_ */ |