Add power management support to DRA7xx DSP
[ipc/ipcdev.git] / packages / ti / pm / IpcPowerDsp_dra7xx.c
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/System.h>
42 #include <xdc/runtime/Assert.h>
43 #include <xdc/runtime/Memory.h>
44 #include <xdc/runtime/Log.h>
45 #include <xdc/runtime/Diags.h>
47 #include <ti/sysbios/BIOS.h>
48 #include <ti/sysbios/knl/Semaphore.h>
49 #include <ti/sysbios/knl/Task.h>
50 #include <ti/sysbios/hal/Hwi.h>
51 #include <ti/sysbios/knl/Swi.h>
52 #include <ti/sysbios/knl/Clock.h>
53 #include <ti/sysbios/timers/dmtimer/Timer.h>
54 #include <ti/sysbios/family/c66/vayu/Power.h>
56 #include <ti/pm/IpcPower.h>
57 #include "_IpcPower.h"
59 #define REG32(A)   (*(volatile UInt32 *) (A))
61 #define DSP_SYS_SYSCONFIG 0x01D00008
62 #define IDLE_STBY_MASK    0x0000003C
64 #define PDCCMD_REG        0x01810000
65 #define SLEEP_MODE        0x10000
67 static UInt32 IpcPower_hibLock;
68 static Swi_Handle suspendResumeSwi;
69 static Semaphore_Handle IpcPower_semSuspend = NULL;
70 static Semaphore_Handle IpcPower_semExit = NULL;
71 static Task_Handle IpcPower_tskSuspend = NULL;
72 static Int32 refWakeLockCnt;
74 /* List for storing all registered callback functions */
75 static IpcPower_CallbackElem *IpcPower_callbackList = NULL;
77 /* Module ref count: */
78 static Int curInit = 0;
80 typedef enum IpcPower_SleepMode {
81     IpcPower_SLEEP_MODE_DEEPSLEEP,
82     IpcPower_SLEEP_MODE_WAKELOCK,
83     IpcPower_SLEEP_MODE_WAKEUNLOCK
84 } IpcPower_SleepMode;
86 /* Deep sleep state variable for IpcPower module */
87 static Bool IpcPower_deepSleep = TRUE;
89 static IpcPower_WugenEvtMask wugenEvtMask;
91 /* PM transition debug counters */
92 UInt32 IpcPower_idleCount = 0;
93 UInt32 IpcPower_suspendCount = 0;
94 UInt32 IpcPower_resumeCount = 0;
96 /* Handle to store BIOS Tick Timer */
97 static Timer_Handle tickTimerHandle = NULL;
99 /*
100  *  ======== IpcPower_callUserFxns ========
101  */
102 static Void IpcPower_callUserFxns(IpcPower_Event event)
104     IpcPower_CallbackElem *node = IpcPower_callbackList;
106     /* Call the registered functions matching the event */
107     while (node != NULL) {
108         if (node->event == event) {
109             (*(node->callback))(event, node->data);
110         }
111         node = node->next;
112     }
115 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
117     IArg hwiKey;
119     /* Set/Restore the DeepSleep bit if no timer already in use */
120     hwiKey = Hwi_disable();
121     switch (opt) {
122         case IpcPower_SLEEP_MODE_WAKEUNLOCK:
123             if (refWakeLockCnt) {
124                 refWakeLockCnt--;
125             }
126             /* Fall through: */
127         case IpcPower_SLEEP_MODE_DEEPSLEEP:
128             if (!refWakeLockCnt) {
129                 IpcPower_deepSleep = TRUE;
130             }
131             break;
132         case IpcPower_SLEEP_MODE_WAKELOCK:
133             refWakeLockCnt++;
134             IpcPower_deepSleep = FALSE;
135             break;
136     }
137     Hwi_restore(hwiKey);
140 /*
141  *  ======== IpcPower_getWugenEvtMask ========
142  */
143 Void IpcPower_getWugenEvtMask(IpcPower_WugenEvtMask *mask)
145     mask->mevt0 = wugenEvtMask.mevt0;
146     mask->mevt1 = wugenEvtMask.mevt1;
149 /*
150  *  ======== IpcPower_setWugenEvtMask ========
151  */
152 Void IpcPower_setWugenEvtMask(IpcPower_WugenEvtMask *mask)
154     wugenEvtMask.mevt0 = mask->mevt0;
155     wugenEvtMask.mevt1 = mask->mevt1;
158 /*
159  * On the C66 DSPs, there is no official WUGEN IP per se, but the
160  * DSP_SYS_IRQWAKEEN0/1 registers have the same role as WUGEN
161  * event mask registers in terms of determining which interrupts
162  * cause wakeup event generation.
163  */
164 static inline Void IpcPower_getWugen(IpcPower_WugenEvtMask *mask)
166     mask->mevt0 = REG32(DSP_SYS_IRQWAKEEN0);
167     mask->mevt1 = REG32(DSP_SYS_IRQWAKEEN1);
170 static inline Void IpcPower_setWugen(IpcPower_WugenEvtMask *mask)
172     REG32(DSP_SYS_IRQWAKEEN0) |= mask->mevt0;
173     REG32(DSP_SYS_IRQWAKEEN1) |= mask->mevt1;
177 /*
178  *  ======== IpcPower_suspendSwi ========
179  */
180 #define FXNN "IpcPower_suspendSwi"
181 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
183     if (refWakeLockCnt) {
184         Log_print0(Diags_INFO, FXNN":Warning: Wake locks in use\n");
185     }
187     /* Invoke the BIOS suspend routine */
188     Power_suspend(Power_Suspend_HIBERNATE);
190     Log_print0(Diags_INFO, FXNN":Resume\n");
192 #undef FXNN
194 /*
195  *  ======== IpcPower_suspendTaskFxn ========
196  */
197 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
199     while (curInit) {
200         /* Wait for suspend notification from host-side */
201         Semaphore_pend(IpcPower_semSuspend, BIOS_WAIT_FOREVER);
203         if (curInit) {
204             /* Call pre-suspend preparation function */
205             IpcPower_preSuspend();
207             Swi_post(suspendResumeSwi);
209             /* Call post-resume preparation function */
210             IpcPower_postResume();
211         }
212     }
214     /* Signal the task end */
215     Semaphore_post(IpcPower_semExit);
218 /*
219  *  ======== IpcPower_init ========
220  */
221 Void IpcPower_init()
223     Swi_Params swiParams;
224     Task_Params taskParams;
225     Int i;
226     UArg arg;
227     UInt func;
228     Timer_Handle tHandle = NULL;
230     if (curInit++) {
231         return;
232     }
234     IpcPower_hibLock = 0;
235     refWakeLockCnt = 0;
237     for (i = 0; i < Timer_Object_count(); i++) {
238         tHandle = Timer_Object_get(NULL, i);
239         func = (UInt) Timer_getFunc(tHandle, &arg);
240         if (func && ((func == (UInt) ti_sysbios_knl_Clock_doTick__I) ||
241                      (func == (UInt) Clock_tick))) {
242             tickTimerHandle = tHandle;
243             break;
244         }
245     }
246     if (tickTimerHandle == NULL) {
247         System_abort("IpcPower_init: Cannot find tickTimer Handle. Custom"
248                         " clock timer functions currently not supported.\n");
249     }
251     IpcPower_semSuspend = Semaphore_create(0, NULL, NULL);
252     IpcPower_semExit = Semaphore_create(0, NULL, NULL);
254     Task_Params_init(&taskParams);
255     taskParams.priority = Task_numPriorities - 1; /* Highest priority */
256     taskParams.instance->name = "ti.pm.IpcPower_tskSuspend";
257     IpcPower_tskSuspend = Task_create(IpcPower_suspendTaskFxn, &taskParams,
258         NULL);
260     Swi_Params_init(&swiParams);
261     swiParams.priority = Swi_numPriorities - 1; /* Max Priority Swi */
262     suspendResumeSwi = Swi_create(IpcPower_suspendSwi, &swiParams, NULL);
264     IpcPower_sleepMode(IpcPower_SLEEP_MODE_DEEPSLEEP);
266     /* Setup IDLEMODE and STANDBYMODE in DSP_SYS_SYSCONFIG */
267     REG32(DSP_SYS_SYSCONFIG) |= IDLE_STBY_MASK;
269     /* Setup DSP_SYS_IRQWAKEEN0/1 */
270     IpcPower_getWugen(&wugenEvtMask);
271     /* TODO: Add DSP2 support */
272     wugenEvtMask.mevt0 |= VAYU_DSP1_WUGEN_INT_MASK0;
273     wugenEvtMask.mevt1 |= VAYU_DSP1_WUGEN_INT_MASK1;
274     IpcPower_setWugen(&wugenEvtMask);
277 /*
278  *  ======== IpcPower_exit ========
279  */
280 Void IpcPower_exit()
283     --curInit;
285     if (curInit == 0) {
286         /* Unblock PM suspend task */
287         Semaphore_post(IpcPower_semSuspend);
289         /* Wait for task completion */
290         Semaphore_pend(IpcPower_semExit, BIOS_WAIT_FOREVER);
292         /* Delete the suspend task and semaphore */
293         Task_delete(&IpcPower_tskSuspend);
294         Semaphore_delete(&IpcPower_semSuspend);
295         Semaphore_delete(&IpcPower_semExit);
296     }
299 /*
300  *  ======== IpcPower_suspend ========
301  */
302 Void IpcPower_suspend()
304     Assert_isTrue((curInit > 0) , NULL);
306     Semaphore_post(IpcPower_semSuspend);
309 /*
310  *  ======== IpcPower_idle ========
311  */
312 Void IpcPower_idle()
314     IpcPower_idleCount++;
316     if (IpcPower_deepSleep) {
317         /* Set deepsleep mode */
318         REG32(PDCCMD_REG) = SLEEP_MODE;
319         REG32(PDCCMD_REG);
320     }
322     asm(" mfence");
323     asm(" nop");
324     asm(" nop");
325     asm(" nop");
326     asm(" nop");
327     asm(" nop");
328     asm(" nop");
329     asm(" nop");
330     asm(" idle");
331     asm(" nop");
332     asm(" nop");
333     asm(" nop");
334     asm(" nop");
335     asm(" nop");
336     asm(" nop");
337     asm(" nop");
340 /*
341  *  ======== IpcPower_wakeLock ========
342  */
343 Void IpcPower_wakeLock()
345     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
348 /*
349  *  ======== IpcPower_wakeUnlock ========
350  */
351 Void IpcPower_wakeUnlock()
353     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
356 /*
357  *  ======== IpcPower_hibernateLock ========
358  */
359 UInt IpcPower_hibernateLock()
361     IArg hwiKey;
363     hwiKey = Hwi_disable();
365     IpcPower_hibLock++;
367     Hwi_restore(hwiKey);
369     return (IpcPower_hibLock);
372 /*
373  *  ======== IpcPower_hibernateUnlock ========
374  */
375 UInt IpcPower_hibernateUnlock()
377     IArg hwiKey;
379     hwiKey = Hwi_disable();
381     if (IpcPower_hibLock > 0) {
382         IpcPower_hibLock--;
383     }
385     Hwi_restore(hwiKey);
387     return (IpcPower_hibLock);
391 /*
392  *  ======== IpcPower_canHibernate ========
393  */
394 Bool IpcPower_canHibernate()
396     if (IpcPower_hibLock) {
397         return (FALSE);
398     }
400     return (TRUE);
403 /*
404  *  ======== IpcPower_registerCallback ========
405  */
406 #define FXNN "IpcPower_registerCallback"
407 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
408                               Ptr data)
410     IArg hwiKey;
411     IpcPower_CallbackElem **list, *node;
412     BIOS_ThreadType context = BIOS_getThreadType();
414     if ((context != BIOS_ThreadType_Task) &&
415         (context != BIOS_ThreadType_Main)) {
416         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
417         return (IpcPower_E_FAIL);
418     }
420     list = &IpcPower_callbackList;
422     /* Allocate and update new element */
423     node = Memory_alloc(NULL, sizeof(IpcPower_CallbackElem), 0, NULL);
424     if (node == NULL) {
425         Log_print0(Diags_ERROR, FXNN":out of memory\n");
426         return (IpcPower_E_MEMORY);
427     }
429     node->next     = NULL;
430     node->event    = (IpcPower_Event) event;
431     node->callback = cbck;
432     node->data     = data;
434     hwiKey = Hwi_disable();  /* begin: critical section */
435     while (*list != NULL) {
436         list = &(*list)->next;
437     }
438     *list = node;
439     Hwi_restore(hwiKey);  /* end: critical section */
441     return (IpcPower_S_SUCCESS);
443 #undef FXNN
445 /*
446  *  ======== IpcPower_unregisterCallback ========
447  */
448 #define FXNN "IpcPower_unregisterCallback"
449 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
451     IArg hwiKey;
452     IpcPower_CallbackElem **list, *node;
453     Int status = IpcPower_E_FAIL;
454     BIOS_ThreadType context = BIOS_getThreadType();
456     if ((context != BIOS_ThreadType_Task) &&
457         (context != BIOS_ThreadType_Main)) {
458         Log_print0(Diags_ERROR, FXNN":Invalid context\n");
459         return (status);
460     }
462     list = &IpcPower_callbackList;
463     node  = NULL;
465     hwiKey = Hwi_disable();  /* begin: critical section */
466     while (*list != NULL) {
467         if ( ((*list)->callback == cbck) &&
468              ((*list)->event == event) ) {
469             node   = *list;
470             *list  = (*list)->next;
471             status = IpcPower_S_SUCCESS;
472             break;
473         }
474         list = &(*list)->next;
475     }
476     Hwi_restore(hwiKey);  /* end: critical section */
478     if (status == IpcPower_S_SUCCESS) {
479         if (node != NULL) {
480             Memory_free(NULL, node, sizeof(IpcPower_CallbackElem));
481         }
482         else {
483             Log_print0(Diags_ERROR, FXNN":Invalid pointer\n");
484         }
485     }
487     return (status);
489 #undef FXNN
491 /*
492  *  ======== IpcPower_preSuspend ========
493  */
494 Void IpcPower_preSuspend(Void)
496     IpcPower_suspendCount++;
498     /* Call all user registered suspend callback functions */
499     IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
502 /*
503  *  ======== IpcPower_postResume ========
504  */
505 Void IpcPower_postResume(Void)
507     /* Restore timer registers */
508     Timer_restoreRegisters(tickTimerHandle, NULL);
509     Timer_start(tickTimerHandle);
511     /* Call all user registered resume callback functions */
512     IpcPower_callUserFxns(IpcPower_Event_RESUME);
514     IpcPower_resumeCount++;