Merge remote-tracking branch 'vincent/3.00.00.11_eng' into tmp
[ipc/ipcdev.git] / packages / ti / pm / IpcPower.c
1 /*
2  * Copyright (c) 2011-2013, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 /** ============================================================================
33  *  @file       IpcPower.c
34  *
35  *  @brief      A simple Power Managment which responses to the host commands.
36  *
37  *  TODO:
38  *     - Add suspend/resume notifications
39  *  ============================================================================
40  */
42 #include <xdc/std.h>
43 #include <xdc/runtime/System.h>
44 #include <xdc/runtime/Error.h>
45 #include <xdc/runtime/Assert.h>
46 #include <xdc/runtime/Memory.h>
47 #include <xdc/runtime/Main.h>
48 #include <xdc/runtime/Registry.h>
49 #include <xdc/runtime/Log.h>
50 #include <xdc/runtime/Diags.h>
52 #include <ti/sysbios/BIOS.h>
53 #include <ti/sysbios/knl/Semaphore.h>
54 #include <ti/sysbios/knl/Task.h>
55 #include <ti/sysbios/hal/Hwi.h>
56 #include <ti/sysbios/knl/Swi.h>
57 #include <ti/sysbios/knl/Clock.h>
58 #include <ti/sysbios/timers/dmtimer/Timer.h>
59 #ifndef SMP
60 #include <ti/sysbios/family/arm/ducati/omap4430/Power.h>
61 #include <ti/sysbios/family/arm/ducati/Core.h>
62 #include <ti/ipc/MultiProc.h>
63 #else
64 #include <ti/sysbios/family/arm/ducati/smp/Power.h>
65 #endif
67 #include <ti/pm/IpcPower.h>
68 #include "_IpcPower.h"
70 #define MASTERCORE                      1
71 #define NO_MASTERCORE                   0
72 #define CPU_COPY                       -1
73 #define REG32(A)   (*(volatile UInt32 *) (A))
75 #define SET_DEEPSLEEP (REG32(M3_SCR_REG) | (1 << DEEPSLEEP_BIT))
76 #define CLR_DEEPSLEEP (REG32(M3_SCR_REG) & (~(1 << DEEPSLEEP_BIT)))
78 #ifndef SMP
79 #pragma DATA_SECTION(IpcPower_hibLocks, ".ipcpower_data")
80 UInt32 IpcPower_hibLocks[2]; /* One lock for each of the IPU cores */
81 #else
82 static UInt32 IpcPower_hibLocks; /* Only one lock in SMP mode */
83 #endif
85 /* PM transition debug counters */
86 UInt32 IpcPower_idleCount = 0;
87 UInt32 IpcPower_suspendCount = 0;
88 UInt32 IpcPower_resumeCount = 0;
90 /* Clock function pointer to handle custom clock functions */
91 Void *IpcPower_clockFxn = NULL;
93 /* Handle to store BIOS Tick Timer */
94 static Timer_Handle tickTimerHandle = NULL;
96 static Power_SuspendArgs PowerSuspArgs;
97 static Swi_Handle suspendResumeSwi;
98 #ifndef SMP
99 static Semaphore_Handle IpcPower_semSuspend = NULL;
100 static Semaphore_Handle IpcPower_semExit = NULL;
101 static Task_Handle IpcPower_tskSuspend = NULL;
102 static UInt16 sysm3ProcId;
103 static UInt16 appm3ProcId;
104 #endif
105 static Int32 refWakeLockCnt;
107 /* List for storing all registered callback functions */
108 static IpcPower_CallbackElem *IpcPower_callbackList = NULL;
110 /* Module ref count: */
111 static Int curInit = 0;
113 typedef enum IpcPower_SleepMode {
114     IpcPower_SLEEP_MODE_DEEPSLEEP,
115     IpcPower_SLEEP_MODE_WAKELOCK,
116     IpcPower_SLEEP_MODE_WAKEUNLOCK
117 } IpcPower_SleepMode;
119 /* Deep sleep state variable for IpcPower module */
120 static Bool IpcPower_deepSleep = TRUE;
123 /*
124  *  ======== IpcPower_callUserFxns ========
125  */
126 static Void IpcPower_callUserFxns(IpcPower_Event event)
128     IpcPower_CallbackElem *node = IpcPower_callbackList;
130     /* Call the registered functions matching the event */
131     while (node != NULL) {
132         if (node->event == event) {
133             (*(node->callback))(event, node->data);
134         }
135         node = node->next;
136     }
139 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
141     IArg hwiKey;
143     /* Set/Restore the DeepSleep bit if no timer already in use */
144     hwiKey = Hwi_disable();
145     switch (opt) {
146         case IpcPower_SLEEP_MODE_WAKEUNLOCK:
147             if (refWakeLockCnt) {
148                 refWakeLockCnt--;
149             }
150         case IpcPower_SLEEP_MODE_DEEPSLEEP:
151             if (!refWakeLockCnt) {
152                 IpcPower_deepSleep = TRUE;
153             }
154             break;
155         case IpcPower_SLEEP_MODE_WAKELOCK:
156             refWakeLockCnt++;
157             IpcPower_deepSleep = FALSE;
158             break;
159     }
160     Hwi_restore(hwiKey);
163 static inline Void IpcPower_setWugen()
165     REG32(WUGEN_MEVT1) |= WUGEN_INT_MASK;
168 /*
169  *  ======== IpcPower_suspendSwi ========
170  */
171 #define FXNN "IpcPower_suspendSwi"
172 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
174 #ifndef SMP
175     if (MultiProc_self() == sysm3ProcId) {
176 #endif
177         Log_print0(Diags_INFO, FXNN":Core0 Hibernation Swi");
178 #ifndef SMP
179         PowerSuspArgs.pmMasterCore = MASTERCORE;
180     }
181     else if (MultiProc_self() == appm3ProcId) {
182         Log_print0(Diags_INFO, FXNN":Core1 Hibernation Swi");
183         PowerSuspArgs.pmMasterCore = NO_MASTERCORE;
184     }
185 #endif
186     if (refWakeLockCnt) {
187         System_printf("Warning: Wake locks in use\n");
188     }
190     Power_suspend(&PowerSuspArgs);
191 #ifndef SMP
192     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
193     IpcPower_setWugen();
195     Log_print0(Diags_INFO, FXNN":Resume");
196 #endif
198 #undef FXNN
200 /* =============================================================================
201  *  IpcPower Functions:
202  * =============================================================================
203  */
205 #ifndef SMP
206 /*
207  *  ======== IpcPower_suspendTaskFxn ========
208  */
209 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
211     while (curInit) {
212         /* Wait for suspend notification from host-side */
213         Semaphore_pend(IpcPower_semSuspend, BIOS_WAIT_FOREVER);
215         if (curInit) {
216             /* Call pre-suspend preparation function */
217             IpcPower_preSuspend();
219             Swi_post(suspendResumeSwi);
221             /* Call post-resume preparation function */
222             IpcPower_postResume();
223         }
224     }
226     /* Signal the task end */
227     Semaphore_post(IpcPower_semExit);
229 #endif
231 /*
232  *  ======== IpcPower_init ========
233  */
234 Void IpcPower_init()
236     Swi_Params swiParams;
237 #ifndef SMP
238     Task_Params taskParams;
239     UInt coreIdx = Core_getId();
240 #endif
241     Int i;
242     UArg arg;
243     UInt func;
244     Timer_Handle tHandle = NULL;
246     if (curInit++) {
247         return;
248     }
250 #ifndef SMP
251     IpcPower_hibLocks[coreIdx] = 0;
253     sysm3ProcId = MultiProc_getId("CORE0");
254     appm3ProcId = MultiProc_getId("CORE1");
255 #else
256     IpcPower_hibLocks = 0;
257 #endif
258     refWakeLockCnt = 0;
260     for (i = 0; i < Timer_Object_count(); i++) {
261         tHandle = Timer_Object_get(NULL, i);
262         func = (UInt) Timer_getFunc(tHandle, &arg);
263         if (func && ((func == (UInt) ti_sysbios_knl_Clock_doTick__I) ||
264                      (func == (UInt) Clock_tick) ||
265                      (func == (UInt) IpcPower_clockFxn))) {
266             tickTimerHandle = tHandle;
267             break;
268         }
269     }
270     if (tickTimerHandle == NULL) {
271         System_abort("IpcPower_init: Cannot find tickTimer Handle. Custom"
272                         " clock timer functions currently not supported.\n");
273     }
275 #ifndef SMP
276     IpcPower_semSuspend = Semaphore_create(0, NULL, NULL);
277     IpcPower_semExit = Semaphore_create(0, NULL, NULL);
279     Task_Params_init(&taskParams);
280     taskParams.priority = Task_numPriorities - 1; /* Highest priority */
281     taskParams.instance->name = "ti.pm.IpcPower_tskSuspend";
282     IpcPower_tskSuspend = Task_create(IpcPower_suspendTaskFxn, &taskParams,
283                                                                         NULL);
284 #endif
286     Swi_Params_init(&swiParams);
287     swiParams.priority = Swi_numPriorities - 1; /* Max Priority Swi */
288     suspendResumeSwi = Swi_create(IpcPower_suspendSwi, &swiParams, NULL);
290     /*Power settings for saving/restoring context */
291     PowerSuspArgs.rendezvousResume = TRUE;
292     PowerSuspArgs.dmaChannel = CPU_COPY;
293     PowerSuspArgs.intMask31_0 = 0x0;
294 #ifndef SMP
295     PowerSuspArgs.intMask63_32 = 0x0;
296 #else
297     PowerSuspArgs.intMask63_32 = WUGEN_MAILBOX_BIT << 16;
298 #endif
299     PowerSuspArgs.intMask79_64 = 0x0;
300     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
301     IpcPower_setWugen();
304 /*
305  *  ======== IpcPower_exit ========
306  */
307 Void IpcPower_exit()
309     --curInit;
311     if (curInit == 0) {
312 #ifndef SMP
313         /* Unblock PM suspend task */
314         Semaphore_post(IpcPower_semSuspend);
316         /* Wait for task completion */
317         Semaphore_pend(IpcPower_semExit, BIOS_WAIT_FOREVER);
319         /* Delete the suspend task and semaphore */
320         Task_delete(&IpcPower_tskSuspend);
321         Semaphore_delete(&IpcPower_semSuspend);
322         Semaphore_delete(&IpcPower_semExit);
323 #endif
324     }
327 /*
328  *  ======== IpcPower_suspend ========
329  */
330 Void IpcPower_suspend()
332     Assert_isTrue((curInit > 0) , NULL);
334 #ifndef SMP
335     Semaphore_post(IpcPower_semSuspend);
336 #else
337     Swi_post(suspendResumeSwi);
338 #endif
341 /*
342  *  ======== IpcPower_idle ========
343  */
344 Void IpcPower_idle()
346     IpcPower_idleCount++;
348     REG32(M3_SCR_REG) = IpcPower_deepSleep ? SET_DEEPSLEEP : CLR_DEEPSLEEP;
349     asm(" wfi");
352 /*
353  *  ======== IpcPower_wakeLock ========
354  */
355 Void IpcPower_wakeLock()
357     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
360 /*
361  *  ======== IpcPower_wakeUnlock ========
362  */
363 Void IpcPower_wakeUnlock()
365     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
368 /*
369  *  ======== IpcPower_hibernateLock ========
370  */
371 UInt IpcPower_hibernateLock()
373     IArg hwiKey;
374 #ifndef SMP
375     UInt coreIdx = Core_getId();
376 #endif
378     hwiKey = Hwi_disable();
380 #ifndef SMP
381     IpcPower_hibLocks[coreIdx] += 1;
382 #else
383     IpcPower_hibLocks++;
384 #endif
386     Hwi_restore(hwiKey);
388 #ifndef SMP
389     return (IpcPower_hibLocks[coreIdx]);
390 #else
391     return IpcPower_hibLocks;
392 #endif
395 /*
396  *  ======== IpcPower_hibernateUnlock ========
397  */
398 UInt IpcPower_hibernateUnlock()
400     IArg hwiKey;
401 #ifndef SMP
402     UInt coreIdx = Core_getId();
403 #endif
405     hwiKey = Hwi_disable();
407 #ifndef SMP
408     if (IpcPower_hibLocks[coreIdx] > 0) {
409         IpcPower_hibLocks[coreIdx] -= 1;
410     }
411 #else
412     if (IpcPower_hibLocks > 0) {
413         IpcPower_hibLocks--;
414     }
415 #endif
417     Hwi_restore(hwiKey);
419 #ifndef SMP
420     return (IpcPower_hibLocks[coreIdx]);
421 #else
422     return (IpcPower_hibLocks);
423 #endif
426 /*
427  *  ======== IpcPower_canHibernate ========
428  */
429 Bool IpcPower_canHibernate()
431 #ifndef SMP
432     if (IpcPower_hibLocks[0] || IpcPower_hibLocks[1]) {
433 #else
434     if (IpcPower_hibLocks) {
435 #endif
436         return (FALSE);
437     }
439     return (TRUE);
442 /*
443  *  ======== IpcPower_registerCallback ========
444  */
445 #define FXNN "IpcPower_registerCallback"
446 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
447                               Ptr data)
449     IArg hwiKey;
450     IpcPower_CallbackElem **list, *node;
451     BIOS_ThreadType context = BIOS_getThreadType();
453     if ((context != BIOS_ThreadType_Task) &&
454         (context != BIOS_ThreadType_Main)) {
455         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
456         return (IpcPower_E_FAIL);
457     }
459     list = &IpcPower_callbackList;
461     /* Allocate and update new element */
462     node = Memory_alloc(NULL, sizeof(IpcPower_CallbackElem), 0, NULL);
463     if (node == NULL) {
464         Log_print0(Diags_ERROR, FXNN":out of memory\n");
465         return (IpcPower_E_MEMORY);
466     }
468     node->next     = NULL;
469     node->event    = (IpcPower_Event) event;
470     node->callback = cbck;
471     node->data     = data;
473     hwiKey = Hwi_disable();  /* begin: critical section */
474     while (*list != NULL) {
475         list = &(*list)->next;
476     }
477     *list = node;
478     Hwi_restore(hwiKey);  /* end: critical section */
480     return (IpcPower_S_SUCCESS);
482 #undef FXNN
484 /*
485  *  ======== IpcPower_unregisterCallback ========
486  */
487 #define FXNN "IpcPower_unregisterCallback"
488 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
490     IArg hwiKey;
491     IpcPower_CallbackElem **list, *node;
492     Int status = IpcPower_E_FAIL;
493     BIOS_ThreadType context = BIOS_getThreadType();
495     if ((context != BIOS_ThreadType_Task) &&
496         (context != BIOS_ThreadType_Main)) {
497         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
498         return (status);
499     }
502     list = &IpcPower_callbackList;
503     node  = NULL;
505     hwiKey = Hwi_disable();  /* begin: critical section */
506     while (*list != NULL) {
507         if ( ((*list)->callback == cbck) &&
508              ((*list)->event == event) ) {
509             node   = *list;
510             *list  = (*list)->next;
511             status = IpcPower_S_SUCCESS;
512             break;
513         }
514         list = &(*list)->next;
515     }
516     Hwi_restore(hwiKey);  /* end: critical section */
518     if (status == IpcPower_S_SUCCESS) {
519         if (node != NULL) {
520             Memory_free(NULL, node, sizeof(IpcPower_CallbackElem));
521         }
522         else {
523             Log_print0(Diags_ERROR, FXNN":Invalid pointer\n");
524         }
525     }
527     return (status);
529 #undef FXNN
531 /*
532  *  ======== IpcPower_preSuspend ========
533  */
534 Void IpcPower_preSuspend(Void)
536     IpcPower_suspendCount++;
538     /* Call all user registered suspend callback functions */
539     IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
542 /*
543  *  ======== IpcPower_postResume ========
544  */
545 Void IpcPower_postResume(Void)
547     /* Restore timer registers */
548     Timer_restoreRegisters(tickTimerHandle, NULL);
549     Timer_start(tickTimerHandle);
551     /* Call all user registered resume callback functions */
552     IpcPower_callUserFxns(IpcPower_Event_RESUME);
554     IpcPower_resumeCount++;