1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2020 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_smc.h"
10 #include "fsl_common.h"
11
12 /*******************************************************************************
13 * Definitions
14 ******************************************************************************/
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.smc"
18 #endif
19
20 #if (defined(FSL_FEATURE_SMC_REG_WIDTH) && (FSL_FEATURE_SMC_REG_WIDTH == 32U))
21 typedef uint32_t smc_reg_t;
22 #else
23 typedef uint8_t smc_reg_t;
24 #endif
25
26 typedef void (*smc_stop_ram_func_t)(void);
27
28 /*******************************************************************************
29 * Prototypes
30 ******************************************************************************/
31 static void SMC_EnterStopRamFunc(void);
32
33 /*******************************************************************************
34 * Variables
35 ******************************************************************************/
36 static uint32_t g_savedPrimask;
37
38 /*
39 * The ram function code is:
40 *
41 * uint32_t i;
42 * for (i=0; i<0x8; i++)
43 * {
44 * __NOP();
45 * }
46 * __DSB();
47 * __WFI();
48 * __ISB();
49 *
50 * When entring the stop modes, the flash prefetch might be interrupted, thus
51 * the prefetched code or data might be broken. To make sure the flash is idle
52 * when entring the stop modes, the code is moved to ram. And delay for a while
53 * before WFI to make sure previous flash prefetch is finished.
54 *
55 * Only need to do like this when code is in flash, if code is in rom or ram,
56 * this is not necessary.
57 */
58 static uint16_t s_stopRamFuncArray[] = {
59 0x2000, /* MOVS R0, #0 */
60 0x2808, /* CMP R0, #8 */
61 0xD202, /* BCS.N */
62 0xBF00, /* NOP */
63 0x1C40, /* ADDS R0, R0, #1 */
64 0xE7FA, /* B.N */
65 0xF3BF, 0x8F4F, /* DSB */
66 0xBF30, /* WFI */
67 0xF3BF, 0x8F6F, /* ISB */
68 0x4770, /* BX LR */
69 };
70
71 /*******************************************************************************
72 * Code
73 ******************************************************************************/
SMC_EnterStopRamFunc(void)74 static void SMC_EnterStopRamFunc(void)
75 {
76 uint32_t ramFuncEntry = ((uint32_t)(s_stopRamFuncArray)) + 1U;
77 smc_stop_ram_func_t stopRamFunc = (smc_stop_ram_func_t)ramFuncEntry;
78 stopRamFunc();
79 }
80
81 #if (defined(FSL_FEATURE_SMC_HAS_PARAM) && FSL_FEATURE_SMC_HAS_PARAM)
82 /*!
83 * brief Gets the SMC parameter.
84 *
85 * This function gets the SMC parameter including the enabled power mdoes.
86 *
87 * param base SMC peripheral base address.
88 * param param Pointer to the SMC param structure.
89 */
SMC_GetParam(SMC_Type * base,smc_param_t * param)90 void SMC_GetParam(SMC_Type *base, smc_param_t *param)
91 {
92 uint32_t reg = base->PARAM;
93 param->hsrunEnable = (bool)(reg & SMC_PARAM_EHSRUN_MASK);
94 param->llsEnable = (bool)(reg & SMC_PARAM_ELLS_MASK);
95 param->lls2Enable = (bool)(reg & SMC_PARAM_ELLS2_MASK);
96 param->vlls0Enable = (bool)(reg & SMC_PARAM_EVLLS0_MASK);
97 }
98 #endif /* FSL_FEATURE_SMC_HAS_PARAM */
99
100 /*!
101 * brief Prepares to enter stop modes.
102 *
103 * This function should be called before entering STOP/VLPS/LLS/VLLS modes.
104 */
SMC_PreEnterStopModes(void)105 void SMC_PreEnterStopModes(void)
106 {
107 g_savedPrimask = DisableGlobalIRQ();
108 __ISB();
109 }
110
111 /*!
112 * brief Recovers after wake up from stop modes.
113 *
114 * This function should be called after wake up from STOP/VLPS/LLS/VLLS modes.
115 * It is used with ref SMC_PreEnterStopModes.
116 */
SMC_PostExitStopModes(void)117 void SMC_PostExitStopModes(void)
118 {
119 EnableGlobalIRQ(g_savedPrimask);
120 __ISB();
121 }
122
123 /*!
124 * brief Prepares to enter wait modes.
125 *
126 * This function should be called before entering WAIT/VLPW modes.
127 */
SMC_PreEnterWaitModes(void)128 void SMC_PreEnterWaitModes(void)
129 {
130 g_savedPrimask = DisableGlobalIRQ();
131 __ISB();
132 }
133
134 /*!
135 * brief Recovers after wake up from stop modes.
136 *
137 * This function should be called after wake up from WAIT/VLPW modes.
138 * It is used with ref SMC_PreEnterWaitModes.
139 */
SMC_PostExitWaitModes(void)140 void SMC_PostExitWaitModes(void)
141 {
142 EnableGlobalIRQ(g_savedPrimask);
143 __ISB();
144 }
145
146 /*!
147 * brief Configures the system to RUN power mode.
148 *
149 * param base SMC peripheral base address.
150 * return SMC configuration error code.
151 */
SMC_SetPowerModeRun(SMC_Type * base)152 status_t SMC_SetPowerModeRun(SMC_Type *base)
153 {
154 smc_reg_t reg;
155
156 reg = base->PMCTRL;
157 /* configure Normal RUN mode */
158 reg &= ~(smc_reg_t)SMC_PMCTRL_RUNM_MASK;
159 reg |= ((smc_reg_t)kSMC_RunNormal << SMC_PMCTRL_RUNM_SHIFT);
160 base->PMCTRL = reg;
161
162 return kStatus_Success;
163 }
164
165 #if (defined(FSL_FEATURE_SMC_HAS_HIGH_SPEED_RUN_MODE) && FSL_FEATURE_SMC_HAS_HIGH_SPEED_RUN_MODE)
166 /*!
167 * brief Configures the system to HSRUN power mode.
168 *
169 * param base SMC peripheral base address.
170 * return SMC configuration error code.
171 */
SMC_SetPowerModeHsrun(SMC_Type * base)172 status_t SMC_SetPowerModeHsrun(SMC_Type *base)
173 {
174 smc_reg_t reg;
175
176 reg = (base->PMCTRL);
177 /* configure High Speed RUN mode */
178 reg &= ~(smc_reg_t)SMC_PMCTRL_RUNM_MASK;
179 reg |= ((smc_reg_t)kSMC_Hsrun << SMC_PMCTRL_RUNM_SHIFT);
180 base->PMCTRL = reg;
181
182 return kStatus_Success;
183 }
184 #endif /* FSL_FEATURE_SMC_HAS_HIGH_SPEED_RUN_MODE */
185
186 /*!
187 * brief Configures the system to WAIT power mode.
188 *
189 * param base SMC peripheral base address.
190 * return SMC configuration error code.
191 */
SMC_SetPowerModeWait(SMC_Type * base)192 status_t SMC_SetPowerModeWait(SMC_Type *base)
193 {
194 /* configure Normal Wait mode */
195 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
196 __DSB();
197 __WFI();
198 __ISB();
199
200 return kStatus_Success;
201 }
202
203 /*!
204 * brief Configures the system to Stop power mode.
205 *
206 * param base SMC peripheral base address.
207 * param option Partial Stop mode option.
208 * return SMC configuration error code.
209 */
SMC_SetPowerModeStop(SMC_Type * base,smc_partial_stop_option_t option)210 status_t SMC_SetPowerModeStop(SMC_Type *base, smc_partial_stop_option_t option)
211 {
212 smc_reg_t reg;
213
214 #if (defined(FSL_FEATURE_SMC_HAS_PSTOPO) && FSL_FEATURE_SMC_HAS_PSTOPO)
215 /* configure the Partial Stop mode in Normal Stop mode */
216 reg = base->STOPCTRL;
217 reg &= ~(smc_reg_t)SMC_STOPCTRL_PSTOPO_MASK;
218 reg |= ((smc_reg_t)option << SMC_STOPCTRL_PSTOPO_SHIFT);
219 base->STOPCTRL = reg;
220 #endif
221
222 /* configure Normal Stop mode */
223 reg = base->PMCTRL;
224 reg &= ~(smc_reg_t)SMC_PMCTRL_STOPM_MASK;
225 reg |= ((smc_reg_t)kSMC_StopNormal << SMC_PMCTRL_STOPM_SHIFT);
226 base->PMCTRL = reg;
227
228 /* Set the SLEEPDEEP bit to enable deep sleep mode (stop mode) */
229 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
230
231 /* read back to make sure the configuration valid before enter stop mode */
232 (void)base->PMCTRL;
233 SMC_EnterStopRamFunc();
234
235 /* check whether the power mode enter Stop mode succeed */
236 if (0U != (base->PMCTRL & SMC_PMCTRL_STOPA_MASK))
237 {
238 return kStatus_SMC_StopAbort;
239 }
240 else
241 {
242 return kStatus_Success;
243 }
244 }
245
246 /*!
247 * brief Configures the system to VLPR power mode.
248 *
249 * param base SMC peripheral base address.
250 * return SMC configuration error code.
251 */
SMC_SetPowerModeVlpr(SMC_Type * base,bool wakeupMode)252 status_t SMC_SetPowerModeVlpr(SMC_Type *base
253 #if (defined(FSL_FEATURE_SMC_HAS_LPWUI) && FSL_FEATURE_SMC_HAS_LPWUI)
254 ,
255 bool wakeupMode
256 #endif
257 )
258 {
259 smc_reg_t reg;
260
261 reg = (smc_reg_t)(base->PMCTRL);
262 #if (defined(FSL_FEATURE_SMC_HAS_LPWUI) && FSL_FEATURE_SMC_HAS_LPWUI)
263 /* configure whether the system remains in VLP mode on an interrupt */
264 if (wakeupMode)
265 {
266 /* exits to RUN mode on an interrupt */
267 reg |= SMC_PMCTRL_LPWUI_MASK;
268 }
269 else
270 {
271 /* remains in VLP mode on an interrupt */
272 reg &= ~(smc_reg_t)SMC_PMCTRL_LPWUI_MASK;
273 }
274 #endif /* FSL_FEATURE_SMC_HAS_LPWUI */
275
276 /* configure VLPR mode */
277 reg &= ~(smc_reg_t)SMC_PMCTRL_RUNM_MASK;
278 reg |= ((smc_reg_t)kSMC_RunVlpr << SMC_PMCTRL_RUNM_SHIFT);
279 base->PMCTRL = reg;
280
281 return kStatus_Success;
282 }
283
284 /*!
285 * brief Configures the system to VLPW power mode.
286 *
287 * param base SMC peripheral base address.
288 * return SMC configuration error code.
289 */
SMC_SetPowerModeVlpw(SMC_Type * base)290 status_t SMC_SetPowerModeVlpw(SMC_Type *base)
291 {
292 /* configure VLPW mode */
293 /* Set the SLEEPDEEP bit to enable deep sleep mode */
294 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
295 __DSB();
296 __WFI();
297 __ISB();
298
299 return kStatus_Success;
300 }
301
302 /*!
303 * brief Configures the system to VLPS power mode.
304 *
305 * param base SMC peripheral base address.
306 * return SMC configuration error code.
307 */
SMC_SetPowerModeVlps(SMC_Type * base)308 status_t SMC_SetPowerModeVlps(SMC_Type *base)
309 {
310 smc_reg_t reg;
311
312 /* configure VLPS mode */
313 reg = base->PMCTRL;
314 reg &= ~(smc_reg_t)SMC_PMCTRL_STOPM_MASK;
315 reg |= ((smc_reg_t)kSMC_StopVlps << SMC_PMCTRL_STOPM_SHIFT);
316 base->PMCTRL = reg;
317
318 /* Set the SLEEPDEEP bit to enable deep sleep mode */
319 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
320
321 /* read back to make sure the configuration valid before enter stop mode */
322 (void)base->PMCTRL;
323 SMC_EnterStopRamFunc();
324
325 /* check whether the power mode enter VLPS mode succeed */
326 if (0U != (base->PMCTRL & SMC_PMCTRL_STOPA_MASK))
327 {
328 return kStatus_SMC_StopAbort;
329 }
330 else
331 {
332 return kStatus_Success;
333 }
334 }
335
336 #if (defined(FSL_FEATURE_SMC_HAS_LOW_LEAKAGE_STOP_MODE) && FSL_FEATURE_SMC_HAS_LOW_LEAKAGE_STOP_MODE)
337 /*!
338 * brief Configures the system to LLS power mode.
339 *
340 * param base SMC peripheral base address.
341 * return SMC configuration error code.
342 */
SMC_SetPowerModeLls(SMC_Type * base,const smc_power_mode_lls_config_t * config)343 status_t SMC_SetPowerModeLls(SMC_Type *base
344 #if ((defined(FSL_FEATURE_SMC_HAS_LLS_SUBMODE) && FSL_FEATURE_SMC_HAS_LLS_SUBMODE) || \
345 (defined(FSL_FEATURE_SMC_HAS_LPOPO) && FSL_FEATURE_SMC_HAS_LPOPO))
346 ,
347 const smc_power_mode_lls_config_t *config
348 #endif
349 )
350 {
351 smc_reg_t reg;
352
353 /* configure to LLS mode */
354 reg = base->PMCTRL;
355 reg &= ~(smc_reg_t)SMC_PMCTRL_STOPM_MASK;
356 reg |= ((smc_reg_t)kSMC_StopLls << SMC_PMCTRL_STOPM_SHIFT);
357 base->PMCTRL = reg;
358
359 /* configure LLS sub-mode*/
360 #if (defined(FSL_FEATURE_SMC_HAS_LLS_SUBMODE) && FSL_FEATURE_SMC_HAS_LLS_SUBMODE)
361 reg = base->STOPCTRL;
362 reg &= ~(smc_reg_t)SMC_STOPCTRL_LLSM_MASK;
363 reg |= ((smc_reg_t)config->subMode << SMC_STOPCTRL_LLSM_SHIFT);
364 base->STOPCTRL = reg;
365 #endif /* FSL_FEATURE_SMC_HAS_LLS_SUBMODE */
366
367 #if (defined(FSL_FEATURE_SMC_HAS_LPOPO) && FSL_FEATURE_SMC_HAS_LPOPO)
368 if (config->enableLpoClock)
369 {
370 base->STOPCTRL &= ~(smc_reg_t)SMC_STOPCTRL_LPOPO_MASK;
371 }
372 else
373 {
374 base->STOPCTRL |= (smc_reg_t)SMC_STOPCTRL_LPOPO_MASK;
375 }
376 #endif /* FSL_FEATURE_SMC_HAS_LPOPO */
377
378 /* Set the SLEEPDEEP bit to enable deep sleep mode */
379 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
380
381 /* read back to make sure the configuration valid before enter stop mode */
382 (void)base->PMCTRL;
383 SMC_EnterStopRamFunc();
384
385 /* check whether the power mode enter LLS mode succeed */
386 if (0U != (base->PMCTRL & SMC_PMCTRL_STOPA_MASK))
387 {
388 return kStatus_SMC_StopAbort;
389 }
390 else
391 {
392 return kStatus_Success;
393 }
394 }
395 #endif /* FSL_FEATURE_SMC_HAS_LOW_LEAKAGE_STOP_MODE */
396
397 #if (defined(FSL_FEATURE_SMC_HAS_VERY_LOW_LEAKAGE_STOP_MODE) && FSL_FEATURE_SMC_HAS_VERY_LOW_LEAKAGE_STOP_MODE)
398 /*!
399 * brief Configures the system to VLLS power mode.
400 *
401 * param base SMC peripheral base address.
402 * param config The VLLS power mode configuration structure.
403 * return SMC configuration error code.
404 */
SMC_SetPowerModeVlls(SMC_Type * base,const smc_power_mode_vlls_config_t * config)405 status_t SMC_SetPowerModeVlls(SMC_Type *base, const smc_power_mode_vlls_config_t *config)
406 {
407 smc_reg_t reg;
408
409 #if (defined(FSL_FEATURE_SMC_HAS_PORPO) && FSL_FEATURE_SMC_HAS_PORPO)
410 #if (defined(FSL_FEATURE_SMC_USE_VLLSCTRL_REG) && FSL_FEATURE_SMC_USE_VLLSCTRL_REG) || \
411 (defined(FSL_FEATURE_SMC_USE_STOPCTRL_VLLSM) && FSL_FEATURE_SMC_USE_STOPCTRL_VLLSM) || \
412 (defined(FSL_FEATURE_SMC_HAS_LLS_SUBMODE) && FSL_FEATURE_SMC_HAS_LLS_SUBMODE)
413 if (config->subMode == kSMC_StopSub0)
414 #endif
415 {
416 /* configure whether the Por Detect work in Vlls0 mode */
417 if (true == config->enablePorDetectInVlls0)
418 {
419 #if (defined(FSL_FEATURE_SMC_USE_VLLSCTRL_REG) && FSL_FEATURE_SMC_USE_VLLSCTRL_REG)
420 base->VLLSCTRL &= ~(smc_reg_t)SMC_VLLSCTRL_PORPO_MASK;
421 #else
422 base->STOPCTRL &= ~(smc_reg_t)SMC_STOPCTRL_PORPO_MASK;
423 #endif
424 }
425 else
426 {
427 #if (defined(FSL_FEATURE_SMC_USE_VLLSCTRL_REG) && FSL_FEATURE_SMC_USE_VLLSCTRL_REG)
428 base->VLLSCTRL |= SMC_VLLSCTRL_PORPO_MASK;
429 #else
430 base->STOPCTRL |= SMC_STOPCTRL_PORPO_MASK;
431 #endif
432 }
433 }
434 #endif /* FSL_FEATURE_SMC_HAS_PORPO */
435
436 #if (defined(FSL_FEATURE_SMC_HAS_RAM2_POWER_OPTION) && FSL_FEATURE_SMC_HAS_RAM2_POWER_OPTION)
437 else if (config->subMode == kSMC_StopSub2)
438 {
439 /* configure whether the Por Detect work in Vlls0 mode */
440 if (true == config->enableRam2InVlls2)
441 {
442 #if (defined(FSL_FEATURE_SMC_USE_VLLSCTRL_REG) && FSL_FEATURE_SMC_USE_VLLSCTRL_REG)
443 base->VLLSCTRL |= SMC_VLLSCTRL_RAM2PO_MASK;
444 #else
445 base->STOPCTRL |= SMC_STOPCTRL_RAM2PO_MASK;
446 #endif
447 }
448 else
449 {
450 #if (defined(FSL_FEATURE_SMC_USE_VLLSCTRL_REG) && FSL_FEATURE_SMC_USE_VLLSCTRL_REG)
451 base->VLLSCTRL &= ~SMC_VLLSCTRL_RAM2PO_MASK;
452 #else
453 base->STOPCTRL &= ~(smc_reg_t)SMC_STOPCTRL_RAM2PO_MASK;
454 #endif
455 }
456 }
457 else
458 {
459 /* Add this to fix MISRA C2012 rule15.7 issue: Empty else without comment. */
460 }
461 #endif /* FSL_FEATURE_SMC_HAS_RAM2_POWER_OPTION */
462
463 /* configure to VLLS mode */
464 reg = base->PMCTRL;
465 reg &= ~(smc_reg_t)SMC_PMCTRL_STOPM_MASK;
466 reg |= ((smc_reg_t)kSMC_StopVlls << SMC_PMCTRL_STOPM_SHIFT);
467 base->PMCTRL = reg;
468
469 /* configure the VLLS sub-mode */
470 #if (defined(FSL_FEATURE_SMC_USE_VLLSCTRL_REG) && FSL_FEATURE_SMC_USE_VLLSCTRL_REG)
471 reg = base->VLLSCTRL;
472 reg &= ~(smc_reg_t)SMC_VLLSCTRL_VLLSM_MASK;
473 reg |= ((smc_reg_t)config->subMode << SMC_VLLSCTRL_VLLSM_SHIFT);
474 base->VLLSCTRL = reg;
475 #else
476 #if (defined(FSL_FEATURE_SMC_HAS_LLS_SUBMODE) && FSL_FEATURE_SMC_HAS_LLS_SUBMODE)
477 reg = base->STOPCTRL;
478 reg &= ~(smc_reg_t)SMC_STOPCTRL_LLSM_MASK;
479 reg |= ((smc_reg_t)config->subMode << SMC_STOPCTRL_LLSM_SHIFT);
480 base->STOPCTRL = reg;
481 #else
482 reg = base->STOPCTRL;
483 reg &= ~(smc_reg_t)SMC_STOPCTRL_VLLSM_MASK;
484 reg |= ((smc_reg_t)config->subMode << SMC_STOPCTRL_VLLSM_SHIFT);
485 base->STOPCTRL = reg;
486 #endif /* FSL_FEATURE_SMC_HAS_LLS_SUBMODE */
487 #endif
488
489 #if (defined(FSL_FEATURE_SMC_HAS_LPOPO) && FSL_FEATURE_SMC_HAS_LPOPO)
490 if (config->enableLpoClock)
491 {
492 base->STOPCTRL &= ~(smc_reg_t)SMC_STOPCTRL_LPOPO_MASK;
493 }
494 else
495 {
496 base->STOPCTRL |= SMC_STOPCTRL_LPOPO_MASK;
497 }
498 #endif /* FSL_FEATURE_SMC_HAS_LPOPO */
499
500 /* Set the SLEEPDEEP bit to enable deep sleep mode */
501 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
502
503 /* read back to make sure the configuration valid before enter stop mode */
504 (void)base->PMCTRL;
505 SMC_EnterStopRamFunc();
506
507 /* check whether the power mode enter LLS mode succeed */
508 if (0U != (base->PMCTRL & SMC_PMCTRL_STOPA_MASK))
509 {
510 return kStatus_SMC_StopAbort;
511 }
512 else
513 {
514 return kStatus_Success;
515 }
516 }
517 #endif /* FSL_FEATURE_SMC_HAS_VERY_LOW_LEAKAGE_STOP_MODE */
518