|
@@ -28,6 +28,8 @@
|
|
|
|
|
|
#include <trace/events/thermal.h>
|
|
|
|
|
|
+#define SCALE_ERROR_MITIGATION 100
|
|
|
+
|
|
|
static DEFINE_IDA(devfreq_ida);
|
|
|
|
|
|
/**
|
|
@@ -45,6 +47,12 @@ static DEFINE_IDA(devfreq_ida);
|
|
|
* @freq_table_size: Size of the @freq_table and @power_table
|
|
|
* @power_ops: Pointer to devfreq_cooling_power, used to generate the
|
|
|
* @power_table.
|
|
|
+ * @res_util: Resource utilization scaling factor for the power.
|
|
|
+ * It is multiplied by 100 to minimize the error. It is used
|
|
|
+ * for estimation of the power budget instead of using
|
|
|
+ * 'utilization' (which is 'busy_time / 'total_time').
|
|
|
+ * The 'res_util' range is from 100 to (power_table[state] * 100)
|
|
|
+ * for the corresponding 'state'.
|
|
|
*/
|
|
|
struct devfreq_cooling_device {
|
|
|
int id;
|
|
@@ -55,6 +63,8 @@ struct devfreq_cooling_device {
|
|
|
u32 *freq_table;
|
|
|
size_t freq_table_size;
|
|
|
struct devfreq_cooling_power *power_ops;
|
|
|
+ u32 res_util;
|
|
|
+ int capped_state;
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -250,6 +260,16 @@ get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq,
|
|
|
return power;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+static inline unsigned long get_total_power(struct devfreq_cooling_device *dfc,
|
|
|
+ unsigned long freq,
|
|
|
+ unsigned long voltage)
|
|
|
+{
|
|
|
+ return get_static_power(dfc, freq) + get_dynamic_power(dfc, freq,
|
|
|
+ voltage);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cdev,
|
|
|
struct thermal_zone_device *tz,
|
|
|
u32 *power)
|
|
@@ -259,27 +279,55 @@ static int devfreq_cooling_get_requested_power(struct thermal_cooling_device *cd
|
|
|
struct devfreq_dev_status *status = &df->last_status;
|
|
|
unsigned long state;
|
|
|
unsigned long freq = status->current_frequency;
|
|
|
- u32 dyn_power, static_power;
|
|
|
+ unsigned long voltage;
|
|
|
+ u32 dyn_power = 0;
|
|
|
+ u32 static_power = 0;
|
|
|
+ int res;
|
|
|
|
|
|
- /* Get dynamic power for state */
|
|
|
state = freq_get_state(dfc, freq);
|
|
|
- if (state == THERMAL_CSTATE_INVALID)
|
|
|
- return -EAGAIN;
|
|
|
+ if (state == THERMAL_CSTATE_INVALID) {
|
|
|
+ res = -EAGAIN;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
|
|
|
- dyn_power = dfc->power_table[state];
|
|
|
+ if (dfc->power_ops->get_real_power) {
|
|
|
+ voltage = get_voltage(df, freq);
|
|
|
+ if (voltage == 0) {
|
|
|
+ res = -EINVAL;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
|
|
|
- /* Scale dynamic power for utilization */
|
|
|
- dyn_power = (dyn_power * status->busy_time) / status->total_time;
|
|
|
+ res = dfc->power_ops->get_real_power(df, power, freq, voltage);
|
|
|
+ if (!res) {
|
|
|
+ state = dfc->capped_state;
|
|
|
+ dfc->res_util = dfc->power_table[state];
|
|
|
+ dfc->res_util *= SCALE_ERROR_MITIGATION;
|
|
|
|
|
|
- /* Get static power */
|
|
|
- static_power = get_static_power(dfc, freq);
|
|
|
+ if (*power > 1)
|
|
|
+ dfc->res_util /= *power;
|
|
|
+ } else {
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dyn_power = dfc->power_table[state];
|
|
|
+
|
|
|
+ /* Scale dynamic power for utilization */
|
|
|
+ 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, dyn_power,
|
|
|
static_power);
|
|
|
|
|
|
- *power = dyn_power + static_power;
|
|
|
-
|
|
|
return 0;
|
|
|
+fail:
|
|
|
+ /* It is safe to set max in this case */
|
|
|
+ dfc->res_util = SCALE_ERROR_MITIGATION;
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
|
|
@@ -312,26 +360,34 @@ static int devfreq_cooling_power2state(struct thermal_cooling_device *cdev,
|
|
|
unsigned long busy_time;
|
|
|
s32 dyn_power;
|
|
|
u32 static_power;
|
|
|
+ s32 est_power;
|
|
|
int i;
|
|
|
|
|
|
- static_power = get_static_power(dfc, freq);
|
|
|
+ if (dfc->power_ops->get_real_power) {
|
|
|
+ /* Scale for resource utilization */
|
|
|
+ est_power = power * dfc->res_util;
|
|
|
+ est_power /= SCALE_ERROR_MITIGATION;
|
|
|
+ } else {
|
|
|
+ static_power = get_static_power(dfc, freq);
|
|
|
|
|
|
- dyn_power = power - static_power;
|
|
|
- dyn_power = dyn_power > 0 ? dyn_power : 0;
|
|
|
+ dyn_power = power - static_power;
|
|
|
+ dyn_power = dyn_power > 0 ? dyn_power : 0;
|
|
|
|
|
|
- /* Scale dynamic power for utilization */
|
|
|
- busy_time = status->busy_time ?: 1;
|
|
|
- dyn_power = (dyn_power * status->total_time) / busy_time;
|
|
|
+ /* Scale dynamic power for utilization */
|
|
|
+ busy_time = status->busy_time ?: 1;
|
|
|
+ est_power = (dyn_power * status->total_time) / busy_time;
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* Find the first cooling state that is within the power
|
|
|
* budget for dynamic power.
|
|
|
*/
|
|
|
for (i = 0; i < dfc->freq_table_size - 1; i++)
|
|
|
- if (dyn_power >= dfc->power_table[i])
|
|
|
+ if (est_power >= dfc->power_table[i])
|
|
|
break;
|
|
|
|
|
|
*state = i;
|
|
|
+ dfc->capped_state = i;
|
|
|
trace_thermal_power_devfreq_limit(cdev, freq, *state, power);
|
|
|
return 0;
|
|
|
}
|
|
@@ -387,7 +443,7 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc)
|
|
|
}
|
|
|
|
|
|
for (i = 0, freq = ULONG_MAX; i < num_opps; i++, freq--) {
|
|
|
- unsigned long power_dyn, voltage;
|
|
|
+ unsigned long power, voltage;
|
|
|
struct dev_pm_opp *opp;
|
|
|
|
|
|
opp = dev_pm_opp_find_freq_floor(dev, &freq);
|
|
@@ -400,12 +456,15 @@ static int devfreq_cooling_gen_tables(struct devfreq_cooling_device *dfc)
|
|
|
dev_pm_opp_put(opp);
|
|
|
|
|
|
if (dfc->power_ops) {
|
|
|
- power_dyn = get_dynamic_power(dfc, freq, voltage);
|
|
|
+ if (dfc->power_ops->get_real_power)
|
|
|
+ power = get_total_power(dfc, freq, voltage);
|
|
|
+ else
|
|
|
+ power = get_dynamic_power(dfc, freq, voltage);
|
|
|
|
|
|
- dev_dbg(dev, "Dynamic power table: %lu MHz @ %lu mV: %lu = %lu mW\n",
|
|
|
- freq / 1000000, voltage, power_dyn, power_dyn);
|
|
|
+ dev_dbg(dev, "Power table: %lu MHz @ %lu mV: %lu = %lu mW\n",
|
|
|
+ freq / 1000000, voltage, power, power);
|
|
|
|
|
|
- power_table[i] = power_dyn;
|
|
|
+ power_table[i] = power;
|
|
|
}
|
|
|
|
|
|
freq_table[i] = freq;
|