From: Zhang Rui <rui.zhang@xxxxxxxxx> 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@xxxxxxxxx> Signed-off-by: Thomas Sujith <sujith.thomas@xxxxxxxxx> --- drivers/acpi/Kconfig | 1 drivers/acpi/thermal.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 292 insertions(+), 10 deletions(-) Index: linux-2.6/drivers/acpi/thermal.c =================================================================== --- linux-2.6.orig/drivers/acpi/thermal.c +++ linux-2.6/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 *dat 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 *dat 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_ 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_ 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 ac } acpi_thermal_remove_fs(device); + acpi_thermal_unregister_thermal_zone(tz); mutex_destroy(&tz->lock); kfree(tz); return 0; Index: linux-2.6/drivers/acpi/Kconfig =================================================================== --- linux-2.6.orig/drivers/acpi/Kconfig +++ linux-2.6/drivers/acpi/Kconfig @@ -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 - To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html