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