diff --git a/dts/vendor/nordic/nrf9280.dtsi b/dts/vendor/nordic/nrf9280.dtsi index 7d887b9a2092..9f64dc7058e9 100644 --- a/dts/vendor/nordic/nrf9280.dtsi +++ b/dts/vendor/nordic/nrf9280.dtsi @@ -83,14 +83,26 @@ clocks { hfxo: hfxo { compatible = "fixed-clock"; + status = "disabled"; + #clock-cells = <0>; + clock-frequency = ; + }; + + lfxo: lfxo { + compatible = "nordic,nrf54h-lfxo"; + status = "disabled"; #clock-cells = <0>; - clock-frequency = ; + clock-frequency = <32768>; }; fll16m: fll16m { - compatible = "fixed-clock"; + compatible = "nordic,nrf-fll16m"; #clock-cells = <0>; clock-frequency = ; + open-loop-accuracy-ppm = <20000>; + open-loop-startup-time-us = <200>; /* To be measured */ + clocks = <&hfxo>, <&lfxo>; + clock-names = "hfxo", "lfxo"; }; hsfll120: hsfll120 { @@ -103,6 +115,19 @@ 256000000 320000000>; }; + + lfclk: lfclk { + compatible = "nordic,nrf-lfclk"; + #clock-cells = <0>; + clock-frequency = <32768>; + status = "okay"; + lfrc-accuracy-ppm = <500>; + lflprc-accuracy-ppm = <1000>; + lfrc-startup-time-us = <200>; /* To be measured */ + lflprc-startup-time-us = <200>; /* To be measured */ + clocks = <&hfxo>, <&lfxo>; + clock-names = "hfxo", "lfxo"; + }; }; soc { @@ -758,6 +783,8 @@ reg = <0x99c000 0x1000>; status = "disabled"; cc-num = <16>; + clocks = <&lfclk>, <&fll16m>; + clock-names = "lfclock", "hfclock"; }; dppic133: dppic@9a1000 { diff --git a/soc/nordic/common/poweroff.c b/soc/nordic/common/poweroff.c index ebce3aae77cd..d11dff60e3f2 100644 --- a/soc/nordic/common/poweroff.c +++ b/soc/nordic/common/poweroff.c @@ -9,7 +9,7 @@ #if defined(CONFIG_SOC_SERIES_NRF51X) || defined(CONFIG_SOC_SERIES_NRF52X) #include -#elif defined(CONFIG_SOC_SERIES_NRF54HX) +#elif defined(CONFIG_SOC_SERIES_NRF54HX) || defined(CONFIG_SOC_SERIES_NRF92X) #include #else #include @@ -72,7 +72,7 @@ void z_sys_poweroff(void) #endif #if defined(CONFIG_SOC_SERIES_NRF51X) || defined(CONFIG_SOC_SERIES_NRF52X) nrf_power_system_off(NRF_POWER); -#elif defined(CONFIG_SOC_SERIES_NRF54HX) +#elif defined(CONFIG_SOC_SERIES_NRF54HX) || defined(CONFIG_SOC_SERIES_NRF92X) nrf_poweroff(); #else nrf_regulators_system_off(NRF_REGULATORS); diff --git a/soc/nordic/nrf92/CMakeLists.txt b/soc/nordic/nrf92/CMakeLists.txt index 1aa4723814f5..8e84392447d4 100644 --- a/soc/nordic/nrf92/CMakeLists.txt +++ b/soc/nordic/nrf92/CMakeLists.txt @@ -3,8 +3,18 @@ if(CONFIG_ARM) zephyr_library_sources(soc.c) + if(CONFIG_PM OR CONFIG_POWEROFF) + zephyr_library_sources(power.c) + zephyr_code_relocate( + FILES power.c + FILTER ".*\\.cache_retain_and_sleep" + LOCATION PMLocalRamfunc_TEXT + ) + endif() endif() +zephyr_library_sources_ifdef(CONFIG_PM_S2RAM pm_s2ram.c) + zephyr_include_directories(.) # Ensure that image size aligns with 16 bytes so that MRAMC finalizes all writes diff --git a/soc/nordic/nrf92/Kconfig b/soc/nordic/nrf92/Kconfig index eea33654f57d..a3b5f1219cdd 100644 --- a/soc/nordic/nrf92/Kconfig +++ b/soc/nordic/nrf92/Kconfig @@ -29,6 +29,9 @@ config SOC_NRF9230_ENGB_CPUAPP select NRFS_HAS_TEMP_SERVICE select NRFS_HAS_VBUS_DETECTOR_SERVICE select NRFS_TEMP_SERVICE_HAS_SUBSCRIPTION_SERVICE + select HAS_PM + select HAS_PM_S2RAM_CUSTOM_MARKING + select HAS_POWEROFF config SOC_NRF9230_ENGB_CPURAD select ARM @@ -48,6 +51,8 @@ config SOC_NRF9230_ENGB_CPURAD select NRFS_HAS_PMIC_SERVICE select NRFS_HAS_TEMP_SERVICE select NRFS_TEMP_SERVICE_HAS_SUBSCRIPTION_SERVICE + select HAS_PM + select HAS_POWEROFF config SOC_NRF9230_ENGB_CPUPPR select RISCV_CORE_NORDIC_VPR diff --git a/soc/nordic/nrf92/Kconfig.defconfig b/soc/nordic/nrf92/Kconfig.defconfig index fe88daacefcf..5fd3793c3394 100644 --- a/soc/nordic/nrf92/Kconfig.defconfig +++ b/soc/nordic/nrf92/Kconfig.defconfig @@ -38,6 +38,33 @@ config SPI_DW_HSSI config SPI_DW_ACCESS_WORD_ONLY default y if SPI_DW +if PM + +config PM_DEVICE + default y + +if LOG + +config LOG_PROCESS_THREAD_STACK_SIZE + default 1536 + +endif # LOG + +endif # PM + +if PM_DEVICE + +config PM_DEVICE_RUNTIME + default y + +config DEVICE_DEPS + default y + +config PM_DEVICE_POWER_DOMAIN + default y + +endif # PM_DEVICE + config SYS_CLOCK_HW_CYCLES_PER_SEC default $(dt_nodelabel_int_prop,grtc,clock-frequency) if NRF_GRTC_TIMER diff --git a/soc/nordic/nrf92/pm_s2ram.c b/soc/nordic/nrf92/pm_s2ram.c new file mode 100644 index 000000000000..9788c179e034 --- /dev/null +++ b/soc/nordic/nrf92/pm_s2ram.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "pm_s2ram.h" +#include "power.h" + +#include + +#define NVIC_MEMBER_SIZE(member) ARRAY_SIZE(((NVIC_Type *)0)->member) + +/* Coprocessor Power Control Register Definitions */ +#define SCnSCB_CPPWR_SU11_Pos 22U /*!< CPPWR: SU11 Position */ +#define SCnSCB_CPPWR_SU11_Msk (1UL << SCnSCB_CPPWR_SU11_Pos) /*!< CPPWR: SU11 Mask */ + +#define SCnSCB_CPPWR_SU10_Pos 20U /*!< CPPWR: SU10 Position */ +#define SCnSCB_CPPWR_SU10_Msk (1UL << SCnSCB_CPPWR_SU10_Pos) /*!< CPPWR: SU10 Mask */ + +/* 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) + +/* TODO: The num-mpu-regions property should be used. Needs to be added to dts bindings. */ +#define MPU_MAX_NUM_REGIONS 16 + +typedef struct { + /* NVIC components stored into RAM. */ + uint32_t ISER[NVIC_MEMBER_SIZE(ISER)]; + uint32_t ISPR[NVIC_MEMBER_SIZE(ISPR)]; + uint8_t IPR[NVIC_MEMBER_SIZE(IPR)]; +} _nvic_context_t; + +typedef struct { + uint32_t RNR; + uint32_t RBAR[MPU_MAX_NUM_REGIONS]; + uint32_t RLAR[MPU_MAX_NUM_REGIONS]; + uint32_t MAIR0; + uint32_t MAIR1; + uint32_t CTRL; +} _mpu_context_t; + +typedef struct { + uint32_t ICSR; + uint32_t VTOR; + uint32_t AIRCR; + uint32_t SCR; + uint32_t CCR; + uint32_t SHPR[12U]; + uint32_t SHCSR; + uint32_t CFSR; + uint32_t HFSR; + uint32_t DFSR; + uint32_t MMFAR; + uint32_t BFAR; + uint32_t AFSR; + uint32_t CPACR; +} _scb_context_t; + +#if defined(CONFIG_FPU) && !defined(CONFIG_FPU_SHARING) +typedef struct { + uint32_t FPCCR; + uint32_t FPCAR; + uint32_t FPDSCR; + uint32_t S[32]; +} _fpu_context_t; +#endif + +struct backup { + _nvic_context_t nvic_context; + _mpu_context_t mpu_context; + _scb_context_t scb_context; +#if defined(CONFIG_FPU) && !defined(CONFIG_FPU_SHARING) + _fpu_context_t fpu_context; +#endif +}; + +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_save(_mpu_context_t *backup) +{ + if (!MPU_USE_DYNAMIC_REGIONS) { + return; + } + + backup->RNR = MPU->RNR; + + for (uint8_t i = 0; i < MPU_MAX_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_restore(_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_MAX_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_save(_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->IPR, (uint32_t *)NVIC->IPR, sizeof(NVIC->IPR)); +} + +static void nvic_restore(_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->IPR, backup->IPR, sizeof(NVIC->IPR)); +} + +static void scb_save(_scb_context_t *backup) +{ + backup->ICSR = SCB->ICSR; + backup->VTOR = SCB->VTOR; + backup->AIRCR = SCB->AIRCR; + backup->SCR = SCB->SCR; + backup->CCR = SCB->CCR; + memcpy(backup->SHPR, (uint32_t *)SCB->SHPR, sizeof(SCB->SHPR)); + backup->SHCSR = SCB->SHCSR; + backup->CFSR = SCB->CFSR; + backup->HFSR = SCB->HFSR; + backup->DFSR = SCB->DFSR; + backup->MMFAR = SCB->MMFAR; + backup->BFAR = SCB->BFAR; + backup->AFSR = SCB->AFSR; + backup->CPACR = SCB->CPACR; +} + +static void scb_restore(_scb_context_t *backup) +{ + SCB->ICSR = backup->ICSR; + SCB->VTOR = backup->VTOR; + SCB->AIRCR = backup->AIRCR; + SCB->SCR = backup->SCR; + SCB->CCR = backup->CCR; + memcpy((uint32_t *)SCB->SHPR, backup->SHPR, sizeof(SCB->SHPR)); + SCB->SHCSR = backup->SHCSR; + SCB->CFSR = backup->CFSR; + SCB->HFSR = backup->HFSR; + SCB->DFSR = backup->DFSR; + SCB->MMFAR = backup->MMFAR; + SCB->BFAR = backup->BFAR; + SCB->AFSR = backup->AFSR; + SCB->CPACR = backup->CPACR; +} + +#if defined(CONFIG_FPU) +static void fpu_power_down(void) +{ + SCB->CPACR &= (~(CPACR_CP10_Msk | CPACR_CP11_Msk)); + SCnSCB->CPPWR |= (SCnSCB_CPPWR_SU11_Msk | SCnSCB_CPPWR_SU10_Msk); + __DSB(); + __ISB(); +} + +static void fpu_power_up(void) +{ + SCnSCB->CPPWR &= (~(SCnSCB_CPPWR_SU11_Msk | SCnSCB_CPPWR_SU10_Msk)); + SCB->CPACR |= (CPACR_CP10_Msk | CPACR_CP11_Msk); + __DSB(); + __ISB(); +} + +#if !defined(CONFIG_FPU_SHARING) +static void fpu_save(_fpu_context_t *backup) +{ + backup->FPCCR = FPU->FPCCR; + backup->FPCAR = FPU->FPCAR; + backup->FPDSCR = FPU->FPDSCR; + + __asm__ volatile("vstmia %0, {s0-s31}\n" : : "r"(backup->S) : "memory"); +} + +static void fpu_restore(_fpu_context_t *backup) +{ + FPU->FPCCR = backup->FPCCR; + FPU->FPCAR = backup->FPCAR; + FPU->FPDSCR = backup->FPDSCR; + + __asm__ volatile("vldmia %0, {s0-s31}\n" : : "r"(backup->S) : "memory"); +} +#endif /* !defined(CONFIG_FPU_SHARING) */ +#endif /* defined(CONFIG_FPU) */ + +#if DT_NODE_EXISTS(DT_NODELABEL(mcuboot_s2ram)) && \ + DT_NODE_HAS_COMPAT(DT_NODELABEL(mcuboot_s2ram), zephyr_memory_region) +/* Linker section name is given by `zephyr,memory-region` property of + * `zephyr,memory-region` compatible DT node with nodelabel `mcuboot_s2ram`. + */ +__attribute__(( + section(DT_PROP(DT_NODELABEL(mcuboot_s2ram), + zephyr_memory_region)))) volatile struct mcuboot_resume_s _mcuboot_resume; + +#define SET_MCUBOOT_RESUME_MAGIC() _mcuboot_resume.magic = MCUBOOT_S2RAM_RESUME_MAGIC +#else +#define SET_MCUBOOT_RESUME_MAGIC() +#endif + +int soc_s2ram_suspend(pm_s2ram_system_off_fn_t system_off) +{ + int ret; + + SET_MCUBOOT_RESUME_MAGIC(); + scb_save(&backup_data.scb_context); +#if defined(CONFIG_FPU) +#if !defined(CONFIG_FPU_SHARING) + fpu_save(&backup_data.fpu_context); +#endif + fpu_power_down(); +#endif + nvic_save(&backup_data.nvic_context); + mpu_save(&backup_data.mpu_context); + ret = arch_pm_s2ram_suspend(system_off); + /* Cache and FPU are powered down so power up is needed even if s2ram failed. */ + nrf_power_up_cache(); +#if defined(CONFIG_FPU) + fpu_power_up(); +#if !defined(CONFIG_FPU_SHARING) + /* Also the FPU content might be lost. */ + fpu_restore(&backup_data.fpu_context); +#endif +#endif + if (ret < 0) { + return ret; + } + + mpu_restore(&backup_data.mpu_context); + nvic_restore(&backup_data.nvic_context); + scb_restore(&backup_data.scb_context); + + return ret; +} + +void pm_s2ram_mark_set(void) +{ + /* empty */ +} + +bool pm_s2ram_mark_check_and_clear(void) +{ + bool restore_valid; + uint32_t reset_reason = nrf_resetinfo_resetreas_local_get(NRF_RESETINFO); + + if (reset_reason != NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK) { + return false; + } + 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 restore_valid; +} diff --git a/soc/nordic/nrf92/pm_s2ram.h b/soc/nordic/nrf92/pm_s2ram.h new file mode 100644 index 000000000000..0906010cbe08 --- /dev/null +++ b/soc/nordic/nrf92/pm_s2ram.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file Common pm_s2ram.h include for Nordic SoCs. + */ + +#ifndef _ZEPHYR_SOC_ARM_NORDIC_NRF_PM_S2RAM_H_ +#define _ZEPHYR_SOC_ARM_NORDIC_NRF_PM_S2RAM_H_ + +#define MCUBOOT_S2RAM_RESUME_MAGIC 0x75832419 + +struct mcuboot_resume_s { + uint32_t magic; /* magic value to identify valid structure */ +}; + +/** + * @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 soc_s2ram_suspend(pm_s2ram_system_off_fn_t system_off); + +#endif /* _ZEPHYR_SOC_ARM_NORDIC_NRF_PM_S2RAM_H_ */ diff --git a/soc/nordic/nrf92/power.c b/soc/nordic/nrf92/power.c new file mode 100644 index 000000000000..9ea232a0f4a3 --- /dev/null +++ b/soc/nordic/nrf92/power.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "soc.h" +#include "pm_s2ram.h" + +extern sys_snode_t soc_node; + +static void nrf_power_down_cache(void) +{ + static const uint32_t msk = + (IS_ENABLED(CONFIG_DCACHE) ? BIT(RAMBLOCK_CONTROL_BIT_DCACHE) : 0) | + (IS_ENABLED(CONFIG_ICACHE) ? BIT(RAMBLOCK_CONTROL_BIT_ICACHE) : 0); + + if (msk == 0) { + return; + } + + /* Functions are non-empty only if cache is enabled. + * Data cache disabling include flushing. + */ + sys_cache_data_disable(); + sys_cache_instr_disable(); + nrf_memconf_ramblock_control_mask_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, msk, false); +} + +void nrf_power_up_cache(void) +{ + static const uint32_t msk = + (IS_ENABLED(CONFIG_DCACHE) ? BIT(RAMBLOCK_CONTROL_BIT_DCACHE) : 0) | + (IS_ENABLED(CONFIG_ICACHE) ? BIT(RAMBLOCK_CONTROL_BIT_ICACHE) : 0); + + if (msk == 0) { + return; + } + + nrf_memconf_ramblock_control_mask_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID, msk, true); + sys_cache_instr_enable(); + sys_cache_data_enable(); +} + +static void common_suspend(void) +{ + soc_lrcconf_poweron_release(&soc_node, NRF_LRCCONF_POWER_DOMAIN_0); + nrf_power_down_cache(); +} + +static void common_resume(void) +{ + /* Common part does not include cache enabling. In case of s2ram it is done + * as early as possible to speed up the process. + */ + soc_lrcconf_poweron_request(&soc_node, NRF_LRCCONF_POWER_DOMAIN_0); +} + +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) + /* 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(); + + nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_SYSTEMOFFREADY); + + __set_BASEPRI(0); + __ISB(); + __DSB(); + __WFI(); + + CODE_UNREACHABLE; +} + +#if CONFIG_MCUBOOT +static __ramfunc +#else +static + __attribute__((__used__, noinline)) +#endif + void + cache_retain_and_sleep(void) +{ + nrf_cache_task_trigger(NRF_DCACHE, NRF_CACHE_TASK_SAVE); + nrf_cache_task_trigger(NRF_ICACHE, NRF_CACHE_TASK_SAVE); + while (nrf_cache_busy_check(NRF_DCACHE) || nrf_cache_busy_check(NRF_ICACHE)) { + } + + __set_BASEPRI(0); + __ISB(); + __DSB(); + __WFI(); + + nrf_cache_task_trigger(NRF_ICACHE, NRF_CACHE_TASK_RESTORE); + nrf_cache_task_trigger(NRF_DCACHE, NRF_CACHE_TASK_RESTORE); + while (nrf_cache_busy_check(NRF_DCACHE) || nrf_cache_busy_check(NRF_ICACHE)) { + } +} + +void s2idle_enter(uint8_t substate_id) +{ +#if !defined(CONFIG_SOC_NRF54H20_CPURAD) + soc_lrcconf_poweron_request(&soc_node, NRF_LRCCONF_POWER_MAIN); +#endif + switch (substate_id) { + case 0: + /* Substate for idle with cache powered on - not implemented yet. */ + break; + case 1: /* Substate for idle with cache retained. */ + soc_lrcconf_poweron_release(&soc_node, NRF_LRCCONF_POWER_DOMAIN_0); + nrf_soc_memconf_retain_set(true); + cache_retain_and_sleep(); + return; + case 2: /* Substate for idle with cache disabled. */ + common_suspend(); + break; + default: /* Unknown substate. */ + return; + } + + __set_BASEPRI(0); + __ISB(); + __DSB(); + __WFI(); +} + +static void s2idle_exit(uint8_t substate_id) +{ + switch (substate_id) { + case 0: + /* Substate for idle with cache powered on - not implemented yet. */ + break; + case 1: /* Substate for idle with cache retained. */ + nrf_soc_memconf_retain_set(false); + break; + case 2: /* Substate for idle with cache disabled. */ + nrf_power_up_cache(); + break; + default: /* Unknown substate. */ + return; + } + common_resume(); +#if !defined(CONFIG_SOC_NRF54H20_CPURAD) + soc_lrcconf_poweron_release(&soc_node, NRF_LRCCONF_POWER_MAIN); +#endif +} + +#if defined(CONFIG_PM_S2RAM) +/* Resume domain after local suspend to RAM. */ +static void s2ram_exit(void) +{ + common_resume(); +#if !defined(CONFIG_SOC_NRF54H20_CPURAD) + /* Re-enable domain retention. */ + nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true); +#endif +} + +/* Function called during local domain suspend to RAM. */ +static int sys_suspend_to_ram(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); + +#if !defined(CONFIG_SOC_NRF54H20_CPURAD) + /* Disable retention */ + nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false); +#endif + common_suspend(); + + __set_BASEPRI(0); + __ISB(); + __DSB(); + __WFI(); + /* + * We might reach this point is k_cpu_idle returns (there is a pre sleep hook that + * can abort sleeping. + */ + return -EBUSY; +} + +static void s2ram_enter(void) +{ + /* + * Save the CPU context (including the return address),set the SRAM + * marker and power off the system. + */ + if (soc_s2ram_suspend(sys_suspend_to_ram)) { + return; + } +} +#endif /* defined(CONFIG_PM_S2RAM) */ + +void pm_state_set(enum pm_state state, uint8_t substate_id) +{ + if (state == PM_STATE_SUSPEND_TO_IDLE) { + __disable_irq(); + sys_trace_idle(); + s2idle_enter(substate_id); + /* Resume here. */ + s2idle_exit(substate_id); + sys_trace_idle_exit(); + __enable_irq(); + } +#if defined(CONFIG_PM_S2RAM) + else if (state == PM_STATE_SUSPEND_TO_RAM) { + __disable_irq(); + sys_trace_idle(); + s2ram_enter(); + /* On resuming or error we return exactly *HERE* */ + s2ram_exit(); + sys_trace_idle_exit(); + __enable_irq(); + } +#endif + else { + k_cpu_idle(); + } +} + +void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id) +{ + irq_unlock(0); +} diff --git a/soc/nordic/nrf92/power.h b/soc/nordic/nrf92/power.h new file mode 100644 index 000000000000..87489574e727 --- /dev/null +++ b/soc/nordic/nrf92/power.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file Common power.h include for Nordic SoCs. + */ + +#ifndef _ZEPHYR_SOC_ARM_NORDIC_NRF_POWER_H_ +#define _ZEPHYR_SOC_ARM_NORDIC_NRF_POWER_H_ + +/** + * @brief Perform a power off. + * + * This function performs a power off of the core. + */ +void nrf_poweroff(void); + +/** + * @brief Power up and enable instruction and data cache. + */ +void nrf_power_up_cache(void); + +#endif /* _ZEPHYR_SOC_ARM_NORDIC_NRF_POWER_H_ */ diff --git a/soc/nordic/nrf92/soc.c b/soc/nordic/nrf92/soc.c index 3652b554711a..a8616a88dcf0 100644 --- a/soc/nordic/nrf92/soc.c +++ b/soc/nordic/nrf92/soc.c @@ -10,10 +10,18 @@ #include #include +#ifdef CONFIG_LOG_FRONTEND_STMESP +#include +#endif + +#include #include #include #include +#include #include +#include +#include LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL); @@ -23,15 +31,45 @@ LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL); #define HSFLL_NODE DT_NODELABEL(cpurad_hsfll) #endif -#define FICR_ADDR_GET(node_id, name) \ - DT_REG_ADDR(DT_PHANDLE_BY_NAME(node_id, nordic_ficrs, name)) + \ +#ifdef CONFIG_USE_DT_CODE_PARTITION +#define FLASH_LOAD_OFFSET DT_REG_ADDR(DT_CHOSEN(zephyr_code_partition)) +#elif defined(CONFIG_FLASH_LOAD_OFFSET) +#define FLASH_LOAD_OFFSET CONFIG_FLASH_LOAD_OFFSET +#endif + +#define PARTITION_IS_RUNNING_APP_PARTITION(label) \ + (DT_REG_ADDR(DT_NODELABEL(label)) <= FLASH_LOAD_OFFSET && \ + DT_REG_ADDR(DT_NODELABEL(label)) + DT_REG_SIZE(DT_NODELABEL(label)) > FLASH_LOAD_OFFSET) + +sys_snode_t soc_node; + +#define FICR_ADDR_GET(node_id, name) \ + DT_REG_ADDR(DT_PHANDLE_BY_NAME(node_id, nordic_ficrs, name)) + \ DT_PHA_BY_NAME(node_id, nordic_ficrs, name, offset) -#define SPU_INSTANCE_GET(p_addr) \ - ((NRF_SPU_Type *)((p_addr) & (ADDRESS_REGION_Msk | \ - ADDRESS_SECURITY_Msk | \ - ADDRESS_DOMAIN_Msk | \ - ADDRESS_BUS_Msk))) +#define SPU_INSTANCE_GET(p_addr) \ + ((NRF_SPU_Type *)((p_addr) & (ADDRESS_REGION_Msk | ADDRESS_SECURITY_Msk | \ + ADDRESS_DOMAIN_Msk | ADDRESS_BUS_Msk))) + +void nrf_soc_memconf_retain_set(bool enable) +{ + uint32_t ret_mask = BIT(RAMBLOCK_RET_BIT_ICACHE) | BIT(RAMBLOCK_RET_BIT_DCACHE); + + nrf_memconf_ramblock_ret_mask_enable_set(NRF_MEMCONF, 0, ret_mask, enable); + nrf_memconf_ramblock_ret_mask_enable_set(NRF_MEMCONF, 1, ret_mask, enable); + +#if defined(RAMBLOCK_RET2_MASK) + ret_mask = 0; +#if defined(RAMBLOCK_RET2_BIT_ICACHE) + ret_mask |= BIT(RAMBLOCK_RET2_BIT_ICACHE); +#endif +#if defined(RAMBLOCK_RET2_BIT_DCACHE) + ret_mask |= BIT(RAMBLOCK_RET2_BIT_DCACHE); +#endif + nrf_memconf_ramblock_ret2_mask_enable_set(NRF_MEMCONF, 0, ret_mask, enable); + nrf_memconf_ramblock_ret2_mask_enable_set(NRF_MEMCONF, 1, ret_mask, enable); +#endif /* defined(RAMBLOCK_RET2_MASK) */ +} static void power_domain_init(void) { @@ -46,11 +84,15 @@ 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_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, true); - nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true); + soc_lrcconf_poweron_request(&soc_node, NRF_LRCCONF_POWER_DOMAIN_0); + nrf_lrcconf_poweron_force_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false); + nrf_soc_memconf_retain_set(false); + nrf_memconf_ramblock_ret_mask_enable_set(NRF_MEMCONF, 0, RAMBLOCK_RET_MASK, true); + nrf_memconf_ramblock_ret_mask_enable_set(NRF_MEMCONF, 1, RAMBLOCK_RET_MASK, true); +#if defined(RAMBLOCK_RET2_MASK) + nrf_memconf_ramblock_ret2_mask_enable_set(NRF_MEMCONF, 0, RAMBLOCK_RET2_MASK, true); + nrf_memconf_ramblock_ret2_mask_enable_set(NRF_MEMCONF, 1, RAMBLOCK_RET2_MASK, true); +#endif } static int trim_hsfll(void) @@ -58,11 +100,9 @@ static int trim_hsfll(void) #if defined(HSFLL_NODE) NRF_HSFLL_Type *hsfll = (NRF_HSFLL_Type *)DT_REG_ADDR(HSFLL_NODE); - nrf_hsfll_trim_t trim = { - .vsup = sys_read32(FICR_ADDR_GET(HSFLL_NODE, vsup)), - .coarse = sys_read32(FICR_ADDR_GET(HSFLL_NODE, coarse)), - .fine = sys_read32(FICR_ADDR_GET(HSFLL_NODE, fine)) - }; + nrf_hsfll_trim_t trim = {.vsup = sys_read32(FICR_ADDR_GET(HSFLL_NODE, vsup)), + .coarse = sys_read32(FICR_ADDR_GET(HSFLL_NODE, coarse)), + .fine = sys_read32(FICR_ADDR_GET(HSFLL_NODE, fine))}; LOG_DBG("Trim: HSFLL VSUP: 0x%.8x", trim.vsup); LOG_DBG("Trim: HSFLL COARSE: 0x%.8x", trim.coarse); @@ -73,6 +113,8 @@ static int trim_hsfll(void) DT_PROP(DT_CLOCKS_CTLR(HSFLL_NODE), clock_frequency)); nrf_hsfll_trim_set(hsfll, &trim); + nrf_hsfll_task_trigger(hsfll, NRF_HSFLL_TASK_FREQ_CHANGE); + /* HSFLL task frequency change needs to be triggered twice to take effect.*/ nrf_hsfll_task_trigger(hsfll, NRF_HSFLL_TASK_FREQ_CHANGE); LOG_DBG("NRF_HSFLL->TRIM.VSUP = %d", hsfll->TRIM.VSUP); @@ -84,8 +126,20 @@ static int trim_hsfll(void) return 0; } +#if defined(CONFIG_ARM_ON_ENTER_CPU_IDLE_HOOK) +bool z_arm_on_enter_cpu_idle(void) +{ +#ifdef CONFIG_LOG_FRONTEND_STMESP + log_frontend_stmesp_pre_sleep(); +#endif + return true; +} +#endif + void soc_early_init_hook(void) { + int err; + sys_cache_instr_enable(); sys_cache_data_enable(); @@ -93,6 +147,9 @@ void soc_early_init_hook(void) trim_hsfll(); + err = dmm_init(); + __ASSERT_NO_MSG(err == 0); + #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(ccm030)) /* DMASEC is set to non-secure by default, which prevents CCM from * accessing secure memory. Change DMASEC to secure. diff --git a/soc/nordic/nrf92/soc.h b/soc/nordic/nrf92/soc.h index 4a495cfcaa13..30a77def979f 100644 --- a/soc/nordic/nrf92/soc.h +++ b/soc/nordic/nrf92/soc.h @@ -9,4 +9,38 @@ #include +#if defined(CONFIG_SOC_NRF9280_CPUAPP) +#define RAMBLOCK_CONTROL_BIT_ICACHE MEMCONF_POWER_CONTROL_MEM1_Pos +#define RAMBLOCK_CONTROL_BIT_DCACHE MEMCONF_POWER_CONTROL_MEM2_Pos +#define RAMBLOCK_POWER_ID 0 +#define RAMBLOCK_CONTROL_OFF 0 +#define RAMBLOCK_RET_MASK (MEMCONF_POWER_RET_MEM0_Msk) +#define RAMBLOCK_RET_BIT_ICACHE MEMCONF_POWER_RET_MEM1_Pos +#define RAMBLOCK_RET_BIT_DCACHE MEMCONF_POWER_RET_MEM2_Pos +#elif defined(CONFIG_SOC_NRF9280_CPURAD) +#define RAMBLOCK_CONTROL_BIT_ICACHE MEMCONF_POWER_CONTROL_MEM6_Pos +#define RAMBLOCK_CONTROL_BIT_DCACHE MEMCONF_POWER_CONTROL_MEM7_Pos +#define RAMBLOCK_POWER_ID 0 +#define RAMBLOCK_CONTROL_OFF 0 +#define RAMBLOCK_RET_MASK \ + (MEMCONF_POWER_RET_MEM0_Msk | MEMCONF_POWER_RET_MEM1_Msk | MEMCONF_POWER_RET_MEM2_Msk | \ + MEMCONF_POWER_RET_MEM3_Msk | MEMCONF_POWER_RET_MEM4_Msk | MEMCONF_POWER_RET_MEM5_Msk | \ + MEMCONF_POWER_RET_MEM8_Msk) +#define RAMBLOCK_RET2_MASK \ + (MEMCONF_POWER_RET2_MEM0_Msk | MEMCONF_POWER_RET2_MEM1_Msk | MEMCONF_POWER_RET2_MEM2_Msk | \ + MEMCONF_POWER_RET2_MEM3_Msk | MEMCONF_POWER_RET2_MEM4_Msk | MEMCONF_POWER_RET2_MEM5_Msk | \ + MEMCONF_POWER_RET2_MEM8_Msk) +#define RAMBLOCK_RET_BIT_ICACHE MEMCONF_POWER_RET_MEM6_Pos +#define RAMBLOCK_RET_BIT_DCACHE MEMCONF_POWER_RET_MEM7_Pos +#define RAMBLOCK_RET2_BIT_ICACHE MEMCONF_POWER_RET2_MEM6_Pos +#define RAMBLOCK_RET2_BIT_DCACHE MEMCONF_POWER_RET2_MEM7_Pos +#endif + +/** + * @brief Enable or disable the retention for cache RAM blocks. + * + * @param enable True if the retention is to be enabled, false otherwise. + */ +void nrf_soc_memconf_retain_set(bool enable); + #endif /* SOC_ARM_NORDIC_NRF_NRF9280_SOC_H_ */