Shared memory cache management
[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 #else
63 #include <ti/sysbios/hal/Core.h>
64 #include <ti/sysbios/family/arm/ducati/smp/Power.h>
65 #endif
67 #include <ti/ipc/MultiProc.h>
68 #include <ti/pm/IpcPower.h>
69 #include "_IpcPower.h"
71 #define MASTERCORE                      (1)
72 #define NO_MASTERCORE                   (0)
73 #define CPU_COPY                       (-1)
74 #define REG32(A)   (*(volatile UInt32 *) (A))
76 #define SET_DEEPSLEEP (REG32(M3_SCR_REG) | (1 << DEEPSLEEP_BIT))
77 #define CLR_DEEPSLEEP (REG32(M3_SCR_REG) & (~(1 << DEEPSLEEP_BIT)))
79 #ifndef SMP
80 #pragma DATA_SECTION(IpcPower_hibLocks, ".ipcpower_data")
81 UInt32 IpcPower_hibLocks[2]; /* One lock for each of the IPU cores */
82 #else
83 static UInt32 IpcPower_hibLocks; /* Only one lock in SMP mode */
84 #endif
86 /* PM transition debug counters */
87 #ifndef SMP
88 UInt32 IpcPower_idleCount = 0;
89 #else
90 UInt32 IpcPower_idleCount[2] = { 0, 0 };
91 #endif
92 UInt32 IpcPower_suspendCount = 0;
93 UInt32 IpcPower_resumeCount = 0;
95 /* Clock function pointer to handle custom clock functions */
96 Void *IpcPower_clockFxn = NULL;
98 /* Handle to store BIOS Tick Timer */
99 static Timer_Handle tickTimerHandle = NULL;
101 static Power_SuspendArgs PowerSuspArgs;
102 static Swi_Handle suspendResumeSwi;
103 #ifndef SMP
104 static Semaphore_Handle IpcPower_semSuspend = NULL;
105 static Semaphore_Handle IpcPower_semExit = NULL;
106 static Task_Handle IpcPower_tskSuspend = NULL;
107 static UInt16 sysm3ProcId;
108 static UInt16 appm3ProcId;
109 #endif
110 static Int32 refWakeLockCnt;
112 /* List for storing all registered callback functions */
113 static IpcPower_CallbackElem *IpcPower_callbackList = NULL;
115 /* Module ref count: */
116 static Int curInit = 0;
118 typedef enum IpcPower_SleepMode {
119     IpcPower_SLEEP_MODE_DEEPSLEEP,
120     IpcPower_SLEEP_MODE_WAKELOCK,
121     IpcPower_SLEEP_MODE_WAKEUNLOCK
122 } IpcPower_SleepMode;
124 /* Deep sleep state variable for IpcPower module */
125 static Bool IpcPower_deepSleep = TRUE;
127 static IpcPower_WugenEvtMask wugenEvtMask;
130 /*
131  *  ======== IpcPower_callUserFxns ========
132  */
133 static Void IpcPower_callUserFxns(IpcPower_Event event)
135     IpcPower_CallbackElem *node = IpcPower_callbackList;
137     /* Call the registered functions matching the event */
138     while (node != NULL) {
139         if (node->event == event) {
140             (*(node->callback))(event, node->data);
141         }
142         node = node->next;
143     }
146 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
148     IArg hwiKey;
150     /* Set/Restore the DeepSleep bit if no timer already in use */
151     hwiKey = Hwi_disable();
152     switch (opt) {
153         case IpcPower_SLEEP_MODE_WAKEUNLOCK:
154             if (refWakeLockCnt) {
155                 refWakeLockCnt--;
156             }
157             /* Fall through: */
158         case IpcPower_SLEEP_MODE_DEEPSLEEP:
159             if (!refWakeLockCnt) {
160                 IpcPower_deepSleep = TRUE;
161             }
162             break;
163         case IpcPower_SLEEP_MODE_WAKELOCK:
164             refWakeLockCnt++;
165             IpcPower_deepSleep = FALSE;
166             break;
167     }
168     Hwi_restore(hwiKey);
171 Void IpcPower_getWugenEvtMask(IpcPower_WugenEvtMask *mask)
173     mask->mevt0 = wugenEvtMask.mevt0;
174     mask->mevt1 = wugenEvtMask.mevt1;
177 Void IpcPower_setWugenEvtMask(IpcPower_WugenEvtMask *mask)
179     wugenEvtMask.mevt0 = mask->mevt0;
180     wugenEvtMask.mevt1 = mask->mevt1;
183 static inline Void IpcPower_getWugen(IpcPower_WugenEvtMask *mask)
185     mask->mevt0 = REG32(WUGEN_MEVT0);
186     mask->mevt1 = REG32(WUGEN_MEVT1);
189 static inline Void IpcPower_setWugen(IpcPower_WugenEvtMask *mask)
191     REG32(WUGEN_MEVT0) = mask->mevt0;
192     REG32(WUGEN_MEVT1) = mask->mevt1;
195 /*
196  *  ======== IpcPower_suspendSwi ========
197  */
198 #define FXNN "IpcPower_suspendSwi"
199 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
201 #ifndef SMP
202     if (MultiProc_self() == sysm3ProcId) {
203 #endif
204         Log_print0(Diags_INFO, FXNN":Core0 Hibernation Swi");
205 #ifndef SMP
206         PowerSuspArgs.pmMasterCore = MASTERCORE;
207     }
208     else if (MultiProc_self() == appm3ProcId) {
209         Log_print0(Diags_INFO, FXNN":Core1 Hibernation Swi");
210         PowerSuspArgs.pmMasterCore = NO_MASTERCORE;
211     }
212 #endif
213     if (refWakeLockCnt) {
214         System_printf("Warning: Wake locks in use\n");
215     }
217     Power_suspend(&PowerSuspArgs);
218 #ifndef SMP
219     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
220     IpcPower_setWugen(&wugenEvtMask);
222     Log_print0(Diags_INFO, FXNN":Resume");
223 #endif
225 #undef FXNN
227 /* =============================================================================
228  *  IpcPower Functions:
229  * =============================================================================
230  */
232 #ifndef SMP
233 /*
234  *  ======== IpcPower_suspendTaskFxn ========
235  */
236 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
238     while (curInit) {
239         /* Wait for suspend notification from host-side */
240         Semaphore_pend(IpcPower_semSuspend, BIOS_WAIT_FOREVER);
242         if (curInit) {
243             /* Call pre-suspend preparation function */
244             IpcPower_preSuspend();
246             Swi_post(suspendResumeSwi);
248             /* Call post-resume preparation function */
249             IpcPower_postResume();
250         }
251     }
253     /* Signal the task end */
254     Semaphore_post(IpcPower_semExit);
256 #endif
258 /*
259  *  ======== IpcPower_init ========
260  */
261 Void IpcPower_init()
263     Swi_Params swiParams;
264 #ifndef SMP
265     Task_Params taskParams;
266     UInt coreIdx = Core_getId();
267 #endif
268     Int i;
269     UArg arg;
270     UInt func;
271     Timer_Handle tHandle = NULL;
273     if (curInit++) {
274         return;
275     }
277 #ifndef SMP
278     IpcPower_hibLocks[coreIdx] = 0;
280     sysm3ProcId = MultiProc_getId("CORE0");
281     appm3ProcId = MultiProc_getId("CORE1");
282 #else
283     IpcPower_hibLocks = 0;
284 #endif
285     refWakeLockCnt = 0;
287     for (i = 0; i < Timer_Object_count(); i++) {
288         tHandle = Timer_Object_get(NULL, i);
289         func = (UInt) Timer_getFunc(tHandle, &arg);
290         if (func && ((func == (UInt) ti_sysbios_knl_Clock_doTick__I) ||
291                      (func == (UInt) Clock_tick) ||
292                      (func == (UInt) IpcPower_clockFxn))) {
293             tickTimerHandle = tHandle;
294             break;
295         }
296     }
297     if (tickTimerHandle == NULL) {
298         System_abort("IpcPower_init: Cannot find tickTimer Handle. Custom"
299                         " clock timer functions currently not supported.\n");
300     }
302 #ifndef SMP
303     IpcPower_semSuspend = Semaphore_create(0, NULL, NULL);
304     IpcPower_semExit = Semaphore_create(0, NULL, NULL);
306     Task_Params_init(&taskParams);
307     taskParams.priority = Task_numPriorities - 1; /* Highest priority */
308     taskParams.instance->name = "ti.pm.IpcPower_tskSuspend";
309     IpcPower_tskSuspend = Task_create(IpcPower_suspendTaskFxn, &taskParams,
310                                                                         NULL);
311 #endif
313     Swi_Params_init(&swiParams);
314     swiParams.priority = Swi_numPriorities - 1; /* Max Priority Swi */
315     suspendResumeSwi = Swi_create(IpcPower_suspendSwi, &swiParams, NULL);
317     /*Power settings for saving/restoring context */
318     PowerSuspArgs.rendezvousResume = TRUE;
319     PowerSuspArgs.dmaChannel = CPU_COPY;
320     PowerSuspArgs.intMask31_0 = 0x0;
321 #ifndef SMP
322     PowerSuspArgs.intMask63_32 = 0x0;
323 #else
324     PowerSuspArgs.intMask63_32 = WUGEN_MAILBOX_BIT << 16;
325 #endif
326     PowerSuspArgs.intMask79_64 = 0x0;
327     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
329     IpcPower_getWugen(&wugenEvtMask);
330 #ifdef OMAP5
331     wugenEvtMask.mevt0 |= OMAP_IPU_WUGEN_INT_MASK0;
332     wugenEvtMask.mevt1 |= OMAP_IPU_WUGEN_INT_MASK1;
333 #else
334     if (MultiProc_self() == MultiProc_getId("IPU2")) {
335         wugenEvtMask.mevt0 |= VAYU_IPU2_WUGEN_INT_MASK0;
336         wugenEvtMask.mevt1 |= VAYU_IPU2_WUGEN_INT_MASK1;
337     }
338     else {
339         wugenEvtMask.mevt0 |= VAYU_IPU1_WUGEN_INT_MASK0;
340         wugenEvtMask.mevt1 |= VAYU_IPU1_WUGEN_INT_MASK1;
341     }
342 #endif
343     IpcPower_setWugen(&wugenEvtMask);
346 /*
347  *  ======== IpcPower_exit ========
348  */
349 Void IpcPower_exit()
351     --curInit;
353     if (curInit == 0) {
354 #ifndef SMP
355         /* Unblock PM suspend task */
356         Semaphore_post(IpcPower_semSuspend);
358         /* Wait for task completion */
359         Semaphore_pend(IpcPower_semExit, BIOS_WAIT_FOREVER);
361         /* Delete the suspend task and semaphore */
362         Task_delete(&IpcPower_tskSuspend);
363         Semaphore_delete(&IpcPower_semSuspend);
364         Semaphore_delete(&IpcPower_semExit);
365 #endif
366     }
369 /*
370  *  ======== IpcPower_suspend ========
371  */
372 Void IpcPower_suspend()
374     Assert_isTrue((curInit > 0) , NULL);
376 #ifndef SMP
377     Semaphore_post(IpcPower_semSuspend);
378 #else
379     Swi_post(suspendResumeSwi);
380 #endif
383 /*
384  *  ======== IpcPower_idle ========
385  */
386 Void IpcPower_idle()
388 #ifndef SMP
389     IpcPower_idleCount++;
390 #else
391     UInt coreId = Core_getId();
392     IpcPower_idleCount[coreId]++;
393 #endif
395     REG32(M3_SCR_REG) = IpcPower_deepSleep ? SET_DEEPSLEEP : CLR_DEEPSLEEP;
396     asm(" wfi");
399 /*
400  *  ======== IpcPower_wakeLock ========
401  */
402 Void IpcPower_wakeLock()
404     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
407 /*
408  *  ======== IpcPower_wakeUnlock ========
409  */
410 Void IpcPower_wakeUnlock()
412     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
415 /*
416  *  ======== IpcPower_hibernateLock ========
417  */
418 UInt IpcPower_hibernateLock()
420     IArg hwiKey;
421 #ifndef SMP
422     UInt coreIdx = Core_getId();
423 #endif
425     hwiKey = Hwi_disable();
427 #ifndef SMP
428     IpcPower_hibLocks[coreIdx] += 1;
429 #else
430     IpcPower_hibLocks++;
431 #endif
433     Hwi_restore(hwiKey);
435 #ifndef SMP
436     return (IpcPower_hibLocks[coreIdx]);
437 #else
438     return IpcPower_hibLocks;
439 #endif
442 /*
443  *  ======== IpcPower_hibernateUnlock ========
444  */
445 UInt IpcPower_hibernateUnlock()
447     IArg hwiKey;
448 #ifndef SMP
449     UInt coreIdx = Core_getId();
450 #endif
452     hwiKey = Hwi_disable();
454 #ifndef SMP
455     if (IpcPower_hibLocks[coreIdx] > 0) {
456         IpcPower_hibLocks[coreIdx] -= 1;
457     }
458 #else
459     if (IpcPower_hibLocks > 0) {
460         IpcPower_hibLocks--;
461     }
462 #endif
464     Hwi_restore(hwiKey);
466 #ifndef SMP
467     return (IpcPower_hibLocks[coreIdx]);
468 #else
469     return (IpcPower_hibLocks);
470 #endif
473 /*
474  *  ======== IpcPower_canHibernate ========
475  */
476 Bool IpcPower_canHibernate()
478 #ifndef SMP
479     if (IpcPower_hibLocks[0] || IpcPower_hibLocks[1]) {
480 #else
481     if (IpcPower_hibLocks) {
482 #endif
483         return (FALSE);
484     }
486     return (TRUE);
489 /*
490  *  ======== IpcPower_registerCallback ========
491  */
492 #define FXNN "IpcPower_registerCallback"
493 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
494                               Ptr data)
496     IArg hwiKey;
497     IpcPower_CallbackElem **list, *node;
498     BIOS_ThreadType context = BIOS_getThreadType();
500     if ((context != BIOS_ThreadType_Task) &&
501         (context != BIOS_ThreadType_Main)) {
502         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
503         return (IpcPower_E_FAIL);
504     }
506     list = &IpcPower_callbackList;
508     /* Allocate and update new element */
509     node = Memory_alloc(NULL, sizeof(IpcPower_CallbackElem), 0, NULL);
510     if (node == NULL) {
511         Log_print0(Diags_ERROR, FXNN":out of memory\n");
512         return (IpcPower_E_MEMORY);
513     }
515     node->next     = NULL;
516     node->event    = (IpcPower_Event) event;
517     node->callback = cbck;
518     node->data     = data;
520     hwiKey = Hwi_disable();  /* begin: critical section */
521     while (*list != NULL) {
522         list = &(*list)->next;
523     }
524     *list = node;
525     Hwi_restore(hwiKey);  /* end: critical section */
527     return (IpcPower_S_SUCCESS);
529 #undef FXNN
531 /*
532  *  ======== IpcPower_unregisterCallback ========
533  */
534 #define FXNN "IpcPower_unregisterCallback"
535 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
537     IArg hwiKey;
538     IpcPower_CallbackElem **list, *node;
539     Int status = IpcPower_E_FAIL;
540     BIOS_ThreadType context = BIOS_getThreadType();
542     if ((context != BIOS_ThreadType_Task) &&
543         (context != BIOS_ThreadType_Main)) {
544         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
545         return (status);
546     }
549     list = &IpcPower_callbackList;
550     node  = NULL;
552     hwiKey = Hwi_disable();  /* begin: critical section */
553     while (*list != NULL) {
554         if ( ((*list)->callback == cbck) &&
555              ((*list)->event == event) ) {
556             node   = *list;
557             *list  = (*list)->next;
558             status = IpcPower_S_SUCCESS;
559             break;
560         }
561         list = &(*list)->next;
562     }
563     Hwi_restore(hwiKey);  /* end: critical section */
565     if (status == IpcPower_S_SUCCESS) {
566         if (node != NULL) {
567             Memory_free(NULL, node, sizeof(IpcPower_CallbackElem));
568         }
569         else {
570             Log_print0(Diags_ERROR, FXNN":Invalid pointer\n");
571         }
572     }
574     return (status);
576 #undef FXNN
578 /*
579  *  ======== IpcPower_preSuspend ========
580  */
581 Void IpcPower_preSuspend(Void)
583     IpcPower_suspendCount++;
585     /* Call all user registered suspend callback functions */
586     IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
589 /*
590  *  ======== IpcPower_postResume ========
591  */
592 Void IpcPower_postResume(Void)
594     /* Restore timer registers */
595     Timer_restoreRegisters(tickTimerHandle, NULL);
596     Timer_start(tickTimerHandle);
598     /* Call all user registered resume callback functions */
599     IpcPower_callUserFxns(IpcPower_Event_RESUME);
601     IpcPower_resumeCount++;