/* * * (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 #ifdef CONFIG_DEVFREQ_THERMAL #include #endif #include #include #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, };