/*
|
* 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 <linux/ktime.h>
|
|
#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);
|
}
|