/* * * (C) COPYRIGHT 2011-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 #include #include #include #include #include /* * This model is primarily designed for the Juno platform. It may not be * suitable for other platforms. */ #define FALLBACK_STATIC_TEMPERATURE 55000 static u32 dynamic_coefficient; static u32 static_coefficient; static s32 ts[4]; static struct thermal_zone_device *gpu_tz; static unsigned long model_static_power(unsigned long voltage) { #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) unsigned long temperature; #else int temperature; #endif unsigned long temp; unsigned long temp_squared, temp_cubed, temp_scaling_factor; const unsigned long voltage_cubed = (voltage * voltage * voltage) >> 10; if (gpu_tz) { int ret; ret = gpu_tz->ops->get_temp(gpu_tz, &temperature); if (ret) { pr_warn_ratelimited("Error reading temperature for gpu thermal zone: %d\n", ret); temperature = FALLBACK_STATIC_TEMPERATURE; } } else { temperature = FALLBACK_STATIC_TEMPERATURE; } /* Calculate the temperature scaling factor. To be applied to the * voltage scaled power. */ temp = temperature / 1000; temp_squared = temp * temp; temp_cubed = temp_squared * temp; temp_scaling_factor = (ts[3] * temp_cubed) + (ts[2] * temp_squared) + (ts[1] * temp) + ts[0]; return (((static_coefficient * voltage_cubed) >> 20) * temp_scaling_factor) / 1000000; } static unsigned long model_dynamic_power(unsigned long freq, unsigned long voltage) { /* The inputs: freq (f) is in Hz, and voltage (v) in mV. * The coefficient (c) is in mW/(MHz mV mV). * * This function calculates the dynamic power after this formula: * Pdyn (mW) = c (mW/(MHz*mV*mV)) * v (mV) * v (mV) * f (MHz) */ const unsigned long v2 = (voltage * voltage) / 1000; /* m*(V*V) */ const unsigned long f_mhz = freq / 1000000; /* MHz */ return (dynamic_coefficient * v2 * f_mhz) / 1000000; /* mW */ } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) struct devfreq_cooling_ops power_model_simple_ops = { #else struct devfreq_cooling_power power_model_simple_ops = { #endif .get_static_power = model_static_power, .get_dynamic_power = model_dynamic_power, }; int kbase_power_model_simple_init(struct kbase_device *kbdev) { struct device_node *power_model_node; const char *tz_name; u32 static_power, dynamic_power; u32 voltage, voltage_squared, voltage_cubed, frequency; power_model_node = of_get_child_by_name(kbdev->dev->of_node, "power_model"); if (!power_model_node) { dev_err(kbdev->dev, "could not find power_model node\n"); return -ENODEV; } if (!of_device_is_compatible(power_model_node, "arm,mali-simple-power-model")) { dev_err(kbdev->dev, "power_model incompatible with simple power model\n"); return -ENODEV; } if (of_property_read_string(power_model_node, "thermal-zone", &tz_name)) { dev_err(kbdev->dev, "ts in power_model not available\n"); return -EINVAL; } gpu_tz = thermal_zone_get_zone_by_name(tz_name); if (IS_ERR(gpu_tz)) { pr_warn_ratelimited("Error getting gpu thermal zone (%ld), not yet ready?\n", PTR_ERR(gpu_tz)); gpu_tz = NULL; return -EPROBE_DEFER; } if (of_property_read_u32(power_model_node, "static-power", &static_power)) { dev_err(kbdev->dev, "static-power in power_model not available\n"); return -EINVAL; } if (of_property_read_u32(power_model_node, "dynamic-power", &dynamic_power)) { dev_err(kbdev->dev, "dynamic-power in power_model not available\n"); return -EINVAL; } if (of_property_read_u32(power_model_node, "voltage", &voltage)) { dev_err(kbdev->dev, "voltage in power_model not available\n"); return -EINVAL; } if (of_property_read_u32(power_model_node, "frequency", &frequency)) { dev_err(kbdev->dev, "frequency in power_model not available\n"); return -EINVAL; } voltage_squared = (voltage * voltage) / 1000; voltage_cubed = voltage * voltage * voltage; static_coefficient = (static_power << 20) / (voltage_cubed >> 10); dynamic_coefficient = (((dynamic_power * 1000) / voltage_squared) * 1000) / frequency; if (of_property_read_u32_array(power_model_node, "ts", (u32 *)ts, 4)) { dev_err(kbdev->dev, "ts in power_model not available\n"); return -EINVAL; } return 0; }