1 /*
2 * Copyright (c) 2012-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 IpcPowerDsp.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/Log.h>
49 #include <xdc/runtime/Diags.h>
51 #include <ti/sysbios/BIOS.h>
52 #include <ti/sysbios/knl/Semaphore.h>
53 #include <ti/sysbios/knl/Task.h>
54 #include <ti/sysbios/hal/Hwi.h>
55 #include <ti/sysbios/knl/Swi.h>
56 #include <ti/sysbios/knl/Clock.h>
57 #include <ti/sysbios/timers/dmtimer/Timer.h>
58 #include <ti/sysbios/family/c64p/tesla/Power.h>
59 #include <ti/sysbios/family/c64p/tesla/Wugen.h>
60 #include <ti/sysbios/family/c64p/tesla/package/internal/Power.xdc.h>
62 #include <ti/ipc/MultiProc.h>
64 #include <ti/pm/IpcPower.h>
65 #include "_IpcPower.h"
67 #define REG32(A) (*(volatile UInt32 *) (A))
69 static UInt32 IpcPower_hibLock;
71 #define PDCCMD_REG 0x01810000
72 #define SLEEP_MODE 0x15555
73 #define GPT5_IRQ 51
74 #define GPT6_IRQ 52
75 #define MBX_DSP_IRQ 55
77 static Swi_Handle suspendResumeSwi;
78 static Semaphore_Handle IpcPower_semSuspend = NULL;
79 static Semaphore_Handle IpcPower_semExit = NULL;
80 static Task_Handle IpcPower_tskSuspend = NULL;
82 /* List for storing all registered callback functions */
83 static IpcPower_CallbackElem *IpcPower_callbackList = NULL;
85 /* Module ref count: */
86 static Int curInit = 0;
88 /* PM transition debug counters */
89 UInt32 IpcPower_idleCount = 0;
90 UInt32 IpcPower_suspendCount = 0;
91 UInt32 IpcPower_resumeCount = 0;
93 /* Clock function pointer to handle custom clock functions */
94 Void *IpcPower_clockFxn = NULL;
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)
103 {
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 }
113 }
115 /*
116 * ======== IpcPower_suspendSwi ========
117 */
118 static Void IpcPower_suspendSwi(UArg arg0, UArg arg1)
119 {
120 /* Disable wakeup events */
121 Wugen_disableEvent(GPT5_IRQ);
122 Wugen_disableEvent(GPT6_IRQ);
123 Wugen_disableEvent(MBX_DSP_IRQ);
125 /* Invoke the BIOS suspend routine */
126 Power_suspend(Power_Suspend_HIBERNATE);
128 /* Re-enable wakeup events */
129 Wugen_enableEvent(GPT5_IRQ);
130 Wugen_enableEvent(GPT6_IRQ);
131 Wugen_enableEvent(MBX_DSP_IRQ);
132 }
134 /*
135 * =============================================================================
136 * IpcPower Functions:
137 * =============================================================================
138 */
140 /*
141 * ======== IpcPower_suspendTaskFxn ========
142 */
143 Void IpcPower_suspendTaskFxn(UArg arg0, UArg arg1)
144 {
145 while (curInit) {
146 /* Wait for suspend notification from host-side */
147 Semaphore_pend(IpcPower_semSuspend, BIOS_WAIT_FOREVER);
149 if (curInit) {
150 /* Call pre-suspend preparation function */
151 IpcPower_preSuspend();
153 Swi_post(suspendResumeSwi);
155 /* Call post-resume preparation function */
156 IpcPower_postResume();
157 }
158 }
160 /* Signal the task end */
161 Semaphore_post(IpcPower_semExit);
162 }
164 /*
165 * ======== IpcPower_init ========
166 */
167 Void IpcPower_init()
168 {
169 Swi_Params swiParams;
170 Task_Params taskParams;
171 Int i;
172 UArg arg;
173 UInt func;
174 Timer_Handle tHandle = NULL;
176 if (curInit++) {
177 return;
178 }
180 IpcPower_hibLock = 0;
182 for (i = 0; i < Timer_Object_count(); i++) {
183 tHandle = Timer_Object_get(NULL, i);
184 func = (UInt) Timer_getFunc(tHandle, &arg);
185 if (func && ((func == (UInt) ti_sysbios_knl_Clock_doTick__I) ||
186 (func == (UInt) Clock_tick) ||
187 (func == (UInt) IpcPower_clockFxn))) {
188 tickTimerHandle = tHandle;
189 break;
190 }
191 }
192 if (tickTimerHandle == NULL) {
193 System_abort("IpcPower_init: Cannot find tickTimer Handle. Custom"
194 " clock timer functions currently not supported.\n");
195 }
197 IpcPower_semSuspend = Semaphore_create(0, NULL, NULL);
198 IpcPower_semExit = Semaphore_create(0, NULL, NULL);
200 Task_Params_init(&taskParams);
201 taskParams.priority = Task_numPriorities - 1; /* Highest priority */
202 taskParams.instance->name = "ti.pm.IpcPower_tskSuspend";
203 IpcPower_tskSuspend = Task_create(IpcPower_suspendTaskFxn, &taskParams,
204 NULL);
206 Swi_Params_init(&swiParams);
207 swiParams.priority = Swi_numPriorities - 1; /* Max Priority Swi */
208 suspendResumeSwi = Swi_create(IpcPower_suspendSwi, &swiParams, NULL);
209 }
211 /*
212 * ======== IpcPower_exit ========
213 */
214 Void IpcPower_exit()
215 {
216 --curInit;
218 if (curInit == 0) {
219 /* Unblock PM suspend task */
220 Semaphore_post(IpcPower_semSuspend);
222 /* Wait for task completion */
223 Semaphore_pend(IpcPower_semExit, BIOS_WAIT_FOREVER);
225 /* Delete the suspend task and semaphore */
226 Task_delete(&IpcPower_tskSuspend);
227 Semaphore_delete(&IpcPower_semSuspend);
228 Semaphore_delete(&IpcPower_semExit);
229 }
230 }
232 /*
233 * ======== IpcPower_suspend ========
234 */
235 Void IpcPower_suspend()
236 {
237 Assert_isTrue((curInit > 0) , NULL);
239 Semaphore_post(IpcPower_semSuspend);
240 }
242 /*
243 * ======== IpcPower_idle ========
244 */
245 Void IpcPower_idle()
246 {
247 IpcPower_idleCount++;
249 REG32(PDCCMD_REG) = SLEEP_MODE;
250 REG32(PDCCMD_REG);
252 /* Enable wakeup events */
253 Wugen_enableEvent(GPT5_IRQ);
254 Wugen_enableEvent(GPT6_IRQ);
255 Wugen_enableEvent(MBX_DSP_IRQ);
257 asm(" idle");
258 }
260 /*
261 * ======== IpcPower_hibernateLock ========
262 */
263 UInt IpcPower_hibernateLock()
264 {
265 IArg hwiKey, swiKey;
267 hwiKey = Hwi_disable();
268 swiKey = Swi_disable();
270 IpcPower_hibLock++;
272 Swi_restore(swiKey);
273 Hwi_restore(hwiKey);
275 return (IpcPower_hibLock);
276 }
278 /*
279 * ======== IpcPower_hibernateUnlock ========
280 */
281 UInt IpcPower_hibernateUnlock()
282 {
283 IArg hwiKey, swiKey;
285 hwiKey = Hwi_disable();
286 swiKey = Swi_disable();
288 if (IpcPower_hibLock > 0) {
289 IpcPower_hibLock--;
290 }
292 Swi_restore(swiKey);
293 Hwi_restore(hwiKey);
295 return (IpcPower_hibLock);
296 }
299 /*
300 * ======== IpcPower_canHibernate ========
301 */
302 Bool IpcPower_canHibernate()
303 {
304 if (IpcPower_hibLock) {
305 return (FALSE);
306 }
308 return (TRUE);
309 }
311 /*
312 * ======== IpcPower_registerCallback ========
313 */
314 #define FXNN "IpcPower_registerCallback"
315 Int IpcPower_registerCallback(Int event, IpcPower_CallbackFuncPtr cbck,
316 Ptr data)
317 {
318 IArg hwiKey;
319 IpcPower_CallbackElem **list, *node;
320 BIOS_ThreadType context = BIOS_getThreadType();
322 if ((context != BIOS_ThreadType_Task) &&
323 (context != BIOS_ThreadType_Main)) {
324 Log_print0(Diags_ERROR, FXNN":Invalid context\n");
325 return (IpcPower_E_FAIL);
326 }
328 list = &IpcPower_callbackList;
330 /* Allocate and update new element */
331 node = Memory_alloc(NULL, sizeof(IpcPower_CallbackElem), 0, NULL);
332 if (node == NULL) {
333 Log_print0(Diags_ERROR, FXNN":out of memory\n");
334 return (IpcPower_E_MEMORY);
335 }
337 node->next = NULL;
338 node->event = (IpcPower_Event) event;
339 node->callback = cbck;
340 node->data = data;
342 hwiKey = Hwi_disable(); /* begin: critical section */
343 while (*list != NULL) {
344 list = &(*list)->next;
345 }
346 *list = node;
347 Hwi_restore(hwiKey); /* end: critical section */
349 return (IpcPower_S_SUCCESS);
350 }
351 #undef FXNN
353 /*
354 * ======== IpcPower_unregisterCallback ========
355 */
356 #define FXNN "IpcPower_unregisterCallback"
357 Int IpcPower_unregisterCallback(Int event, IpcPower_CallbackFuncPtr cbck)
358 {
359 IArg hwiKey;
360 IpcPower_CallbackElem **list, *node;
361 Int status = IpcPower_E_FAIL;
362 BIOS_ThreadType context = BIOS_getThreadType();
364 if ((context != BIOS_ThreadType_Task) &&
365 (context != BIOS_ThreadType_Main)) {
366 Log_print0(Diags_ERROR, FXNN":Invalid context\n");
367 return (status);
368 }
370 list = &IpcPower_callbackList;
371 node = NULL;
373 hwiKey = Hwi_disable(); /* begin: critical section */
374 while (*list != NULL) {
375 if ( ((*list)->callback == cbck) &&
376 ((*list)->event == event) ) {
377 node = *list;
378 *list = (*list)->next;
379 status = IpcPower_S_SUCCESS;
380 break;
381 }
382 list = &(*list)->next;
383 }
384 Hwi_restore(hwiKey); /* end: critical section */
386 if (status == IpcPower_S_SUCCESS) {
387 if (node != NULL) {
388 Memory_free(NULL, node, sizeof(IpcPower_CallbackElem));
389 }
390 else {
391 Log_print0(Diags_ERROR, FXNN":Invalid pointer\n");
392 }
393 }
395 return (status);
396 }
397 #undef FXNN
399 /*
400 * ======== IpcPower_preSuspend ========
401 */
402 Void IpcPower_preSuspend(Void)
403 {
404 IpcPower_suspendCount++;
406 /* Call all user registered suspend callback functions */
407 IpcPower_callUserFxns(IpcPower_Event_SUSPEND);
408 }
410 /*
411 * ======== IpcPower_postResume ========
412 */
413 Void IpcPower_postResume(Void)
414 {
415 /* Restore timer registers */
416 Timer_restoreRegisters(tickTimerHandle, NULL);
417 Timer_start(tickTimerHandle);
419 /* Call all user registered resume callback functions */
420 IpcPower_callUserFxns(IpcPower_Event_RESUME);
422 IpcPower_resumeCount++;
423 }