summaryrefslogtreecommitdiffstats
path: root/drivers/thermal/gov_power_allocator.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/gov_power_allocator.c')
-rw-r--r--drivers/thermal/gov_power_allocator.c116
1 files changed, 87 insertions, 29 deletions
diff --git a/drivers/thermal/gov_power_allocator.c b/drivers/thermal/gov_power_allocator.c
index ab0be26f0816..7a4170a0b51f 100644
--- a/drivers/thermal/gov_power_allocator.c
+++ b/drivers/thermal/gov_power_allocator.c
@@ -63,6 +63,8 @@ static inline s64 div_frac(s64 x, s64 y)
* @trip_max_desired_temperature: last passive trip point of the thermal
* zone. The temperature we are
* controlling for.
+ * @sustainable_power: Sustainable power (heat) that this thermal zone can
+ * dissipate
*/
struct power_allocator_params {
bool allocated_tzp;
@@ -70,6 +72,7 @@ struct power_allocator_params {
s32 prev_err;
int trip_switch_on;
int trip_max_desired_temperature;
+ u32 sustainable_power;
};
/**
@@ -96,7 +99,10 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
if (instance->trip != params->trip_max_desired_temperature)
continue;
- if (power_actor_get_min_power(cdev, &min_power))
+ if (!cdev_is_power_actor(cdev))
+ continue;
+
+ if (cdev->ops->state2power(cdev, instance->upper, &min_power))
continue;
sustainable_power += min_power;
@@ -111,26 +117,18 @@ static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
* @sustainable_power: sustainable power for the thermal zone
* @trip_switch_on: trip point number for the switch on temperature
* @control_temp: target temperature for the power allocator governor
- * @force: whether to force the update of the constants
*
* This function is used to update the estimation of the PID
* controller constants in struct thermal_zone_parameters.
- * Sustainable power is provided in case it was estimated. The
- * estimated sustainable_power should not be stored in the
- * thermal_zone_parameters so it has to be passed explicitly to this
- * function.
- *
- * If @force is not set, the values in the thermal zone's parameters
- * are preserved if they are not zero. If @force is set, the values
- * in thermal zone's parameters are overwritten.
*/
static void estimate_pid_constants(struct thermal_zone_device *tz,
u32 sustainable_power, int trip_switch_on,
- int control_temp, bool force)
+ int control_temp)
{
int ret;
int switch_on_temp;
u32 temperature_threshold;
+ s32 k_i;
ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
if (ret)
@@ -148,16 +146,15 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
if (!temperature_threshold)
return;
- if (!tz->tzp->k_po || force)
- tz->tzp->k_po = int_to_frac(sustainable_power) /
- temperature_threshold;
+ tz->tzp->k_po = int_to_frac(sustainable_power) /
+ temperature_threshold;
+
+ tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
+ temperature_threshold;
- if (!tz->tzp->k_pu || force)
- tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
- temperature_threshold;
+ k_i = tz->tzp->k_pu / 10;
+ tz->tzp->k_i = k_i > 0 ? k_i : 1;
- if (!tz->tzp->k_i || force)
- tz->tzp->k_i = int_to_frac(10) / 1000;
/*
* The default for k_d and integral_cutoff is 0, so we can
* leave them as they are.
@@ -165,6 +162,41 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
}
/**
+ * get_sustainable_power() - Get the right sustainable power
+ * @tz: thermal zone for which to estimate the constants
+ * @params: parameters for the power allocator governor
+ * @control_temp: target temperature for the power allocator governor
+ *
+ * This function is used for getting the proper sustainable power value based
+ * on variables which might be updated by the user sysfs interface. If that
+ * happen the new value is going to be estimated and updated. It is also used
+ * after thermal zone binding, where the initial values where set to 0.
+ */
+static u32 get_sustainable_power(struct thermal_zone_device *tz,
+ struct power_allocator_params *params,
+ int control_temp)
+{
+ u32 sustainable_power;
+
+ if (!tz->tzp->sustainable_power)
+ sustainable_power = estimate_sustainable_power(tz);
+ else
+ sustainable_power = tz->tzp->sustainable_power;
+
+ /* Check if it's init value 0 or there was update via sysfs */
+ if (sustainable_power != params->sustainable_power) {
+ estimate_pid_constants(tz, sustainable_power,
+ params->trip_switch_on, control_temp);
+
+ /* Do the estimation only once and make available in sysfs */
+ tz->tzp->sustainable_power = sustainable_power;
+ params->sustainable_power = sustainable_power;
+ }
+
+ return sustainable_power;
+}
+
+/**
* pid_controller() - PID controller
* @tz: thermal zone we are operating in
* @control_temp: the target temperature in millicelsius
@@ -193,14 +225,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
max_power_frac = int_to_frac(max_allocatable_power);
- if (tz->tzp->sustainable_power) {
- sustainable_power = tz->tzp->sustainable_power;
- } else {
- sustainable_power = estimate_sustainable_power(tz);
- estimate_pid_constants(tz, sustainable_power,
- params->trip_switch_on, control_temp,
- true);
- }
+ sustainable_power = get_sustainable_power(tz, params, control_temp);
err = control_temp - tz->temperature;
err = int_to_frac(err);
@@ -252,6 +277,38 @@ static u32 pid_controller(struct thermal_zone_device *tz,
}
/**
+ * power_actor_set_power() - limit the maximum power a cooling device consumes
+ * @cdev: pointer to &thermal_cooling_device
+ * @instance: thermal instance to update
+ * @power: the power in milliwatts
+ *
+ * Set the cooling device to consume at most @power milliwatts. The limit is
+ * expected to be a cap at the maximum power consumption.
+ *
+ * Return: 0 on success, -EINVAL if the cooling device does not
+ * implement the power actor API or -E* for other failures.
+ */
+static int
+power_actor_set_power(struct thermal_cooling_device *cdev,
+ struct thermal_instance *instance, u32 power)
+{
+ unsigned long state;
+ int ret;
+
+ ret = cdev->ops->power2state(cdev, power, &state);
+ if (ret)
+ return ret;
+
+ instance->target = clamp_val(state, instance->lower, instance->upper);
+ mutex_lock(&cdev->lock);
+ cdev->updated = false;
+ mutex_unlock(&cdev->lock);
+ thermal_cdev_update(cdev);
+
+ return 0;
+}
+
+/**
* divvy_up_power() - divvy the allocated power between the actors
* @req_power: each actor's requested power
* @max_power: each actor's maximum available power
@@ -398,7 +455,8 @@ static int allocate_power(struct thermal_zone_device *tz,
weighted_req_power[i] = frac_to_int(weight * req_power[i]);
- if (power_actor_get_max_power(cdev, &max_power[i]))
+ if (cdev->ops->state2power(cdev, instance->lower,
+ &max_power[i]))
continue;
total_req_power += req_power[i];
@@ -572,7 +630,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
if (!ret)
estimate_pid_constants(tz, tz->tzp->sustainable_power,
params->trip_switch_on,
- control_temp, false);
+ control_temp);
}
reset_pid_controller(params);