diff options
author | Finley Xiao <finley.xiao@rock-chips.com> | 2018-11-06 11:51:00 +0800 |
---|---|---|
committer | Tao Huang <huangtao@rock-chips.com> | 2018-11-14 10:14:15 +0800 |
commit | 0f77f53e395e7aa5282f069c43475d920326b788 (patch) | |
tree | 74e21e7cac256743583b57fc7896c9c4781a2a48 /drivers/soc | |
parent | 28041002fa0665187bd0a988cc5abdf52006c18b (diff) |
soc: rockchip: Add a simple power model for ipa
This patch adds support to calculate the static power in milliwatts
consumed by devices.
Change-Id: Ied4ba5adecea45ff34b372a6e23c70882000aef3
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/rockchip/Kconfig | 11 | ||||
-rw-r--r-- | drivers/soc/rockchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/rockchip/rockchip_ipa.c | 161 |
3 files changed, 173 insertions, 0 deletions
diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 91ace3473156..0377c11b47fe 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -30,6 +30,17 @@ config ROCKCHIP_DEVICEINFO help Say y here if you have a deviceinfo partition. +config ROCKCHIP_IPA + bool "Rockchip IPA support" + depends on THERMAL && OF + default y + help + Say y here to enable rockchip IPA. + Add a simple power model for ipa to calculate static power and + dynamic power. + + If unsure, say N. + config ROCKCHIP_OPP bool "Rockchip OPP select support" depends on PM_DEVFREQ diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index 6e1dec25a028..154228c29ee0 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o obj-$(CONFIG_FIQ_DEBUGGER) += rk_fiq_debugger.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += sdmmc_vendor_storage.o obj-$(CONFIG_RK_FLASH) += flash_vendor_storage.o +obj-$(CONFIG_ROCKCHIP_IPA) += rockchip_ipa.o obj-$(CONFIG_ROCKCHIP_OPP) += rockchip_opp_select.o obj-$(CONFIG_ROCKCHIP_PVTM) += rockchip_pvtm.o obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o diff --git a/drivers/soc/rockchip/rockchip_ipa.c b/drivers/soc/rockchip/rockchip_ipa.c new file mode 100644 index 000000000000..4589a0831d55 --- /dev/null +++ b/drivers/soc/rockchip/rockchip_ipa.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd + */ +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/thermal.h> +#include <soc/rockchip/rockchip_ipa.h> + +#define FALLBACK_STATIC_TEMPERATURE 55000 + +int rockchip_ipa_power_model_init(struct device *dev, + struct ipa_power_model_data **data) +{ + struct device_node *model_node; + struct ipa_power_model_data *model_data; + const char *tz_name; + int ret; + + model_data = devm_kzalloc(dev, sizeof(*model_data), GFP_KERNEL); + if (!model_data) + return -ENOMEM; + + model_node = of_find_compatible_node(dev->of_node, + NULL, "simple-power-model"); + if (!model_node) { + dev_err(dev, "failed to find power_model node\n"); + ret = -ENODEV; + goto err; + } + + if (of_property_read_string(model_node, "thermal-zone", &tz_name)) { + dev_err(dev, "ts in power_model not available\n"); + ret = -EINVAL; + goto err; + } + model_data->tz = thermal_zone_get_zone_by_name(tz_name); + if (IS_ERR_OR_NULL(model_data->tz)) { + dev_err(dev, "failed to get thermal zone\n"); + model_data->tz = NULL; + ret = -EPROBE_DEFER; + goto err; + } + if (of_property_read_u32(model_node, "static-coefficient", + &model_data->static_coefficient)) { + dev_err(dev, "static-coefficient not available\n"); + ret = -EINVAL; + goto err; + } + /* cpu power model node doesn't contain dynamic-coefficient */ + of_property_read_u32(model_node, "dynamic-coefficient", + &model_data->dynamic_coefficient); + if (of_property_read_u32_array + (model_node, "ts", (u32 *)model_data->ts, 4)) { + dev_err(dev, "ts in power_model not available\n"); + ret = -EINVAL; + goto err; + } + *data = model_data; + + return 0; +err: + devm_kfree(dev, model_data); + + return ret; +} +EXPORT_SYMBOL(rockchip_ipa_power_model_init); + +/** + * 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. 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) */ + const s64 t2 = div_s64((t * t), 1000); + + /* Range: -2^31 < t3 < 2^31 m(Deg^3) */ + const s64 t3 = div_s64((t * t2), 1000); + + /* + * 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(res_big, 1000); + + /* Clamp to range of 0x to 10x the static power */ + return clamp(res_unclamped, (s64)0, (s64)10000000); +} + +/** + * scale_static_power() - Scale a static power coefficient to an OPP + * @c: Static model coefficient, in uW/V^3. Should be in range + * 0 < c < 2^32 to prevent overflow. + * @voltage: Voltage, in mV. Range: 2^9 < voltage < 2^13 (~0.5V to ~8V) + * + * Return: Power consumption, in mW. Range: 0 < p < 2^13 (0W to ~8W) + */ +static u32 scale_static_power(const u32 c, const u32 voltage) +{ + /* Range: 2^8 < v2 < 2^16 m(V^2) */ + const u32 v2 = (voltage * voltage) / 1000; + + /* Range: 2^17 < v3_big < 2^29 m(V^2) mV */ + const u32 v3_big = v2 * voltage; + + /* Range: 2^7 < v3 < 2^19 m(V^3) */ + const u32 v3 = v3_big / 1000; + + /* + * Range (working backwards from next line): 0 < v3c_big < 2^33 nW. + * The result should be < 2^52 to avoid overflowing the return value. + */ + const u64 v3c_big = (u64)c * (u64)v3; + + /* Range: 0 < v3c_big / 1000000 < 2^13 mW */ + return div_u64(v3c_big, 1000000); +} + +unsigned long +rockchip_ipa_get_static_power(struct ipa_power_model_data *data, + unsigned long voltage) +{ + u32 temp_scaling_factor, coeffp; + u64 coeff_big; + int temp; + int ret; + + ret = data->tz->ops->get_temp(data->tz, &temp); + if (ret) { + pr_err("%s:failed to read %s temp\n", __func__, data->tz->type); + temp = FALLBACK_STATIC_TEMPERATURE; + } + + /* Range: 0 <= temp_scaling_factor < 2^24 */ + temp_scaling_factor = calculate_temp_scaling_factor(data->ts, temp); + /* + * Range: 0 <= coeff_big < 2^52 to avoid overflowing *coeffp. This + * means static_coefficient must be in range + * 0 <= static_coefficient < 2^28. + */ + coeff_big = (u64)data->static_coefficient * (u64)temp_scaling_factor; + coeffp = div_u64(coeff_big, 1000000); + + return scale_static_power(coeffp, (u32)voltage); +} +EXPORT_SYMBOL(rockchip_ipa_get_static_power); |