struct _thermal_zone {
struct thermal_zone_device *tz;
bool single_cooling_dev;
+ int num_cooling;
struct list_head node;
struct list_head cooling_list;
struct trip_ctrl_alg trip_ctrl_alg;
struct _cooling_instance {
struct list_head node;
struct _cooling_dev *cooling;
+ u32 weight;
};
static LIST_HEAD(tz_list);
return total_min_power;
}
-static u32 calc_power_budget(struct _thermal_zone *zone, int control_temp)
+static u32 calc_power_budget(struct _thermal_zone *zone)
{
s64 temp_diff;
+ int desired_temp = zone->trip_ctrl_alg.desired_temp;
s64 power_budget;
struct thermal_zone_device *tz = zone->tz;
/* temperature is represented in milidegress */
- temp_diff = control_temp - tz->temperature;
+ temp_diff = desired_temp - tz->temperature;
power_budget = temp_diff;
return 0;
}
+static int cooling_dev_set_state(struct _thermal_zone *zone,
+ struct _cooling_dev *cooling,
+ unsigned long target)
+{
+ struct thermal_cooling_device *cdev = cooling->cdev;
+ unsigned long curr_state;
+
+ cdev->ops->get_cur_state(cdev, &curr_state);
+
+ if (curr_state == target)
+ return 0;
+
+ /* check if we can it go with higher freq for zone with a few devices*/
+ if (!zone->single_cooling_dev && cooling->max_single_state > target)
+ return -EINVAL;
+
+ if (zone->single_cooling_dev && cooling->max_single_state > target)
+ cooling->max_single_state = target;
+
+ mutex_lock(&cdev->lock);
+ if (!cdev->ops->set_cur_state(cdev, target))
+ thermal_cooling_device_stats_update(cdev, target);
+
+ cdev->updated = true;
+ mutex_unlock(&cdev->lock);
+
+ return 0;
+}
+
+static int set_power(struct _cooling_instance *inst, struct _thermal_zone *zone,
+ u32 power)
+{
+ int ret;
+ unsigned long state;
+ struct thermal_cooling_device *cdev;
+ struct thermal_zone_device *tz = zone->tz;
+
+ cdev = inst->cooling->cdev;
+ ret = cdev->ops->power2state(cdev, tz, power, &state);
+
+ pr_info("set_power=%u, state=%lu, temp=%d\n", power, state,
+ tz->temperature);
+ if (!ret) {
+ ret = cooling_dev_set_state(zone, inst->cooling, state);
+ }
+
+ return ret;
+}
+
+static int get_requested_power(struct _cooling_instance *inst,
+ struct _thermal_zone *zone, u32 *power)
+{
+ struct thermal_cooling_device *cdev;
+ struct thermal_zone_device *tz = zone->tz;
+
+ cdev = inst->cooling->cdev;
+ return cdev->ops->get_requested_power(cdev, tz, power);
+}
+
+static int share_power_budget(struct _thermal_zone *zone, u32 power_budget)
+{
+ struct _cooling_instance *inst;
+ u32 *power;
+ u64 sum_power = 0;
+ int i = 0;
+
+ power = kzalloc(sizeof(u32) * zone->num_cooling, GFP_KERNEL);
+ if (!power)
+ return -ENOMEM;
+
+ /* estimate cooling dev's power and total power */
+ list_for_each_entry(inst, &zone->cooling_list, node) {
+ get_requested_power(inst, zone, &power[i]);
+
+ power[i] = (inst->weight * power[i]) >> 10;
+ sum_power += power[i];
+ i++;
+ }
+
+ if (sum_power <= 0)
+ goto cleanup;
+
+ /* split power budget to cooling devices */
+ i = 0;
+ list_for_each_entry(inst, &zone->cooling_list, node) {
+ power[i] = (power[i] * power_budget) / sum_power;
+ i++;
+ }
+
+ /* set the new state for cooling device based on its granted power */
+ i = 0;
+ list_for_each_entry(inst, &zone->cooling_list, node) {
+ set_power(inst, zone, power[i]);
+ i++;
+ }
+
+cleanup:
+ kfree(power);
+
+ return 0;
+}
static int sched_power_gov_throttle(struct thermal_zone_device *tz, int trip)
{
- struct thermal_cooling_device *cdev = NULL;
- struct thermal_instance *inst = NULL;
- u32 dev_power;
u32 power_budget;
- int control_temp = 0;
struct _thermal_zone *zone;
int ret;
if (!zone)
return -EINVAL;
+ if (zone->trip_ctrl_alg.desired_id < 0)
+ return -EINVAL;
- if (zone->single_cooling_dev) {
- power_budget = calc_power_budget(zone, control_temp);
- /* only deadline tasks can ask for over-speed with idlers */
- ret = inject_more_idle(zone, power_budget);
- if (ret) {
- throttle_single_cdev(zone);
- power_actor_set_power(cdev, inst, dev_power);
- }
- } else {
+ /* skip calls from other trip points */
+ if (trip != zone->trip_ctrl_alg.desired_id)
+ return 0;
- }
+ power_budget = calc_power_budget(zone);
+ ret = share_power_budget(zone, power_budget);
return 0;
}
goto cleanup;
_inst->cooling = cooling;
+ _inst->weight = DEFAULT_WEIGHT;
list_add(&_inst->node, &zone->cooling_list);
+ zone->num_cooling++;
pr_info("pinned cooling device into zone\n");
}
ret = sched_power_setup_trips(zone);
if (ret < 0) {
- pr_info("lack of temp settings in DT allowing to control \
- the algorithm\n");
+ pr_warn("lack of temp settings needed in control algorithm\n");
}
mutex_lock(&tz_list_lock);
list_add(&zone->node, &tz_list);
mutex_unlock(&tz_list_lock);
- return 0;
+ return ret;
cleanup:
if (prev_cooling) {