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