drivers: timer: add GRTC driver

Add Nordic driver for GRTC.

Signed-off-by: Magdalena Pastula <magdalena.pastula@nordicsemi.no>
This commit is contained in:
Magdalena Pastula 2024-01-09 15:49:46 +01:00 committed by Fabio Baltieri
parent 70b21845b2
commit b605c4219b
6 changed files with 820 additions and 2 deletions

View file

@ -24,6 +24,7 @@ zephyr_library_sources_ifdef(CONFIG_MCUX_GPT_TIMER mcux_gpt_timer.c)
zephyr_library_sources_ifdef(CONFIG_MIPS_CP0_TIMER mips_cp0_timer.c)
zephyr_library_sources_ifdef(CONFIG_NATIVE_POSIX_TIMER native_posix_timer.c)
zephyr_library_sources_ifdef(CONFIG_NPCX_ITIM_TIMER npcx_itim_timer.c)
zephyr_library_sources_ifdef(CONFIG_NRF_GRTC_TIMER nrf_grtc_timer.c)
zephyr_library_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c)
zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c)

View file

@ -84,6 +84,7 @@ source "drivers/timer/Kconfig.mips_cp0"
source "drivers/timer/Kconfig.native_posix"
source "drivers/timer/Kconfig.npcx_itim"
source "drivers/timer/Kconfig.nrf_rtc"
source "drivers/timer/Kconfig.nrf_grtc"
source "drivers/timer/Kconfig.nrf_xrtc"
source "drivers/timer/Kconfig.rcar_cmt"
source "drivers/timer/Kconfig.riscv_machine"

View file

@ -0,0 +1,48 @@
# Timer driver configuration options
# Copyright (c) 2024 Nordic Semiconductor ASA
menuconfig NRF_GRTC_TIMER
bool "nRF GRTC Timer"
default y if DT_HAS_NORDIC_NRF_GRTC_ENABLED
select TICKLESS_CAPABLE
select TIMER_HAS_64BIT_CYCLE_COUNTER
select NRFX_GRTC
help
This module implements a kernel device driver for the nRF Global Real
Time Counter NRF_GRTC and provides the standard "system clock driver"
interfaces.
if NRF_GRTC_TIMER
config NRF_GRTC_SLEEP_ALLOWED
def_bool y
depends on POWEROFF
help
This feature allows GRTC SYSCOUNTER to go to sleep state.
config NRF_GRTC_TIMER_APP_DEFINED_INIT
bool "Application defines GRTC initialization"
help
Application defines the initialization procedure and time of the GRTC
drivers, rather than leaving it up to SYS_INIT.
config NRF_GRTC_START_SYSCOUNTER
bool "Start SYSCOUNTER on driver init"
select NRF_GRTC_TIMER_CLOCK_MANAGEMENT
help
Start the SYSCOUNTER when initializing the GRTC. This should only be
handled by one processor in the system.
config NRF_GRTC_TIMER_CLOCK_MANAGEMENT
bool
help
Compile additional driver code for enabling management functionality of
the GRTC. Usually this is only needed by the processor that is starting
the SYSCOUNTER, but can be shared by multiple processors in the system.
config NRF_GRTC_SLEEP_MINIMUM_LATENCY
int
default 1000
depends on NRF_GRTC_SLEEP_ALLOWED
endif # NRF_GRTC_TIMER

View file

@ -3,7 +3,7 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
if NRF_RTC_TIMER
if NRF_RTC_TIMER || NRF_GRTC_TIMER
choice
prompt "Clock startup policy"
default SYSTEM_CLOCK_WAIT_FOR_STABILITY
@ -35,4 +35,4 @@ config SYSTEM_CLOCK_WAIT_FOR_STABILITY
driver initialization.
endchoice
endif # NRF_RTC_TIMER
endif # NRF_RTC_TIMER || NRF_GRTC_TIMER

View file

@ -0,0 +1,566 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <zephyr/drivers/timer/system_timer.h>
#include <zephyr/drivers/timer/nrf_grtc_timer.h>
#include <nrfx_grtc.h>
#include <zephyr/sys/math_extras.h>
#define CHAN_COUNT NRFX_GRTC_CONFIG_NUM_OF_CC_CHANNELS
#define EXT_CHAN_COUNT (CHAN_COUNT - 1)
/* The reset value of waketime is 1, which doesn't seem to work.
* It's being looked into, but for the time being use 4.
* Timeout must always be higher than waketime, so setting that to 5.
*/
#define WAKETIME (4)
#define TIMEOUT (WAKETIME + 1)
#define GRTC_NODE DT_NODELABEL(grtc)
#ifndef GRTC_SYSCOUNTERL_VALUE_Msk
#define GRTC_SYSCOUNTERL_VALUE_Msk GRTC_SYSCOUNTER_SYSCOUNTERL_VALUE_Msk
#endif
#ifndef GRTC_SYSCOUNTERH_VALUE_Msk
#define GRTC_SYSCOUNTERH_VALUE_Msk GRTC_SYSCOUNTER_SYSCOUNTERH_VALUE_Msk
#endif
#define MAX_CC_LATCH_WAIT_TIME_US 77
#define CYC_PER_TICK \
((uint64_t)sys_clock_hw_cycles_per_sec() / (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC)
#define COUNTER_SPAN (GRTC_SYSCOUNTERL_VALUE_Msk | ((uint64_t)GRTC_SYSCOUNTERH_VALUE_Msk << 32))
#define MAX_TICKS \
(((COUNTER_SPAN / CYC_PER_TICK) > INT_MAX) ? INT_MAX : (COUNTER_SPAN / CYC_PER_TICK))
#define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK)
/* The maximum SYSCOUNTERVALID settling time equals 1x32k cycles + 20x1MHz cycles. */
#define GRTC_SYSCOUNTERVALID_SETTLE_MAX_TIME_US 51
#if defined(CONFIG_TEST)
const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE);
#endif
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context);
static struct k_spinlock lock;
static uint64_t last_count;
static atomic_t int_mask;
static uint8_t ext_channels_allocated;
static nrfx_grtc_channel_t system_clock_channel_data = {
.handler = sys_clock_timeout_handler,
.p_context = NULL,
.channel = (uint8_t)-1,
};
#define IS_CHANNEL_ALLOWED_ASSERT(chan) \
__ASSERT_NO_MSG((NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK & (1UL << (chan))) && \
((chan) != system_clock_channel_data.channel))
static inline void grtc_active_set(void)
{
#if defined(NRF_GRTC_HAS_SYSCOUNTER_ARRAY) && (NRF_GRTC_HAS_SYSCOUNTER_ARRAY == 1)
nrfy_grtc_sys_counter_active_set(NRF_GRTC, true);
while (!nrfy_grtc_sys_conter_ready_check(NRF_GRTC)) {
}
#else
nrfy_grtc_sys_counter_active_state_request_set(NRF_GRTC, true);
k_busy_wait(GRTC_SYSCOUNTERVALID_SETTLE_MAX_TIME_US);
#endif
}
static inline void grtc_wakeup(void)
{
if (IS_ENABLED(CONFIG_NRF_GRTC_SLEEP_ALLOWED)) {
grtc_active_set();
}
}
static inline void grtc_sleep(void)
{
if (IS_ENABLED(CONFIG_NRF_GRTC_SLEEP_ALLOWED)) {
#if defined(NRF_GRTC_HAS_SYSCOUNTER_ARRAY) && (NRF_GRTC_HAS_SYSCOUNTER_ARRAY == 1)
nrfy_grtc_sys_counter_active_set(NRF_GRTC, false);
#else
nrfy_grtc_sys_counter_active_state_request_set(NRF_GRTC, false);
#endif
}
}
static inline uint64_t counter_sub(uint64_t a, uint64_t b)
{
return (a - b);
}
static inline uint64_t counter(void)
{
uint64_t now;
grtc_wakeup();
nrfx_grtc_syscounter_get(&now);
grtc_sleep();
return now;
}
static inline uint64_t get_comparator(uint32_t chan)
{
uint64_t cc;
nrfx_err_t result;
result = nrfx_grtc_syscounter_cc_value_read(chan, &cc);
if (result != NRFX_SUCCESS) {
if (result != NRFX_ERROR_INVALID_PARAM) {
return -EAGAIN;
}
return -EPERM;
}
return cc;
}
static void system_timeout_set(uint64_t value)
{
if (value <= NRF_GRTC_SYSCOUNTER_CCADD_MASK) {
grtc_wakeup();
nrfx_grtc_syscounter_cc_relative_set(&system_clock_channel_data, value, true,
NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
grtc_sleep();
} else {
nrfx_grtc_syscounter_cc_absolute_set(&system_clock_channel_data, value + counter(),
true);
}
}
static bool compare_int_lock(int32_t chan)
{
atomic_val_t prev = atomic_and(&int_mask, ~BIT(chan));
nrfx_grtc_syscounter_cc_int_disable(chan);
return prev & BIT(chan);
}
static void compare_int_unlock(int32_t chan, bool key)
{
if (key) {
atomic_or(&int_mask, BIT(chan));
nrfx_grtc_syscounter_cc_int_enable(chan);
}
}
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context)
{
ARG_UNUSED(id);
ARG_UNUSED(p_context);
uint64_t dticks;
uint64_t now = counter();
if (unlikely(now < cc_val)) {
return;
}
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
/* protection is not needed because we are in the GRTC interrupt
* so it won't get preempted by the interrupt.
*/
system_timeout_set(CYC_PER_TICK);
}
dticks = counter_sub(now, last_count) / CYC_PER_TICK;
last_count += dticks * CYC_PER_TICK;
sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? (int32_t)dticks : (dticks > 0));
}
int32_t z_nrf_grtc_timer_chan_alloc(void)
{
uint8_t chan;
nrfx_err_t err_code;
/* Prevent allocating all available channels - one must be left for system purposes. */
if (ext_channels_allocated >= EXT_CHAN_COUNT) {
return -ENOMEM;
}
err_code = nrfx_grtc_channel_alloc(&chan);
if (err_code != NRFX_SUCCESS) {
return -ENOMEM;
}
ext_channels_allocated++;
return (int32_t)chan;
}
void z_nrf_grtc_timer_chan_free(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
nrfx_err_t err_code = nrfx_grtc_channel_free(chan);
if (err_code == NRFX_SUCCESS) {
ext_channels_allocated--;
}
}
bool z_nrf_grtc_timer_compare_evt_check(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
uint32_t event_address = nrfx_grtc_event_compare_address_get(chan);
return *(volatile uint32_t *)event_address != 0;
}
uint32_t z_nrf_grtc_timer_compare_evt_address_get(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
return nrfx_grtc_event_compare_address_get(chan);
}
uint32_t z_nrf_grtc_timer_capture_task_address_get(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
return nrfx_grtc_capture_task_address_get(chan);
}
uint64_t z_nrf_grtc_timer_read(void)
{
return counter();
}
bool z_nrf_grtc_timer_compare_int_lock(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
return compare_int_lock(chan);
}
void z_nrf_grtc_timer_compare_int_unlock(int32_t chan, bool key)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
compare_int_unlock(chan, key);
}
uint64_t z_nrf_grtc_timer_compare_read(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
return get_comparator(chan);
}
static int compare_set_nolocks(int32_t chan, uint64_t target_time,
z_nrf_grtc_timer_compare_handler_t handler, void *user_data)
{
nrfx_err_t result;
__ASSERT_NO_MSG(target_time < COUNTER_SPAN);
nrfx_grtc_channel_t user_channel_data = {
.handler = handler,
.p_context = user_data,
.channel = chan,
};
result = nrfx_grtc_syscounter_cc_absolute_set(&user_channel_data, target_time, true);
if (result != NRFX_SUCCESS) {
return -EPERM;
}
return 0;
}
static int compare_set(int32_t chan, uint64_t target_time,
z_nrf_grtc_timer_compare_handler_t handler, void *user_data)
{
bool key = compare_int_lock(chan);
int ret = compare_set_nolocks(chan, target_time, handler, user_data);
compare_int_unlock(chan, key);
return ret;
}
int z_nrf_grtc_timer_set(int32_t chan, uint64_t target_time,
z_nrf_grtc_timer_compare_handler_t handler, void *user_data)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
return compare_set(chan, target_time, (nrfx_grtc_cc_handler_t)handler, user_data);
}
void z_nrf_grtc_timer_abort(int32_t chan)
{
IS_CHANNEL_ALLOWED_ASSERT(chan);
bool key = compare_int_lock(chan);
(void)nrfx_grtc_syscounter_cc_disable(chan);
compare_int_unlock(chan, key);
}
uint64_t z_nrf_grtc_timer_get_ticks(k_timeout_t t)
{
uint64_t curr_time;
int64_t curr_tick;
int64_t result;
int64_t abs_ticks;
curr_time = counter();
curr_tick = sys_clock_tick_get();
abs_ticks = Z_TICK_ABS(t.ticks);
if (abs_ticks < 0) {
/* relative timeout */
return (t.ticks > (int64_t)COUNTER_SPAN) ? -EINVAL : (curr_time + t.ticks);
}
/* absolute timeout */
result = abs_ticks - curr_tick;
if (result > (int64_t)COUNTER_SPAN) {
return -EINVAL;
}
return curr_time + result;
}
int z_nrf_grtc_timer_capture_prepare(int32_t chan)
{
nrfx_grtc_channel_t user_channel_data = {
.handler = NULL,
.p_context = NULL,
.channel = chan,
};
nrfx_err_t result;
IS_CHANNEL_ALLOWED_ASSERT(chan);
/* Set the CC value to mark channel as not triggered and also to enable it
* (makes CCEN=1). COUNTER_SPAN is used so as not to fire an event unnecessarily
* - it can be assumed that such a large value will never be reached.
*/
result = nrfx_grtc_syscounter_cc_absolute_set(&user_channel_data, COUNTER_SPAN, false);
if (result != NRFX_SUCCESS) {
return -EPERM;
}
return 0;
}
int z_nrf_grtc_timer_capture_read(int32_t chan, uint64_t *captured_time)
{
/* TODO: The implementation should probably go to nrfx_grtc and this
* should be just a wrapper for some nrfx_grtc_syscounter_capture_read.
*/
uint64_t capt_time;
IS_CHANNEL_ALLOWED_ASSERT(chan);
/* TODO: Use `nrfy_grtc_sys_counter_enable_check` when available (NRFX-2480) */
if (NRF_GRTC->CC[chan].CCEN == GRTC_CC_CCEN_ACTIVE_Enable) {
/* If the channel is enabled (.CCEN), it means that there was no capture
* triggering event.
*/
return -EBUSY;
}
capt_time = nrfy_grtc_sys_counter_cc_get(NRF_GRTC, chan);
__ASSERT_NO_MSG(capt_time < COUNTER_SPAN);
*captured_time = capt_time;
return 0;
}
#if defined(CONFIG_NRF_GRTC_SLEEP_ALLOWED)
int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us)
{
nrfx_err_t err_code;
static uint8_t systemoff_channel;
uint64_t now = counter();
/* Minimum time that ensures valid execution of system-off procedure. */
uint32_t minimum_latency_us = nrfy_grtc_waketime_get(NRF_GRTC) +
nrfy_grtc_timeout_get(NRF_GRTC) +
CONFIG_NRF_GRTC_SLEEP_MINIMUM_LATENCY;
uint32_t chan;
int ret;
if (minimum_latency_us > wake_time_us) {
return -EINVAL;
}
k_spinlock_key_t key = k_spin_lock(&lock);
err_code = nrfx_grtc_channel_alloc(&systemoff_channel);
if (err_code != NRFX_SUCCESS) {
k_spin_unlock(&lock, key);
return -ENOMEM;
}
(void)nrfx_grtc_syscounter_cc_int_disable(systemoff_channel);
ret = compare_set(systemoff_channel, now + wake_time_us, NULL, NULL);
if (ret < 0) {
k_spin_unlock(&lock, key);
return ret;
}
for (uint32_t grtc_chan_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
grtc_chan_mask > 0; grtc_chan_mask &= ~BIT(chan)) {
/* Clear all GRTC channels except the systemoff_channel. */
chan = u32_count_trailing_zeros(grtc_chan_mask);
if (chan != systemoff_channel) {
nrfx_grtc_syscounter_cc_disable(chan);
}
}
/* Make sure that wake_time_us was not triggered yet. */
if (nrfy_grtc_sys_counter_compare_event_check(NRF_GRTC, systemoff_channel)) {
k_spin_unlock(&lock, key);
return -EINVAL;
}
/* This mechanism ensures that stored CC value is latched. */
uint32_t wait_time =
nrfy_grtc_timeout_get(NRF_GRTC) * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 32768 +
MAX_CC_LATCH_WAIT_TIME_US;
k_busy_wait(wait_time);
#if NRF_GRTC_HAS_CLKSEL
nrfy_grtc_clksel_set(NRF_GRTC, NRF_GRTC_CLKSEL_LFXO);
#endif
k_spin_unlock(&lock, key);
return 0;
}
#endif /* CONFIG_NRF_GRTC_SLEEP_ALLOWED */
uint32_t sys_clock_cycle_get_32(void)
{
k_spinlock_key_t key = k_spin_lock(&lock);
uint32_t ret = (uint32_t)counter();
k_spin_unlock(&lock, key);
return ret;
}
uint64_t sys_clock_cycle_get_64(void)
{
k_spinlock_key_t key = k_spin_lock(&lock);
uint64_t ret = counter();
k_spin_unlock(&lock, key);
return ret;
}
uint32_t sys_clock_elapsed(void)
{
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return 0;
}
return (uint32_t)(counter_sub(counter(), last_count) / CYC_PER_TICK);
}
static int sys_clock_driver_init(void)
{
nrfx_err_t err_code;
#if defined(CONFIG_NRF_GRTC_TIMER_CLOCK_MANAGEMENT) && \
(defined(NRF_GRTC_HAS_CLKSEL) && (NRF_GRTC_HAS_CLKSEL == 1))
/* Use System LFCLK as the low-frequency clock source. */
nrfy_grtc_clksel_set(NRF_GRTC, NRF_GRTC_CLKSEL_LFCLK);
#endif
#if defined(CONFIG_NRF_GRTC_START_SYSCOUNTER)
/* SYSCOUNTER needs to be turned off before initialization. */
nrfy_grtc_sys_counter_set(NRF_GRTC, false);
nrfy_grtc_timeout_set(NRF_GRTC, TIMEOUT);
nrfy_grtc_waketime_set(NRF_GRTC, WAKETIME);
#endif /* CONFIG_NRF_GRTC_START_SYSCOUNTER */
IRQ_CONNECT(DT_IRQN(GRTC_NODE), DT_IRQ(GRTC_NODE, priority), nrfx_grtc_irq_handler, 0, 0);
err_code = nrfx_grtc_init(0);
if (err_code != NRFX_SUCCESS) {
return -EPERM;
}
#if defined(CONFIG_NRF_GRTC_START_SYSCOUNTER)
err_code = nrfx_grtc_syscounter_start(true, &system_clock_channel_data.channel);
if (err_code != NRFX_SUCCESS) {
return err_code == NRFX_ERROR_NO_MEM ? -ENOMEM : -EPERM;
}
if (IS_ENABLED(CONFIG_NRF_GRTC_SLEEP_ALLOWED)) {
nrfy_grtc_sys_counter_auto_mode_set(NRF_GRTC, false);
}
#else
err_code = nrfx_grtc_channel_alloc(&system_clock_channel_data.channel);
if (err_code != NRFX_SUCCESS) {
return -ENOMEM;
}
#endif /* CONFIG_NRF_GRTC_START_SYSCOUNTER */
if (!IS_ENABLED(CONFIG_NRF_GRTC_SLEEP_ALLOWED)) {
grtc_active_set();
}
int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
system_timeout_set(CYC_PER_TICK);
}
static const enum nrf_lfclk_start_mode mode =
IS_ENABLED(CONFIG_SYSTEM_CLOCK_NO_WAIT)
? CLOCK_CONTROL_NRF_LF_START_NOWAIT
: (IS_ENABLED(CONFIG_SYSTEM_CLOCK_WAIT_FOR_AVAILABILITY)
? CLOCK_CONTROL_NRF_LF_START_AVAILABLE
: CLOCK_CONTROL_NRF_LF_START_STABLE);
z_nrf_clock_control_lf_on(mode);
return 0;
}
void sys_clock_set_timeout(int32_t ticks, bool idle)
{
ARG_UNUSED(idle);
uint64_t cyc, off, now;
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return;
}
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks - 1, 0));
now = counter();
/* Round up to the next tick boundary */
off = (now - last_count) + (CYC_PER_TICK - 1);
off = (off / CYC_PER_TICK) * CYC_PER_TICK;
/* Get the offset with respect to now */
off -= (now - last_count);
/* Add the offset to get to the next tick boundary */
cyc = (uint64_t)ticks * CYC_PER_TICK + off;
/* Due to elapsed time the calculation above might produce a
* duration that laps the counter. Don't let it.
*/
if (cyc > MAX_CYCLES) {
cyc = MAX_CYCLES;
}
system_timeout_set(cyc == 0 ? 1 : cyc);
}
#if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT)
int nrf_grtc_timer_clock_driver_init(void)
{
return sys_clock_driver_init();
}
#else
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
#endif

View file

@ -0,0 +1,202 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_TIMER_NRF_GRTC_TIMER_H
#define ZEPHYR_INCLUDE_DRIVERS_TIMER_NRF_GRTC_TIMER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <zephyr/sys_clock.h>
/** @brief GRTC timer compare event handler.
*
* Called from GRTC ISR context when processing a compare event.
*
* @param id Compare channel ID.
*
* @param expire_time An actual absolute expiration time set for a compare
* channel. It can differ from the requested target time
* and the difference can be used to determine whether the
* time set was delayed.
*
* @param user_data Pointer to a user context data.
*/
typedef void (*z_nrf_grtc_timer_compare_handler_t)(int32_t id, uint64_t expire_time,
void *user_data);
/** @brief Allocate GRTC capture/compare channel.
*
* @retval >=0 Non-negative indicates allocated channel ID.
* @retval -ENOMEM if channel cannot be allocated.
*/
int32_t z_nrf_grtc_timer_chan_alloc(void);
/** @brief Free GRTC capture/compare channel.
*
* @param chan Previously allocated channel ID.
*/
void z_nrf_grtc_timer_chan_free(int32_t chan);
/** @brief Read current absolute time.
*
* @return Current absolute time.
*/
uint64_t z_nrf_grtc_timer_read(void);
/** @brief Check COMPARE event state.
*
* @param chan Channel ID.
*
* @retval true The event has been generated.
* @retval false The event has not been generated.
*/
bool z_nrf_grtc_timer_compare_evt_check(int32_t chan);
/** @brief Get COMPARE event register address.
*
* Address can be used for DPPIC.
*
* @param chan Channel ID.
*
* @return Register address.
*/
uint32_t z_nrf_grtc_timer_compare_evt_address_get(int32_t chan);
/** @brief Get CAPTURE task register address.
*
* Address can be used for DPPIC.
*
* @param chan Channel ID.
*
* @return Register address.
*/
uint32_t z_nrf_grtc_timer_capture_task_address_get(int32_t chan);
/** @brief Safely disable compare event interrupt.
*
* Function returns key indicating whether interrupt was already disabled.
*
* @param chan Channel ID.
*
* @return key passed to z_nrf_grtc_timer_compare_int_unlock().
*/
bool z_nrf_grtc_timer_compare_int_lock(int32_t chan);
/** @brief Safely enable compare event interrupt.
*
* Event interrupt is conditionally enabled based on @p key.
*
* @param chan Channel ID.
*
* @param key Key returned by z_nrf_grtc_timer_compare_int_lock().
*/
void z_nrf_grtc_timer_compare_int_unlock(int32_t chan, bool key);
/** @brief Read compare register value.
*
* @param chan Channel ID.
*
* @retval >=0 Positive is a Value set in the compare register
* @retval -EAGAIN if compare for given channel is not set.
* @retval -EPERM if either channel is unavailable or SYSCOUNTER is not running.
*/
uint64_t z_nrf_grtc_timer_compare_read(int32_t chan);
/** @brief Set compare channel to given value.
*
* @param chan Channel ID.
*
* @param target_time Absolute target time in ticks.
*
* @param handler User function called in the context of the GRTC interrupt.
*
* @param user_data Data passed to the handler.
*
* @retval 0 if the compare channel was set successfully.
* @retval -EPERM if either channel is unavailable or SYSCOUNTER is not running.
*/
int z_nrf_grtc_timer_set(int32_t chan, uint64_t target_time,
z_nrf_grtc_timer_compare_handler_t handler, void *user_data);
/** @brief Abort a timer requested with z_nrf_grtc_timer_set().
*
* If an abort operation is performed too late it is still possible for an event
* to fire. The user can detect a spurious event by comparing absolute time
* provided in callback and a result of z_nrf_grtc_timer_read(). During this
* operation interrupt from that compare channel is disabled. Other interrupts
* are not locked during this operation.
*
* @param chan Channel ID between 1 and CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT.
*/
void z_nrf_grtc_timer_abort(int32_t chan);
/** @brief Convert system clock time to GRTC ticks.
*
* @p t can be absolute or relative.
*
* @retval >=0 Positive value represents @p t in GRTC tick value.
* @retval -EINVAL if @p t is out of range.
*/
uint64_t z_nrf_grtc_timer_get_ticks(k_timeout_t t);
/** @brief Prepare channel for timestamp capture.
*
* Use z_nrf_grtc_timer_capture_task_address_get() to determine the register
* address that is used to trigger capture.
*
* @note Capture and compare are mutually exclusive features - they cannot be
* used simultaneously on the same GRTC channel.
*
* @param chan Channel ID.
*
* @retval 0 if the channel was successfully prepared.
* @retval -EPERM if either channel is unavailable or SYSCOUNTER is not running.
*/
int z_nrf_grtc_timer_capture_prepare(int32_t chan);
/** @brief Read timestamp value captured on the channel.
*
* The @p chan must be prepared using z_nrf_grtc_timer_capture_prepare().
*
* @param chan Channel ID.
*
* @param captured_time Pointer to store the value.
*
* @retval 0 if the timestamp was successfully caught and read.
* @retval -EBUSY if capturing has not been triggered.
*/
int z_nrf_grtc_timer_capture_read(int32_t chan, uint64_t *captured_time);
/** @brief Prepare GRTC as a source of wake up event and set the wake up time.
*
* @note Calling this function should be immediately followed by low-power mode enter
* (if it executed successfully).
*
* @param wake_time_us Relative wake up time in microseconds.
*
* @retval 0 if wake up time was successfully set.
* @retval -EPERM if the SYSCOUNTER is not running.
* @retval -ENOMEM if no available GRTC channels were found.
* @retval -EINVAL if @p wake_time_us is too low.
*/
int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us);
/**
* @brief Initialize the GRTC clock timer driver from an application-
* defined function.
*
* @retval 0 on success.
* @retval -errno Negative error code on failure.
*/
int nrf_grtc_timer_clock_driver_init(void);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_TIMER_NRF_GRTC_TIMER_H */