diff options
Diffstat (limited to 'drivers/thermal/gov_power_allocator.c')
-rw-r--r-- | drivers/thermal/gov_power_allocator.c | 116 |
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); |