aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/thermal/sysfs-api.txt246
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/Kconfig1
-rw-r--r--drivers/acpi/bus.c25
-rw-r--r--drivers/acpi/fan.c92
-rw-r--r--drivers/acpi/processor_core.c23
-rw-r--r--drivers/acpi/processor_thermal.c134
-rw-r--r--drivers/acpi/thermal.c646
-rw-r--r--drivers/acpi/video.c78
-rw-r--r--drivers/misc/Kconfig9
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/intel_menlow.c526
-rw-r--r--drivers/thermal/Kconfig15
-rw-r--r--drivers/thermal/Makefile5
-rw-r--r--drivers/thermal/thermal.c714
-rw-r--r--include/acpi/acpi_bus.h2
-rw-r--r--include/acpi/processor.h6
-rw-r--r--include/linux/thermal.h94
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));
- }