diff options
Diffstat (limited to 'drivers/thermal/thermal_core.c')
-rw-r--r-- | drivers/thermal/thermal_core.c | 573 |
1 files changed, 267 insertions, 306 deletions
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 9a321dc548c8..117eeaf7dd24 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -9,9 +9,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/module.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/slab.h> #include <linux/kdev_t.h> #include <linux/idr.h> @@ -27,10 +27,6 @@ #include "thermal_core.h" #include "thermal_hwmon.h" -MODULE_AUTHOR("Zhang Rui"); -MODULE_DESCRIPTION("Generic thermal management sysfs support"); -MODULE_LICENSE("GPL v2"); - static DEFINE_IDA(thermal_tz_ida); static DEFINE_IDA(thermal_cdev_ida); @@ -40,10 +36,8 @@ static LIST_HEAD(thermal_governor_list); static DEFINE_MUTEX(thermal_list_lock); static DEFINE_MUTEX(thermal_governor_lock); -static DEFINE_MUTEX(poweroff_lock); static atomic_t in_suspend; -static bool power_off_triggered; static struct thermal_governor *def_governor; @@ -219,6 +213,8 @@ exit: mutex_unlock(&tz->lock); mutex_unlock(&thermal_governor_lock); + thermal_notify_tz_gov_change(tz->id, policy); + return ret; } @@ -226,15 +222,14 @@ int thermal_build_list_of_policies(char *buf) { struct thermal_governor *pos; ssize_t count = 0; - ssize_t size = PAGE_SIZE; mutex_lock(&thermal_governor_lock); list_for_each_entry(pos, &thermal_governor_list, governor_list) { - size = PAGE_SIZE - count; - count += scnprintf(buf + count, size, "%s ", pos->name); + count += scnprintf(buf + count, PAGE_SIZE - count, "%s ", + pos->name); } - count += scnprintf(buf + count, size, "\n"); + count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); mutex_unlock(&thermal_governor_lock); @@ -291,32 +286,23 @@ static int __init thermal_register_governors(void) * - Critical trip point will cause a system shutdown. */ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, - int delay) + unsigned long delay) { - if (delay > 1000) - mod_delayed_work(system_freezable_power_efficient_wq, - &tz->poll_queue, - round_jiffies(msecs_to_jiffies(delay))); - else if (delay) + if (delay) mod_delayed_work(system_freezable_power_efficient_wq, - &tz->poll_queue, - msecs_to_jiffies(delay)); + &tz->poll_queue, delay); else cancel_delayed_work(&tz->poll_queue); } static void monitor_thermal_zone(struct thermal_zone_device *tz) { - mutex_lock(&tz->lock); - - if (tz->passive) - thermal_zone_device_set_polling(tz, tz->passive_delay); - else if (tz->polling_delay) - thermal_zone_device_set_polling(tz, tz->polling_delay); - else + if (tz->mode != THERMAL_DEVICE_ENABLED) thermal_zone_device_set_polling(tz, 0); - - mutex_unlock(&tz->lock); + else if (tz->passive) + thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies); + else if (tz->polling_delay_jiffies) + thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); } static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip) @@ -325,114 +311,72 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip) def_governor->throttle(tz, trip); } -/** - * thermal_emergency_poweroff_func - emergency poweroff work after a known delay - * @work: work_struct associated with the emergency poweroff function - * - * This function is called in very critical situations to force - * a kernel poweroff after a configurable timeout value. - */ -static void thermal_emergency_poweroff_func(struct work_struct *work) +void thermal_zone_device_critical(struct thermal_zone_device *tz) { /* - * We have reached here after the emergency thermal shutdown - * Waiting period has expired. This means orderly_poweroff has - * not been able to shut off the system for some reason. - * Try to shut down the system immediately using kernel_power_off - * if populated - */ - WARN(1, "Attempting kernel_power_off: Temperature too high\n"); - kernel_power_off(); - - /* - * Worst of the worst case trigger emergency restart + * poweroff_delay_ms must be a carefully profiled positive value. + * Its a must for forced_emergency_poweroff_work to be scheduled. */ - WARN(1, "Attempting emergency_restart: Temperature too high\n"); - emergency_restart(); -} + int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; -static DECLARE_DELAYED_WORK(thermal_emergency_poweroff_work, - thermal_emergency_poweroff_func); + dev_emerg(&tz->device, "%s: critical temperature reached, " + "shutting down\n", tz->type); -/** - * thermal_emergency_poweroff - Trigger an emergency system poweroff - * - * This may be called from any critical situation to trigger a system shutdown - * after a known period of time. By default this is not scheduled. - */ -static void thermal_emergency_poweroff(void) -{ - int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; - /* - * poweroff_delay_ms must be a carefully profiled positive value. - * Its a must for thermal_emergency_poweroff_work to be scheduled - */ - if (poweroff_delay_ms <= 0) - return; - schedule_delayed_work(&thermal_emergency_poweroff_work, - msecs_to_jiffies(poweroff_delay_ms)); + hw_protection_shutdown("Temperature too high", poweroff_delay_ms); } +EXPORT_SYMBOL(thermal_zone_device_critical); static void handle_critical_trips(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, int trip_temp, enum thermal_trip_type trip_type) { - int trip_temp; - - tz->ops->get_trip_temp(tz, trip, &trip_temp); - /* If we have not crossed the trip_temp, we do not care. */ if (trip_temp <= 0 || tz->temperature < trip_temp) return; trace_thermal_zone_trip(tz, trip, trip_type); - if (tz->ops->notify) - tz->ops->notify(tz, trip, trip_type); - - if (trip_type == THERMAL_TRIP_CRITICAL) { - dev_emerg(&tz->device, - "critical temperature reached (%d C), shutting down\n", - tz->temperature / 1000); - mutex_lock(&poweroff_lock); - if (!power_off_triggered) { - /* - * Queue a backup emergency shutdown in the event of - * orderly_poweroff failure - */ - thermal_emergency_poweroff(); - orderly_poweroff(true); - power_off_triggered = true; - } - mutex_unlock(&poweroff_lock); - } + if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot) + tz->ops->hot(tz); + else if (trip_type == THERMAL_TRIP_CRITICAL) + tz->ops->critical(tz); } static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) { enum thermal_trip_type type; + int trip_temp, hyst = 0; /* Ignore disabled trip points */ if (test_bit(trip, &tz->trips_disabled)) return; + tz->ops->get_trip_temp(tz, trip, &trip_temp); tz->ops->get_trip_type(tz, trip, &type); + if (tz->ops->get_trip_hyst) + tz->ops->get_trip_hyst(tz, trip, &hyst); + + if (tz->last_temperature != THERMAL_TEMP_INVALID) { + if (tz->last_temperature < trip_temp && + tz->temperature >= trip_temp) + thermal_notify_tz_trip_up(tz->id, trip, + tz->temperature); + if (tz->last_temperature >= trip_temp && + tz->temperature < (trip_temp - hyst)) + thermal_notify_tz_trip_down(tz->id, trip, + tz->temperature); + } if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) - handle_critical_trips(tz, trip, type); + handle_critical_trips(tz, trip, trip_temp, type); else handle_non_critical_trips(tz, trip); - /* - * Alright, we handled this trip successfully. - * So, start monitoring again. - */ - monitor_thermal_zone(tz); } static void update_temperature(struct thermal_zone_device *tz) { int temp, ret; - ret = thermal_zone_get_temp(tz, &temp); + ret = __thermal_zone_get_temp(tz, &temp); if (ret) { if (ret != -EAGAIN) dev_warn(&tz->device, @@ -441,32 +385,73 @@ static void update_temperature(struct thermal_zone_device *tz) return; } - mutex_lock(&tz->lock); tz->last_temperature = tz->temperature; tz->temperature = temp; - mutex_unlock(&tz->lock); trace_thermal_temperature(tz); - if (tz->last_temperature == THERMAL_TEMP_INVALID) - dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n", - tz->temperature); - else - dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", - tz->last_temperature, tz->temperature); + + thermal_genl_sampling_temp(tz->id, temp); } static void thermal_zone_device_init(struct thermal_zone_device *tz) { struct thermal_instance *pos; tz->temperature = THERMAL_TEMP_INVALID; + tz->prev_low_trip = -INT_MAX; + tz->prev_high_trip = INT_MAX; list_for_each_entry(pos, &tz->thermal_instances, tz_node) pos->initialized = false; } -static void thermal_zone_device_reset(struct thermal_zone_device *tz) +static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, + enum thermal_device_mode mode) { - tz->passive = 0; - thermal_zone_device_init(tz); + int ret = 0; + + mutex_lock(&tz->lock); + + /* do nothing if mode isn't changing */ + if (mode == tz->mode) { + mutex_unlock(&tz->lock); + + return ret; + } + + if (tz->ops->change_mode) + ret = tz->ops->change_mode(tz, mode); + + if (!ret) + tz->mode = mode; + + mutex_unlock(&tz->lock); + + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + + if (mode == THERMAL_DEVICE_ENABLED) + thermal_notify_tz_enable(tz->id); + else + thermal_notify_tz_disable(tz->id); + + return ret; +} + +int thermal_zone_device_enable(struct thermal_zone_device *tz) +{ + return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_ENABLED); +} +EXPORT_SYMBOL_GPL(thermal_zone_device_enable); + +int thermal_zone_device_disable(struct thermal_zone_device *tz) +{ + return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED); +} +EXPORT_SYMBOL_GPL(thermal_zone_device_disable); + +int thermal_zone_device_is_enabled(struct thermal_zone_device *tz) +{ + lockdep_assert_held(&tz->lock); + + return tz->mode == THERMAL_DEVICE_ENABLED; } void thermal_zone_device_update(struct thermal_zone_device *tz, @@ -477,37 +462,29 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, if (atomic_read(&in_suspend)) return; - if (!tz->ops->get_temp) + if (WARN_ONCE(!tz->ops->get_temp, "'%s' must not be called without " + "'get_temp' ops set\n", __func__)) return; + mutex_lock(&tz->lock); + + if (!thermal_zone_device_is_enabled(tz)) + goto out; + update_temperature(tz); - thermal_zone_set_trips(tz); + __thermal_zone_set_trips(tz); tz->notify_event = event; - for (count = 0; count < tz->trips; count++) + for (count = 0; count < tz->num_trips; count++) handle_thermal_trip(tz, count); -} -EXPORT_SYMBOL_GPL(thermal_zone_device_update); -/** - * thermal_notify_framework - Sensor drivers use this API to notify framework - * @tz: thermal zone device - * @trip: indicates which trip point has been crossed - * - * This function handles the trip events from sensor drivers. It starts - * throttling the cooling devices according to the policy configured. - * For CRITICAL and HOT trip points, this notifies the respective drivers, - * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. - * The throttling policy is based on the configured platform data; if no - * platform data is provided, this uses the step_wise throttling policy. - */ -void thermal_notify_framework(struct thermal_zone_device *tz, int trip) -{ - handle_thermal_trip(tz, trip); + monitor_thermal_zone(tz); +out: + mutex_unlock(&tz->lock); } -EXPORT_SYMBOL_GPL(thermal_notify_framework); +EXPORT_SYMBOL_GPL(thermal_zone_device_update); static void thermal_zone_device_check(struct work_struct *work) { @@ -517,131 +494,71 @@ static void thermal_zone_device_check(struct work_struct *work) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } -/* - * Power actor section: interface to power actors to estimate power - * - * Set of functions used to interact to cooling devices that know - * how to estimate their devices power consumption. - */ - -/** - * power_actor_get_max_power() - get the maximum power that a cdev can consume - * @cdev: pointer to &thermal_cooling_device - * @tz: a valid thermal zone device pointer - * @max_power: pointer in which to store the maximum power - * - * Calculate the maximum power consumption in milliwats that the - * cooling device can currently consume and store it in @max_power. - * - * Return: 0 on success, -EINVAL if @cdev doesn't support the - * power_actor API or -E* on other error. - */ -int power_actor_get_max_power(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, u32 *max_power) -{ - if (!cdev_is_power_actor(cdev)) - return -EINVAL; - - return cdev->ops->state2power(cdev, tz, 0, max_power); -} - -/** - * power_actor_get_min_power() - get the mainimum power that a cdev can consume - * @cdev: pointer to &thermal_cooling_device - * @tz: a valid thermal zone device pointer - * @min_power: pointer in which to store the minimum power - * - * Calculate the minimum power consumption in milliwatts that the - * cooling device can currently consume and store it in @min_power. - * - * Return: 0 on success, -EINVAL if @cdev doesn't support the - * power_actor API or -E* on other error. - */ -int power_actor_get_min_power(struct thermal_cooling_device *cdev, - struct thermal_zone_device *tz, u32 *min_power) +int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), + void *data) { - unsigned long max_state; - int ret; - - if (!cdev_is_power_actor(cdev)) - return -EINVAL; + struct thermal_governor *gov; + int ret = 0; - ret = cdev->ops->get_max_state(cdev, &max_state); - if (ret) - return ret; + mutex_lock(&thermal_governor_lock); + list_for_each_entry(gov, &thermal_governor_list, governor_list) { + ret = cb(gov, data); + if (ret) + break; + } + mutex_unlock(&thermal_governor_lock); - return cdev->ops->state2power(cdev, tz, max_state, min_power); + return ret; } -/** - * 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. - */ -int power_actor_set_power(struct thermal_cooling_device *cdev, - struct thermal_instance *instance, u32 power) +int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, + void *), void *data) { - unsigned long state; - int ret; - - if (!cdev_is_power_actor(cdev)) - return -EINVAL; - - ret = cdev->ops->power2state(cdev, instance->tz, power, &state); - if (ret) - return ret; + struct thermal_cooling_device *cdev; + int ret = 0; - instance->target = state; - mutex_lock(&cdev->lock); - cdev->updated = false; - mutex_unlock(&cdev->lock); - thermal_cdev_update(cdev); + mutex_lock(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + ret = cb(cdev, data); + if (ret) + break; + } + mutex_unlock(&thermal_list_lock); - return 0; + return ret; } -void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz, - const char *cdev_type, size_t size) +int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), + void *data) { - struct thermal_cooling_device *cdev = NULL; + struct thermal_zone_device *tz; + int ret = 0; mutex_lock(&thermal_list_lock); - list_for_each_entry(cdev, &thermal_cdev_list, node) { - /* skip non matching cdevs */ - if (strncmp(cdev_type, cdev->type, size)) - continue; - - /* re binding the exception matching the type pattern */ - thermal_zone_bind_cooling_device(tz, THERMAL_TRIPS_NONE, cdev, - THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); + list_for_each_entry(tz, &thermal_tz_list, node) { + ret = cb(tz, data); + if (ret) + break; } mutex_unlock(&thermal_list_lock); + + return ret; } -void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, - const char *cdev_type, size_t size) +struct thermal_zone_device *thermal_zone_get_by_id(int id) { - struct thermal_cooling_device *cdev = NULL; + struct thermal_zone_device *tz, *match = NULL; mutex_lock(&thermal_list_lock); - list_for_each_entry(cdev, &thermal_cdev_list, node) { - /* skip non matching cdevs */ - if (strncmp(cdev_type, cdev->type, size)) - continue; - /* unbinding the exception matching the type pattern */ - thermal_zone_unbind_cooling_device(tz, THERMAL_TRIPS_NONE, - cdev); + list_for_each_entry(tz, &thermal_tz_list, node) { + if (tz->id == id) { + match = tz; + break; + } } mutex_unlock(&thermal_list_lock); + + return match; } /* @@ -689,7 +606,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, unsigned long max_state; int result, ret; - if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE)) + if (trip >= tz->num_trips || trip < 0) return -EINVAL; list_for_each_entry(pos1, &thermal_tz_list, node) { @@ -726,7 +643,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, dev->target = THERMAL_NO_TARGET; dev->weight = weight; - result = ida_simple_get(&tz->ida, 0, 0, GFP_KERNEL); + result = ida_alloc(&tz->ida, GFP_KERNEL); if (result < 0) goto free_mem; @@ -780,7 +697,7 @@ remove_trip_file: remove_symbol_link: sysfs_remove_link(&tz->device.kobj, dev->name); release_ida: - ida_simple_remove(&tz->ida, dev->id); + ida_free(&tz->ida, dev->id); free_mem: kfree(dev); return result; @@ -827,7 +744,7 @@ unbind: device_remove_file(&tz->device, &pos->weight_attr); device_remove_file(&tz->device, &pos->attr); sysfs_remove_link(&tz->device.kobj, pos->name); - ida_simple_remove(&tz->ida, pos->id); + ida_free(&tz->ida, pos->id); kfree(pos); return 0; } @@ -870,7 +787,7 @@ static void __bind(struct thermal_zone_device *tz, int mask, { int i, ret; - for (i = 0; i < tz->trips; i++) { + for (i = 0; i < tz->num_trips; i++) { if (mask & (1 << i)) { unsigned long upper, lower; @@ -950,10 +867,7 @@ __thermal_cooling_device_register(struct device_node *np, { struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; - int result; - - if (type && strlen(type) >= THERMAL_NAME_LENGTH) - return ERR_PTR(-EINVAL); + int id, ret; if (!ops || !ops->get_max_state || !ops->get_cur_state || !ops->set_cur_state) @@ -963,14 +877,22 @@ __thermal_cooling_device_register(struct device_node *np, if (!cdev) return ERR_PTR(-ENOMEM); - result = ida_simple_get(&thermal_cdev_ida, 0, 0, GFP_KERNEL); - if (result < 0) { - kfree(cdev); - return ERR_PTR(result); + ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL); + if (ret < 0) + goto out_kfree_cdev; + cdev->id = ret; + id = ret; + + ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); + if (ret) + goto out_ida_remove; + + cdev->type = kstrdup(type ? type : "", GFP_KERNEL); + if (!cdev->type) { + ret = -ENOMEM; + goto out_ida_remove; } - cdev->id = result; - strlcpy(cdev->type, type ? : "", sizeof(cdev->type)); mutex_init(&cdev->lock); INIT_LIST_HEAD(&cdev->thermal_instances); cdev->np = np; @@ -979,13 +901,9 @@ __thermal_cooling_device_register(struct device_node *np, cdev->device.class = &thermal_class; cdev->devdata = devdata; thermal_cooling_device_setup_sysfs(cdev); - dev_set_name(&cdev->device, "cooling_device%d", cdev->id); - result = device_register(&cdev->device); - if (result) { - ida_simple_remove(&thermal_cdev_ida, cdev->id); - put_device(&cdev->device); - return ERR_PTR(result); - } + ret = device_register(&cdev->device); + if (ret) + goto out_kfree_type; /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); @@ -1003,6 +921,17 @@ __thermal_cooling_device_register(struct device_node *np, mutex_unlock(&thermal_list_lock); return cdev; + +out_kfree_type: + thermal_cooling_device_destroy_sysfs(cdev); + kfree(cdev->type); + put_device(&cdev->device); + cdev = NULL; +out_ida_remove: + ida_free(&thermal_cdev_ida, id); +out_kfree_cdev: + kfree(cdev); + return ERR_PTR(ret); } /** @@ -1104,7 +1033,7 @@ static void __unbind(struct thermal_zone_device *tz, int mask, { int i; - for (i = 0; i < tz->trips; i++) + for (i = 0; i < tz->num_trips; i++) if (mask & (1 << i)) thermal_zone_unbind_cooling_device(tz, i, cdev); } @@ -1158,9 +1087,10 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) mutex_unlock(&thermal_list_lock); - ida_simple_remove(&thermal_cdev_ida, cdev->id); + ida_free(&thermal_cdev_ida, cdev->id); device_del(&cdev->device); thermal_cooling_device_destroy_sysfs(cdev); + kfree(cdev->type); put_device(&cdev->device); } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); @@ -1205,10 +1135,18 @@ exit: 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); +} + /** - * thermal_zone_device_register() - register a new thermal zone device + * thermal_zone_device_register_with_trips() - register a new thermal zone device * @type: the thermal zone device type - * @trips: the number of trip points the thermal zone support + * @trips: a pointer to an array of thermal trips + * @num_trips: the number of trip points the thermal zone support * @mask: a bit string indicating the writeablility of trip points * @devdata: private device data * @ops: standard thermal zone device callbacks @@ -1230,10 +1168,10 @@ exit: * IS_ERR*() helpers. */ struct thermal_zone_device * -thermal_zone_device_register(const char *type, int trips, int mask, - void *devdata, struct thermal_zone_device_ops *ops, - struct thermal_zone_params *tzp, int passive_delay, - int polling_delay) +thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips, int mask, + void *devdata, struct thermal_zone_device_ops *ops, + struct thermal_zone_params *tzp, int passive_delay, + int polling_delay) { struct thermal_zone_device *tz; enum thermal_trip_type trip_type; @@ -1244,27 +1182,40 @@ thermal_zone_device_register(const char *type, int trips, int mask, struct thermal_governor *governor; if (!type || strlen(type) == 0) { - pr_err("Error: No thermal zone type defined\n"); + pr_err("No thermal zone type defined\n"); return ERR_PTR(-EINVAL); } - if (type && strlen(type) >= THERMAL_NAME_LENGTH) { - pr_err("Error: Thermal zone name (%s) too long, should be under %d chars\n", + if (strlen(type) >= THERMAL_NAME_LENGTH) { + pr_err("Thermal zone name (%s) too long, should be under %d chars\n", type, THERMAL_NAME_LENGTH); return ERR_PTR(-EINVAL); } - if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips) { - pr_err("Error: Incorrect number of thermal trips\n"); + /* + * Max trip count can't exceed 31 as the "mask >> num_trips" condition. + * For example, shifting by 32 will result in compiler warning: + * warning: right shift count >= width of type [-Wshift-count- overflow] + * + * Also "mask >> num_trips" will always be true with 32 bit shift. + * E.g. mask = 0x80000000 for trip id 31 to be RW. Then + * mask >> 32 = 0x80000000 + * This will result in failure for the below condition. + * + * Check will be true when the bit 31 of the mask is set. + * 32 bit shift will cause overflow of 4 byte integer. + */ + if (num_trips > (BITS_PER_TYPE(int) - 1) || num_trips < 0 || mask >> num_trips) { + pr_err("Incorrect number of thermal trips\n"); return ERR_PTR(-EINVAL); } if (!ops) { - pr_err("Error: Thermal zone device ops not defined\n"); + pr_err("Thermal zone device ops not defined\n"); return ERR_PTR(-EINVAL); } - if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) + if (num_trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) return ERR_PTR(-EINVAL); tz = kzalloc(sizeof(*tz), GFP_KERNEL); @@ -1274,21 +1225,31 @@ thermal_zone_device_register(const char *type, int trips, int mask, INIT_LIST_HEAD(&tz->thermal_instances); ida_init(&tz->ida); mutex_init(&tz->lock); - id = ida_simple_get(&thermal_tz_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&thermal_tz_ida, GFP_KERNEL); if (id < 0) { result = id; goto free_tz; } tz->id = id; - strlcpy(tz->type, type, sizeof(tz->type)); + strscpy(tz->type, type, sizeof(tz->type)); + + result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); + if (result) + goto remove_id; + + if (!ops->critical) + ops->critical = thermal_zone_device_critical; + tz->ops = ops; tz->tzp = tzp; tz->device.class = &thermal_class; tz->devdata = devdata; tz->trips = trips; - tz->passive_delay = passive_delay; - tz->polling_delay = polling_delay; + tz->num_trips = num_trips; + + thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); + thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); /* sys I/F */ /* Add nodes that are always present via .groups */ @@ -1299,18 +1260,14 @@ thermal_zone_device_register(const char *type, int trips, int mask, /* A new thermal zone needs to be updated anyway. */ atomic_set(&tz->need_update, 1); - dev_set_name(&tz->device, "thermal_zone%d", tz->id); result = device_register(&tz->device); if (result) goto release_device; - for (count = 0; count < trips; count++) { - if (tz->ops->get_trip_type(tz, count, &trip_type)) - set_bit(count, &tz->trips_disabled); - if (tz->ops->get_trip_temp(tz, count, &trip_temp)) - set_bit(count, &tz->trips_disabled); - /* Check for bogus trip points */ - if (trip_temp == 0) + for (count = 0; count < num_trips; count++) { + if (tz->ops->get_trip_type(tz, count, &trip_type) || + tz->ops->get_trip_temp(tz, count, &trip_temp) || + !trip_temp) set_bit(count, &tz->trips_disabled); } @@ -1345,11 +1302,13 @@ thermal_zone_device_register(const char *type, int trips, int mask, INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); - thermal_zone_device_reset(tz); + thermal_zone_device_init(tz); /* Update the new thermal zone and mark it as already updated. */ if (atomic_cmpxchg(&tz->need_update, 1, 0)) thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); + thermal_notify_tz_create(tz->id, tz->type); + return tz; unregister: @@ -1358,20 +1317,31 @@ release_device: put_device(&tz->device); tz = NULL; remove_id: - ida_simple_remove(&thermal_tz_ida, id); + ida_free(&thermal_tz_ida, id); free_tz: kfree(tz); return ERR_PTR(result); } +EXPORT_SYMBOL_GPL(thermal_zone_device_register_with_trips); + +struct thermal_zone_device *thermal_zone_device_register(const char *type, int ntrips, int mask, + void *devdata, struct thermal_zone_device_ops *ops, + struct thermal_zone_params *tzp, int passive_delay, + int polling_delay) +{ + return thermal_zone_device_register_with_trips(type, NULL, ntrips, mask, + devdata, ops, tzp, + passive_delay, polling_delay); +} EXPORT_SYMBOL_GPL(thermal_zone_device_register); /** - * thermal_device_unregister - removes the registered thermal zone device + * thermal_zone_device_unregister - removes the registered thermal zone device * @tz: the thermal zone device to remove */ void thermal_zone_device_unregister(struct thermal_zone_device *tz) { - int i; + int i, tz_id; const struct thermal_zone_params *tzp; struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; @@ -1380,6 +1350,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) return; tzp = tz->tzp; + tz_id = tz->id; mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) @@ -1417,10 +1388,12 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) thermal_set_governor(tz, NULL); thermal_remove_hwmon_sysfs(tz); - ida_simple_remove(&thermal_tz_ida, tz->id); + ida_free(&thermal_tz_ida, tz->id); ida_destroy(&tz->ida); mutex_destroy(&tz->lock); device_unregister(&tz->device); + + thermal_notify_tz_delete(tz_id); } EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); @@ -1466,7 +1439,6 @@ static int thermal_pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { struct thermal_zone_device *tz; - enum thermal_device_mode tz_mode; switch (mode) { case PM_HIBERNATION_PREPARE: @@ -1479,13 +1451,6 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_POST_SUSPEND: atomic_set(&in_suspend, 0); list_for_each_entry(tz, &thermal_tz_list, node) { - tz_mode = THERMAL_DEVICE_ENABLED; - if (tz->ops->get_mode) - tz->ops->get_mode(tz, &tz_mode); - - if (tz_mode == THERMAL_DEVICE_DISABLED) - continue; - thermal_zone_device_init(tz); thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); @@ -1505,7 +1470,10 @@ static int __init thermal_init(void) { int result; - mutex_init(&poweroff_lock); + result = thermal_netlink_init(); + if (result) + goto error; + result = thermal_register_governors(); if (result) goto error; @@ -1514,10 +1482,6 @@ static int __init thermal_init(void) if (result) goto unregister_governors; - result = of_parse_thermal_zones(); - if (result) - goto unregister_class; - result = register_pm_notifier(&thermal_pm_nb); if (result) pr_warn("Thermal: Can not register suspend notifier, return %d\n", @@ -1525,8 +1489,6 @@ static int __init thermal_init(void) return 0; -unregister_class: - class_unregister(&thermal_class); unregister_governors: thermal_unregister_governors(); error: @@ -1534,7 +1496,6 @@ error: ida_destroy(&thermal_cdev_ida); mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_governor_lock); - mutex_destroy(&poweroff_lock); return result; } -core_initcall(thermal_init); +postcore_initcall(thermal_init); |