xref: /OK3568_Linux_fs/kernel/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_pm_metrics.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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