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)
134 {
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 }
144 }
146 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
147 {
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);
169 }
171 Void IpcPower_getWugenEvtMask(IpcPower_WugenEvtMask *mask)
172 {
173 mask->mevt0 = wugenEvtMask.mevt0;
174 mask->mevt1 = wugenEvtMask.mevt1;
175 }
177 Void IpcPower_setWugenEvtMask(IpcPower_WugenEvtMask *mask)
178 {
179 wugenEvtMask.mevt0 = mask->mevt0;
180 wugenEvtMask.mevt1 = mask->mevt1;
181 }
183 static inline Void IpcPower_getWugen(IpcPower_WugenEvtMask *mask)
184 {
185 mask->mevt0 = REG32(WUGEN_MEVT0);
186 mask->mevt1 = REG32(WUGEN_MEVT1);
187 }
189 static inline Void IpcPower_setWugen(IpcPower_WugenEvtMask *mask)
190 {
191 REG32(WUGEN_MEVT0) = mask->mevt0;
192 REG32(WUGEN_MEVT1) = mask->mevt1;
193 }
195 /*
196 * ======== IpcPower_suspendSwi ========
197 */
198 #define FXNN "IpcPower_suspendSwi"
199 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
200 {
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
224 }
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)
237 {
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);
255 }
256 #endif
258 /*
259 * ======== IpcPower_init ========
260 */
261 Void IpcPower_init()
262 {
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);
344 }
346 /*
347 * ======== IpcPower_exit ========
348 */
349 Void IpcPower_exit()
350 {
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 }
367 }
369 /*
370 * ======== IpcPower_suspend ========
371 */
372 Void IpcPower_suspend()
373 {
374 Assert_isTrue((curInit > 0) , NULL);
376 #ifndef SMP
377 Semaphore_post(IpcPower_semSuspend);
378 #else
379 Swi_post(suspendResumeSwi);
380 #endif
381 }
383 /*
384 * ======== IpcPower_idle ========
385 */
386 Void IpcPower_idle()
387 {
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");
397 }
399 /*
400 * ======== IpcPower_wakeLock ========
401 */
402 Void IpcPower_wakeLock()
403 {
404 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
405 }
407 /*
408 * ======== IpcPower_wakeUnlock ========
409 */
410 Void IpcPower_wakeUnlock()
411 {
412 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
413 }
415 /*
416 * ======== IpcPower_hibernateLock ========
417 */
418 UInt IpcPower_hibernateLock()
419 {
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
440 }
442 /*
443 * ======== IpcPower_hibernateUnlock ========
444 */
445 UInt IpcPower_hibernateUnlock()
446 {
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
471 }
473 /*
474 * ======== IpcPower_canHibernate ========
475 */
476 Bool IpcPower_canHibernate()
477 {
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);
487 }
489 /*
490 * ======== IpcPower_registerCallback ========
491 */
492 #define FXNN "IpcPower_registerCallback"
493 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
494 Ptr data)
495 {
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);
528 }
529 #undef FXNN
531 /*
532 * ======== IpcPower_unregisterCallback ========
533 */
534 #define FXNN "IpcPower_unregisterCallback"
535 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
536 {
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);
575 }
576 #undef FXNN
578 /*
579 * ======== IpcPower_preSuspend ========
580 */
581 Void IpcPower_preSuspend(Void)
582 {
583 IpcPower_suspendCount++;
585 /* Call all user registered suspend callback functions */
586 IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
587 }
589 /*
590 * ======== IpcPower_postResume ========
591 */
592 Void IpcPower_postResume(Void)
593 {
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++;
602 }