| /* |
| * |
| * (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); |