• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 #include <linux/kernel.h>
6 #include <linux/of.h>
7 #include <linux/module.h>
8 #include <linux/slab.h>
9 #include <linux/thermal.h>
10 #include <soc/rockchip/rockchip_ipa.h>
11 #include <soc/rockchip/rockchip_opp_select.h>
12 #include <trace/events/thermal.h>
13 
14 #define CREATE_TRACE_POINTS
15 #include <trace/events/thermal_ipa_power.h>
16 
17 #define FALLBACK_STATIC_TEMPERATURE 55000
18 
calculate_static_coefficient(struct ipa_power_model_data * data)19 static void calculate_static_coefficient(struct ipa_power_model_data *data)
20 {
21 	s32 *ls = data->ls;
22 	u32 lkg = data->leakage;
23 	u32 ref_lkg = data->ref_leakage;
24 	u32 min = data->lkg_range[0], max = data->lkg_range[1];
25 	u32 static_coeff = data->static_coefficient;
26 	u32 lkg_scaling_factor;
27 
28 	if (!lkg) {
29 		if (ref_lkg)
30 			lkg = ref_lkg;
31 		else
32 			lkg = (min + max) / 2;
33 	}
34 	if (ref_lkg) {
35 		data->static_coefficient = static_coeff * lkg / ref_lkg;
36 		return;
37 	}
38 	if (lkg < min)
39 		lkg = min;
40 	if (lkg > max)
41 		lkg = max;
42 	/* As ts have beed multiplied by 1000 in devicetree */
43 	lkg_scaling_factor = (ls[2] * lkg * lkg + ls[1] * lkg + ls[0]) / 1000;
44 	data->static_coefficient = static_coeff * lkg_scaling_factor / 100;
45 }
46 
47 /**
48  * rockchip_ipa_power_model_init() - initialise ipa power model parameter
49  * @dev:	device for which we do this operation
50  * @lkg_name:	nvmem cell name from nvmem-cell-names property
51  *
52  * Return: a valid struct ipa_power_model_data pointer on success, and the onwer
53  * should use kfree to release the memory by itself. on failure, it returns a
54  * corresponding ERR_PTR().
55  */
rockchip_ipa_power_model_init(struct device * dev,char * lkg_name)56 struct ipa_power_model_data *rockchip_ipa_power_model_init(struct device *dev,
57 							   char *lkg_name)
58 {
59 	struct device_node *model_node;
60 	struct ipa_power_model_data *model_data;
61 	const char *tz_name;
62 	int ret;
63 
64 	model_data = kzalloc(sizeof(*model_data), GFP_KERNEL);
65 	if (!model_data)
66 		return ERR_PTR(-ENOMEM);
67 
68 	model_node = of_get_compatible_child(dev->of_node,
69 					     "simple-power-model");
70 	if (!model_node) {
71 		dev_err(dev, "failed to find power_model node\n");
72 		ret = -ENODEV;
73 		goto err;
74 	}
75 
76 	if (of_property_read_string(model_node, "thermal-zone", &tz_name)) {
77 		dev_err(dev, "ts in power_model not available\n");
78 		ret = -EINVAL;
79 		goto err;
80 	}
81 	model_data->tz = thermal_zone_get_zone_by_name(tz_name);
82 	if (IS_ERR_OR_NULL(model_data->tz)) {
83 		dev_err(dev, "failed to get thermal zone\n");
84 		model_data->tz = NULL;
85 		ret = -EPROBE_DEFER;
86 		goto err;
87 	}
88 	if (of_property_read_u32(model_node, "static-coefficient",
89 				 &model_data->static_coefficient)) {
90 		dev_err(dev, "static-coefficient not available\n");
91 		ret = -EINVAL;
92 		goto err;
93 	}
94 	/* cpu power model node doesn't contain dynamic-coefficient */
95 	of_property_read_u32(model_node, "dynamic-coefficient",
96 			     &model_data->dynamic_coefficient);
97 	if (of_property_read_u32_array
98 	    (model_node, "ts", (u32 *)model_data->ts, 4)) {
99 		dev_err(dev, "ts in power_model not available\n");
100 		ret = -EINVAL;
101 		goto err;
102 	}
103 	rockchip_of_get_leakage(dev, lkg_name, &model_data->leakage);
104 	if (!of_property_read_u32(model_node, "ref-leakage",
105 				&model_data->ref_leakage))
106 		goto cal_static_coeff;
107 	if (of_property_read_u32_array(model_node, "leakage-range",
108 				       (u32 *)model_data->lkg_range, 2)) {
109 		dev_err(dev, "leakage-range isn't available\n");
110 		ret = -EINVAL;
111 		goto err;
112 	}
113 	if (of_property_read_u32_array(model_node, "ls",
114 				       (u32 *)model_data->ls, 3)) {
115 		dev_err(dev, "ls isn't available\n");
116 		ret = -EINVAL;
117 		goto err;
118 	}
119 cal_static_coeff:
120 	calculate_static_coefficient(model_data);
121 
122 	of_node_put(model_node);
123 
124 	return model_data;
125 err:
126 	of_node_put(model_node);
127 	kfree(model_data);
128 
129 	return ERR_PTR(ret);
130 }
131 EXPORT_SYMBOL(rockchip_ipa_power_model_init);
132 
133 /**
134  * calculate_temp_scaling_factor() - Calculate temperature scaling coefficient
135  * @ts:		Signed coefficients, in order t^0 to t^3, with units Deg^-N
136  * @t:		Temperature, in mDeg C. Range: -40000 < t < 125000
137  *
138  * Scale the temperature according to a cubic polynomial whose coefficients are
139  * provided in the device tree. The result is used to scale the static power
140  * coefficient, where 1000000 means no change.
141  *
142  * Return: Temperature scaling factor.
143  */
calculate_temp_scaling_factor(s32 ts[4],s64 t)144 static u32 calculate_temp_scaling_factor(s32 ts[4], s64 t)
145 {
146 	const s64 t2 = div_s64((t * t), 1000);
147 
148 	const s64 t3 = div_s64((t * t2), 1000);
149 
150 	/*
151 	 * Sum the parts. t^[1-3] are in m(Deg^N), but the coefficients are in
152 	 * Deg^-N, so we need to multiply the last coefficient by 1000.
153 	 */
154 	const s64 res_big = ts[3] * t3
155 			  + ts[2] * t2
156 			  + ts[1] * t
157 			  + ts[0] * 1000LL;
158 
159 	/* ts has beed multiplied by 10 in devicetree */
160 	s64 res_unclamped = div_s64(res_big, 10000);
161 
162 	/* Clamp to range of 0x to 10x the static power */
163 	return clamp(res_unclamped, (s64)0, (s64)10000000);
164 }
165 
166 /**
167  * calculate_volt_scaling_factor() - Calculate voltage scaling coefficient
168  * voltage_mv:	Voltage, in mV. Range: 750 < voltage < 1350
169  *
170  * Return: Voltage scaling factor.
171  */
calculate_volt_scaling_factor(const u32 voltage_mv)172 static u32 calculate_volt_scaling_factor(const u32 voltage_mv)
173 {
174 	const u32 v2 = (voltage_mv * voltage_mv) / 1000;
175 
176 	const u32 v3_big = v2 * voltage_mv;
177 
178 	const u32 v3 = v3_big / 1000;
179 
180 	const u32 v4_big = v3 * voltage_mv;
181 
182 	const u32 v4 = v4_big / 1000;
183 
184 	return v4;
185 }
186 
187 /**
188  * rockchip_ipa_get_static_power() - Calculate static power
189  * @data:	Pointer to IPA model
190  * voltage_mv:	Voltage, in mV. Range: 750 < voltage < 1350
191  *
192  * Return: Static power.
193  */
194 unsigned long
rockchip_ipa_get_static_power(struct ipa_power_model_data * data,unsigned long voltage_mv)195 rockchip_ipa_get_static_power(struct ipa_power_model_data *data,
196 			      unsigned long voltage_mv)
197 {
198 	u32 temp_scaling_factor, volt_scaling_factor, static_power;
199 	u64 power_big;
200 	int temp;
201 	int ret;
202 
203 	ret = data->tz->ops->get_temp(data->tz, &temp);
204 	if (ret) {
205 		pr_err("%s:failed to read %s temp\n",
206 		       __func__, data->tz->type);
207 		temp = FALLBACK_STATIC_TEMPERATURE;
208 	}
209 
210 	temp_scaling_factor = calculate_temp_scaling_factor(data->ts, temp);
211 	volt_scaling_factor = calculate_volt_scaling_factor((u32)voltage_mv);
212 
213 	power_big = (u64)data->static_coefficient * (u64)temp_scaling_factor;
214 	static_power = div_u64(power_big, 1000000);
215 	power_big = (u64)static_power * (u64)volt_scaling_factor;
216 	static_power = div_u64(power_big, 1000000);
217 
218 	trace_thermal_ipa_get_static_power(data->leakage,
219 					   data->static_coefficient,
220 					   temp,
221 					   temp_scaling_factor,
222 					   (u32)voltage_mv,
223 					   volt_scaling_factor,
224 					   static_power);
225 
226 	return static_power;
227 }
228 EXPORT_SYMBOL(rockchip_ipa_get_static_power);
229 
230 MODULE_DESCRIPTION("Rockchip IPA driver");
231 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
232 MODULE_LICENSE("GPL");
233