summaryrefslogtreecommitdiff
path: root/drivers/thermal
diff options
context:
space:
mode:
authorMatthew Garrett <mjg59@srcf.ucam.org>2008-12-03 18:00:38 +0000
committerLen Brown <len.brown@intel.com>2009-03-27 16:58:22 -0400
commit03a971a2899886006f19f3495973bbd646d8bdae (patch)
tree6b82e8d67ca5eaf94fce4bd419276d90ac281c8e /drivers/thermal
parentf6f5c45e06e86d94621cd5c1c4508bdee1952fc8 (diff)
thermal: support forcing support for passive cooling
Due to poor thermal design or Linux driving hardware outside its thermal envelope, some systems will reach critical temperature and shut down under high load. This patch adds support for forcing a polling-based passive trip point if the firmware doesn't provide one. The assumption is made that the processor is the most practical means to reduce the dynamic heat generation, so hitting the passive thermal limit will cause the CPU to be throttled until the temperature stabalises around the defined value. UI is provided via a "passive" sysfs entry in the thermal zone directory. It accepts a decimal value in millidegrees celsius, or "0" to disable the functionality. Default behaviour is for this functionality to be disabled. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/thermal')
-rw-r--r--drivers/thermal/thermal_sys.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 6378741882f3..d0b093b66adc 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -214,9 +214,69 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%ld\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;
+
+ 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);
+ }
+ mutex_unlock(&thermal_list_lock);
+ } 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->tc1 = 1;
+ tz->tc2 = 1;
+
+ if (!tz->passive_delay)
+ tz->passive_delay = 1000;
+
+ if (!tz->polling_delay)
+ tz->polling_delay = 10000;
+
+ tz->forced_passive = state;
+
+ thermal_zone_device_update(tz);
+
+ 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 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 struct device_attribute trip_point_attrs[] = {
__ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
@@ -939,6 +999,11 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
break;
}
}
+
+ if (tz->forced_passive)
+ thermal_zone_device_passive(tz, temp, tz->forced_passive,
+ THERMAL_TRIPS_NONE);
+
tz->last_temperature = temp;
if (tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay);
@@ -977,8 +1042,10 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
{
struct thermal_zone_device *tz;
struct thermal_cooling_device *pos;
+ enum thermal_trip_type trip_type;
int result;
int count;
+ int passive = 0;
if (strlen(type) >= THERMAL_NAME_LENGTH)
return ERR_PTR(-EINVAL);
@@ -1041,8 +1108,18 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
TRIP_POINT_ATTR_ADD(&tz->device, count, result);
if (result)
goto unregister;
+ tz->ops->get_trip_type(tz, count, &trip_type);
+ if (trip_type == THERMAL_TRIP_PASSIVE)
+ passive = 1;
}
+ if (!passive)
+ result = device_create_file(&tz->device,
+ &dev_attr_passive);
+
+ if (result)
+ goto unregister;
+
result = thermal_add_hwmon_sysfs(tz);
if (result)
goto unregister;