diff --git a/drivers/timer/Kconfig.nrf_grtc b/drivers/timer/Kconfig.nrf_grtc index 082c15333dc..0daee3e4374 100644 --- a/drivers/timer/Kconfig.nrf_grtc +++ b/drivers/timer/Kconfig.nrf_grtc @@ -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 diff --git a/drivers/timer/nrf_grtc_timer.c b/drivers/timer/nrf_grtc_timer.c index 75278e104bd..3b69085ece3 100644 --- a/drivers/timer/nrf_grtc_timer.c +++ b/drivers/timer/nrf_grtc_timer.c @@ -368,17 +368,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) * @@ -387,34 +411,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)) { @@ -423,11 +447,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 / LFCLK_FREQUENCY_HZ + 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 */ diff --git a/include/zephyr/drivers/timer/nrf_grtc_timer.h b/include/zephyr/drivers/timer/nrf_grtc_timer.h index 5a51df21744..64c0e61653f 100644 --- a/include/zephyr/drivers/timer/nrf_grtc_timer.h +++ b/include/zephyr/drivers/timer/nrf_grtc_timer.h @@ -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 diff --git a/samples/boards/nordic/system_off/Kconfig.sysbuild b/samples/boards/nordic/system_off/Kconfig.sysbuild new file mode 100644 index 00000000000..f7d3e94e3e5 --- /dev/null +++ b/samples/boards/nordic/system_off/Kconfig.sysbuild @@ -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" diff --git a/samples/boards/nordic/system_off/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/samples/boards/nordic/system_off/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 00000000000..cc10b7737d1 --- /dev/null +++ b/samples/boards/nordic/system_off/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_SOC_NRF54H20_CPURAD_ENABLE=y diff --git a/samples/boards/nordic/system_off/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/boards/nordic/system_off/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..b93c888ad56 --- /dev/null +++ b/samples/boards/nordic/system_off/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,4 @@ +&uart136 { + zephyr,pm-device-runtime-auto; + disable-rx; +}; diff --git a/samples/boards/nordic/system_off/prj.conf b/samples/boards/nordic/system_off/prj.conf index 04450d5a22d..962ad80de21 100644 --- a/samples/boards/nordic/system_off/prj.conf +++ b/samples/boards/nordic/system_off/prj.conf @@ -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 diff --git a/samples/boards/nordic/system_off/remote/CMakeLists.txt b/samples/boards/nordic/system_off/remote/CMakeLists.txt new file mode 100644 index 00000000000..6bcf49327d1 --- /dev/null +++ b/samples/boards/nordic/system_off/remote/CMakeLists.txt @@ -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) diff --git a/samples/boards/nordic/system_off/remote/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/samples/boards/nordic/system_off/remote/boards/nrf54h20dk_nrf54h20_cpurad.overlay new file mode 100644 index 00000000000..5f27a7328bb --- /dev/null +++ b/samples/boards/nordic/system_off/remote/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -0,0 +1,5 @@ +&uart135 { + status = "disabled"; + zephyr,pm-device-runtime-auto; + disable-rx; +}; diff --git a/samples/boards/nordic/system_off/remote/prj.conf b/samples/boards/nordic/system_off/remote/prj.conf new file mode 100644 index 00000000000..0e338826c4d --- /dev/null +++ b/samples/boards/nordic/system_off/remote/prj.conf @@ -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 diff --git a/samples/boards/nordic/system_off/remote/src/main.c b/samples/boards/nordic/system_off/remote/src/main.c new file mode 100644 index 00000000000..9a2bd8725c6 --- /dev/null +++ b/samples/boards/nordic/system_off/remote/src/main.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + + if (IS_ENABLED(CONFIG_CONSOLE)) { + printf("%s system off demo. Ready for system off.\n", CONFIG_BOARD); + } + + sys_poweroff(); + + return 0; +} diff --git a/samples/boards/nordic/system_off/src/main.c b/samples/boards/nordic/system_off/src/main.c index b4c2eeb2b1b..9cf9ab24db6 100644 --- a/samples/boards/nordic/system_off/src/main.c +++ b/samples/boards/nordic/system_off/src/main.c @@ -19,6 +19,9 @@ #include #define NON_WAKEUP_RESET_REASON (RESET_PIN | RESET_SOFTWARE | RESET_POR | RESET_DEBUG) +#include +#include +#include #if defined(CONFIG_GRTC_WAKEUP_ENABLE) #include @@ -26,17 +29,21 @@ #endif #if defined(CONFIG_GPIO_WAKEUP_ENABLE) static const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios); +static const uint32_t port_sw0 = DT_PROP(DT_GPIO_CTLR_BY_IDX(DT_ALIAS(sw0), gpios, 0), port); #endif #if defined(CONFIG_LPCOMP_WAKEUP_ENABLE) static const struct device *comp_dev = DEVICE_DT_GET(DT_NODELABEL(comp)); #endif +static const struct gpio_dt_spec sw1 = GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios); +static const uint32_t port_sw1 = DT_PROP(DT_GPIO_CTLR_BY_IDX(DT_ALIAS(sw1), gpios, 0), port); + int print_reset_cause(uint32_t reset_cause) { int32_t ret; uint32_t supported; - ret = hwinfo_get_supported_reset_cause((uint32_t *) &supported); + ret = hwinfo_get_supported_reset_cause((uint32_t *)&supported); if (ret || !(reset_cause & supported)) { return -ENOTSUP; @@ -59,17 +66,40 @@ int main(void) { int rc; uint32_t reset_cause; - const struct device *const cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); - - if (!device_is_ready(cons)) { - printf("%s: device not ready.\n", cons->name); - return 0; + uint32_t nrf_pin_sw1 = 32 * port_sw1 + sw1.pin; + bool do_poweroff = true; + + if (nrf_gpio_pin_latch_get(nrf_pin_sw1)) { + nrf_gpio_pin_latch_clear(nrf_pin_sw1); +#if defined(CONFIG_SOC_NRF54H20) + /* Set gpio default if sense is set, prevent 300uA additional current after wakeup + * This is only needed for nrf54 series as PIN config is different from PAD config. + * After wakeup from system-off, SENSE bit is not reset but INPUT is. + */ + if (nrf_gpio_pin_sense_get(nrf_pin_sw1) != GPIO_PIN_CNF_SENSE_Disabled) { + nrf_gpio_cfg_default(nrf_pin_sw1); + } +#endif + do_poweroff = false; } printf("\n%s system off demo\n", CONFIG_BOARD); hwinfo_get_reset_cause(&reset_cause); rc = print_reset_cause(reset_cause); +#if defined(CONFIG_SOC_NRF54H20) +#if defined(CONFIG_GPIO_WAKEUP_ENABLE) + /* Set gpio default if sense is set, prevent 300uA additional current after wakeup + * This is only needed for nrf54 series as PIN config is different from PAD config. + * After wakeup from system-off, SENSE bit is not reset but INPUT is. + */ + uint32_t nrf_pin_sw0 = 32 * port_sw0 + sw0.pin; + if(nrf_gpio_pin_sense_get(nrf_pin_sw0) != GPIO_PIN_CNF_SENSE_Disabled) { + nrf_gpio_cfg_default(nrf_pin_sw0); + } +#endif +#endif + if (rc < 0) { printf("Reset cause not supported.\n"); return 0; @@ -97,13 +127,16 @@ int main(void) printf("Retained data not supported\n"); } + k_sleep(K_MSEC(4000)); + #if defined(CONFIG_GRTC_WAKEUP_ENABLE) + int err = z_nrf_grtc_wakeup_prepare(DEEP_SLEEP_TIME_S * USEC_PER_SEC); if (err < 0) { - printk("Unable to prepare GRTC as a wake up source (err = %d).\n", err); + printf("Unable to prepare GRTC as a wake up source (err = %d).\n", err); } else { - printk("Entering system off; wait %u seconds to restart\n", DEEP_SLEEP_TIME_S); + printf("Entering system off; wait %u seconds to restart\n", DEEP_SLEEP_TIME_S); } #endif #if defined(CONFIG_GPIO_WAKEUP_ENABLE) @@ -120,26 +153,67 @@ int main(void) return 0; } - printf("Entering system off; press sw0 to restart\n"); +#if defined(CONFIG_SOC_NRF54H20) + nrf_gpio_pin_retain_disable(nrf_pin_sw0); +#endif #endif #if defined(CONFIG_LPCOMP_WAKEUP_ENABLE) comparator_set_trigger(comp_dev, COMPARATOR_TRIGGER_BOTH_EDGES); comparator_trigger_is_pending(comp_dev); printf("Entering system off; change signal level at comparator input to restart\n"); #endif + rc = gpio_pin_configure_dt(&sw1, GPIO_INPUT); + if (rc < 0) { + printf("Could not configure sw1 GPIO (%d)\n", rc); + return 0; + } - rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND); + rc = gpio_pin_interrupt_configure_dt(&sw1, GPIO_INT_LEVEL_ACTIVE); if (rc < 0) { - printf("Could not suspend console (%d)\n", rc); + printf("Could not configure sw0 GPIO interrupt (%d)\n", rc); return 0; } +#if defined(CONFIG_SOC_NRF54H20) + nrf_gpio_pin_retain_disable(nrf_pin_sw1); +#endif + + if (do_poweroff) { + printf("Entering system off; press sw0 or sw1 to restart\n"); + } else { + printf("Button sw1 pressed, not entering system off\n"); + } + + if (IS_ENABLED(CONFIG_PM_DEVICE) && IS_ENABLED(CONFIG_SERIAL)) { + static const struct device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); + int err; + enum pm_device_state state; + + if (dev) { + do { + err = pm_device_state_get(dev, &state); + } while ((err == 0) && (state == PM_DEVICE_STATE_ACTIVE)); + } + } + if (IS_ENABLED(CONFIG_APP_USE_RETAINED_MEM)) { /* Update the retained state */ retained.off_count += 1; retained_update(); } + + if (do_poweroff) { +#if CONFIG_SOC_NRF54H20_CPUAPP + /* Local RAM0 (TCM) is currently not used so retention can be disabled. */ + nrf_memconf_ramblock_ret_mask_enable_set(NRF_MEMCONF, 0, RAMBLOCK_RET_MASK, false); + nrf_memconf_ramblock_ret_mask_enable_set(NRF_MEMCONF, 1, RAMBLOCK_RET_MASK, false); +#endif + sys_poweroff(); + } else { + k_sleep(K_FOREVER); + } + hwinfo_clear_reset_cause(); sys_poweroff(); diff --git a/samples/boards/nordic/system_off/sysbuild.cmake b/samples/boards/nordic/system_off/sysbuild.cmake new file mode 100644 index 00000000000..f22cab97904 --- /dev/null +++ b/samples/boards/nordic/system_off/sysbuild.cmake @@ -0,0 +1,22 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if("${SB_CONFIG_REMOTE_CORE_BOARD}" STREQUAL "") + message(FATAL_ERROR + "Target ${BOARD} not supported for this sample. " + "There is no remote board selected in Kconfig.sysbuild") +endif() + +set(REMOTE_APP remote) + +ExternalZephyrProject_Add( + APPLICATION ${REMOTE_APP} + SOURCE_DIR ${APP_DIR}/${REMOTE_APP} + BOARD ${SB_CONFIG_REMOTE_CORE_BOARD} +) + +# Add dependencies so that the remote sample will be built first +# This is required because some primary cores need information from the +# remote core's build, such as the output image's LMA +add_dependencies(${DEFAULT_IMAGE} ${REMOTE_APP}) +sysbuild_add_dependencies(CONFIGURE ${DEFAULT_IMAGE} ${REMOTE_APP}) diff --git a/soc/nordic/nrf54h/power.c b/soc/nordic/nrf54h/power.c index e1263be0d0e..6b16877e08a 100644 --- a/soc/nordic/nrf54h/power.c +++ b/soc/nordic/nrf54h/power.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -70,13 +71,22 @@ void nrf_poweroff(void) nrf_resetinfo_resetreas_local_set(NRF_RESETINFO, 0); nrf_resetinfo_restore_valid_set(NRF_RESETINFO, false); -#if !defined(CONFIG_SOC_NRF54H20_CPURAD) +#if defined(CONFIG_SOC_NRF54H20_CPURAD) + nrf_lrcconf_retain_set(NRF_LRCCONF010, + NRF_LRCCONF_POWER_DOMAIN_0 | NRF_LRCCONF_POWER_DOMAIN_1, false); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, + NRF_LRCCONF_POWER_DOMAIN_0 | NRF_LRCCONF_POWER_DOMAIN_1, false); +#else /* Disable retention */ nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false); nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false); #endif common_suspend(); + /* Disable all owned compare channels except the one used for wakeup from system-off. */ + z_nrf_grtc_timer_disable_owned_cc_channels(z_nrf_grtc_timer_wakeup_channel_get()); + + /* Indicate that we are ready for system off. */ nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_SYSTEMOFFREADY); __set_BASEPRI(0); diff --git a/soc/nordic/nrf54h/soc.c b/soc/nordic/nrf54h/soc.c index c996bc93e1b..e026f36c63c 100644 --- a/soc/nordic/nrf54h/soc.c +++ b/soc/nordic/nrf54h/soc.c @@ -148,7 +148,18 @@ void soc_early_init_hook(void) { int err; + if (IS_ENABLED(CONFIG_ICACHE)) { + nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, + RAMBLOCK_CONTROL_BIT_ICACHE, true); + } + sys_cache_instr_enable(); + + if (IS_ENABLED(CONFIG_DCACHE)) { + nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, + RAMBLOCK_CONTROL_BIT_DCACHE, true); + } + sys_cache_data_enable(); power_domain_init();