1 /*
2 * Copyright (c) 2015, 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 IpcPowerDsp_dra7xx.c
34 *
35 * @brief Power Managment for DRA7xx DSP.
36 *
37 * ============================================================================
38 */
40 #include <xdc/std.h>
41 #include <xdc/runtime/Assert.h>
42 #include <xdc/runtime/Memory.h>
43 #include <xdc/runtime/Log.h>
44 #include <xdc/runtime/Diags.h>
46 #include <ti/sysbios/BIOS.h>
47 #include <ti/sysbios/knl/Semaphore.h>
48 #include <ti/sysbios/knl/Task.h>
49 #include <ti/sysbios/hal/Hwi.h>
50 #include <ti/sysbios/knl/Clock.h>
51 #include <ti/sysbios/timers/dmtimer/Timer.h>
52 #include <ti/sysbios/family/c66/vayu/Power.h>
54 #include <ti/pm/IpcPower.h>
55 #include "_IpcPower.h"
57 #define REG32(A) (*(volatile UInt32 *) (A))
59 #define DSP_SYS_SYSCONFIG 0x01D00008
60 #define IDLE_STBY_MASK 0x0000003C
62 #define PDCCMD_REG 0x01810000
63 #define SLEEP_MASK 0x10000
64 #define AWAKE_MASK 0xFFFEFFFF
66 static UInt32 IpcPower_hibLock;
67 static Semaphore_Handle IpcPower_semSuspend = NULL;
68 static Semaphore_Handle IpcPower_semExit = NULL;
69 static Task_Handle IpcPower_tskSuspend = NULL;
70 static Int32 refWakeLockCnt;
72 /* List for storing all registered callback functions */
73 static IpcPower_CallbackElem *IpcPower_callbackList = NULL;
75 /* Module ref count: */
76 static Int curInit = 0;
78 typedef enum IpcPower_SleepMode {
79 IpcPower_SLEEP_MODE_DEEPSLEEP,
80 IpcPower_SLEEP_MODE_WAKELOCK,
81 IpcPower_SLEEP_MODE_WAKEUNLOCK
82 } IpcPower_SleepMode;
84 static IpcPower_WugenEvtMask wugenEvtMask;
86 /* PM transition debug counters */
87 UInt32 IpcPower_idleCount = 0;
88 UInt32 IpcPower_suspendCount = 0;
89 UInt32 IpcPower_resumeCount = 0;
91 /* Handle to store BIOS Tick Timer */
92 static Timer_Handle tickTimerHandle = NULL;
94 /*
95 * Assembly function that calls idle using a workaround for a silicon bug that
96 * would hang the CPU when prefetch is enabled.
97 */
98 extern Void IpcPower_callIdle();
100 /*
101 * ======== IpcPower_callUserFxns ========
102 */
103 static Void IpcPower_callUserFxns(IpcPower_Event event)
104 {
105 IpcPower_CallbackElem *node = IpcPower_callbackList;
107 /* Call the registered functions matching the event */
108 while (node != NULL) {
109 if (node->event == event) {
110 (*(node->callback))(event, node->data);
111 }
112 node = node->next;
113 }
114 }
116 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
117 {
118 IArg hwiKey;
120 /* Set/Restore the DeepSleep bit if no timer already in use */
121 hwiKey = Hwi_disable();
122 switch (opt) {
123 case IpcPower_SLEEP_MODE_WAKEUNLOCK:
124 if (refWakeLockCnt) {
125 refWakeLockCnt--;
126 }
127 /* Fall through: */
128 case IpcPower_SLEEP_MODE_DEEPSLEEP:
129 if (!refWakeLockCnt) {
130 REG32(PDCCMD_REG) |= SLEEP_MASK;
131 REG32(PDCCMD_REG);
132 }
133 break;
134 case IpcPower_SLEEP_MODE_WAKELOCK:
135 refWakeLockCnt++;
136 REG32(PDCCMD_REG) &= AWAKE_MASK;
137 REG32(PDCCMD_REG);
138 break;
139 }
140 Hwi_restore(hwiKey);
141 }
143 /*
144 * ======== IpcPower_getWugenEvtMask ========
145 */
146 Void IpcPower_getWugenEvtMask(IpcPower_WugenEvtMask *mask)
147 {
148 mask->mevt0 = wugenEvtMask.mevt0;
149 mask->mevt1 = wugenEvtMask.mevt1;
150 }
152 /*
153 * ======== IpcPower_setWugenEvtMask ========
154 */
155 Void IpcPower_setWugenEvtMask(IpcPower_WugenEvtMask *mask)
156 {
157 wugenEvtMask.mevt0 = mask->mevt0;
158 wugenEvtMask.mevt1 = mask->mevt1;
159 }
161 /*
162 * On the C66 DSPs, there is no official WUGEN IP per se, but the
163 * DSP_SYS_IRQWAKEEN0/1 registers have the same role as WUGEN
164 * event mask registers in terms of determining which interrupts
165 * cause wakeup event generation.
166 */
167 static inline Void IpcPower_getWugen(IpcPower_WugenEvtMask *mask)
168 {
169 mask->mevt0 = REG32(DSP_SYS_IRQWAKEEN0);
170 mask->mevt1 = REG32(DSP_SYS_IRQWAKEEN1);
171 }
173 static inline Void IpcPower_setWugen(IpcPower_WugenEvtMask *mask)
174 {
175 REG32(DSP_SYS_IRQWAKEEN0) |= mask->mevt0;
176 REG32(DSP_SYS_IRQWAKEEN1) |= mask->mevt1;
177 }
179 /*
180 * ======== IpcPower_suspendTaskFxn ========
181 */
182 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
183 {
184 while (curInit) {
185 /* Wait for suspend notification from host-side */
186 Semaphore_pend(IpcPower_semSuspend, BIOS_WAIT_FOREVER);
188 if (curInit) {
189 /* Call pre-suspend preparation function */
190 IpcPower_preSuspend();
192 Power_suspend(Power_Suspend_HIBERNATE);
194 /* Call post-resume preparation function */
195 IpcPower_postResume();
196 }
197 }
199 /* Signal the task end */
200 Semaphore_post(IpcPower_semExit);
201 }
203 /*
204 * ======== IpcPower_init ========
205 */
206 #define FXNN "IpcPower_init"
207 Void IpcPower_init()
208 {
209 extern cregister volatile UInt DNUM;
210 Task_Params taskParams;
211 Int i;
212 UArg arg;
213 UInt func;
214 Timer_Handle tHandle = NULL;
216 if (curInit++) {
217 return;
218 }
220 IpcPower_hibLock = 0;
221 refWakeLockCnt = 0;
223 for (i = 0; i < Timer_Object_count(); i++) {
224 tHandle = Timer_Object_get(NULL, i);
225 func = (UInt) Timer_getFunc(tHandle, &arg);
226 if (func && ((func == (UInt) ti_sysbios_knl_Clock_doTick__I) ||
227 (func == (UInt) Clock_tick))) {
228 tickTimerHandle = tHandle;
229 break;
230 }
231 }
232 if (tickTimerHandle == NULL) {
233 Log_print0(Diags_INFO, FXNN": Cannot find tickTimer Handle. "
234 "Custom clock timer functions currently not "
235 "supported.\n");
236 }
238 IpcPower_semSuspend = Semaphore_create(0, NULL, NULL);
239 IpcPower_semExit = Semaphore_create(0, NULL, NULL);
241 Task_Params_init(&taskParams);
242 taskParams.priority = Task_numPriorities - 1; /* Highest priority */
243 taskParams.instance->name = "ti.pm.IpcPower_tskSuspend";
244 IpcPower_tskSuspend = Task_create(IpcPower_suspendTaskFxn, &taskParams,
245 NULL);
247 IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
249 /* Setup IDLEMODE and STANDBYMODE in DSP_SYS_SYSCONFIG */
250 REG32(DSP_SYS_SYSCONFIG) |= IDLE_STBY_MASK;
252 /* Setup DSP_SYS_IRQWAKEEN0/1 */
253 IpcPower_getWugen(&wugenEvtMask);
254 if (DNUM == 0) { /* DSP1 */
255 wugenEvtMask.mevt0 |= VAYU_DSP1_WUGEN_INT_MASK0;
256 wugenEvtMask.mevt1 |= VAYU_DSP1_WUGEN_INT_MASK1;
257 }
258 else {
259 wugenEvtMask.mevt0 |= VAYU_DSP2_WUGEN_INT_MASK0;
260 wugenEvtMask.mevt1 |= VAYU_DSP2_WUGEN_INT_MASK1;
261 }
262 IpcPower_setWugen(&wugenEvtMask);
263 }
264 #undef FXNN
266 /*
267 * ======== IpcPower_exit ========
268 */
269 Void IpcPower_exit()
270 {
272 --curInit;
274 if (curInit == 0) {
275 /* Unblock PM suspend task */
276 Semaphore_post(IpcPower_semSuspend);
278 /* Wait for task completion */
279 Semaphore_pend(IpcPower_semExit, BIOS_WAIT_FOREVER);
281 /* Delete the suspend task and semaphore */
282 Task_delete(&IpcPower_tskSuspend);
283 Semaphore_delete(&IpcPower_semSuspend);
284 Semaphore_delete(&IpcPower_semExit);
285 }
286 }
288 /*
289 * ======== IpcPower_suspend ========
290 */
291 Void IpcPower_suspend()
292 {
293 Assert_isTrue((curInit > 0) , NULL);
295 Semaphore_post(IpcPower_semSuspend);
296 }
298 /*
299 * ======== IpcPower_idle ========
300 */
301 Void IpcPower_idle()
302 {
303 Hwi_disable();
304 IpcPower_idleCount++;
306 IpcPower_callIdle();
307 }
309 /*
310 * ======== IpcPower_wakeLock ========
311 */
312 Void IpcPower_wakeLock()
313 {
314 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
315 }
317 /*
318 * ======== IpcPower_wakeUnlock ========
319 */
320 Void IpcPower_wakeUnlock()
321 {
322 IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
323 }
325 /*
326 * ======== IpcPower_hibernateLock ========
327 */
328 UInt IpcPower_hibernateLock()
329 {
330 IArg hwiKey;
332 hwiKey = Hwi_disable();
334 IpcPower_hibLock++;
336 Hwi_restore(hwiKey);
338 return (IpcPower_hibLock);
339 }
341 /*
342 * ======== IpcPower_hibernateUnlock ========
343 */
344 UInt IpcPower_hibernateUnlock()
345 {
346 IArg hwiKey;
348 hwiKey = Hwi_disable();
350 if (IpcPower_hibLock > 0) {
351 IpcPower_hibLock--;
352 }
354 Hwi_restore(hwiKey);
356 return (IpcPower_hibLock);
357 }
360 /*
361 * ======== IpcPower_canHibernate ========
362 */
363 Bool IpcPower_canHibernate()
364 {
365 if (IpcPower_hibLock) {
366 return (FALSE);
367 }
369 return (TRUE);
370 }
372 /*
373 * ======== IpcPower_registerCallback ========
374 */
375 #define FXNN "IpcPower_registerCallback"
376 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
377 Ptr data)
378 {
379 IArg hwiKey;
380 IpcPower_CallbackElem **list, *node;
381 BIOS_ThreadType context = BIOS_getThreadType();
383 if ((context != BIOS_ThreadType_Task) &&
384 (context != BIOS_ThreadType_Main)) {
385 Log_print0(Diags_ERROR, FXNN":Invalid context\n");
386 return (IpcPower_E_FAIL);
387 }
389 list = &IpcPower_callbackList;
391 /* Allocate and update new element */
392 node = Memory_alloc(NULL, sizeof(IpcPower_CallbackElem), 0, NULL);
393 if (node == NULL) {
394 Log_print0(Diags_ERROR, FXNN":out of memory\n");
395 return (IpcPower_E_MEMORY);
396 }
398 node->next = NULL;
399 node->event = (IpcPower_Event) event;
400 node->callback = cbck;
401 node->data = data;
403 hwiKey = Hwi_disable(); /* begin: critical section */
404 while (*list != NULL) {
405 list = &(*list)->next;
406 }
407 *list = node;
408 Hwi_restore(hwiKey); /* end: critical section */
410 return (IpcPower_S_SUCCESS);
411 }
412 #undef FXNN
414 /*
415 * ======== IpcPower_unregisterCallback ========
416 */
417 #define FXNN "IpcPower_unregisterCallback"
418 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
419 {
420 IArg hwiKey;
421 IpcPower_CallbackElem **list, *node;
422 Int status = IpcPower_E_FAIL;
423 BIOS_ThreadType context = BIOS_getThreadType();
425 if ((context != BIOS_ThreadType_Task) &&
426 (context != BIOS_ThreadType_Main)) {
427 Log_print0(Diags_ERROR, FXNN":Invalid context\n");
428 return (status);
429 }
431 list = &IpcPower_callbackList;
432 node = NULL;
434 hwiKey = Hwi_disable(); /* begin: critical section */
435 while (*list != NULL) {
436 if ( ((*list)->callback == cbck) &&
437 ((*list)->event == event) ) {
438 node = *list;
439 *list = (*list)->next;
440 status = IpcPower_S_SUCCESS;
441 break;
442 }
443 list = &(*list)->next;
444 }
445 Hwi_restore(hwiKey); /* end: critical section */
447 if (status == IpcPower_S_SUCCESS) {
448 if (node != NULL) {
449 Memory_free(NULL, node, sizeof(IpcPower_CallbackElem));
450 }
451 else {
452 Log_print0(Diags_ERROR, FXNN":Invalid pointer\n");
453 }
454 }
456 return (status);
457 }
458 #undef FXNN
460 /*
461 * ======== IpcPower_preSuspend ========
462 */
463 Void IpcPower_preSuspend(Void)
464 {
465 IpcPower_suspendCount++;
467 /* Call all user registered suspend callback functions */
468 IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
469 }
471 /*
472 * ======== IpcPower_postResume ========
473 */
474 Void IpcPower_postResume(Void)
475 {
476 /* Restore timer registers */
477 if (tickTimerHandle != NULL) {
478 Timer_restoreRegisters(tickTimerHandle, NULL);
479 Timer_start(tickTimerHandle);
480 }
482 /* Call all user registered resume callback functions */
483 IpcPower_callUserFxns(IpcPower_Event_RESUME);
484 IpcPower_resumeCount++;
485 }