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 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);
161 }
163 static inline Void IpcPower_setWugen()
164 {
165 REG32(WUGEN_MEVT1) |= WUGEN_INT_MASK;
166 }
168 /*
169 * ======== IpcPower_suspendSwi ========
170 */
171 #define FXNN "IpcPower_suspendSwi"
172 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
173 {
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
197 }
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)
210 {
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);
228 }
229 #endif
231 /*
232 * ======== IpcPower_init ========
233 */
234 Void IpcPower_init()
235 {
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();
302 }
304 /*
305 * ======== IpcPower_exit ========
306 */
307 Void IpcPower_exit()
308 {
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 }
325 }
327 /*
328 * ======== IpcPower_suspend ========
329 */
330 Void IpcPower_suspend()
331 {
332 Assert_isTrue((curInit > 0) , NULL);
334 #ifndef SMP
335 Semaphore_post(IpcPower_semSuspend);
336 #else
337 Swi_post(suspendResumeSwi);
338 #endif
339 }
341 /*
342 * ======== IpcPower_idle ========
343 */
344 Void IpcPower_idle()
345 {
346 IpcPower_idleCount++;
348 REG32(M3_SCR_REG) = IpcPower_deepSleep ? SET_DEEPSLEEP : CLR_DEEPSLEEP;
349 asm(" wfi");
350 }
352 /*
353 * ======== IpcPower_wakeLock ========
354 */
355 Void IpcPower_wakeLock()
356 {
357 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
358 }
360 /*
361 * ======== IpcPower_wakeUnlock ========
362 */
363 Void IpcPower_wakeUnlock()
364 {
365 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
366 }
368 /*
369 * ======== IpcPower_hibernateLock ========
370 */
371 UInt IpcPower_hibernateLock()
372 {
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
393 }
395 /*
396 * ======== IpcPower_hibernateUnlock ========
397 */
398 UInt IpcPower_hibernateUnlock()
399 {
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
424 }
426 /*
427 * ======== IpcPower_canHibernate ========
428 */
429 Bool IpcPower_canHibernate()
430 {
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);
440 }
442 /*
443 * ======== IpcPower_registerCallback ========
444 */
445 #define FXNN "IpcPower_registerCallback"
446 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
447 Ptr data)
448 {
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);
481 }
482 #undef FXNN
484 /*
485 * ======== IpcPower_unregisterCallback ========
486 */
487 #define FXNN "IpcPower_unregisterCallback"
488 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
489 {
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);
528 }
529 #undef FXNN
531 /*
532 * ======== IpcPower_preSuspend ========
533 */
534 Void IpcPower_preSuspend(Void)
535 {
536 IpcPower_suspendCount++;
538 /* Call all user registered suspend callback functions */
539 IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
540 }
542 /*
543 * ======== IpcPower_postResume ========
544 */
545 Void IpcPower_postResume(Void)
546 {
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++;
555 }