diff options
Diffstat (limited to 'drivers/acpi/thermal.c')
| -rw-r--r-- | drivers/acpi/thermal.c | 775 |
1 files changed, 243 insertions, 532 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 9073ada8883..112817e963e 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -35,27 +35,21 @@ #include <linux/module.h> #include <linux/dmi.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/types.h> -#include <linux/proc_fs.h> #include <linux/jiffies.h> #include <linux/kmod.h> -#include <linux/seq_file.h> #include <linux/reboot.h> #include <linux/device.h> -#include <asm/uaccess.h> #include <linux/thermal.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h> +#include <linux/workqueue.h> +#include <asm/uaccess.h> #define PREFIX "ACPI: " #define ACPI_THERMAL_CLASS "thermal_zone" #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" -#define ACPI_THERMAL_FILE_STATE "state" -#define ACPI_THERMAL_FILE_TEMPERATURE "temperature" -#define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points" -#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode" -#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency" #define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80 #define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81 #define ACPI_THERMAL_NOTIFY_DEVICES 0x82 @@ -97,20 +91,11 @@ static int psv; module_param(psv, int, 0644); MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); +static struct workqueue_struct *acpi_thermal_pm_queue; + static int acpi_thermal_add(struct acpi_device *device); -static int acpi_thermal_remove(struct acpi_device *device, int type); -static int acpi_thermal_resume(struct acpi_device *device); +static int acpi_thermal_remove(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); -static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file); -static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file); -static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file); -static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_thermal_write_cooling_mode(struct file *, - const char __user *, size_t, - loff_t *); -static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file); -static ssize_t acpi_thermal_write_polling(struct file *, const char __user *, - size_t, loff_t *); static const struct acpi_device_id thermal_device_ids[] = { {ACPI_THERMAL_HID, 0}, @@ -118,6 +103,15 @@ static const struct acpi_device_id thermal_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, thermal_device_ids); +#ifdef CONFIG_PM_SLEEP +static int acpi_thermal_suspend(struct device *dev); +static int acpi_thermal_resume(struct device *dev); +#else +#define acpi_thermal_suspend NULL +#define acpi_thermal_resume NULL +#endif +static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume); + static struct acpi_driver acpi_thermal_driver = { .name = "thermal", .class = ACPI_THERMAL_CLASS, @@ -125,9 +119,9 @@ static struct acpi_driver acpi_thermal_driver = { .ops = { .add = acpi_thermal_add, .remove = acpi_thermal_remove, - .resume = acpi_thermal_resume, .notify = acpi_thermal_notify, }, + .drv.pm = &acpi_thermal_pm, }; struct acpi_thermal_state { @@ -197,49 +191,7 @@ struct acpi_thermal { struct thermal_zone_device *thermal_zone; int tz_enabled; int kelvin_offset; - struct mutex lock; -}; - -static const struct file_operations acpi_thermal_state_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_state_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_temp_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_temp_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_trip_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_trip_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_cooling_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_cooling_open_fs, - .read = seq_read, - .write = acpi_thermal_write_cooling_mode, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations acpi_thermal_polling_fops = { - .owner = THIS_MODULE, - .open = acpi_thermal_polling_open_fs, - .read = seq_read, - .write = acpi_thermal_write_polling, - .llseek = seq_lseek, - .release = single_release, + struct work_struct thermal_check_work; }; /* -------------------------------------------------------------------------- @@ -286,48 +238,18 @@ static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz) return 0; } -static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds) -{ - - if (!tz) - return -EINVAL; - - tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */ - - tz->thermal_zone->polling_delay = seconds * 1000; - - if (tz->tz_enabled) - thermal_zone_device_update(tz->thermal_zone); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Polling frequency set to %lu seconds\n", - tz->polling_frequency/10)); - - return 0; -} - static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) { - acpi_status status = AE_OK; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list arg_list = { 1, &arg0 }; - acpi_handle handle = NULL; - - if (!tz) return -EINVAL; - status = acpi_get_handle(tz->device->handle, "_SCP", &handle); - if (ACPI_FAILURE(status)) { + if (!acpi_has_method(tz->device->handle, "_SCP")) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); return -ENODEV; - } - - arg0.integer.value = mode; - - status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); - if (ACPI_FAILURE(status)) + } else if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle, + "_SCP", mode))) { return -ENODEV; + } return 0; } @@ -357,7 +279,7 @@ do { \ if (flags != ACPI_TRIPS_INIT) \ ACPI_EXCEPTION((AE_INFO, AE_ERROR, \ "ACPI thermal trip point %s changed\n" \ - "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \ + "Please send acpidump to linux-acpi@vger.kernel.org", str)); \ } while (0) static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) @@ -368,7 +290,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) int valid = 0; int i; - /* Critical Shutdown (required) */ + /* Critical Shutdown */ if (flag & ACPI_TRIPS_CRITICAL) { status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, &tmp); @@ -379,17 +301,19 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) * Below zero (Celsius) values clearly aren't right for sure.. * ... so lets discard those as invalid. */ - if (ACPI_FAILURE(status) || - tz->trips.critical.temperature <= 2732) { + if (ACPI_FAILURE(status)) { + tz->trips.critical.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No critical threshold\n")); + } else if (tmp <= 2732) { + pr_warn(FW_BUG "Invalid critical threshold (%llu)\n", + tmp); tz->trips.critical.flags.valid = 0; - ACPI_EXCEPTION((AE_INFO, status, - "No or invalid critical threshold")); - return -ENODEV; } else { tz->trips.critical.flags.valid = 1; ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found critical threshold [%lu]\n", - tz->trips.critical.temperature)); + "Found critical threshold [%lu]\n", + tz->trips.critical.temperature)); } if (tz->trips.critical.flags.valid == 1) { if (crt == -1) { @@ -400,8 +324,8 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) * Allow override critical threshold */ if (crt_k > tz->trips.critical.temperature) - printk(KERN_WARNING PREFIX - "Critical threshold %d C\n", crt); + pr_warn(PREFIX "Critical threshold %d C\n", + crt); tz->trips.critical.temperature = crt_k; } } @@ -420,7 +344,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) tz->trips.hot.flags.valid = 1; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", - tz->trips.critical.temperature)); + tz->trips.hot.temperature)); } } @@ -473,8 +397,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) status = acpi_evaluate_reference(tz->device->handle, "_PSL", NULL, &devices); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING PREFIX - "Invalid passive threshold\n"); + pr_warn(PREFIX "Invalid passive threshold\n"); tz->trips.passive.flags.valid = 0; } else @@ -536,8 +459,8 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) status = acpi_evaluate_reference(tz->device->handle, name, NULL, &devices); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING PREFIX - "Invalid active%d threshold\n", i); + pr_warn(PREFIX "Invalid active%d threshold\n", + i); tz->trips.active[i].flags.valid = 0; } else @@ -558,14 +481,14 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) break; } - if (flag & ACPI_TRIPS_DEVICES) { - memset(&devices, 0, sizeof(struct acpi_handle_list)); + if ((flag & ACPI_TRIPS_DEVICES) + && acpi_has_method(tz->device->handle, "_TZD")) { + memset(&devices, 0, sizeof(devices)); status = acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &devices); - if (memcmp(&tz->devices, &devices, - sizeof(struct acpi_handle_list))) { - memcpy(&tz->devices, &devices, - sizeof(struct acpi_handle_list)); + if (ACPI_SUCCESS(status) + && memcmp(&tz->devices, &devices, sizeof(devices))) { + tz->devices = devices; ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); } } @@ -575,13 +498,32 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) { - return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); + int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); + + if (ret) + return ret; + + valid = tz->trips.critical.flags.valid | + tz->trips.hot.flags.valid | + tz->trips.passive.flags.valid; + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) + valid |= tz->trips.active[i].flags.valid; + + if (!valid) { + pr_warn(FW_BUG "No valid trip found\n"); + return -ENODEV; + } + return 0; } static void acpi_thermal_check(void *data) { struct acpi_thermal *tz = data; + if (!tz->tz_enabled) + return; + thermal_zone_device_update(tz->thermal_zone); } @@ -605,8 +547,6 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, return 0; } -static const char enabled[] = "kernel"; -static const char disabled[] = "user"; static int thermal_get_mode(struct thermal_zone_device *thermal, enum thermal_device_mode *mode) { @@ -635,16 +575,17 @@ static int thermal_set_mode(struct thermal_zone_device *thermal, */ if (mode == THERMAL_DEVICE_ENABLED) enable = 1; - else if (mode == THERMAL_DEVICE_DISABLED) + else if (mode == THERMAL_DEVICE_DISABLED) { enable = 0; - else + pr_warn("thermal zone will be disabled\n"); + } 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)); + "%s kernel ACPI thermal control\n", + tz->tz_enabled ? "Enable" : "Disable")); acpi_thermal_check(tz); } return 0; @@ -761,6 +702,50 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, return -EINVAL; } +static int thermal_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct acpi_thermal *tz = thermal->devdata; + enum thermal_trip_type type; + int i; + + if (thermal_get_trip_type(thermal, trip, &type)) + return -EINVAL; + + if (type == THERMAL_TRIP_ACTIVE) { + unsigned long trip_temp; + unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature, + tz->kelvin_offset); + if (thermal_get_trip_temp(thermal, trip, &trip_temp)) + return -EINVAL; + + if (temp > trip_temp) { + *trend = THERMAL_TREND_RAISING; + return 0; + } else { + /* Fall back on default trend */ + return -EINVAL; + } + } + + /* + * tz->temperature has already been updated by generic thermal layer, + * before this callback being invoked + */ + i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + + (tz->trips.passive.tc2 + * (tz->temperature - tz->trips.passive.temperature)); + + if (i > 0) + *trend = THERMAL_TREND_RAISING; + else if (i < 0) + *trend = THERMAL_TREND_DROPPING; + else + *trend = THERMAL_TREND_STABLE; + return 0; +} + + static int thermal_notify(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type trip_type) { @@ -774,7 +759,6 @@ static int thermal_notify(struct thermal_zone_device *thermal, int trip, else return 0; - acpi_bus_generate_proc_event(tz->device, type, 1); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, dev_name(&tz->device->dev), type, 1); @@ -784,11 +768,9 @@ static int thermal_notify(struct thermal_zone_device *thermal, int trip, return 0; } -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) + bool bind) { struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; @@ -812,11 +794,19 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, i++) { handle = tz->trips.passive.devices.handles[i]; status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, trip, cdev); - if (result) - goto failed; - } + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = + thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = + thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; } } @@ -829,11 +819,17 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j++) { handle = tz->trips.active[i].devices.handles[j]; status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, trip, cdev); - if (result) - goto failed; - } + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; } } @@ -841,7 +837,15 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, handle = tz->devices.handles[i]; status = acpi_bus_get_device(handle, &dev); if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, -1, cdev); + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, THERMAL_TRIPS_NONE, + cdev, THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, THERMAL_TRIPS_NONE, + cdev); if (result) goto failed; } @@ -855,16 +859,14 @@ 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); + return acpi_thermal_cooling_device_cb(thermal, cdev, true); } 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); + return acpi_thermal_cooling_device_cb(thermal, cdev, false); } static struct thermal_zone_device_ops acpi_thermal_zone_ops = { @@ -876,6 +878,7 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = { .get_trip_type = thermal_get_trip_type, .get_trip_temp = thermal_get_trip_temp, .get_crit_temp = thermal_get_crit_temp, + .get_trend = thermal_get_trend, .notify = thermal_notify, }; @@ -900,18 +903,15 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (tz->trips.passive.flags.valid) tz->thermal_zone = - thermal_zone_device_register("acpitz", trips, tz, - &acpi_thermal_zone_ops, - tz->trips.passive.tc1, - tz->trips.passive.tc2, + thermal_zone_device_register("acpitz", trips, 0, tz, + &acpi_thermal_zone_ops, NULL, tz->trips.passive.tsp*100, tz->polling_frequency*100); else tz->thermal_zone = - thermal_zone_device_register("acpitz", trips, tz, - &acpi_thermal_zone_ops, - 0, 0, 0, - tz->polling_frequency*100); + thermal_zone_device_register("acpitz", trips, 0, tz, + &acpi_thermal_zone_ops, NULL, + 0, tz->polling_frequency*100); if (IS_ERR(tz->thermal_zone)) return -ENODEV; @@ -925,14 +925,10 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (result) return result; - status = acpi_attach_data(tz->device->handle, - acpi_bus_private_data_handler, - tz->thermal_zone); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX - "Error attaching device data\n"); + status = acpi_bus_attach_private_data(tz->device->handle, + tz->thermal_zone); + if (ACPI_FAILURE(status)) return -ENODEV; - } tz->tz_enabled = 1; @@ -947,335 +943,9 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); thermal_zone_device_unregister(tz->thermal_zone); tz->thermal_zone = NULL; - acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler); -} - - -/* -------------------------------------------------------------------------- - FS Interface (/proc) - -------------------------------------------------------------------------- */ - -static struct proc_dir_entry *acpi_thermal_dir; - -static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - seq_puts(seq, "state: "); - - if (!tz->state.critical && !tz->state.hot && !tz->state.passive - && !tz->state.active) - seq_puts(seq, "ok\n"); - else { - if (tz->state.critical) - seq_puts(seq, "critical "); - if (tz->state.hot) - seq_puts(seq, "hot "); - if (tz->state.passive) - seq_puts(seq, "passive "); - if (tz->state.active) - seq_printf(seq, "active[%d]", tz->state.active_index); - seq_puts(seq, "\n"); - } - - end: - return 0; -} - -static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data); -} - -static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset) -{ - int result = 0; - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - result = acpi_thermal_get_temperature(tz); - if (result) - goto end; - - seq_printf(seq, "temperature: %ld C\n", - KELVIN_TO_CELSIUS(tz->temperature)); - - end: - return 0; -} - -static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data); -} - -static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - struct acpi_device *device; - acpi_status status; - - int i = 0; - int j = 0; - - - if (!tz) - goto end; - - if (tz->trips.critical.flags.valid) - seq_printf(seq, "critical (S5): %ld C%s", - KELVIN_TO_CELSIUS(tz->trips.critical.temperature), - nocrt ? " <disabled>\n" : "\n"); - - if (tz->trips.hot.flags.valid) - seq_printf(seq, "hot (S4): %ld C%s", - KELVIN_TO_CELSIUS(tz->trips.hot.temperature), - nocrt ? " <disabled>\n" : "\n"); - - if (tz->trips.passive.flags.valid) { - seq_printf(seq, - "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=", - KELVIN_TO_CELSIUS(tz->trips.passive.temperature), - tz->trips.passive.tc1, tz->trips.passive.tc2, - tz->trips.passive.tsp); - for (j = 0; j < tz->trips.passive.devices.count; j++) { - status = acpi_bus_get_device(tz->trips.passive.devices. - handles[j], &device); - seq_printf(seq, "%4.4s ", status ? "" : - acpi_device_bid(device)); - } - seq_puts(seq, "\n"); - } else { - seq_printf(seq, "passive (forced):"); - if (tz->thermal_zone->forced_passive) - seq_printf(seq, " %i C\n", - tz->thermal_zone->forced_passive / 1000); - else - seq_printf(seq, "<not set>\n"); - } - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - if (!(tz->trips.active[i].flags.valid)) - break; - seq_printf(seq, "active[%d]: %ld C: devices=", - i, - KELVIN_TO_CELSIUS(tz->trips.active[i].temperature)); - for (j = 0; j < tz->trips.active[i].devices.count; j++){ - status = acpi_bus_get_device(tz->trips.active[i]. - devices.handles[j], - &device); - seq_printf(seq, "%4.4s ", status ? "" : - acpi_device_bid(device)); - } - seq_puts(seq, "\n"); - } - - end: - return 0; -} - -static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data); -} - -static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - if (!tz->flags.cooling_mode) - seq_puts(seq, "<setting not supported>\n"); - else - seq_puts(seq, "0 - Active; 1 - Passive\n"); - - end: - return 0; -} - -static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_cooling_seq_show, - PDE(inode)->data); -} - -static ssize_t -acpi_thermal_write_cooling_mode(struct file *file, - const char __user * buffer, - size_t count, loff_t * ppos) -{ - struct seq_file *m = file->private_data; - struct acpi_thermal *tz = m->private; - int result = 0; - char mode_string[12] = { '\0' }; - - - if (!tz || (count > sizeof(mode_string) - 1)) - return -EINVAL; - - if (!tz->flags.cooling_mode) - return -ENODEV; - - if (copy_from_user(mode_string, buffer, count)) - return -EFAULT; - - mode_string[count] = '\0'; - - result = acpi_thermal_set_cooling_mode(tz, - simple_strtoul(mode_string, NULL, - 0)); - if (result) - return result; - - acpi_thermal_check(tz); - - return count; -} - -static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_thermal *tz = seq->private; - - - if (!tz) - goto end; - - if (!tz->thermal_zone->polling_delay) { - seq_puts(seq, "<polling disabled>\n"); - goto end; - } - - seq_printf(seq, "polling frequency: %d seconds\n", - (tz->thermal_zone->polling_delay / 1000)); - - end: - return 0; -} - -static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_thermal_polling_seq_show, - PDE(inode)->data); -} - -static ssize_t -acpi_thermal_write_polling(struct file *file, - const char __user * buffer, - size_t count, loff_t * ppos) -{ - struct seq_file *m = file->private_data; - struct acpi_thermal *tz = m->private; - int result = 0; - char polling_string[12] = { '\0' }; - int seconds = 0; - - - if (!tz || (count > sizeof(polling_string) - 1)) - return -EINVAL; - - if (copy_from_user(polling_string, buffer, count)) - return -EFAULT; - - polling_string[count] = '\0'; - - seconds = simple_strtoul(polling_string, NULL, 0); - - result = acpi_thermal_set_polling(tz, seconds); - if (result) - return result; - - acpi_thermal_check(tz); - - return count; -} - -static int acpi_thermal_add_fs(struct acpi_device *device) -{ - struct proc_dir_entry *entry = NULL; - - - if (!acpi_device_dir(device)) { - acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), - acpi_thermal_dir); - if (!acpi_device_dir(device)) - return -ENODEV; - } - - /* 'state' [R] */ - entry = proc_create_data(ACPI_THERMAL_FILE_STATE, - S_IRUGO, acpi_device_dir(device), - &acpi_thermal_state_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'temperature' [R] */ - entry = proc_create_data(ACPI_THERMAL_FILE_TEMPERATURE, - S_IRUGO, acpi_device_dir(device), - &acpi_thermal_temp_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'trip_points' [R] */ - entry = proc_create_data(ACPI_THERMAL_FILE_TRIP_POINTS, - S_IRUGO, - acpi_device_dir(device), - &acpi_thermal_trip_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'cooling_mode' [R/W] */ - entry = proc_create_data(ACPI_THERMAL_FILE_COOLING_MODE, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device), - &acpi_thermal_cooling_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - - /* 'polling_frequency' [R/W] */ - entry = proc_create_data(ACPI_THERMAL_FILE_POLLING_FREQ, - S_IFREG | S_IRUGO | S_IWUSR, - acpi_device_dir(device), - &acpi_thermal_polling_fops, - acpi_driver_data(device)); - if (!entry) - return -ENODEV; - return 0; + acpi_bus_detach_private_data(tz->device->handle); } -static int acpi_thermal_remove_fs(struct acpi_device *device) -{ - - if (acpi_device_dir(device)) { - remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, - acpi_device_dir(device)); - remove_proc_entry(ACPI_THERMAL_FILE_STATE, - acpi_device_dir(device)); - remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir); - acpi_device_dir(device) = NULL; - } - - return 0; -} /* -------------------------------------------------------------------------- Driver Interface @@ -1296,14 +966,12 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) case ACPI_THERMAL_NOTIFY_THRESHOLDS: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); acpi_thermal_check(tz); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); acpi_thermal_check(tz); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; @@ -1314,6 +982,38 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) } } +/* + * On some platforms, the AML code has dependency about + * the evaluating order of _TMP and _CRT/_HOT/_PSV/_ACx. + * 1. On HP Pavilion G4-1016tx, _TMP must be invoked after + * /_CRT/_HOT/_PSV/_ACx, or else system will be power off. + * 2. On HP Compaq 6715b/6715s, the return value of _PSV is 0 + * if _TMP has never been evaluated. + * + * As this dependency is totally transparent to OS, evaluate + * all of them once, in the order of _CRT/_HOT/_PSV/_ACx, + * _TMP, before they are actually used. + */ +static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz) +{ + acpi_handle handle = tz->device->handle; + unsigned long long value; + int i; + + acpi_evaluate_integer(handle, "_CRT", NULL, &value); + acpi_evaluate_integer(handle, "_HOT", NULL, &value); + acpi_evaluate_integer(handle, "_PSV", NULL, &value); + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; + acpi_status status; + + status = acpi_evaluate_integer(handle, name, NULL, &value); + if (status == AE_NOT_FOUND) + break; + } + acpi_evaluate_integer(handle, "_TMP", NULL, &value); +} + static int acpi_thermal_get_info(struct acpi_thermal *tz) { int result = 0; @@ -1322,16 +1022,18 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) if (!tz) return -EINVAL; - /* Get temperature [_TMP] (required) */ - result = acpi_thermal_get_temperature(tz); - if (result) - return result; + acpi_thermal_aml_dependency_fix(tz); /* Get trip points [_CRT, _PSV, etc.] (required) */ result = acpi_thermal_get_trip_points(tz); if (result) return result; + /* Get temperature [_TMP] (required) */ + result = acpi_thermal_get_temperature(tz); + if (result) + return result; + /* Set the cooling mode [_SCP] to active cooling (default) */ result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); if (!result) @@ -1365,6 +1067,13 @@ static void acpi_thermal_guess_offset(struct acpi_thermal *tz) tz->kelvin_offset = 2732; } +static void acpi_thermal_check_fn(struct work_struct *work) +{ + struct acpi_thermal *tz = container_of(work, struct acpi_thermal, + thermal_check_work); + acpi_thermal_check(tz); +} + static int acpi_thermal_add(struct acpi_device *device) { int result = 0; @@ -1383,8 +1092,6 @@ static int acpi_thermal_add(struct acpi_device *device) strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); device->driver_data = tz; - mutex_init(&tz->lock); - result = acpi_thermal_get_info(tz); if (result) @@ -1396,49 +1103,52 @@ static int acpi_thermal_add(struct acpi_device *device) if (result) goto free_memory; - result = acpi_thermal_add_fs(device); - if (result) - goto unregister_thermal_zone; + INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn); - printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", - acpi_device_name(device), acpi_device_bid(device), - KELVIN_TO_CELSIUS(tz->temperature)); + pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device), + acpi_device_bid(device), KELVIN_TO_CELSIUS(tz->temperature)); goto end; -unregister_thermal_zone: - thermal_zone_device_unregister(tz->thermal_zone); free_memory: kfree(tz); end: return result; } -static int acpi_thermal_remove(struct acpi_device *device, int type) +static int acpi_thermal_remove(struct acpi_device *device) { struct acpi_thermal *tz = NULL; if (!device || !acpi_driver_data(device)) return -EINVAL; + flush_workqueue(acpi_thermal_pm_queue); tz = acpi_driver_data(device); - acpi_thermal_remove_fs(device); acpi_thermal_unregister_thermal_zone(tz); - mutex_destroy(&tz->lock); kfree(tz); return 0; } -static int acpi_thermal_resume(struct acpi_device *device) +#ifdef CONFIG_PM_SLEEP +static int acpi_thermal_suspend(struct device *dev) { - struct acpi_thermal *tz = NULL; - int i, j, power_state, result; + /* Make sure the previously queued thermal check work has been done */ + flush_workqueue(acpi_thermal_pm_queue); + return 0; +} +static int acpi_thermal_resume(struct device *dev) +{ + struct acpi_thermal *tz; + int i, j, power_state, result; - if (!device || !acpi_driver_data(device)) + if (!dev) return -EINVAL; - tz = acpi_driver_data(device); + tz = acpi_driver_data(to_acpi_device(dev)); + if (!tz) + return -EINVAL; for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!(&tz->trips.active[i])) @@ -1447,8 +1157,9 @@ static int acpi_thermal_resume(struct acpi_device *device) break; tz->trips.active[i].flags.enabled = 1; for (j = 0; j < tz->trips.active[i].devices.count; j++) { - result = acpi_bus_get_power(tz->trips.active[i].devices. - handles[j], &power_state); + result = acpi_bus_update_power( + tz->trips.active[i].devices.handles[j], + &power_state); if (result || (power_state != ACPI_STATE_D0)) { tz->trips.active[i].flags.enabled = 0; break; @@ -1457,32 +1168,33 @@ static int acpi_thermal_resume(struct acpi_device *device) tz->state.active |= tz->trips.active[i].flags.enabled; } - acpi_thermal_check(tz); + queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work); return AE_OK; } +#endif static int thermal_act(const struct dmi_system_id *d) { if (act == 0) { - printk(KERN_NOTICE "ACPI: %s detected: " - "disabling all active thermal trip points\n", d->ident); + pr_notice(PREFIX "%s detected: " + "disabling all active thermal trip points\n", d->ident); act = -1; } return 0; } static int thermal_nocrt(const struct dmi_system_id *d) { - printk(KERN_NOTICE "ACPI: %s detected: " - "disabling all critical thermal trip point actions.\n", d->ident); + pr_notice(PREFIX "%s detected: " + "disabling all critical thermal trip point actions.\n", d->ident); nocrt = 1; return 0; } static int thermal_tzp(const struct dmi_system_id *d) { if (tzp == 0) { - printk(KERN_NOTICE "ACPI: %s detected: " - "enabling thermal zone polling\n", d->ident); + pr_notice(PREFIX "%s detected: " + "enabling thermal zone polling\n", d->ident); tzp = 300; /* 300 dS = 30 Seconds */ } return 0; @@ -1490,8 +1202,8 @@ static int thermal_tzp(const struct dmi_system_id *d) { static int thermal_psv(const struct dmi_system_id *d) { if (psv == 0) { - printk(KERN_NOTICE "ACPI: %s detected: " - "disabling all passive thermal trip points\n", d->ident); + pr_notice(PREFIX "%s detected: " + "disabling all passive thermal trip points\n", d->ident); psv = -1; } return 0; @@ -1544,16 +1256,17 @@ static int __init acpi_thermal_init(void) dmi_check_system(thermal_dmi_table); if (off) { - printk(KERN_NOTICE "ACPI: thermal control disabled\n"); + pr_notice(PREFIX "thermal control disabled\n"); return -ENODEV; } - acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); - if (!acpi_thermal_dir) + + acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm"); + if (!acpi_thermal_pm_queue) return -ENODEV; result = acpi_bus_register_driver(&acpi_thermal_driver); if (result < 0) { - remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); + destroy_workqueue(acpi_thermal_pm_queue); return -ENODEV; } @@ -1562,10 +1275,8 @@ static int __init acpi_thermal_init(void) static void __exit acpi_thermal_exit(void) { - acpi_bus_unregister_driver(&acpi_thermal_driver); - - remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); + destroy_workqueue(acpi_thermal_pm_queue); return; } |
