/* * Copyright (C) 2010-2014, 2016-2017 ARM Limited. All rights reserved. * * This program is free software and is provided to you under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. * * A copy of the licence is included with the program, and can also be obtained from Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mali_pm_metrics.h" #include "mali_osk_locks.h" #include "mali_osk_mali.h" #include #define MALI_PM_TIME_SHIFT 0 #define MALI_UTILIZATION_MAX_PERIOD 80000000/* ns = 100ms */ _mali_osk_errcode_t mali_pm_metrics_init(struct mali_device *mdev) { int i = 0; MALI_DEBUG_ASSERT(mdev != NULL); mdev->mali_metrics.time_period_start = ktime_get(); mdev->mali_metrics.time_period_start_gp = mdev->mali_metrics.time_period_start; mdev->mali_metrics.time_period_start_pp = mdev->mali_metrics.time_period_start; mdev->mali_metrics.time_busy = 0; mdev->mali_metrics.time_idle = 0; mdev->mali_metrics.prev_busy = 0; mdev->mali_metrics.prev_idle = 0; mdev->mali_metrics.num_running_gp_cores = 0; mdev->mali_metrics.num_running_pp_cores = 0; mdev->mali_metrics.time_busy_gp = 0; mdev->mali_metrics.time_idle_gp = 0; for (i = 0; i < MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS; i++) { mdev->mali_metrics.time_busy_pp[i] = 0; mdev->mali_metrics.time_idle_pp[i] = 0; } mdev->mali_metrics.gpu_active = MALI_FALSE; mdev->mali_metrics.lock = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_UNORDERED, _MALI_OSK_LOCK_ORDER_FIRST); if (NULL == mdev->mali_metrics.lock) { return _MALI_OSK_ERR_NOMEM; } return _MALI_OSK_ERR_OK; } void mali_pm_metrics_term(struct mali_device *mdev) { _mali_osk_spinlock_irq_term(mdev->mali_metrics.lock); } /*caller needs to hold mdev->mali_metrics.lock before calling this function*/ void mali_pm_record_job_status(struct mali_device *mdev) { ktime_t now; ktime_t diff; u64 ns_time; MALI_DEBUG_ASSERT(mdev != NULL); now = ktime_get(); diff = ktime_sub(now, mdev->mali_metrics.time_period_start); ns_time = (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_busy += ns_time; mdev->mali_metrics.time_period_start = now; } void mali_pm_record_gpu_idle(mali_bool is_gp) { ktime_t now; ktime_t diff; u64 ns_time; struct mali_device *mdev = dev_get_drvdata(&mali_platform_device->dev); MALI_DEBUG_ASSERT(mdev != NULL); _mali_osk_spinlock_irq_lock(mdev->mali_metrics.lock); now = ktime_get(); if (MALI_TRUE == is_gp) { --mdev->mali_metrics.num_running_gp_cores; if (0 == mdev->mali_metrics.num_running_gp_cores) { diff = ktime_sub(now, mdev->mali_metrics.time_period_start_gp); ns_time = (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_busy_gp += ns_time; mdev->mali_metrics.time_period_start_gp = now; if (0 == mdev->mali_metrics.num_running_pp_cores) { MALI_DEBUG_ASSERT(mdev->mali_metrics.gpu_active == MALI_TRUE); diff = ktime_sub(now, mdev->mali_metrics.time_period_start); ns_time = (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_busy += ns_time; mdev->mali_metrics.time_period_start = now; mdev->mali_metrics.gpu_active = MALI_FALSE; } } } else { --mdev->mali_metrics.num_running_pp_cores; if (0 == mdev->mali_metrics.num_running_pp_cores) { diff = ktime_sub(now, mdev->mali_metrics.time_period_start_pp); ns_time = (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_busy_pp[0] += ns_time; mdev->mali_metrics.time_period_start_pp = now; if (0 == mdev->mali_metrics.num_running_gp_cores) { MALI_DEBUG_ASSERT(mdev->mali_metrics.gpu_active == MALI_TRUE); diff = ktime_sub(now, mdev->mali_metrics.time_period_start); ns_time = (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_busy += ns_time; mdev->mali_metrics.time_period_start = now; mdev->mali_metrics.gpu_active = MALI_FALSE; } } } _mali_osk_spinlock_irq_unlock(mdev->mali_metrics.lock); } void mali_pm_record_gpu_active(mali_bool is_gp) { ktime_t now; ktime_t diff; struct mali_device *mdev = dev_get_drvdata(&mali_platform_device->dev); MALI_DEBUG_ASSERT(mdev != NULL); _mali_osk_spinlock_irq_lock(mdev->mali_metrics.lock); now = ktime_get(); if (MALI_TRUE == is_gp) { mdev->mali_metrics.num_running_gp_cores++; if (1 == mdev->mali_metrics.num_running_gp_cores) { diff = ktime_sub(now, mdev->mali_metrics.time_period_start_gp); mdev->mali_metrics.time_idle_gp += (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_period_start_gp = now; if (0 == mdev->mali_metrics.num_running_pp_cores) { MALI_DEBUG_ASSERT(mdev->mali_metrics.gpu_active == MALI_FALSE); diff = ktime_sub(now, mdev->mali_metrics.time_period_start); mdev->mali_metrics.time_idle += (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_period_start = now; mdev->mali_metrics.gpu_active = MALI_TRUE; } } else { MALI_DEBUG_ASSERT(mdev->mali_metrics.gpu_active == MALI_TRUE); } } else { mdev->mali_metrics.num_running_pp_cores++; if (1 == mdev->mali_metrics.num_running_pp_cores) { diff = ktime_sub(now, mdev->mali_metrics.time_period_start_pp); mdev->mali_metrics.time_idle_pp[0] += (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_period_start_pp = now; if (0 == mdev->mali_metrics.num_running_gp_cores) { MALI_DEBUG_ASSERT(mdev->mali_metrics.gpu_active == MALI_FALSE); diff = ktime_sub(now, mdev->mali_metrics.time_period_start); mdev->mali_metrics.time_idle += (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); mdev->mali_metrics.time_period_start = now; mdev->mali_metrics.gpu_active = MALI_TRUE; } } else { MALI_DEBUG_ASSERT(mdev->mali_metrics.gpu_active == MALI_TRUE); } } _mali_osk_spinlock_irq_unlock(mdev->mali_metrics.lock); } /*caller needs to hold mdev->mali_metrics.lock before calling this function*/ static void mali_pm_get_dvfs_utilisation_calc(struct mali_device *mdev, ktime_t now) { ktime_t diff; MALI_DEBUG_ASSERT(mdev != NULL); diff = ktime_sub(now, mdev->mali_metrics.time_period_start); if (mdev->mali_metrics.gpu_active) { mdev->mali_metrics.time_busy += (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); } else { mdev->mali_metrics.time_idle += (u64)(ktime_to_ns(diff) >> MALI_PM_TIME_SHIFT); } } /* Caller needs to hold mdev->mali_metrics.lock before calling this function. */ static void mali_pm_reset_dvfs_utilisation_unlocked(struct mali_device *mdev, ktime_t now) { /* Store previous value */ mdev->mali_metrics.prev_idle = mdev->mali_metrics.time_idle; mdev->mali_metrics.prev_busy = mdev->mali_metrics.time_busy; /* Reset current values */ mdev->mali_metrics.time_period_start = now; mdev->mali_metrics.time_period_start_gp = now; mdev->mali_metrics.time_period_start_pp = now; mdev->mali_metrics.time_idle = 0; mdev->mali_metrics.time_busy = 0; mdev->mali_metrics.time_busy_gp = 0; mdev->mali_metrics.time_idle_gp = 0; mdev->mali_metrics.time_busy_pp[0] = 0; mdev->mali_metrics.time_idle_pp[0] = 0; } void mali_pm_reset_dvfs_utilisation(struct mali_device *mdev) { _mali_osk_spinlock_irq_lock(mdev->mali_metrics.lock); mali_pm_reset_dvfs_utilisation_unlocked(mdev, ktime_get()); _mali_osk_spinlock_irq_unlock(mdev->mali_metrics.lock); } void mali_pm_get_dvfs_utilisation(struct mali_device *mdev, unsigned long *total_out, unsigned long *busy_out) { ktime_t now = ktime_get(); u64 busy = 0; u64 total = 0; _mali_osk_spinlock_irq_lock(mdev->mali_metrics.lock); mali_pm_get_dvfs_utilisation_calc(mdev, now); busy = mdev->mali_metrics.time_busy; total = busy + mdev->mali_metrics.time_idle; /* Reset stats if older than MALI_UTILIZATION_MAX_PERIOD (default * 100ms) */ if (total >= MALI_UTILIZATION_MAX_PERIOD) { mali_pm_reset_dvfs_utilisation_unlocked(mdev, now); } else if (total < (MALI_UTILIZATION_MAX_PERIOD / 2)) { total += mdev->mali_metrics.prev_idle + mdev->mali_metrics.prev_busy; busy += mdev->mali_metrics.prev_busy; } *total_out = (unsigned long)total; *busy_out = (unsigned long)busy; _mali_osk_spinlock_irq_unlock(mdev->mali_metrics.lock); } void mali_pm_metrics_spin_lock(void) { struct mali_device *mdev = dev_get_drvdata(&mali_platform_device->dev); _mali_osk_spinlock_irq_lock(mdev->mali_metrics.lock); } void mali_pm_metrics_spin_unlock(void) { struct mali_device *mdev = dev_get_drvdata(&mali_platform_device->dev); _mali_osk_spinlock_irq_unlock(mdev->mali_metrics.lock); }