2 * Copyright (C) 2010-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.
11 #include "mali_kernel_utilization.h"
13 #include "mali_osk_mali.h"
14 #include "mali_kernel_common.h"
15 #include "mali_session.h"
16 #include "mali_scheduler.h"
18 /* Thresholds for GP bound detection. */
19 #define MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD 240
20 #define MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD 250
22 /* Define how often to calculate and report GPU utilization, in milliseconds */
23 static _mali_osk_spinlock_irq_t *time_data_lock;
25 static u32 num_running_gp_cores;
26 static u32 num_running_pp_cores;
28 static u64 work_start_time_gpu = 0;
29 static u64 work_start_time_gp = 0;
30 static u64 work_start_time_pp = 0;
31 static u64 accumulated_work_time_gpu = 0;
32 static u64 accumulated_work_time_gp = 0;
33 static u64 accumulated_work_time_pp = 0;
35 static u64 period_start_time = 0;
36 static _mali_osk_timer_t *utilization_timer = NULL;
37 static mali_bool timer_running = MALI_FALSE;
39 static u32 last_utilization_gpu = 0 ;
40 static u32 last_utilization_gp = 0 ;
41 static u32 last_utilization_pp = 0 ;
43 static u32 mali_utilization_timeout = 100;
44 void (*mali_utilization_callback)(struct mali_gpu_utilization_data *data) = NULL;
45 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
46 extern void mali_power_performance_policy_callback(struct mali_gpu_utilization_data *data);
47 #define NUMBER_OF_NANOSECONDS_PER_SECOND 1000000000ULL
49 static u32 calculate_window_render_fps(u64 time_period)
51 u32 max_window_number;
53 u64 max = time_period;
56 u32 time_period_shift;
57 u32 max_window_number_shift;
60 max_window_number = mali_session_max_window_num();
61 /* To avoid float division, extend the dividend to ns unit */
62 tmp = (u64)max_window_number * NUMBER_OF_NANOSECONDS_PER_SECOND;
63 if (tmp > time_period) {
68 * We may have 64-bit values, a dividend or a divisor or both
69 * To avoid dependencies to a 64-bit divider, we shift down the two values
72 leading_zeroes = _mali_osk_clz((u32)(max >> 32));
73 shift_val = 32 - leading_zeroes;
75 time_period_shift = (u32)(time_period >> shift_val);
76 max_window_number_shift = (u32)(tmp >> shift_val);
78 ret_val = max_window_number_shift / time_period_shift;
82 #endif /* defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) */
84 static void calculate_gpu_utilization(void* arg)
90 u32 work_normalized_gpu;
91 u32 work_normalized_gp;
92 u32 work_normalized_pp;
93 u32 period_normalized;
97 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
98 u32 window_render_fps;
101 _mali_osk_spinlock_irq_lock(time_data_lock);
103 if (accumulated_work_time_gpu == 0 && work_start_time_gpu == 0) {
105 * No work done for this period
106 * - No need to reschedule timer
107 * - Report zero usage
109 timer_running = MALI_FALSE;
111 last_utilization_gpu = 0;
112 last_utilization_gp = 0;
113 last_utilization_pp = 0;
115 _mali_osk_spinlock_irq_unlock(time_data_lock);
117 if (NULL != mali_utilization_callback) {
118 struct mali_gpu_utilization_data data = { 0, };
119 mali_utilization_callback(&data);
122 mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND);
127 time_now = _mali_osk_time_get_ns();
129 time_period = time_now - period_start_time;
131 /* If we are currently busy, update working period up to now */
132 if (work_start_time_gpu != 0) {
133 accumulated_work_time_gpu += (time_now - work_start_time_gpu);
134 work_start_time_gpu = time_now;
136 /* GP and/or PP will also be busy if the GPU is busy at this point */
138 if (work_start_time_gp != 0) {
139 accumulated_work_time_gp += (time_now - work_start_time_gp);
140 work_start_time_gp = time_now;
143 if (work_start_time_pp != 0) {
144 accumulated_work_time_pp += (time_now - work_start_time_pp);
145 work_start_time_pp = time_now;
150 * We have two 64-bit values, a dividend and a divisor.
151 * To avoid dependencies to a 64-bit divider, we shift down the two values
153 * We shift the dividend up and possibly the divisor down, making the result X in 256.
156 /* Shift the 64-bit values down so they fit inside a 32-bit integer */
157 leading_zeroes = _mali_osk_clz((u32)(time_period >> 32));
158 shift_val = 32 - leading_zeroes;
159 work_normalized_gpu = (u32)(accumulated_work_time_gpu >> shift_val);
160 work_normalized_gp = (u32)(accumulated_work_time_gp >> shift_val);
161 work_normalized_pp = (u32)(accumulated_work_time_pp >> shift_val);
162 period_normalized = (u32)(time_period >> shift_val);
165 * Now, we should report the usage in parts of 256
166 * this means we must shift up the dividend or down the divisor by 8
167 * (we could do a combination, but we just use one for simplicity,
168 * but the end result should be good enough anyway)
170 if (period_normalized > 0x00FFFFFF) {
171 /* The divisor is so big that it is safe to shift it down */
172 period_normalized >>= 8;
175 * The divisor is so small that we can shift up the dividend, without loosing any data.
176 * (dividend is always smaller than the divisor)
178 work_normalized_gpu <<= 8;
179 work_normalized_gp <<= 8;
180 work_normalized_pp <<= 8;
183 utilization_gpu = work_normalized_gpu / period_normalized;
184 utilization_gp = work_normalized_gp / period_normalized;
185 utilization_pp = work_normalized_pp / period_normalized;
187 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
188 window_render_fps = calculate_window_render_fps(time_period);
191 last_utilization_gpu = utilization_gpu;
192 last_utilization_gp = utilization_gp;
193 last_utilization_pp = utilization_pp;
195 if ((MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD < last_utilization_gp) &&
196 (MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD > last_utilization_pp)) {
197 mali_scheduler_hint_enable(MALI_SCHEDULER_HINT_GP_BOUND);
199 mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND);
202 /* starting a new period */
203 accumulated_work_time_gpu = 0;
204 accumulated_work_time_gp = 0;
205 accumulated_work_time_pp = 0;
206 period_start_time = time_now;
208 _mali_osk_spinlock_irq_unlock(time_data_lock);
210 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
212 if (NULL != mali_utilization_callback) {
213 struct mali_gpu_utilization_data data = {
214 utilization_gpu, utilization_gp, utilization_pp,
215 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
216 window_render_fps, window_render_fps
219 mali_utilization_callback(&data);
223 _mali_osk_errcode_t mali_utilization_init(void)
225 #if USING_GPU_UTILIZATION
226 struct _mali_osk_device_data data;
227 if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data)) {
228 /* Use device specific settings (if defined) */
229 if (0 != data.utilization_interval) {
230 mali_utilization_timeout = data.utilization_interval;
232 if (NULL != data.utilization_callback) {
233 mali_utilization_callback = data.utilization_callback;
234 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Platform has it's own policy \n"));
235 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Utilization handler installed with interval %u\n", mali_utilization_timeout));
240 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
241 if (mali_utilization_callback == NULL) {
242 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: MALI Power Performance Policy Algorithm \n"));
243 mali_utilization_callback = mali_power_performance_policy_callback;
247 if (NULL == mali_utilization_callback) {
248 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: No utilization handler installed\n"));
251 time_data_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_UTILIZATION);
253 if (NULL == time_data_lock) {
254 return _MALI_OSK_ERR_FAULT;
257 num_running_gp_cores = 0;
258 num_running_pp_cores = 0;
260 utilization_timer = _mali_osk_timer_init();
261 if (NULL == utilization_timer) {
262 _mali_osk_spinlock_irq_term(time_data_lock);
263 return _MALI_OSK_ERR_FAULT;
265 _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL);
267 return _MALI_OSK_ERR_OK;
270 void mali_utilization_suspend(void)
272 _mali_osk_spinlock_irq_lock(time_data_lock);
274 if (timer_running == MALI_TRUE) {
275 timer_running = MALI_FALSE;
276 _mali_osk_spinlock_irq_unlock(time_data_lock);
277 _mali_osk_timer_del(utilization_timer);
281 _mali_osk_spinlock_irq_unlock(time_data_lock);
284 void mali_utilization_term(void)
286 if (NULL != utilization_timer) {
287 _mali_osk_timer_del(utilization_timer);
288 timer_running = MALI_FALSE;
289 _mali_osk_timer_term(utilization_timer);
290 utilization_timer = NULL;
293 _mali_osk_spinlock_irq_term(time_data_lock);
296 void mali_utilization_gp_start(void)
298 _mali_osk_spinlock_irq_lock(time_data_lock);
300 ++num_running_gp_cores;
301 if (1 == num_running_gp_cores) {
302 u64 time_now = _mali_osk_time_get_ns();
304 /* First GP core started, consider GP busy from now and onwards */
305 work_start_time_gp = time_now;
307 if (0 == num_running_pp_cores) {
309 * There are no PP cores running, so this is also the point
310 * at which we consider the GPU to be busy as well.
312 work_start_time_gpu = time_now;
315 /* Start a new period (and timer) if needed */
316 if (timer_running != MALI_TRUE) {
317 timer_running = MALI_TRUE;
318 period_start_time = time_now;
320 /* Clear session->number_of_window_jobs */
321 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
322 mali_session_max_window_num();
324 _mali_osk_spinlock_irq_unlock(time_data_lock);
326 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
328 _mali_osk_spinlock_irq_unlock(time_data_lock);
332 _mali_osk_spinlock_irq_unlock(time_data_lock);
336 void mali_utilization_pp_start(void)
338 _mali_osk_spinlock_irq_lock(time_data_lock);
340 ++num_running_pp_cores;
341 if (1 == num_running_pp_cores) {
342 u64 time_now = _mali_osk_time_get_ns();
344 /* First PP core started, consider PP busy from now and onwards */
345 work_start_time_pp = time_now;
347 if (0 == num_running_gp_cores) {
349 * There are no GP cores running, so this is also the point
350 * at which we consider the GPU to be busy as well.
352 work_start_time_gpu = time_now;
355 /* Start a new period (and timer) if needed */
356 if (timer_running != MALI_TRUE) {
357 timer_running = MALI_TRUE;
358 period_start_time = time_now;
360 /* Clear session->number_of_window_jobs */
361 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
362 mali_session_max_window_num();
364 _mali_osk_spinlock_irq_unlock(time_data_lock);
366 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
368 _mali_osk_spinlock_irq_unlock(time_data_lock);
372 _mali_osk_spinlock_irq_unlock(time_data_lock);
376 void mali_utilization_gp_end(void)
378 _mali_osk_spinlock_irq_lock(time_data_lock);
380 --num_running_gp_cores;
381 if (0 == num_running_gp_cores) {
382 u64 time_now = _mali_osk_time_get_ns();
384 /* Last GP core ended, consider GP idle from now and onwards */
385 accumulated_work_time_gp += (time_now - work_start_time_gp);
386 work_start_time_gp = 0;
388 if (0 == num_running_pp_cores) {
390 * There are no PP cores running, so this is also the point
391 * at which we consider the GPU to be idle as well.
393 accumulated_work_time_gpu += (time_now - work_start_time_gpu);
394 work_start_time_gpu = 0;
398 _mali_osk_spinlock_irq_unlock(time_data_lock);
401 void mali_utilization_pp_end(void)
403 _mali_osk_spinlock_irq_lock(time_data_lock);
405 --num_running_pp_cores;
406 if (0 == num_running_pp_cores) {
407 u64 time_now = _mali_osk_time_get_ns();
409 /* Last PP core ended, consider PP idle from now and onwards */
410 accumulated_work_time_pp += (time_now - work_start_time_pp);
411 work_start_time_pp = 0;
413 if (0 == num_running_gp_cores) {
415 * There are no GP cores running, so this is also the point
416 * at which we consider the GPU to be idle as well.
418 accumulated_work_time_gpu += (time_now - work_start_time_gpu);
419 work_start_time_gpu = 0;
423 _mali_osk_spinlock_irq_unlock(time_data_lock);
426 u32 _mali_ukk_utilization_gp_pp(void)
428 return last_utilization_gpu;
431 u32 _mali_ukk_utilization_gp(void)
433 return last_utilization_gp;
436 u32 _mali_ukk_utilization_pp(void)
438 return last_utilization_pp;