Devfreq cooling needs to now the correct status of the device in order to operate. Do not rely on Devfreq last_status which might be a stale data and get more up-to-date values of load and frequency. In addition this patch adds normalization function, which also makes sure that whatever data comes from the device, it is in a sane range. Signed-off-by: Lukasz Luba <lukasz.luba@xxxxxxx> --- drivers/thermal/devfreq_cooling.c | 38 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 52694d4bd819..396f16bb6566 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -235,6 +235,24 @@ static inline unsigned long get_total_power(struct devfreq_cooling_device *dfc, voltage); } +static void _normalize_load(struct devfreq_dev_status *status) +{ + /* Make some space if needed */ + if (status->busy_time > 0xffff) { + status->busy_time >>= 10; + status->total_time >>= 10; + } + + if (status->busy_time > status->total_time) + status->busy_time = status->total_time; + + status->busy_time *= 100; + status->busy_time /= status->total_time ? : 1; + + /* Avoid division by 0 */ + status->busy_time = status->busy_time ? : 1; + status->total_time = 100; +} static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev, struct thermal_zone_device *tz, @@ -242,14 +260,22 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd { struct devfreq_cooling_device *dfc = cdev->devdata; struct devfreq *df = dfc->devfreq; - struct devfreq_dev_status *status = &df->last_status; + struct devfreq_dev_status status; unsigned long state; - unsigned long freq = status->current_frequency; + unsigned long freq; unsigned long voltage; u32 dyn_power = 0; u32 static_power = 0; int res; + mutex_lock(&df->lock); + res = df->profile->get_dev_status(df->dev.parent, &status); + mutex_unlock(&df->lock); + if (res) + return res; + + freq = status.current_frequency; + state = freq_get_state(dfc, freq); if (state == THERMAL_CSTATE_INVALID) { res = -EAGAIN; @@ -277,16 +303,18 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd } else { dyn_power = dfc->power_table[state]; + _normalize_load(&status); + /* Scale dynamic power for utilization */ - dyn_power *= status->busy_time; - dyn_power /= status->total_time; + dyn_power *= status.busy_time; + dyn_power /= status.total_time; /* Get static power */ static_power = get_static_power(dfc, freq); *power = dyn_power + static_power; } - trace_thermal_power_devfreq_get_power(cdev, status, freq, *power); + trace_thermal_power_devfreq_get_power(cdev, &status, freq, *power); return 0; fail: -- 2.17.1