3 * (C) COPYRIGHT ARM Limited. All rights reserved.
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
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.
21 * @file mali_kbase_pm_metrics.c
22 * Metrics for power management
25 #include <kbase/src/common/mali_kbase.h>
26 #include <kbase/src/common/mali_kbase_pm.h>
28 /* When VSync is being hit aim for utilisation between 70-90% */
29 #define KBASE_PM_VSYNC_MIN_UTILISATION 70
30 #define KBASE_PM_VSYNC_MAX_UTILISATION 90
31 /* Otherwise aim for 10-40% */
32 #define KBASE_PM_NO_VSYNC_MIN_UTILISATION 10
33 #define KBASE_PM_NO_VSYNC_MAX_UTILISATION 40
35 /* Shift used for kbasep_pm_metrics_data.time_busy/idle - units of (1 << 8) ns
36 This gives a maximum period between samples of 2^(32+8)/100 ns = slightly under 11s.
37 Exceeding this will cause overflow */
38 #define KBASE_PM_TIME_SHIFT 8
40 static enum hrtimer_restart dvfs_callback(struct hrtimer *timer)
43 kbase_pm_dvfs_action action;
44 kbasep_pm_metrics_data *metrics;
46 KBASE_DEBUG_ASSERT(timer != NULL);
48 metrics = container_of(timer, kbasep_pm_metrics_data, timer);
49 action = kbase_pm_get_dvfs_action(metrics->kbdev);
51 spin_lock_irqsave(&metrics->lock, flags);
53 if (metrics->timer_active)
55 HR_TIMER_DELAY_MSEC(metrics->kbdev->pm.platform_dvfs_frequency),
58 spin_unlock_irqrestore(&metrics->lock, flags);
60 return HRTIMER_NORESTART;
63 mali_error kbasep_pm_metrics_init(kbase_device *kbdev)
65 KBASE_DEBUG_ASSERT(kbdev != NULL);
67 kbdev->pm.metrics.kbdev = kbdev;
68 kbdev->pm.metrics.vsync_hit = 0;
69 kbdev->pm.metrics.utilisation = 0;
71 kbdev->pm.metrics.time_period_start = ktime_get();
72 kbdev->pm.metrics.time_busy = 0;
73 kbdev->pm.metrics.time_idle = 0;
74 kbdev->pm.metrics.gpu_active = MALI_TRUE;
75 kbdev->pm.metrics.timer_active = MALI_TRUE;
77 spin_lock_init(&kbdev->pm.metrics.lock);
79 hrtimer_init(&kbdev->pm.metrics.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
80 kbdev->pm.metrics.timer.function = dvfs_callback;
82 hrtimer_start(&kbdev->pm.metrics.timer, HR_TIMER_DELAY_MSEC(kbdev->pm.platform_dvfs_frequency), HRTIMER_MODE_REL);
84 kbase_pm_register_vsync_callback(kbdev);
86 return MALI_ERROR_NONE;
89 KBASE_EXPORT_TEST_API(kbasep_pm_metrics_init)
91 void kbasep_pm_metrics_term(kbase_device *kbdev)
94 KBASE_DEBUG_ASSERT(kbdev != NULL);
96 spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
97 kbdev->pm.metrics.timer_active = MALI_FALSE;
98 spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
100 hrtimer_cancel(&kbdev->pm.metrics.timer);
102 kbase_pm_unregister_vsync_callback(kbdev);
105 KBASE_EXPORT_TEST_API(kbasep_pm_metrics_term)
107 void kbasep_pm_record_gpu_idle(kbase_device *kbdev)
113 KBASE_DEBUG_ASSERT(kbdev != NULL);
115 spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
117 KBASE_DEBUG_ASSERT(kbdev->pm.metrics.gpu_active == MALI_TRUE);
119 kbdev->pm.metrics.gpu_active = MALI_FALSE;
122 diff = ktime_sub(now, kbdev->pm.metrics.time_period_start);
124 kbdev->pm.metrics.time_busy += (u32) (ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
125 kbdev->pm.metrics.time_period_start = now;
127 spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
130 KBASE_EXPORT_TEST_API(kbasep_pm_record_gpu_idle)
132 void kbasep_pm_record_gpu_active(kbase_device *kbdev)
138 KBASE_DEBUG_ASSERT(kbdev != NULL);
140 spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
142 KBASE_DEBUG_ASSERT(kbdev->pm.metrics.gpu_active == MALI_FALSE);
144 kbdev->pm.metrics.gpu_active = MALI_TRUE;
147 diff = ktime_sub(now, kbdev->pm.metrics.time_period_start);
149 kbdev->pm.metrics.time_idle += (u32) (ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
150 kbdev->pm.metrics.time_period_start = now;
152 spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
155 KBASE_EXPORT_TEST_API(kbasep_pm_record_gpu_active)
157 void kbase_pm_report_vsync(kbase_device *kbdev, int buffer_updated)
160 KBASE_DEBUG_ASSERT(kbdev != NULL);
162 spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
163 kbdev->pm.metrics.vsync_hit = buffer_updated;
164 spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
167 KBASE_EXPORT_TEST_API(kbase_pm_report_vsync)
169 /*caller needs to hold kbdev->pm.metrics.lock before calling this function*/
170 int kbase_pm_get_dvfs_utilisation(kbase_device *kbdev)
173 ktime_t now = ktime_get();
176 KBASE_DEBUG_ASSERT(kbdev != NULL);
178 diff = ktime_sub(now, kbdev->pm.metrics.time_period_start);
180 if (kbdev->pm.metrics.gpu_active) {
181 kbdev->pm.metrics.time_busy += (u32) (ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
182 kbdev->pm.metrics.time_period_start = now;
184 kbdev->pm.metrics.time_idle += (u32) (ktime_to_ns(diff) >> KBASE_PM_TIME_SHIFT);
185 kbdev->pm.metrics.time_period_start = now;
188 if (kbdev->pm.metrics.time_idle + kbdev->pm.metrics.time_busy == 0) {
189 /* No data - so we return NOP */
194 utilisation = (100 * kbdev->pm.metrics.time_busy) / (kbdev->pm.metrics.time_idle + kbdev->pm.metrics.time_busy);
198 kbdev->pm.metrics.time_idle = 0;
199 kbdev->pm.metrics.time_busy = 0;
204 kbase_pm_dvfs_action kbase_pm_get_dvfs_action(kbase_device *kbdev)
208 kbase_pm_dvfs_action action;
210 KBASE_DEBUG_ASSERT(kbdev != NULL);
212 spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
214 utilisation = kbase_pm_get_dvfs_utilisation(kbdev);
216 if (utilisation < 0) {
217 action = KBASE_PM_DVFS_NOP;
222 if (kbdev->pm.metrics.vsync_hit) {
223 /* VSync is being met */
224 if (utilisation < KBASE_PM_VSYNC_MIN_UTILISATION)
225 action = KBASE_PM_DVFS_CLOCK_DOWN;
226 else if (utilisation > KBASE_PM_VSYNC_MAX_UTILISATION)
227 action = KBASE_PM_DVFS_CLOCK_UP;
229 action = KBASE_PM_DVFS_NOP;
231 /* VSync is being missed */
232 if (utilisation < KBASE_PM_NO_VSYNC_MIN_UTILISATION)
233 action = KBASE_PM_DVFS_CLOCK_DOWN;
234 else if (utilisation > KBASE_PM_NO_VSYNC_MAX_UTILISATION)
235 action = KBASE_PM_DVFS_CLOCK_UP;
237 action = KBASE_PM_DVFS_NOP;
240 kbdev->pm.metrics.utilisation = utilisation;
242 #ifdef CONFIG_MALI_T6XX_DVFS
243 kbase_platform_dvfs_event(kbdev, utilisation);
244 #endif /*CONFIG_MALI_T6XX_DVFS */
245 kbdev->pm.metrics.time_idle = 0;
246 kbdev->pm.metrics.time_busy = 0;
247 spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
251 KBASE_EXPORT_TEST_API(kbase_pm_get_dvfs_action)
253 mali_bool kbase_pm_metrics_is_active(kbase_device *kbdev)
258 KBASE_DEBUG_ASSERT(kbdev != NULL);
260 spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
261 isactive = (kbdev->pm.metrics.timer_active == MALI_TRUE);
262 spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
266 KBASE_EXPORT_TEST_API(kbase_pm_metrics_is_active)