]> git.sur5r.net Git - freertos/blob
16bc8c46423fb046463311febf32a509e052f51c
[freertos] /
1 /***************************************************************************//**\r
2  * @file sleep.c\r
3  * @brief Energy Modes management driver.\r
4  * @version 4.0.0\r
5  * @details\r
6  * This is a energy modes management module consisting of sleep.c and sleep.h\r
7  * source files. The main purpose of the module is to ease energy\r
8  * optimization with a simple API. The module allows the system to always sleep\r
9  * in the lowest possible energy mode. Users could set up callbacks that are\r
10  * being called before and after each and every sleep. A counting semaphore is\r
11  * available for each low energy mode (EM1/EM2/EM3) to protect certain system\r
12  * states from being corrupted. This semaphore has limit set to maximum 255 locks.\r
13  *\r
14  * The module provides the following public API to the users:\r
15  * SLEEP_Init()\r
16  * SLEEP_Sleep()\r
17  * SLEEP_SleepBlockBegin()\r
18  * SLEEP_SleepBlockEnd()\r
19  * SLEEP_ForceSleepInEM4()\r
20  *\r
21  *******************************************************************************\r
22  * @section License\r
23  * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>\r
24  *******************************************************************************\r
25  *\r
26  * This file is licensed under the Silabs License Agreement. See the file\r
27  * "Silabs_License_Agreement.txt" for details. Before using this software for\r
28  * any purpose, you must agree to the terms of that agreement.\r
29  *\r
30  ******************************************************************************/\r
31 \r
32 \r
33 /* Chip specific header file(s). */\r
34 #include "em_device.h"\r
35 #include "em_assert.h"\r
36 #include "em_int.h"\r
37 #include "em_rmu.h"\r
38 #include "em_emu.h"\r
39 \r
40 /* Module header file(s). */\r
41 #include "sleep.h"\r
42 \r
43 /* stdlib is needed for NULL definition */\r
44 #include <stdlib.h>\r
45 \r
46 /***************************************************************************//**\r
47  * @addtogroup EM_Drivers\r
48  * @{\r
49  ******************************************************************************/\r
50 \r
51 /***************************************************************************//**\r
52  * @addtogroup SLEEP\r
53  * @brief Energy Modes management driver.\r
54  * @details\r
55  * This is a energy modes management module consisting of sleep.c and sleep.h\r
56  * source files. The main purpose of the module is to ease energy\r
57  * optimization with a simple API. The module allows the system to always sleep\r
58  * in the lowest possible energy mode. Users could set up callbacks that are\r
59  * being called before and after each and every sleep. A counting semaphore is\r
60  * available for each low energy mode (EM1/EM2/EM3) to protect certain system\r
61  * states from being corrupted. This semaphore has limit set to maximum 255 locks.\r
62  * @{\r
63  ******************************************************************************/\r
64 \r
65 /*******************************************************************************\r
66  *******************************   MACROS   ************************************\r
67  ******************************************************************************/\r
68 \r
69 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */\r
70 \r
71 /* Number of low energy modes (EM1, EM2, EM3). Note: EM4 sleep/wakeup is handled\r
72  * differently therefore it is not part of the list! */\r
73 #define SLEEP_NUMOF_LOW_ENERGY_MODES    3U\r
74 \r
75 \r
76 \r
77 /*******************************************************************************\r
78  ******************************   TYPEDEFS   ***********************************\r
79  ******************************************************************************/\r
80 \r
81 \r
82 /*******************************************************************************\r
83  ******************************   CONSTANTS   **********************************\r
84  ******************************************************************************/\r
85 \r
86 \r
87 /*******************************************************************************\r
88  *******************************   STATICS   ***********************************\r
89  ******************************************************************************/\r
90 \r
91 /* Callback functions to call before and after sleep. */\r
92 static SLEEP_CbFuncPtr_t sleepCallback  = NULL;\r
93 static SLEEP_CbFuncPtr_t wakeUpCallback = NULL;\r
94 \r
95 /* Sleep block counter array representing the nested sleep blocks for the low\r
96  * energy modes (EM1/EM2/EM3). Array index 0 corresponds to EM1, 1 to EM2 and 2\r
97  * to EM3 accordingly.\r
98  *\r
99  * Note:\r
100  * - EM4 sleep/wakeup is handled differently therefore it is not part of the\r
101  *   list!\r
102  * - Max. number of sleep block nesting is 255. */\r
103 static uint8_t sleepBlockCnt[SLEEP_NUMOF_LOW_ENERGY_MODES];\r
104 \r
105 /*******************************************************************************\r
106  ******************************   PROTOTYPES   *********************************\r
107  ******************************************************************************/\r
108 \r
109 static void SLEEP_EnterEMx(SLEEP_EnergyMode_t eMode);\r
110 //static SLEEP_EnergyMode_t SLEEP_LowestEnergyModeGet(void);\r
111 \r
112 /** @endcond */\r
113 \r
114 /*******************************************************************************\r
115  ***************************   GLOBAL FUNCTIONS   ******************************\r
116  ******************************************************************************/\r
117 \r
118 /***************************************************************************//**\r
119  * @brief\r
120  *   Initialize the Sleep module.\r
121  *\r
122  * @details\r
123  *   Use this function to initialize the Sleep module, should be called\r
124  *   only once! Pointers to sleep and wake-up callback functions shall be\r
125  *   provided when calling this function.\r
126  *   If SLEEP_EM4_WAKEUP_CALLBACK_ENABLED is set to true, this function checks\r
127  *   for the cause of the reset that implicitly called it and calls the wakeup\r
128  *   callback if the reset was a wakeup from EM4 (does not work on Gecko MCU).\r
129  *\r
130  * @param[in] pSleepCb\r
131  *   Pointer to the callback function that is being called before the device is\r
132  *   going to sleep.\r
133  *\r
134  * @param[in] pWakeUpCb\r
135  *   Pointer to the callback function that is being called after wake up.\r
136  ******************************************************************************/\r
137 void SLEEP_Init(SLEEP_CbFuncPtr_t pSleepCb, SLEEP_CbFuncPtr_t pWakeUpCb)\r
138 {\r
139   /* Initialize callback functions. */\r
140   sleepCallback  = pSleepCb;\r
141   wakeUpCallback = pWakeUpCb;\r
142 \r
143   /* Reset sleep block counters. Note: not using for() saves code! */\r
144   sleepBlockCnt[0U] = 0U;\r
145   sleepBlockCnt[1U] = 0U;\r
146   sleepBlockCnt[2U] = 0U;\r
147 \r
148 #if (SLEEP_EM4_WAKEUP_CALLBACK_ENABLED == true) && defined(RMU_RSTCAUSE_EM4WURST)\r
149   /* Check if the Init() happened after an EM4 reset. */\r
150   if (RMU_ResetCauseGet() & RMU_RSTCAUSE_EM4WURST)\r
151   {\r
152     /* Clear the cause of the reset. */\r
153     RMU_ResetCauseClear();\r
154     /* Call wakeup callback with EM4 parameter. */\r
155     if (NULL != wakeUpCallback)\r
156     {\r
157       wakeUpCallback(sleepEM4);\r
158     }\r
159   }\r
160 #endif\r
161 }\r
162 \r
163 \r
164 /***************************************************************************//**\r
165  * @brief\r
166  *   Sets the system to sleep into the lowest possible energy mode.\r
167  *\r
168  * @details\r
169  *   This function takes care of the system states protected by the sleep block\r
170  *   provided by SLEEP_SleepBlockBegin() / SLEEP_SleepBlockEnd(). It allows\r
171  *   the system to go into the lowest possible energy mode that the device can\r
172  *   be set into at the time of the call of this function.\r
173  *   This function will not go lower than EM3 because leaving EM4 requires\r
174  *   resetting MCU. To enter into EM4 call SLEEP_ForceSleepInEM4().\r
175  *\r
176  * @return\r
177  *   Energy Mode that was entered. Possible values:\r
178  *   @li sleepEM0\r
179  *   @li sleepEM1\r
180  *   @li sleepEM2\r
181  *   @li sleepEM3\r
182  ******************************************************************************/\r
183 SLEEP_EnergyMode_t SLEEP_Sleep(void)\r
184 {\r
185   SLEEP_EnergyMode_t allowedEM;\r
186 \r
187   INT_Disable();\r
188 \r
189   allowedEM = SLEEP_LowestEnergyModeGet();\r
190 \r
191   if ((allowedEM >= sleepEM1) && (allowedEM <= sleepEM3))\r
192   {\r
193     SLEEP_EnterEMx(allowedEM);\r
194   }\r
195   else\r
196   {\r
197     allowedEM = sleepEM0;\r
198   }\r
199 \r
200   INT_Enable();\r
201 \r
202   return(allowedEM);\r
203 }\r
204 \r
205 \r
206 /***************************************************************************//**\r
207  * @brief\r
208  *   Force the device to go to EM4 without doing any checks.\r
209  *\r
210  * @details\r
211  *   This function unblocks the low energy sleep block then goes to EM4.\r
212  *\r
213  * @note\r
214  *   Regular RAM is not retained in EM4 and the wake up causes a reset.\r
215  *   If the configuration option SLEEP_EM4_WAKEUP_CALLBACK_ENABLED is set to\r
216  *   true, the SLEEP_Init() function checks for the reset cause and calls the\r
217  *   EM4 wakeup callback.\r
218  ******************************************************************************/\r
219 void SLEEP_ForceSleepInEM4(void)\r
220 {\r
221 #if (SLEEP_HW_LOW_ENERGY_BLOCK_ENABLED == true)\r
222   /* Unblock the EM2/EM3/EM4 block in the EMU. */\r
223   EMU_EM2UnBlock();\r
224 #endif\r
225 \r
226   /* Request entering to EM4. */\r
227   SLEEP_EnterEMx(sleepEM4);\r
228 }\r
229 \r
230 /***************************************************************************//**\r
231  * @brief\r
232  *   Begin sleep block in the requested energy mode.\r
233  *\r
234  * @details\r
235  *   Blocking a critical system state from a certain energy mode makes sure that\r
236  *   the system is not set to that energy mode while the block is not being\r
237  *   released.\r
238  *   Every SLEEP_SleepBlockBegin() increases the corresponding counter and\r
239  *   every SLEEP_SleepBlockEnd() decreases it.\r
240  *\r
241  *   Example:\code\r
242  *      SLEEP_SleepBlockBegin(sleepEM2);  // do not allow EM2 or higher\r
243  *      // do some stuff that requires EM1 at least, like ADC sampling\r
244  *      SLEEP_SleepBlockEnd(sleepEM2);    // remove restriction for EM2\endcode\r
245  *\r
246  * @note\r
247  *   Be aware that there is limit of maximum blocks nesting to 255.\r
248  *\r
249  * @param[in] eMode\r
250  *   Energy mode to begin to block. Possible values:\r
251  *   @li sleepEM1 - Begin to block the system from being set to EM1 (and EM2..4).\r
252  *   @li sleepEM2 - Begin to block the system from being set to EM2 (and EM3/EM4).\r
253  *   @li sleepEM3 - Begin to block the system from being set to EM3 (and EM4).\r
254  ******************************************************************************/\r
255 void SLEEP_SleepBlockBegin(SLEEP_EnergyMode_t eMode)\r
256 {\r
257   EFM_ASSERT((eMode >= sleepEM1) && (eMode < sleepEM4));\r
258   EFM_ASSERT((sleepBlockCnt[(uint8_t) eMode - 1U]) < 255U);\r
259 \r
260   /* Increase the sleep block counter of the selected energy mode. */\r
261   sleepBlockCnt[(uint8_t) eMode - 1U]++;\r
262 \r
263 #if (SLEEP_HW_LOW_ENERGY_BLOCK_ENABLED == true)\r
264   /* Block EM2/EM3 sleep if the EM2 block begins. */\r
265   if (eMode == sleepEM2)\r
266   {\r
267     EMU_EM2Block();\r
268   }\r
269 #endif\r
270 }\r
271 \r
272 /***************************************************************************//**\r
273  * @brief\r
274  *   End sleep block in the requested energy mode.\r
275  *\r
276  * @details\r
277  *   Release restriction for entering certain energy mode. Every call of this\r
278  *   function reduce blocking counter by 1. Once the counter for specific energy\r
279  *   mode is 0 and all counters for lower energy modes are 0 as well, using\r
280  *   particular energy mode is allowed.\r
281  *   Every SLEEP_SleepBlockBegin() increases the corresponding counter and\r
282  *   every SLEEP_SleepBlockEnd() decreases it.\r
283  *\r
284  *   Example:\code\r
285  *      // at start all energy modes are allowed\r
286  *      SLEEP_SleepBlockBegin(sleepEM2); // EM2, EM3, EM4 are blocked\r
287  *      SLEEP_SleepBlockBegin(sleepEM1); // EM1, EM2, EM3, EM4 are blocked\r
288  *      SLEEP_SleepBlockBegin(sleepEM1); // EM1, EM2, EM3, EM4 are blocked\r
289  *      SLEEP_SleepBlockEnd(sleepEM2);   // still EM1, EM2, EM3, EM4 are blocked\r
290  *      SLEEP_SleepBlockEnd(sleepEM1);   // still EM1, EM2, EM3, EM4 are blocked\r
291  *      SLEEP_SleepBlockEnd(sleepEM1);   // all energy modes are allowed now\endcode\r
292  *\r
293  * @param[in] eMode\r
294  *   Energy mode to end to block. Possible values:\r
295  *   @li sleepEM1 - End to block the system from being set to EM1 (and EM2..4).\r
296  *   @li sleepEM2 - End to block the system from being set to EM2 (and EM3/EM4).\r
297  *   @li sleepEM3 - End to block the system from being set to EM3 (and EM4).\r
298  ******************************************************************************/\r
299 void SLEEP_SleepBlockEnd(SLEEP_EnergyMode_t eMode)\r
300 {\r
301   EFM_ASSERT((eMode >= sleepEM1) && (eMode < sleepEM4));\r
302 \r
303   /* Decrease the sleep block counter of the selected energy mode. */\r
304   if (sleepBlockCnt[(uint8_t) eMode - 1U] > 0U)\r
305   {\r
306     sleepBlockCnt[(uint8_t) eMode - 1U]--;\r
307   }\r
308 \r
309 #if (SLEEP_HW_LOW_ENERGY_BLOCK_ENABLED == true)\r
310   /* Check if the EM2/EM3 block should be unblocked in the EMU. */\r
311   if (0U == sleepBlockCnt[(uint8_t) sleepEM2 - 1U])\r
312   {\r
313     EMU_EM2UnBlock();\r
314   }\r
315 #endif\r
316 }\r
317 \r
318 /***************************************************************************//**\r
319  * @brief\r
320  *   Gets the lowest energy mode that the system is allowed to be set to.\r
321  *\r
322  * @details\r
323  *   This function uses the low energy mode block counters to determine the\r
324  *   lowest possible that the system is allowed to be set to.\r
325  *\r
326  * @return\r
327  *   Lowest energy mode that the system can be set to. Possible values:\r
328  *   @li sleepEM0\r
329  *   @li sleepEM1\r
330  *   @li sleepEM2\r
331  *   @li sleepEM3\r
332  ******************************************************************************/\r
333 SLEEP_EnergyMode_t SLEEP_LowestEnergyModeGet(void)\r
334 {\r
335   SLEEP_EnergyMode_t tmpLowestEM = sleepEM0;\r
336 \r
337   /* Check which is the lowest energy mode that the system can be set to. */\r
338   if (0U == sleepBlockCnt[(uint8_t) sleepEM1 - 1U])\r
339   {\r
340     tmpLowestEM = sleepEM1;\r
341     if (0U == sleepBlockCnt[(uint8_t) sleepEM2 - 1U])\r
342     {\r
343       tmpLowestEM = sleepEM2;\r
344       if (0U == sleepBlockCnt[(uint8_t) sleepEM3 - 1U])\r
345       {\r
346         tmpLowestEM = sleepEM3;\r
347       }\r
348     }\r
349   }\r
350 \r
351   /* Compare with the default lowest energy mode setting. */\r
352   if (SLEEP_LOWEST_ENERGY_MODE_DEFAULT < tmpLowestEM)\r
353   {\r
354     tmpLowestEM = SLEEP_LOWEST_ENERGY_MODE_DEFAULT;\r
355   }\r
356 \r
357   return tmpLowestEM;\r
358 }\r
359 \r
360 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */\r
361 \r
362 /***************************************************************************//**\r
363  * @brief\r
364  *   Call the callbacks and enter the requested energy mode.\r
365  *\r
366  * @details\r
367  *   This function is not part of the API, therefore it shall not be called by\r
368  *   the user directly as it doesn not have any checks if the system is ready\r
369  *   for sleep!\r
370  *\r
371  * @note\r
372  *   The EM4 wakeup callback is not being called from this function because\r
373  *   waking up from EM4 causes a reset.\r
374  *   If SLEEP_EM4_WAKEUP_CALLBACK_ENABLED is set to true, SLEEP_Init() function\r
375  *   checks for the cause of the reset and calls the wakeup callback if the\r
376  *   reset was a wakeup from EM4.\r
377  ******************************************************************************/\r
378 static void SLEEP_EnterEMx(SLEEP_EnergyMode_t eMode)\r
379 {\r
380   EFM_ASSERT((eMode > sleepEM0) && (eMode <= sleepEM4));\r
381 \r
382   /* Call sleepCallback() before going to sleep. */\r
383   if (NULL != sleepCallback)\r
384   {\r
385     /* Call the callback before going to sleep. */\r
386     sleepCallback(eMode);\r
387   }\r
388 \r
389   /* Enter the requested energy mode. */\r
390   switch (eMode)\r
391   {\r
392   case sleepEM1:\r
393   {\r
394     EMU_EnterEM1();\r
395   } break;\r
396 \r
397   case sleepEM2:\r
398   {\r
399     EMU_EnterEM2(true);\r
400   } break;\r
401 \r
402   case sleepEM3:\r
403   {\r
404     EMU_EnterEM3(true);\r
405   } break;\r
406 \r
407   case sleepEM4:\r
408   {\r
409     EMU_EnterEM4();\r
410   } break;\r
411 \r
412   default:\r
413   {\r
414     /* Don't do anything, stay in EM0. */\r
415   } break;\r
416   }\r
417 \r
418   /* Call the callback after waking up from sleep. */\r
419   if (NULL != wakeUpCallback)\r
420   {\r
421     wakeUpCallback(eMode);\r
422   }\r
423 }\r
424 /** @endcond */\r
425 \r
426 /** @} (end addtogroup SLEEP */\r
427 /** @} (end addtogroup EM_Drivers) */\r