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.
11 #include "mali_kernel_utilization.h"
13 #include "mali_osk_mali.h"
14 #include "mali_kernel_common.h"
16 /* Define how often to calculate and report GPU utilization, in milliseconds */
17 static _mali_osk_lock_t *time_data_lock;
19 static _mali_osk_atomic_t num_running_cores;
21 static u64 period_start_time = 0;
22 static u64 work_start_time = 0;
23 static u64 accumulated_work_time = 0;
25 static _mali_osk_timer_t *utilization_timer = NULL;
26 static mali_bool timer_running = MALI_FALSE;
28 static u32 last_utilization = 0 ;
30 static u32 mali_utilization_timeout = 1000;
31 static int gpu_entry_count = 0;
32 void (*mali_utilization_callback)(unsigned int) = NULL;
34 static void calculate_gpu_utilization(void* arg)
41 u32 period_normalized;
44 _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW);
46 if (accumulated_work_time == 0 && work_start_time == 0)
48 /* Don't reschedule timer, this will be started if new work arrives */
49 timer_running = MALI_FALSE;
51 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
53 /* No work done for this period, report zero usage */
54 if (NULL != mali_utilization_callback)
56 mali_utilization_callback(0);
62 time_now = _mali_osk_time_get_ns();
63 time_period = time_now - period_start_time;
65 /* If we are currently busy, update working period up to now */
66 if (work_start_time != 0)
68 accumulated_work_time += (time_now - work_start_time);
69 work_start_time = time_now;
73 * We have two 64-bit values, a dividend and a divisor.
74 * To avoid dependencies to a 64-bit divider, we shift down the two values
76 * We shift the dividend up and possibly the divisor down, making the result X in 256.
79 /* Shift the 64-bit values down so they fit inside a 32-bit integer */
80 leading_zeroes = _mali_osk_clz((u32)(time_period >> 32));
81 shift_val = 32 - leading_zeroes;
82 work_normalized = (u32)(accumulated_work_time >> shift_val);
83 period_normalized = (u32)(time_period >> shift_val);
86 * Now, we should report the usage in parts of 256
87 * this means we must shift up the dividend or down the divisor by 8
88 * (we could do a combination, but we just use one for simplicity,
89 * but the end result should be good enough anyway)
91 if (period_normalized > 0x00FFFFFF)
93 /* The divisor is so big that it is safe to shift it down */
94 period_normalized >>= 8;
99 * The divisor is so small that we can shift up the dividend, without loosing any data.
100 * (dividend is always smaller than the divisor)
102 work_normalized <<= 8;
105 utilization = work_normalized / period_normalized;
107 last_utilization = utilization;
109 accumulated_work_time = 0;
110 period_start_time = time_now; /* starting a new period */
112 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
114 if (gpu_entry_count <= 3) {
116 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks((mali_utilization_timeout/2)));
118 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
121 if (NULL != mali_utilization_callback)
123 mali_utilization_callback(utilization);
127 _mali_osk_errcode_t mali_utilization_init(void)
129 #if USING_GPU_UTILIZATION
130 struct _mali_osk_device_data data;
131 if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data))
133 /* Use device specific settings (if defined) */
134 if (0 != data.utilization_interval)
136 mali_utilization_timeout = data.utilization_interval;
138 if (NULL != data.utilization_handler)
140 mali_utilization_callback = data.utilization_handler;
145 if (NULL != mali_utilization_callback)
147 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Utilization handler installed with interval %u\n", mali_utilization_timeout));
151 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: No utilization handler installed\n"));
154 time_data_lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ |
155 _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, _MALI_OSK_LOCK_ORDER_UTILIZATION);
157 if (NULL == time_data_lock)
159 return _MALI_OSK_ERR_FAULT;
162 _mali_osk_atomic_init(&num_running_cores, 0);
164 utilization_timer = _mali_osk_timer_init();
165 if (NULL == utilization_timer)
167 _mali_osk_lock_term(time_data_lock);
168 return _MALI_OSK_ERR_FAULT;
170 _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL);
172 return _MALI_OSK_ERR_OK;
175 void mali_utilization_suspend(void)
177 if (NULL != utilization_timer)
179 _mali_osk_timer_del(utilization_timer);
180 timer_running = MALI_FALSE;
184 void mali_utilization_term(void)
186 if (NULL != utilization_timer)
188 _mali_osk_timer_del(utilization_timer);
189 timer_running = MALI_FALSE;
190 _mali_osk_timer_term(utilization_timer);
191 utilization_timer = NULL;
194 _mali_osk_atomic_term(&num_running_cores);
196 _mali_osk_lock_term(time_data_lock);
199 void mali_utilization_core_start(u64 time_now)
201 if (_mali_osk_atomic_inc_return(&num_running_cores) == 1)
204 * We went from zero cores working, to one core working,
205 * we now consider the entire GPU for being busy
208 _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW);
210 if (time_now < period_start_time)
213 * This might happen if the calculate_gpu_utilization() was able
214 * to run between the sampling of time_now and us grabbing the lock above
216 time_now = period_start_time;
219 work_start_time = time_now;
220 if (timer_running != MALI_TRUE)
222 timer_running = MALI_TRUE;
223 period_start_time = work_start_time; /* starting a new period */
225 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
228 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks((mali_utilization_timeout/2)));
232 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
237 void mali_utilization_core_end(u64 time_now)
239 if (_mali_osk_atomic_dec_return(&num_running_cores) == 0)
242 * No more cores are working, so accumulate the time we was busy.
244 _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW);
246 if (time_now < work_start_time)
249 * This might happen if the calculate_gpu_utilization() was able
250 * to run between the sampling of time_now and us grabbing the lock above
252 time_now = work_start_time;
255 accumulated_work_time += (time_now - work_start_time);
258 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
262 u32 _mali_ukk_utilization_gp_pp(void)
264 return last_utilization;
267 u32 _mali_ukk_utilization_gp(void)
269 return last_utilization;
272 u32 _mali_ukk_utilization_pp(void)
274 return last_utilization;