thermal: sysfs: Add a new sysfs node emul_temp for thermal emulation
authorAmit Daniel Kachhap <amit.daniel@samsung.com>
Mon, 4 Feb 2013 00:30:15 +0000 (00:30 +0000)
committerZhang Rui <rui.zhang@intel.com>
Wed, 6 Feb 2013 05:45:42 +0000 (13:45 +0800)
This patch adds support to set the emulated temperature method in
thermal zone (sensor). After setting this feature thermal zone may
report this temperature and not the actual temperature. The emulation
implementation may be based on sensor capability through platform
specific handler or pure software emulation if no platform handler defined.

This is useful in debugging different temperature threshold and its
associated cooling action. Critical threshold's cannot be emulated.
Writing 0 on this node should disable emulation.

Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Documentation/thermal/sysfs-api.txt
drivers/thermal/Kconfig
drivers/thermal/thermal_sys.c
include/linux/thermal.h

index 526d4b90d6c197c2995f1456de78d9ca053cbe3e..6859661c9d31be090a78e5a2c35a9a5fdad7ca80 100644 (file)
@@ -55,6 +55,8 @@ temperature) and throttle appropriate devices.
        .get_trip_type: get the type of certain trip point.
        .get_trip_temp: get the temperature above which the certain trip point
                        will be fired.
+       .set_emul_temp: set the emulation temperature which helps in debugging
+                       different threshold temperature points.
 
 1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
 
@@ -153,6 +155,7 @@ Thermal zone device sys I/F, created once it's registered:
     |---trip_point_[0-*]_temp: Trip point temperature
     |---trip_point_[0-*]_type: Trip point type
     |---trip_point_[0-*]_hyst: Hysteresis value for this trip point
+    |---emul_temp:             Emulated temperature set node
 
 Thermal cooling device sys I/F, created once it's registered:
 /sys/class/thermal/cooling_device[0-*]:
@@ -252,6 +255,16 @@ passive
        Valid values: 0 (disabled) or greater than 1000
        RW, Optional
 
+emul_temp
+       Interface to set the emulated temperature method in thermal zone
+       (sensor). After setting this temperature, the thermal zone may pass
+       this temperature to platform emulation function if registered or
+       cache it locally. This is useful in debugging different temperature
+       threshold and its associated cooling action. This is write only node
+       and writing 0 on this node should disable emulation.
+       Unit: millidegree Celsius
+       WO, Optional
+
 *****************************
 * Cooling device attributes *
 *****************************
index faf38c522fa8bb5c7a5f3cb90de93af84af7c639..e4cf7fbc3a598f2764a7b09b0638030abafb9172 100644 (file)
@@ -78,6 +78,14 @@ config CPU_THERMAL
          and not the ACPI interface.
          If you want this support, you should say Y here.
 
+config THERMAL_EMULATION
+       bool "Thermal emulation mode support"
+       help
+         Enable this option to make a emul_temp sysfs node in thermal zone
+         directory to support temperature emulation. With emulation sysfs node,
+         user can manually input temperature and test the different trip
+         threshold behaviour for simulation purpose.
+
 config SPEAR_THERMAL
        bool "SPEAr thermal sensor driver"
        depends on PLAT_SPEAR
index 0a1bf6b032ea8649a322eef1a800215570e0b391..0675687c6de85f469710c051407229d8387b8483 100644 (file)
@@ -378,24 +378,54 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
        monitor_thermal_zone(tz);
 }
 
+static int thermal_zone_get_temp(struct thermal_zone_device *tz,
+                               unsigned long *temp)
+{
+       int ret = 0, count;
+       unsigned long crit_temp = -1UL;
+       enum thermal_trip_type type;
+
+       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 (*temp < crit_temp)
+               *temp = tz->emul_temperature;
+skip_emul:
+#endif
+       mutex_unlock(&tz->lock);
+       return ret;
+}
+
 static void update_temperature(struct thermal_zone_device *tz)
 {
        long temp;
        int ret;
 
-       mutex_lock(&tz->lock);
-
-       ret = tz->ops->get_temp(tz, &temp);
+       ret = thermal_zone_get_temp(tz, &temp);
        if (ret) {
                dev_warn(&tz->device, "failed to read out thermal zone %d\n",
                         tz->id);
-               goto exit;
+               return;
        }
 
+       mutex_lock(&tz->lock);
        tz->last_temperature = tz->temperature;
        tz->temperature = temp;
-
-exit:
        mutex_unlock(&tz->lock);
 }
 
@@ -438,10 +468,7 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf)
        long temperature;
        int ret;
 
-       if (!tz->ops->get_temp)
-               return -EPERM;
-
-       ret = tz->ops->get_temp(tz, &temperature);
+       ret = thermal_zone_get_temp(tz, &temperature);
 
        if (ret)
                return ret;
@@ -701,6 +728,31 @@ 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
+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;
+       unsigned long temperature;
+
+       if (kstrtoul(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);
+       }
+
+       return ret ? ret : count;
+}
+static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
+#endif/*CONFIG_THERMAL_EMULATION*/
+
 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);
@@ -843,7 +895,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
                                       temp_input);
        struct thermal_zone_device *tz = temp->tz;
 
-       ret = tz->ops->get_temp(tz, &temperature);
+       ret = thermal_zone_get_temp(tz, &temperature);
 
        if (ret)
                return ret;
@@ -1596,6 +1648,11 @@ 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
        /* Create policy attribute */
        result = device_create_file(&tz->device, &dev_attr_policy);
        if (result)
index 9b78f8c6f77374ee743c2adc41cf31d592d37546..f0bd7f90a90d45d3aeeb3aed8bc2d2f8295c8be0 100644 (file)
@@ -123,6 +123,7 @@ struct thermal_zone_device_ops {
        int (*set_trip_hyst) (struct thermal_zone_device *, int,
                              unsigned long);
        int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *);
+       int (*set_emul_temp) (struct thermal_zone_device *, unsigned long);
        int (*get_trend) (struct thermal_zone_device *, int,
                          enum thermal_trend *);
        int (*notify) (struct thermal_zone_device *, int,
@@ -165,6 +166,7 @@ struct thermal_zone_device {
        int polling_delay;
        int temperature;
        int last_temperature;
+       int emul_temperature;
        int passive;
        unsigned int forced_passive;
        const struct thermal_zone_device_ops *ops;