thermal: export thermal_zone_parameters to sysfs
[deliverable/linux.git] / drivers / thermal / thermal_core.c
index a6cb9b78b6293bc7725e5859571d5528c3a7ef97..962de1847cc08b40855df8c532e9f7e2d5ed0766 100644 (file)
@@ -75,6 +75,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 +159,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 +193,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);
@@ -770,8 +829,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);
@@ -815,6 +875,154 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
 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);
+#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,
+};
+
+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_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);
@@ -922,6 +1130,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 */
 
 /**
@@ -1016,6 +1252,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)
@@ -1033,6 +1279,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);
@@ -1387,7 +1635,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;
@@ -1464,7 +1713,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;
@@ -1472,6 +1721,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);
@@ -1558,13 +1808,24 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        if (result)
                goto unregister;
 
+       /* Add thermal zone params */
+       result = create_tzp_attrs(&tz->device);
+       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);
 
@@ -1653,7 +1914,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
                device_remove_file(&tz->device, &dev_attr_mode);
        device_remove_file(&tz->device, &dev_attr_policy);
        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);
@@ -1809,7 +2070,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)
@@ -1818,6 +2083,7 @@ 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 __init thermal_init(void)
This page took 0.09095 seconds and 5 git commands to generate.