X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fthermal%2Fthermal_sys.c;h=8f0f37bb2825992bb14ba400975a1d15f5583fa9;hb=791700cdfc2453eb927ff7875d183d9808d89bfb;hp=9ee42ca4d289754cb99b374a32eb8bdc59371e46;hpb=3529e730b562f105764cc3dd287ff0a86cedfa85;p=platform%2Fadaptation%2Frenesas_rcar%2Frenesas_kernel.git diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 9ee42ca..8f0f37b 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -37,38 +37,98 @@ #include #include +#include "thermal_core.h" + MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Generic thermal management sysfs support"); MODULE_LICENSE("GPL"); -#define THERMAL_NO_TARGET -1UL -/* - * This structure is used to describe the behavior of - * a certain cooling device on a certain trip point - * in a certain thermal zone - */ -struct thermal_instance { - int id; - char name[THERMAL_NAME_LENGTH]; - struct thermal_zone_device *tz; - struct thermal_cooling_device *cdev; - int trip; - unsigned long upper; /* Highest cooling state for this trip point */ - unsigned long lower; /* Lowest cooling state for this trip point */ - unsigned long target; /* expected cooling state */ - char attr_name[THERMAL_NAME_LENGTH]; - struct device_attribute attr; - struct list_head tz_node; /* node in tz->thermal_instances */ - struct list_head cdev_node; /* node in cdev->thermal_instances */ -}; - static DEFINE_IDR(thermal_tz_idr); static DEFINE_IDR(thermal_cdev_idr); static DEFINE_MUTEX(thermal_idr_lock); static LIST_HEAD(thermal_tz_list); static LIST_HEAD(thermal_cdev_list); +static LIST_HEAD(thermal_governor_list); + static DEFINE_MUTEX(thermal_list_lock); +static DEFINE_MUTEX(thermal_governor_lock); + +static struct thermal_governor *__find_governor(const char *name) +{ + struct thermal_governor *pos; + + list_for_each_entry(pos, &thermal_governor_list, governor_list) + if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH)) + return pos; + + return NULL; +} + +int thermal_register_governor(struct thermal_governor *governor) +{ + int err; + const char *name; + struct thermal_zone_device *pos; + + if (!governor) + return -EINVAL; + + mutex_lock(&thermal_governor_lock); + + err = -EBUSY; + if (__find_governor(governor->name) == NULL) { + err = 0; + list_add(&governor->governor_list, &thermal_governor_list); + } + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (pos->governor) + continue; + if (pos->tzp) + name = pos->tzp->governor_name; + else + name = DEFAULT_THERMAL_GOVERNOR; + if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH)) + pos->governor = governor; + } + + mutex_unlock(&thermal_list_lock); + mutex_unlock(&thermal_governor_lock); + + return err; +} +EXPORT_SYMBOL_GPL(thermal_register_governor); + +void thermal_unregister_governor(struct thermal_governor *governor) +{ + struct thermal_zone_device *pos; + + if (!governor) + return; + + mutex_lock(&thermal_governor_lock); + + if (__find_governor(governor->name) == NULL) + goto exit; + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!strnicmp(pos->governor->name, governor->name, + THERMAL_NAME_LENGTH)) + pos->governor = NULL; + } + + mutex_unlock(&thermal_list_lock); + list_del(&governor->governor_list); +exit: + mutex_unlock(&thermal_governor_lock); + return; +} +EXPORT_SYMBOL_GPL(thermal_unregister_governor); static int get_idr(struct idr *idr, struct mutex *lock, int *id) { @@ -101,6 +161,261 @@ static void release_idr(struct idr *idr, struct mutex *lock, int id) mutex_unlock(lock); } +int get_tz_trend(struct thermal_zone_device *tz, int trip) +{ + enum thermal_trend trend; + + if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) { + if (tz->temperature > tz->last_temperature) + trend = THERMAL_TREND_RAISING; + else if (tz->temperature < tz->last_temperature) + trend = THERMAL_TREND_DROPPING; + else + trend = THERMAL_TREND_STABLE; + } + + return trend; +} +EXPORT_SYMBOL(get_tz_trend); + +struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int trip) +{ + struct thermal_instance *pos = NULL; + struct thermal_instance *target_instance = NULL; + + 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) { + target_instance = pos; + break; + } + } + + mutex_unlock(&cdev->lock); + mutex_unlock(&tz->lock); + + return target_instance; +} +EXPORT_SYMBOL(get_thermal_instance); + +static void print_bind_err_msg(struct thermal_zone_device *tz, + 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); +} + +static void __bind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev) +{ + int i, ret; + + for (i = 0; i < tz->trips; i++) { + if (mask & (1 << i)) { + ret = thermal_zone_bind_cooling_device(tz, i, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + if (ret) + print_bind_err_msg(tz, cdev, ret); + } + } +} + +static void __unbind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev) +{ + int i; + + for (i = 0; i < tz->trips; i++) + if (mask & (1 << i)) + thermal_zone_unbind_cooling_device(tz, i, cdev); +} + +static void bind_cdev(struct thermal_cooling_device *cdev) +{ + int i, ret; + const struct thermal_zone_params *tzp; + struct thermal_zone_device *pos = NULL; + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!pos->tzp && !pos->ops->bind) + continue; + + if (!pos->tzp && pos->ops->bind) { + ret = pos->ops->bind(pos, cdev); + if (ret) + print_bind_err_msg(pos, cdev, ret); + } + + tzp = pos->tzp; + if (!tzp || !tzp->tbp) + continue; + + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(pos, cdev)) + continue; + tzp->tbp[i].cdev = cdev; + __bind(pos, tzp->tbp[i].trip_mask, cdev); + } + } + + mutex_unlock(&thermal_list_lock); +} + +static void bind_tz(struct thermal_zone_device *tz) +{ + int i, ret; + struct thermal_cooling_device *pos = NULL; + const struct thermal_zone_params *tzp = tz->tzp; + + if (!tzp && !tz->ops->bind) + return; + + mutex_lock(&thermal_list_lock); + + /* If there is no platform data, try to use ops->bind */ + if (!tzp && tz->ops->bind) { + list_for_each_entry(pos, &thermal_cdev_list, node) { + ret = tz->ops->bind(tz, pos); + if (ret) + print_bind_err_msg(tz, pos, ret); + } + goto exit; + } + + if (!tzp || !tzp->tbp) + goto exit; + + list_for_each_entry(pos, &thermal_cdev_list, node) { + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(tz, pos)) + continue; + tzp->tbp[i].cdev = pos; + __bind(tz, tzp->tbp[i].trip_mask, pos); + } + } +exit: + mutex_unlock(&thermal_list_lock); +} + +static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, + int delay) +{ + if (delay > 1000) + mod_delayed_work(system_freezable_wq, &tz->poll_queue, + round_jiffies(msecs_to_jiffies(delay))); + else if (delay) + mod_delayed_work(system_freezable_wq, &tz->poll_queue, + msecs_to_jiffies(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 + thermal_zone_device_set_polling(tz, 0); + + mutex_unlock(&tz->lock); +} + +static void handle_non_critical_trips(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type) +{ + tz->governor->throttle(tz, trip); +} + +static void handle_critical_trips(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type) +{ + long trip_temp; + + tz->ops->get_trip_temp(tz, trip, &trip_temp); + + /* If we have not crossed the trip_temp, we do not care. */ + if (tz->temperature < trip_temp) + return; + + if (tz->ops->notify) + tz->ops->notify(tz, trip, trip_type); + + if (trip_type == THERMAL_TRIP_CRITICAL) { + pr_emerg("Critical temperature reached(%d C),shutting down\n", + tz->temperature / 1000); + orderly_poweroff(true); + } +} + +static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) +{ + enum thermal_trip_type type; + + tz->ops->get_trip_type(tz, trip, &type); + + if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) + handle_critical_trips(tz, trip, type); + else + handle_non_critical_trips(tz, trip, type); + /* + * Alright, we handled this trip successfully. + * So, start monitoring again. + */ + monitor_thermal_zone(tz); +} + +static void update_temperature(struct thermal_zone_device *tz) +{ + long temp; + int ret; + + mutex_lock(&tz->lock); + + ret = tz->ops->get_temp(tz, &temp); + if (ret) { + pr_warn("failed to read out thermal zone %d\n", tz->id); + goto exit; + } + + tz->last_temperature = tz->temperature; + tz->temperature = temp; + +exit: + mutex_unlock(&tz->lock); +} + +void thermal_zone_device_update(struct thermal_zone_device *tz) +{ + int count; + + update_temperature(tz); + + for (count = 0; count < tz->trips; count++) + handle_thermal_trip(tz, count); +} +EXPORT_SYMBOL(thermal_zone_device_update); + +static void thermal_zone_device_check(struct work_struct *work) +{ + struct thermal_zone_device *tz = container_of(work, struct + thermal_zone_device, + poll_queue.work); + thermal_zone_device_update(tz); +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -354,10 +669,41 @@ passive_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", tz->forced_passive); } +static ssize_t +policy_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -EINVAL; + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_governor *gov; + + mutex_lock(&thermal_governor_lock); + + gov = __find_governor(buf); + if (!gov) + goto exit; + + tz->governor = gov; + ret = count; + +exit: + mutex_unlock(&thermal_governor_lock); + return ret; +} + +static ssize_t +policy_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + return sprintf(buf, "%s\n", tz->governor->name); +} + static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); static DEVICE_ATTR(mode, 0644, mode_show, mode_store); static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); +static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ @@ -700,27 +1046,6 @@ thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) } #endif -static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, - int delay) -{ - if (delay > 1000) - mod_delayed_work(system_freezable_wq, &tz->poll_queue, - round_jiffies(msecs_to_jiffies(delay))); - else if (delay) - mod_delayed_work(system_freezable_wq, &tz->poll_queue, - msecs_to_jiffies(delay)); - else - cancel_delayed_work(&tz->poll_queue); -} - -static void thermal_zone_device_check(struct work_struct *work) -{ - struct thermal_zone_device *tz = container_of(work, struct - thermal_zone_device, - poll_queue.work); - thermal_zone_device_update(tz); -} - /** * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone * @tz: thermal zone device @@ -895,7 +1220,6 @@ thermal_cooling_device_register(char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; - struct thermal_zone_device *pos; int result; if (type && strlen(type) >= THERMAL_NAME_LENGTH) @@ -945,20 +1269,15 @@ thermal_cooling_device_register(char *type, void *devdata, if (result) goto unregister; + /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); list_add(&cdev->node, &thermal_cdev_list); - list_for_each_entry(pos, &thermal_tz_list, node) { - if (!pos->ops->bind) - continue; - result = pos->ops->bind(pos, cdev); - if (result) - break; - - } mutex_unlock(&thermal_list_lock); - if (!result) - return cdev; + /* Update binding information for 'this' new cdev */ + bind_cdev(cdev); + + return cdev; unregister: release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); @@ -974,10 +1293,10 @@ EXPORT_SYMBOL(thermal_cooling_device_register); * thermal_cooling_device_unregister() must be called when the device is no * longer needed. */ -void thermal_cooling_device_unregister(struct - thermal_cooling_device - *cdev) +void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) { + int i; + const struct thermal_zone_params *tzp; struct thermal_zone_device *tz; struct thermal_cooling_device *pos = NULL; @@ -994,12 +1313,28 @@ void thermal_cooling_device_unregister(struct return; } 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) + if (tz->ops->unbind) { + tz->ops->unbind(tz, cdev); continue; - tz->ops->unbind(tz, cdev); + } + + if (!tz->tzp || !tz->tzp->tbp) + continue; + + tzp = tz->tzp; + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev == cdev) { + __unbind(tz, tzp->tbp[i].trip_mask, cdev); + tzp->tbp[i].cdev = NULL; + } + } } + mutex_unlock(&thermal_list_lock); + if (cdev->type[0]) device_remove_file(&cdev->device, &dev_attr_cdev_type); device_remove_file(&cdev->device, &dev_attr_max_state); @@ -1011,7 +1346,7 @@ void thermal_cooling_device_unregister(struct } EXPORT_SYMBOL(thermal_cooling_device_unregister); -static void thermal_cdev_do_update(struct thermal_cooling_device *cdev) +void thermal_cdev_update(struct thermal_cooling_device *cdev) { struct thermal_instance *instance; unsigned long target = 0; @@ -1032,183 +1367,25 @@ static void thermal_cdev_do_update(struct thermal_cooling_device *cdev) cdev->ops->set_cur_state(cdev, target); cdev->updated = true; } +EXPORT_SYMBOL(thermal_cdev_update); -static void thermal_zone_do_update(struct thermal_zone_device *tz) -{ - struct thermal_instance *instance; - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) - thermal_cdev_do_update(instance->cdev); -} - -/* - * Cooling algorithm for both active and passive cooling - * - * 1. if the temperature is higher than a trip point, - * a. if the trend is THERMAL_TREND_RAISING, use higher cooling - * state for this trip point - * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling - * state for this trip point - * - * 2. if the temperature is lower than a trip point, use lower - * cooling state for this trip point - * - * Note that this behaves the same as the previous passive cooling - * algorithm. - */ - -static void thermal_zone_trip_update(struct thermal_zone_device *tz, - int trip, long temp) -{ - struct thermal_instance *instance; - struct thermal_cooling_device *cdev = NULL; - unsigned long cur_state, max_state; - long trip_temp; - enum thermal_trip_type trip_type; - enum thermal_trend trend; - - if (trip == THERMAL_TRIPS_NONE) { - trip_temp = tz->forced_passive; - trip_type = THERMAL_TRIPS_NONE; - } else { - tz->ops->get_trip_temp(tz, trip, &trip_temp); - tz->ops->get_trip_type(tz, trip, &trip_type); - } - - if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) { - /* - * compare the current temperature and previous temperature - * to get the thermal trend, if no special requirement - */ - if (tz->temperature > tz->last_temperature) - trend = THERMAL_TREND_RAISING; - else if (tz->temperature < tz->last_temperature) - trend = THERMAL_TREND_DROPPING; - else - trend = THERMAL_TREND_STABLE; - } - - if (temp >= trip_temp) { - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - cdev = instance->cdev; - - cdev->ops->get_cur_state(cdev, &cur_state); - cdev->ops->get_max_state(cdev, &max_state); - - if (trend == THERMAL_TREND_RAISING) { - cur_state = cur_state < instance->upper ? - (cur_state + 1) : instance->upper; - } else if (trend == THERMAL_TREND_DROPPING) { - cur_state = cur_state > instance->lower ? - (cur_state - 1) : instance->lower; - } - - /* activate a passive thermal instance */ - if ((trip_type == THERMAL_TRIP_PASSIVE || - trip_type == THERMAL_TRIPS_NONE) && - instance->target == THERMAL_NO_TARGET) - tz->passive++; - - instance->target = cur_state; - cdev->updated = false; /* cooling device needs update */ - } - } else { /* below trip */ - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - /* Do not use the inactive thermal instance */ - if (instance->target == THERMAL_NO_TARGET) - continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &cur_state); - - cur_state = cur_state > instance->lower ? - (cur_state - 1) : THERMAL_NO_TARGET; - - /* deactivate a passive thermal instance */ - if ((trip_type == THERMAL_TRIP_PASSIVE || - trip_type == THERMAL_TRIPS_NONE) && - cur_state == THERMAL_NO_TARGET) - tz->passive--; - instance->target = cur_state; - cdev->updated = false; /* cooling device needs update */ - } - } - - return; -} /** - * thermal_zone_device_update - force an update of a thermal zone's state - * @ttz: the thermal zone to update + * notify_thermal_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_zone_device_update(struct thermal_zone_device *tz) +void notify_thermal_framework(struct thermal_zone_device *tz, int trip) { - int count, ret = 0; - long temp, trip_temp; - enum thermal_trip_type trip_type; - - mutex_lock(&tz->lock); - - if (tz->ops->get_temp(tz, &temp)) { - /* get_temp failed - retry it later */ - pr_warn("failed to read out thermal zone %d\n", tz->id); - goto leave; - } - - tz->last_temperature = tz->temperature; - tz->temperature = temp; - - for (count = 0; count < tz->trips; count++) { - tz->ops->get_trip_type(tz, count, &trip_type); - tz->ops->get_trip_temp(tz, count, &trip_temp); - - switch (trip_type) { - case THERMAL_TRIP_CRITICAL: - if (temp >= trip_temp) { - if (tz->ops->notify) - ret = tz->ops->notify(tz, count, - trip_type); - if (!ret) { - pr_emerg("Critical temperature reached (%ld C), shutting down\n", - temp/1000); - orderly_poweroff(true); - } - } - break; - case THERMAL_TRIP_HOT: - if (temp >= trip_temp) - if (tz->ops->notify) - tz->ops->notify(tz, count, trip_type); - break; - case THERMAL_TRIP_ACTIVE: - thermal_zone_trip_update(tz, count, temp); - break; - case THERMAL_TRIP_PASSIVE: - if (temp >= trip_temp || tz->passive) - thermal_zone_trip_update(tz, count, temp); - break; - } - } - - if (tz->forced_passive) - thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE, temp); - thermal_zone_do_update(tz); - -leave: - 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 - thermal_zone_device_set_polling(tz, 0); - mutex_unlock(&tz->lock); + handle_thermal_trip(tz, trip); } -EXPORT_SYMBOL(thermal_zone_device_update); +EXPORT_SYMBOL(notify_thermal_framework); /** * create_trip_attrs - create attributes for trip points @@ -1320,6 +1497,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * @mask: a bit string indicating the writeablility of trip points * @devdata: private device data * @ops: standard thermal zone device callbacks + * @tzp: thermal zone platform parameters * @passive_delay: number of milliseconds to wait between polls when * performing passive cooling * @polling_delay: number of milliseconds to wait between polls when checking @@ -1332,10 +1510,10 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) struct thermal_zone_device *thermal_zone_device_register(const char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp, int passive_delay, int polling_delay) { struct thermal_zone_device *tz; - struct thermal_cooling_device *pos; enum thermal_trip_type trip_type; int result; int count; @@ -1365,6 +1543,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, strcpy(tz->type, type ? : ""); tz->ops = ops; + tz->tzp = tzp; tz->device.class = &thermal_class; tz->devdata = devdata; tz->trips = trips; @@ -1406,27 +1585,38 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, passive = 1; } - if (!passive) - result = device_create_file(&tz->device, - &dev_attr_passive); + if (!passive) { + result = device_create_file(&tz->device, &dev_attr_passive); + if (result) + goto unregister; + } + /* Create policy attribute */ + result = device_create_file(&tz->device, &dev_attr_policy); if (result) goto unregister; + /* Update 'this' zone's governor information */ + mutex_lock(&thermal_governor_lock); + + if (tz->tzp) + tz->governor = __find_governor(tz->tzp->governor_name); + else + tz->governor = __find_governor(DEFAULT_THERMAL_GOVERNOR); + + mutex_unlock(&thermal_governor_lock); + result = thermal_add_hwmon_sysfs(tz); if (result) goto unregister; mutex_lock(&thermal_list_lock); list_add_tail(&tz->node, &thermal_tz_list); - if (ops->bind) - list_for_each_entry(pos, &thermal_cdev_list, node) { - result = ops->bind(tz, pos); - if (result) - break; - } mutex_unlock(&thermal_list_lock); + /* Bind cooling devices for this zone */ + bind_tz(tz); + INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); thermal_zone_device_update(tz); @@ -1447,12 +1637,16 @@ EXPORT_SYMBOL(thermal_zone_device_register); */ void thermal_zone_device_unregister(struct thermal_zone_device *tz) { + int i; + const struct thermal_zone_params *tzp; struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; if (!tz) return; + tzp = tz->tzp; + mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) if (pos == tz) @@ -1463,9 +1657,25 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) return; } list_del(&tz->node); - if (tz->ops->unbind) - list_for_each_entry(cdev, &thermal_cdev_list, node) - tz->ops->unbind(tz, cdev); + + /* 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); + continue; + } + + if (!tzp || !tzp->tbp) + break; + + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev == cdev) { + __unbind(tz, tzp->tbp[i].trip_mask, cdev); + tzp->tbp[i].cdev = NULL; + } + } + } + mutex_unlock(&thermal_list_lock); thermal_zone_device_set_polling(tz, 0); @@ -1475,7 +1685,9 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) device_remove_file(&tz->device, &dev_attr_temp); if (tz->ops->get_mode) device_remove_file(&tz->device, &dev_attr_mode); + device_remove_file(&tz->device, &dev_attr_policy); remove_trip_attrs(tz); + tz->governor = NULL; thermal_remove_hwmon_sysfs(tz); release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);