ACPI: register ACPI thermal zone as generic thermal zone devices
authorZhang Rui <rui.zhang@intel.com>
Thu, 17 Jan 2008 07:51:11 +0000 (15:51 +0800)
committerLen Brown <len.brown@intel.com>
Sat, 2 Feb 2008 04:12:19 +0000 (23:12 -0500)
Register ACPI thermal zone as thermal zone device.

the new sys I/F for ACPI thermal zone will be like this:

/sys/class/thermal:
|thermal_zone1:
|-----type: "ACPI thermal zone". RO
|-----temp: the current temperature. RO
|-----mode: the current working mode. RW.
the default value is "kernel"  which means  thermal
management is done by ACPI thermal driver.
"echo user > mode" prevents all the ACPI thermal driver
actions upon any trip points.
|-----trip_point_0_temp: the threshold of trip point 0. RO.
|-----trip_point_0_type: "critical". RO.
the type of trip point 0
This may be one of critical/hot/passive/active[x]
for an ACPI thermal zone.
...
|-----trip_point_3_temp:
|-----trip_point_3_type: "active[1]"

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Thomas Sujith <sujith.thomas@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
drivers/acpi/Kconfig
drivers/acpi/thermal.c

index ccf6ea9..5583729 100644 (file)
@@ -186,6 +186,7 @@ config ACPI_HOTPLUG_CPU
 config ACPI_THERMAL
        tristate "Thermal Zone"
        depends on ACPI_PROCESSOR
+       select THERMAL
        default y
        help
          This driver adds support for ACPI thermal zones.  Most mobile and
index 5f79b44..c6cfce4 100644 (file)
@@ -43,7 +43,7 @@
 #include <linux/seq_file.h>
 #include <linux/reboot.h>
 #include <asm/uaccess.h>
-
+#include <linux/thermal.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -195,6 +195,8 @@ struct acpi_thermal {
        struct acpi_thermal_trips trips;
        struct acpi_handle_list devices;
        struct timer_list timer;
+       struct thermal_zone_device *thermal_zone;
+       int tz_enabled;
        struct mutex lock;
 };
 
@@ -732,6 +734,9 @@ static void acpi_thermal_check(void *data)
        if (result)
                goto unlock;
 
+       if (!tz->tz_enabled)
+               goto unlock;
+
        memset(&tz->state, 0, sizeof(tz->state));
 
        /*
@@ -825,6 +830,273 @@ static void acpi_thermal_check(void *data)
        mutex_unlock(&tz->lock);
 }
 
+/* sys I/F for generic thermal sysfs support */
+static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+
+       if (!tz)
+               return -EINVAL;
+
+       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature));
+}
+
+static const char enabled[] = "kernel";
+static const char disabled[] = "user";
+static int thermal_get_mode(struct thermal_zone_device *thermal,
+                               char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+
+       if (!tz)
+               return -EINVAL;
+
+       return sprintf(buf, "%s\n", tz->tz_enabled ?
+                       enabled : disabled);
+}
+
+static int thermal_set_mode(struct thermal_zone_device *thermal,
+                               const char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+       int enable;
+
+       if (!tz)
+               return -EINVAL;
+
+       /*
+        * enable/disable thermal management from ACPI thermal driver
+        */
+       if (!strncmp(buf, enabled, sizeof enabled - 1))
+               enable = 1;
+       else if (!strncmp(buf, disabled, sizeof disabled - 1))
+               enable = 0;
+       else
+               return -EINVAL;
+
+       if (enable != tz->tz_enabled) {
+               tz->tz_enabled = enable;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "%s ACPI thermal control\n",
+                       tz->tz_enabled ? enabled : disabled));
+               acpi_thermal_check(tz);
+       }
+       return 0;
+}
+
+static int thermal_get_trip_type(struct thermal_zone_device *thermal,
+                                int trip, char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+       int i;
+
+       if (!tz || trip < 0)
+               return -EINVAL;
+
+       if (tz->trips.critical.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "critical\n");
+               trip--;
+       }
+
+       if (tz->trips.hot.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "hot\n");
+               trip--;
+       }
+
+       if (tz->trips.passive.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "passive\n");
+               trip--;
+       }
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
+               tz->trips.active[i].flags.valid; i++) {
+               if (!trip)
+                       return sprintf(buf, "active%d\n", i);
+               trip--;
+       }
+
+       return -EINVAL;
+}
+
+static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
+                                int trip, char *buf)
+{
+       struct acpi_thermal *tz = thermal->devdata;
+       int i;
+
+       if (!tz || trip < 0)
+               return -EINVAL;
+
+       if (tz->trips.critical.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                               tz->trips.critical.temperature));
+               trip--;
+       }
+
+       if (tz->trips.hot.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                                       tz->trips.hot.temperature));
+               trip--;
+       }
+
+       if (tz->trips.passive.flags.valid) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                                       tz->trips.passive.temperature));
+               trip--;
+       }
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
+               tz->trips.active[i].flags.valid; i++) {
+               if (!trip)
+                       return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
+                                       tz->trips.active[i].temperature));
+               trip--;
+       }
+
+       return -EINVAL;
+}
+
+typedef int (*cb)(struct thermal_zone_device *, int,
+                 struct thermal_cooling_device *);
+static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
+                                       struct thermal_cooling_device *cdev,
+                                       cb action)
+{
+       struct acpi_device *device = cdev->devdata;
+       struct acpi_thermal *tz = thermal->devdata;
+       acpi_handle handle = device->handle;
+       int i;
+       int j;
+       int trip = -1;
+       int result = 0;
+
+       if (tz->trips.critical.flags.valid)
+               trip++;
+
+       if (tz->trips.hot.flags.valid)
+               trip++;
+
+       if (tz->trips.passive.flags.valid) {
+               trip++;
+               for (i = 0; i < tz->trips.passive.devices.count;
+                   i++) {
+                       if (tz->trips.passive.devices.handles[i] !=
+                               handle)
+                               continue;
+                       result = action(thermal, trip, cdev);
+                       if (result)
+                               goto failed;
+               }
+       }
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+               if (!tz->trips.active[i].flags.valid)
+                       break;
+               trip++;
+               for (j = 0;
+                   j < tz->trips.active[i].devices.count;
+                   j++) {
+                       if (tz->trips.active[i].devices.
+                                               handles[j] != handle)
+                               continue;
+                       result = action(thermal, trip, cdev);
+                       if (result)
+                               goto failed;
+               }
+       }
+
+       for (i = 0; i < tz->devices.count; i++) {
+               if (tz->devices.handles[i] != handle)
+                       continue;
+               result = action(thermal, -1, cdev);
+               if (result)
+                       goto failed;
+       }
+
+failed:
+       return result;
+}
+
+static int
+acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
+                                       struct thermal_cooling_device *cdev)
+{
+       return acpi_thermal_cooling_device_cb(thermal, cdev,
+                               thermal_zone_bind_cooling_device);
+}
+
+static int
+acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
+                                       struct thermal_cooling_device *cdev)
+{
+       return acpi_thermal_cooling_device_cb(thermal, cdev,
+                               thermal_zone_unbind_cooling_device);
+}
+
+static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+       .bind = acpi_thermal_bind_cooling_device,
+       .unbind = acpi_thermal_unbind_cooling_device,
+       .get_temp = thermal_get_temp,
+       .get_mode = thermal_get_mode,
+       .set_mode = thermal_set_mode,
+       .get_trip_type = thermal_get_trip_type,
+       .get_trip_temp = thermal_get_trip_temp,
+};
+
+static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
+{
+       int trips = 0;
+       int result;
+       int i;
+
+       if (tz->trips.critical.flags.valid)
+               trips++;
+
+       if (tz->trips.hot.flags.valid)
+               trips++;
+
+       if (tz->trips.passive.flags.valid)
+               trips++;
+
+       for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
+                       tz->trips.active[i].flags.valid; i++, trips++);
+       tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",
+                                       trips, tz, &acpi_thermal_zone_ops);
+       if (!tz->thermal_zone)
+               return -ENODEV;
+
+       result = sysfs_create_link(&tz->device->dev.kobj,
+                                  &tz->thermal_zone->device.kobj, "thermal_zone");
+       if (result)
+               return result;
+
+       result = sysfs_create_link(&tz->thermal_zone->device.kobj,
+                                  &tz->device->dev.kobj, "device");
+       if (result)
+               return result;
+
+       tz->tz_enabled = 1;
+
+       printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",
+                       tz->device->dev.bus_id, tz->thermal_zone->id);
+       return 0;
+}
+
+static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
+{
+       sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
+       sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
+       thermal_zone_device_unregister(tz->thermal_zone);
+       tz->thermal_zone = NULL;
+}
+
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
@@ -1260,13 +1532,19 @@ static int acpi_thermal_add(struct acpi_device *device)
        strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
        acpi_driver_data(device) = tz;
        mutex_init(&tz->lock);
+
+
        result = acpi_thermal_get_info(tz);
        if (result)
-               goto end;
+               goto free_memory;
+
+       result = acpi_thermal_register_thermal_zone(tz);
+       if (result)
+               goto free_memory;
 
        result = acpi_thermal_add_fs(device);
        if (result)
-               goto end;
+               goto unregister_thermal_zone;
 
        init_timer(&tz->timer);
 
@@ -1277,19 +1555,21 @@ static int acpi_thermal_add(struct acpi_device *device)
                                             acpi_thermal_notify, tz);
        if (ACPI_FAILURE(status)) {
                result = -ENODEV;
-               goto end;
+               goto remove_fs;
        }
 
        printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
               acpi_device_name(device), acpi_device_bid(device),
               KELVIN_TO_CELSIUS(tz->temperature));
+       goto end;
 
-      end:
-       if (result) {
-               acpi_thermal_remove_fs(device);
-               kfree(tz);
-       }
-
+remove_fs:
+       acpi_thermal_remove_fs(device);
+unregister_thermal_zone:
+       thermal_zone_device_unregister(tz->thermal_zone);
+free_memory:
+       kfree(tz);
+end:
        return result;
 }
 
@@ -1329,6 +1609,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
        }
 
        acpi_thermal_remove_fs(device);
+       acpi_thermal_unregister_thermal_zone(tz);
        mutex_destroy(&tz->lock);
        kfree(tz);
        return 0;