Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
[linux-2.6-block.git] / drivers / thermal / thermal_core.c
index 911fd964c742485847fde04b83254135abcead53..641faab6e24b50fef4d70d3334edfc49e0ab0ce3 100644 (file)
@@ -5,22 +5,9 @@
  *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
  *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
  *
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -64,6 +51,13 @@ static atomic_t in_suspend;
 
 static struct thermal_governor *def_governor;
 
+/*
+ * Governor section: set of functions to handle thermal governors
+ *
+ * Functions to help in the life cycle of thermal governors within
+ * the thermal core and by the thermal governor code.
+ */
+
 static struct thermal_governor *__find_governor(const char *name)
 {
        struct thermal_governor *pos;
@@ -142,11 +136,16 @@ int thermal_register_governor(struct thermal_governor *governor)
        mutex_lock(&thermal_governor_lock);
 
        err = -EBUSY;
-       if (__find_governor(governor->name) == NULL) {
+       if (!__find_governor(governor->name)) {
+               bool match_default;
+
                err = 0;
                list_add(&governor->governor_list, &thermal_governor_list);
-               if (!def_governor && !strncmp(governor->name,
-                       DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
+               match_default = !strncmp(governor->name,
+                                        DEFAULT_THERMAL_GOVERNOR,
+                                        THERMAL_NAME_LENGTH);
+
+               if (!def_governor && match_default)
                        def_governor = governor;
        }
 
@@ -188,14 +187,14 @@ void thermal_unregister_governor(struct thermal_governor *governor)
 
        mutex_lock(&thermal_governor_lock);
 
-       if (__find_governor(governor->name) == NULL)
+       if (!__find_governor(governor->name))
                goto exit;
 
        mutex_lock(&thermal_list_lock);
 
        list_for_each_entry(pos, &thermal_tz_list, node) {
                if (!strncasecmp(pos->governor->name, governor->name,
-                                               THERMAL_NAME_LENGTH))
+                                THERMAL_NAME_LENGTH))
                        thermal_set_governor(pos, NULL);
        }
 
@@ -203,195 +202,92 @@ void thermal_unregister_governor(struct thermal_governor *governor)
        list_del(&governor->governor_list);
 exit:
        mutex_unlock(&thermal_governor_lock);
-       return;
-}
-
-static int get_idr(struct idr *idr, struct mutex *lock, int *id)
-{
-       int ret;
-
-       if (lock)
-               mutex_lock(lock);
-       ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
-       if (lock)
-               mutex_unlock(lock);
-       if (unlikely(ret < 0))
-               return ret;
-       *id = ret;
-       return 0;
-}
-
-static void release_idr(struct idr *idr, struct mutex *lock, int id)
-{
-       if (lock)
-               mutex_lock(lock);
-       idr_remove(idr, id);
-       if (lock)
-               mutex_unlock(lock);
-}
-
-int get_tz_trend(struct thermal_zone_device *tz, int trip)
-{
-       enum thermal_trend trend;
-
-       if (tz->emul_temperature || !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)
+int thermal_zone_device_set_policy(struct thermal_zone_device *tz,
+                                  char *policy)
 {
-       struct thermal_instance *pos = NULL;
-       struct thermal_instance *target_instance = NULL;
+       struct thermal_governor *gov;
+       int ret = -EINVAL;
 
+       mutex_lock(&thermal_governor_lock);
        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;
-               }
-       }
+       gov = __find_governor(strim(policy));
+       if (!gov)
+               goto exit;
 
-       mutex_unlock(&cdev->lock);
-       mutex_unlock(&tz->lock);
+       ret = thermal_set_governor(tz, gov);
 
-       return target_instance;
-}
-EXPORT_SYMBOL(get_thermal_instance);
+exit:
+       mutex_unlock(&tz->lock);
+       mutex_unlock(&thermal_governor_lock);
 
-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);
+       return ret;
 }
 
-static void __bind(struct thermal_zone_device *tz, int mask,
-                       struct thermal_cooling_device *cdev,
-                       unsigned long *limits,
-                       unsigned int weight)
+int thermal_build_list_of_policies(char *buf)
 {
-       int i, ret;
+       struct thermal_governor *pos;
+       ssize_t count = 0;
+       ssize_t size = PAGE_SIZE;
 
-       for (i = 0; i < tz->trips; i++) {
-               if (mask & (1 << i)) {
-                       unsigned long upper, lower;
+       mutex_lock(&thermal_governor_lock);
 
-                       upper = THERMAL_NO_LIMIT;
-                       lower = THERMAL_NO_LIMIT;
-                       if (limits) {
-                               lower = limits[i * 2];
-                               upper = limits[i * 2 + 1];
-                       }
-                       ret = thermal_zone_bind_cooling_device(tz, i, cdev,
-                                                              upper, lower,
-                                                              weight);
-                       if (ret)
-                               print_bind_err_msg(tz, cdev, ret);
-               }
+       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, size, "\n");
 
-static void __unbind(struct thermal_zone_device *tz, int mask,
-                       struct thermal_cooling_device *cdev)
-{
-       int i;
+       mutex_unlock(&thermal_governor_lock);
 
-       for (i = 0; i < tz->trips; i++)
-               if (mask & (1 << i))
-                       thermal_zone_unbind_cooling_device(tz, i, cdev);
+       return count;
 }
 
-static void bind_cdev(struct thermal_cooling_device *cdev)
+static int __init thermal_register_governors(void)
 {
-       int i, ret;
-       const struct thermal_zone_params *tzp;
-       struct thermal_zone_device *pos = NULL;
-
-       mutex_lock(&thermal_list_lock);
+       int result;
 
-       list_for_each_entry(pos, &thermal_tz_list, node) {
-               if (!pos->tzp && !pos->ops->bind)
-                       continue;
+       result = thermal_gov_step_wise_register();
+       if (result)
+               return result;
 
-               if (pos->ops->bind) {
-                       ret = pos->ops->bind(pos, cdev);
-                       if (ret)
-                               print_bind_err_msg(pos, cdev, ret);
-                       continue;
-               }
+       result = thermal_gov_fair_share_register();
+       if (result)
+               return result;
 
-               tzp = pos->tzp;
-               if (!tzp || !tzp->tbp)
-                       continue;
+       result = thermal_gov_bang_bang_register();
+       if (result)
+               return result;
 
-               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,
-                              tzp->tbp[i].binding_limits,
-                              tzp->tbp[i].weight);
-               }
-       }
+       result = thermal_gov_user_space_register();
+       if (result)
+               return result;
 
-       mutex_unlock(&thermal_list_lock);
+       return thermal_gov_power_allocator_register();
 }
 
-static void bind_tz(struct thermal_zone_device *tz)
+static void thermal_unregister_governors(void)
 {
-       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 ops->bind, try to use ops->bind */
-       if (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,
-                              tzp->tbp[i].binding_limits,
-                              tzp->tbp[i].weight);
-               }
-       }
-exit:
-       mutex_unlock(&thermal_list_lock);
+       thermal_gov_step_wise_unregister();
+       thermal_gov_fair_share_unregister();
+       thermal_gov_bang_bang_unregister();
+       thermal_gov_user_space_unregister();
+       thermal_gov_power_allocator_unregister();
 }
 
+/*
+ * Zone update section: main control loop applied to each zone while monitoring
+ *
+ * in polling mode. The monitoring is done using a workqueue.
+ * Same update may be done on a zone by calling thermal_zone_device_update().
+ *
+ * An update means:
+ * - Non-critical trips will invoke the governor responsible for that zone;
+ * - Hot trips will produce a notification to userspace;
+ * - Critical trip point will cause a system shutdown.
+ */
 static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
                                            int delay)
 {
@@ -420,14 +316,15 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
 }
 
 static void handle_non_critical_trips(struct thermal_zone_device *tz,
-                       int trip, enum thermal_trip_type trip_type)
+                                     int trip,
+                                     enum thermal_trip_type trip_type)
 {
        tz->governor ? tz->governor->throttle(tz, trip) :
                       def_governor->throttle(tz, trip);
 }
 
 static void handle_critical_trips(struct thermal_zone_device *tz,
-                               int trip, enum thermal_trip_type trip_type)
+                                 int trip, enum thermal_trip_type trip_type)
 {
        int trip_temp;
 
@@ -471,105 +368,6 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
        monitor_thermal_zone(tz);
 }
 
-/**
- * thermal_zone_get_temp() - returns the temperature of a thermal zone
- * @tz: a valid pointer to a struct thermal_zone_device
- * @temp: a valid pointer to where to store the resulting temperature.
- *
- * When a valid thermal zone reference is passed, it will fetch its
- * temperature and fill @temp.
- *
- * Return: On success returns 0, an error code otherwise
- */
-int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
-{
-       int ret = -EINVAL;
-       int count;
-       int crit_temp = INT_MAX;
-       enum thermal_trip_type type;
-
-       if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
-               goto exit;
-
-       mutex_lock(&tz->lock);
-
-       ret = tz->ops->get_temp(tz, temp);
-
-       if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
-               for (count = 0; count < tz->trips; count++) {
-                       ret = tz->ops->get_trip_type(tz, count, &type);
-                       if (!ret && type == THERMAL_TRIP_CRITICAL) {
-                               ret = tz->ops->get_trip_temp(tz, count,
-                                               &crit_temp);
-                               break;
-                       }
-               }
-
-               /*
-                * Only allow emulating a temperature when the real temperature
-                * is below the critical temperature so that the emulation code
-                * cannot hide critical conditions.
-                */
-               if (!ret && *temp < crit_temp)
-                       *temp = tz->emul_temperature;
-       }
-       mutex_unlock(&tz->lock);
-exit:
-       return ret;
-}
-EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
-
-void thermal_zone_set_trips(struct thermal_zone_device *tz)
-{
-       int low = -INT_MAX;
-       int high = INT_MAX;
-       int trip_temp, hysteresis;
-       int i, ret;
-
-       mutex_lock(&tz->lock);
-
-       if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
-               goto exit;
-
-       for (i = 0; i < tz->trips; i++) {
-               int trip_low;
-
-               tz->ops->get_trip_temp(tz, i, &trip_temp);
-               tz->ops->get_trip_hyst(tz, i, &hysteresis);
-
-               trip_low = trip_temp - hysteresis;
-
-               if (trip_low < tz->temperature && trip_low > low)
-                       low = trip_low;
-
-               if (trip_temp > tz->temperature && trip_temp < high)
-                       high = trip_temp;
-       }
-
-       /* No need to change trip points */
-       if (tz->prev_low_trip == low && tz->prev_high_trip == high)
-               goto exit;
-
-       tz->prev_low_trip = low;
-       tz->prev_high_trip = high;
-
-       dev_dbg(&tz->device,
-               "new temperature boundaries: %d < x < %d\n", low, high);
-
-       /*
-        * Set a temperature window. When this window is left the driver
-        * must inform the thermal core via thermal_zone_device_update.
-        */
-       ret = tz->ops->set_trips(tz, low, high);
-       if (ret)
-               dev_err(&tz->device, "Failed to set trips: %d\n", ret);
-
-exit:
-       mutex_unlock(&tz->lock);
-}
-EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
-
 static void update_temperature(struct thermal_zone_device *tz)
 {
        int temp, ret;
@@ -629,6 +427,24 @@ void thermal_zone_device_update(struct thermal_zone_device *tz,
 }
 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);
+}
+EXPORT_SYMBOL_GPL(thermal_notify_framework);
+
 static void thermal_zone_device_check(struct work_struct *work)
 {
        struct thermal_zone_device *tz = container_of(work, struct
@@ -637,445 +453,12 @@ static void thermal_zone_device_check(struct work_struct *work)
        thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 }
 
-/* sys I/F for thermal zone */
-
-#define to_thermal_zone(_dev) \
-       container_of(_dev, struct thermal_zone_device, device)
-
-static ssize_t
-type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-
-       return sprintf(buf, "%s\n", tz->type);
-}
-
-static ssize_t
-temp_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int temperature, ret;
-
-       ret = thermal_zone_get_temp(tz, &temperature);
-
-       if (ret)
-               return ret;
-
-       return sprintf(buf, "%d\n", temperature);
-}
-
-static ssize_t
-mode_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       enum thermal_device_mode mode;
-       int result;
-
-       if (!tz->ops->get_mode)
-               return -EPERM;
-
-       result = tz->ops->get_mode(tz, &mode);
-       if (result)
-               return result;
-
-       return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
-                      : "disabled");
-}
-
-static ssize_t
-mode_store(struct device *dev, struct device_attribute *attr,
-          const char *buf, size_t count)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int result;
-
-       if (!tz->ops->set_mode)
-               return -EPERM;
-
-       if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
-               result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
-       else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
-               result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
-       else
-               result = -EINVAL;
-
-       if (result)
-               return result;
-
-       return count;
-}
-
-static ssize_t
-trip_point_type_show(struct device *dev, struct device_attribute *attr,
-                    char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       enum thermal_trip_type type;
-       int trip, result;
-
-       if (!tz->ops->get_trip_type)
-               return -EPERM;
-
-       if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
-               return -EINVAL;
-
-       result = tz->ops->get_trip_type(tz, trip, &type);
-       if (result)
-               return result;
-
-       switch (type) {
-       case THERMAL_TRIP_CRITICAL:
-               return sprintf(buf, "critical\n");
-       case THERMAL_TRIP_HOT:
-               return sprintf(buf, "hot\n");
-       case THERMAL_TRIP_PASSIVE:
-               return sprintf(buf, "passive\n");
-       case THERMAL_TRIP_ACTIVE:
-               return sprintf(buf, "active\n");
-       default:
-               return sprintf(buf, "unknown\n");
-       }
-}
-
-static ssize_t
-trip_point_temp_store(struct device *dev, struct device_attribute *attr,
-                    const char *buf, size_t count)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int trip, ret;
-       int temperature;
-
-       if (!tz->ops->set_trip_temp)
-               return -EPERM;
-
-       if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
-               return -EINVAL;
-
-       if (kstrtoint(buf, 10, &temperature))
-               return -EINVAL;
-
-       ret = tz->ops->set_trip_temp(tz, trip, temperature);
-       if (ret)
-               return ret;
-
-       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
-
-       return count;
-}
-
-static ssize_t
-trip_point_temp_show(struct device *dev, struct device_attribute *attr,
-                    char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int trip, ret;
-       int temperature;
-
-       if (!tz->ops->get_trip_temp)
-               return -EPERM;
-
-       if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
-               return -EINVAL;
-
-       ret = tz->ops->get_trip_temp(tz, trip, &temperature);
-
-       if (ret)
-               return ret;
-
-       return sprintf(buf, "%d\n", temperature);
-}
-
-static ssize_t
-trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
-                       const char *buf, size_t count)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int trip, ret;
-       int temperature;
-
-       if (!tz->ops->set_trip_hyst)
-               return -EPERM;
-
-       if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip))
-               return -EINVAL;
-
-       if (kstrtoint(buf, 10, &temperature))
-               return -EINVAL;
-
-       /*
-        * We are not doing any check on the 'temperature' value
-        * here. The driver implementing 'set_trip_hyst' has to
-        * take care of this.
-        */
-       ret = tz->ops->set_trip_hyst(tz, trip, temperature);
-
-       if (!ret)
-               thermal_zone_set_trips(tz);
-
-       return ret ? ret : count;
-}
-
-static ssize_t
-trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
-                       char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int trip, ret;
-       int temperature;
-
-       if (!tz->ops->get_trip_hyst)
-               return -EPERM;
-
-       if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip))
-               return -EINVAL;
-
-       ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
-
-       return ret ? ret : sprintf(buf, "%d\n", temperature);
-}
-
-static ssize_t
-passive_store(struct device *dev, struct device_attribute *attr,
-                   const char *buf, size_t count)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       struct thermal_cooling_device *cdev = NULL;
-       int state;
-
-       if (!sscanf(buf, "%d\n", &state))
-               return -EINVAL;
-
-       /* sanity check: values below 1000 millicelcius don't make sense
-        * and can cause the system to go into a thermal heart attack
-        */
-       if (state && state < 1000)
-               return -EINVAL;
-
-       if (state && !tz->forced_passive) {
-               mutex_lock(&thermal_list_lock);
-               list_for_each_entry(cdev, &thermal_cdev_list, node) {
-                       if (!strncmp("Processor", cdev->type,
-                                    sizeof("Processor")))
-                               thermal_zone_bind_cooling_device(tz,
-                                               THERMAL_TRIPS_NONE, cdev,
-                                               THERMAL_NO_LIMIT,
-                                               THERMAL_NO_LIMIT,
-                                               THERMAL_WEIGHT_DEFAULT);
-               }
-               mutex_unlock(&thermal_list_lock);
-               if (!tz->passive_delay)
-                       tz->passive_delay = 1000;
-       } else if (!state && tz->forced_passive) {
-               mutex_lock(&thermal_list_lock);
-               list_for_each_entry(cdev, &thermal_cdev_list, node) {
-                       if (!strncmp("Processor", cdev->type,
-                                    sizeof("Processor")))
-                               thermal_zone_unbind_cooling_device(tz,
-                                                                  THERMAL_TRIPS_NONE,
-                                                                  cdev);
-               }
-               mutex_unlock(&thermal_list_lock);
-               tz->passive_delay = 0;
-       }
-
-       tz->forced_passive = state;
-
-       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
-
-       return count;
-}
-
-static ssize_t
-passive_show(struct device *dev, struct device_attribute *attr,
-                  char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-
-       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;
-       char name[THERMAL_NAME_LENGTH];
-
-       snprintf(name, sizeof(name), "%s", buf);
-
-       mutex_lock(&thermal_governor_lock);
-       mutex_lock(&tz->lock);
-
-       gov = __find_governor(strim(name));
-       if (!gov)
-               goto exit;
-
-       ret = thermal_set_governor(tz, gov);
-       if (!ret)
-               ret = count;
-
-exit:
-       mutex_unlock(&tz->lock);
-       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 ssize_t
-available_policies_show(struct device *dev, struct device_attribute *devattr,
-                       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, size, "\n");
-
-       mutex_unlock(&thermal_governor_lock);
-
-       return count;
-}
-
-static ssize_t
-emul_temp_store(struct device *dev, struct device_attribute *attr,
-                    const char *buf, size_t count)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       int ret = 0;
-       int temperature;
-
-       if (kstrtoint(buf, 10, &temperature))
-               return -EINVAL;
-
-       if (!tz->ops->set_emul_temp) {
-               mutex_lock(&tz->lock);
-               tz->emul_temperature = temperature;
-               mutex_unlock(&tz->lock);
-       } else {
-               ret = tz->ops->set_emul_temp(tz, temperature);
-       }
-
-       if (!ret)
-               thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
-
-       return ret ? ret : count;
-}
-static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
-
-static ssize_t
-sustainable_power_show(struct device *dev, struct device_attribute *devattr,
-                      char *buf)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-
-       if (tz->tzp)
-               return sprintf(buf, "%u\n", tz->tzp->sustainable_power);
-       else
-               return -EIO;
-}
-
-static ssize_t
-sustainable_power_store(struct device *dev, struct device_attribute *devattr,
-                       const char *buf, size_t count)
-{
-       struct thermal_zone_device *tz = to_thermal_zone(dev);
-       u32 sustainable_power;
-
-       if (!tz->tzp)
-               return -EIO;
-
-       if (kstrtou32(buf, 10, &sustainable_power))
-               return -EINVAL;
-
-       tz->tzp->sustainable_power = sustainable_power;
-
-       return count;
-}
-static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show,
-               sustainable_power_store);
-
-#define create_s32_tzp_attr(name)                                      \
-       static ssize_t                                                  \
-       name##_show(struct device *dev, struct device_attribute *devattr, \
-               char *buf)                                              \
-       {                                                               \
-       struct thermal_zone_device *tz = to_thermal_zone(dev);          \
-                                                                       \
-       if (tz->tzp)                                                    \
-               return sprintf(buf, "%d\n", tz->tzp->name);             \
-       else                                                            \
-               return -EIO;                                            \
-       }                                                               \
-                                                                       \
-       static ssize_t                                                  \
-       name##_store(struct device *dev, struct device_attribute *devattr, \
-               const char *buf, size_t count)                          \
-       {                                                               \
-               struct thermal_zone_device *tz = to_thermal_zone(dev);  \
-               s32 value;                                              \
-                                                                       \
-               if (!tz->tzp)                                           \
-                       return -EIO;                                    \
-                                                                       \
-               if (kstrtos32(buf, 10, &value))                         \
-                       return -EINVAL;                                 \
-                                                                       \
-               tz->tzp->name = value;                                  \
-                                                                       \
-               return count;                                           \
-       }                                                               \
-       static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store)
-
-create_s32_tzp_attr(k_po);
-create_s32_tzp_attr(k_pu);
-create_s32_tzp_attr(k_i);
-create_s32_tzp_attr(k_d);
-create_s32_tzp_attr(integral_cutoff);
-create_s32_tzp_attr(slope);
-create_s32_tzp_attr(offset);
-#undef create_s32_tzp_attr
-
-static struct device_attribute *dev_tzp_attrs[] = {
-       &dev_attr_sustainable_power,
-       &dev_attr_k_po,
-       &dev_attr_k_pu,
-       &dev_attr_k_i,
-       &dev_attr_k_d,
-       &dev_attr_integral_cutoff,
-       &dev_attr_slope,
-       &dev_attr_offset,
-};
-
-static int create_tzp_attrs(struct device *dev)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(dev_tzp_attrs); i++) {
-               int ret;
-               struct device_attribute *dev_attr = dev_tzp_attrs[i];
-
-               ret = device_create_file(dev, dev_attr);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
+/*
+ * 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
@@ -1127,12 +510,13 @@ int power_actor_get_min_power(struct thermal_cooling_device *cdev,
 }
 
 /**
- * power_actor_set_power() - limit the maximum power that a cooling device can consume
+ * 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.
+ * 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.
@@ -1159,143 +543,75 @@ int power_actor_set_power(struct thermal_cooling_device *cdev,
        return 0;
 }
 
-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);
-static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL);
-
-/* sys I/F for cooling device */
-#define to_cooling_device(_dev)        \
-       container_of(_dev, struct thermal_cooling_device, device)
-
-static ssize_t
-thermal_cooling_device_type_show(struct device *dev,
-                                struct device_attribute *attr, char *buf)
+void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz,
+                                         const char *cdev_type, size_t size)
 {
-       struct thermal_cooling_device *cdev = to_cooling_device(dev);
+       struct thermal_cooling_device *cdev = NULL;
 
-       return sprintf(buf, "%s\n", cdev->type);
+       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);
+       }
+       mutex_unlock(&thermal_list_lock);
 }
 
-static ssize_t
-thermal_cooling_device_max_state_show(struct device *dev,
-                                     struct device_attribute *attr, char *buf)
+void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz,
+                                         const char *cdev_type, size_t size)
 {
-       struct thermal_cooling_device *cdev = to_cooling_device(dev);
-       unsigned long state;
-       int ret;
+       struct thermal_cooling_device *cdev = NULL;
 
-       ret = cdev->ops->get_max_state(cdev, &state);
-       if (ret)
-               return ret;
-       return sprintf(buf, "%ld\n", state);
+       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);
+       }
+       mutex_unlock(&thermal_list_lock);
 }
 
-static ssize_t
-thermal_cooling_device_cur_state_show(struct device *dev,
-                                     struct device_attribute *attr, char *buf)
+/*
+ * Device management section: cooling devices, zones devices, and binding
+ *
+ * Set of functions provided by the thermal core for:
+ * - cooling devices lifecycle: registration, unregistration,
+ *                             binding, and unbinding.
+ * - thermal zone devices lifecycle: registration, unregistration,
+ *                                  binding, and unbinding.
+ */
+static int get_idr(struct idr *idr, struct mutex *lock, int *id)
 {
-       struct thermal_cooling_device *cdev = to_cooling_device(dev);
-       unsigned long state;
        int ret;
 
-       ret = cdev->ops->get_cur_state(cdev, &state);
-       if (ret)
+       if (lock)
+               mutex_lock(lock);
+       ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
+       if (lock)
+               mutex_unlock(lock);
+       if (unlikely(ret < 0))
                return ret;
-       return sprintf(buf, "%ld\n", state);
-}
-
-static ssize_t
-thermal_cooling_device_cur_state_store(struct device *dev,
-                                      struct device_attribute *attr,
-                                      const char *buf, size_t count)
-{
-       struct thermal_cooling_device *cdev = to_cooling_device(dev);
-       unsigned long state;
-       int result;
-
-       if (!sscanf(buf, "%ld\n", &state))
-               return -EINVAL;
-
-       if ((long)state < 0)
-               return -EINVAL;
-
-       result = cdev->ops->set_cur_state(cdev, state);
-       if (result)
-               return result;
-       return count;
-}
-
-static struct device_attribute dev_attr_cdev_type =
-__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
-static DEVICE_ATTR(max_state, 0444,
-                  thermal_cooling_device_max_state_show, NULL);
-static DEVICE_ATTR(cur_state, 0644,
-                  thermal_cooling_device_cur_state_show,
-                  thermal_cooling_device_cur_state_store);
-
-static ssize_t
-thermal_cooling_device_trip_point_show(struct device *dev,
-                                      struct device_attribute *attr, char *buf)
-{
-       struct thermal_instance *instance;
-
-       instance =
-           container_of(attr, struct thermal_instance, attr);
-
-       if (instance->trip == THERMAL_TRIPS_NONE)
-               return sprintf(buf, "-1\n");
-       else
-               return sprintf(buf, "%d\n", instance->trip);
-}
-
-static struct attribute *cooling_device_attrs[] = {
-       &dev_attr_cdev_type.attr,
-       &dev_attr_max_state.attr,
-       &dev_attr_cur_state.attr,
-       NULL,
-};
-
-static const struct attribute_group cooling_device_attr_group = {
-       .attrs = cooling_device_attrs,
-};
-
-static const struct attribute_group *cooling_device_attr_groups[] = {
-       &cooling_device_attr_group,
-       NULL,
-};
-
-static ssize_t
-thermal_cooling_device_weight_show(struct device *dev,
-                                  struct device_attribute *attr, char *buf)
-{
-       struct thermal_instance *instance;
-
-       instance = container_of(attr, struct thermal_instance, weight_attr);
-
-       return sprintf(buf, "%d\n", instance->weight);
+       *id = ret;
+       return 0;
 }
 
-static ssize_t
-thermal_cooling_device_weight_store(struct device *dev,
-                                   struct device_attribute *attr,
-                                   const char *buf, size_t count)
-{
-       struct thermal_instance *instance;
-       int ret, weight;
-
-       ret = kstrtoint(buf, 0, &weight);
-       if (ret)
-               return ret;
-
-       instance = container_of(attr, struct thermal_instance, weight_attr);
-       instance->weight = weight;
-
-       return count;
+static void release_idr(struct idr *idr, struct mutex *lock, int id)
+{
+       if (lock)
+               mutex_lock(lock);
+       idr_remove(idr, id);
+       if (lock)
+               mutex_unlock(lock);
 }
-/* Device management */
 
 /**
  * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone
@@ -1358,8 +674,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        if (lower > upper || upper > max_state)
                return -EINVAL;
 
-       dev =
-           kzalloc(sizeof(struct thermal_instance), GFP_KERNEL);
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
                return -ENOMEM;
        dev->tz = tz;
@@ -1402,10 +717,10 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        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) {
-               result = -EEXIST;
-               break;
-       }
+               if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
+                       result = -EEXIST;
+                       break;
+               }
        if (!result) {
                list_add_tail(&dev->tz_node, &tz->thermal_instances);
                list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
@@ -1485,8 +800,8 @@ static void thermal_release(struct device *dev)
                     sizeof("thermal_zone") - 1)) {
                tz = to_thermal_zone(dev);
                kfree(tz);
-       } else if(!strncmp(dev_name(dev), "cooling_device",
-                       sizeof("cooling_device") - 1)){
+       } else if (!strncmp(dev_name(dev), "cooling_device",
+                           sizeof("cooling_device") - 1)) {
                cdev = to_cooling_device(dev);
                kfree(cdev);
        }
@@ -1497,6 +812,78 @@ static struct class thermal_class = {
        .dev_release = thermal_release,
 };
 
+static inline
+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,
+                  unsigned long *limits,
+                  unsigned int weight)
+{
+       int i, ret;
+
+       for (i = 0; i < tz->trips; i++) {
+               if (mask & (1 << i)) {
+                       unsigned long upper, lower;
+
+                       upper = THERMAL_NO_LIMIT;
+                       lower = THERMAL_NO_LIMIT;
+                       if (limits) {
+                               lower = limits[i * 2];
+                               upper = limits[i * 2 + 1];
+                       }
+                       ret = thermal_zone_bind_cooling_device(tz, i, cdev,
+                                                              upper, lower,
+                                                              weight);
+                       if (ret)
+                               print_bind_err_msg(tz, cdev, ret);
+               }
+       }
+}
+
+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->ops->bind) {
+                       ret = pos->ops->bind(pos, cdev);
+                       if (ret)
+                               print_bind_err_msg(pos, cdev, ret);
+                       continue;
+               }
+
+               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,
+                              tzp->tbp[i].binding_limits,
+                              tzp->tbp[i].weight);
+               }
+       }
+
+       mutex_unlock(&thermal_list_lock);
+}
+
 /**
  * __thermal_cooling_device_register() - register a new thermal cooling device
  * @np:                a pointer to a device tree node.
@@ -1529,7 +916,7 @@ __thermal_cooling_device_register(struct device_node *np,
            !ops->set_cur_state)
                return ERR_PTR(-EINVAL);
 
-       cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
+       cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
        if (!cdev)
                return ERR_PTR(-ENOMEM);
 
@@ -1546,7 +933,7 @@ __thermal_cooling_device_register(struct device_node *np,
        cdev->ops = ops;
        cdev->updated = false;
        cdev->device.class = &thermal_class;
-       cdev->device.groups = cooling_device_attr_groups;
+       thermal_cooling_device_setup_sysfs(cdev);
        cdev->devdata = devdata;
        dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
        result = device_register(&cdev->device);
@@ -1619,12 +1006,22 @@ thermal_of_cooling_device_register(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register);
 
+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);
+}
+
 /**
- * thermal_cooling_device_unregister - removes the registered thermal cooling device
+ * thermal_cooling_device_unregister - removes a thermal cooling device
  * @cdev:      the thermal cooling device to remove.
  *
- * thermal_cooling_device_unregister() must be called when the device is no
- * longer needed.
+ * thermal_cooling_device_unregister() must be called when a registered
+ * thermal cooling device is no longer needed.
  */
 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
 {
@@ -1638,8 +1035,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
 
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(pos, &thermal_cdev_list, node)
-           if (pos == cdev)
-               break;
+               if (pos == cdev)
+                       break;
        if (pos != cdev) {
                /* thermal cooling device not found */
                mutex_unlock(&thermal_list_lock);
@@ -1668,171 +1065,49 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
 
        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);
-       device_remove_file(&cdev->device, &dev_attr_cur_state);
-
        release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
        device_unregister(&cdev->device);
-       return;
 }
 EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
 
-void thermal_cdev_update(struct thermal_cooling_device *cdev)
+static void bind_tz(struct thermal_zone_device *tz)
 {
-       struct thermal_instance *instance;
-       unsigned long target = 0;
+       int i, ret;
+       struct thermal_cooling_device *pos = NULL;
+       const struct thermal_zone_params *tzp = tz->tzp;
 
-       mutex_lock(&cdev->lock);
-       /* cooling device is updated*/
-       if (cdev->updated) {
-               mutex_unlock(&cdev->lock);
+       if (!tzp && !tz->ops->bind)
                return;
-       }
-
-       /* Make sure cdev enters the deepest cooling state */
-       list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
-               dev_dbg(&cdev->device, "zone%d->target=%lu\n",
-                               instance->tz->id, instance->target);
-               if (instance->target == THERMAL_NO_TARGET)
-                       continue;
-               if (instance->target > target)
-                       target = instance->target;
-       }
-       cdev->ops->set_cur_state(cdev, target);
-       cdev->updated = true;
-       mutex_unlock(&cdev->lock);
-       trace_cdev_update(cdev, target);
-       dev_dbg(&cdev->device, "set to state %lu\n", target);
-}
-EXPORT_SYMBOL(thermal_cdev_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);
-}
-EXPORT_SYMBOL_GPL(thermal_notify_framework);
-
-/**
- * create_trip_attrs() - create attributes for trip points
- * @tz:                the thermal zone device
- * @mask:      Writeable trip point bitmap.
- *
- * helper function to instantiate sysfs entries for every trip
- * point and its properties of a struct thermal_zone_device.
- *
- * Return: 0 on success, the proper error value otherwise.
- */
-static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
-{
-       int indx;
-       int size = sizeof(struct thermal_attr) * tz->trips;
 
-       tz->trip_type_attrs = kzalloc(size, GFP_KERNEL);
-       if (!tz->trip_type_attrs)
-               return -ENOMEM;
-
-       tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL);
-       if (!tz->trip_temp_attrs) {
-               kfree(tz->trip_type_attrs);
-               return -ENOMEM;
-       }
+       mutex_lock(&thermal_list_lock);
 
-       if (tz->ops->get_trip_hyst) {
-               tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL);
-               if (!tz->trip_hyst_attrs) {
-                       kfree(tz->trip_type_attrs);
-                       kfree(tz->trip_temp_attrs);
-                       return -ENOMEM;
+       /* If there is ops->bind, try to use ops->bind */
+       if (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;
 
-       for (indx = 0; indx < tz->trips; indx++) {
-               /* create trip type attribute */
-               snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH,
-                        "trip_point_%d_type", indx);
-
-               sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr);
-               tz->trip_type_attrs[indx].attr.attr.name =
-                                               tz->trip_type_attrs[indx].name;
-               tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO;
-               tz->trip_type_attrs[indx].attr.show = trip_point_type_show;
-
-               device_create_file(&tz->device,
-                                  &tz->trip_type_attrs[indx].attr);
-
-               /* create trip temp attribute */
-               snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH,
-                        "trip_point_%d_temp", indx);
-
-               sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr);
-               tz->trip_temp_attrs[indx].attr.attr.name =
-                                               tz->trip_temp_attrs[indx].name;
-               tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO;
-               tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show;
-               if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) &&
-                   mask & (1 << indx)) {
-                       tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR;
-                       tz->trip_temp_attrs[indx].attr.store =
-                                                       trip_point_temp_store;
-               }
-
-               device_create_file(&tz->device,
-                                  &tz->trip_temp_attrs[indx].attr);
-
-               /* create Optional trip hyst attribute */
-               if (!tz->ops->get_trip_hyst)
-                       continue;
-               snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH,
-                        "trip_point_%d_hyst", indx);
-
-               sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr);
-               tz->trip_hyst_attrs[indx].attr.attr.name =
-                                       tz->trip_hyst_attrs[indx].name;
-               tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO;
-               tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show;
-               if (tz->ops->set_trip_hyst) {
-                       tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR;
-                       tz->trip_hyst_attrs[indx].attr.store =
-                                       trip_point_hyst_store;
+       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,
+                              tzp->tbp[i].binding_limits,
+                              tzp->tbp[i].weight);
                }
-
-               device_create_file(&tz->device,
-                                  &tz->trip_hyst_attrs[indx].attr);
-       }
-       return 0;
-}
-
-static void remove_trip_attrs(struct thermal_zone_device *tz)
-{
-       int indx;
-
-       for (indx = 0; indx < tz->trips; indx++) {
-               device_remove_file(&tz->device,
-                                  &tz->trip_type_attrs[indx].attr);
-               device_remove_file(&tz->device,
-                                  &tz->trip_temp_attrs[indx].attr);
-               if (tz->ops->get_trip_hyst)
-                       device_remove_file(&tz->device,
-                                 &tz->trip_hyst_attrs[indx].attr);
        }
-       kfree(tz->trip_type_attrs);
-       kfree(tz->trip_temp_attrs);
-       kfree(tz->trip_hyst_attrs);
+exit:
+       mutex_unlock(&thermal_list_lock);
 }
 
 /**
@@ -1859,20 +1134,22 @@ static void remove_trip_attrs(struct thermal_zone_device *tz)
  * in case of error, an ERR_PTR. Caller must check return value with
  * 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)
+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)
 {
        struct thermal_zone_device *tz;
        enum thermal_trip_type trip_type;
        int trip_temp;
        int result;
        int count;
-       int passive = 0;
        struct thermal_governor *governor;
 
+       if (!type || strlen(type) == 0)
+               return ERR_PTR(-EINVAL);
+
        if (type && strlen(type) >= THERMAL_NAME_LENGTH)
                return ERR_PTR(-EINVAL);
 
@@ -1885,7 +1162,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
                return ERR_PTR(-EINVAL);
 
-       tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
+       tz = kzalloc(sizeof(*tz), GFP_KERNEL);
        if (!tz)
                return ERR_PTR(-ENOMEM);
 
@@ -1898,7 +1175,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
                return ERR_PTR(result);
        }
 
-       strlcpy(tz->type, type ? : "", sizeof(tz->type));
+       strlcpy(tz->type, type, sizeof(tz->type));
        tz->ops = ops;
        tz->tzp = tzp;
        tz->device.class = &thermal_class;
@@ -1906,6 +1183,13 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        tz->trips = trips;
        tz->passive_delay = passive_delay;
        tz->polling_delay = polling_delay;
+
+       /* sys I/F */
+       /* Add nodes that are always present via .groups */
+       result = thermal_zone_create_device_groups(tz, mask);
+       if (result)
+               goto unregister;
+
        /* A new thermal zone needs to be updated anyway. */
        atomic_set(&tz->need_update, 1);
 
@@ -1917,32 +1201,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
                return ERR_PTR(result);
        }
 
-       /* sys I/F */
-       if (type) {
-               result = device_create_file(&tz->device, &dev_attr_type);
-               if (result)
-                       goto unregister;
-       }
-
-       result = device_create_file(&tz->device, &dev_attr_temp);
-       if (result)
-               goto unregister;
-
-       if (ops->get_mode) {
-               result = device_create_file(&tz->device, &dev_attr_mode);
-               if (result)
-                       goto unregister;
-       }
-
-       result = create_trip_attrs(tz, mask);
-       if (result)
-               goto unregister;
-
        for (count = 0; count < trips; count++) {
                if (tz->ops->get_trip_type(tz, count, &trip_type))
                        set_bit(count, &tz->trips_disabled);
-               if (trip_type == THERMAL_TRIP_PASSIVE)
-                       passive = 1;
                if (tz->ops->get_trip_temp(tz, count, &trip_temp))
                        set_bit(count, &tz->trips_disabled);
                /* Check for bogus trip points */
@@ -1950,33 +1211,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
                        set_bit(count, &tz->trips_disabled);
        }
 
-       if (!passive) {
-               result = device_create_file(&tz->device, &dev_attr_passive);
-               if (result)
-                       goto unregister;
-       }
-
-       if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) {
-               result = device_create_file(&tz->device, &dev_attr_emul_temp);
-               if (result)
-                       goto unregister;
-       }
-
-       /* Create policy attribute */
-       result = device_create_file(&tz->device, &dev_attr_policy);
-       if (result)
-               goto unregister;
-
-       /* Add thermal zone params */
-       result = create_tzp_attrs(&tz->device);
-       if (result)
-               goto unregister;
-
-       /* Create available_policies attribute */
-       result = device_create_file(&tz->device, &dev_attr_available_policies);
-       if (result)
-               goto unregister;
-
        /* Update 'this' zone's governor information */
        mutex_lock(&thermal_governor_lock);
 
@@ -2006,7 +1240,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        /* Bind cooling devices for this zone */
        bind_tz(tz);
 
-       INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
+       INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
 
        thermal_zone_device_reset(tz);
        /* Update the new thermal zone and mark it as already updated. */
@@ -2040,8 +1274,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(pos, &thermal_tz_list, node)
-           if (pos == tz)
-               break;
+               if (pos == tz)
+                       break;
        if (pos != tz) {
                /* thermal zone device not found */
                mutex_unlock(&thermal_list_lock);
@@ -2071,14 +1305,10 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 
        thermal_zone_device_set_polling(tz, 0);
 
-       if (tz->type[0])
-               device_remove_file(&tz->device, &dev_attr_type);
-       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);
-       device_remove_file(&tz->device, &dev_attr_available_policies);
-       remove_trip_attrs(tz);
+       kfree(tz->trip_type_attrs);
+       kfree(tz->trip_temp_attrs);
+       kfree(tz->trip_hyst_attrs);
+       kfree(tz->trips_attribute_group.attrs);
        thermal_set_governor(tz, NULL);
 
        thermal_remove_hwmon_sysfs(tz);
@@ -2086,7 +1316,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
        idr_destroy(&tz->idr);
        mutex_destroy(&tz->lock);
        device_unregister(&tz->device);
-       return;
+       kfree(tz->device.groups);
 }
 EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
 
@@ -2128,36 +1358,6 @@ exit:
 }
 EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
 
-/**
- * thermal_zone_get_slope - return the slope attribute of the thermal zone
- * @tz: thermal zone device with the slope attribute
- *
- * Return: If the thermal zone device has a slope attribute, return it, else
- * return 1.
- */
-int thermal_zone_get_slope(struct thermal_zone_device *tz)
-{
-       if (tz && tz->tzp)
-               return tz->tzp->slope;
-       return 1;
-}
-EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
-
-/**
- * thermal_zone_get_offset - return the offset attribute of the thermal zone
- * @tz: thermal zone device with the offset attribute
- *
- * Return: If the thermal zone device has a offset attribute, return it, else
- * return 0.
- */
-int thermal_zone_get_offset(struct thermal_zone_device *tz)
-{
-       if (tz && tz->tzp)
-               return tz->tzp->offset;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
-
 #ifdef CONFIG_NET
 static const struct genl_multicast_group thermal_event_mcgrps[] = {
        { .name = THERMAL_GENL_MCAST_GROUP_NAME, },
@@ -2173,7 +1373,7 @@ static struct genl_family thermal_event_genl_family __ro_after_init = {
 };
 
 int thermal_generate_netlink_event(struct thermal_zone_device *tz,
-                                       enum events event)
+                                  enum events event)
 {
        struct sk_buff *skb;
        struct nlattr *attr;
@@ -2249,40 +1449,8 @@ static inline int genetlink_init(void) { return 0; }
 static inline void genetlink_exit(void) {}
 #endif /* !CONFIG_NET */
 
-static int __init thermal_register_governors(void)
-{
-       int result;
-
-       result = thermal_gov_step_wise_register();
-       if (result)
-               return result;
-
-       result = thermal_gov_fair_share_register();
-       if (result)
-               return result;
-
-       result = thermal_gov_bang_bang_register();
-       if (result)
-               return result;
-
-       result = thermal_gov_user_space_register();
-       if (result)
-               return result;
-
-       return thermal_gov_power_allocator_register();
-}
-
-static void thermal_unregister_governors(void)
-{
-       thermal_gov_step_wise_unregister();
-       thermal_gov_fair_share_unregister();
-       thermal_gov_bang_bang_unregister();
-       thermal_gov_user_space_unregister();
-       thermal_gov_power_allocator_unregister();
-}
-
 static int thermal_pm_notify(struct notifier_block *nb,
-                               unsigned long mode, void *_unused)
+                            unsigned long mode, void *_unused)
 {
        struct thermal_zone_device *tz;