summaryrefslogtreecommitdiff
path: root/drivers/thermal/thermal_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/thermal_core.c')
-rw-r--r--drivers/thermal/thermal_core.c223
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);