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