diff options
-rw-r--r-- | Documentation/thermal/sysfs-api.txt | 246 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-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 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/intel_menlow.c | 526 | ||||
-rw-r--r-- | drivers/thermal/Kconfig | 15 | ||||
-rw-r--r-- | drivers/thermal/Makefile | 5 | ||||
-rw-r--r-- | drivers/thermal/thermal.c | 714 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 2 | ||||
-rw-r--r-- | include/acpi/processor.h | 6 | ||||
-rw-r--r-- | include/linux/thermal.h | 94 |
19 files changed, 2450 insertions, 170 deletions
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt new file mode 100644 index 00000000000..5776e090359 --- /dev/null +++ b/Documentation/thermal/sysfs-api.txt @@ -0,0 +1,246 @@ +Generic Thermal Sysfs driver How To +========================= + +Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com> + +Updated: 2 January 2008 + +Copyright (c) 2008 Intel Corporation + + +0. Introduction + +The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors) +and thermal cooling devices (fan, processor...) to register with the thermal management +solution and to be a part of it. + +This how-to focusses on enabling new thermal zone and cooling devices to participate +in thermal management. +This solution is platform independent and any type of thermal zone devices and +cooling devices should be able to make use of the infrastructure. + +The main task of the thermal sysfs driver is to expose thermal zone attributes as well +as cooling device attributes to the user space. +An intelligent thermal management application can make decisions based on inputs +from thermal zone attributes (the current temperature and trip point temperature) +and throttle appropriate devices. + +[0-*] denotes any positive number starting from 0 +[1-*] denotes any positive number starting from 1 + +1. thermal sysfs driver interface functions + +1.1 thermal zone device interface +1.1.1 struct thermal_zone_device *thermal_zone_device_register(char *name, int trips, + void *devdata, struct thermal_zone_device_ops *ops) + + This interface function adds a new thermal zone device (sensor) to + /sys/class/thermal folder as thermal_zone[0-*]. + It tries to bind all the thermal cooling devices registered at the same time. + + name: the thermal zone name. + trips: the total number of trip points this thermal zone supports. + devdata: device private data + ops: thermal zone device callbacks. + .bind: bind the thermal zone device with a thermal cooling device. + .unbind: unbing the thermal zone device with a thermal cooling device. + .get_temp: get the current temperature of the thermal zone. + .get_mode: get the current mode (user/kernel) of the thermal zone. + "kernel" means thermal management is done in kernel. + "user" will prevent kernel thermal driver actions upon trip points + so that user applications can take charge of thermal management. + .set_mode: set the mode (user/kernel) of the thermal zone. + .get_trip_type: get the type of certain trip point. + .get_trip_temp: get the temperature above which the certain trip point + will be fired. + +1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz) + + This interface function removes the thermal zone device. + It deletes the corresponding entry form /sys/class/thermal folder and unbind all + the thermal cooling devices it uses. + +1.2 thermal cooling device interface +1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name, + void *devdata, struct thermal_cooling_device_ops *) + + This interface function adds a new thermal cooling device (fan/processor/...) to + /sys/class/thermal/ folder as cooling_device[0-*]. + It tries to bind itself to all the thermal zone devices register at the same time. + name: the cooling device name. + devdata: device private data. + ops: thermal cooling devices callbacks. + .get_max_state: get the Maximum throttle state of the cooling device. + .get_cur_state: get the Current throttle state of the cooling device. + .set_cur_state: set the Current throttle state of the cooling device. + +1.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) + + This interface function remove the thermal cooling device. + It deletes the corresponding entry form /sys/class/thermal folder and unbind + itself from all the thermal zone devices using it. + +1.3 interface for binding a thermal zone device with a thermal cooling device +1.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, + int trip, struct thermal_cooling_device *cdev); + + This interface function bind a thermal cooling device to the certain trip point + of a thermal zone device. + This function is usually called in the thermal zone device .bind callback. + tz: the thermal zone device + cdev: thermal cooling device + trip: indicates which trip point the cooling devices is associated with + in this thermal zone. + +1.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, + int trip, struct thermal_cooling_device *cdev); + + This interface function unbind a thermal cooling device from the certain trip point + of a thermal zone device. + This function is usually called in the thermal zone device .unbind callback. + tz: the thermal zone device + cdev: thermal cooling device + trip: indicates which trip point the cooling devices is associated with + in this thermal zone. + +2. sysfs attributes structure + +RO read only value +RW read/write value + +All thermal sysfs attributes will be represented under /sys/class/thermal +/sys/class/thermal/ + +Thermal zone device sys I/F, created once it's registered: +|thermal_zone[0-*]: + |-----type: Type of the thermal zone + |-----temp: Current temperature + |-----mode: Working mode of the thermal zone + |-----trip_point_[0-*]_temp: Trip point temperature + |-----trip_point_[0-*]_type: Trip point type + +Thermal cooling device sys I/F, created once it's registered: +|cooling_device[0-*]: + |-----type : Type of the cooling device(processor/fan/...) + |-----max_state: Maximum cooling state of the cooling device + |-----cur_state: Current cooling state of the cooling device + + +These two dynamic attributes are created/removed in pairs. +They represent the relationship between a thermal zone and its associated cooling device. +They are created/removed for each +thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful exection. + +|thermal_zone[0-*] + |-----cdev[0-*]: The [0-*]th cooling device in the current thermal zone + |-----cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with + + +*************************** +* Thermal zone attributes * +*************************** + +type Strings which represent the thermal zone type. + This is given by thermal zone driver as part of registration. + Eg: "ACPI thermal zone" indicates it's a ACPI thermal device + RO + Optional + +temp Current temperature as reported by thermal zone (sensor) + Unit: degree celsius + RO + Required + +mode One of the predifned values in [kernel, user] + This file gives information about the algorithm + that is currently managing the thermal zone. + It can be either default kernel based algorithm + or user space application. + RW + Optional + kernel = Thermal management in kernel thermal zone driver. + user = Preventing kernel thermal zone driver actions upon + trip points so that user application can take full + charge of the thermal management. + +trip_point_[0-*]_temp The temperature above which trip point will be fired + Unit: degree celsius + RO + Optional + +trip_point_[0-*]_type Strings which indicate the type of the trip point + Eg. it can be one of critical, hot, passive, + active[0-*] for ACPI thermal zone. + RO + Optional + +cdev[0-*] Sysfs link to the thermal cooling device node where the sys I/F + for cooling device throttling control represents. + RO + Optional + +cdev[0-*]_trip_point The trip point with which cdev[0-*] is assocated in this thermal zone + -1 means the cooling device is not associated with any trip point. + RO + Optional + +****************************** +* Cooling device attributes * +****************************** + +type String which represents the type of device + eg: For generic ACPI: this should be "Fan", + "Processor" or "LCD" + eg. For memory controller device on intel_menlow platform: + this should be "Memory controller" + RO + Optional + +max_state The maximum permissible cooling state of this cooling device. + RO + Required + +cur_state The current cooling state of this cooling device. + the value can any integer numbers between 0 and max_state, + cur_state == 0 means no cooling + cur_state == max_state means the maximum cooling. + RW + Required + +3. A simple implementation + +ACPI thermal zone may support multiple trip points like critical/hot/passive/active. +If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time, +it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all. +It has one processor and one fan, which are both registered as thermal_cooling_device. +If the processor is listed in _PSL method, and the fan is listed in _AL0 method, +the sys I/F structure will be built like this: + +/sys/class/thermal: + +|thermal_zone1: + |-----type: ACPI thermal zone + |-----temp: 37 + |-----mode: kernel + |-----trip_point_0_temp: 100 + |-----trip_point_0_type: critical + |-----trip_point_1_temp: 80 + |-----trip_point_1_type: passive + |-----trip_point_2_temp: 70 + |-----trip_point_2_type: active[0] + |-----trip_point_3_temp: 60 + |-----trip_point_3_type: active[1] + |-----cdev0: --->/sys/class/thermal/cooling_device0 + |-----cdev0_trip_point: 1 /* cdev0 can be used for passive */ + |-----cdev1: --->/sys/class/thermal/cooling_device3 + |-----cdev1_trip_point: 2 /* cdev1 can be used for active[0]*/ + +|cooling_device0: + |-----type: Processor + |-----max_state: 8 + |-----cur_state: 0 + +|cooling_device3: + |-----type: Fan + |-----max_state: 2 + |-----cur_state: 0 diff --git a/drivers/Kconfig b/drivers/Kconfig index d74d9fbb9fd..b86877bdc7a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -60,6 +60,8 @@ source "drivers/power/Kconfig" source "drivers/hwmon/Kconfig" +source "drivers/thermal/Kconfig" + source "drivers/watchdog/Kconfig" source "drivers/ssb/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index f1c11db52a5..30ba97ec5eb 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -65,6 +65,7 @@ obj-y += i2c/ obj-$(CONFIG_W1) += w1/ obj-$(CONFIG_POWER_SUPPLY) += power/ obj-$(CONFIG_HWMON) += hwmon/ +obj-$(CONFIG_THERMAL) += thermal/ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ccf6ea95f68..558372957fd 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 1b4cf984b08..8df325dafe0 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 a6e149d692c..48cb705b274 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 c53113e1800..315fd8f7e8a 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 06e6f3fb882..9cb43f52f7b 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 3a0af9a8cd2..8d4b79b4f93 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)); - } |