1 /*
2 * Copyright (c) 2021, NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/kernel.h>
7 #include <zephyr/device.h>
8 #include <zephyr/pm/pm.h>
9 #include <fsl_dcdc.h>
10 #include <fsl_gpc.h>
11 #include <zephyr/dt-bindings/pm/imx_spc.h>
12 #include "power.h"
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
16
17 /*
18 * NOTE: When multicore support in RT1170/1160 is properly implemented,
19 * power saving will improve when both cores request a transition to a low
20 * power mode
21 */
22
23 #ifdef CONFIG_CPU_CORTEX_M7
24 #define GPC_CPU_MODE_CTRL GPC_CPU_MODE_CTRL_0
25 #elif CONFIG_CPU_CORTEX_M4
26 #define GPC_CPU_MODE_CTRL GPC_CPU_MODE_CTRL_1
27 #else
28 #error "RT11xx power code supports M4 and M7 cores only"
29 #endif
30
31 /* Configure the set point mappings for Cortex M4 and M7 cores */
gpc_set_core_mappings(void)32 static void gpc_set_core_mappings(void)
33 {
34 uint8_t i, j;
35 uint32_t tmp;
36
37 #ifdef CONFIG_CPU_CORTEX_M7
38 uint8_t mapping[SET_POINT_COUNT][SET_POINT_COUNT] = CPU0_COMPATIBLE_SP_TABLE;
39 #elif CONFIG_CPU_CORTEX_M4
40 uint8_t mapping[SET_POINT_COUNT][SET_POINT_COUNT] = CPU1_COMPATIBLE_SP_TABLE;
41 #else
42 #error "RT11xx power code supports M4 and M7 cores only"
43 #endif
44 /* Cortex Set point mappings */
45 for (i = 0; i < SET_POINT_COUNT; i++) {
46 tmp = 0x0;
47 for (j = 0; j < SET_POINT_COUNT; j++) {
48 tmp |= mapping[i][j] << mapping[0][j];
49 }
50 GPC_CM_SetSetPointMapping(GPC_CPU_MODE_CTRL, mapping[i][0], tmp);
51 }
52 }
53
54 /* Configure GPC transition steps to enabled */
gpc_set_transition_flow(void)55 static void gpc_set_transition_flow(void)
56 {
57 gpc_tran_step_config_t step_cfg;
58
59 step_cfg.enableStep = true;
60 step_cfg.cntMode = kGPC_StepCounterDisableMode;
61
62 /* Cortex M7 */
63 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
64 kGPC_CM_SleepSsar, &step_cfg);
65 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
66 kGPC_CM_SleepLpcg, &step_cfg);
67 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
68 kGPC_CM_SleepPll, &step_cfg);
69 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
70 kGPC_CM_SleepIso, &step_cfg);
71 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
72 kGPC_CM_SleepReset, &step_cfg);
73 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
74 kGPC_CM_SleepPower, &step_cfg);
75 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
76 kGPC_CM_WakeupPower, &step_cfg);
77 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
78 kGPC_CM_WakeupReset, &step_cfg);
79 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
80 kGPC_CM_WakeupIso, &step_cfg);
81 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
82 kGPC_CM_WakeupPll, &step_cfg);
83 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
84 kGPC_CM_WakeupLpcg, &step_cfg);
85 GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL,
86 kGPC_CM_WakeupSsar, &step_cfg);
87
88 /* Enable all steps in flow of set point transition */
89 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
90 kGPC_SP_SsarSave, &step_cfg);
91 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
92 kGPC_SP_LpcgOff, &step_cfg);
93 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
94 kGPC_SP_GroupDown, &step_cfg);
95 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
96 kGPC_SP_RootDown, &step_cfg);
97 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
98 kGPC_SP_PllOff, &step_cfg);
99 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
100 kGPC_SP_IsoOn, &step_cfg);
101 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
102 kGPC_SP_ResetEarly, &step_cfg);
103 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
104 kGPC_SP_PowerOff, &step_cfg);
105 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
106 kGPC_SP_BiasOff, &step_cfg);
107 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
108 kGPC_SP_BandgapPllLdoOff, &step_cfg);
109 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
110 kGPC_SP_LdoPre, &step_cfg);
111 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
112 kGPC_SP_DcdcDown, &step_cfg);
113 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
114 kGPC_SP_DcdcUp, &step_cfg);
115 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
116 kGPC_SP_LdoPost, &step_cfg);
117 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
118 kGPC_SP_BandgapPllLdoOn, &step_cfg);
119 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
120 kGPC_SP_BiasOn, &step_cfg);
121 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
122 kGPC_SP_PowerOn, &step_cfg);
123 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
124 kGPC_SP_ResetLate, &step_cfg);
125 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
126 kGPC_SP_IsoOff, &step_cfg);
127 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
128 kGPC_SP_PllOn, &step_cfg);
129 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
130 kGPC_SP_RootUp, &step_cfg);
131 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
132 kGPC_SP_GroupUp, &step_cfg);
133 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
134 kGPC_SP_LpcgOn, &step_cfg);
135 GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL,
136 kGPC_SP_SsarRestore, &step_cfg);
137
138 /* Enable all steps in standby transition */
139 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
140 kGPC_STBY_LpcgIn, &step_cfg);
141 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
142 kGPC_STBY_PllIn, &step_cfg);
143 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
144 kGPC_STBY_BiasIn, &step_cfg);
145 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
146 kGPC_STBY_PldoIn, &step_cfg);
147 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
148 kGPC_STBY_BandgapIn, &step_cfg);
149 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
150 kGPC_STBY_LdoIn, &step_cfg);
151 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
152 kGPC_STBY_DcdcIn, &step_cfg);
153 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
154 kGPC_STBY_PmicIn, &step_cfg);
155 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
156 kGPC_STBY_PmicOut, &step_cfg);
157 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
158 kGPC_STBY_DcdcOut, &step_cfg);
159 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
160 kGPC_STBY_LdoOut, &step_cfg);
161 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
162 kGPC_STBY_BandgapOut, &step_cfg);
163 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
164 kGPC_STBY_PldoOut, &step_cfg);
165 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
166 kGPC_STBY_BiasOut, &step_cfg);
167 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
168 kGPC_STBY_PllOut, &step_cfg);
169 GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL,
170 kGPC_STBY_LpcgOut, &step_cfg);
171 }
172
gpc_configure_interrupts(void)173 static void gpc_configure_interrupts(void)
174 {
175 uint8_t i;
176 uint32_t irq = DT_IRQN(DT_INST(0, nxp_gpt_hw_timer));
177
178 /* Disable all GPC interrupt sources */
179 for (i = 0; i < GPC_CPU_MODE_CTRL_CM_IRQ_WAKEUP_MASK_COUNT; i++) {
180 GPC_CPU_MODE_CTRL->CM_IRQ_WAKEUP_MASK[i] |= 0xFFFFFFFF;
181 }
182
183 /* Enable GPT interrupt source for GPC- this is system timer */
184 GPC_CM_EnableIrqWakeup(GPC_CPU_MODE_CTRL, irq, true);
185 }
186
187 /* Initializes configuration for the GPC */
gpc_init(void)188 static void gpc_init(void)
189 {
190 /* Setup GPC set point mappings */
191 gpc_set_core_mappings();
192 /* Setup GPC set point transition flow */
193 gpc_set_transition_flow();
194 /* Allow GPC to disable ROSC */
195 GPC_SET_POINT_CTRL->SP_ROSC_CTRL = ~OSC_RC_16M_STBY_VAL;
196 /* Setup GPC interrupts */
197 gpc_configure_interrupts();
198 }
199
200 /* Initializes DCDC converter with power saving settings */
dcdc_init(void)201 static void dcdc_init(void)
202 {
203 dcdc_config_t dcdc_config;
204 dcdc_setpoint_config_t dcdc_setpoint_config;
205
206 dcdc_buck_mode_1P8_target_vol_t buck1_8_voltage[16] =
207 DCDC_1P8_BUCK_MODE_CONFIGURATION_TABLE;
208 dcdc_buck_mode_1P0_target_vol_t buck1_0_voltage[16] =
209 DCDC_1P0_BUCK_MODE_CONFIGURATION_TABLE;
210 dcdc_standby_mode_1P8_target_vol_t standby1_8_voltage[16] =
211 DCDC_1P8_STANDBY_MODE_CONFIGURATION_TABLE;
212 dcdc_standby_mode_1P0_target_vol_t standby1_0_voltage[16] =
213 DCDC_1P0_STANDBY_MODE_CONFIGURATION_TABLE;
214
215
216 DCDC_BootIntoDCM(DCDC);
217
218 dcdc_setpoint_config.enableDCDCMap = DCDC_ONOFF_SP_VAL;
219 dcdc_setpoint_config.enableDigLogicMap = DCDC_DIG_ONOFF_SP_VAL;
220 dcdc_setpoint_config.lowpowerMap = DCDC_LP_MODE_SP_VAL;
221 dcdc_setpoint_config.standbyMap = DCDC_ONOFF_STBY_VAL;
222 dcdc_setpoint_config.standbyLowpowerMap = DCDC_LP_MODE_STBY_VAL;
223 dcdc_setpoint_config.buckVDD1P8TargetVoltage = buck1_8_voltage;
224 dcdc_setpoint_config.buckVDD1P0TargetVoltage = buck1_0_voltage;
225 dcdc_setpoint_config.standbyVDD1P8TargetVoltage = standby1_8_voltage;
226 dcdc_setpoint_config.standbyVDD1P0TargetVoltage = standby1_0_voltage;
227 DCDC_SetPointInit(DCDC, &dcdc_setpoint_config);
228
229 DCDC_GetDefaultConfig(&dcdc_config);
230 dcdc_config.controlMode = kDCDC_SetPointControl;
231 DCDC_Init(DCDC, &dcdc_config);
232 }
233
system_enter_sleep(gpc_cpu_mode_t gpc_mode)234 static void system_enter_sleep(gpc_cpu_mode_t gpc_mode)
235 {
236 __ASSERT_NO_MSG(gpc_mode != kGPC_RunMode);
237
238 if (gpc_mode == kGPC_WaitMode) {
239 /* Clear SLEEPDEEP bit to enter WAIT mode*/
240 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
241 } else {
242 /* Set SLEEPDEEP bit to enter STOP mode */
243 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
244 }
245 /* When this function is entered the Kernel has disabled
246 * interrupts using BASEPRI register. We will clear BASEPRI, and use PRIMASK
247 * to disable interrupts, so that the WFI instruction works correctly.
248 */
249
250 /* Set PRIMASK */
251 __disable_irq();
252 /* Set BASEPRI to 0 */
253 irq_unlock(0);
254
255 /* WFI instruction will start entry into WAIT/STOP mode */
256 LOG_DBG("Entering LPM via WFI");
257 __WFI();
258 }
259
cpu_mode_transition(gpc_cpu_mode_t mode,bool enable_standby)260 void cpu_mode_transition(gpc_cpu_mode_t mode, bool enable_standby)
261 {
262 GPC_CM_SetNextCpuMode(GPC_CPU_MODE_CTRL, mode);
263 GPC_CM_EnableCpuSleepHold(GPC_CPU_MODE_CTRL, true);
264
265 /* Mask debugger wakeup */
266 GPC_CPU_MODE_CTRL->CM_NON_IRQ_WAKEUP_MASK |=
267 GPC_CPU_MODE_CTRL_CM_NON_IRQ_WAKEUP_MASK_EVENT_WAKEUP_MASK_MASK |
268 GPC_CPU_MODE_CTRL_CM_NON_IRQ_WAKEUP_MASK_DEBUG_WAKEUP_MASK_MASK;
269
270 if (enable_standby) {
271 /* Set standby request */
272 GPC_CM_RequestStandbyMode(GPC_CPU_MODE_CTRL, mode);
273 } else {
274 /* Clear standby request */
275 GPC_CM_ClearStandbyModeRequest(GPC_CPU_MODE_CTRL, mode);
276 }
277
278 /* Execute WFI- GPC will receive sleep request from CPU */
279 system_enter_sleep(mode);
280 }
281
282 /**
283 * SOC specific low power mode implementation
284 * Drop to lowest power state possible given system's request
285 */
pm_state_set(enum pm_state state,uint8_t substate_id)286 void pm_state_set(enum pm_state state, uint8_t substate_id)
287 {
288 ARG_UNUSED(state);
289
290 /* Extract set point and GPC mode from the substate ID */
291 uint8_t set_point = IMX_SPC(substate_id);
292 gpc_cpu_mode_t gpc_mode = IMX_GPC_MODE(substate_id);
293 uint8_t current_set_point = GPC_SP_GetCurrentSetPoint(GPC_SET_POINT_CTRL);
294
295 LOG_DBG("Switch to Set Point %d, GPC Mode %d requested", set_point, gpc_mode);
296 if (gpc_mode != kGPC_RunMode && (current_set_point != set_point)) {
297 /* Request set point transition at sleep */
298 GPC_CM_RequestSleepModeSetPointTransition(GPC_CPU_MODE_CTRL,
299 set_point, set_point, kGPC_CM_RequestPreviousSetpoint);
300 cpu_mode_transition(gpc_mode, true);
301 } else if (gpc_mode != kGPC_RunMode) {
302 /* Request CPU mode transition without set mode transition */
303 GPC_CM_RequestRunModeSetPointTransition(GPC_CPU_MODE_CTRL,
304 current_set_point);
305 cpu_mode_transition(gpc_mode, true);
306 }
307 }
308
pm_state_exit_post_ops(enum pm_state state,uint8_t substate_id)309 void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
310 {
311 ARG_UNUSED(state);
312 ARG_UNUSED(substate_id);
313
314 /* Clear PRIMASK */
315 __enable_irq();
316 LOG_DBG("Exiting LPM");
317 LOG_DBG("CM7 mode was %d", GPC_CM_GetPreviousCpuMode(GPC_CPU_MODE_CTRL_0));
318 LOG_DBG("CM4 mode was %d", GPC_CM_GetPreviousCpuMode(GPC_CPU_MODE_CTRL_1));
319 LOG_DBG("Previous set point was %d", GPC_SP_GetPreviousSetPoint(GPC_SET_POINT_CTRL));
320 }
321
322 /* Initialize RT11xx Power */
rt11xx_power_init(void)323 static int rt11xx_power_init(void)
324 {
325 /* Drop SOC target voltage to 1.0 V */
326 DCDC_SetVDD1P0BuckModeTargetVoltage(DCDC, kDCDC_1P0BuckTarget1P0V);
327 /* Initialize general power controller */
328 gpc_init();
329 /* Initialize dcdc */
330 dcdc_init();
331 return 0;
332 }
333
334 SYS_INIT(rt11xx_power_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
335