tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / r4p0_rel0 / common / mali_kernel_utilization.c
1 /*
2  * Copyright (C) 2010-2012 ARM Limited. All rights reserved.
3  * 
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.
6  * 
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.
9  */
10
11 #include "mali_kernel_utilization.h"
12 #include "mali_osk.h"
13 #include "mali_osk_mali.h"
14 #include "mali_kernel_common.h"
15 #include "mali_session.h"
16 #include "mali_scheduler.h"
17
18 /* Thresholds for GP bound detection. */
19 #define MALI_GP_BOUND_GP_UTILIZATION_THRESHOLD 240
20 #define MALI_GP_BOUND_PP_UTILIZATION_THRESHOLD 250
21
22 /* Define how often to calculate and report GPU utilization, in milliseconds */
23 static _mali_osk_spinlock_irq_t *time_data_lock;
24
25 static u32 num_running_gp_cores;
26 static u32 num_running_pp_cores;
27
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;
34
35 static u64 period_start_time = 0;
36 static _mali_osk_timer_t *utilization_timer = NULL;
37 static mali_bool timer_running = MALI_FALSE;
38
39 static u32 last_utilization_gpu = 0 ;
40 static u32 last_utilization_gp = 0 ;
41 static u32 last_utilization_pp = 0 ;
42
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
48
49 static u32 calculate_window_render_fps(u64 time_period)
50 {
51         u32 max_window_number;
52         u64 tmp;
53         u64 max = time_period;
54         u32 leading_zeroes;
55         u32 shift_val;
56         u32 time_period_shift;
57         u32 max_window_number_shift;
58         u32 ret_val;
59
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) {
64                 max = tmp;
65         }
66
67         /*
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
70          * equally first.
71          */
72         leading_zeroes = _mali_osk_clz((u32)(max >> 32));
73         shift_val = 32 - leading_zeroes;
74
75         time_period_shift = (u32)(time_period >> shift_val);
76         max_window_number_shift = (u32)(tmp >> shift_val);
77
78         ret_val = max_window_number_shift / time_period_shift;
79
80         return ret_val;
81 }
82 #endif  /* defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY) */
83
84 static void calculate_gpu_utilization(void* arg)
85 {
86         u64 time_now;
87         u64 time_period;
88         u32 leading_zeroes;
89         u32 shift_val;
90         u32 work_normalized_gpu;
91         u32 work_normalized_gp;
92         u32 work_normalized_pp;
93         u32 period_normalized;
94         u32 utilization_gpu;
95         u32 utilization_gp;
96         u32 utilization_pp;
97 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
98         u32 window_render_fps;
99 #endif
100
101         _mali_osk_spinlock_irq_lock(time_data_lock);
102
103         if (accumulated_work_time_gpu == 0 && work_start_time_gpu == 0) {
104                 /*
105                  * No work done for this period
106                  * - No need to reschedule timer
107                  * - Report zero usage
108                  */
109                 timer_running = MALI_FALSE;
110
111                 last_utilization_gpu = 0;
112                 last_utilization_gp = 0;
113                 last_utilization_pp = 0;
114
115                 _mali_osk_spinlock_irq_unlock(time_data_lock);
116
117                 if (NULL != mali_utilization_callback) {
118                         struct mali_gpu_utilization_data data = { 0, };
119                         mali_utilization_callback(&data);
120                 }
121
122                 mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND);
123
124                 return;
125         }
126
127         time_now = _mali_osk_time_get_ns();
128
129         time_period = time_now - period_start_time;
130
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;
135
136                 /* GP and/or PP will also be busy if the GPU is busy at this point */
137
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;
141                 }
142
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;
146                 }
147         }
148
149         /*
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
152          * equally first.
153          * We shift the dividend up and possibly the divisor down, making the result X in 256.
154          */
155
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);
163
164         /*
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)
169          */
170         if (period_normalized > 0x00FFFFFF) {
171                 /* The divisor is so big that it is safe to shift it down */
172                 period_normalized >>= 8;
173         } else {
174                 /*
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)
177                  */
178                 work_normalized_gpu <<= 8;
179                 work_normalized_gp <<= 8;
180                 work_normalized_pp <<= 8;
181         }
182
183         utilization_gpu = work_normalized_gpu / period_normalized;
184         utilization_gp = work_normalized_gp / period_normalized;
185         utilization_pp = work_normalized_pp / period_normalized;
186
187 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
188         window_render_fps = calculate_window_render_fps(time_period);
189 #endif
190
191         last_utilization_gpu = utilization_gpu;
192         last_utilization_gp = utilization_gp;
193         last_utilization_pp = utilization_pp;
194
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);
198         } else {
199                 mali_scheduler_hint_disable(MALI_SCHEDULER_HINT_GP_BOUND);
200         }
201
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;
207
208         _mali_osk_spinlock_irq_unlock(time_data_lock);
209
210         _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
211
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
217 #endif
218                 };
219                 mali_utilization_callback(&data);
220         }
221 }
222
223 _mali_osk_errcode_t mali_utilization_init(void)
224 {
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;
231                 }
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));
236                 }
237         }
238 #endif
239
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;
244         }
245 #endif
246
247         if (NULL == mali_utilization_callback) {
248                 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: No utilization handler installed\n"));
249         }
250
251         time_data_lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, _MALI_OSK_LOCK_ORDER_UTILIZATION);
252
253         if (NULL == time_data_lock) {
254                 return _MALI_OSK_ERR_FAULT;
255         }
256
257         num_running_gp_cores = 0;
258         num_running_pp_cores = 0;
259
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;
264         }
265         _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL);
266
267         return _MALI_OSK_ERR_OK;
268 }
269
270 void mali_utilization_suspend(void)
271 {
272         _mali_osk_spinlock_irq_lock(time_data_lock);
273
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);
278                 return;
279         }
280
281         _mali_osk_spinlock_irq_unlock(time_data_lock);
282 }
283
284 void mali_utilization_term(void)
285 {
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;
291         }
292
293         _mali_osk_spinlock_irq_term(time_data_lock);
294 }
295
296 void mali_utilization_gp_start(void)
297 {
298         _mali_osk_spinlock_irq_lock(time_data_lock);
299
300         ++num_running_gp_cores;
301         if (1 == num_running_gp_cores) {
302                 u64 time_now = _mali_osk_time_get_ns();
303
304                 /* First GP core started, consider GP busy from now and onwards */
305                 work_start_time_gp = time_now;
306
307                 if (0 == num_running_pp_cores) {
308                         /*
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.
311                          */
312                         work_start_time_gpu = time_now;
313                 }
314
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;
319
320                         /* Clear session->number_of_window_jobs */
321 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
322                         mali_session_max_window_num();
323 #endif
324                         _mali_osk_spinlock_irq_unlock(time_data_lock);
325
326                         _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
327                 } else {
328                         _mali_osk_spinlock_irq_unlock(time_data_lock);
329                 }
330         } else {
331                 /* Nothing to do */
332                 _mali_osk_spinlock_irq_unlock(time_data_lock);
333         }
334 }
335
336 void mali_utilization_pp_start(void)
337 {
338         _mali_osk_spinlock_irq_lock(time_data_lock);
339
340         ++num_running_pp_cores;
341         if (1 == num_running_pp_cores) {
342                 u64 time_now = _mali_osk_time_get_ns();
343
344                 /* First PP core started, consider PP busy from now and onwards */
345                 work_start_time_pp = time_now;
346
347                 if (0 == num_running_gp_cores) {
348                         /*
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.
351                          */
352                         work_start_time_gpu = time_now;
353                 }
354
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;
359
360                         /* Clear session->number_of_window_jobs */
361 #if defined(CONFIG_MALI400_POWER_PERFORMANCE_POLICY)
362                         mali_session_max_window_num();
363 #endif
364                         _mali_osk_spinlock_irq_unlock(time_data_lock);
365
366                         _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
367                 } else {
368                         _mali_osk_spinlock_irq_unlock(time_data_lock);
369                 }
370         } else {
371                 /* Nothing to do */
372                 _mali_osk_spinlock_irq_unlock(time_data_lock);
373         }
374 }
375
376 void mali_utilization_gp_end(void)
377 {
378         _mali_osk_spinlock_irq_lock(time_data_lock);
379
380         --num_running_gp_cores;
381         if (0 == num_running_gp_cores) {
382                 u64 time_now = _mali_osk_time_get_ns();
383
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;
387
388                 if (0 == num_running_pp_cores) {
389                         /*
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.
392                          */
393                         accumulated_work_time_gpu += (time_now - work_start_time_gpu);
394                         work_start_time_gpu = 0;
395                 }
396         }
397
398         _mali_osk_spinlock_irq_unlock(time_data_lock);
399 }
400
401 void mali_utilization_pp_end(void)
402 {
403         _mali_osk_spinlock_irq_lock(time_data_lock);
404
405         --num_running_pp_cores;
406         if (0 == num_running_pp_cores) {
407                 u64 time_now = _mali_osk_time_get_ns();
408
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;
412
413                 if (0 == num_running_gp_cores) {
414                         /*
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.
417                          */
418                         accumulated_work_time_gpu += (time_now - work_start_time_gpu);
419                         work_start_time_gpu = 0;
420                 }
421         }
422
423         _mali_osk_spinlock_irq_unlock(time_data_lock);
424 }
425
426 u32 _mali_ukk_utilization_gp_pp(void)
427 {
428         return last_utilization_gpu;
429 }
430
431 u32 _mali_ukk_utilization_gp(void)
432 {
433         return last_utilization_gp;
434 }
435
436 u32 _mali_ukk_utilization_pp(void)
437 {
438         return last_utilization_pp;
439 }