diff options
Diffstat (limited to 'drivers/thermal/thermal_core.c')
-rw-r--r-- | drivers/thermal/thermal_core.c | 223 |
1 files changed, 93 insertions, 130 deletions
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e6669aeda1ff..4187f207bce9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -323,11 +323,15 @@ static void thermal_zone_broken_disable(struct thermal_zone_device *tz) static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, unsigned long delay) { - if (delay) - mod_delayed_work(system_freezable_power_efficient_wq, - &tz->poll_queue, delay); - else + if (!delay) { cancel_delayed_work(&tz->poll_queue); + return; + } + + if (delay > HZ) + delay = round_jiffies_relative(delay); + + mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay); } static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) @@ -547,6 +551,7 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, struct thermal_trip_desc *td; LIST_HEAD(way_down_list); LIST_HEAD(way_up_list); + int low = -INT_MAX, high = INT_MAX; int temp, ret; if (tz->suspended) @@ -580,10 +585,17 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, tz->notify_event = event; - for_each_trip_desc(tz, td) + for_each_trip_desc(tz, td) { handle_thermal_trip(tz, td, &way_up_list, &way_down_list); - thermal_zone_set_trips(tz); + if (td->threshold <= tz->temperature && td->threshold > low) + low = td->threshold; + + if (td->threshold >= tz->temperature && td->threshold < high) + high = td->threshold; + } + + thermal_zone_set_trips(tz, low, high); list_sort(NULL, &way_up_list, thermal_trip_notify_cmp); list_for_each_entry(td, &way_up_list, notify_list_node) @@ -757,15 +769,7 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * @tz: pointer to struct thermal_zone_device * @trip: trip point the cooling devices is associated with in this zone. * @cdev: pointer to struct thermal_cooling_device - * @upper: the Maximum cooling state for this trip point. - * THERMAL_NO_LIMIT means no upper limit, - * and the cooling device can be in max_state. - * @lower: the Minimum cooling state can be used for this trip point. - * THERMAL_NO_LIMIT means no lower limit, - * and the cooling device can be in cooling state 0. - * @weight: The weight of the cooling device to be bound to the - * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the - * default value + * @cool_spec: cooling specification for @trip and @cdev * * This interface function bind a thermal cooling device to the certain trip * point of a thermal zone device. @@ -773,55 +777,41 @@ struct thermal_zone_device *thermal_zone_get_by_id(int id) * * Return: 0 on success, the proper error value otherwise. */ -int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, +static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_cooling_device *cdev, - unsigned long upper, unsigned long lower, - unsigned int weight) + struct cooling_spec *cool_spec) { struct thermal_instance *dev; struct thermal_instance *pos; - struct thermal_zone_device *pos1; - struct thermal_cooling_device *pos2; bool upper_no_limit; int result; - list_for_each_entry(pos1, &thermal_tz_list, node) { - if (pos1 == tz) - break; - } - list_for_each_entry(pos2, &thermal_cdev_list, node) { - if (pos2 == cdev) - break; - } - - if (tz != pos1 || cdev != pos2) - return -EINVAL; - /* lower default 0, upper default max_state */ - lower = lower == THERMAL_NO_LIMIT ? 0 : lower; + if (cool_spec->lower == THERMAL_NO_LIMIT) + cool_spec->lower = 0; - if (upper == THERMAL_NO_LIMIT) { - upper = cdev->max_state; + if (cool_spec->upper == THERMAL_NO_LIMIT) { + cool_spec->upper = cdev->max_state; upper_no_limit = true; } else { upper_no_limit = false; } - if (lower > upper || upper > cdev->max_state) + if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state) return -EINVAL; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - dev->tz = tz; + dev->cdev = cdev; dev->trip = trip; - dev->upper = upper; + dev->upper = cool_spec->upper; dev->upper_no_limit = upper_no_limit; - dev->lower = lower; + dev->lower = cool_spec->lower; dev->target = THERMAL_NO_TARGET; - dev->weight = weight; + dev->weight = cool_spec->weight; result = ida_alloc(&tz->ida, GFP_KERNEL); if (result < 0) @@ -855,10 +845,9 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, if (result) goto remove_trip_file; - mutex_lock(&tz->lock); mutex_lock(&cdev->lock); list_for_each_entry(pos, &tz->thermal_instances, tz_node) - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + if (pos->trip == trip && pos->cdev == cdev) { result = -EEXIST; break; } @@ -870,7 +859,6 @@ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV); } mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); if (!result) return 0; @@ -886,21 +874,6 @@ free_mem: kfree(dev); return result; } -EXPORT_SYMBOL_GPL(thermal_bind_cdev_to_trip); - -int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, - int trip_index, - struct thermal_cooling_device *cdev, - unsigned long upper, unsigned long lower, - unsigned int weight) -{ - if (trip_index < 0 || trip_index >= tz->num_trips) - return -EINVAL; - - return thermal_bind_cdev_to_trip(tz, &tz->trips[trip_index].trip, cdev, - upper, lower, weight); -} -EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); /** * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. @@ -911,33 +884,30 @@ EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); * This interface function unbind a thermal cooling device from the certain * trip point of a thermal zone device. * This function is usually called in the thermal zone device .unbind callback. - * - * Return: 0 on success, the proper error value otherwise. */ -int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, - const struct thermal_trip *trip, - struct thermal_cooling_device *cdev) +static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev) { struct thermal_instance *pos, *next; - mutex_lock(&tz->lock); + lockdep_assert_held(&tz->lock); + mutex_lock(&cdev->lock); list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + if (pos->trip == trip && pos->cdev == cdev) { list_del(&pos->tz_node); list_del(&pos->cdev_node); thermal_governor_update_tz(tz, THERMAL_TZ_UNBIND_CDEV); mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); goto unbind; } } mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); - return -ENODEV; + return; unbind: device_remove_file(&tz->device, &pos->weight_attr); @@ -945,20 +915,7 @@ unbind: sysfs_remove_link(&tz->device.kobj, pos->name); ida_free(&tz->ida, pos->id); kfree(pos); - return 0; -} -EXPORT_SYMBOL_GPL(thermal_unbind_cdev_from_trip); - -int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, - int trip_index, - struct thermal_cooling_device *cdev) -{ - if (trip_index < 0 || trip_index >= tz->num_trips) - return -EINVAL; - - return thermal_unbind_cdev_from_trip(tz, &tz->trips[trip_index].trip, cdev); } -EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); static void thermal_release(struct device *dev) { @@ -985,24 +942,41 @@ static struct class *thermal_class; static inline void print_bind_err_msg(struct thermal_zone_device *tz, + const struct thermal_trip *trip, struct thermal_cooling_device *cdev, int ret) { - dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", - tz->type, cdev->type, ret); + dev_err(&tz->device, "binding cdev %s to trip %d failed: %d\n", + cdev->type, thermal_zone_trip_id(tz, trip), ret); } -static void bind_cdev(struct thermal_cooling_device *cdev) +static void thermal_zone_cdev_bind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) { - int ret; - struct thermal_zone_device *pos = NULL; + struct thermal_trip_desc *td; - list_for_each_entry(pos, &thermal_tz_list, node) { - if (pos->ops.bind) { - ret = pos->ops.bind(pos, cdev); - if (ret) - print_bind_err_msg(pos, cdev, ret); - } + if (!tz->ops.should_bind) + return; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) { + struct thermal_trip *trip = &td->trip; + struct cooling_spec c = { + .upper = THERMAL_NO_LIMIT, + .lower = THERMAL_NO_LIMIT, + .weight = THERMAL_WEIGHT_DEFAULT + }; + int ret; + + if (!tz->ops.should_bind(tz, trip, cdev, &c)) + continue; + + ret = thermal_bind_cdev_to_trip(tz, trip, cdev, &c); + if (ret) + print_bind_err_msg(tz, trip, cdev, ret); } + + mutex_unlock(&tz->lock); } /** @@ -1100,7 +1074,8 @@ __thermal_cooling_device_register(struct device_node *np, list_add(&cdev->node, &thermal_cdev_list); /* Update binding information for 'this' new cdev */ - bind_cdev(cdev); + list_for_each_entry(pos, &thermal_tz_list, node) + thermal_zone_cdev_bind(pos, cdev); list_for_each_entry(pos, &thermal_tz_list, node) if (atomic_cmpxchg(&pos->need_update, 1, 0)) @@ -1301,6 +1276,19 @@ unlock_list: } EXPORT_SYMBOL_GPL(thermal_cooling_device_update); +static void thermal_zone_cdev_unbind(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev) +{ + struct thermal_trip_desc *td; + + mutex_lock(&tz->lock); + + for_each_trip_desc(tz, td) + thermal_unbind_cdev_from_trip(tz, &td->trip, cdev); + + mutex_unlock(&tz->lock); +} + /** * thermal_cooling_device_unregister - removes a thermal cooling device * @cdev: the thermal cooling device to remove. @@ -1327,10 +1315,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) list_del(&cdev->node); /* Unbind all thermal zones associated with 'this' cdev */ - list_for_each_entry(tz, &thermal_tz_list, node) { - if (tz->ops.unbind) - tz->ops.unbind(tz, cdev); - } + list_for_each_entry(tz, &thermal_tz_list, node) + thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&thermal_list_lock); @@ -1338,32 +1324,6 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); -static void bind_tz(struct thermal_zone_device *tz) -{ - int ret; - struct thermal_cooling_device *pos = NULL; - - if (!tz->ops.bind) - return; - - mutex_lock(&thermal_list_lock); - - list_for_each_entry(pos, &thermal_cdev_list, node) { - ret = tz->ops.bind(tz, pos); - if (ret) - print_bind_err_msg(tz, pos, ret); - } - - mutex_unlock(&thermal_list_lock); -} - -static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) -{ - *delay_jiffies = msecs_to_jiffies(delay_ms); - if (delay_ms > 1000) - *delay_jiffies = round_jiffies(*delay_jiffies); -} - int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) { const struct thermal_trip_desc *td; @@ -1424,6 +1384,7 @@ thermal_zone_device_register_with_trips(const char *type, unsigned int polling_delay) { const struct thermal_trip *trip = trips; + struct thermal_cooling_device *cdev; struct thermal_zone_device *tz; struct thermal_trip_desc *td; int id; @@ -1447,7 +1408,7 @@ thermal_zone_device_register_with_trips(const char *type, } if (!ops || !ops->get_temp) { - pr_err("Thermal zone device ops not defined\n"); + pr_err("Thermal zone device ops not defined or invalid\n"); return ERR_PTR(-EINVAL); } @@ -1509,8 +1470,8 @@ thermal_zone_device_register_with_trips(const char *type, td->threshold = INT_MAX; } - thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); - thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); + tz->polling_delay_jiffies = msecs_to_jiffies(polling_delay); + tz->passive_delay_jiffies = msecs_to_jiffies(passive_delay); tz->recheck_delay_jiffies = THERMAL_RECHECK_DELAY; /* sys I/F */ @@ -1554,13 +1515,16 @@ thermal_zone_device_register_with_trips(const char *type, } mutex_lock(&thermal_list_lock); + mutex_lock(&tz->lock); list_add_tail(&tz->node, &thermal_tz_list); mutex_unlock(&tz->lock); - mutex_unlock(&thermal_list_lock); /* Bind cooling devices for this zone */ - bind_tz(tz); + list_for_each_entry(cdev, &thermal_cdev_list, node) + thermal_zone_cdev_bind(tz, cdev); + + mutex_unlock(&thermal_list_lock); thermal_zone_device_init(tz); /* Update the new thermal zone and mark it as already updated. */ @@ -1652,8 +1616,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) /* Unbind all cdevs associated with 'this' thermal zone */ list_for_each_entry(cdev, &thermal_cdev_list, node) - if (tz->ops.unbind) - tz->ops.unbind(tz, cdev); + thermal_zone_cdev_unbind(tz, cdev); mutex_unlock(&thermal_list_lock); |