ae632564b96ab3af5e3b1e31a378f68ea49ff8b2
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / midgard / backend / gpu / mali_kbase_pm_metrics.c
1 /*
2  *
3  * (C) COPYRIGHT 2011-2015 ARM Limited. All rights reserved.
4  *
5  * This program is free software and is provided to you under the terms of the
6  * GNU General Public License version 2 as published by the Free Software
7  * Foundation, and any use by you of this program is subject to the terms
8  * of such GNU licence.
9  *
10  * A copy of the licence is included with the program, and can also be obtained
11  * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12  * Boston, MA  02110-1301, USA.
13  *
14  */
15
16
17
18
19
20 /*
21  * Metrics for power management
22  */
23
24 #include <mali_kbase.h>
25 #include <mali_kbase_pm.h>
26 #include <backend/gpu/mali_kbase_pm_internal.h>
27 #include <backend/gpu/mali_kbase_jm_rb.h>
28
29 /* When VSync is being hit aim for utilisation between 70-90% */
30 #define KBASE_PM_VSYNC_MIN_UTILISATION          70
31 #define KBASE_PM_VSYNC_MAX_UTILISATION          90
32 /* Otherwise aim for 10-40% */
33 #define KBASE_PM_NO_VSYNC_MIN_UTILISATION       10
34 #define KBASE_PM_NO_VSYNC_MAX_UTILISATION       40
35
36 /* Shift used for kbasep_pm_metrics_data.time_busy/idle - units of (1 << 8) ns
37  * This gives a maximum period between samples of 2^(32+8)/100 ns = slightly
38  * under 11s. Exceeding this will cause overflow */
39 #define KBASE_PM_TIME_SHIFT                     8
40
41 /* Maximum time between sampling of utilization data, without resetting the
42  * counters. */
43 #define MALI_UTILIZATION_MAX_PERIOD 100000 /* ns = 100ms */
44
45 #ifdef CONFIG_MALI_MIDGARD_DVFS
46 static enum hrtimer_restart dvfs_callback(struct hrtimer *timer)
47 {
48         unsigned long flags;
49         struct kbasep_pm_metrics_data *metrics;
50
51         KBASE_DEBUG_ASSERT(timer != NULL);
52
53         metrics = container_of(timer, struct kbasep_pm_metrics_data, timer);
54         kbase_pm_get_dvfs_action(metrics->kbdev);
55
56         spin_lock_irqsave(&metrics->lock, flags);
57
58         if (metrics->timer_active)
59                 hrtimer_start(timer,
60                         HR_TIMER_DELAY_MSEC(metrics->kbdev->pm.dvfs_period),
61                         HRTIMER_MODE_REL);
62
63         spin_unlock_irqrestore(&metrics->lock, flags);
64
65         return HRTIMER_NORESTART;
66 }
67 #endif /* CONFIG_MALI_MIDGARD_DVFS */
68
69 int kbasep_pm_metrics_init(struct kbase_device *kbdev)
70 {
71         KBASE_DEBUG_ASSERT(kbdev != NULL);
72
73         kbdev->pm.backend.metrics.kbdev = kbdev;
74
75         kbdev->pm.backend.metrics.time_period_start = ktime_get();
76         kbdev->pm.backend.metrics.time_busy = 0;
77         kbdev->pm.backend.metrics.time_idle = 0;
78         kbdev->pm.backend.metrics.prev_busy = 0;
79         kbdev->pm.backend.metrics.prev_idle = 0;
80         kbdev->pm.backend.metrics.gpu_active = false;
81         kbdev->pm.backend.metrics.active_cl_ctx[0] = 0;
82         kbdev->pm.backend.metrics.active_cl_ctx[1] = 0;
83         kbdev->pm.backend.metrics.active_gl_ctx[0] = 0;
84         kbdev->pm.backend.metrics.active_gl_ctx[1] = 0;
85         kbdev->pm.backend.metrics.busy_cl[0] = 0;
86         kbdev->pm.backend.metrics.busy_cl[1] = 0;
87         kbdev->pm.backend.metrics.busy_gl = 0;
88
89         spin_lock_init(&kbdev->pm.backend.metrics.lock);
90
91 #ifdef CONFIG_MALI_MIDGARD_DVFS
92         kbdev->pm.backend.metrics.timer_active = true;
93         hrtimer_init(&kbdev->pm.backend.metrics.timer, CLOCK_MONOTONIC,
94                                                         HRTIMER_MODE_REL);
95         kbdev->pm.backend.metrics.timer.function = dvfs_callback;
96
97         hrtimer_start(&kbdev->pm.backend.metrics.timer,
98                         HR_TIMER_DELAY_MSEC(kbdev->pm.dvfs_period),
99                         HRTIMER_MODE_REL);
100 #endif /* CONFIG_MALI_MIDGARD_DVFS */
101
102         return 0;
103 }
104
105 KBASE_EXPORT_TEST_API(kbasep_pm_metrics_init);
106
107 void kbasep_pm_metrics_term(struct kbase_device *kbdev)
108 {
109 #ifdef CONFIG_MALI_MIDGARD_DVFS
110         unsigned long flags;
111
112         KBASE_DEBUG_ASSERT(kbdev != NULL);
113
114         spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
115         kbdev->pm.backend.metrics.timer_active = false;
116         spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
117
118         hrtimer_cancel(&kbdev->pm.backend.metrics.timer);
119 #endif /* CONFIG_MALI_MIDGARD_DVFS */
120 }
121
122 KBASE_EXPORT_TEST_API(kbasep_pm_metrics_term);
123
124 /* caller needs to hold kbdev->pm.backend.metrics.lock before calling this
125  * function
126  */
127 static void kbase_pm_get_dvfs_utilisation_calc(struct kbase_device *kbdev,
128                                                                 ktime_t now)
129 {
130         ktime_t diff;
131
132         lockdep_assert_held(&kbdev->pm.backend.metrics.lock);
133
134         diff = ktime_sub(now, kbdev->pm.backend.metrics.time_period_start);
135         if (ktime_to_ns(diff) < 0)
136                 return;
137
138         if (kbdev->pm.backend.metrics.gpu_active) {
139                 u32 ns_time = (u32) (ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
140
141                 kbdev->pm.backend.metrics.time_busy += ns_time;
142                 if (kbdev->pm.backend.metrics.active_cl_ctx[0])
143                         kbdev->pm.backend.metrics.busy_cl[0] += ns_time;
144                 if (kbdev->pm.backend.metrics.active_cl_ctx[1])
145                         kbdev->pm.backend.metrics.busy_cl[1] += ns_time;
146                 if (kbdev->pm.backend.metrics.active_gl_ctx[0])
147                         kbdev->pm.backend.metrics.busy_gl += ns_time;
148                 if (kbdev->pm.backend.metrics.active_gl_ctx[1])
149                         kbdev->pm.backend.metrics.busy_gl += ns_time;
150         } else {
151                 kbdev->pm.backend.metrics.time_idle += (u32) (ktime_to_ns(diff)
152                                                         >> KBASE_PM_TIME_SHIFT);
153         }
154
155         kbdev->pm.backend.metrics.time_period_start = now;
156 }
157
158 #if defined(CONFIG_PM_DEVFREQ) || defined(CONFIG_MALI_MIDGARD_DVFS)
159 /* Caller needs to hold kbdev->pm.backend.metrics.lock before calling this
160  * function.
161  */
162 static void kbase_pm_reset_dvfs_utilisation_unlocked(struct kbase_device *kbdev,
163                                                                 ktime_t now)
164 {
165         /* Store previous value */
166         kbdev->pm.backend.metrics.prev_idle =
167                                         kbdev->pm.backend.metrics.time_idle;
168         kbdev->pm.backend.metrics.prev_busy =
169                                         kbdev->pm.backend.metrics.time_busy;
170
171         /* Reset current values */
172         kbdev->pm.backend.metrics.time_period_start = now;
173         kbdev->pm.backend.metrics.time_idle = 0;
174         kbdev->pm.backend.metrics.time_busy = 0;
175         kbdev->pm.backend.metrics.busy_cl[0] = 0;
176         kbdev->pm.backend.metrics.busy_cl[1] = 0;
177         kbdev->pm.backend.metrics.busy_gl = 0;
178 }
179
180 void kbase_pm_reset_dvfs_utilisation(struct kbase_device *kbdev)
181 {
182         unsigned long flags;
183
184         spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
185         kbase_pm_reset_dvfs_utilisation_unlocked(kbdev, ktime_get());
186         spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
187 }
188
189 void kbase_pm_get_dvfs_utilisation(struct kbase_device *kbdev,
190                 unsigned long *total_out, unsigned long *busy_out)
191 {
192         ktime_t now = ktime_get();
193         unsigned long flags, busy, total;
194
195         spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
196         kbase_pm_get_dvfs_utilisation_calc(kbdev, now);
197
198         busy = kbdev->pm.backend.metrics.time_busy;
199         total = busy + kbdev->pm.backend.metrics.time_idle;
200
201         /* Reset stats if older than MALI_UTILIZATION_MAX_PERIOD (default
202          * 100ms) */
203         if (total >= MALI_UTILIZATION_MAX_PERIOD) {
204                 kbase_pm_reset_dvfs_utilisation_unlocked(kbdev, now);
205         } else if (total < (MALI_UTILIZATION_MAX_PERIOD / 2)) {
206                 total += kbdev->pm.backend.metrics.prev_idle +
207                                 kbdev->pm.backend.metrics.prev_busy;
208                 busy += kbdev->pm.backend.metrics.prev_busy;
209         }
210
211         *total_out = total;
212         *busy_out = busy;
213         spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
214 }
215 #endif
216
217 #ifdef CONFIG_MALI_MIDGARD_DVFS
218
219 /* caller needs to hold kbdev->pm.backend.metrics.lock before calling this
220  * function
221  */
222 int kbase_pm_get_dvfs_utilisation_old(struct kbase_device *kbdev,
223                                         int *util_gl_share,
224                                         int util_cl_share[2],
225                                         ktime_t now)
226 {
227         int utilisation;
228         int busy;
229
230         kbase_pm_get_dvfs_utilisation_calc(kbdev, now);
231
232         if (kbdev->pm.backend.metrics.time_idle +
233                                 kbdev->pm.backend.metrics.time_busy == 0) {
234                 /* No data - so we return NOP */
235                 utilisation = -1;
236                 if (util_gl_share)
237                         *util_gl_share = -1;
238                 if (util_cl_share) {
239                         util_cl_share[0] = -1;
240                         util_cl_share[1] = -1;
241                 }
242                 goto out;
243         }
244
245         utilisation = (100 * kbdev->pm.backend.metrics.time_busy) /
246                         (kbdev->pm.backend.metrics.time_idle +
247                          kbdev->pm.backend.metrics.time_busy);
248
249         busy = kbdev->pm.backend.metrics.busy_gl +
250                 kbdev->pm.backend.metrics.busy_cl[0] +
251                 kbdev->pm.backend.metrics.busy_cl[1];
252
253         if (busy != 0) {
254                 if (util_gl_share)
255                         *util_gl_share =
256                                 (100 * kbdev->pm.backend.metrics.busy_gl) /
257                                                                         busy;
258                 if (util_cl_share) {
259                         util_cl_share[0] =
260                                 (100 * kbdev->pm.backend.metrics.busy_cl[0]) /
261                                                                         busy;
262                         util_cl_share[1] =
263                                 (100 * kbdev->pm.backend.metrics.busy_cl[1]) /
264                                                                         busy;
265                 }
266         } else {
267                 if (util_gl_share)
268                         *util_gl_share = -1;
269                 if (util_cl_share) {
270                         util_cl_share[0] = -1;
271                         util_cl_share[1] = -1;
272                 }
273         }
274
275 out:
276         return utilisation;
277 }
278
279 void kbase_pm_get_dvfs_action(struct kbase_device *kbdev)
280 {
281         unsigned long flags;
282         int utilisation, util_gl_share;
283         int util_cl_share[2];
284         ktime_t now;
285
286         KBASE_DEBUG_ASSERT(kbdev != NULL);
287
288         spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
289
290         now = ktime_get();
291
292         utilisation = kbase_pm_get_dvfs_utilisation_old(kbdev, &util_gl_share,
293                         util_cl_share, now);
294
295         if (utilisation < 0 || util_gl_share < 0 || util_cl_share[0] < 0 ||
296                                                         util_cl_share[1] < 0) {
297                 utilisation = 0;
298                 util_gl_share = 0;
299                 util_cl_share[0] = 0;
300                 util_cl_share[1] = 0;
301                 goto out;
302         }
303
304 out:
305 #ifdef CONFIG_MALI_MIDGARD_DVFS
306         kbase_platform_dvfs_event(kbdev, utilisation, util_gl_share,
307                                                                 util_cl_share);
308 #endif                          /*CONFIG_MALI_MIDGARD_DVFS */
309
310         kbase_pm_reset_dvfs_utilisation_unlocked(kbdev, now);
311
312         spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
313 }
314
315 bool kbase_pm_metrics_is_active(struct kbase_device *kbdev)
316 {
317         bool isactive;
318         unsigned long flags;
319
320         KBASE_DEBUG_ASSERT(kbdev != NULL);
321
322         spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
323         isactive = kbdev->pm.backend.metrics.timer_active;
324         spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
325
326         return isactive;
327 }
328 KBASE_EXPORT_TEST_API(kbase_pm_metrics_is_active);
329
330 #endif /* CONFIG_MALI_MIDGARD_DVFS */
331
332 /**
333  * kbase_pm_metrics_active_calc - Update PM active counts based on currently
334  *                                running atoms
335  * @kbdev: Device pointer
336  *
337  * The caller must hold kbdev->pm.backend.metrics.lock
338  */
339 static void kbase_pm_metrics_active_calc(struct kbase_device *kbdev)
340 {
341         int js;
342
343         lockdep_assert_held(&kbdev->pm.backend.metrics.lock);
344
345         kbdev->pm.backend.metrics.active_gl_ctx[0] = 0;
346         kbdev->pm.backend.metrics.active_gl_ctx[1] = 0;
347         kbdev->pm.backend.metrics.active_cl_ctx[0] = 0;
348         kbdev->pm.backend.metrics.active_cl_ctx[1] = 0;
349         kbdev->pm.backend.metrics.gpu_active = false;
350
351         for (js = 0; js < BASE_JM_MAX_NR_SLOTS; js++) {
352                 struct kbase_jd_atom *katom = kbase_gpu_inspect(kbdev, js, 0);
353
354                 /* Head atom may have just completed, so if it isn't running
355                  * then try the next atom */
356                 if (katom && katom->gpu_rb_state != KBASE_ATOM_GPU_RB_SUBMITTED)
357                         katom = kbase_gpu_inspect(kbdev, js, 1);
358
359                 if (katom && katom->gpu_rb_state ==
360                                 KBASE_ATOM_GPU_RB_SUBMITTED) {
361                         if (katom->core_req & BASE_JD_REQ_ONLY_COMPUTE) {
362                                 int device_nr = (katom->core_req &
363                                         BASE_JD_REQ_SPECIFIC_COHERENT_GROUP)
364                                                 ? katom->device_nr : 0;
365                                 WARN_ON(device_nr >= 2);
366                                 kbdev->pm.backend.metrics.active_cl_ctx[
367                                                 device_nr] = 1;
368                         } else {
369                                 /* Slot 2 should not be running non-compute
370                                  * atoms */
371                                 WARN_ON(js >= 2);
372                                 kbdev->pm.backend.metrics.active_gl_ctx[js] = 1;
373                         }
374                         kbdev->pm.backend.metrics.gpu_active = true;
375                 }
376         }
377 }
378
379 /* called when job is submitted to or removed from a GPU slot */
380 void kbase_pm_metrics_update(struct kbase_device *kbdev, ktime_t *timestamp)
381 {
382         unsigned long flags;
383         ktime_t now;
384
385         lockdep_assert_held(&kbdev->js_data.runpool_irq.lock);
386
387         spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
388
389         if (!timestamp) {
390                 now = ktime_get();
391                 timestamp = &now;
392         }
393
394         /* Track how long CL and/or GL jobs have been busy for */
395         kbase_pm_get_dvfs_utilisation_calc(kbdev, *timestamp);
396
397         kbase_pm_metrics_active_calc(kbdev);
398
399         spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
400 }