2
* Copyright (C) 2011-2012 ARM Limited. All rights reserved.
4
* This program is free software and is provided to you under the terms of the GNU General Public License version 2
5
* as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
7
* A copy of the licence is included with the program, and can also be obtained from Free Software
8
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12
#include "mali_kernel_common.h"
14
#include "mali_gp_scheduler.h"
15
#include "mali_pp_scheduler.h"
16
#include "mali_platform.h"
17
#include "mali_kernel_utilization.h"
18
#include "mali_kernel_core.h"
19
#include "mali_group.h"
21
#define MALI_PM_LIGHT_SLEEP_TIMEOUT 1000
25
MALI_PM_SCHEME_DYNAMIC,
26
MALI_PM_SCHEME_OS_SUSPENDED,
27
MALI_PM_SCHEME_ALWAYS_ON
33
MALI_PM_LEVEL_2_STANDBY,
34
MALI_PM_LEVEL_3_LIGHT_SLEEP,
35
MALI_PM_LEVEL_4_DEEP_SLEEP
37
static _mali_osk_lock_t *mali_pm_lock_set_next_state;
38
static _mali_osk_lock_t *mali_pm_lock_set_core_states;
39
static _mali_osk_lock_t *mali_pm_lock_execute_state_change;
40
static _mali_osk_irq_t *wq_irq;
42
static _mali_osk_timer_t *idle_timer = NULL;
43
static mali_bool idle_timer_running = MALI_FALSE;
44
static u32 mali_pm_event_number = 0;
46
static u32 num_active_gps = 0;
47
static u32 num_active_pps = 0;
49
static enum mali_pm_scheme current_scheme = MALI_PM_SCHEME_DYNAMIC;
50
static enum mali_pm_level current_level = MALI_PM_LEVEL_1_ON;
51
static enum mali_pm_level next_level_dynamic = MALI_PM_LEVEL_2_STANDBY; /* Should be the state we go to when we go out of MALI_PM_SCHEME_ALWAYS_ON during init */
55
static _mali_osk_errcode_t mali_pm_upper_half(void *data);
56
static void mali_pm_bottom_half(void *data);
57
static void mali_pm_powerup(void);
58
static void mali_pm_powerdown(mali_power_mode power_mode);
60
static void timeout_light_sleep(void* arg);
62
/* Deep sleep timout not supported */
63
static void timeout_deep_sleep(void* arg);
65
static u32 mali_pm_event_number_get(void);
66
static void mali_pm_event(enum mali_pm_event pm_event, mali_bool schedule_work, u32 timer_time );
68
_mali_osk_errcode_t mali_pm_initialize(void)
70
mali_pm_lock_execute_state_change = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ORDERED |_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, _MALI_OSK_LOCK_ORDER_PM_EXECUTE);
72
if (NULL != mali_pm_lock_execute_state_change )
74
mali_pm_lock_set_next_state = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_ONELOCK| _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ |_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, _MALI_OSK_LOCK_ORDER_LAST);
76
if (NULL != mali_pm_lock_set_next_state)
78
mali_pm_lock_set_core_states = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, _MALI_OSK_LOCK_ORDER_PM_CORE_STATE);
80
if (NULL != mali_pm_lock_set_core_states)
82
idle_timer = _mali_osk_timer_init();
83
if (NULL != idle_timer)
85
wq_irq = _mali_osk_irq_init(_MALI_OSK_IRQ_NUMBER_PMM,
91
"Mali PM deferred work");
94
if (_MALI_OSK_ERR_OK == mali_platform_init())
96
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
97
_mali_osk_pm_dev_enable();
100
return _MALI_OSK_ERR_OK;
103
_mali_osk_irq_term(wq_irq);
106
_mali_osk_timer_del(idle_timer);
107
_mali_osk_timer_term(idle_timer);
109
_mali_osk_lock_term(mali_pm_lock_set_core_states);
111
_mali_osk_lock_term(mali_pm_lock_set_next_state);
113
_mali_osk_lock_term(mali_pm_lock_execute_state_change);
116
return _MALI_OSK_ERR_FAULT;
119
void mali_pm_terminate(void)
121
mali_platform_deinit();
122
_mali_osk_irq_term(wq_irq);
123
_mali_osk_timer_del(idle_timer);
124
_mali_osk_timer_term(idle_timer);
125
_mali_osk_lock_term(mali_pm_lock_execute_state_change);
126
_mali_osk_lock_term(mali_pm_lock_set_next_state);
127
_mali_osk_lock_term(mali_pm_lock_set_core_states);
131
inline void mali_pm_lock(void)
133
_mali_osk_lock_wait(mali_pm_lock_set_next_state, _MALI_OSK_LOCKMODE_RW);
136
inline void mali_pm_unlock(void)
138
_mali_osk_lock_signal(mali_pm_lock_set_next_state, _MALI_OSK_LOCKMODE_RW);
141
inline void mali_pm_execute_state_change_lock(void)
143
_mali_osk_lock_wait(mali_pm_lock_execute_state_change,_MALI_OSK_LOCKMODE_RW);
146
inline void mali_pm_execute_state_change_unlock(void)
148
_mali_osk_lock_signal(mali_pm_lock_execute_state_change, _MALI_OSK_LOCKMODE_RW);
151
static void mali_pm_powerup(void)
153
MALI_DEBUG_PRINT(3, ("Mali PM: Setting GPU power mode to MALI_POWER_MODE_ON\n"));
154
mali_platform_power_mode_change(MALI_POWER_MODE_ON);
156
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
158
/* Aquire our reference */
159
MALI_DEBUG_PRINT(4, ("Mali PM: Getting device PM reference (=> requesting MALI_POWER_MODE_ON)\n"));
160
_mali_osk_pm_dev_activate();
163
mali_group_power_on();
166
static void mali_pm_powerdown(mali_power_mode power_mode)
168
if ( (MALI_PM_LEVEL_1_ON == current_level) || (MALI_PM_LEVEL_2_STANDBY == current_level) )
170
mali_group_power_off();
172
mali_platform_power_mode_change(power_mode);
174
#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
175
_mali_osk_pm_dev_idle();
179
mali_bool mali_pm_is_powered_on(void)
181
mali_bool is_on = MALI_TRUE;
183
if( ! (MALI_PM_SCHEME_ALWAYS_ON == current_scheme || MALI_PM_SCHEME_DYNAMIC == current_scheme) )
187
else if ( ! (MALI_PM_LEVEL_1_ON == current_level || MALI_PM_LEVEL_2_STANDBY == current_level))
191
else if ( ! (MALI_PM_LEVEL_1_ON == next_level_dynamic || MALI_PM_LEVEL_2_STANDBY == next_level_dynamic))
200
static const char *state_as_string(enum mali_pm_level level)
204
case MALI_PM_LEVEL_1_ON:
205
return "MALI_PM_LEVEL_1_ON";
206
case MALI_PM_LEVEL_2_STANDBY:
207
return "MALI_PM_LEVEL_2_STANDBY";
208
case MALI_PM_LEVEL_3_LIGHT_SLEEP:
209
return "MALI_PM_LEVEL_3_LIGHT_SLEEP";
210
case MALI_PM_LEVEL_4_DEEP_SLEEP:
211
return "MALI_PM_LEVEL_4_DEEP_SLEEP";
213
return "UNKNOWN LEVEL";
217
/* This could be used from another thread (work queue), if we need that */
218
static void mali_pm_process_next(void)
220
enum mali_pm_level pm_level_to_set;
222
_mali_osk_lock_wait(mali_pm_lock_execute_state_change, _MALI_OSK_LOCKMODE_RW);
224
pm_level_to_set = current_level;
226
if (MALI_PM_SCHEME_DYNAMIC == current_scheme)
228
pm_level_to_set = next_level_dynamic;
230
MALI_DEBUG_PRINT(4, ("Mali PM: Dynamic scheme; Changing Mali GPU power state from %s to: %s\n", state_as_string(current_level), state_as_string(pm_level_to_set)));
232
if (current_level == pm_level_to_set)
234
goto end_function; /* early out, no change in power level */
237
/* Start timers according to new state, so we get STANDBY -> LIGHT_SLEEP -> DEEP_SLEEP */
239
if (MALI_TRUE == idle_timer_running)
241
/* There is an existing timeout, so delete it */
242
_mali_osk_timer_del(idle_timer);
243
idle_timer_running = MALI_FALSE;
246
/* Making sure that we turn on through the platform file
247
Since it was turned OFF directly through the platform file.
248
This might lead to double turn-on, but the plaform file supports that.*/
249
if ( current_level == MALI_PM_LEVEL_4_DEEP_SLEEP)
252
mali_kernel_core_wakeup();
255
if (MALI_PM_LEVEL_1_ON == pm_level_to_set)
257
if (MALI_PM_LEVEL_2_STANDBY != current_level)
259
/* We only need to do anything if we came from one of the sleeping states */
262
/* Wake up Mali cores since we came from a sleep state */
263
mali_kernel_core_wakeup();
266
else if (MALI_PM_LEVEL_2_STANDBY == pm_level_to_set)
268
/* This is just an internal state, so we don't bother to report it to the platform file */
269
idle_timer_running = MALI_TRUE;
270
_mali_osk_timer_setcallback(idle_timer, timeout_light_sleep, (void*) mali_pm_event_number_get());
271
_mali_osk_timer_add(idle_timer, _mali_osk_time_mstoticks(MALI_PM_LIGHT_SLEEP_TIMEOUT));
273
else if (MALI_PM_LEVEL_3_LIGHT_SLEEP == pm_level_to_set)
275
mali_pm_powerdown(MALI_POWER_MODE_LIGHT_SLEEP);
277
else if (MALI_PM_LEVEL_4_DEEP_SLEEP == pm_level_to_set)
279
MALI_DEBUG_PRINT(2, ("Mali PM: Setting GPU power mode to MALI_POWER_MODE_DEEP_SLEEP\n"));
280
mali_pm_powerdown(MALI_POWER_MODE_DEEP_SLEEP);
283
else if (MALI_PM_SCHEME_OS_SUSPENDED == current_scheme)
285
MALI_DEBUG_PRINT(4, ("Mali PM: OS scheme; Changing Mali GPU power state from %s to: %s\n", state_as_string(current_level), state_as_string(MALI_PM_LEVEL_4_DEEP_SLEEP)));
287
pm_level_to_set = MALI_PM_LEVEL_4_DEEP_SLEEP;
289
if (current_level == pm_level_to_set)
291
goto end_function; /* early out, no change in power level */
294
/* Cancel any timers */
295
if (MALI_TRUE == idle_timer_running)
297
/* There is an existing timeout, so delete it */
298
_mali_osk_timer_del(idle_timer);
299
idle_timer_running = MALI_FALSE;
302
MALI_DEBUG_PRINT(2, ("Mali PM: Setting GPU power mode to MALI_POWER_MODE_DEEP_SLEEP\n"));
303
mali_pm_powerdown(MALI_POWER_MODE_DEEP_SLEEP);
304
next_level_dynamic = current_level;
306
else if (MALI_PM_SCHEME_ALWAYS_ON == current_scheme)
308
MALI_DEBUG_PRINT(4, ("Mali PM: Always on scheme; Changing Mali GPU power state from %s to: %s\n", state_as_string(current_level), state_as_string(MALI_PM_LEVEL_1_ON)));
310
pm_level_to_set = MALI_PM_LEVEL_1_ON;
311
if (current_level == pm_level_to_set)
313
goto end_function; /* early out, no change in power level */
316
MALI_DEBUG_PRINT(2, ("Mali PM: Setting GPU power mode to MALI_POWER_MODE_ON\n"));
318
if (MALI_PM_LEVEL_2_STANDBY != current_level)
320
/* Wake up Mali cores since we came from a sleep state */
321
mali_kernel_core_wakeup();
326
MALI_PRINT_ERROR(("MALI PM: Illegal scheme"));
329
current_level = pm_level_to_set;
332
_mali_osk_lock_signal(mali_pm_lock_execute_state_change, _MALI_OSK_LOCKMODE_RW);
336
void mali_pm_always_on(mali_bool enable)
338
if (MALI_TRUE == enable)
340
/* The event is processed in current thread synchronously */
341
mali_pm_event(MALI_PM_EVENT_SCHEME_ALWAYS_ON, MALI_FALSE, 0 );
345
/* The event is processed in current thread synchronously */
346
mali_pm_event(MALI_PM_EVENT_SCHEME_DYNAMIC_CONTROLL, MALI_FALSE, 0 );
350
static _mali_osk_errcode_t mali_pm_upper_half(void *data)
353
return _MALI_OSK_ERR_OK;
356
static void mali_pm_bottom_half(void *data)
358
mali_pm_process_next();
361
static u32 mali_pm_event_number_get(void)
365
mali_pm_lock(); /* spinlock: mali_pm_lock_set_next_state */
366
retval = ++mali_pm_event_number;
367
if (0==retval ) retval = ++mali_pm_event_number;
373
static void mali_pm_event(enum mali_pm_event pm_event, mali_bool schedule_work, u32 timer_time )
375
mali_pm_lock(); /* spinlock: mali_pm_lock_set_next_state */
376
/* Only timer events should set this variable, all other events must set it to zero. */
377
if ( 0 != timer_time )
379
MALI_DEBUG_ASSERT( (pm_event==MALI_PM_EVENT_TIMER_LIGHT_SLEEP) || (pm_event==MALI_PM_EVENT_TIMER_DEEP_SLEEP) );
380
if ( mali_pm_event_number != timer_time )
382
/* In this case there have been processed newer events since the timer event was set up.
383
If so we always ignore the timing event */
390
/* Delete possible ongoing timers
391
if ( (MALI_PM_LEVEL_2_STANDBY==current_level) || (MALI_PM_LEVEL_3_LIGHT_SLEEP==current_level) )
393
_mali_osk_timer_del(idle_timer);
397
mali_pm_event_number++;
400
case MALI_PM_EVENT_CORES_WORKING:
401
next_level_dynamic = MALI_PM_LEVEL_1_ON;
402
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_OS_SUSPENDED != current_scheme );
404
case MALI_PM_EVENT_CORES_IDLE:
405
next_level_dynamic = MALI_PM_LEVEL_2_STANDBY;
406
/*MALI_DEBUG_ASSERT( MALI_PM_SCHEME_OS_SUSPENDED != current_scheme );*/
408
case MALI_PM_EVENT_TIMER_LIGHT_SLEEP:
409
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_ALWAYS_ON != current_scheme );
410
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_OS_SUSPENDED != current_scheme );
411
next_level_dynamic = MALI_PM_LEVEL_3_LIGHT_SLEEP;
413
case MALI_PM_EVENT_TIMER_DEEP_SLEEP:
414
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_ALWAYS_ON != current_scheme );
415
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_OS_SUSPENDED != current_scheme );
416
next_level_dynamic = MALI_PM_LEVEL_4_DEEP_SLEEP;
418
case MALI_PM_EVENT_OS_SUSPEND:
419
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_ALWAYS_ON != current_scheme );
420
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_OS_SUSPENDED != current_scheme );
421
current_scheme = MALI_PM_SCHEME_OS_SUSPENDED;
422
next_level_dynamic = MALI_PM_LEVEL_4_DEEP_SLEEP; /* Dynamic scheme will go into level when we are resumed */
424
case MALI_PM_EVENT_OS_RESUME:
425
MALI_DEBUG_ASSERT(MALI_PM_SCHEME_OS_SUSPENDED == current_scheme );
426
current_scheme = MALI_PM_SCHEME_DYNAMIC;
428
case MALI_PM_EVENT_SCHEME_ALWAYS_ON:
429
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_OS_SUSPENDED != current_scheme );
430
current_scheme = MALI_PM_SCHEME_ALWAYS_ON;
432
case MALI_PM_EVENT_SCHEME_DYNAMIC_CONTROLL:
433
MALI_DEBUG_ASSERT( MALI_PM_SCHEME_ALWAYS_ON == current_scheme || MALI_PM_SCHEME_DYNAMIC == current_scheme );
434
current_scheme = MALI_PM_SCHEME_DYNAMIC;
437
MALI_DEBUG_PRINT_ERROR(("Unknown next state."));
443
if (MALI_TRUE == schedule_work)
445
_mali_osk_irq_schedulework(wq_irq);
449
mali_pm_process_next();
453
static void timeout_light_sleep(void* arg)
455
/* State change only if no newer power events have happend from the time in arg.
456
Actual work will be scheduled on worker thread. */
457
mali_pm_event(MALI_PM_EVENT_TIMER_LIGHT_SLEEP, MALI_TRUE, (u32) arg);
460
void mali_pm_core_event(enum mali_core_event core_event)
462
mali_bool transition_working = MALI_FALSE;
463
mali_bool transition_idle = MALI_FALSE;
465
_mali_osk_lock_wait(mali_pm_lock_set_core_states, _MALI_OSK_LOCKMODE_RW);
469
case MALI_CORE_EVENT_GP_START:
470
if (num_active_pps + num_active_gps == 0)
472
transition_working = MALI_TRUE;
476
case MALI_CORE_EVENT_GP_STOP:
477
if (num_active_pps + num_active_gps == 1)
479
transition_idle = MALI_TRUE;
483
case MALI_CORE_EVENT_PP_START:
484
if (num_active_pps + num_active_gps == 0)
486
transition_working = MALI_TRUE;
490
case MALI_CORE_EVENT_PP_STOP:
491
if (num_active_pps + num_active_gps == 1)
493
transition_idle = MALI_TRUE;
499
if (transition_working == MALI_TRUE)
501
#ifdef CONFIG_MALI400_GPU_UTILIZATION
502
mali_utilization_core_start(_mali_osk_time_get_ns());
504
mali_pm_event(MALI_PM_EVENT_CORES_WORKING, MALI_FALSE, 0); /* process event in same thread */
506
else if (transition_idle == MALI_TRUE)
508
#ifdef CONFIG_MALI400_GPU_UTILIZATION
509
mali_utilization_core_end(_mali_osk_time_get_ns());
511
mali_pm_event(MALI_PM_EVENT_CORES_IDLE, MALI_FALSE, 0); /* process event in same thread */
514
_mali_osk_lock_signal(mali_pm_lock_set_core_states, _MALI_OSK_LOCKMODE_RW);
517
void mali_pm_os_suspend(void)
519
MALI_DEBUG_PRINT(2, ("Mali PM: OS suspending...\n"));
521
mali_gp_scheduler_suspend();
522
mali_pp_scheduler_suspend();
523
mali_pm_event(MALI_PM_EVENT_OS_SUSPEND, MALI_FALSE, 0); /* process event in same thread */
525
MALI_DEBUG_PRINT(2, ("Mali PM: OS suspend completed\n"));
528
void mali_pm_os_resume(void)
530
MALI_DEBUG_PRINT(2, ("Mali PM: OS resuming...\n"));
532
mali_pm_event(MALI_PM_EVENT_OS_RESUME, MALI_FALSE, 0); /* process event in same thread */
533
mali_gp_scheduler_resume();
534
mali_pp_scheduler_resume();
536
MALI_DEBUG_PRINT(2, ("Mali PM: OS resume completed\n"));
539
void mali_pm_runtime_suspend(void)
541
MALI_DEBUG_PRINT(2, ("Mali PM: OS runtime suspended\n"));
544
void mali_pm_runtime_resume(void)
546
MALI_DEBUG_PRINT(3, ("Mali PM: OS runtime resumed\n"));