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)
127 {
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 }
137 }
139 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
140 {
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);
162 }
164 static inline Void IpcPower_setWugen()
165 {
166 REG32(WUGEN_MEVT1) |= WUGEN_INT_MASK;
167 }
169 /*
170 * ======== IpcPower_suspendSwi ========
171 */
172 #define FXNN "IpcPower_suspendSwi"
173 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
174 {
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
198 }
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)
211 {
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);
229 }
230 #endif
232 /*
233 * ======== IpcPower_init ========
234 */
235 Void IpcPower_init()
236 {
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();
303 }
305 /*
306 * ======== IpcPower_exit ========
307 */
308 Void IpcPower_exit()
309 {
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 }
326 }
328 /*
329 * ======== IpcPower_suspend ========
330 */
331 Void IpcPower_suspend()
332 {
333 Assert_isTrue((curInit > 0) , NULL);
335 #ifndef SMP
336 Semaphore_post(IpcPower_semSuspend);
337 #else
338 Swi_post(suspendResumeSwi);
339 #endif
340 }
342 /*
343 * ======== IpcPower_idle ========
344 */
345 Void IpcPower_idle()
346 {
347 IpcPower_idleCount++;
349 REG32(M3_SCR_REG) = IpcPower_deepSleep ? SET_DEEPSLEEP : CLR_DEEPSLEEP;
350 asm(" wfi");
351 }
353 /*
354 * ======== IpcPower_wakeLock ========
355 */
356 Void IpcPower_wakeLock()
357 {
358 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
359 }
361 /*
362 * ======== IpcPower_wakeUnlock ========
363 */
364 Void IpcPower_wakeUnlock()
365 {
366 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
367 }
369 /*
370 * ======== IpcPower_hibernateLock ========
371 */
372 UInt IpcPower_hibernateLock()
373 {
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
394 }
396 /*
397 * ======== IpcPower_hibernateUnlock ========
398 */
399 UInt IpcPower_hibernateUnlock()
400 {
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
425 }
427 /*
428 * ======== IpcPower_canHibernate ========
429 */
430 Bool IpcPower_canHibernate()
431 {
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);
441 }
443 /*
444 * ======== IpcPower_registerCallback ========
445 */
446 #define FXNN "IpcPower_registerCallback"
447 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
448 Ptr data)
449 {
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);
482 }
483 #undef FXNN
485 /*
486 * ======== IpcPower_unregisterCallback ========
487 */
488 #define FXNN "IpcPower_unregisterCallback"
489 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
490 {
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);
529 }
530 #undef FXNN
532 /*
533 * ======== IpcPower_preSuspend ========
534 */
535 Void IpcPower_preSuspend(Void)
536 {
537 IpcPower_suspendCount++;
539 /* Call all user registered suspend callback functions */
540 IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
541 }
543 /*
544 * ======== IpcPower_postResume ========
545 */
546 Void IpcPower_postResume(Void)
547 {
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++;
556 }