1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <zephyr/kernel.h>
7 #include <zephyr/sys/poweroff.h>
8 #include <zephyr/toolchain.h>
9 #include <zephyr/pm/policy.h>
10 #include <zephyr/arch/common/pm_s2ram.h>
11 #include <hal/nrf_resetinfo.h>
12 #include <hal/nrf_memconf.h>
13 #include <zephyr/cache.h>
14 #include <power.h>
15 #include <soc_lrcconf.h>
16 #include "soc.h"
17 #include "pm_s2ram.h"
18 
19 extern sys_snode_t soc_node;
20 
common_suspend(void)21 static void common_suspend(void)
22 {
23 	if (IS_ENABLED(CONFIG_DCACHE)) {
24 		/* Flush, disable and power down DCACHE */
25 		sys_cache_data_flush_all();
26 		sys_cache_data_disable();
27 		nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
28 							RAMBLOCK_CONTROL_BIT_DCACHE, false);
29 	}
30 
31 	if (IS_ENABLED(CONFIG_ICACHE)) {
32 		/* Disable and power down ICACHE */
33 		sys_cache_instr_disable();
34 		nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
35 							RAMBLOCK_CONTROL_BIT_ICACHE, false);
36 	}
37 
38 	soc_lrcconf_poweron_release(&soc_node, NRF_LRCCONF_POWER_DOMAIN_0);
39 }
40 
common_resume(void)41 static void common_resume(void)
42 {
43 	if (IS_ENABLED(CONFIG_ICACHE)) {
44 		/* Power up and re-enable ICACHE */
45 		nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
46 							RAMBLOCK_CONTROL_BIT_ICACHE, true);
47 		sys_cache_instr_enable();
48 	}
49 
50 	if (IS_ENABLED(CONFIG_DCACHE)) {
51 		/* Power up and re-enable DCACHE */
52 		nrf_memconf_ramblock_control_enable_set(NRF_MEMCONF, RAMBLOCK_POWER_ID,
53 							RAMBLOCK_CONTROL_BIT_DCACHE, true);
54 		sys_cache_data_enable();
55 	}
56 
57 	soc_lrcconf_poweron_request(&soc_node, NRF_LRCCONF_POWER_DOMAIN_0);
58 }
59 
nrf_poweroff(void)60 void nrf_poweroff(void)
61 {
62 	nrf_resetinfo_resetreas_local_set(NRF_RESETINFO, 0);
63 	nrf_resetinfo_restore_valid_set(NRF_RESETINFO, false);
64 
65 #if !defined(CONFIG_SOC_NRF54H20_CPURAD)
66 	/* Disable retention */
67 	nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_MAIN, false);
68 	nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false);
69 #endif
70 	common_suspend();
71 
72 	nrf_lrcconf_task_trigger(NRF_LRCCONF010, NRF_LRCCONF_TASK_SYSTEMOFFREADY);
73 
74 	__set_BASEPRI(0);
75 	__ISB();
76 	__DSB();
77 	__WFI();
78 
79 	CODE_UNREACHABLE;
80 }
81 
s2idle_enter(uint8_t substate_id)82 static void s2idle_enter(uint8_t substate_id)
83 {
84 	switch (substate_id) {
85 	case 0:
86 		/* Substate for idle with cache powered on - not implemented yet. */
87 		break;
88 	case 1: /* Substate for idle with cache retained - not implemented yet. */
89 		break;
90 	case 2: /* Substate for idle with cache disabled. */
91 #if !defined(CONFIG_SOC_NRF54H20_CPURAD)
92 		soc_lrcconf_poweron_request(&soc_node, NRF_LRCCONF_POWER_MAIN);
93 #endif
94 		common_suspend();
95 		break;
96 	default: /* Unknown substate. */
97 		return;
98 	}
99 
100 	__set_BASEPRI(0);
101 	__ISB();
102 	__DSB();
103 	__WFI();
104 }
105 
s2idle_exit(uint8_t substate_id)106 static void s2idle_exit(uint8_t substate_id)
107 {
108 	switch (substate_id) {
109 	case 0:
110 		/* Substate for idle with cache powered on - not implemented yet. */
111 		break;
112 	case 1: /* Substate for idle with cache retained - not implemented yet. */
113 		break;
114 	case 2: /* Substate for idle with cache disabled. */
115 		common_resume();
116 #if !defined(CONFIG_SOC_NRF54H20_CPURAD)
117 		soc_lrcconf_poweron_release(&soc_node, NRF_LRCCONF_POWER_MAIN);
118 #endif
119 	default: /* Unknown substate. */
120 		return;
121 	}
122 }
123 
124 #if defined(CONFIG_PM_S2RAM)
125 /* Resume domain after local suspend to RAM. */
s2ram_exit(void)126 static void s2ram_exit(void)
127 {
128 	common_resume();
129 #if !defined(CONFIG_SOC_NRF54H20_CPURAD)
130 	/* Re-enable domain retention. */
131 	nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, true);
132 #endif
133 }
134 
135 /* Function called during local domain suspend to RAM. */
sys_suspend_to_ram(void)136 static int sys_suspend_to_ram(void)
137 {
138 	/* Set intormation which is used on domain wakeup to determine if resume from RAM shall
139 	 * be performed.
140 	 */
141 	nrf_resetinfo_resetreas_local_set(NRF_RESETINFO,
142 					  NRF_RESETINFO_RESETREAS_LOCAL_UNRETAINED_MASK);
143 	nrf_resetinfo_restore_valid_set(NRF_RESETINFO, true);
144 
145 #if !defined(CONFIG_SOC_NRF54H20_CPURAD)
146 	/* Disable retention */
147 	nrf_lrcconf_retain_set(NRF_LRCCONF010, NRF_LRCCONF_POWER_DOMAIN_0, false);
148 #endif
149 	common_suspend();
150 
151 	__set_BASEPRI(0);
152 	__ISB();
153 	__DSB();
154 	__WFI();
155 	/*
156 	 * We might reach this point is k_cpu_idle returns (there is a pre sleep hook that
157 	 * can abort sleeping.
158 	 */
159 	return -EBUSY;
160 }
161 
s2ram_enter(void)162 static void s2ram_enter(void)
163 {
164 	/*
165 	 * Save the CPU context (including the return address),set the SRAM
166 	 * marker and power off the system.
167 	 */
168 	if (soc_s2ram_suspend(sys_suspend_to_ram)) {
169 		return;
170 	}
171 }
172 #endif /* defined(CONFIG_PM_S2RAM) */
173 
pm_state_set(enum pm_state state,uint8_t substate_id)174 void pm_state_set(enum pm_state state, uint8_t substate_id)
175 {
176 	if (state == PM_STATE_SUSPEND_TO_IDLE) {
177 		__disable_irq();
178 		s2idle_enter(substate_id);
179 		/* Resume here. */
180 		s2idle_exit(substate_id);
181 		__enable_irq();
182 	}
183 #if defined(CONFIG_PM_S2RAM)
184 	else if (state == PM_STATE_SUSPEND_TO_RAM) {
185 		__disable_irq();
186 		s2ram_enter();
187 		/* On resuming or error we return exactly *HERE* */
188 		s2ram_exit();
189 		__enable_irq();
190 	}
191 #endif
192 	else {
193 		k_cpu_idle();
194 	}
195 }
196 
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)197 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
198 {
199 	irq_unlock(0);
200 }
201