diff --git a/arch/arm/core/cortex_m/pm_s2ram.S b/arch/arm/core/cortex_m/pm_s2ram.S index aa715c8bbb9..1e5bca04fe2 100644 --- a/arch/arm/core/cortex_m/pm_s2ram.S +++ b/arch/arm/core/cortex_m/pm_s2ram.S @@ -14,12 +14,11 @@ #include #include -#define MAGIC (0xDABBAD00) - _ASM_FILE_PROLOGUE +GTEXT(pm_s2ram_mark_set) +GTEXT(pm_s2ram_mark_check_and_clear) GDATA(_cpu_context) -GDATA(marker) SECTION_FUNC(TEXT, arch_pm_s2ram_suspend) /* @@ -64,11 +63,9 @@ SECTION_FUNC(TEXT, arch_pm_s2ram_suspend) str r2, [r1, #___cpu_context_t_control_OFFSET] /* - * Set the marker to MAGIC value + * Mark entering suspend to RAM. */ - ldr r1, =marker - ldr r2, =MAGIC - str r2, [r1] + bl pm_s2ram_mark_set /* * Call the system_off function passed as parameter. This should never @@ -82,35 +79,29 @@ SECTION_FUNC(TEXT, arch_pm_s2ram_suspend) */ /* - * Reset the marker + * Reset the marking of suspend to RAM, return is ignored. */ - ldr r1, =marker - mov r2, #0x0 - str r2, [r1] + push {r0} + bl pm_s2ram_mark_check_and_clear + pop {r0} pop {r4-r12, lr} bx lr + GTEXT(arch_pm_s2ram_resume) SECTION_FUNC(TEXT, arch_pm_s2ram_resume) /* - * Check if the marker is set + * Check if reset occurred after suspending to RAM. */ - ldr r0, =marker - ldr r0, [r0] - ldr r1, =MAGIC - cmp r0, r1 + push {lr} + bl pm_s2ram_mark_check_and_clear + cmp r0, #0x1 + pop {lr} beq resume bx lr resume: - /* - * Reset the marker - */ - ldr r0, =marker - mov r1, #0x0 - str r1, [r0] - /* * Restore the CPU context */ diff --git a/arch/arm/core/cortex_m/pm_s2ram.c b/arch/arm/core/cortex_m/pm_s2ram.c index 7b499278dd2..5301720b7a0 100644 --- a/arch/arm/core/cortex_m/pm_s2ram.c +++ b/arch/arm/core/cortex_m/pm_s2ram.c @@ -9,12 +9,150 @@ #include +#define NVIC_MEMBER_SIZE(member) ARRAY_SIZE(((NVIC_Type *)0)->member) + +/* Currently dynamic regions are only used in case of userspace or stack guard and + * stack guard is not used by default on Cortex-M33 because there is a dedicated + * mechanism for stack overflow detection. Unless those condition change we don't + * need to store MPU content, it can just be reinitialized on resuming. + */ +#define MPU_USE_DYNAMIC_REGIONS IS_ENABLED(CONFIG_USERSPACE) || IS_ENABLED(CONFIG_MPU_STACK_GUARD) +#define MPU_NUM_REGIONS DT_PROP(DT_INST(0, arm_armv8m_mpu), arm_num_mpu_regions) + +#define MAGIC (0xDABBAD00) + +typedef struct { + /* NVIC components backuped into RAM. */ + uint32_t ISER[NVIC_MEMBER_SIZE(ISER)]; + uint32_t ISPR[NVIC_MEMBER_SIZE(ISPR)]; + uint32_t IABR[NVIC_MEMBER_SIZE(IABR)]; + uint8_t IPR[NVIC_MEMBER_SIZE(IPR)]; +} _nvic_context_t; + +typedef struct { + uint32_t RNR; + uint32_t RBAR[MPU_NUM_REGIONS]; + uint32_t RLAR[MPU_NUM_REGIONS]; + uint32_t MAIR0; + uint32_t MAIR1; + uint32_t CTRL; +} _mpu_context_t; + /** * CPU context for S2RAM */ __noinit _cpu_context_t _cpu_context; +/** + * NVIC context for S2RAM + */ + +struct backup { + _nvic_context_t nvic_context; + _mpu_context_t mpu_context; +}; + +static __noinit struct backup backup_data; + +extern void z_arm_configure_static_mpu_regions(void); +extern int z_arm_mpu_init(void); + +/* MPU registers cannot be simply copied because content of RBARx RLARx registers + * depends on region which is selected by RNR register. + */ +static void mpu_suspend(_mpu_context_t *backup) +{ + if (!MPU_USE_DYNAMIC_REGIONS) { + return; + } + + backup->RNR = MPU->RNR; + + for (uint8_t i = 0; i < MPU_NUM_REGIONS; i++) { + MPU->RNR = i; + backup->RBAR[i] = MPU->RBAR; + backup->RLAR[i] = MPU->RLAR; + } + backup->MAIR0 = MPU->MAIR0; + backup->MAIR1 = MPU->MAIR1; + backup->CTRL = MPU->CTRL; +} + +static void mpu_resume(_mpu_context_t *backup) +{ + if (!MPU_USE_DYNAMIC_REGIONS) { + z_arm_mpu_init(); + z_arm_configure_static_mpu_regions(); + return; + } + + uint32_t rnr = backup->RNR; + + for (uint8_t i = 0; i < MPU_NUM_REGIONS; i++) { + MPU->RNR = i; + MPU->RBAR = backup->RBAR[i]; + MPU->RLAR = backup->RLAR[i]; + } + + MPU->MAIR0 = backup->MAIR0; + MPU->MAIR1 = backup->MAIR1; + MPU->RNR = rnr; + MPU->CTRL = backup->CTRL; +} + +static void nvic_suspend(_nvic_context_t *backup) +{ + memcpy(backup->ISER, (uint32_t *)NVIC->ISER, sizeof(NVIC->ISER)); + memcpy(backup->ISPR, (uint32_t *)NVIC->ISPR, sizeof(NVIC->ISPR)); + memcpy(backup->IABR, (uint32_t *)NVIC->IABR, sizeof(NVIC->IABR)); + memcpy(backup->IPR, (uint32_t *)NVIC->IPR, sizeof(NVIC->IPR)); +} + +static void nvic_resume(_nvic_context_t *backup) +{ + memcpy((uint32_t *)NVIC->ISER, backup->ISER, sizeof(NVIC->ISER)); + memcpy((uint32_t *)NVIC->ISPR, backup->ISPR, sizeof(NVIC->ISPR)); + memcpy((uint32_t *)NVIC->IABR, backup->IABR, sizeof(NVIC->IABR)); + memcpy((uint32_t *)NVIC->IPR, backup->IPR, sizeof(NVIC->IPR)); +} + +int pm_s2ram_suspend(pm_s2ram_system_off_fn_t system_off) +{ + int ret; + + nvic_suspend(&backup_data.nvic_context); + mpu_suspend(&backup_data.mpu_context); + ret = arch_pm_s2ram_suspend(system_off); + if (ret < 0) { + return ret; + } + + mpu_resume(&backup_data.mpu_context); + nvic_resume(&backup_data.nvic_context); + + return ret; +} + +#ifndef CONFIG_PM_S2RAM_CUSTOM_MARKING /** * S2RAM Marker */ -__noinit uint32_t marker; +static __noinit uint32_t marker; + +void pm_s2ram_mark_set(void) +{ + marker = MAGIC; +} + +bool pm_s2ram_mark_check_and_clear(void) +{ + if (marker == MAGIC) { + marker = 0; + + return true; + } + + return false; +} + +#endif /* CONFIG_PM_S2RAM_CUSTOM_MARKING */ diff --git a/drivers/cache/Kconfig.nrf b/drivers/cache/Kconfig.nrf index 820445db432..c1cfc2c8c58 100644 --- a/drivers/cache/Kconfig.nrf +++ b/drivers/cache/Kconfig.nrf @@ -7,3 +7,9 @@ config CACHE_NRF_CACHE depends on HAS_NRFX && CACHE_MANAGEMENT help Enable support for the nRF cache driver. + +config CACHE_NRF_PATCH_LINEADDR + bool "Patch lineaddr" + default y if SOC_NRF54H20 + help + Manually set 28th bit in the LINEADDR in Trustzone Secure build. diff --git a/drivers/cache/cache_nrf.c b/drivers/cache/cache_nrf.c index 63d76a47d6e..3cf79897874 100644 --- a/drivers/cache/cache_nrf.c +++ b/drivers/cache/cache_nrf.c @@ -14,10 +14,8 @@ LOG_MODULE_REGISTER(cache_nrfx, CONFIG_CACHE_LOG_LEVEL); #define NRF_ICACHE NRF_CACHE #endif -#define CACHE_LINE_SIZE 32 #define CACHE_BUSY_RETRY_INTERVAL_US 10 -static struct k_spinlock lock; enum k_nrf_cache_op { /* @@ -55,7 +53,6 @@ static inline bool is_cache_busy(NRF_CACHE_Type *cache) static inline void wait_for_cache(NRF_CACHE_Type *cache) { while (is_cache_busy(cache)) { - k_busy_wait(CACHE_BUSY_RETRY_INTERVAL_US); } } @@ -68,14 +65,6 @@ static inline int _cache_all(NRF_CACHE_Type *cache, enum k_nrf_cache_op op) return -ENOTSUP; } - k_spinlock_key_t key = k_spin_lock(&lock); - - /* - * Invalidating the whole cache is dangerous. For good measure - * disable the cache. - */ - nrf_cache_disable(cache); - wait_for_cache(cache); switch (op) { @@ -102,66 +91,68 @@ static inline int _cache_all(NRF_CACHE_Type *cache, enum k_nrf_cache_op op) wait_for_cache(cache); - nrf_cache_enable(cache); - - k_spin_unlock(&lock, key); - return 0; } static inline void _cache_line(NRF_CACHE_Type *cache, enum k_nrf_cache_op op, uintptr_t line_addr) { - wait_for_cache(cache); + do { + wait_for_cache(cache); - nrf_cache_lineaddr_set(cache, line_addr); + nrf_cache_lineaddr_set(cache, line_addr); - switch (op) { + switch (op) { #if NRF_CACHE_HAS_TASK_CLEAN - case K_NRF_CACHE_CLEAN: - nrf_cache_task_trigger(cache, NRF_CACHE_TASK_CLEANLINE); - break; + case K_NRF_CACHE_CLEAN: + nrf_cache_task_trigger(cache, NRF_CACHE_TASK_CLEANLINE); + break; #endif - case K_NRF_CACHE_INVD: - nrf_cache_task_trigger(cache, NRF_CACHE_TASK_INVALIDATELINE); - break; + case K_NRF_CACHE_INVD: + nrf_cache_task_trigger(cache, NRF_CACHE_TASK_INVALIDATELINE); + break; #if NRF_CACHE_HAS_TASK_FLUSH - case K_NRF_CACHE_FLUSH: - nrf_cache_task_trigger(cache, NRF_CACHE_TASK_FLUSHLINE); - break; + case K_NRF_CACHE_FLUSH: + nrf_cache_task_trigger(cache, NRF_CACHE_TASK_FLUSHLINE); + break; #endif - default: - break; - } - - wait_for_cache(cache); + default: + break; + } + } while (nrf_cache_lineaddr_get(cache) != line_addr); } static inline int _cache_range(NRF_CACHE_Type *cache, enum k_nrf_cache_op op, void *addr, size_t size) { uintptr_t line_addr = (uintptr_t)addr; - uintptr_t end_addr = line_addr + size; + uintptr_t end_addr; + + /* Some SOCs has a bug that requires to set 28th bit in the address on + * Trustzone secure builds. + */ + if (IS_ENABLED(CONFIG_CACHE_NRF_PATCH_LINEADDR) && + !IS_ENABLED(CONFIG_TRUSTED_EXECUTION_NONSECURE)) { + line_addr |= BIT(28); + } + + end_addr = line_addr + size; /* * Align address to line size */ - line_addr &= ~(CACHE_LINE_SIZE - 1); + line_addr &= ~(CONFIG_DCACHE_LINE_SIZE - 1); do { - k_spinlock_key_t key = k_spin_lock(&lock); - _cache_line(cache, op, line_addr); - - k_spin_unlock(&lock, key); - - line_addr += CACHE_LINE_SIZE; - + line_addr += CONFIG_DCACHE_LINE_SIZE; } while (line_addr < end_addr); + wait_for_cache(cache); + return 0; } @@ -192,11 +183,6 @@ void cache_data_enable(void) nrf_cache_enable(NRF_DCACHE); } -void cache_data_disable(void) -{ - nrf_cache_disable(NRF_DCACHE); -} - int cache_data_flush_all(void) { #if NRF_CACHE_HAS_TASK_CLEAN @@ -206,6 +192,14 @@ int cache_data_flush_all(void) #endif } +void cache_data_disable(void) +{ + if (nrf_cache_enable_check(NRF_DCACHE)) { + (void)cache_data_flush_all(); + } + nrf_cache_disable(NRF_DCACHE); +} + int cache_data_invd_all(void) { return _cache_checks(NRF_DCACHE, K_NRF_CACHE_INVD, NULL, 0, false); diff --git a/drivers/timer/nrf_grtc_timer.c b/drivers/timer/nrf_grtc_timer.c index 51f00687e7e..102d17b09a5 100644 --- a/drivers/timer/nrf_grtc_timer.c +++ b/drivers/timer/nrf_grtc_timer.c @@ -352,7 +352,7 @@ int z_nrf_grtc_timer_capture_read(int32_t chan, uint64_t *captured_time) return 0; } -#if defined(CONFIG_NRF_GRTC_SLEEP_ALLOWED) +#if defined(CONFIG_NRF_GRTC_SLEEP_ALLOWED) && defined(CONFIG_NRF_GRTC_START_SYSCOUNTER) int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us) { nrfx_err_t err_code; diff --git a/dts/bindings/mmu_mpu/arm,armv6m-mpu.yaml b/dts/bindings/mmu_mpu/arm,armv6m-mpu.yaml index 16bdac395e4..00bc8927f58 100644 --- a/dts/bindings/mmu_mpu/arm,armv6m-mpu.yaml +++ b/dts/bindings/mmu_mpu/arm,armv6m-mpu.yaml @@ -10,3 +10,8 @@ include: base.yaml properties: reg: required: true + + arm,num-mpu-regions: + type: int + const: 8 + description: number of MPU regions supported by hardware diff --git a/dts/bindings/mmu_mpu/arm,armv7m-mpu.yaml b/dts/bindings/mmu_mpu/arm,armv7m-mpu.yaml index e0c6c3b4cec..09c2388a7a2 100644 --- a/dts/bindings/mmu_mpu/arm,armv7m-mpu.yaml +++ b/dts/bindings/mmu_mpu/arm,armv7m-mpu.yaml @@ -7,3 +7,7 @@ include: base.yaml properties: reg: required: true + + arm,num-mpu-regions: + type: int + description: number of MPU regions supported by hardware diff --git a/dts/bindings/mmu_mpu/arm,armv8.1m-mpu.yaml b/dts/bindings/mmu_mpu/arm,armv8.1m-mpu.yaml index 7800e36d7a1..407d23a8d78 100644 --- a/dts/bindings/mmu_mpu/arm,armv8.1m-mpu.yaml +++ b/dts/bindings/mmu_mpu/arm,armv8.1m-mpu.yaml @@ -7,3 +7,7 @@ include: base.yaml properties: reg: required: true + + arm,num-mpu-regions: + type: int + description: number of MPU regions supported by hardware diff --git a/dts/bindings/mmu_mpu/arm,armv8m-mpu.yaml b/dts/bindings/mmu_mpu/arm,armv8m-mpu.yaml index 0e7c12bf1b0..15c5085a33d 100644 --- a/dts/bindings/mmu_mpu/arm,armv8m-mpu.yaml +++ b/dts/bindings/mmu_mpu/arm,armv8m-mpu.yaml @@ -7,3 +7,7 @@ include: base.yaml properties: reg: required: true + + arm,num-mpu-regions: + type: int + description: number of MPU regions supported by hardware diff --git a/dts/common/nordic/nrf54h20.dtsi b/dts/common/nordic/nrf54h20.dtsi index 92f2576e733..5b96cf862b2 100644 --- a/dts/common/nordic/nrf54h20.dtsi +++ b/dts/common/nordic/nrf54h20.dtsi @@ -30,6 +30,14 @@ reg = <2>; device_type = "cpu"; clock-frequency = ; + #address-cells = <1>; + #size-cells = <1>; + + mpu: mpu@e000ed90 { + compatible = "arm,armv8m-mpu"; + reg = <0xe000ed90 0x40>; + arm,num-mpu-regions = <16>; + }; }; cpurad: cpu@3 { @@ -37,6 +45,14 @@ reg = <3>; device_type = "cpu"; clock-frequency = ; + #address-cells = <1>; + #size-cells = <1>; + + mpu: mpu@e000ed90 { + compatible = "arm,armv8m-mpu"; + reg = <0xe000ed90 0x40>; + arm,num-mpu-regions = <16>; + }; }; cpuppr: cpu@d { diff --git a/include/zephyr/arch/common/pm_s2ram.h b/include/zephyr/arch/common/pm_s2ram.h index 16443f2d105..358d917ec98 100644 --- a/include/zephyr/arch/common/pm_s2ram.h +++ b/include/zephyr/arch/common/pm_s2ram.h @@ -7,7 +7,6 @@ * * @brief public S2RAM APIs. * @defgroup pm_s2ram S2RAM APIs - * @ingroup subsys_pm * @{ */ @@ -57,6 +56,48 @@ typedef int (*pm_s2ram_system_off_fn_t)(void); */ int arch_pm_s2ram_suspend(pm_s2ram_system_off_fn_t system_off); +/** + * @brief Save CPU state on suspend + * + * This function is used on suspend-to-RAM (S2RAM) to save the CPU state in + * (retained) RAM before powering the system off using the provided function. + * This function is usually called from the PM subsystem / hooks. + * + * The CPU state consist of internal registers and peripherals like + * interrupt controller, memory controllers, etc. + * + * @param system_off Function to power off the system. + * + * @retval 0 The CPU context was successfully saved and restored. + * @retval -EBUSY The system is busy and cannot be suspended at this time. + * @retval -errno Negative errno code in case of failure. + */ +int pm_s2ram_suspend(pm_s2ram_system_off_fn_t system_off); + +/** + * @brief Mark that core is entering suspend-to-RAM state. + * + * Function is called when system state is stored to RAM, just before going to system + * off. + * + * Default implementation is setting a magic word in RAM. CONFIG_PM_S2RAM_CUSTOM_MARKING + * allows custom implementation. + */ +void pm_s2ram_mark_set(void); + +/** + * @brief Check suspend-to-RAM marking and clear its state. + * + * Function is used to determine if resuming after suspend-to-RAM shall be performed + * or standard boot code shall be executed. + * + * Default implementation is checking a magic word in RAM. CONFIG_PM_S2RAM_CUSTOM_MARKING + * allows custom implementation. + * + * @retval true if marking is found which indicates resuming after suspend-to-RAM. + * @retval false if marking is not found which indicates standard boot. + */ +bool pm_s2ram_mark_check_and_clear(void); /** * @} */ diff --git a/soc/nordic/common/poweroff.c b/soc/nordic/common/poweroff.c index 1c43da3e9ea..6f848fa170a 100644 --- a/soc/nordic/common/poweroff.c +++ b/soc/nordic/common/poweroff.c @@ -6,6 +6,7 @@ #include #include +#if !defined(CONFIG_SOC_SERIES_NRF54HX) #if defined(CONFIG_SOC_SERIES_NRF51X) || defined(CONFIG_SOC_SERIES_NRF52X) #include #else @@ -22,3 +23,4 @@ void z_sys_poweroff(void) CODE_UNREACHABLE; } +#endif /* !defined(CONFIG_SOC_SERIES_NRF54HX) */ diff --git a/soc/nordic/nrf54h/CMakeLists.txt b/soc/nordic/nrf54h/CMakeLists.txt index 77290a332da..f81bb4f4abd 100644 --- a/soc/nordic/nrf54h/CMakeLists.txt +++ b/soc/nordic/nrf54h/CMakeLists.txt @@ -4,6 +4,7 @@ if(CONFIG_ARM) zephyr_include_directories(.) zephyr_library_sources(soc.c) + zephyr_sources_ifdef(CONFIG_POWEROFF poweroff.c) endif() # Ensure that image size aligns with 16 bytes so that MRAMC finalizes all writes diff --git a/soc/nordic/nrf54h/Kconfig b/soc/nordic/nrf54h/Kconfig index 2b967bece7c..a003688bdd0 100644 --- a/soc/nordic/nrf54h/Kconfig +++ b/soc/nordic/nrf54h/Kconfig @@ -17,6 +17,8 @@ config SOC_NRF54H20_CPUAPP select CPU_HAS_ICACHE select CPU_HAS_FPU select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE + select HAS_PM + select HAS_POWEROFF config SOC_NRF54H20_CPURAD select ARM @@ -28,14 +30,8 @@ config SOC_NRF54H20_CPURAD select CPU_HAS_ICACHE select CPU_HAS_FPU select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE + select HAS_PM + select HAS_POWEROFF config SOC_NRF54H20_CPUPPR depends on RISCV_CORE_NORDIC_VPR - -if SOC_NRF54H20 - -config NRF_ENABLE_ICACHE - bool "Instruction cache (I-Cache)" - default y - -endif # SOC_NRF54H20 diff --git a/soc/nordic/nrf54h/poweroff.c b/soc/nordic/nrf54h/poweroff.c new file mode 100644 index 00000000000..f3d5c63480c --- /dev/null +++ b/soc/nordic/nrf54h/poweroff.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(NRF_APPLICATION) +#define RAMBLOCK_CONTROL_BIT_ICACHE 1 +#define RAMBLOCK_CONTROL_BIT_DCACHE 2 +#define RAMBLOCK_POWER_ID 0 +#define RAMBLOCK_CONTROL_OFF 0 +#elif defined(NRF_RADIOCORE) +#define RAMBLOCK_CONTROL_BIT_ICACHE 2 +#define RAMBLOCK_CONTROL_BIT_DCACHE 3 +#define RAMBLOCK_POWER_ID 0 +#define RAMBLOCK_CONTROL_OFF 0 +#else +#error "Unsupported domain." +#endif + +static void suspend_common(void) +{ + if (IS_ENABLED(CONFIG_DCACHE)) { + /* Flush, disable and power down DCACHE */ + sys_cache_data_flush_all(); + sys_cache_data_disable(); + nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, + RAMBLOCK_CONTROL_BIT_DCACHE, false); + } + + if (IS_ENABLED(CONFIG_ICACHE)) { + /* Flush, disable and power down ICACHE */ + sys_cache_instr_disable(); + nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, + RAMBLOCK_CONTROL_BIT_ICACHE, false); + } + + /* Disable retention */ + nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false); + + k_cpu_idle(); +} + +void z_sys_poweroff(void) +{ + nrf_resetinfo_resetreas_local_set(NRF_RESETINFO, 0); + nrf_resetinfo_restore_valid_set(NRF_RESETINFO, false); + + nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false); + + nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_SYSTEMOFFREADY); + + suspend_common(); + + CODE_UNREACHABLE; +} + +__weak void z_pm_soc_resume(void) +{ +} + +/* Resume domain after local suspend to RAM. */ +void z_pm_sys_resume(void) +{ + if (IS_ENABLED(CONFIG_ICACHE)) { + /* Power up and re-enable 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)) { + /* Power up and re-enable DCACHE */ + nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, + RAMBLOCK_CONTROL_BIT_DCACHE, true); + sys_cache_data_enable(); + } + + /* Re-enable domain retention. */ + nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, true); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true); + + + z_pm_soc_resume(); +} + +/* Function called during local domain suspend to RAM. */ +int z_pm_sys_suspend(void) +{ + /* Set intormation which is used on domain wakeup to determine if resume from RAM shall + * be performed. + */ + nrf_resetinfo_resetreas_local_set(NRF_RESETINFO, + NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK); + nrf_resetinfo_restore_valid_set(NRF_RESETINFO, true); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false); + + suspend_common(); + + /* + * We might reach this point is k_cpu_idle returns (there is a pre sleep hook that + * can abort sleeping. + */ + return -EBUSY; +} + +void pm_s2ram_mark_set(void) +{ + /* empty */ +} + +bool pm_s2ram_mark_check_and_clear(void) +{ + bool unretained_wake; + bool restore_valid; + + unretained_wake = nrf_resetinfo_resetreas_local_get(NRF_RESETINFO) & + NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK; + nrf_resetinfo_resetreas_local_set(NRF_RESETINFO, 0); + + restore_valid = nrf_resetinfo_restore_valid_check(NRF_RESETINFO); + nrf_resetinfo_restore_valid_set(NRF_RESETINFO, false); + + return (unretained_wake & restore_valid) ? true : false; +} + +#if CONFIG_PM + +__weak bool z_pm_do_suspend(void) +{ + return true; +} + +void do_suspend(void) +{ + if (!z_pm_do_suspend()) { + return; + } + + /* + * Save the CPU context (including the return address),set the SRAM + * marker and power off the system. + */ + (void)pm_s2ram_suspend(z_pm_sys_suspend); + + /* + * On resuming or error we return exactly *HERE* + */ + + z_pm_sys_resume(); +} + +void pm_state_set(enum pm_state state, uint8_t substate_id) +{ + if (state != PM_STATE_SUSPEND_TO_RAM) { + return; + } + + do_suspend(); +} + +void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) +{ + if (state != PM_STATE_SUSPEND_TO_RAM) { + return; + } + + irq_unlock(0); +} +#endif diff --git a/soc/nordic/nrf54h/soc.c b/soc/nordic/nrf54h/soc.c index 1d071f96b20..6ede3a02f01 100644 --- a/soc/nordic/nrf54h/soc.c +++ b/soc/nordic/nrf54h/soc.c @@ -46,8 +46,9 @@ static void power_domain_init(void) * WFI the power domain will be correctly retained. */ - nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, true); - nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, + !IS_ENABLED(CONFIG_HALTIUM_CPURAD)); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false); nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, true); nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true); @@ -82,9 +83,8 @@ static int trim_hsfll(void) static int nordicsemi_nrf54h_init(void) { -#if defined(CONFIG_NRF_ENABLE_ICACHE) sys_cache_instr_enable(); -#endif + sys_cache_data_enable(); power_domain_init(); diff --git a/subsys/pm/Kconfig b/subsys/pm/Kconfig index aeffe8c60ec..859ee6e25a7 100644 --- a/subsys/pm/Kconfig +++ b/subsys/pm/Kconfig @@ -37,6 +37,13 @@ config PM_S2RAM help This option enables suspend-to-RAM (S2RAM). +config PM_S2RAM_CUSTOM_MARKING + bool "Use custom marking functions" + depends on PM_S2RAM + help + By default a magic word in RAM is used to mark entering suspend-to-RAM. Enabling + this option allows custom implementation of functions which handle the marking. + config PM_NEED_ALL_DEVICES_IDLE bool "System Low Power Mode Needs All Devices Idle" depends on PM_DEVICE && !SMP diff --git a/west.yml b/west.yml index 39252a3dfa4..a81bbc3e666 100644 --- a/west.yml +++ b/west.yml @@ -183,7 +183,7 @@ manifest: groups: - hal - name: hal_nordic - revision: f8e4d73a78316ea9ef85f09f24a3a229e40c1a80 + revision: bdef8b66d5f59d95c09889918a04ddaecce322c8 path: modules/hal/nordic groups: - hal