Shared memory cache management
[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/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)
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     }
116 static inline Void IpcPower_sleepMode(IpcPower_SleepMode opt)
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);
143 /*
144  *  ======== IpcPower_getWugenEvtMask ========
145  */
146 Void IpcPower_getWugenEvtMask(IpcPower_WugenEvtMask *mask)
148     mask->mevt0 = wugenEvtMask.mevt0;
149     mask->mevt1 = wugenEvtMask.mevt1;
152 /*
153  *  ======== IpcPower_setWugenEvtMask ========
154  */
155 Void IpcPower_setWugenEvtMask(IpcPower_WugenEvtMask *mask)
157     wugenEvtMask.mevt0 = mask->mevt0;
158     wugenEvtMask.mevt1 = mask->mevt1;
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)
169     mask->mevt0 = REG32(DSP_SYS_IRQWAKEEN0);
170     mask->mevt1 = REG32(DSP_SYS_IRQWAKEEN1);
173 static inline Void IpcPower_setWugen(IpcPower_WugenEvtMask *mask)
175     REG32(DSP_SYS_IRQWAKEEN0) |= mask->mevt0;
176     REG32(DSP_SYS_IRQWAKEEN1) |= mask->mevt1;
179 /*
180  *  ======== IpcPower_suspendTaskFxn ========
181  */
182 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
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);
203 /*
204  *  ======== IpcPower_init ========
205  */
206 #define FXNN "IpcPower_init"
207 Void IpcPower_init()
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);
264 #undef FXNN
266 /*
267  *  ======== IpcPower_exit ========
268  */
269 Void IpcPower_exit()
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     }
288 /*
289  *  ======== IpcPower_suspend ========
290  */
291 Void IpcPower_suspend()
293     Assert_isTrue((curInit > 0) , NULL);
295     Semaphore_post(IpcPower_semSuspend);
298 /*
299  *  ======== IpcPower_idle ========
300  */
301 Void IpcPower_idle()
303     Hwi_disable();
304     IpcPower_idleCount++;
306     IpcPower_callIdle();
309 /*
310  *  ======== IpcPower_wakeLock ========
311  */
312 Void IpcPower_wakeLock()
314     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKELOCK);
317 /*
318  *  ======== IpcPower_wakeUnlock ========
319  */
320 Void IpcPower_wakeUnlock()
322     IpcPower_sleepMode(IpcPower_SLEEP_MODE_WAKEUNLOCK);
325 /*
326  *  ======== IpcPower_hibernateLock ========
327  */
328 UInt IpcPower_hibernateLock()
330     IArg hwiKey;
332     hwiKey = Hwi_disable();
334     IpcPower_hibLock++;
336     Hwi_restore(hwiKey);
338     return (IpcPower_hibLock);
341 /*
342  *  ======== IpcPower_hibernateUnlock ========
343  */
344 UInt IpcPower_hibernateUnlock()
346     IArg hwiKey;
348     hwiKey = Hwi_disable();
350     if (IpcPower_hibLock > 0) {
351         IpcPower_hibLock--;
352     }
354     Hwi_restore(hwiKey);
356     return (IpcPower_hibLock);
360 /*
361  *  ======== IpcPower_canHibernate ========
362  */
363 Bool IpcPower_canHibernate()
365     if (IpcPower_hibLock) {
366         return (FALSE);
367     }
369     return (TRUE);
372 /*
373  *  ======== IpcPower_registerCallback ========
374  */
375 #define FXNN "IpcPower_registerCallback"
376 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
377                               Ptr data)
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);
412 #undef FXNN
414 /*
415  *  ======== IpcPower_unregisterCallback ========
416  */
417 #define FXNN "IpcPower_unregisterCallback"
418 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
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);
458 #undef FXNN
460 /*
461  *  ======== IpcPower_preSuspend ========
462  */
463 Void IpcPower_preSuspend(Void)
465     IpcPower_suspendCount++;
467     /* Call all user registered suspend callback functions */
468     IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
471 /*
472  *  ======== IpcPower_postResume ========
473  */
474 Void IpcPower_postResume(Void)
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++;