236 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
 | |
|  */
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/of.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/thermal.h>
 | |
| #include <soc/rockchip/rockchip_ipa.h>
 | |
| #include <soc/rockchip/rockchip_opp_select.h>
 | |
| #include <trace/events/thermal.h>
 | |
| 
 | |
| #define CREATE_TRACE_POINTS
 | |
| #include <trace/events/thermal_ipa_power.h>
 | |
| 
 | |
| #define FALLBACK_STATIC_TEMPERATURE 55000
 | |
| 
 | |
| static void calculate_static_coefficient(struct ipa_power_model_data *data)
 | |
| {
 | |
| 	s32 *ls = data->ls;
 | |
| 	u32 lkg = data->leakage;
 | |
| 	u32 ref_lkg = data->ref_leakage;
 | |
| 	u32 min = data->lkg_range[0], max = data->lkg_range[1];
 | |
| 	u32 static_coeff = data->static_coefficient;
 | |
| 	u32 lkg_scaling_factor;
 | |
| 
 | |
| 	if (!lkg) {
 | |
| 		if (ref_lkg)
 | |
| 			lkg = ref_lkg;
 | |
| 		else
 | |
| 			lkg = (min + max) / 2;
 | |
| 	}
 | |
| 	if (ref_lkg) {
 | |
| 		data->static_coefficient = static_coeff * lkg / ref_lkg;
 | |
| 		return;
 | |
| 	}
 | |
| 	if (lkg < min)
 | |
| 		lkg = min;
 | |
| 	if (lkg > max)
 | |
| 		lkg = max;
 | |
| 	/* As ts have beed multiplied by 1000 in devicetree */
 | |
| 	lkg_scaling_factor = (ls[2] * lkg * lkg + ls[1] * lkg + ls[0]) / 1000;
 | |
| 	data->static_coefficient = static_coeff * lkg_scaling_factor / 100;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * rockchip_ipa_power_model_init() - initialise ipa power model parameter
 | |
|  * @dev:	device for which we do this operation
 | |
|  * @lkg_name:	nvmem cell name from nvmem-cell-names property
 | |
|  *
 | |
|  * Return: a valid struct ipa_power_model_data pointer on success, and the onwer
 | |
|  * should use kfree to release the memory by itself. on failure, it returns a
 | |
|  * corresponding ERR_PTR().
 | |
|  */
 | |
| struct ipa_power_model_data *rockchip_ipa_power_model_init(struct device *dev,
 | |
| 							   char *lkg_name)
 | |
| {
 | |
| 	struct device_node *model_node;
 | |
| 	struct ipa_power_model_data *model_data;
 | |
| 	const char *tz_name;
 | |
| 	int ret;
 | |
| 
 | |
| 	model_data = kzalloc(sizeof(*model_data), GFP_KERNEL);
 | |
| 	if (!model_data)
 | |
| 		return ERR_PTR(-ENOMEM);
 | |
| 
 | |
| 	model_node = of_get_compatible_child(dev->of_node,
 | |
| 					     "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;
 | |
| 	}
 | |
| 	rockchip_of_get_leakage(dev, lkg_name, &model_data->leakage);
 | |
| 	if (!of_property_read_u32(model_node, "ref-leakage",
 | |
| 				&model_data->ref_leakage))
 | |
| 		goto cal_static_coeff;
 | |
| 	if (of_property_read_u32_array(model_node, "leakage-range",
 | |
| 				       (u32 *)model_data->lkg_range, 2)) {
 | |
| 		dev_err(dev, "leakage-range isn't available\n");
 | |
| 		ret = -EINVAL;
 | |
| 		goto err;
 | |
| 	}
 | |
| 	if (of_property_read_u32_array(model_node, "ls",
 | |
| 				       (u32 *)model_data->ls, 3)) {
 | |
| 		dev_err(dev, "ls isn't available\n");
 | |
| 		ret = -EINVAL;
 | |
| 		goto err;
 | |
| 	}
 | |
| cal_static_coeff:
 | |
| 	calculate_static_coefficient(model_data);
 | |
| 
 | |
| 	of_node_put(model_node);
 | |
| 
 | |
| 	return model_data;
 | |
| err:
 | |
| 	of_node_put(model_node);
 | |
| 	kfree(model_data);
 | |
| 
 | |
| 	return ERR_PTR(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: -40000 < t < 125000
 | |
|  *
 | |
|  * 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.
 | |
|  */
 | |
| static u32 calculate_temp_scaling_factor(s32 ts[4], s64 t)
 | |
| {
 | |
| 	const s64 t2 = div_s64((t * t), 1000);
 | |
| 
 | |
| 	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.
 | |
| 	 */
 | |
| 	const s64 res_big = ts[3] * t3
 | |
| 			  + ts[2] * t2
 | |
| 			  + ts[1] * t
 | |
| 			  + ts[0] * 1000LL;
 | |
| 
 | |
| 	/* ts has beed multiplied by 10 in devicetree */
 | |
| 	s64 res_unclamped = div_s64(res_big, 10000);
 | |
| 
 | |
| 	/* Clamp to range of 0x to 10x the static power */
 | |
| 	return clamp(res_unclamped, (s64)0, (s64)10000000);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * calculate_volt_scaling_factor() - Calculate voltage scaling coefficient
 | |
|  * voltage_mv:	Voltage, in mV. Range: 750 < voltage < 1350
 | |
|  *
 | |
|  * Return: Voltage scaling factor.
 | |
|  */
 | |
| static u32 calculate_volt_scaling_factor(const u32 voltage_mv)
 | |
| {
 | |
| 	const u32 v2 = (voltage_mv * voltage_mv) / 1000;
 | |
| 
 | |
| 	const u32 v3_big = v2 * voltage_mv;
 | |
| 
 | |
| 	const u32 v3 = v3_big / 1000;
 | |
| 
 | |
| 	const u32 v4_big = v3 * voltage_mv;
 | |
| 
 | |
| 	const u32 v4 = v4_big / 1000;
 | |
| 
 | |
| 	return v4;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * rockchip_ipa_get_static_power() - Calculate static power
 | |
|  * @data:	Pointer to IPA model
 | |
|  * voltage_mv:	Voltage, in mV. Range: 750 < voltage < 1350
 | |
|  *
 | |
|  * Return: Static power.
 | |
|  */
 | |
| unsigned long
 | |
| rockchip_ipa_get_static_power(struct ipa_power_model_data *data,
 | |
| 			      unsigned long voltage_mv)
 | |
| {
 | |
| 	u32 temp_scaling_factor, volt_scaling_factor, static_power;
 | |
| 	u64 power_big;
 | |
| 	int temp;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (!data)
 | |
| 		return 0;
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	temp_scaling_factor = calculate_temp_scaling_factor(data->ts, temp);
 | |
| 	volt_scaling_factor = calculate_volt_scaling_factor((u32)voltage_mv);
 | |
| 
 | |
| 	power_big = (u64)data->static_coefficient * (u64)temp_scaling_factor;
 | |
| 	static_power = div_u64(power_big, 1000000);
 | |
| 	power_big = (u64)static_power * (u64)volt_scaling_factor;
 | |
| 	static_power = div_u64(power_big, 1000000);
 | |
| 
 | |
| 	trace_thermal_ipa_get_static_power(data->leakage,
 | |
| 					   data->static_coefficient,
 | |
| 					   temp,
 | |
| 					   temp_scaling_factor,
 | |
| 					   (u32)voltage_mv,
 | |
| 					   volt_scaling_factor,
 | |
| 					   static_power);
 | |
| 
 | |
| 	return static_power;
 | |
| }
 | |
| EXPORT_SYMBOL(rockchip_ipa_get_static_power);
 | |
| 
 | |
| MODULE_DESCRIPTION("Rockchip IPA driver");
 | |
| MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
 | |
| MODULE_LICENSE("GPL");
 |