KernelNanopiR5S/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_simple.c
2024-09-10 09:59:09 +02:00

223 lines
6.2 KiB
C

/*
*
* (C) COPYRIGHT 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 <linux/thermal.h>
#ifdef CONFIG_DEVFREQ_THERMAL
#include <linux/devfreq_cooling.h>
#endif
#include <linux/of.h>
#include <linux/math64.h>
#include "mali_kbase.h"
#include "mali_kbase_defs.h"
/*
* This model is primarily designed for the Juno platform. It may not be
* suitable for other platforms. The additional resources in this model
* should preferably be minimal, as this model is rarely used when a dynamic
* model is available.
*/
/**
* struct kbase_ipa_model_simple_data - IPA context per device
* @dynamic_coefficient: dynamic coefficient of the model
* @static_coefficient: static coefficient of the model
* @ts: Thermal scaling coefficients of the model
* @tz_name: Thermal zone name
* @gpu_tz: thermal zone device
*/
struct kbase_ipa_model_simple_data {
u32 dynamic_coefficient;
u32 static_coefficient;
s32 ts[4];
char tz_name[16];
struct thermal_zone_device *gpu_tz;
};
#define FALLBACK_STATIC_TEMPERATURE 55000
/**
* calculate_temp_scaling_factor() - Calculate temperature scaling coefficient
* @ts: Signed coefficients, in order t^0 to t^3, with units Deg^-N
* @t: Temperature, in mDeg C. Range: -2^17 < t < 2^17
*
* Scale the temperature according to a cubic polynomial whose coefficients are
* provided in the device tree. The result is used to scale the static power
* coefficient, where 1000000 means no change.
*
* Return: Temperature scaling factor. Approx range 0 < ret < 10,000,000.
*/
static u32 calculate_temp_scaling_factor(s32 ts[4], s64 t)
{
/* Range: -2^24 < t2 < 2^24 m(Deg^2) */
u32 remainder;
// static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
const s64 t2 = div_s64_rem((t * t), 1000, &remainder);
/* Range: -2^31 < t3 < 2^31 m(Deg^3) */
const s64 t3 = div_s64_rem((t * t2), 1000, &remainder);
/*
* Sum the parts. t^[1-3] are in m(Deg^N), but the coefficients are in
* Deg^-N, so we need to multiply the last coefficient by 1000.
* Range: -2^63 < res_big < 2^63
*/
const s64 res_big = ts[3] * t3 /* +/- 2^62 */
+ ts[2] * t2 /* +/- 2^55 */
+ ts[1] * t /* +/- 2^48 */
+ ts[0] * 1000; /* +/- 2^41 */
/* Range: -2^60 < res_unclamped < 2^60 */
s64 res_unclamped = div_s64_rem(res_big, 1000, &remainder);
/* Clamp to range of 0x to 10x the static power */
return clamp(res_unclamped, (s64) 0, (s64) 10000000);
}
static int model_static_coeff(struct kbase_ipa_model *model, u32 *coeffp)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
unsigned long temp;
#else
int temp;
#endif
u32 temp_scaling_factor;
struct kbase_ipa_model_simple_data *model_data =
(struct kbase_ipa_model_simple_data *) model->model_data;
struct thermal_zone_device *gpu_tz = model_data->gpu_tz;
u64 coeffp_big;
if (gpu_tz) {
int ret;
ret = gpu_tz->ops->get_temp(gpu_tz, &temp);
if (ret) {
pr_warn_ratelimited("Error reading temperature for gpu thermal zone: %d\n",
ret);
temp = FALLBACK_STATIC_TEMPERATURE;
}
} else {
temp = FALLBACK_STATIC_TEMPERATURE;
}
temp_scaling_factor = calculate_temp_scaling_factor(model_data->ts,
temp);
coeffp_big = (u64)model_data->static_coefficient * temp_scaling_factor;
*coeffp = div_u64(coeffp_big, 1000000);
return 0;
}
static int model_dynamic_coeff(struct kbase_ipa_model *model, u32 *coeffp,
u32 current_freq)
{
struct kbase_ipa_model_simple_data *model_data =
(struct kbase_ipa_model_simple_data *) model->model_data;
*coeffp = model_data->dynamic_coefficient;
return 0;
}
static int add_params(struct kbase_ipa_model *model)
{
int err = 0;
struct kbase_ipa_model_simple_data *model_data =
(struct kbase_ipa_model_simple_data *)model->model_data;
err = kbase_ipa_model_add_param_s32(model, "static-coefficient",
&model_data->static_coefficient,
1, true);
if (err)
goto end;
err = kbase_ipa_model_add_param_s32(model, "dynamic-coefficient",
&model_data->dynamic_coefficient,
1, true);
if (err)
goto end;
err = kbase_ipa_model_add_param_s32(model, "ts",
model_data->ts, 4, true);
if (err)
goto end;
err = kbase_ipa_model_add_param_string(model, "thermal-zone",
model_data->tz_name,
sizeof(model_data->tz_name), true);
end:
return err;
}
static int kbase_simple_power_model_init(struct kbase_ipa_model *model)
{
int err;
struct kbase_ipa_model_simple_data *model_data;
model_data = kzalloc(sizeof(struct kbase_ipa_model_simple_data),
GFP_KERNEL);
if (!model_data)
return -ENOMEM;
model->model_data = (void *) model_data;
err = add_params(model);
return err;
}
static int kbase_simple_power_model_recalculate(struct kbase_ipa_model *model)
{
struct kbase_ipa_model_simple_data *model_data =
(struct kbase_ipa_model_simple_data *)model->model_data;
if (!strnlen(model_data->tz_name, sizeof(model_data->tz_name))) {
model_data->gpu_tz = NULL;
} else {
model_data->gpu_tz = thermal_zone_get_zone_by_name(model_data->tz_name);
if (IS_ERR(model_data->gpu_tz)) {
pr_warn_ratelimited("Error %ld getting thermal zone \'%s\', not yet ready?\n",
PTR_ERR(model_data->gpu_tz),
model_data->tz_name);
model_data->gpu_tz = NULL;
return -EPROBE_DEFER;
}
}
return 0;
}
static void kbase_simple_power_model_term(struct kbase_ipa_model *model)
{
struct kbase_ipa_model_simple_data *model_data =
(struct kbase_ipa_model_simple_data *)model->model_data;
kfree(model_data);
}
struct kbase_ipa_model_ops kbase_simple_ipa_model_ops = {
.name = "mali-simple-power-model",
.init = &kbase_simple_power_model_init,
.recalculate = &kbase_simple_power_model_recalculate,
.term = &kbase_simple_power_model_term,
.get_dynamic_coeff = &model_dynamic_coeff,
.get_static_coeff = &model_static_coeff,
.do_utilization_scaling_in_framework = true,
};