More minor coding convention fixes
[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             /* Fall through: */
151         case IpcPower_SLEEP_MODE_DEEPSLEEP:
152             if (!refWakeLockCnt) {
153                 IpcPower_deepSleep = TRUE;
154             }
155             break;
156         case IpcPower_SLEEP_MODE_WAKELOCK:
157             refWakeLockCnt++;
158             IpcPower_deepSleep = FALSE;
159             break;
160     }
161     Hwi_restore(hwiKey);
164 static inline Void IpcPower_setWugen()
166     REG32(WUGEN_MEVT1) |= WUGEN_INT_MASK;
169 /*
170  *  ======== IpcPower_suspendSwi ========
171  */
172 #define FXNN "IpcPower_suspendSwi"
173 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
175 #ifndef SMP
176     if (MultiProc_self() == sysm3ProcId) {
177 #endif
178         Log_print0(Diags_INFO, FXNN":Core0 Hibernation Swi");
179 #ifndef SMP
180         PowerSuspArgs.pmMasterCore = MASTERCORE;
181     }
182     else if (MultiProc_self() == appm3ProcId) {
183         Log_print0(Diags_INFO, FXNN":Core1 Hibernation Swi");
184         PowerSuspArgs.pmMasterCore = NO_MASTERCORE;
185     }
186 #endif
187     if (refWakeLockCnt) {
188         System_printf("Warning: Wake locks in use\n");
189     }
191     Power_suspend(&PowerSuspArgs);
192 #ifndef SMP
193     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
194     IpcPower_setWugen();
196     Log_print0(Diags_INFO, FXNN":Resume");
197 #endif
199 #undef FXNN
201 /* =============================================================================
202  *  IpcPower Functions:
203  * =============================================================================
204  */
206 #ifndef SMP
207 /*
208  *  ======== IpcPower_suspendTaskFxn ========
209  */
210 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
212     while (curInit) {
213         /* Wait for suspend notification from host-side */
214         Semaphore_pend(IpcPower_semSuspend, BIOS_WAIT_FOREVER);
216         if (curInit) {
217             /* Call pre-suspend preparation function */
218             IpcPower_preSuspend();
220             Swi_post(suspendResumeSwi);
222             /* Call post-resume preparation function */
223             IpcPower_postResume();
224         }
225     }
227     /* Signal the task end */
228     Semaphore_post(IpcPower_semExit);
230 #endif
232 /*
233  *  ======== IpcPower_init ========
234  */
235 Void IpcPower_init()
237     Swi_Params swiParams;
238 #ifndef SMP
239     Task_Params taskParams;
240     UInt coreIdx = Core_getId();
241 #endif
242     Int i;
243     UArg arg;
244     UInt func;
245     Timer_Handle tHandle = NULL;
247     if (curInit++) {
248         return;
249     }
251 #ifndef SMP
252     IpcPower_hibLocks[coreIdx] = 0;
254     sysm3ProcId = MultiProc_getId("CORE0");
255     appm3ProcId = MultiProc_getId("CORE1");
256 #else
257     IpcPower_hibLocks = 0;
258 #endif
259     refWakeLockCnt = 0;
261     for (i = 0; i < Timer_Object_count(); i++) {
262         tHandle = Timer_Object_get(NULL, i);
263         func = (UInt) Timer_getFunc(tHandle, &arg);
264         if (func && ((func == (UInt) ti_sysbios_knl_Clock_doTick__I) ||
265                      (func == (UInt) Clock_tick) ||
266                      (func == (UInt) IpcPower_clockFxn))) {
267             tickTimerHandle = tHandle;
268             break;
269         }
270     }
271     if (tickTimerHandle == NULL) {
272         System_abort("IpcPower_init: Cannot find tickTimer Handle. Custom"
273                         " clock timer functions currently not supported.\n");
274     }
276 #ifndef SMP
277     IpcPower_semSuspend = Semaphore_create(0, NULL, NULL);
278     IpcPower_semExit = Semaphore_create(0, NULL, NULL);
280     Task_Params_init(&taskParams);
281     taskParams.priority = Task_numPriorities - 1; /* Highest priority */
282     taskParams.instance->name = "ti.pm.IpcPower_tskSuspend";
283     IpcPower_tskSuspend = Task_create(IpcPower_suspendTaskFxn, &taskParams,
284                                                                         NULL);
285 #endif
287     Swi_Params_init(&swiParams);
288     swiParams.priority = Swi_numPriorities - 1; /* Max Priority Swi */
289     suspendResumeSwi = Swi_create(IpcPower_suspendSwi, &swiParams, NULL);
291     /*Power settings for saving/restoring context */
292     PowerSuspArgs.rendezvousResume = TRUE;
293     PowerSuspArgs.dmaChannel = CPU_COPY;
294     PowerSuspArgs.intMask31_0 = 0x0;
295 #ifndef SMP
296     PowerSuspArgs.intMask63_32 = 0x0;
297 #else
298     PowerSuspArgs.intMask63_32 = WUGEN_MAILBOX_BIT << 16;
299 #endif
300     PowerSuspArgs.intMask79_64 = 0x0;
301     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
302     IpcPower_setWugen();
305 /*
306  *  ======== IpcPower_exit ========
307  */
308 Void IpcPower_exit()
310     --curInit;
312     if (curInit == 0) {
313 #ifndef SMP
314         /* Unblock PM suspend task */
315         Semaphore_post(IpcPower_semSuspend);
317         /* Wait for task completion */
318         Semaphore_pend(IpcPower_semExit, BIOS_WAIT_FOREVER);
320         /* Delete the suspend task and semaphore */
321         Task_delete(&IpcPower_tskSuspend);
322         Semaphore_delete(&IpcPower_semSuspend);
323         Semaphore_delete(&IpcPower_semExit);
324 #endif
325     }
328 /*
329  *  ======== IpcPower_suspend ========
330  */
331 Void IpcPower_suspend()
333     Assert_isTrue((curInit > 0) , NULL);
335 #ifndef SMP
336     Semaphore_post(IpcPower_semSuspend);
337 #else
338     Swi_post(suspendResumeSwi);
339 #endif
342 /*
343  *  ======== IpcPower_idle ========
344  */
345 Void IpcPower_idle()
347     IpcPower_idleCount++;
349     REG32(M3_SCR_REG) = IpcPower_deepSleep ? SET_DEEPSLEEP : CLR_DEEPSLEEP;
350     asm(" wfi");
353 /*
354  *  ======== IpcPower_wakeLock ========
355  */
356 Void IpcPower_wakeLock()
358     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
361 /*
362  *  ======== IpcPower_wakeUnlock ========
363  */
364 Void IpcPower_wakeUnlock()
366     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
369 /*
370  *  ======== IpcPower_hibernateLock ========
371  */
372 UInt IpcPower_hibernateLock()
374     IArg hwiKey;
375 #ifndef SMP
376     UInt coreIdx = Core_getId();
377 #endif
379     hwiKey = Hwi_disable();
381 #ifndef SMP
382     IpcPower_hibLocks[coreIdx] += 1;
383 #else
384     IpcPower_hibLocks++;
385 #endif
387     Hwi_restore(hwiKey);
389 #ifndef SMP
390     return (IpcPower_hibLocks[coreIdx]);
391 #else
392     return IpcPower_hibLocks;
393 #endif
396 /*
397  *  ======== IpcPower_hibernateUnlock ========
398  */
399 UInt IpcPower_hibernateUnlock()
401     IArg hwiKey;
402 #ifndef SMP
403     UInt coreIdx = Core_getId();
404 #endif
406     hwiKey = Hwi_disable();
408 #ifndef SMP
409     if (IpcPower_hibLocks[coreIdx] > 0) {
410         IpcPower_hibLocks[coreIdx] -= 1;
411     }
412 #else
413     if (IpcPower_hibLocks > 0) {
414         IpcPower_hibLocks--;
415     }
416 #endif
418     Hwi_restore(hwiKey);
420 #ifndef SMP
421     return (IpcPower_hibLocks[coreIdx]);
422 #else
423     return (IpcPower_hibLocks);
424 #endif
427 /*
428  *  ======== IpcPower_canHibernate ========
429  */
430 Bool IpcPower_canHibernate()
432 #ifndef SMP
433     if (IpcPower_hibLocks[0] || IpcPower_hibLocks[1]) {
434 #else
435     if (IpcPower_hibLocks) {
436 #endif
437         return (FALSE);
438     }
440     return (TRUE);
443 /*
444  *  ======== IpcPower_registerCallback ========
445  */
446 #define FXNN "IpcPower_registerCallback"
447 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
448                               Ptr data)
450     IArg hwiKey;
451     IpcPower_CallbackElem **list, *node;
452     BIOS_ThreadType context = BIOS_getThreadType();
454     if ((context != BIOS_ThreadType_Task) &&
455         (context != BIOS_ThreadType_Main)) {
456         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
457         return (IpcPower_E_FAIL);
458     }
460     list = &IpcPower_callbackList;
462     /* Allocate and update new element */
463     node = Memory_alloc(NULL, sizeof(IpcPower_CallbackElem), 0, NULL);
464     if (node == NULL) {
465         Log_print0(Diags_ERROR, FXNN":out of memory\n");
466         return (IpcPower_E_MEMORY);
467     }
469     node->next     = NULL;
470     node->event    = (IpcPower_Event) event;
471     node->callback = cbck;
472     node->data     = data;
474     hwiKey = Hwi_disable();  /* begin: critical section */
475     while (*list != NULL) {
476         list = &(*list)->next;
477     }
478     *list = node;
479     Hwi_restore(hwiKey);  /* end: critical section */
481     return (IpcPower_S_SUCCESS);
483 #undef FXNN
485 /*
486  *  ======== IpcPower_unregisterCallback ========
487  */
488 #define FXNN "IpcPower_unregisterCallback"
489 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
491     IArg hwiKey;
492     IpcPower_CallbackElem **list, *node;
493     Int status = IpcPower_E_FAIL;
494     BIOS_ThreadType context = BIOS_getThreadType();
496     if ((context != BIOS_ThreadType_Task) &&
497         (context != BIOS_ThreadType_Main)) {
498         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
499         return (status);
500     }
503     list = &IpcPower_callbackList;
504     node  = NULL;
506     hwiKey = Hwi_disable();  /* begin: critical section */
507     while (*list != NULL) {
508         if ( ((*list)->callback == cbck) &&
509              ((*list)->event == event) ) {
510             node   = *list;
511             *list  = (*list)->next;
512             status = IpcPower_S_SUCCESS;
513             break;
514         }
515         list = &(*list)->next;
516     }
517     Hwi_restore(hwiKey);  /* end: critical section */
519     if (status == IpcPower_S_SUCCESS) {
520         if (node != NULL) {
521             Memory_free(NULL, node, sizeof(IpcPower_CallbackElem));
522         }
523         else {
524             Log_print0(Diags_ERROR, FXNN":Invalid pointer\n");
525         }
526     }
528     return (status);
530 #undef FXNN
532 /*
533  *  ======== IpcPower_preSuspend ========
534  */
535 Void IpcPower_preSuspend(Void)
537     IpcPower_suspendCount++;
539     /* Call all user registered suspend callback functions */
540     IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
543 /*
544  *  ======== IpcPower_postResume ========
545  */
546 Void IpcPower_postResume(Void)
548     /* Restore timer registers */
549     Timer_restoreRegisters(tickTimerHandle, NULL);
550     Timer_start(tickTimerHandle);
552     /* Call all user registered resume callback functions */
553     IpcPower_callUserFxns(IpcPower_Event_RESUME);
555     IpcPower_resumeCount++;