diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 25 | ||||
-rw-r--r-- | drivers/acpi/fan.c | 92 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 23 | ||||
-rw-r--r-- | drivers/acpi/processor_thermal.c | 134 | ||||
-rw-r--r-- | drivers/acpi/thermal.c | 646 | ||||
-rw-r--r-- | drivers/acpi/video.c | 78 |
7 files changed, 832 insertions, 167 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ccf6ea9..5583729 100644 --- a/drivers/acpi/Kconfig +++ b/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 diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 1b4cf98..8df325d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -122,6 +122,31 @@ int acpi_bus_get_status(struct acpi_device *device) EXPORT_SYMBOL(acpi_bus_get_status); +void acpi_bus_private_data_handler(acpi_handle handle, + u32 function, void *context) +{ + return; +} +EXPORT_SYMBOL(acpi_bus_private_data_handler); + +int acpi_bus_get_private_data(acpi_handle handle, void **data) +{ + acpi_status status = AE_OK; + + if (!*data) + return -EINVAL; + + status = acpi_get_data(handle, acpi_bus_private_data_handler, data); + if (ACPI_FAILURE(status) || !*data) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", + handle)); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(acpi_bus_get_private_data); + /* -------------------------------------------------------------------------- Power Management -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index a6e149d..48cb705 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -30,7 +30,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <asm/uaccess.h> - +#include <linux/thermal.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -68,9 +68,55 @@ static struct acpi_driver acpi_fan_driver = { }, }; +/* thermal cooling device callbacks */ +static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf) +{ + /* ACPI fan device only support two states: ON/OFF */ + return sprintf(buf, "1\n"); +} + +static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + int state; + int result; + + if (!device) + return -EINVAL; + + result = acpi_bus_get_power(device->handle, &state); + if (result) + return result; + + return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" : + (state == ACPI_STATE_D0 ? "1" : "unknown")); +} + +static int +fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state) +{ + struct acpi_device *device = cdev->devdata; + int result; + + if (!device || (state != 0 && state != 1)) + return -EINVAL; + + result = acpi_bus_set_power(device->handle, + state ? ACPI_STATE_D0 : ACPI_STATE_D3); + + return result; +} + +static struct thermal_cooling_device_ops fan_cooling_ops = { + .get_max_state = fan_get_max_state, + .get_cur_state = fan_get_cur_state, + .set_cur_state = fan_set_cur_state, +}; + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS static struct proc_dir_entry *acpi_fan_dir; @@ -171,7 +217,17 @@ static int acpi_fan_remove_fs(struct acpi_device *device) return 0; } +#else +static int acpi_fan_add_fs(struct acpi_device *device) +{ + return 0; +} +static int acpi_fan_remove_fs(struct acpi_device *device) +{ + return 0; +} +#endif /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -179,9 +235,8 @@ static int acpi_fan_remove_fs(struct acpi_device *device) static int acpi_fan_add(struct acpi_device *device) { int result = 0; - struct acpi_fan *fan = NULL; int state = 0; - + struct thermal_cooling_device *cdev; if (!device) return -EINVAL; @@ -199,6 +254,25 @@ static int acpi_fan_add(struct acpi_device *device) acpi_bus_set_power(device->handle, state); device->flags.force_power_state = 0; + cdev = thermal_cooling_device_register("Fan", device, + &fan_cooling_ops); + if (cdev) + printk(KERN_INFO PREFIX + "%s is registered as cooling_device%d\n", + device->dev.bus_id, cdev->id); + else + goto end; + acpi_driver_data(device) = cdev; + result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj, + "thermal_cooling"); + if (result) + return result; + + result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj, + "device"); + if (result) + return result; + result = acpi_fan_add_fs(device); if (result) goto end; @@ -208,18 +282,20 @@ static int acpi_fan_add(struct acpi_device *device) !device->power.state ? "on" : "off"); end: - if (result) - kfree(fan); - return result; } static int acpi_fan_remove(struct acpi_device *device, int type) { - if (!device || !acpi_driver_data(device)) + struct thermal_cooling_device *cdev = acpi_driver_data(device); + + if (!device || !cdev) return -EINVAL; acpi_fan_remove_fs(device); + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&cdev->device.kobj, "device"); + thermal_cooling_device_unregister(cdev); return 0; } @@ -261,10 +337,12 @@ static int __init acpi_fan_init(void) int result = 0; +#ifdef CONFIG_ACPI_PROCFS acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); if (!acpi_fan_dir) return -ENODEV; acpi_fan_dir->owner = THIS_MODULE; +#endif result = acpi_bus_register_driver(&acpi_fan_driver); if (result < 0) { diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index c53113e..315fd8f 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -668,6 +668,24 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) acpi_processor_power_init(pr, device); + pr->cdev = thermal_cooling_device_register("Processor", device, + &processor_cooling_ops); + if (pr->cdev) + printk(KERN_INFO PREFIX + "%s is registered as cooling_device%d\n", + device->dev.bus_id, pr->cdev->id); + else + goto end; + + result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj, + "thermal_cooling"); + if (result) + return result; + result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj, + "device"); + if (result) + return result; + if (pr->flags.throttling) { printk(KERN_INFO PREFIX "%s [%s] (supports", acpi_device_name(device), acpi_device_bid(device)); @@ -791,6 +809,11 @@ static int acpi_processor_remove(struct acpi_device *device, int type) acpi_processor_remove_fs(device); + sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&pr->cdev->device.kobj, "device"); + thermal_cooling_device_unregister(pr->cdev); + pr->cdev = NULL; + processors[pr->id] = NULL; kfree(pr); diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 06e6f3f..9cb43f5 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -32,6 +32,7 @@ #include <linux/cpufreq.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/sysdev.h> #include <asm/uaccess.h> @@ -93,6 +94,9 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr) * _any_ cpufreq driver and not only the acpi-cpufreq driver. */ +#define CPUFREQ_THERMAL_MIN_STEP 0 +#define CPUFREQ_THERMAL_MAX_STEP 3 + static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; static unsigned int acpi_thermal_cpufreq_is_init = 0; @@ -109,8 +113,9 @@ static int acpi_thermal_cpufreq_increase(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return -ENODEV; - if (cpufreq_thermal_reduction_pctg[cpu] < 60) { - cpufreq_thermal_reduction_pctg[cpu] += 20; + if (cpufreq_thermal_reduction_pctg[cpu] < + CPUFREQ_THERMAL_MAX_STEP) { + cpufreq_thermal_reduction_pctg[cpu]++; cpufreq_update_policy(cpu); return 0; } @@ -123,8 +128,9 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu) if (!cpu_has_cpufreq(cpu)) return -ENODEV; - if (cpufreq_thermal_reduction_pctg[cpu] > 20) - cpufreq_thermal_reduction_pctg[cpu] -= 20; + if (cpufreq_thermal_reduction_pctg[cpu] > + (CPUFREQ_THERMAL_MIN_STEP + 1)) + cpufreq_thermal_reduction_pctg[cpu]--; else cpufreq_thermal_reduction_pctg[cpu] = 0; cpufreq_update_policy(cpu); @@ -143,7 +149,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, max_freq = (policy->cpuinfo.max_freq * - (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100; + (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100; cpufreq_verify_within_limits(policy, 0, max_freq); @@ -155,6 +161,32 @@ static struct notifier_block acpi_thermal_cpufreq_notifier_block = { .notifier_call = acpi_thermal_cpufreq_notifier, }; +static int cpufreq_get_max_state(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + return CPUFREQ_THERMAL_MAX_STEP; +} + +static int cpufreq_get_cur_state(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + return cpufreq_thermal_reduction_pctg[cpu]; +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) +{ + if (!cpu_has_cpufreq(cpu)) + return 0; + + cpufreq_thermal_reduction_pctg[cpu] = state; + cpufreq_update_policy(cpu); + return 0; +} + void acpi_thermal_cpufreq_init(void) { int i; @@ -179,6 +211,20 @@ void acpi_thermal_cpufreq_exit(void) } #else /* ! CONFIG_CPU_FREQ */ +static int cpufreq_get_max_state(unsigned int cpu) +{ + return 0; +} + +static int cpufreq_get_cur_state(unsigned int cpu) +{ + return 0; +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) +{ + return 0; +} static int acpi_thermal_cpufreq_increase(unsigned int cpu) { @@ -310,6 +356,84 @@ int acpi_processor_get_limit_info(struct acpi_processor *pr) return 0; } +/* thermal coolign device callbacks */ +static int acpi_processor_max_state(struct acpi_processor *pr) +{ + int max_state = 0; + + /* + * There exists four states according to + * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3 + */ + max_state += cpufreq_get_max_state(pr->id); + if (pr->flags.throttling) + max_state += (pr->throttling.state_count -1); + + return max_state; +} +static int +processor_get_max_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr = acpi_driver_data(device); + + if (!device || !pr) + return -EINVAL; + + return sprintf(buf, "%d\n", acpi_processor_max_state(pr)); +} + +static int +processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr = acpi_driver_data(device); + int cur_state; + + if (!device || !pr) + return -EINVAL; + + cur_state = cpufreq_get_cur_state(pr->id); + if (pr->flags.throttling) + cur_state += pr->throttling.state; + + return sprintf(buf, "%d\n", cur_state); +} + +static int +processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr = acpi_driver_data(device); + int result = 0; + int max_pstate; + + if (!device || !pr) + return -EINVAL; + + max_pstate = cpufreq_get_max_state(pr->id); + + if (state > acpi_processor_max_state(pr)) + return -EINVAL; + + if (state <= max_pstate) { + if (pr->flags.throttling && pr->throttling.state) + result = acpi_processor_set_throttling(pr, 0); + cpufreq_set_cur_state(pr->id, state); + } else { + cpufreq_set_cur_state(pr->id, max_pstate); + result = acpi_processor_set_throttling(pr, + state - max_pstate); + } + return result; +} + +struct thermal_cooling_device_ops processor_cooling_ops = { + .get_max_state = processor_get_max_state, + .get_cur_state = processor_get_cur_state, + .set_cur_state = processor_set_cur_state, +}; + /* /proc interface */ static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 3a0af9a..8d4b79b 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> @@ -65,9 +65,6 @@ #define ACPI_THERMAL_MAX_ACTIVE 10 #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 -#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10) -#define CELSIUS_TO_KELVIN(t) ((t+273)*10) - #define _COMPONENT ACPI_THERMAL_COMPONENT ACPI_MODULE_NAME("thermal"); @@ -195,6 +192,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; }; @@ -321,173 +320,221 @@ static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) return 0; } -static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) -{ - acpi_status status = AE_OK; - int i = 0; +#define ACPI_TRIPS_CRITICAL 0x01 +#define ACPI_TRIPS_HOT 0x02 +#define ACPI_TRIPS_PASSIVE 0x04 +#define ACPI_TRIPS_ACTIVE 0x08 +#define ACPI_TRIPS_DEVICES 0x10 +#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE) +#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES - if (!tz) - return -EINVAL; +#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \ + ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \ + ACPI_TRIPS_DEVICES) - /* Critical Shutdown (required) */ - - status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, - &tz->trips.critical.temperature); - if (ACPI_FAILURE(status)) { - tz->trips.critical.flags.valid = 0; - ACPI_EXCEPTION((AE_INFO, status, "No 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)); - } +/* + * This exception is thrown out in two cases: + * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid + * when re-evaluating the AML code. + * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change. + * We need to re-bind the cooling devices of a thermal zone when this occurs. + */ +#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \ +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)); \ +} while (0) + +static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) +{ + acpi_status status = AE_OK; + struct acpi_handle_list devices; + int valid = 0; + int i; - if (tz->trips.critical.flags.valid == 1) { - if (crt == -1) { + /* Critical Shutdown (required) */ + if (flag & ACPI_TRIPS_CRITICAL) { + status = acpi_evaluate_integer(tz->device->handle, + "_CRT", NULL, &tz->trips.critical.temperature); + if (ACPI_FAILURE(status)) { tz->trips.critical.flags.valid = 0; - } else if (crt > 0) { - unsigned long crt_k = CELSIUS_TO_KELVIN(crt); - - /* - * Allow override to lower critical threshold - */ - if (crt_k < tz->trips.critical.temperature) - tz->trips.critical.temperature = crt_k; + ACPI_EXCEPTION((AE_INFO, status, + "No 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)); + } + if (tz->trips.critical.flags.valid == 1) { + if (crt == -1) { + tz->trips.critical.flags.valid = 0; + } else if (crt > 0) { + unsigned long crt_k = CELSIUS_TO_KELVIN(crt); + /* + * Allow override to lower critical threshold + */ + if (crt_k < tz->trips.critical.temperature) + tz->trips.critical.temperature = crt_k; + } } } /* Critical Sleep (optional) */ - - status = - acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, - &tz->trips.hot.temperature); - if (ACPI_FAILURE(status)) { - tz->trips.hot.flags.valid = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n")); - } else { - tz->trips.hot.flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", - tz->trips.hot.temperature)); - } - - /* Passive: Processors (optional) */ - - if (psv == -1) { - status = AE_SUPPORT; - } else if (psv > 0) { - tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); - status = AE_OK; - } else { + if (flag & ACPI_TRIPS_HOT) { status = acpi_evaluate_integer(tz->device->handle, - "_PSV", NULL, &tz->trips.passive.temperature); + "_HOT", NULL, &tz->trips.hot.temperature); + if (ACPI_FAILURE(status)) { + tz->trips.hot.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No hot threshold\n")); + } else { + tz->trips.hot.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found hot threshold [%lu]\n", + tz->trips.critical.temperature)); + } } - if (ACPI_FAILURE(status)) { - tz->trips.passive.flags.valid = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); - } else { - tz->trips.passive.flags.valid = 1; - - status = - acpi_evaluate_integer(tz->device->handle, "_TC1", NULL, - &tz->trips.passive.tc1); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; - - status = - acpi_evaluate_integer(tz->device->handle, "_TC2", NULL, - &tz->trips.passive.tc2); - if (ACPI_FAILURE(status)) - tz->trips.passive.flags.valid = 0; + /* Passive (optional) */ + if (flag & ACPI_TRIPS_PASSIVE) { + valid = tz->trips.passive.flags.valid; + if (psv == -1) { + status = AE_SUPPORT; + } else if (psv > 0) { + tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); + status = AE_OK; + } else { + status = acpi_evaluate_integer(tz->device->handle, + "_PSV", NULL, &tz->trips.passive.temperature); + } - status = - acpi_evaluate_integer(tz->device->handle, "_TSP", NULL, - &tz->trips.passive.tsp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; - - status = - acpi_evaluate_reference(tz->device->handle, "_PSL", NULL, - &tz->trips.passive.devices); + else { + tz->trips.passive.flags.valid = 1; + if (flag == ACPI_TRIPS_INIT) { + status = acpi_evaluate_integer( + tz->device->handle, "_TC1", + NULL, &tz->trips.passive.tc1); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + status = acpi_evaluate_integer( + tz->device->handle, "_TC2", + NULL, &tz->trips.passive.tc2); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + status = acpi_evaluate_integer( + tz->device->handle, "_TSP", + NULL, &tz->trips.passive.tsp); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + } + } + } + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, "_PSL", + NULL, &devices); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; - - if (!tz->trips.passive.flags.valid) - printk(KERN_WARNING PREFIX "Invalid passive threshold\n"); else - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found passive threshold [%lu]\n", - tz->trips.passive.temperature)); - } + tz->trips.passive.flags.valid = 1; - /* Active: Fans, etc. (optional) */ + if (memcmp(&tz->trips.passive.devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->trips.passive.devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { + if (valid != tz->trips.passive.flags.valid) + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); + } + /* Active (optional) */ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; + valid = tz->trips.active[i].flags.valid; if (act == -1) - break; /* disable all active trip points */ - - status = acpi_evaluate_integer(tz->device->handle, - name, NULL, &tz->trips.active[i].temperature); - - if (ACPI_FAILURE(status)) { - if (i == 0) /* no active trip points */ + break; /* disable all active trip points */ + + if (flag & ACPI_TRIPS_ACTIVE) { + status = acpi_evaluate_integer(tz->device->handle, + name, NULL, &tz->trips.active[i].temperature); + if (ACPI_FAILURE(status)) { + tz->trips.active[i].flags.valid = 0; + if (i == 0) + break; + if (act <= 0) + break; + if (i == 1) + tz->trips.active[0].temperature = + CELSIUS_TO_KELVIN(act); + else + /* + * Don't allow override higher than + * the next higher trip point + */ + tz->trips.active[i - 1].temperature = + (tz->trips.active[i - 2].temperature < + CELSIUS_TO_KELVIN(act) ? + tz->trips.active[i - 2].temperature : + CELSIUS_TO_KELVIN(act)); break; - if (act <= 0) /* no override requested */ - break; - if (i == 1) { /* 1 trip point */ - tz->trips.active[0].temperature = - CELSIUS_TO_KELVIN(act); - } else { /* multiple trips */ - /* - * Don't allow override higher than - * the next higher trip point - */ - tz->trips.active[i - 1].temperature = - (tz->trips.active[i - 2].temperature < - CELSIUS_TO_KELVIN(act) ? - tz->trips.active[i - 2].temperature : - CELSIUS_TO_KELVIN(act)); - } - break; + } else + tz->trips.active[i].flags.valid = 1; } name[2] = 'L'; - status = - acpi_evaluate_reference(tz->device->handle, name, NULL, - &tz->trips.active[i].devices); - if (ACPI_SUCCESS(status)) { - tz->trips.active[i].flags.valid = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found active threshold [%d]:[%lu]\n", - i, tz->trips.active[i].temperature)); - } else - ACPI_EXCEPTION((AE_INFO, status, - "Invalid active threshold [%d]", i)); + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + status = acpi_evaluate_reference(tz->device->handle, + name, NULL, &devices); + if (ACPI_FAILURE(status)) + tz->trips.active[i].flags.valid = 0; + else + tz->trips.active[i].flags.valid = 1; + + if (memcmp(&tz->trips.active[i].devices, &devices, + sizeof(struct acpi_handle_list))) { + memcpy(&tz->trips.active[i].devices, &devices, + sizeof(struct acpi_handle_list)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } + } + if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES)) + if (valid != tz->trips.active[i].flags.valid) + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); + + if (!tz->trips.active[i].flags.valid) + break; + } + + if (flag & ACPI_TRIPS_DEVICES) { + memset(&devices, 0, sizeof(struct acpi_handle_list)); + 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)); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); + } } return 0; } -static int acpi_thermal_get_devices(struct acpi_thermal *tz) +static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) { - acpi_status status = AE_OK; - - - if (!tz) - return -EINVAL; - - status = - acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return 0; + return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); } static int acpi_thermal_critical(struct acpi_thermal *tz) @@ -735,6 +782,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)); /* @@ -828,6 +878,290 @@ 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; + struct acpi_device *dev; + acpi_status status; + acpi_handle 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++) { + 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; + } + } + } + + 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++) { + 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; + } + } + } + + for (i = 0; i < tz->devices.count; i++) { + handle = tz->devices.handles[i]; + status = acpi_bus_get_device(handle, &dev); + if (ACPI_SUCCESS(status) && (dev == device)) { + 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; + acpi_status status; + 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; + + status = acpi_attach_data(tz->device->handle, + acpi_bus_private_data_handler, + tz->thermal_zone); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error attaching device data\n")); + return -ENODEV; + } + + 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; + acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler); +} + + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -1184,15 +1518,15 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) acpi_thermal_check(tz); break; case ACPI_THERMAL_NOTIFY_THRESHOLDS: - acpi_thermal_get_trip_points(tz); + 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, device->dev.bus_id, event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: - if (tz->flags.devices) - acpi_thermal_get_devices(tz); + 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, device->dev.bus_id, event, 0); @@ -1235,11 +1569,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) else acpi_thermal_get_polling_frequency(tz); - /* Get devices in this thermal zone [_TZD] (optional) */ - result = acpi_thermal_get_devices(tz); - if (!result) - tz->flags.devices = 1; - return 0; } @@ -1263,13 +1592,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); @@ -1280,19 +1615,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; } @@ -1332,6 +1669,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; diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index a54ff6b..82815cf 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -34,6 +34,7 @@ #include <linux/seq_file.h> #include <linux/input.h> #include <linux/backlight.h> +#include <linux/thermal.h> #include <linux/video_output.h> #include <asm/uaccess.h> @@ -179,6 +180,7 @@ struct acpi_video_device { struct acpi_device *dev; struct acpi_video_device_brightness *brightness; struct backlight_device *backlight; + struct thermal_cooling_device *cdev; struct output_device *output_dev; }; @@ -342,6 +344,54 @@ static struct output_properties acpi_output_properties = { .set_state = acpi_video_output_set, .get_status = acpi_video_output_get, }; + + +/* thermal cooling device callbacks */ +static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + + return sprintf(buf, "%d\n", video->brightness->count - 3); +} + +static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + unsigned long level; + int state; + + acpi_video_device_lcd_get_level_current(video, &level); + for (state = 2; state < video->brightness->count; state++) + if (level == video->brightness->levels[state]) + return sprintf(buf, "%d\n", + video->brightness->count - state - 1); + + return -EINVAL; +} + +static int +video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state) +{ + struct acpi_device *device = cdev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + int level; + + if ( state >= video->brightness->count - 2) + return -EINVAL; + + state = video->brightness->count - state; + level = video->brightness->levels[state -1]; + return acpi_video_device_lcd_set_level(video, level); +} + +static struct thermal_cooling_device_ops video_cooling_ops = { + .get_max_state = video_get_max_state, + .get_cur_state = video_get_cur_state, + .set_cur_state = video_set_cur_state, +}; + /* -------------------------------------------------------------------------- Video Management -------------------------------------------------------------------------- */ @@ -660,6 +710,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) kfree(obj); if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ + int result; static int count = 0; char *name; name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); @@ -672,8 +723,25 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->backlight->props.max_brightness = device->brightness->count-3; device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); backlight_update_status(device->backlight); - kfree(name); + + device->cdev = thermal_cooling_device_register("LCD", + device->dev, &video_cooling_ops); + if (device->cdev) { + printk(KERN_INFO PREFIX + "%s is registered as cooling_device%d\n", + device->dev->dev.bus_id, device->cdev->id); + result = sysfs_create_link(&device->dev->dev.kobj, + &device->cdev->device.kobj, + "thermal_cooling"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + result = sysfs_create_link(&device->cdev->device.kobj, + &device->dev->dev.kobj, + "device"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + } } if (device->cap._DCS && device->cap._DSS){ static int count = 0; @@ -1764,6 +1832,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) ACPI_DEVICE_NOTIFY, acpi_video_device_notify); backlight_device_unregister(device->backlight); + if (device->cdev) { + sysfs_remove_link(&device->dev->dev.kobj, + "thermal_cooling"); + sysfs_remove_link(&device->cdev->device.kobj, + "device"); + thermal_cooling_device_unregister(device->cdev); + device->cdev = NULL; + } video_output_unregister(device->output_dev); return 0; |