1 /*
2 *
3 * (C) COPYRIGHT 2011-2017 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
dvfs_callback(struct hrtimer * timer)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
kbasep_pm_metrics_init(struct kbase_device * kbdev)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
kbasep_pm_metrics_term(struct kbase_device * kbdev)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 */
kbase_pm_get_dvfs_utilisation_calc(struct kbase_device * kbdev,ktime_t now)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_MALI_DEVFREQ) || defined(CONFIG_MALI_MIDGARD_DVFS)
159 /* Caller needs to hold kbdev->pm.backend.metrics.lock before calling this
160 * function.
161 */
kbase_pm_reset_dvfs_utilisation_unlocked(struct kbase_device * kbdev,ktime_t now)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
kbase_pm_reset_dvfs_utilisation(struct kbase_device * kbdev)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
kbase_pm_get_dvfs_utilisation(struct kbase_device * kbdev,unsigned long * total_out,unsigned long * busy_out)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 */
kbase_pm_get_dvfs_utilisation_old(struct kbase_device * kbdev,int * util_gl_share,int util_cl_share[2],ktime_t now)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
kbase_pm_get_dvfs_action(struct kbase_device * kbdev)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
kbase_pm_metrics_is_active(struct kbase_device * kbdev)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 */
kbase_pm_metrics_active_calc(struct kbase_device * kbdev)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 if (!WARN_ON(device_nr >= 2))
366 kbdev->pm.backend.metrics.
367 active_cl_ctx[device_nr] = 1;
368 } else {
369 /* Slot 2 should not be running non-compute
370 * atoms */
371 if (!WARN_ON(js >= 2))
372 kbdev->pm.backend.metrics.
373 active_gl_ctx[js] = 1;
374 }
375 kbdev->pm.backend.metrics.gpu_active = true;
376 }
377 }
378 }
379
380 /* called when job is submitted to or removed from a GPU slot */
kbase_pm_metrics_update(struct kbase_device * kbdev,ktime_t * timestamp)381 void kbase_pm_metrics_update(struct kbase_device *kbdev, ktime_t *timestamp)
382 {
383 unsigned long flags;
384 ktime_t now;
385
386 lockdep_assert_held(&kbdev->hwaccess_lock);
387
388 spin_lock_irqsave(&kbdev->pm.backend.metrics.lock, flags);
389
390 if (!timestamp) {
391 now = ktime_get();
392 timestamp = &now;
393 }
394
395 /* Track how long CL and/or GL jobs have been busy for */
396 kbase_pm_get_dvfs_utilisation_calc(kbdev, *timestamp);
397
398 kbase_pm_metrics_active_calc(kbdev);
399
400 spin_unlock_irqrestore(&kbdev->pm.backend.metrics.lock, flags);
401 }
402