diff options
Diffstat (limited to 'driver/product/kernel/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c')
-rwxr-xr-x | driver/product/kernel/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/driver/product/kernel/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c b/driver/product/kernel/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c new file mode 100755 index 0000000..dd0f1d6 --- /dev/null +++ b/driver/product/kernel/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c @@ -0,0 +1,327 @@ +/* + * + * (C) COPYRIGHT 2016 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/sysfs.h> +#include <linux/thermal.h> +#include <linux/devfreq_cooling.h> +#include <linux/of.h> +#include "mali_kbase.h" + +int kbase_ipa_model_ops_register(struct kbase_device *kbdev, + struct kbase_ipa_model_ops *new_model_ops) +{ + struct kbase_ipa_model *new_model; + + new_model = kzalloc(sizeof(struct kbase_ipa_model), GFP_KERNEL); + if (!new_model) + return -ENOMEM; + INIT_LIST_HEAD(&new_model->link); + new_model->kbdev = kbdev; + new_model->ops = new_model_ops; + + list_add(&new_model->link, &kbdev->ipa_power_models); + + return 0; +} + +static int kbase_ipa_internal_models_append_list(struct kbase_device *kbdev) +{ + int err; + + /* Always have the generic IPA */ + err = kbase_ipa_model_ops_register(kbdev, &kbase_generic_ipa_model_ops); + + if (err) + return err; + + return err; +} + +struct kbase_ipa_model *kbase_ipa_get_model(struct kbase_device *kbdev, + const char *name) +{ + /* Search registered power models first */ + struct list_head *it; + + list_for_each(it, &kbdev->ipa_power_models) { + struct kbase_ipa_model *model = + list_entry(it, + struct kbase_ipa_model, + link); + if (strcmp(model->ops->name, name) == 0) + return model; + } + + return NULL; +} + +void kbase_ipa_model_use_fallback_locked(struct kbase_device *kbdev) +{ + lockdep_assert_held(&kbdev->hwaccess_lock); + if (kbdev->ipa_current_model != kbdev->ipa_fallback_model) + kbdev->ipa_current_model = kbdev->ipa_fallback_model; +} + + +void kbase_ipa_model_use_configured_locked(struct kbase_device *kbdev) +{ + lockdep_assert_held(&kbdev->hwaccess_lock); + if (kbdev->ipa_current_model != kbdev->ipa_configured_model) + kbdev->ipa_current_model = kbdev->ipa_configured_model; +} + +const char *kbase_ipa_model_name_from_id(u32 gpu_id) +{ + const u32 prod_id = (gpu_id & GPU_ID_VERSION_PRODUCT_ID) >> + GPU_ID_VERSION_PRODUCT_ID_SHIFT; + + if (GPU_ID_IS_NEW_FORMAT(prod_id)) { + switch (GPU_ID2_MODEL_MATCH_VALUE(prod_id)) { + case GPU_ID2_PRODUCT_TMIX: + return "generic_ipa_model"; + default: + return "generic_ipa_model"; + } + } + + return "generic_ipa_model"; +} + +int kbase_ipa_model_init(struct kbase_device *kbdev) +{ + + const char *model_name; + struct kbase_ipa_model *default_model = NULL; + struct kbase_ipa_model *model = NULL; + int err; + + /* Add default ones to the list */ + err = kbase_ipa_internal_models_append_list(kbdev); + + default_model = kbase_ipa_get_model(kbdev, "generic_ipa_model"); + /* The generic_ipa_model 'MUST' always be present.*/ + if (!default_model) { + err = -EINVAL; + goto end; + } + + err = default_model->ops->init(default_model); + if (err) { + dev_err(kbdev->dev, + "failed to init default generic power model err %d\n", + err); + goto end; + } + kbdev->ipa_fallback_model = default_model; + err = of_property_read_string(kbdev->dev->of_node, + "ipa-model", + &model_name); + if (err) { + /* Attempt to load a match from GPU-ID */ + u32 gpu_id; + + gpu_id = kbdev->gpu_props.props.raw_props.gpu_id; + model_name = kbase_ipa_model_name_from_id(gpu_id); + } + + if (strcmp("generic_ipa_model", model_name) != 0) { + + model = kbase_ipa_get_model(kbdev, model_name); + kbdev->ipa_configured_model = model; + + if (model) { + kbdev->ipa_current_model = model; + err = model->ops->init(model); + goto end; + } + dev_err(kbdev->dev, + "Failed to find IPA model matching: %s\n", + model_name); + err = -EINVAL; + } else { + + kbdev->ipa_current_model = default_model; + kbdev->ipa_configured_model = default_model; + dev_dbg(kbdev->dev, "Using generic-ipa-model by default\n"); + err = 0; + } +end: + if (err) + kbase_ipa_model_term(kbdev); + + return err; +} + +void kbase_ipa_model_term(struct kbase_device *kbdev) +{ + /* Clean up the models */ + struct kbase_ipa_model *model = kbdev->ipa_configured_model; + + if (model) { + if (model->ops->term) + model->ops->term(model); + /* Always terminate the default model, + * unless it was the configured model */ + if (model != kbdev->ipa_fallback_model) + kbdev->ipa_fallback_model->ops->term(model); + } + /* Clean up the list */ + if (!list_empty(&kbdev->ipa_power_models)) { + struct kbase_ipa_model *model_p, *model_n; + + list_for_each_entry_safe(model_p, model_n, &kbdev->ipa_power_models, link) { + list_del(&model_p->link); + kfree(model_p); + } + } + +} + +/** + * kbase_scale_power() - Scale a model-specific power coefficient to an OPP + * @c: Model coefficient, in pW/(Hz V^2). Should be in range + * 0 < c < 2^16 to prevent overflow. + * @freq: Frequency, in Hz. Range: 2^23 < freq < 2^30 (~8MHz to ~1GHz) + * @voltage: Voltage, in mV. Range: 2^9 < voltage < 2^13 (~0.5V to ~8V) + * + * Keep a record of the approximate range of each value at every stage of the + * calculation, to ensure we don't overflow. This makes heavy use of the + * approximations 1000 = 2^10 and 1000000 = 2^20, but does the actual + * calculations in decimal for increased accuracy. + * + * Return: Power consumption, in mW. Range: 0 < p < 2^13 (0W to ~8W) + */ +static inline unsigned long kbase_scale_power(const unsigned long c, + const unsigned long freq, + const unsigned long voltage) +{ + /* Range: 2^8 < v2 < 2^16 m(V^2) */ + const unsigned long v2 = (voltage * voltage) / 1000; + + /* Range: 2^3 < f_MHz < 2^10 MHz */ + const unsigned long f_MHz = freq / 1000000; + + /* Range: 2^11 < v2f_big < 2^26 kHz V^2 */ + const unsigned long v2f_big = v2 * f_MHz; + + /* Range: 2^1 < v2f < 2^16 MHz V^2 */ + const unsigned long v2f = v2f_big / 1000; + + /* Range (working backwards from next line): 0 < v2fc < 2^23 uW */ + const unsigned long v2fc = c * v2f; + + /* Range: 0 < v2fc / 1000 < 2^13 mW */ + return v2fc / 1000; +} + +#ifdef CONFIG_MALI_PWRSOFT_765 +static unsigned long kbase_get_static_power(struct devfreq *df, + unsigned long voltage) +#else +static unsigned long kbase_get_static_power(unsigned long voltage) + +#endif +{ + struct kbase_ipa_model *model; +#ifdef CONFIG_MALI_PWRSOFT_765 + struct kbase_device *kbdev = dev_get_drvdata(&df->dev); +#else + struct kbase_device *kbdev = kbase_find_device(-1); +#endif + + /* We make sure that the model we access is the correct one */ + model = ACCESS_ONCE(kbdev->ipa_current_model); + +#ifndef CONFIG_MALI_PWRSOFT_765 + kbase_release_device(kbdev); +#endif + + if (model) + return model->ops->get_static_power(model, voltage); + return 0; +} + +#ifdef CONFIG_MALI_PWRSOFT_765 +static unsigned long kbase_get_dynamic_power(struct devfreq *df, + unsigned long freq, + unsigned long voltage) +#else +static unsigned long kbase_get_dynamic_power(unsigned long freq, + unsigned long voltage) +#endif +{ + struct kbase_ipa_model *model; +#ifdef CONFIG_MALI_PWRSOFT_765 + struct kbase_device *kbdev = dev_get_drvdata(&df->dev); +#else + struct kbase_device *kbdev = kbase_find_device(-1); +#endif + + /* We make sure that the model we access is the correct one */ + model = ACCESS_ONCE(kbdev->ipa_current_model); + +#ifndef CONFIG_MALI_PWRSOFT_765 + kbase_release_device(kbdev); +#endif + + if (model) { + const unsigned long c = model->ops->get_dynamic_power(model); + + return kbase_scale_power(c, freq, voltage); + } + + return 0; +} + +#ifdef CONFIG_MALI_PWRSOFT_765 +unsigned long kbase_power_to_state(struct devfreq *df, u32 target_power) +{ + struct kbase_ipa_model *model; + struct kbase_device *kbdev = dev_get_drvdata(&df->dev); + + /* We make sure that the model we access is the correct one */ + model = ACCESS_ONCE(kbdev->ipa_current_model); + + if (model && model->ops->power_to_state) + return model->ops->power_to_state(model, target_power); + + return 0; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) +struct devfreq_cooling_ops power_model_ops = { +#else +struct devfreq_cooling_power power_model_ops = { +#endif + .get_static_power = &kbase_get_static_power, + .get_dynamic_power = &kbase_get_dynamic_power, +}; + +unsigned long kbase_ipa_dynamic_power(struct kbase_device *kbdev, + unsigned long freq, + unsigned long voltage) +{ +#ifdef CONFIG_MALI_PWRSOFT_765 + struct devfreq *df = kbdev->devfreq; + + return kbase_get_dynamic_power(df, freq, voltage); +#else + return kbase_get_dynamic_power(freq, voltage); +#endif +} + +KBASE_EXPORT_TEST_API(kbase_ipa_dynamic_power); |