Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions drivers/timer/Kconfig.nrf_grtc
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ config NRF_GRTC_TIMER_AUTO_KEEP_ALIVE
This feature prevents the SYSCOUNTER from sleeping when any core is in
active state.

config NRF_GRTC_TIMER_USE_SYSTEM_CHANNEL_TO_WAKEUP_FROM_SYSTEMOFF
bool
depends on POWEROFF
default y if SOC_NRF54H20
help
Use system channel to schedule wake up time.

endif # NRF_GRTC_TIMER
68 changes: 50 additions & 18 deletions drivers/timer/nrf_grtc_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,17 +364,41 @@ uint64_t z_nrf_grtc_timer_startup_value_get(void)
return grtc_start_value;
}

#if defined(CONFIG_POWEROFF) && defined(CONFIG_NRF_GRTC_START_SYSCOUNTER)
#if defined(CONFIG_POWEROFF)
static int32_t wakeup_channel = -1;

int32_t z_nrf_grtc_timer_wakeup_channel_get(void)
{
return wakeup_channel;
}

void z_nrf_grtc_timer_disable_owned_cc_channels(int32_t reserved_channel)
{
uint32_t chan;

k_spinlock_key_t key = k_spin_lock(&lock);

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 reserved_channel. */
chan = u32_count_trailing_zeros(grtc_chan_mask);
if (chan != reserved_channel) {
nrfx_grtc_syscounter_cc_disable(chan);
}
}
k_spin_unlock(&lock, key);
}

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();
nrfx_grtc_sleep_config_t sleep_cfg;
int ret;
/* Minimum time that ensures valid execution of system-off procedure. */
uint32_t minimum_latency_us;
uint32_t chan;
int ret;

#if CONFIG_NRF_GRTC_START_SYSCOUNTER
nrfx_grtc_sleep_config_t sleep_cfg;

nrfx_grtc_sleep_configuration_get(&sleep_cfg);
minimum_latency_us = (sleep_cfg.waketime + sleep_cfg.timeout) *
Expand All @@ -383,34 +407,34 @@ int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us)
sleep_cfg.auto_mode = false;
nrfx_grtc_sleep_configure(&sleep_cfg);

#else
minimum_latency_us = CONFIG_NRF_GRTC_SYSCOUNTER_SLEEP_MINIMUM_LATENCY;
#endif
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 CONFIG_NRF_GRTC_TIMER_USE_SYSTEM_CHANNEL_TO_WAKEUP_FROM_SYSTEMOFF
systemoff_channel = system_clock_channel_data.channel;
#else
nrfx_err_t 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 * sys_clock_hw_cycles_per_sec() / USEC_PER_SEC, NULL,
NULL);
#endif
(void) nrfx_grtc_syscounter_cc_int_disable(systemoff_channel);
int64_t delay = now + wake_time_us * sys_clock_hw_cycles_per_sec() / USEC_PER_SEC;

ret = compare_set(systemoff_channel, delay, 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);
}
}
z_nrf_grtc_timer_disable_owned_cc_channels(systemoff_channel);

/* Make sure that wake_time_us was not triggered yet. */
if (nrfx_grtc_syscounter_compare_event_check(systemoff_channel)) {
Expand All @@ -419,11 +443,19 @@ int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us)
}

/* This mechanism ensures that stored CC value is latched. */
#if CONFIG_NRF_GRTC_START_SYSCOUNTER
uint32_t wait_time =
nrfy_grtc_timeout_get(NRF_GRTC) * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 32768 +
MAX_CC_LATCH_WAIT_TIME_US;
#else
uint32_t wait_time = MAX_CC_LATCH_WAIT_TIME_US;
#endif
/* Wait until the CC value is latched. */
k_busy_wait(wait_time);
k_spin_unlock(&lock, key);

wakeup_channel = systemoff_channel;

return 0;
}
#endif /* CONFIG_POWEROFF */
Expand Down
18 changes: 18 additions & 0 deletions include/zephyr/drivers/timer/nrf_grtc_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,24 @@ int z_nrf_grtc_timer_capture_prepare(int32_t chan);
*/
int z_nrf_grtc_timer_capture_read(int32_t chan, uint64_t *captured_time);

/** @brief Get the channel used for wakeup from system-off.
*
* @return Channel ID or -1 if no channel is reserved for that purpose.
*/
int32_t z_nrf_grtc_timer_wakeup_channel_get(void);

/** @brief Disable all owned compare channels except the one specified.
*
* This function is used when preparing the system to enter system-off mode.
* It disables all compare channels owned by the driver except the one
* specified by @p reserved_channel. The reserved channel is typically used
* for wake-up from system-off.
* @param reserved_channel Channel ID to remain enabled.
*
* @note Set @p reserved_channel to -1 to disable all owned channels.
*/
void z_nrf_grtc_timer_disable_owned_cc_channels(int32_t reserved_channel);

/** @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
Expand Down
9 changes: 9 additions & 0 deletions samples/boards/nordic/system_off/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0

source "share/sysbuild/Kconfig"

config REMOTE_CORE_BOARD
string
default "nrf54h20dk/nrf54h20/cpurad" if $(BOARD) = "nrf54h20dk"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

CONFIG_SOC_NRF54H20_CPURAD_ENABLE=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
&uart136 {
zephyr,pm-device-runtime-auto;
disable-rx;
};
3 changes: 3 additions & 0 deletions samples/boards/nordic/system_off/prj.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_UART_ASYNC_API=y
CONFIG_GPIO=y
CONFIG_CRC=y
CONFIG_POWEROFF=y
Expand Down
8 changes: 8 additions & 0 deletions samples/boards/nordic/system_off/remote/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(nrf_system_off_remote)

target_sources(app PRIVATE src/main.c)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
&uart135 {
status = "disabled";
zephyr,pm-device-runtime-auto;
disable-rx;
};
8 changes: 8 additions & 0 deletions samples/boards/nordic/system_off/remote/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_POWEROFF=y
CONFIG_UART_ASYNC_API=y
CONFIG_SERIAL=n
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
24 changes: 24 additions & 0 deletions samples/boards/nordic/system_off/remote/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/sys/poweroff.h>
#include <hal/nrf_memconf.h>
#include <zephyr/drivers/timer/nrf_grtc_timer.h>

int main(void)
{

if (IS_ENABLED(CONFIG_CONSOLE)) {
printf("%s system off demo. Ready for system off.\n", CONFIG_BOARD);
}

sys_poweroff();

return 0;
}
Loading