These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / thermal / thermal_core.c
index 4108db7..ba08b55 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/of.h>
 #include <net/netlink.h>
 #include <net/genetlink.h>
+#include <linux/suspend.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/thermal.h>
@@ -59,6 +60,8 @@ static LIST_HEAD(thermal_governor_list);
 static DEFINE_MUTEX(thermal_list_lock);
 static DEFINE_MUTEX(thermal_governor_lock);
 
+static atomic_t in_suspend;
+
 static struct thermal_governor *def_governor;
 
 static struct thermal_governor *__find_governor(const char *name)
@@ -75,6 +78,58 @@ static struct thermal_governor *__find_governor(const char *name)
        return NULL;
 }
 
+/**
+ * bind_previous_governor() - bind the previous governor of the thermal zone
+ * @tz:                a valid pointer to a struct thermal_zone_device
+ * @failed_gov_name:   the name of the governor that failed to register
+ *
+ * Register the previous governor of the thermal zone after a new
+ * governor has failed to be bound.
+ */
+static void bind_previous_governor(struct thermal_zone_device *tz,
+                                  const char *failed_gov_name)
+{
+       if (tz->governor && tz->governor->bind_to_tz) {
+               if (tz->governor->bind_to_tz(tz)) {
+                       dev_err(&tz->device,
+                               "governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n",
+                               failed_gov_name, tz->governor->name, tz->type);
+                       tz->governor = NULL;
+               }
+       }
+}
+
+/**
+ * thermal_set_governor() - Switch to another governor
+ * @tz:                a valid pointer to a struct thermal_zone_device
+ * @new_gov:   pointer to the new governor
+ *
+ * Change the governor of thermal zone @tz.
+ *
+ * Return: 0 on success, an error if the new governor's bind_to_tz() failed.
+ */
+static int thermal_set_governor(struct thermal_zone_device *tz,
+                               struct thermal_governor *new_gov)
+{
+       int ret = 0;
+
+       if (tz->governor && tz->governor->unbind_from_tz)
+               tz->governor->unbind_from_tz(tz);
+
+       if (new_gov && new_gov->bind_to_tz) {
+               ret = new_gov->bind_to_tz(tz);
+               if (ret) {
+                       bind_previous_governor(tz, new_gov->name);
+
+                       return ret;
+               }
+       }
+
+       tz->governor = new_gov;
+
+       return ret;
+}
+
 int thermal_register_governor(struct thermal_governor *governor)
 {
        int err;
@@ -107,8 +162,15 @@ int thermal_register_governor(struct thermal_governor *governor)
 
                name = pos->tzp->governor_name;
 
-               if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH))
-                       pos->governor = governor;
+               if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {
+                       int ret;
+
+                       ret = thermal_set_governor(pos, governor);
+                       if (ret)
+                               dev_err(&pos->device,
+                                       "Failed to set governor %s for thermal zone %s: %d\n",
+                                       governor->name, pos->type, ret);
+               }
        }
 
        mutex_unlock(&thermal_list_lock);
@@ -134,7 +196,7 @@ void thermal_unregister_governor(struct thermal_governor *governor)
        list_for_each_entry(pos, &thermal_tz_list, node) {
                if (!strncasecmp(pos->governor->name, governor->name,
                                                THERMAL_NAME_LENGTH))
-                       pos->governor = NULL;
+                       thermal_set_governor(pos, NULL);
        }
 
        mutex_unlock(&thermal_list_lock);
@@ -218,7 +280,8 @@ static void print_bind_err_msg(struct thermal_zone_device *tz,
 
 static void __bind(struct thermal_zone_device *tz, int mask,
                        struct thermal_cooling_device *cdev,
-                       unsigned long *limits)
+                       unsigned long *limits,
+                       unsigned int weight)
 {
        int i, ret;
 
@@ -233,7 +296,8 @@ static void __bind(struct thermal_zone_device *tz, int mask,
                                upper = limits[i * 2 + 1];
                        }
                        ret = thermal_zone_bind_cooling_device(tz, i, cdev,
-                                                              upper, lower);
+                                                              upper, lower,
+                                                              weight);
                        if (ret)
                                print_bind_err_msg(tz, cdev, ret);
                }
@@ -280,7 +344,8 @@ static void bind_cdev(struct thermal_cooling_device *cdev)
                                continue;
                        tzp->tbp[i].cdev = cdev;
                        __bind(pos, tzp->tbp[i].trip_mask, cdev,
-                              tzp->tbp[i].binding_limits);
+                              tzp->tbp[i].binding_limits,
+                              tzp->tbp[i].weight);
                }
        }
 
@@ -319,7 +384,8 @@ static void bind_tz(struct thermal_zone_device *tz)
                                continue;
                        tzp->tbp[i].cdev = pos;
                        __bind(tz, tzp->tbp[i].trip_mask, pos,
-                              tzp->tbp[i].binding_limits);
+                              tzp->tbp[i].binding_limits,
+                              tzp->tbp[i].weight);
                }
        }
 exit:
@@ -363,7 +429,7 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz,
 static void handle_critical_trips(struct thermal_zone_device *tz,
                                int trip, enum thermal_trip_type trip_type)
 {
-       long trip_temp;
+       int trip_temp;
 
        tz->ops->get_trip_temp(tz, trip, &trip_temp);
 
@@ -402,7 +468,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
 }
 
 /**
- * thermal_zone_get_temp() - returns its the temperature of thermal zone
+ * 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.
  *
@@ -411,14 +477,12 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
  *
  * Return: On success returns 0, an error code otherwise
  */
-int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
+int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
 {
        int ret = -EINVAL;
-#ifdef CONFIG_THERMAL_EMULATION
        int count;
-       unsigned long crit_temp = -1UL;
+       int crit_temp = INT_MAX;
        enum thermal_trip_type type;
-#endif
 
        if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
                goto exit;
@@ -426,25 +490,26 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
        mutex_lock(&tz->lock);
 
        ret = tz->ops->get_temp(tz, temp);
-#ifdef CONFIG_THERMAL_EMULATION
-       if (!tz->emul_temperature)
-               goto skip_emul;
-
-       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;
-               }
-       }
 
-       if (ret)
-               goto skip_emul;
+       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;
+                       }
+               }
 
-       if (*temp < crit_temp)
-               *temp = tz->emul_temperature;
-skip_emul:
-#endif
+               /*
+                * 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;
@@ -453,8 +518,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
 
 static void update_temperature(struct thermal_zone_device *tz)
 {
-       long temp;
-       int ret;
+       int temp, ret;
 
        ret = thermal_zone_get_temp(tz, &temp);
        if (ret) {
@@ -471,14 +535,31 @@ static void update_temperature(struct thermal_zone_device *tz)
        mutex_unlock(&tz->lock);
 
        trace_thermal_temperature(tz);
-       dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
-                               tz->last_temperature, tz->temperature);
+       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);
+}
+
+static void thermal_zone_device_reset(struct thermal_zone_device *tz)
+{
+       struct thermal_instance *pos;
+
+       tz->temperature = THERMAL_TEMP_INVALID;
+       tz->passive = 0;
+       list_for_each_entry(pos, &tz->thermal_instances, tz_node)
+               pos->initialized = false;
 }
 
 void thermal_zone_device_update(struct thermal_zone_device *tz)
 {
        int count;
 
+       if (atomic_read(&in_suspend))
+               return;
+
        if (!tz->ops->get_temp)
                return;
 
@@ -514,15 +595,14 @@ static ssize_t
 temp_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
-       long temperature;
-       int ret;
+       int temperature, ret;
 
        ret = thermal_zone_get_temp(tz, &temperature);
 
        if (ret)
                return ret;
 
-       return sprintf(buf, "%ld\n", temperature);
+       return sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -626,7 +706,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
        int trip, ret;
-       long temperature;
+       int temperature;
 
        if (!tz->ops->get_trip_temp)
                return -EPERM;
@@ -639,7 +719,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
        if (ret)
                return ret;
 
-       return sprintf(buf, "%ld\n", temperature);
+       return sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -648,7 +728,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
        int trip, ret;
-       unsigned long temperature;
+       int temperature;
 
        if (!tz->ops->set_trip_hyst)
                return -EPERM;
@@ -656,7 +736,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
        if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip))
                return -EINVAL;
 
-       if (kstrtoul(buf, 10, &temperature))
+       if (kstrtoint(buf, 10, &temperature))
                return -EINVAL;
 
        /*
@@ -675,7 +755,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
        int trip, ret;
-       unsigned long temperature;
+       int temperature;
 
        if (!tz->ops->get_trip_hyst)
                return -EPERM;
@@ -685,7 +765,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
 
        ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
 
-       return ret ? ret : sprintf(buf, "%ld\n", temperature);
+       return ret ? ret : sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -713,7 +793,8 @@ passive_store(struct device *dev, struct device_attribute *attr,
                                thermal_zone_bind_cooling_device(tz,
                                                THERMAL_TRIPS_NONE, cdev,
                                                THERMAL_NO_LIMIT,
-                                               THERMAL_NO_LIMIT);
+                                               THERMAL_NO_LIMIT,
+                                               THERMAL_WEIGHT_DEFAULT);
                }
                mutex_unlock(&thermal_list_lock);
                if (!tz->passive_delay)
@@ -765,8 +846,9 @@ policy_store(struct device *dev, struct device_attribute *attr,
        if (!gov)
                goto exit;
 
-       tz->governor = gov;
-       ret = count;
+       ret = thermal_set_governor(tz, gov);
+       if (!ret)
+               ret = count;
 
 exit:
        mutex_unlock(&tz->lock);
@@ -782,7 +864,27 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
        return sprintf(buf, "%s\n", tz->governor->name);
 }
 
-#ifdef CONFIG_THERMAL_EMULATION
+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)
@@ -808,13 +910,193 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
        return ret ? ret : count;
 }
 static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
-#endif/*CONFIG_THERMAL_EMULATION*/
+
+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, "%u\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_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)
+{
+       unsigned long max_state;
+       int ret;
+
+       if (!cdev_is_power_actor(cdev))
+               return -EINVAL;
+
+       ret = cdev->ops->get_max_state(cdev, &max_state);
+       if (ret)
+               return ret;
+
+       return cdev->ops->state2power(cdev, tz, max_state, min_power);
+}
+
+/**
+ * power_actor_set_power() - limit the maximum power that a cooling device can consume
+ * @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.
+ *
+ * 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)
+{
+       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;
+
+       instance->target = state;
+       cdev->updated = false;
+       thermal_cdev_update(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)        \
@@ -917,6 +1199,34 @@ static const struct attribute_group *cooling_device_attr_groups[] = {
        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);
+}
+
+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;
+}
 /* Device management */
 
 /**
@@ -931,6 +1241,9 @@ static const struct attribute_group *cooling_device_attr_groups[] = {
  * @lower:     the Minimum cooling state can be used for this trip point.
  *             THERMAL_NO_LIMIT means no lower limit,
  *             and the cooling device can be in cooling state 0.
+ * @weight:    The weight of the cooling device to be bound to the
+ *             thermal zone. Use THERMAL_WEIGHT_DEFAULT for the
+ *             default value
  *
  * This interface function bind a thermal cooling device to the certain trip
  * point of a thermal zone device.
@@ -941,7 +1254,8 @@ static const struct attribute_group *cooling_device_attr_groups[] = {
 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
                                     int trip,
                                     struct thermal_cooling_device *cdev,
-                                    unsigned long upper, unsigned long lower)
+                                    unsigned long upper, unsigned long lower,
+                                    unsigned int weight)
 {
        struct thermal_instance *dev;
        struct thermal_instance *pos;
@@ -986,6 +1300,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        dev->upper = upper;
        dev->lower = lower;
        dev->target = THERMAL_NO_TARGET;
+       dev->weight = weight;
 
        result = get_idr(&tz->idr, &tz->lock, &dev->id);
        if (result)
@@ -1006,6 +1321,16 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        if (result)
                goto remove_symbol_link;
 
+       sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id);
+       sysfs_attr_init(&dev->weight_attr.attr);
+       dev->weight_attr.attr.name = dev->weight_attr_name;
+       dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;
+       dev->weight_attr.show = thermal_cooling_device_weight_show;
+       dev->weight_attr.store = thermal_cooling_device_weight_store;
+       result = device_create_file(&tz->device, &dev->weight_attr);
+       if (result)
+               goto remove_trip_file;
+
        mutex_lock(&tz->lock);
        mutex_lock(&cdev->lock);
        list_for_each_entry(pos, &tz->thermal_instances, tz_node)
@@ -1016,6 +1341,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        if (!result) {
                list_add_tail(&dev->tz_node, &tz->thermal_instances);
                list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
+               atomic_set(&tz->need_update, 1);
        }
        mutex_unlock(&cdev->lock);
        mutex_unlock(&tz->lock);
@@ -1023,6 +1349,8 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
        if (!result)
                return 0;
 
+       device_remove_file(&tz->device, &dev->weight_attr);
+remove_trip_file:
        device_remove_file(&tz->device, &dev->attr);
 remove_symbol_link:
        sysfs_remove_link(&tz->device.kobj, dev->name);
@@ -1071,6 +1399,7 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
        return -ENODEV;
 
 unbind:
+       device_remove_file(&tz->device, &pos->weight_attr);
        device_remove_file(&tz->device, &pos->attr);
        sysfs_remove_link(&tz->device.kobj, pos->name);
        release_idr(&tz->idr, &tz->lock, pos->id);
@@ -1122,6 +1451,7 @@ __thermal_cooling_device_register(struct device_node *np,
                                  const struct thermal_cooling_device_ops *ops)
 {
        struct thermal_cooling_device *cdev;
+       struct thermal_zone_device *pos = NULL;
        int result;
 
        if (type && strlen(type) >= THERMAL_NAME_LENGTH)
@@ -1166,6 +1496,12 @@ __thermal_cooling_device_register(struct device_node *np,
        /* Update binding information for 'this' new cdev */
        bind_cdev(cdev);
 
+       mutex_lock(&thermal_list_lock);
+       list_for_each_entry(pos, &thermal_tz_list, node)
+               if (atomic_cmpxchg(&pos->need_update, 1, 0))
+                       thermal_zone_device_update(pos);
+       mutex_unlock(&thermal_list_lock);
+
        return cdev;
 }
 
@@ -1377,7 +1713,8 @@ static int create_trip_attrs(struct thermal_zone_device *tz, int mask)
                                                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 (mask & (1 << indx)) {
+               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;
@@ -1454,7 +1791,7 @@ 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,
        struct thermal_zone_device_ops *ops,
-       const struct thermal_zone_params *tzp,
+       struct thermal_zone_params *tzp,
        int passive_delay, int polling_delay)
 {
        struct thermal_zone_device *tz;
@@ -1462,6 +1799,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        int result;
        int count;
        int passive = 0;
+       struct thermal_governor *governor;
 
        if (type && strlen(type) >= THERMAL_NAME_LENGTH)
                return ERR_PTR(-EINVAL);
@@ -1496,6 +1834,8 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        tz->trips = trips;
        tz->passive_delay = passive_delay;
        tz->polling_delay = polling_delay;
+       /* 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);
@@ -1538,23 +1878,40 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
                        goto unregister;
        }
 
-#ifdef CONFIG_THERMAL_EMULATION
-       result = device_create_file(&tz->device, &dev_attr_emul_temp);
-       if (result)
-               goto unregister;
-#endif
+       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);
 
        if (tz->tzp)
-               tz->governor = __find_governor(tz->tzp->governor_name);
+               governor = __find_governor(tz->tzp->governor_name);
        else
-               tz->governor = def_governor;
+               governor = def_governor;
+
+       result = thermal_set_governor(tz, governor);
+       if (result) {
+               mutex_unlock(&thermal_governor_lock);
+               goto unregister;
+       }
 
        mutex_unlock(&thermal_governor_lock);
 
@@ -1573,10 +1930,10 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
 
        INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
 
-       if (!tz->ops->get_temp)
-               thermal_zone_device_set_polling(tz, 0);
-
-       thermal_zone_device_update(tz);
+       thermal_zone_device_reset(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);
 
        return tz;
 
@@ -1642,8 +1999,9 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
        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);
-       tz->governor = NULL;
+       thermal_set_governor(tz, NULL);
 
        thermal_remove_hwmon_sysfs(tz);
        release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
@@ -1799,7 +2157,11 @@ static int __init thermal_register_governors(void)
        if (result)
                return result;
 
-       return thermal_gov_user_space_register();
+       result = thermal_gov_user_space_register();
+       if (result)
+               return result;
+
+       return thermal_gov_power_allocator_register();
 }
 
 static void thermal_unregister_governors(void)
@@ -1808,8 +2170,39 @@ static void thermal_unregister_governors(void)
        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)
+{
+       struct thermal_zone_device *tz;
+
+       switch (mode) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_RESTORE_PREPARE:
+       case PM_SUSPEND_PREPARE:
+               atomic_set(&in_suspend, 1);
+               break;
+       case PM_POST_HIBERNATION:
+       case PM_POST_RESTORE:
+       case PM_POST_SUSPEND:
+               atomic_set(&in_suspend, 0);
+               list_for_each_entry(tz, &thermal_tz_list, node) {
+                       thermal_zone_device_reset(tz);
+                       thermal_zone_device_update(tz);
+               }
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block thermal_pm_nb = {
+       .notifier_call = thermal_pm_notify,
+};
+
 static int __init thermal_init(void)
 {
        int result;
@@ -1830,6 +2223,11 @@ static int __init thermal_init(void)
        if (result)
                goto exit_netlink;
 
+       result = register_pm_notifier(&thermal_pm_nb);
+       if (result)
+               pr_warn("Thermal: Can not register suspend notifier, return %d\n",
+                       result);
+
        return 0;
 
 exit_netlink:
@@ -1849,6 +2247,7 @@ error:
 
 static void __exit thermal_exit(void)
 {
+       unregister_pm_notifier(&thermal_pm_nb);
        of_thermal_destroy_zones();
        genetlink_exit();
        class_unregister(&thermal_class);