diff options
author | Zhang Rui <rui.zhang@intel.com> | 2008-01-17 15:51:11 +0800 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-01 23:12:19 -0500 |
commit | 3f655ef8c439e0775ffb7d1ead5d1d4f060e1f8b (patch) | |
tree | 9ac375f1f4c54a698e3f2736497193c0f00d638c /drivers/acpi/thermal.c | |
parent | 203d3d4aa482339b4816f131f713e1b8ee37f6dd (diff) |
ACPI: register ACPI thermal zone as generic thermal zone devices
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>
Diffstat (limited to 'drivers/acpi/thermal.c')
-rw-r--r-- | drivers/acpi/thermal.c | 301 |
1 files changed, 291 insertions, 10 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5f79b4451212..c6cfce4c0122 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -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; |