aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukasz Luba <lukasz.luba@arm.com>2017-05-04 07:34:32 -0400
committerZhang Rui <rui.zhang@intel.com>2017-05-05 03:54:45 -0400
commit2be83da85a64773efaa407639de81bd1377f880e (patch)
tree71ee9c4b2a318f11345c3770a2dd3900c14bf2b0
parente34cab4cd1f98b4daed5bc98fe727e63f8dbf4e4 (diff)
thermal: devfreq_cooling: add new interface for direct power read
This patch introduces a new interface for device drivers connected to devfreq_cooling in the thermal framework: get_real_power(). Some devices have more sophisticated methods (like power counters) to approximate the actual power that they use. In the previous implementation we had a pre-calculated power table which was then scaled by 'utilization' ('busy_time' and 'total_time' taken from devfreq 'last_status'). With this new interface the driver can provide more precise data regarding actual power to the thermal governor every time the power budget is calculated. We then use this value and calculate the real resource utilization scaling factor. Reviewed-by: Chris Diamand <chris.diamand@arm.com> Acked-by: Javi Merino <javi.merino@kernel.org> Signed-off-by: Lukasz Luba <lukasz.luba@arm.com>
-rw-r--r--drivers/thermal/devfreq_cooling.c105
-rw-r--r--include/linux/devfreq_cooling.h19
2 files changed, 101 insertions, 23 deletions
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c
index af9d32837a3a..26c31571a12c 100644
--- a/drivers/thermal/devfreq_cooling.c
+++ b/drivers/thermal/devfreq_cooling.c
@@ -28,6 +28,8 @@
28 28
29#include <trace/events/thermal.h> 29#include <trace/events/thermal.h>
30 30
31#define SCALE_ERROR_MITIGATION 100
32
31static DEFINE_IDA(devfreq_ida); 33static DEFINE_IDA(devfreq_ida);
32 34
33/** 35/**
@@ -45,6 +47,12 @@ static DEFINE_IDA(devfreq_ida);
45 * @freq_table_size: Size of the @freq_table and @power_table 47 * @freq_table_size: Size of the @freq_table and @power_table
46 * @power_ops: Pointer to devfreq_cooling_power, used to generate the 48 * @power_ops: Pointer to devfreq_cooling_power, used to generate the
47 * @power_table. 49 * @power_table.
50 * @res_util: Resource utilization scaling factor for the power.
51 * It is multiplied by 100 to minimize the error. It is used
52 * for estimation of the power budget instead of using
53 * 'utilization' (which is 'busy_time / 'total_time').
54 * The 'res_util' range is from 100 to (power_table[state] * 100)
55 * for the corresponding 'state'.
48 */ 56 */
49struct devfreq_cooling_device { 57struct devfreq_cooling_device {
50 int id; 58 int id;
@@ -55,6 +63,8 @@ struct devfreq_cooling_device {
55 u32 *freq_table; 63 u32 *freq_table;
56 size_t freq_table_size; 64 size_t freq_table_size;
57 struct devfreq_cooling_power *power_ops; 65 struct devfreq_cooling_power *power_ops;
66 u32 res_util;
67 int capped_state;
58}; 68};
59 69
60/** 70/**
@@ -250,6 +260,16 @@ get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq,
250 return power; 260 return power;
251} 261}
252 262
263
264static inline unsigned long get_total_power(struct devfreq_cooling_device *dfc,
265 unsigned long freq,
266 unsigned long voltage)
267{
268 return get_static_power(dfc, freq) + get_dynamic_power(dfc, freq,
269 voltage);
270}
271
272
253static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev, 273static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev,
254 struct thermal_zone_device *tz, 274 struct thermal_zone_device *tz,
255 u32 *power) 275 u32 *power)
@@ -259,27 +279,55 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
259 struct devfreq_dev_status *status = &df->last_status; 279 struct devfreq_dev_status *status = &df->last_status;
260 unsigned long state; 280 unsigned long state;
261 unsigned long freq = status->current_frequency; 281 unsigned long freq = status->current_frequency;
262 u32 dyn_power, static_power; 282 unsigned long voltage;
283 u32 dyn_power = 0;
284 u32 static_power = 0;
285 int res;
263 286
264 /* Get dynamic power for state */
265 state = freq_get_state(dfc, freq); 287 state = freq_get_state(dfc, freq);
266 if (state == THERMAL_CSTATE_INVALID) 288 if (state == THERMAL_CSTATE_INVALID) {
267 return -EAGAIN; 289 res = -EAGAIN;
290 goto fail;
291 }
268 292
269 dyn_power = dfc->power_table[state]; 293 if (dfc->power_ops->get_real_power) {
294 voltage = get_voltage(df, freq);
295 if (voltage == 0) {
296 res = -EINVAL;
297 goto fail;
298 }
270 299
271 /* Scale dynamic power for utilization */ 300 res = dfc->power_ops->get_real_power(df, power, freq, voltage);
272 dyn_power = (dyn_power * status->busy_time) / status->total_time; 301 if (!res) {
302 state = dfc->capped_state;
303 dfc->res_util = dfc->power_table[state];
304 dfc->res_util *= SCALE_ERROR_MITIGATION;
273 305
274 /* Get static power */ 306 if (*power > 1)
275 static_power = get_static_power(dfc, freq); 307 dfc->res_util /= *power;
308 } else {
309 goto fail;
310 }
311 } else {
312 dyn_power = dfc->power_table[state];
313
314 /* Scale dynamic power for utilization */
315 dyn_power *= status->busy_time;
316 dyn_power /= status->total_time;
317 /* Get static power */
318 static_power = get_static_power(dfc, freq);
319
320 *power = dyn_power + static_power;
321 }
276 322
277 trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power, 323 trace_thermal_power_devfreq_get_power(cdev, status, freq, dyn_power,
278 static_power); 324 static_power);
279 325
280 *power = dyn_power + static_power;
281
282 return 0; 326 return 0;
327fail:
328 /* It is safe to set max in this case */
329 dfc->res_util = SCALE_ERROR_MITIGATION;
330 return res;
283} 331}
284 332
285static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev, 333static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
@@ -312,26 +360,34 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev,
312 unsigned long busy_time; 360 unsigned long busy_time;
313 s32 dyn_power; 361 s32 dyn_power;
314 u32 static_power; 362 u32 static_power;
363 s32 est_power;
315 int i; 364 int i;
316 365
317 static_power = get_static_power(dfc, freq); 366 if (dfc->power_ops->get_real_power) {
367 /* Scale for resource utilization */
368 est_power = power * dfc->res_util;
369 est_power /= SCALE_ERROR_MITIGATION;
370 } else {
371 static_power = get_static_power(dfc, freq);
318 372
319 dyn_power = power - static_power; 373 dyn_power = power - static_power;
320 dyn_power = dyn_power > 0 ? dyn_power : 0; 374 dyn_power = dyn_power > 0 ? dyn_power : 0;
321 375
322 /* Scale dynamic power for utilization */ 376 /* Scale dynamic power for utilization */
323 busy_time = status->busy_time ?: 1; 377 busy_time = status->busy_time ?: 1;
324 dyn_power = (dyn_power * status->total_time) / busy_time; 378 est_power = (dyn_power * status->total_time) / busy_time;
379 }
325 380
326 /* 381 /*
327 * Find the first cooling state that is within the power 382 * Find the first cooling state that is within the power
328 * budget for dynamic power. 383 * budget for dynamic power.
329 */ 384 */
330 for (i = 0; i < dfc->freq_table_size - 1; i++) 385 for (i = 0; i < dfc->freq_table_size - 1; i++)
331 if (dyn_power >= dfc->power_table[i]) 386 if (est_power >= dfc->power_table[i])
332 break; 387 break;
333 388
334 *state = i; 389 *state = i;
390 dfc->capped_state = i;
335 trace_thermal_power_devfreq_limit(cdev, freq, *state, power); 391 trace_thermal_power_devfreq_limit(cdev, freq, *state, power);
336 return 0; 392 return 0;
337} 393}
@@ -387,7 +443,7 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc)
387 } 443 }
388 444
389 for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) { 445 for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) {
390 unsigned long power_dyn, voltage; 446 unsigned long power, voltage;
391 struct dev_pm_opp *opp; 447 struct dev_pm_opp *opp;
392 448
393 opp = dev_pm_opp_find_freq_floor(dev, &freq); 449 opp = dev_pm_opp_find_freq_floor(dev, &freq);
@@ -400,12 +456,15 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc)
400 dev_pm_opp_put(opp); 456 dev_pm_opp_put(opp);
401 457
402 if (dfc->power_ops) { 458 if (dfc->power_ops) {
403 power_dyn = get_dynamic_power(dfc, freq, voltage); 459 if (dfc->power_ops->get_real_power)
460 power = get_total_power(dfc, freq, voltage);
461 else
462 power = get_dynamic_power(dfc, freq, voltage);
404 463
405 dev_dbg(dev, "Dynamic power table: %lu MHz @ %lu mV: %lu = %lu mW\n", 464 dev_dbg(dev, "Power table: %lu MHz @ %lu mV: %lu = %lu mW\n",
406 freq / 1000000, voltage, power_dyn, power_dyn); 465 freq / 1000000, voltage, power, power);
407 466
408 power_table[i] = power_dyn; 467 power_table[i] = power;
409 } 468 }
410 469
411 freq_table[i] = freq; 470 freq_table[i] = freq;
diff --git a/include/linux/devfreq_cooling.h b/include/linux/devfreq_cooling.h
index c35d0c0e0ada..4635f95000a4 100644
--- a/include/linux/devfreq_cooling.h
+++ b/include/linux/devfreq_cooling.h
@@ -34,6 +34,23 @@
34 * If get_dynamic_power() is NULL, then the 34 * If get_dynamic_power() is NULL, then the
35 * dynamic power is calculated as 35 * dynamic power is calculated as
36 * @dyn_power_coeff * frequency * voltage^2 36 * @dyn_power_coeff * frequency * voltage^2
37 * @get_real_power: When this is set, the framework uses it to ask the
38 * device driver for the actual power.
39 * Some devices have more sophisticated methods
40 * (like power counters) to approximate the actual power
41 * that they use.
42 * This function provides more accurate data to the
43 * thermal governor. When the driver does not provide
44 * such function, framework just uses pre-calculated
45 * table and scale the power by 'utilization'
46 * (based on 'busy_time' and 'total_time' taken from
47 * devfreq 'last_status').
48 * The value returned by this function must be lower
49 * or equal than the maximum power value
50 * for the current state
51 * (which can be found in power_table[state]).
52 * When this interface is used, the power_table holds
53 * max total (static + dynamic) power value for each OPP.
37 */ 54 */
38struct devfreq_cooling_power { 55struct devfreq_cooling_power {
39 unsigned long (*get_static_power)(struct devfreq *devfreq, 56 unsigned long (*get_static_power)(struct devfreq *devfreq,
@@ -41,6 +58,8 @@ struct devfreq_cooling_power {
41 unsigned long (*get_dynamic_power)(struct devfreq *devfreq, 58 unsigned long (*get_dynamic_power)(struct devfreq *devfreq,
42 unsigned long freq, 59 unsigned long freq,
43 unsigned long voltage); 60 unsigned long voltage);
61 int (*get_real_power)(struct devfreq *df, u32 *power,
62 unsigned long freq, unsigned long voltage);
44 unsigned long dyn_power_coeff; 63 unsigned long dyn_power_coeff;
45}; 64};
46 65