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