tizen 2.4 release
[kernel/linux-3.0.git] / drivers / gpu / arm / mali400 / mali / common / mali_kernel_utilization.c
1 /*
2  * Copyright (C) 2011-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
16 /* Define how often to calculate and report GPU utilization, in milliseconds */
17 static _mali_osk_lock_t *time_data_lock;
18
19 static _mali_osk_atomic_t num_running_cores;
20
21 static u64 period_start_time = 0;
22 static u64 work_start_time = 0;
23 static u64 accumulated_work_time = 0;
24
25 static _mali_osk_timer_t *utilization_timer = NULL;
26 static mali_bool timer_running = MALI_FALSE;
27
28 static u32 last_utilization = 0 ;
29
30 static u32 mali_utilization_timeout = 1000;
31 static int gpu_entry_count = 0;
32 void (*mali_utilization_callback)(unsigned int) = NULL;
33
34 static void calculate_gpu_utilization(void* arg)
35 {
36         u64 time_now;
37         u64 time_period;
38         u32 leading_zeroes;
39         u32 shift_val;
40         u32 work_normalized;
41         u32 period_normalized;
42         u32 utilization;
43
44         _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW);
45
46         if (accumulated_work_time == 0 && work_start_time == 0)
47         {
48                 /* Don't reschedule timer, this will be started if new work arrives */
49                 timer_running = MALI_FALSE;
50
51                 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
52
53                 /* No work done for this period, report zero usage */
54                 if (NULL != mali_utilization_callback)
55                 {
56                         mali_utilization_callback(0);
57                 }
58
59                 return;
60         }
61
62         time_now = _mali_osk_time_get_ns();
63         time_period = time_now - period_start_time;
64
65         /* If we are currently busy, update working period up to now */
66         if (work_start_time != 0)
67         {
68                 accumulated_work_time += (time_now - work_start_time);
69                 work_start_time = time_now;
70         }
71
72         /*
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
75          * equally first.
76          * We shift the dividend up and possibly the divisor down, making the result X in 256.
77          */
78
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);
84
85         /*
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)
90          */
91         if (period_normalized > 0x00FFFFFF)
92         {
93                 /* The divisor is so big that it is safe to shift it down */
94                 period_normalized >>= 8;
95         }
96         else
97         {
98                 /*
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)
101                  */
102                 work_normalized <<= 8;
103         }
104
105         utilization = work_normalized / period_normalized;
106
107         last_utilization = utilization;
108
109         accumulated_work_time = 0;
110         period_start_time = time_now; /* starting a new period */
111
112         _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
113
114         if (gpu_entry_count <= 3) {
115                 gpu_entry_count++;
116                 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks((mali_utilization_timeout/2)));
117         } else {
118                 _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(mali_utilization_timeout));
119         }
120
121         if (NULL != mali_utilization_callback)
122         {
123                 mali_utilization_callback(utilization);
124         }
125 }
126
127 _mali_osk_errcode_t mali_utilization_init(void)
128 {
129 #if USING_GPU_UTILIZATION
130         struct _mali_osk_device_data data;
131         if (_MALI_OSK_ERR_OK == _mali_osk_device_data_get(&data))
132         {
133                 /* Use device specific settings (if defined) */
134                 if (0 != data.utilization_interval)
135                 {
136                         mali_utilization_timeout = data.utilization_interval;
137                 }
138                 if (NULL != data.utilization_handler)
139                 {
140                         mali_utilization_callback = data.utilization_handler;
141                 }
142         }
143 #endif
144
145         if (NULL != mali_utilization_callback)
146         {
147                 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: Utilization handler installed with interval %u\n", mali_utilization_timeout));
148         }
149         else
150         {
151                 MALI_DEBUG_PRINT(2, ("Mali GPU Utilization: No utilization handler installed\n"));
152         }
153
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);
156
157         if (NULL == time_data_lock)
158         {
159                 return _MALI_OSK_ERR_FAULT;
160         }
161
162         _mali_osk_atomic_init(&num_running_cores, 0);
163
164         utilization_timer = _mali_osk_timer_init();
165         if (NULL == utilization_timer)
166         {
167                 _mali_osk_lock_term(time_data_lock);
168                 return _MALI_OSK_ERR_FAULT;
169         }
170         _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL);
171
172         return _MALI_OSK_ERR_OK;
173 }
174
175 void mali_utilization_suspend(void)
176 {
177         if (NULL != utilization_timer)
178         {
179                 _mali_osk_timer_del(utilization_timer);
180                 timer_running = MALI_FALSE;
181         }
182 }
183
184 void mali_utilization_term(void)
185 {
186         if (NULL != utilization_timer)
187         {
188                 _mali_osk_timer_del(utilization_timer);
189                 timer_running = MALI_FALSE;
190                 _mali_osk_timer_term(utilization_timer);
191                 utilization_timer = NULL;
192         }
193
194         _mali_osk_atomic_term(&num_running_cores);
195
196         _mali_osk_lock_term(time_data_lock);
197 }
198
199 void mali_utilization_core_start(u64 time_now)
200 {
201         if (_mali_osk_atomic_inc_return(&num_running_cores) == 1)
202         {
203                 /*
204                  * We went from zero cores working, to one core working,
205                  * we now consider the entire GPU for being busy
206                  */
207
208                 _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW);
209
210                 if (time_now < period_start_time)
211                 {
212                         /*
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
215                          */
216                         time_now = period_start_time;
217                 }
218
219                 work_start_time = time_now;
220                 if (timer_running != MALI_TRUE)
221                 {
222                         timer_running = MALI_TRUE;
223                         period_start_time = work_start_time; /* starting a new period */
224
225                         _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
226
227                         gpu_entry_count = 0;
228                         _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks((mali_utilization_timeout/2)));
229                 }
230                 else
231                 {
232                         _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
233                 }
234         }
235 }
236
237 void mali_utilization_core_end(u64 time_now)
238 {
239         if (_mali_osk_atomic_dec_return(&num_running_cores) == 0)
240         {
241                 /*
242                  * No more cores are working, so accumulate the time we was busy.
243                  */
244                 _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW);
245
246                 if (time_now < work_start_time)
247                 {
248                         /*
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
251                          */
252                         time_now = work_start_time;
253                 }
254
255                 accumulated_work_time += (time_now - work_start_time);
256                 work_start_time = 0;
257
258                 _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW);
259         }
260 }
261
262 u32 _mali_ukk_utilization_gp_pp(void)
263 {
264         return last_utilization;
265 }
266
267 u32 _mali_ukk_utilization_gp(void)
268 {
269         return last_utilization;
270 }
271
272 u32 _mali_ukk_utilization_pp(void)
273 {
274         return last_utilization;
275 }