1 /*
2  * Copyright 2021-2024 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 #include <stdarg.h>
8 
9 #include "fsl_pm_core.h"
10 #include "fsl_pm_device.h"
11 
12 /*
13  * $Coverage Justification Reference$
14  *
15  * $Justification pm_core_c_ref_1$
16  * This depends on device implementation. The "clean" function is not NULL with tested devices.
17  *
18  * $Justification pm_core_c_ref_2$
19  * Error doesn't happen in test case.
20  */
21 
22 /*******************************************************************************
23  * Variables
24  ******************************************************************************/
25 extern pm_device_option_t g_devicePMOption;
26 AT_ALWAYS_ON_DATA(static pm_handle_t *s_pmHandle);
27 
28 static uint32_t s_defaultPMIrqMask = 0UL;
29 
30 /*******************************************************************************
31  * Prototypes
32  ******************************************************************************/
33 static uint8_t PM_findDeepestState(uint64_t duration);
34 
35 #if (defined(FSL_PM_SUPPORT_NOTIFICATION) && FSL_PM_SUPPORT_NOTIFICATION)
36 static status_t PM_notifyPowerStateEntry(uint8_t powerState);
37 static status_t PM_notifyPowerStateExit(uint8_t powerState);
38 #endif /* FSL_PM_SUPPORT_NOTIFICATION */
39 
40 static void PM_SetAllowedLowestPowerMode(void);
41 
42 /*******************************************************************************
43  * Code
44  ******************************************************************************/
45 
46 /***************************************************************
47  * Private Funtions
48  ***************************************************************/
PM_findDeepestState(uint64_t duration)49 static uint8_t PM_findDeepestState(uint64_t duration)
50 {
51     uint8_t i              = 0U;
52     uint8_t ret            = 0xFFU;
53     uint8_t j              = 0U;
54     pm_state_t *state      = NULL;
55     bool stateSatisfy      = false;
56     pm_state_t *stateArray = s_pmHandle->deviceOption->states;
57     uint8_t stateCount     = (s_pmHandle->deviceOption->stateCount);
58     pm_resc_mask_t tmpSoftRescMask;
59 
60     for (i = stateCount; i >= 1U; i--)
61     {
62         state = &stateArray[(i - 1U)];
63 
64         stateSatisfy = false;
65         /*
66          * The next power state's exit latency should be smaller than low power duration.
67          * And the next power state should satisfy the whole system's constraints.
68          */
69         if (duration == 0U)
70         {
71             stateSatisfy = true;
72         }
73         else
74         {
75             if (state->exitLatency < duration)
76             {
77                 /* If the total latency is less than the duration, then this state
78                  * satisfies the duration constraints */
79                 stateSatisfy = true;
80             }
81         }
82 
83         /* If the state satisfies the duration constraints, check the other system
84          * constraints */
85         if (stateSatisfy == true)
86         {
87             for (j = 0U; j < PM_RESC_MASK_ARRAY_SIZE; j++)
88             {
89                 if ((s_pmHandle->resConstraintMask.rescMask[j] & state->fixConstraintsMask.rescMask[j]) == 0UL)
90                 {
91                     tmpSoftRescMask.rescMask[j] =
92                         state->varConstraintsMask.rescMask[j] & (s_pmHandle->resConstraintMask.rescMask[j]);
93                 }
94                 else
95                 {
96                     stateSatisfy = false;
97                     break;
98                 }
99             }
100         }
101 
102         if (stateSatisfy && ((i - 1U) <= s_pmHandle->powerModeConstraint))
103         {
104             (void)memcpy(&s_pmHandle->softConstraints, &tmpSoftRescMask, sizeof(pm_resc_mask_t));
105             ret = (i - 1U);
106             break;
107         }
108     }
109 
110     return ret;
111 }
112 
113 #if (defined(FSL_PM_SUPPORT_NOTIFICATION) && FSL_PM_SUPPORT_NOTIFICATION)
PM_notifyPowerStateEntry(uint8_t powerState)114 static status_t PM_notifyPowerStateEntry(uint8_t powerState)
115 {
116     uint8_t i;
117     pm_notify_callback_func_t callback;
118     pm_notify_element_t *currElement = NULL;
119     status_t status                  = kStatus_PMSuccess;
120 
121     /* Execute from group0 to group2. */
122     for (i = (uint8_t)kPM_NotifyGroup0; i <= (uint8_t)kPM_NotifyGroup2; i++)
123     {
124         s_pmHandle->curNotifyGroup = (pm_notify_group_t)i;
125         if (LIST_GetSize((list_handle_t) & (s_pmHandle->notifyList[i])) != 0UL)
126         {
127             currElement = (pm_notify_element_t *)(void *)(s_pmHandle->notifyList[i].head);
128 
129             do
130             {
131                 callback = currElement->notifyCallback;
132                 status   = callback(kPM_EventEnteringSleep, powerState, currElement->data);
133                 if (status != kStatus_Success)
134                 {
135                     s_pmHandle->curNotifyElement = currElement;
136                     return kStatus_PMNotifyEventError;
137                 }
138                 currElement = (pm_notify_element_t *)(void *)currElement->link.next;
139             } while (currElement != NULL);
140         }
141     }
142 
143     s_pmHandle->curNotifyGroup   = kPM_NotifyGroup2;
144     s_pmHandle->curNotifyElement = NULL;
145     return kStatus_PMSuccess;
146 }
147 
PM_notifyPowerStateExit(uint8_t powerState)148 static status_t PM_notifyPowerStateExit(uint8_t powerState)
149 {
150     int8_t i;
151     pm_notify_element_t *currElement = NULL;
152 
153     if (s_pmHandle->curNotifyElement != NULL)
154     {
155         i = (int8_t)(s_pmHandle->curNotifyGroup);
156     }
157     else
158     {
159         /* execute Group 2 first on exit from low power */
160         i = (int8_t)kPM_NotifyGroup2;
161     }
162 
163     /* Execute from group2 to group0. */
164     for (; i >= (int8_t)kPM_NotifyGroup0; i--)
165     {
166         if (LIST_GetSize((list_handle_t) & (s_pmHandle->notifyList[i])) != 0UL)
167         {
168             currElement = (pm_notify_element_t *)(void *)(s_pmHandle->notifyList[i].head);
169 
170             do
171             {
172                 (void)(currElement->notifyCallback(kPM_EventExitingSleep, powerState, currElement->data));
173                 if (currElement == s_pmHandle->curNotifyElement)
174                 {
175                     break;
176                 }
177                 currElement = (pm_notify_element_t *)(void *)(currElement->link.next);
178             } while (currElement != NULL);
179         }
180     }
181 
182     return kStatus_PMSuccess;
183 }
184 #endif /* FSL_PM_SUPPORT_NOTIFICATION */
185 
PM_SetAllowedLowestPowerMode(void)186 static void PM_SetAllowedLowestPowerMode(void)
187 {
188     uint8_t lowestPowerMode = PM_LP_STATE_COUNT - 1U;
189     uint8_t index;
190 
191     for (index = 0U; index < PM_LP_STATE_COUNT; index++)
192     {
193         if (s_pmHandle->powerModeConstraintCount[index] > 0U)
194         {
195             lowestPowerMode = index;
196             break;
197         }
198     }
199 
200     s_pmHandle->powerModeConstraint = lowestPowerMode;
201 }
202 
PM_EnterCriticalDefault(void)203 static void PM_EnterCriticalDefault(void)
204 {
205     s_defaultPMIrqMask = DisableGlobalIRQ();
206 }
207 
PM_ExitCriticalDefault(void)208 static void PM_ExitCriticalDefault(void)
209 {
210     EnableGlobalIRQ(s_defaultPMIrqMask);
211 }
212 
213 /***************************************************************
214  * Public Funtions
215  ***************************************************************/
216 /*!
217  * brief Initialize the power manager handle, this function should be invoked before using other power manager
218  * APIs.
219  *
220  * note In default, the power manager is disabled.
221  *
222  * param handle Pointer to the pm_handle_t structure, upper layer software should pre-allocate the handle global
223  * variable.
224  */
PM_CreateHandle(pm_handle_t * handle)225 void PM_CreateHandle(pm_handle_t *handle)
226 {
227     assert(handle != NULL);
228 
229     /* Clear handle. */
230     (void)memset(handle, 0, sizeof(*handle));
231 
232     handle->enable = false;
233     /* Initial value is set to 1 as Power Manager is disabled by default */
234     handle->disableCount = 1;
235     handle->deviceOption = &g_devicePMOption;
236 #if (defined(FSL_PM_SUPPORT_LP_TIMER_CONTROLLER) && FSL_PM_SUPPORT_LP_TIMER_CONTROLLER)
237     handle->timerStart       = NULL;
238     handle->timerStop        = NULL;
239     handle->getTimerDuration = NULL;
240     handle->getTimestamp     = NULL;
241 #endif /* FSL_PM_SUPPORT_LP_TIMER_CONTROLLER */
242 
243     handle->enterCritical = PM_EnterCriticalDefault;
244     handle->exitCritical  = PM_ExitCriticalDefault;
245 
246 #if (defined(FSL_PM_SUPPORT_NOTIFICATION) && FSL_PM_SUPPORT_NOTIFICATION)
247     /* Create notify lists. */
248     LIST_Init((list_handle_t) & (handle->notifyList[kPM_NotifyGroup0]), 0UL);
249     LIST_Init((list_handle_t) & (handle->notifyList[kPM_NotifyGroup1]), 0UL);
250     LIST_Init((list_handle_t) & (handle->notifyList[kPM_NotifyGroup2]), 0UL);
251 #endif /* FSL_PM_SUPPORT_NOTIFICATION */
252 
253 #if (defined(FSL_PM_SUPPORT_WAKEUP_SOURCE_MANAGER) && FSL_PM_SUPPORT_WAKEUP_SOURCE_MANAGER)
254     LIST_Init((list_handle_t) & (handle->wakeupSourceList), 0UL);
255 #endif /* FSL_PM_SUPPORT_WAKEUP_SOURCE_MANAGER */
256 
257     s_pmHandle = handle;
258 
259     if (s_pmHandle->deviceOption->prepare != NULL)
260     {
261         s_pmHandle->deviceOption->prepare();
262     }
263 
264     /* Need to clean some device register for proper functioning */
265     /*
266      * $Branch Coverage Justification$
267      * $ref pm_core_c_ref_1$.
268      */
269     if (s_pmHandle->deviceOption->clean != NULL)
270     {
271         s_pmHandle->deviceOption->clean();
272     }
273 
274     PM_SetAllowedLowestPowerMode();
275 }
276 
277 /*!
278  * brief Enable/disable power manager functions.
279  *
280  * param enable Used to enable/disable power manager functions.
281  */
PM_EnablePowerManager(bool enable)282 void PM_EnablePowerManager(bool enable)
283 {
284     /* Check whether Power Manager has been initialized or not */
285     assert(s_pmHandle != NULL);
286 
287     if (s_pmHandle->enterCritical != NULL)
288     {
289         s_pmHandle->enterCritical();
290     }
291 
292     if (enable == true)
293     {
294         assert(s_pmHandle->disableCount > 0);
295 
296         s_pmHandle->disableCount--;
297 
298         if (s_pmHandle->disableCount == 0)
299         {
300             s_pmHandle->enable = true;
301         }
302     }
303     else
304     {
305         s_pmHandle->disableCount++;
306 
307         if (s_pmHandle->disableCount >= 1)
308         {
309             s_pmHandle->enable = false;
310         }
311     }
312 
313     if (s_pmHandle->exitCritical != NULL)
314     {
315         s_pmHandle->exitCritical();
316     }
317 }
318 
319 /*!
320  * brief Power manager core API, this API should be used on RTOS's IDLE task.
321  *
322  * This function contains several steps:
323  * 1. Compute target power state based on the policy module.
324  * 2. Notify upper layer software of the power mode entering.
325  * 3. Enter into target power state.
326  * 4. Exit from low power state, if wakeup event occurred.
327  * 5. Notify upper layer software of the power mode exiting.
328  *
329  * The target power state is determined based on two factors:
330  *   a. The input parameter should be larger than state's exitLatency attribution.
331  *   b. resConstraintsMask logical AND state's lossFeature should equal to 0, because constraint can be understand as
332  * some features can not loss.
333  *
334  * param duration The time in low power mode, this value is calculate from RTOS API.
335  */
PM_EnterLowPower(uint64_t duration)336 void PM_EnterLowPower(uint64_t duration)
337 {
338     uint8_t stateIndex;
339     status_t status = kStatus_PMSuccess;
340 
341     if (s_pmHandle->enable)
342     {
343         /* 1. Based on duration and system constraints compute the next allowed deepest power state. */
344         stateIndex = PM_findDeepestState(duration);
345         if (stateIndex != 0xFFU)
346         {
347             s_pmHandle->targetState = stateIndex;
348 
349 #if (defined(FSL_PM_SUPPORT_NOTIFICATION) && FSL_PM_SUPPORT_NOTIFICATION)
350             /* Notify the enter of power state */
351             status = PM_notifyPowerStateEntry(stateIndex);
352 #endif /* FSL_PM_SUPPORT_NOTIFICATION */
353 
354             if (status == kStatus_PMSuccess)
355             {
356 #if (defined(FSL_PM_SUPPORT_LP_TIMER_CONTROLLER) && FSL_PM_SUPPORT_LP_TIMER_CONTROLLER)
357                 if (s_pmHandle->getTimestamp != NULL)
358                 {
359                     s_pmHandle->entryTimestamp = s_pmHandle->getTimestamp();
360                 }
361 
362                 /* Start low power timer if needed */
363                 if ((s_pmHandle->timerStart != NULL) && (duration != 0UL))
364                 {
365                     s_pmHandle->timerStart(duration - (s_pmHandle->deviceOption->states[stateIndex].exitLatency));
366                 }
367 #endif /* FSL_PM_SUPPORT_LP_TIMER_CONTROLLER */
368 
369                 /* Enter into low power state. */
370                 s_pmHandle->deviceOption->enter(stateIndex, &s_pmHandle->softConstraints, &s_pmHandle->sysRescGroup);
371 
372 #if (defined(FSL_PM_SUPPORT_LP_TIMER_CONTROLLER) && FSL_PM_SUPPORT_LP_TIMER_CONTROLLER)
373                 /* Stop low power timer if it is started */
374                 if (s_pmHandle->timerStop != NULL)
375                 {
376                     s_pmHandle->timerStop();
377                 }
378 
379                 if (s_pmHandle->getTimestamp != NULL)
380                 {
381                     s_pmHandle->exitTimestamp = s_pmHandle->getTimestamp();
382                 }
383 #endif /* FSL_PM_SUPPORT_LP_TIMER_CONTROLLER */
384             }
385 
386 #if (defined(FSL_PM_SUPPORT_NOTIFICATION) && FSL_PM_SUPPORT_NOTIFICATION)
387             /* Notify the exit of power state. */
388             (void)PM_notifyPowerStateExit(stateIndex);
389 #endif /* FSL_PM_SUPPORT_NOTIFICATION */
390 
391             /*
392              * $Branch Coverage Justification$
393              * $ref pm_core_c_ref_1$.
394              */
395             if (s_pmHandle->deviceOption->clean != NULL)
396             {
397                 s_pmHandle->deviceOption->clean();
398             }
399         }
400     }
401 }
402 
403 #if (defined(FSL_PM_SUPPORT_LP_TIMER_CONTROLLER) && FSL_PM_SUPPORT_LP_TIMER_CONTROLLER)
404 /*!
405  * brief Register timer controller related functions to power manager.
406  *
407  * If low power timer is the wakeup source, please remember to register it into power manager by using
408  * PM_RegisterWakeupSource() function.
409  *
410  * param handle Pointer to the pm_handle_t structure
411  * param timerStart Low power timer start function, this parameter can be NULL, and it means low power timer is not set
412  *  as the wakeup source.
413  * param timerStop Low power timer stop function, this parameter can also be set as NULL.
414  * param timerSync Low power timer sync function, this parameter can also be set as NULL.
415  * param getTimestamp Low power timestamp function, this parameter can also be set as NULL.
416  * param getTimerDuration Get timer count function. this parameter can also be set as NULL.
417  */
PM_RegisterTimerController(pm_handle_t * handle,pm_low_power_timer_start_func_t timerStart,pm_low_power_timer_stop_func_t timerStop,pm_low_power_timer_get_timestamp_func_t getTimestamp,pm_low_power_timer_get_duration_func_t getTimerDuration)418 void PM_RegisterTimerController(pm_handle_t *handle,
419                                 pm_low_power_timer_start_func_t timerStart,
420                                 pm_low_power_timer_stop_func_t timerStop,
421                                 pm_low_power_timer_get_timestamp_func_t getTimestamp,
422                                 pm_low_power_timer_get_duration_func_t getTimerDuration)
423 {
424     assert(handle != NULL);
425 
426     if (s_pmHandle->enterCritical != NULL)
427     {
428         s_pmHandle->enterCritical();
429     }
430 
431     handle->timerStart       = timerStart;
432     handle->timerStop        = timerStop;
433     handle->getTimerDuration = getTimerDuration;
434     handle->getTimestamp     = getTimestamp;
435 
436     if (s_pmHandle->exitCritical != NULL)
437     {
438         s_pmHandle->exitCritical();
439     }
440 }
441 
PM_RegisterCriticalRegionController(pm_handle_t * handle,pm_enter_critical criticalEntry,pm_exit_critical criticalExit)442 void PM_RegisterCriticalRegionController(pm_handle_t *handle,
443                                          pm_enter_critical criticalEntry,
444                                          pm_exit_critical criticalExit)
445 {
446     assert(handle != NULL);
447 
448     handle->enterCritical = criticalEntry;
449     handle->exitCritical  = criticalExit;
450 }
451 
452 /*!
453  * brief Get the actual low power state duration.
454  */
PM_GetLastLowPowerDuration(void)455 uint64_t PM_GetLastLowPowerDuration(void)
456 {
457     return s_pmHandle->getTimerDuration(s_pmHandle->entryTimestamp, s_pmHandle->exitTimestamp);
458 }
459 #endif /* FSL_PM_SUPPORT_LP_TIMER_CONTROLLER */
460 
461 #if (defined(FSL_PM_SUPPORT_NOTIFICATION) && FSL_PM_SUPPORT_NOTIFICATION)
462 /*!
463  * brief Register notify element into the selected group.
464  *
465  * param groupId The group of the notify list, this will affect the execution sequence.
466  * param notifyElement The pointer to pm_notify_element_t.
467  * return status_t The status of register notify object behavior.
468  */
PM_RegisterNotify(pm_notify_group_t groupId,pm_notify_element_t * notifyElement)469 status_t PM_RegisterNotify(pm_notify_group_t groupId, pm_notify_element_t *notifyElement)
470 {
471     assert(notifyElement != NULL);
472 
473     status_t status = kStatus_PMSuccess;
474 
475     if (LIST_AddTail((list_handle_t) & (s_pmHandle->notifyList[groupId]),
476                      (list_element_handle_t) & (notifyElement->link)) != kLIST_Ok)
477     {
478         status = kStatus_PMFail;
479     }
480 
481     return status;
482 }
483 
484 /*!
485  * brief Update notify element's callback function and application data.
486  *
487  * param notifyElement The pointer to the notify element to update.
488  * param callback The callback function to be updated.
489  * param data Pointer to the callback function private data.
490  */
PM_UpdateNotify(void * notifyElement,pm_notify_callback_func_t callback,void * data)491 void PM_UpdateNotify(void *notifyElement, pm_notify_callback_func_t callback, void *data)
492 {
493     if (s_pmHandle->enterCritical != NULL)
494     {
495         s_pmHandle->enterCritical();
496     }
497 
498     ((pm_notify_element_t *)notifyElement)->data           = data;
499     ((pm_notify_element_t *)notifyElement)->notifyCallback = callback;
500 
501     if (s_pmHandle->exitCritical != NULL)
502     {
503         s_pmHandle->exitCritical();
504     }
505 }
506 
507 /*!
508  * brief Remove notify element from its notify group.
509  *
510  * param notifyElement The pointer to the notify element to remove.
511  */
PM_UnregisterNotify(void * notifyElement)512 status_t PM_UnregisterNotify(void *notifyElement)
513 {
514     status_t status = kStatus_PMSuccess;
515 
516     if (s_pmHandle->enterCritical != NULL)
517     {
518         s_pmHandle->enterCritical();
519     }
520 
521     if (LIST_RemoveElement((list_element_handle_t) & (((pm_notify_element_t *)notifyElement)->link)) != kLIST_Ok)
522     {
523         status = kStatus_PMFail;
524     }
525 
526     if (s_pmHandle->exitCritical != NULL)
527     {
528         s_pmHandle->exitCritical();
529     }
530 
531     return status;
532 }
533 #endif /* FSL_PM_SUPPORT_NOTIFICATION */
534 
535 #if (defined(FSL_PM_SUPPORT_WAKEUP_SOURCE_MANAGER) && FSL_PM_SUPPORT_WAKEUP_SOURCE_MANAGER)
536 /*!
537  * brief Initialize the wakeup source object.
538  *
539  * param ws    Pointer to the pm_wakeup_source_t variable.
540  * param wsId  Used to select the wakeup source, the wsId of each wakeup source can be found in fsl_pm_device.h
541  * param service The function to be invoked when wake up source asserted.
542  * param enable Used to enable/disable the selected wakeup source.
543  */
PM_InitWakeupSource(pm_wakeup_source_t * ws,uint32_t wsId,pm_wake_up_source_service_func_t service,bool enable)544 void PM_InitWakeupSource(pm_wakeup_source_t *ws, uint32_t wsId, pm_wake_up_source_service_func_t service, bool enable)
545 {
546     assert(ws != NULL);
547 
548     if (s_pmHandle->enterCritical != NULL)
549     {
550         s_pmHandle->enterCritical();
551     }
552 
553     ws->wsId    = wsId;
554     ws->service = service;
555     ws->enabled = enable;
556     ws->active  = false;
557 
558     if (enable == true)
559     {
560         (void)LIST_AddTail((list_handle_t) & (s_pmHandle->wakeupSourceList), (list_element_handle_t) & (ws->link));
561     }
562 
563     (void)(s_pmHandle->deviceOption->manageWakeupSource(ws, enable));
564 
565     if (s_pmHandle->exitCritical != NULL)
566     {
567         s_pmHandle->exitCritical();
568     }
569 }
570 
571 /*!
572  * brief Enable wakeup source.
573  *
574  * param ws Pointer to the wakeup source object to be enabled.
575  * return status_t The status of enable wakeup source behavior.
576  */
PM_EnableWakeupSource(pm_wakeup_source_t * ws)577 status_t PM_EnableWakeupSource(pm_wakeup_source_t *ws)
578 {
579     assert(ws != NULL);
580 
581     status_t status = kStatus_PMSuccess;
582 
583     if (s_pmHandle->enterCritical != NULL)
584     {
585         s_pmHandle->enterCritical();
586     }
587 
588     if (!(ws->enabled))
589     {
590         /* Add wake up source to list so PM can parse the list if wake up event
591          * occurs, and trigger the service callback if needed */
592         (void)LIST_AddTail((list_handle_t) & (s_pmHandle->wakeupSourceList), (list_element_handle_t) & (ws->link));
593         status = s_pmHandle->deviceOption->manageWakeupSource(ws, true);
594 
595         if (status == kStatus_Success)
596         {
597             ws->enabled = true;
598         }
599         else
600         {
601             /*
602              * $Line Coverage Justification$
603              * $ref pm_core_c_ref_2$.
604              */
605             status = kStatus_PMWakeupSourceEnableError;
606         }
607     }
608 
609     if (s_pmHandle->exitCritical != NULL)
610     {
611         s_pmHandle->exitCritical();
612     }
613 
614     return status;
615 }
616 
617 /*!
618  * brief Disable wakeup source
619  *
620  * param ws Pointer to the wakeup source object to be disabled.
621  * return status_t The status of disable wakeup source behavior.
622  */
PM_DisableWakeupSource(pm_wakeup_source_t * ws)623 status_t PM_DisableWakeupSource(pm_wakeup_source_t *ws)
624 {
625     assert(ws != NULL);
626 
627     status_t status = kStatus_PMSuccess;
628 
629     if (s_pmHandle->enterCritical != NULL)
630     {
631         s_pmHandle->enterCritical();
632     }
633 
634     if (ws->enabled)
635     {
636         /* Remove the wake up source from the list */
637         (void)LIST_RemoveElement((list_element_handle_t) & (ws->link));
638         status = s_pmHandle->deviceOption->manageWakeupSource(ws, false);
639 
640         if (status == kStatus_Success)
641         {
642             ws->enabled = false;
643         }
644         else
645         {
646             /*
647              * $Line Coverage Justification$
648              * $ref pm_core_c_ref_2$.
649              */
650             status = kStatus_PMWakeupSourceDisableError;
651         }
652     }
653 
654     if (s_pmHandle->exitCritical != NULL)
655     {
656         s_pmHandle->exitCritical();
657     }
658 
659     return status;
660 }
661 
662 /*!
663  * brief Checks if any enabled wake up source is responsible for last wake up
664  *       event. In such case, it will call the wake up source callback if it
665  *       has been registered. Likely to be called from Wake Up Unit IRQ Handler.
666  *
667  * return status_t The status of handling the wake up event.
668  */
PM_HandleWakeUpEvent(void)669 status_t PM_HandleWakeUpEvent(void)
670 {
671     status_t status = kStatus_PMSuccess;
672     pm_wakeup_source_t *currWakeUpSource;
673 
674     if (LIST_GetSize((list_handle_t) & (s_pmHandle->wakeupSourceList)) != 0UL)
675     {
676         currWakeUpSource = (pm_wakeup_source_t *)(void *)(s_pmHandle->wakeupSourceList.head);
677 
678         /* the list should contain only enabled ws */
679         assert(currWakeUpSource->enabled == true);
680 
681         do
682         {
683             if (currWakeUpSource->service != NULL)
684             {
685                 if (s_pmHandle->deviceOption->isWakeupSource(currWakeUpSource) == true)
686                 {
687                     /* The wake up source trigger the last wake up event
688                      * we can call the callback */
689                     status = PM_TriggerWakeSourceService(currWakeUpSource);
690                 }
691             }
692 
693             currWakeUpSource = (pm_wakeup_source_t *)(void *)currWakeUpSource->link.next;
694         } while (currWakeUpSource != NULL);
695     }
696 
697     return status;
698 }
699 
700 /*!
701  * brief If the specfic wakeup event occurs, invoke this API to execute its service function.
702  *
703  * param ws Pointer to the wakeup source object.
704  * return status_t The status of trigger wakeup source behavior.
705  */
PM_TriggerWakeSourceService(pm_wakeup_source_t * ws)706 status_t PM_TriggerWakeSourceService(pm_wakeup_source_t *ws)
707 {
708     assert(ws != NULL);
709 
710     status_t status = kStatus_PMSuccess;
711 
712     if (s_pmHandle->enterCritical != NULL)
713     {
714         s_pmHandle->enterCritical();
715     }
716 
717     if (ws->enabled)
718     {
719         if (ws->active)
720         {
721             status = kStatus_PMWakeupSourceServiceBusy;
722         }
723         else
724         {
725             ws->active = true;
726             ws->service();
727             ws->active = false;
728 
729             status = kStatus_PMSuccess;
730         }
731     }
732     else
733     {
734         /*
735          * $Line Coverage Justification$
736          * $ref pm_core_c_ref_2$.
737          */
738         status = kStatus_PMWakeupSourceEnableError;
739     }
740 
741     if (s_pmHandle->exitCritical != NULL)
742     {
743         s_pmHandle->exitCritical();
744     }
745 
746     return status;
747 }
748 #endif /* FSL_PM_SUPPORT_WAKEUP_SOURCE_MANAGER */
749 
750 /*!
751  * brief Used to set constraints(including power mode constraint and resource constraints)
752  *
753  * For example, if the device support 3 resource constraints: PM_RESC_1, PM_RESC_2, PM_RESC3
754  *  code
755  *      PM_SetConstraints(Sleep_Mode, 3, PM_RESC_1, PM_RESC_2, PM_RESC_3);
756  *  endcode
757  *
758  * param powerModeConstraint The lowest power mode allowed, the power mode constraint macros
759  *                            can be found in fsl_pm_device.h
760  * param rescNum The number of resource constraints to be set.
761  * return status_t The status of set constraints behavior.
762  */
PM_SetConstraints(uint8_t powerModeConstraint,int32_t rescNum,...)763 status_t PM_SetConstraints(uint8_t powerModeConstraint, int32_t rescNum, ...)
764 {
765     status_t ret = kStatus_Success;
766     uint32_t opMode;
767     uint32_t rescShift;
768     int32_t inputResc;
769     va_list ap;
770     int32_t i;
771 
772     if (s_pmHandle->enterCritical != NULL)
773     {
774         s_pmHandle->enterCritical();
775     }
776 
777     if (powerModeConstraint != PM_LP_STATE_NO_CONSTRAINT)
778     {
779         if (powerModeConstraint >= PM_LP_STATE_COUNT)
780         {
781             /* wrong power mode index passed in parameter */
782             ret = kStatus_Fail;
783         }
784         else
785         {
786             /* Set power mode constraint */
787             s_pmHandle->powerModeConstraintCount[powerModeConstraint]++;
788             PM_SetAllowedLowestPowerMode();
789         }
790     }
791     else
792     {
793         /* No power mode constraint to apply, only ressource constraints */
794         ;
795     }
796 
797     if (rescNum != 0)
798     {
799         va_start(ap, rescNum);
800 
801         for (i = 0; i < rescNum; i++)
802         {
803             inputResc = (int32_t)va_arg(ap, int32_t);
804             PM_DECODE_RESC(inputResc);
805 
806             assert(rescShift < (uint32_t)PM_CONSTRAINT_COUNT);
807 
808             if (opMode != PM_RESOURCE_OFF)
809             {
810                 switch (opMode)
811                 {
812                     case PM_RESOURCE_FULL_ON:
813                     {
814                         assert(s_pmHandle->resConstraintCount[rescShift].subConter.fullOnCounter <
815                                ((1U << PM_FULL_ON_COUNTER_SIZE) - 1U));
816                         s_pmHandle->resConstraintCount[rescShift].subConter.fullOnCounter++;
817                         break;
818                     }
819                     case PM_RESOURCE_PARTABLE_ON2:
820                     {
821                         assert(s_pmHandle->resConstraintCount[rescShift].subConter.partOn2Counter <
822                                ((1U << PM_PARTABLE_ON2_COUNTER_SIZE) - 1U));
823                         s_pmHandle->resConstraintCount[rescShift].subConter.partOn2Counter++;
824                         break;
825                     }
826                     case PM_RESOURCE_PARTABLE_ON1:
827                     {
828                         assert(s_pmHandle->resConstraintCount[rescShift].subConter.partOn1Counter <
829                                ((1U << PM_PARTABLE_ON1_COUNTER_SIZE) - 1U));
830                         s_pmHandle->resConstraintCount[rescShift].subConter.partOn1Counter++;
831                         break;
832                     }
833                     default:
834                     {
835                         assert(false);
836                         break;
837                     }
838                 }
839                 /* Set corresponding bit of operate mode in system resource group. */
840                 s_pmHandle->sysRescGroup.groupSlice[rescShift / 8UL] |=
841                     ((uint32_t)opMode << (4UL * ((uint32_t)rescShift % 8UL)));
842                 /* Enable corresponding bit of constraint in system resource mask. */
843                 s_pmHandle->resConstraintMask.rescMask[rescShift / 32UL] |= (1UL << ((uint32_t)rescShift % 32UL));
844             }
845         }
846         va_end(ap);
847     }
848 
849     if (s_pmHandle->exitCritical != NULL)
850     {
851         s_pmHandle->exitCritical();
852     }
853 
854     return ret;
855 }
856 
857 /*!
858  * brief Used to release constraints(including power mode constraint and resource constraints)
859  *
860  * For example, if the device support 3 resource constraints: PM_RESC_1, PM_RESC_2, PM_RESC3
861  *  code
862  *      PM_ReleaseConstraints(Sleep_Mode, 1, PM_RESC_1);
863  *  endcode
864  *
865  * param powerModeConstraint The lowest power mode allowed, the power mode constraint macros
866  *                            can be found in fsl_pm_device.h
867  * param rescNum The number of resource constraints to be released.
868  * return status_t The status of set constraints behavior.
869  */
PM_ReleaseConstraints(uint8_t powerModeConstraint,int32_t rescNum,...)870 status_t PM_ReleaseConstraints(uint8_t powerModeConstraint, int32_t rescNum, ...)
871 {
872     status_t ret = kStatus_Success;
873     uint32_t opMode;
874     uint32_t rescShift;
875     int32_t inputResc;
876     va_list ap;
877     int32_t i;
878     uint32_t curRescOpMode;
879     uint32_t opModeToRelease;
880 
881     if (s_pmHandle->enterCritical != NULL)
882     {
883         s_pmHandle->enterCritical();
884     }
885 
886     if (powerModeConstraint != PM_LP_STATE_NO_CONSTRAINT)
887     {
888         if (powerModeConstraint >= PM_LP_STATE_COUNT)
889         {
890             /* wrong power mode index passed in parameter */
891             ret = kStatus_Fail;
892         }
893         else
894         {
895             /* Release power mode constraint */
896             if (s_pmHandle->powerModeConstraintCount[powerModeConstraint] > 0U)
897             {
898                 s_pmHandle->powerModeConstraintCount[powerModeConstraint]--;
899             }
900             PM_SetAllowedLowestPowerMode();
901         }
902     }
903     else
904     {
905         /* No power mode constraint to release, only ressource constraints */
906         ;
907     }
908 
909     if (rescNum != 0)
910     {
911         va_start(ap, rescNum);
912 
913         for (i = 0; i < rescNum; i++)
914         {
915             inputResc = (int32_t)va_arg(ap, int32_t);
916             PM_DECODE_RESC(inputResc);
917 
918             curRescOpMode   = s_pmHandle->sysRescGroup.groupSlice[rescShift / 8UL];
919             opModeToRelease = ((uint32_t)opMode << (4UL * ((uint32_t)rescShift % 8UL)));
920             if ((curRescOpMode & opModeToRelease) != 0UL)
921             {
922                 uint8_t subCounterValue = 0U;
923                 switch (opMode)
924                 {
925                     case PM_RESOURCE_FULL_ON:
926                     {
927                         assert(s_pmHandle->resConstraintCount[rescShift].subConter.fullOnCounter >= 1U);
928                         s_pmHandle->resConstraintCount[rescShift].subConter.fullOnCounter--;
929                         subCounterValue = (s_pmHandle->resConstraintCount[rescShift].u8Count & PM_FULL_ON_COUNTER_MASK);
930                         break;
931                     }
932                     case PM_RESOURCE_PARTABLE_ON2:
933                     {
934                         assert(s_pmHandle->resConstraintCount[rescShift].subConter.partOn2Counter >= 1U);
935                         s_pmHandle->resConstraintCount[rescShift].subConter.partOn2Counter--;
936                         subCounterValue =
937                             (s_pmHandle->resConstraintCount[rescShift].u8Count & PM_PARTABLE_ON2_COUNTER_MASK);
938                         break;
939                     }
940                     case PM_RESOURCE_PARTABLE_ON1:
941                     {
942                         assert(s_pmHandle->resConstraintCount[rescShift].subConter.partOn1Counter >= 1U);
943                         s_pmHandle->resConstraintCount[rescShift].subConter.partOn1Counter--;
944                         subCounterValue =
945                             (s_pmHandle->resConstraintCount[rescShift].u8Count & PM_PARTABLE_ON1_COUNTER_MASK);
946                         break;
947                     }
948                     default:
949                     {
950                         assert(false);
951                         break;
952                     }
953                 }
954 
955                 if (subCounterValue == 0U)
956                 {
957                     /* Clear corresponding bit of operate mode in system resource group. */
958                     s_pmHandle->sysRescGroup.groupSlice[rescShift / 8UL] &=
959                         ~((uint32_t)opMode << (4UL * ((uint32_t)rescShift % 8UL)));
960                     if (s_pmHandle->resConstraintCount[rescShift].u8Count == 0U)
961                     {
962                         /* Disable corresponding bit of constraint in system resource mask. */
963                         s_pmHandle->resConstraintMask.rescMask[rescShift / 32UL] &=
964                             ~(1UL << ((uint32_t)rescShift % 32UL));
965                     }
966                 }
967             }
968         }
969         va_end(ap);
970     }
971 
972     if (s_pmHandle->exitCritical != NULL)
973     {
974         s_pmHandle->exitCritical();
975     }
976 
977     return ret;
978 }
979 
980 /*!
981  * brief Get current system resource constraints.
982  *
983  * return Current system constraints.
984  */
PM_GetResourceConstraintsMask(void)985 pm_resc_mask_t PM_GetResourceConstraintsMask(void)
986 {
987     return s_pmHandle->resConstraintMask;
988 }
989 
990 /*!
991  * brief Get current system allowed power mode.
992  *
993  * return Allowed lowest power mode.
994  */
PM_GetAllowedLowestPowerMode(void)995 uint8_t PM_GetAllowedLowestPowerMode(void)
996 {
997     return s_pmHandle->powerModeConstraint;
998 }
999