aboutsummaryrefslogtreecommitdiffstats
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 000000000000..5776e090359d
--- /dev/null
+++ b/Documentation/thermal/sysfs-api.txt
@@ -0,0 +1,246 @@
1Generic Thermal Sysfs driver How To
2=========================
3
4Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com>
5
6Updated: 2 January 2008
7
8Copyright (c) 2008 Intel Corporation
9
10
110. Introduction
12
13The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors)
14and thermal cooling devices (fan, processor...) to register with the thermal management
15solution and to be a part of it.
16
17This how-to focusses on enabling new thermal zone and cooling devices to participate
18in thermal management.
19This solution is platform independent and any type of thermal zone devices and
20cooling devices should be able to make use of the infrastructure.
21
22The main task of the thermal sysfs driver is to expose thermal zone attributes as well
23as cooling device attributes to the user space.
24An intelligent thermal management application can make decisions based on inputs
25from thermal zone attributes (the current temperature and trip point temperature)
26and throttle appropriate devices.
27
28[0-*] denotes any positive number starting from 0
29[1-*] denotes any positive number starting from 1
30
311. thermal sysfs driver interface functions
32
331.1 thermal zone device interface
341.1.1 struct thermal_zone_device *thermal_zone_device_register(char *name, int trips,
35 void *devdata, struct thermal_zone_device_ops *ops)
36
37 This interface function adds a new thermal zone device (sensor) to
38 /sys/class/thermal folder as thermal_zone[0-*].
39 It tries to bind all the thermal cooling devices registered at the same time.
40
41 name: the thermal zone name.
42 trips: the total number of trip points this thermal zone supports.
43 devdata: device private data
44 ops: thermal zone device callbacks.
45 .bind: bind the thermal zone device with a thermal cooling device.
46 .unbind: unbing the thermal zone device with a thermal cooling device.
47 .get_temp: get the current temperature of the thermal zone.
48 .get_mode: get the current mode (user/kernel) of the thermal zone.
49 "kernel" means thermal management is done in kernel.
50 "user" will prevent kernel thermal driver actions upon trip points
51 so that user applications can take charge of thermal management.
52 .set_mode: set the mode (user/kernel) of the thermal zone.
53 .get_trip_type: get the type of certain trip point.
54 .get_trip_temp: get the temperature above which the certain trip point
55 will be fired.
56
571.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
58
59 This interface function removes the thermal zone device.
60 It deletes the corresponding entry form /sys/class/thermal folder and unbind all
61 the thermal cooling devices it uses.
62
631.2 thermal cooling device interface
641.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
65 void *devdata, struct thermal_cooling_device_ops *)
66
67 This interface function adds a new thermal cooling device (fan/processor/...) to
68 /sys/class/thermal/ folder as cooling_device[0-*].
69 It tries to bind itself to all the thermal zone devices register at the same time.
70 name: the cooling device name.
71 devdata: device private data.
72 ops: thermal cooling devices callbacks.
73 .get_max_state: get the Maximum throttle state of the cooling device.
74 .get_cur_state: get the Current throttle state of the cooling device.
75 .set_cur_state: set the Current throttle state of the cooling device.
76
771.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
78
79 This interface function remove the thermal cooling device.
80 It deletes the corresponding entry form /sys/class/thermal folder and unbind
81 itself from all the thermal zone devices using it.
82
831.3 interface for binding a thermal zone device with a thermal cooling device
841.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
85 int trip, struct thermal_cooling_device *cdev);
86
87 This interface function bind a thermal cooling device to the certain trip point
88 of a thermal zone device.
89 This function is usually called in the thermal zone device .bind callback.
90 tz: the thermal zone device
91 cdev: thermal cooling device
92 trip: indicates which trip point the cooling devices is associated with
93 in this thermal zone.
94
951.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
96 int trip, struct thermal_cooling_device *cdev);
97
98 This interface function unbind a thermal cooling device from the certain trip point
99 of a thermal zone device.
100 This function is usually called in the thermal zone device .unbind callback.
101 tz: the thermal zone device
102 cdev: thermal cooling device
103 trip: indicates which trip point the cooling devices is associated with
104 in this thermal zone.
105
1062. sysfs attributes structure
107
108RO read only value
109RW read/write value
110
111All thermal sysfs attributes will be represented under /sys/class/thermal
112/sys/class/thermal/
113
114Thermal zone device sys I/F, created once it's registered:
115|thermal_zone[0-*]:
116 |-----type: Type of the thermal zone
117 |-----temp: Current temperature
118 |-----mode: Working mode of the thermal zone
119 |-----trip_point_[0-*]_temp: Trip point temperature
120 |-----trip_point_[0-*]_type: Trip point type
121
122Thermal cooling device sys I/F, created once it's registered:
123|cooling_device[0-*]:
124 |-----type : Type of the cooling device(processor/fan/...)
125 |-----max_state: Maximum cooling state of the cooling device
126 |-----cur_state: Current cooling state of the cooling device
127
128
129These two dynamic attributes are created/removed in pairs.
130They represent the relationship between a thermal zone and its associated cooling device.
131They are created/removed for each
132thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful exection.
133
134|thermal_zone[0-*]
135 |-----cdev[0-*]: The [0-*]th cooling device in the current thermal zone
136 |-----cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with
137
138
139***************************
140* Thermal zone attributes *
141***************************
142
143type Strings which represent the thermal zone type.
144 This is given by thermal zone driver as part of registration.
145 Eg: "ACPI thermal zone" indicates it's a ACPI thermal device
146 RO
147 Optional
148
149temp Current temperature as reported by thermal zone (sensor)
150 Unit: degree celsius
151 RO
152 Required
153
154mode One of the predifned values in [kernel, user]
155 This file gives information about the algorithm
156 that is currently managing the thermal zone.
157 It can be either default kernel based algorithm
158 or user space application.
159 RW
160 Optional
161 kernel = Thermal management in kernel thermal zone driver.
162 user = Preventing kernel thermal zone driver actions upon
163 trip points so that user application can take full
164 charge of the thermal management.
165
166trip_point_[0-*]_temp The temperature above which trip point will be fired
167 Unit: degree celsius
168 RO
169 Optional
170
171trip_point_[0-*]_type Strings which indicate the type of the trip point
172 Eg. it can be one of critical, hot, passive,
173 active[0-*] for ACPI thermal zone.
174 RO
175 Optional
176
177cdev[0-*] Sysfs link to the thermal cooling device node where the sys I/F
178 for cooling device throttling control represents.
179 RO
180 Optional
181
182cdev[0-*]_trip_point The trip point with which cdev[0-*] is assocated in this thermal zone
183 -1 means the cooling device is not associated with any trip point.
184 RO
185 Optional
186
187******************************
188* Cooling device attributes *
189******************************
190
191type String which represents the type of device
192 eg: For generic ACPI: this should be "Fan",
193 "Processor" or "LCD"
194 eg. For memory controller device on intel_menlow platform:
195 this should be "Memory controller"
196 RO
197 Optional
198
199max_state The maximum permissible cooling state of this cooling device.
200 RO
201 Required
202
203cur_state The current cooling state of this cooling device.
204 the value can any integer numbers between 0 and max_state,
205 cur_state == 0 means no cooling
206 cur_state == max_state means the maximum cooling.
207 RW
208 Required
209
2103. A simple implementation
211
212ACPI thermal zone may support multiple trip points like critical/hot/passive/active.
213If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time,
214it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all.
215It has one processor and one fan, which are both registered as thermal_cooling_device.
216If the processor is listed in _PSL method, and the fan is listed in _AL0 method,
217the sys I/F structure will be built like this:
218
219/sys/class/thermal:
220
221|thermal_zone1:
222 |-----type: ACPI thermal zone
223 |-----temp: 37
224 |-----mode: kernel
225 |-----trip_point_0_temp: 100
226 |-----trip_point_0_type: critical
227 |-----trip_point_1_temp: 80
228 |-----trip_point_1_type: passive
229 |-----trip_point_2_temp: 70
230 |-----trip_point_2_type: active[0]
231 |-----trip_point_3_temp: 60
232 |-----trip_point_3_type: active[1]
233 |-----cdev0: --->/sys/class/thermal/cooling_device0
234 |-----cdev0_trip_point: 1 /* cdev0 can be used for passive */
235 |-----cdev1: --->/sys/class/thermal/cooling_device3
236 |-----cdev1_trip_point: 2 /* cdev1 can be used for active[0]*/
237
238|cooling_device0:
239 |-----type: Processor
240 |-----max_state: 8
241 |-----cur_state: 0
242
243|cooling_device3:
244 |-----type: Fan
245 |-----max_state: 2
246 |-----cur_state: 0
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d74d9fbb9fd2..b86877bdc7ac 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -60,6 +60,8 @@ source "drivers/power/Kconfig"
60 60
61source "drivers/hwmon/Kconfig" 61source "drivers/hwmon/Kconfig"
62 62
63source "drivers/thermal/Kconfig"
64
63source "drivers/watchdog/Kconfig" 65source "drivers/watchdog/Kconfig"
64 66
65source "drivers/ssb/Kconfig" 67source "drivers/ssb/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index f1c11db52a57..30ba97ec5eb5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -65,6 +65,7 @@ obj-y += i2c/
65obj-$(CONFIG_W1) += w1/ 65obj-$(CONFIG_W1) += w1/
66obj-$(CONFIG_POWER_SUPPLY) += power/ 66obj-$(CONFIG_POWER_SUPPLY) += power/
67obj-$(CONFIG_HWMON) += hwmon/ 67obj-$(CONFIG_HWMON) += hwmon/
68obj-$(CONFIG_THERMAL) += thermal/
68obj-$(CONFIG_WATCHDOG) += watchdog/ 69obj-$(CONFIG_WATCHDOG) += watchdog/
69obj-$(CONFIG_PHONE) += telephony/ 70obj-$(CONFIG_PHONE) += telephony/
70obj-$(CONFIG_MD) += md/ 71obj-$(CONFIG_MD) += md/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ccf6ea95f68c..558372957fd3 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -186,6 +186,7 @@ config ACPI_HOTPLUG_CPU
186config ACPI_THERMAL 186config ACPI_THERMAL
187 tristate "Thermal Zone" 187 tristate "Thermal Zone"
188 depends on ACPI_PROCESSOR 188 depends on ACPI_PROCESSOR
189 select THERMAL
189 default y 190 default y
190 help 191 help
191 This driver adds support for ACPI thermal zones. Most mobile and 192 This driver adds support for ACPI thermal zones. Most mobile and
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 1b4cf984b081..8df325dafe0f 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -122,6 +122,31 @@ int acpi_bus_get_status(struct acpi_device *device)
122 122
123EXPORT_SYMBOL(acpi_bus_get_status); 123EXPORT_SYMBOL(acpi_bus_get_status);
124 124
125void acpi_bus_private_data_handler(acpi_handle handle,
126 u32 function, void *context)
127{
128 return;
129}
130EXPORT_SYMBOL(acpi_bus_private_data_handler);
131
132int acpi_bus_get_private_data(acpi_handle handle, void **data)
133{
134 acpi_status status = AE_OK;
135
136 if (!*data)
137 return -EINVAL;
138
139 status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
140 if (ACPI_FAILURE(status) || !*data) {
141 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
142 handle));
143 return -ENODEV;
144 }
145
146 return 0;
147}
148EXPORT_SYMBOL(acpi_bus_get_private_data);
149
125/* -------------------------------------------------------------------------- 150/* --------------------------------------------------------------------------
126 Power Management 151 Power Management
127 -------------------------------------------------------------------------- */ 152 -------------------------------------------------------------------------- */
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index a6e149d692cb..48cb705b274a 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -30,7 +30,7 @@
30#include <linux/proc_fs.h> 30#include <linux/proc_fs.h>
31#include <linux/seq_file.h> 31#include <linux/seq_file.h>
32#include <asm/uaccess.h> 32#include <asm/uaccess.h>
33 33#include <linux/thermal.h>
34#include <acpi/acpi_bus.h> 34#include <acpi/acpi_bus.h>
35#include <acpi/acpi_drivers.h> 35#include <acpi/acpi_drivers.h>
36 36
@@ -68,9 +68,55 @@ static struct acpi_driver acpi_fan_driver = {
68 }, 68 },
69}; 69};
70 70
71/* thermal cooling device callbacks */
72static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf)
73{
74 /* ACPI fan device only support two states: ON/OFF */
75 return sprintf(buf, "1\n");
76}
77
78static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
79{
80 struct acpi_device *device = cdev->devdata;
81 int state;
82 int result;
83
84 if (!device)
85 return -EINVAL;
86
87 result = acpi_bus_get_power(device->handle, &state);
88 if (result)
89 return result;
90
91 return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
92 (state == ACPI_STATE_D0 ? "1" : "unknown"));
93}
94
95static int
96fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
97{
98 struct acpi_device *device = cdev->devdata;
99 int result;
100
101 if (!device || (state != 0 && state != 1))
102 return -EINVAL;
103
104 result = acpi_bus_set_power(device->handle,
105 state ? ACPI_STATE_D0 : ACPI_STATE_D3);
106
107 return result;
108}
109
110static struct thermal_cooling_device_ops fan_cooling_ops = {
111 .get_max_state = fan_get_max_state,
112 .get_cur_state = fan_get_cur_state,
113 .set_cur_state = fan_set_cur_state,
114};
115
71/* -------------------------------------------------------------------------- 116/* --------------------------------------------------------------------------
72 FS Interface (/proc) 117 FS Interface (/proc)
73 -------------------------------------------------------------------------- */ 118 -------------------------------------------------------------------------- */
119#ifdef CONFIG_ACPI_PROCFS
74 120
75static struct proc_dir_entry *acpi_fan_dir; 121static struct proc_dir_entry *acpi_fan_dir;
76 122
@@ -171,7 +217,17 @@ static int acpi_fan_remove_fs(struct acpi_device *device)
171 217
172 return 0; 218 return 0;
173} 219}
220#else
221static int acpi_fan_add_fs(struct acpi_device *device)
222{
223 return 0;
224}
174 225
226static int acpi_fan_remove_fs(struct acpi_device *device)
227{
228 return 0;
229}
230#endif
175/* -------------------------------------------------------------------------- 231/* --------------------------------------------------------------------------
176 Driver Interface 232 Driver Interface
177 -------------------------------------------------------------------------- */ 233 -------------------------------------------------------------------------- */
@@ -179,9 +235,8 @@ static int acpi_fan_remove_fs(struct acpi_device *device)
179static int acpi_fan_add(struct acpi_device *device) 235static int acpi_fan_add(struct acpi_device *device)
180{ 236{
181 int result = 0; 237 int result = 0;
182 struct acpi_fan *fan = NULL;
183 int state = 0; 238 int state = 0;
184 239 struct thermal_cooling_device *cdev;
185 240
186 if (!device) 241 if (!device)
187 return -EINVAL; 242 return -EINVAL;
@@ -199,6 +254,25 @@ static int acpi_fan_add(struct acpi_device *device)
199 acpi_bus_set_power(device->handle, state); 254 acpi_bus_set_power(device->handle, state);
200 device->flags.force_power_state = 0; 255 device->flags.force_power_state = 0;
201 256
257 cdev = thermal_cooling_device_register("Fan", device,
258 &fan_cooling_ops);
259 if (cdev)
260 printk(KERN_INFO PREFIX
261 "%s is registered as cooling_device%d\n",
262 device->dev.bus_id, cdev->id);
263 else
264 goto end;
265 acpi_driver_data(device) = cdev;
266 result = sysfs_create_link(&device->dev.kobj, &cdev->device.kobj,
267 "thermal_cooling");
268 if (result)
269 return result;
270
271 result = sysfs_create_link(&cdev->device.kobj, &device->dev.kobj,
272 "device");
273 if (result)
274 return result;
275
202 result = acpi_fan_add_fs(device); 276 result = acpi_fan_add_fs(device);
203 if (result) 277 if (result)
204 goto end; 278 goto end;
@@ -208,18 +282,20 @@ static int acpi_fan_add(struct acpi_device *device)
208 !device->power.state ? "on" : "off"); 282 !device->power.state ? "on" : "off");
209 283
210 end: 284 end:
211 if (result)
212 kfree(fan);
213
214 return result; 285 return result;
215} 286}
216 287
217static int acpi_fan_remove(struct acpi_device *device, int type) 288static int acpi_fan_remove(struct acpi_device *device, int type)
218{ 289{
219 if (!device || !acpi_driver_data(device)) 290 struct thermal_cooling_device *cdev = acpi_driver_data(device);
291
292 if (!device || !cdev)
220 return -EINVAL; 293 return -EINVAL;
221 294
222 acpi_fan_remove_fs(device); 295 acpi_fan_remove_fs(device);
296 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
297 sysfs_remove_link(&cdev->device.kobj, "device");
298 thermal_cooling_device_unregister(cdev);
223 299
224 return 0; 300 return 0;
225} 301}
@@ -261,10 +337,12 @@ static int __init acpi_fan_init(void)
261 int result = 0; 337 int result = 0;
262 338
263 339
340#ifdef CONFIG_ACPI_PROCFS
264 acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); 341 acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
265 if (!acpi_fan_dir) 342 if (!acpi_fan_dir)
266 return -ENODEV; 343 return -ENODEV;
267 acpi_fan_dir->owner = THIS_MODULE; 344 acpi_fan_dir->owner = THIS_MODULE;
345#endif
268 346
269 result = acpi_bus_register_driver(&acpi_fan_driver); 347 result = acpi_bus_register_driver(&acpi_fan_driver);
270 if (result < 0) { 348 if (result < 0) {
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index c53113e18004..315fd8f7e8a1 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)
668 668
669 acpi_processor_power_init(pr, device); 669 acpi_processor_power_init(pr, device);
670 670
671 pr->cdev = thermal_cooling_device_register("Processor", device,
672 &processor_cooling_ops);
673 if (pr->cdev)
674 printk(KERN_INFO PREFIX
675 "%s is registered as cooling_device%d\n",
676 device->dev.bus_id, pr->cdev->id);
677 else
678 goto end;
679
680 result = sysfs_create_link(&device->dev.kobj, &pr->cdev->device.kobj,
681 "thermal_cooling");
682 if (result)
683 return result;
684 result = sysfs_create_link(&pr->cdev->device.kobj, &device->dev.kobj,
685 "device");
686 if (result)
687 return result;
688
671 if (pr->flags.throttling) { 689 if (pr->flags.throttling) {
672 printk(KERN_INFO PREFIX "%s [%s] (supports", 690 printk(KERN_INFO PREFIX "%s [%s] (supports",
673 acpi_device_name(device), acpi_device_bid(device)); 691 acpi_device_name(device), acpi_device_bid(device));
@@ -791,6 +809,11 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
791 809
792 acpi_processor_remove_fs(device); 810 acpi_processor_remove_fs(device);
793 811
812 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
813 sysfs_remove_link(&pr->cdev->device.kobj, "device");
814 thermal_cooling_device_unregister(pr->cdev);
815 pr->cdev = NULL;
816
794 processors[pr->id] = NULL; 817 processors[pr->id] = NULL;
795 818
796 kfree(pr); 819 kfree(pr);
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index 06e6f3fb8825..9cb43f52f7b6 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -32,6 +32,7 @@
32#include <linux/cpufreq.h> 32#include <linux/cpufreq.h>
33#include <linux/proc_fs.h> 33#include <linux/proc_fs.h>
34#include <linux/seq_file.h> 34#include <linux/seq_file.h>
35#include <linux/sysdev.h>
35 36
36#include <asm/uaccess.h> 37#include <asm/uaccess.h>
37 38
@@ -93,6 +94,9 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr)
93 * _any_ cpufreq driver and not only the acpi-cpufreq driver. 94 * _any_ cpufreq driver and not only the acpi-cpufreq driver.
94 */ 95 */
95 96
97#define CPUFREQ_THERMAL_MIN_STEP 0
98#define CPUFREQ_THERMAL_MAX_STEP 3
99
96static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; 100static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];
97static unsigned int acpi_thermal_cpufreq_is_init = 0; 101static unsigned int acpi_thermal_cpufreq_is_init = 0;
98 102
@@ -109,8 +113,9 @@ static int acpi_thermal_cpufreq_increase(unsigned int cpu)
109 if (!cpu_has_cpufreq(cpu)) 113 if (!cpu_has_cpufreq(cpu))
110 return -ENODEV; 114 return -ENODEV;
111 115
112 if (cpufreq_thermal_reduction_pctg[cpu] < 60) { 116 if (cpufreq_thermal_reduction_pctg[cpu] <
113 cpufreq_thermal_reduction_pctg[cpu] += 20; 117 CPUFREQ_THERMAL_MAX_STEP) {
118 cpufreq_thermal_reduction_pctg[cpu]++;
114 cpufreq_update_policy(cpu); 119 cpufreq_update_policy(cpu);
115 return 0; 120 return 0;
116 } 121 }
@@ -123,8 +128,9 @@ static int acpi_thermal_cpufreq_decrease(unsigned int cpu)
123 if (!cpu_has_cpufreq(cpu)) 128 if (!cpu_has_cpufreq(cpu))
124 return -ENODEV; 129 return -ENODEV;
125 130
126 if (cpufreq_thermal_reduction_pctg[cpu] > 20) 131 if (cpufreq_thermal_reduction_pctg[cpu] >
127 cpufreq_thermal_reduction_pctg[cpu] -= 20; 132 (CPUFREQ_THERMAL_MIN_STEP + 1))
133 cpufreq_thermal_reduction_pctg[cpu]--;
128 else 134 else
129 cpufreq_thermal_reduction_pctg[cpu] = 0; 135 cpufreq_thermal_reduction_pctg[cpu] = 0;
130 cpufreq_update_policy(cpu); 136 cpufreq_update_policy(cpu);
@@ -143,7 +149,7 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb,
143 149
144 max_freq = 150 max_freq =
145 (policy->cpuinfo.max_freq * 151 (policy->cpuinfo.max_freq *
146 (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100; 152 (100 - cpufreq_thermal_reduction_pctg[policy->cpu] * 20)) / 100;
147 153
148 cpufreq_verify_within_limits(policy, 0, max_freq); 154 cpufreq_verify_within_limits(policy, 0, max_freq);
149 155
@@ -155,6 +161,32 @@ static struct notifier_block acpi_thermal_cpufreq_notifier_block = {
155 .notifier_call = acpi_thermal_cpufreq_notifier, 161 .notifier_call = acpi_thermal_cpufreq_notifier,
156}; 162};
157 163
164static int cpufreq_get_max_state(unsigned int cpu)
165{
166 if (!cpu_has_cpufreq(cpu))
167 return 0;
168
169 return CPUFREQ_THERMAL_MAX_STEP;
170}
171
172static int cpufreq_get_cur_state(unsigned int cpu)
173{
174 if (!cpu_has_cpufreq(cpu))
175 return 0;
176
177 return cpufreq_thermal_reduction_pctg[cpu];
178}
179
180static int cpufreq_set_cur_state(unsigned int cpu, int state)
181{
182 if (!cpu_has_cpufreq(cpu))
183 return 0;
184
185 cpufreq_thermal_reduction_pctg[cpu] = state;
186 cpufreq_update_policy(cpu);
187 return 0;
188}
189
158void acpi_thermal_cpufreq_init(void) 190void acpi_thermal_cpufreq_init(void)
159{ 191{
160 int i; 192 int i;
@@ -179,6 +211,20 @@ void acpi_thermal_cpufreq_exit(void)
179} 211}
180 212
181#else /* ! CONFIG_CPU_FREQ */ 213#else /* ! CONFIG_CPU_FREQ */
214static int cpufreq_get_max_state(unsigned int cpu)
215{
216 return 0;
217}
218
219static int cpufreq_get_cur_state(unsigned int cpu)
220{
221 return 0;
222}
223
224static int cpufreq_set_cur_state(unsigned int cpu, int state)
225{
226 return 0;
227}
182 228
183static int acpi_thermal_cpufreq_increase(unsigned int cpu) 229static int acpi_thermal_cpufreq_increase(unsigned int cpu)
184{ 230{
@@ -310,6 +356,84 @@ int acpi_processor_get_limit_info(struct acpi_processor *pr)
310 return 0; 356 return 0;
311} 357}
312 358
359/* thermal coolign device callbacks */
360static int acpi_processor_max_state(struct acpi_processor *pr)
361{
362 int max_state = 0;
363
364 /*
365 * There exists four states according to
366 * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3
367 */
368 max_state += cpufreq_get_max_state(pr->id);
369 if (pr->flags.throttling)
370 max_state += (pr->throttling.state_count -1);
371
372 return max_state;
373}
374static int
375processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)
376{
377 struct acpi_device *device = cdev->devdata;
378 struct acpi_processor *pr = acpi_driver_data(device);
379
380 if (!device || !pr)
381 return -EINVAL;
382
383 return sprintf(buf, "%d\n", acpi_processor_max_state(pr));
384}
385
386static int
387processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
388{
389 struct acpi_device *device = cdev->devdata;
390 struct acpi_processor *pr = acpi_driver_data(device);
391 int cur_state;
392
393 if (!device || !pr)
394 return -EINVAL;
395
396 cur_state = cpufreq_get_cur_state(pr->id);
397 if (pr->flags.throttling)
398 cur_state += pr->throttling.state;
399
400 return sprintf(buf, "%d\n", cur_state);
401}
402
403static int
404processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
405{
406 struct acpi_device *device = cdev->devdata;
407 struct acpi_processor *pr = acpi_driver_data(device);
408 int result = 0;
409 int max_pstate;
410
411 if (!device || !pr)
412 return -EINVAL;
413
414 max_pstate = cpufreq_get_max_state(pr->id);
415
416 if (state > acpi_processor_max_state(pr))
417 return -EINVAL;
418
419 if (state <= max_pstate) {
420 if (pr->flags.throttling && pr->throttling.state)
421 result = acpi_processor_set_throttling(pr, 0);
422 cpufreq_set_cur_state(pr->id, state);
423 } else {
424 cpufreq_set_cur_state(pr->id, max_pstate);
425 result = acpi_processor_set_throttling(pr,
426 state - max_pstate);
427 }
428 return result;
429}
430
431struct thermal_cooling_device_ops processor_cooling_ops = {
432 .get_max_state = processor_get_max_state,
433 .get_cur_state = processor_get_cur_state,
434 .set_cur_state = processor_set_cur_state,
435};
436
313/* /proc interface */ 437/* /proc interface */
314 438
315static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) 439static 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 3a0af9a8cd27..8d4b79b4f933 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -43,7 +43,7 @@
43#include <linux/seq_file.h> 43#include <linux/seq_file.h>
44#include <linux/reboot.h> 44#include <linux/reboot.h>
45#include <asm/uaccess.h> 45#include <asm/uaccess.h>
46 46#include <linux/thermal.h>
47#include <acpi/acpi_bus.h> 47#include <acpi/acpi_bus.h>
48#include <acpi/acpi_drivers.h> 48#include <acpi/acpi_drivers.h>
49 49
@@ -65,9 +65,6 @@
65#define ACPI_THERMAL_MAX_ACTIVE 10 65#define ACPI_THERMAL_MAX_ACTIVE 10
66#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 66#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
67 67
68#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
69#define CELSIUS_TO_KELVIN(t) ((t+273)*10)
70
71#define _COMPONENT ACPI_THERMAL_COMPONENT 68#define _COMPONENT ACPI_THERMAL_COMPONENT
72ACPI_MODULE_NAME("thermal"); 69ACPI_MODULE_NAME("thermal");
73 70
@@ -195,6 +192,8 @@ struct acpi_thermal {
195 struct acpi_thermal_trips trips; 192 struct acpi_thermal_trips trips;
196 struct acpi_handle_list devices; 193 struct acpi_handle_list devices;
197 struct timer_list timer; 194 struct timer_list timer;
195 struct thermal_zone_device *thermal_zone;
196 int tz_enabled;
198 struct mutex lock; 197 struct mutex lock;
199}; 198};
200 199
@@ -321,173 +320,221 @@ static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
321 return 0; 320 return 0;
322} 321}
323 322
324static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) 323#define ACPI_TRIPS_CRITICAL 0x01
325{ 324#define ACPI_TRIPS_HOT 0x02
326 acpi_status status = AE_OK; 325#define ACPI_TRIPS_PASSIVE 0x04
327 int i = 0; 326#define ACPI_TRIPS_ACTIVE 0x08
327#define ACPI_TRIPS_DEVICES 0x10
328 328
329#define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
330#define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
329 331
330 if (!tz) 332#define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
331 return -EINVAL; 333 ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
334 ACPI_TRIPS_DEVICES)
332 335
333 /* Critical Shutdown (required) */ 336/*
334 337 * This exception is thrown out in two cases:
335 status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, 338 * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
336 &tz->trips.critical.temperature); 339 * when re-evaluating the AML code.
337 if (ACPI_FAILURE(status)) { 340 * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
338 tz->trips.critical.flags.valid = 0; 341 * We need to re-bind the cooling devices of a thermal zone when this occurs.
339 ACPI_EXCEPTION((AE_INFO, status, "No critical threshold")); 342 */
340 return -ENODEV; 343#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \
341 } else { 344do { \
342 tz->trips.critical.flags.valid = 1; 345 if (flags != ACPI_TRIPS_INIT) \
343 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 346 ACPI_EXCEPTION((AE_INFO, AE_ERROR, \
344 "Found critical threshold [%lu]\n", 347 "ACPI thermal trip point %s changed\n" \
345 tz->trips.critical.temperature)); 348 "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \
346 } 349} while (0)
350
351static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
352{
353 acpi_status status = AE_OK;
354 struct acpi_handle_list devices;
355 int valid = 0;
356 int i;
347 357
348 if (tz->trips.critical.flags.valid == 1) { 358 /* Critical Shutdown (required) */
349 if (crt == -1) { 359 if (flag & ACPI_TRIPS_CRITICAL) {
360 status = acpi_evaluate_integer(tz->device->handle,
361 "_CRT", NULL, &tz->trips.critical.temperature);
362 if (ACPI_FAILURE(status)) {
350 tz->trips.critical.flags.valid = 0; 363 tz->trips.critical.flags.valid = 0;
351 } else if (crt > 0) { 364 ACPI_EXCEPTION((AE_INFO, status,
352 unsigned long crt_k = CELSIUS_TO_KELVIN(crt); 365 "No critical threshold"));
353 366 return -ENODEV;
354 /* 367 } else {
355 * Allow override to lower critical threshold 368 tz->trips.critical.flags.valid = 1;
356 */ 369 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
357 if (crt_k < tz->trips.critical.temperature) 370 "Found critical threshold [%lu]\n",
358 tz->trips.critical.temperature = crt_k; 371 tz->trips.critical.temperature));
372 }
373 if (tz->trips.critical.flags.valid == 1) {
374 if (crt == -1) {
375 tz->trips.critical.flags.valid = 0;
376 } else if (crt > 0) {
377 unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
378 /*
379 * Allow override to lower critical threshold
380 */
381 if (crt_k < tz->trips.critical.temperature)
382 tz->trips.critical.temperature = crt_k;
383 }
359 } 384 }
360 } 385 }
361 386
362 /* Critical Sleep (optional) */ 387 /* Critical Sleep (optional) */
363 388 if (flag & ACPI_TRIPS_HOT) {
364 status =
365 acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,
366 &tz->trips.hot.temperature);
367 if (ACPI_FAILURE(status)) {
368 tz->trips.hot.flags.valid = 0;
369 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
370 } else {
371 tz->trips.hot.flags.valid = 1;
372 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",
373 tz->trips.hot.temperature));
374 }
375
376 /* Passive: Processors (optional) */
377
378 if (psv == -1) {
379 status = AE_SUPPORT;
380 } else if (psv > 0) {
381 tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
382 status = AE_OK;
383 } else {
384 status = acpi_evaluate_integer(tz->device->handle, 389 status = acpi_evaluate_integer(tz->device->handle,
385 "_PSV", NULL, &tz->trips.passive.temperature); 390 "_HOT", NULL, &tz->trips.hot.temperature);
391 if (ACPI_FAILURE(status)) {
392 tz->trips.hot.flags.valid = 0;
393 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
394 "No hot threshold\n"));
395 } else {
396 tz->trips.hot.flags.valid = 1;
397 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
398 "Found hot threshold [%lu]\n",
399 tz->trips.critical.temperature));
400 }
386 } 401 }
387 402
388 if (ACPI_FAILURE(status)) { 403 /* Passive (optional) */
389 tz->trips.passive.flags.valid = 0; 404 if (flag & ACPI_TRIPS_PASSIVE) {
390 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); 405 valid = tz->trips.passive.flags.valid;
391 } else { 406 if (psv == -1) {
392 tz->trips.passive.flags.valid = 1; 407 status = AE_SUPPORT;
393 408 } else if (psv > 0) {
394 status = 409 tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
395 acpi_evaluate_integer(tz->device->handle, "_TC1", NULL, 410 status = AE_OK;
396 &tz->trips.passive.tc1); 411 } else {
397 if (ACPI_FAILURE(status)) 412 status = acpi_evaluate_integer(tz->device->handle,
398 tz->trips.passive.flags.valid = 0; 413 "_PSV", NULL, &tz->trips.passive.temperature);
399 414 }
400 status =
401 acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,
402 &tz->trips.passive.tc2);
403 if (ACPI_FAILURE(status))
404 tz->trips.passive.flags.valid = 0;
405 415
406 status =
407 acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,
408 &tz->trips.passive.tsp);
409 if (ACPI_FAILURE(status)) 416 if (ACPI_FAILURE(status))
410 tz->trips.passive.flags.valid = 0; 417 tz->trips.passive.flags.valid = 0;
411 418 else {
412 status = 419 tz->trips.passive.flags.valid = 1;
413 acpi_evaluate_reference(tz->device->handle, "_PSL", NULL, 420 if (flag == ACPI_TRIPS_INIT) {
414 &tz->trips.passive.devices); 421 status = acpi_evaluate_integer(
422 tz->device->handle, "_TC1",
423 NULL, &tz->trips.passive.tc1);
424 if (ACPI_FAILURE(status))
425 tz->trips.passive.flags.valid = 0;
426 status = acpi_evaluate_integer(
427 tz->device->handle, "_TC2",
428 NULL, &tz->trips.passive.tc2);
429 if (ACPI_FAILURE(status))
430 tz->trips.passive.flags.valid = 0;
431 status = acpi_evaluate_integer(
432 tz->device->handle, "_TSP",
433 NULL, &tz->trips.passive.tsp);
434 if (ACPI_FAILURE(status))
435 tz->trips.passive.flags.valid = 0;
436 }
437 }
438 }
439 if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
440 memset(&devices, 0, sizeof(struct acpi_handle_list));
441 status = acpi_evaluate_reference(tz->device->handle, "_PSL",
442 NULL, &devices);
415 if (ACPI_FAILURE(status)) 443 if (ACPI_FAILURE(status))
416 tz->trips.passive.flags.valid = 0; 444 tz->trips.passive.flags.valid = 0;
417
418 if (!tz->trips.passive.flags.valid)
419 printk(KERN_WARNING PREFIX "Invalid passive threshold\n");
420 else 445 else
421 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 446 tz->trips.passive.flags.valid = 1;
422 "Found passive threshold [%lu]\n",
423 tz->trips.passive.temperature));
424 }
425 447
426 /* Active: Fans, etc. (optional) */ 448 if (memcmp(&tz->trips.passive.devices, &devices,
449 sizeof(struct acpi_handle_list))) {
450 memcpy(&tz->trips.passive.devices, &devices,
451 sizeof(struct acpi_handle_list));
452 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
453 }
454 }
455 if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
456 if (valid != tz->trips.passive.flags.valid)
457 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
458 }
427 459
460 /* Active (optional) */
428 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { 461 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
429
430 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; 462 char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
463 valid = tz->trips.active[i].flags.valid;
431 464
432 if (act == -1) 465 if (act == -1)
433 break; /* disable all active trip points */ 466 break; /* disable all active trip points */
434 467
435 status = acpi_evaluate_integer(tz->device->handle, 468 if (flag & ACPI_TRIPS_ACTIVE) {
436 name, NULL, &tz->trips.active[i].temperature); 469 status = acpi_evaluate_integer(tz->device->handle,
437 470 name, NULL, &tz->trips.active[i].temperature);
438 if (ACPI_FAILURE(status)) { 471 if (ACPI_FAILURE(status)) {
439 if (i == 0) /* no active trip points */ 472 tz->trips.active[i].flags.valid = 0;
473 if (i == 0)
474 break;
475 if (act <= 0)
476 break;
477 if (i == 1)
478 tz->trips.active[0].temperature =
479 CELSIUS_TO_KELVIN(act);
480 else
481 /*
482 * Don't allow override higher than
483 * the next higher trip point
484 */
485 tz->trips.active[i - 1].temperature =
486 (tz->trips.active[i - 2].temperature <
487 CELSIUS_TO_KELVIN(act) ?
488 tz->trips.active[i - 2].temperature :
489 CELSIUS_TO_KELVIN(act));
440 break; 490 break;
441 if (act <= 0) /* no override requested */ 491 } else
442 break; 492 tz->trips.active[i].flags.valid = 1;
443 if (i == 1) { /* 1 trip point */
444 tz->trips.active[0].temperature =
445 CELSIUS_TO_KELVIN(act);
446 } else { /* multiple trips */
447 /*
448 * Don't allow override higher than
449 * the next higher trip point
450 */
451 tz->trips.active[i - 1].temperature =
452 (tz->trips.active[i - 2].temperature <
453 CELSIUS_TO_KELVIN(act) ?
454 tz->trips.active[i - 2].temperature :
455 CELSIUS_TO_KELVIN(act));
456 }
457 break;
458 } 493 }
459 494
460 name[2] = 'L'; 495 name[2] = 'L';
461 status = 496 if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
462 acpi_evaluate_reference(tz->device->handle, name, NULL, 497 memset(&devices, 0, sizeof(struct acpi_handle_list));
463 &tz->trips.active[i].devices); 498 status = acpi_evaluate_reference(tz->device->handle,
464 if (ACPI_SUCCESS(status)) { 499 name, NULL, &devices);
465 tz->trips.active[i].flags.valid = 1; 500 if (ACPI_FAILURE(status))
466 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 501 tz->trips.active[i].flags.valid = 0;
467 "Found active threshold [%d]:[%lu]\n", 502 else
468 i, tz->trips.active[i].temperature)); 503 tz->trips.active[i].flags.valid = 1;
469 } else 504
470 ACPI_EXCEPTION((AE_INFO, status, 505 if (memcmp(&tz->trips.active[i].devices, &devices,
471 "Invalid active threshold [%d]", i)); 506 sizeof(struct acpi_handle_list))) {
507 memcpy(&tz->trips.active[i].devices, &devices,
508 sizeof(struct acpi_handle_list));
509 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
510 }
511 }
512 if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
513 if (valid != tz->trips.active[i].flags.valid)
514 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
515
516 if (!tz->trips.active[i].flags.valid)
517 break;
518 }
519
520 if (flag & ACPI_TRIPS_DEVICES) {
521 memset(&devices, 0, sizeof(struct acpi_handle_list));
522 status = acpi_evaluate_reference(tz->device->handle, "_TZD",
523 NULL, &devices);
524 if (memcmp(&tz->devices, &devices,
525 sizeof(struct acpi_handle_list))) {
526 memcpy(&tz->devices, &devices,
527 sizeof(struct acpi_handle_list));
528 ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
529 }
472 } 530 }
473 531
474 return 0; 532 return 0;
475} 533}
476 534
477static int acpi_thermal_get_devices(struct acpi_thermal *tz) 535static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
478{ 536{
479 acpi_status status = AE_OK; 537 return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
480
481
482 if (!tz)
483 return -EINVAL;
484
485 status =
486 acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);
487 if (ACPI_FAILURE(status))
488 return -ENODEV;
489
490 return 0;
491} 538}
492 539
493static int acpi_thermal_critical(struct acpi_thermal *tz) 540static int acpi_thermal_critical(struct acpi_thermal *tz)
@@ -735,6 +782,9 @@ static void acpi_thermal_check(void *data)
735 if (result) 782 if (result)
736 goto unlock; 783 goto unlock;
737 784
785 if (!tz->tz_enabled)
786 goto unlock;
787
738 memset(&tz->state, 0, sizeof(tz->state)); 788 memset(&tz->state, 0, sizeof(tz->state));
739 789
740 /* 790 /*
@@ -828,6 +878,290 @@ static void acpi_thermal_check(void *data)
828 mutex_unlock(&tz->lock); 878 mutex_unlock(&tz->lock);
829} 879}
830 880
881/* sys I/F for generic thermal sysfs support */
882static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
883{
884 struct acpi_thermal *tz = thermal->devdata;
885
886 if (!tz)
887 return -EINVAL;
888
889 return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature));
890}
891
892static const char enabled[] = "kernel";
893static const char disabled[] = "user";
894static int thermal_get_mode(struct thermal_zone_device *thermal,
895 char *buf)
896{
897 struct acpi_thermal *tz = thermal->devdata;
898
899 if (!tz)
900 return -EINVAL;
901
902 return sprintf(buf, "%s\n", tz->tz_enabled ?
903 enabled : disabled);
904}
905
906static int thermal_set_mode(struct thermal_zone_device *thermal,
907 const char *buf)
908{
909 struct acpi_thermal *tz = thermal->devdata;
910 int enable;
911
912 if (!tz)
913 return -EINVAL;
914
915 /*
916 * enable/disable thermal management from ACPI thermal driver
917 */
918 if (!strncmp(buf, enabled, sizeof enabled - 1))
919 enable = 1;
920 else if (!strncmp(buf, disabled, sizeof disabled - 1))
921 enable = 0;
922 else
923 return -EINVAL;
924
925 if (enable != tz->tz_enabled) {
926 tz->tz_enabled = enable;
927 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
928 "%s ACPI thermal control\n",
929 tz->tz_enabled ? enabled : disabled));
930 acpi_thermal_check(tz);
931 }
932 return 0;
933}
934
935static int thermal_get_trip_type(struct thermal_zone_device *thermal,
936 int trip, char *buf)
937{
938 struct acpi_thermal *tz = thermal->devdata;
939 int i;
940
941 if (!tz || trip < 0)
942 return -EINVAL;
943
944 if (tz->trips.critical.flags.valid) {
945 if (!trip)
946 return sprintf(buf, "critical\n");
947 trip--;
948 }
949
950 if (tz->trips.hot.flags.valid) {
951 if (!trip)
952 return sprintf(buf, "hot\n");
953 trip--;
954 }
955
956 if (tz->trips.passive.flags.valid) {
957 if (!trip)
958 return sprintf(buf, "passive\n");
959 trip--;
960 }
961
962 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
963 tz->trips.active[i].flags.valid; i++) {
964 if (!trip)
965 return sprintf(buf, "active%d\n", i);
966 trip--;
967 }
968
969 return -EINVAL;
970}
971
972static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
973 int trip, char *buf)
974{
975 struct acpi_thermal *tz = thermal->devdata;
976 int i;
977
978 if (!tz || trip < 0)
979 return -EINVAL;
980
981 if (tz->trips.critical.flags.valid) {
982 if (!trip)
983 return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
984 tz->trips.critical.temperature));
985 trip--;
986 }
987
988 if (tz->trips.hot.flags.valid) {
989 if (!trip)
990 return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
991 tz->trips.hot.temperature));
992 trip--;
993 }
994
995 if (tz->trips.passive.flags.valid) {
996 if (!trip)
997 return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
998 tz->trips.passive.temperature));
999 trip--;
1000 }
1001
1002 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
1003 tz->trips.active[i].flags.valid; i++) {
1004 if (!trip)
1005 return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
1006 tz->trips.active[i].temperature));
1007 trip--;
1008 }
1009
1010 return -EINVAL;
1011}
1012
1013typedef int (*cb)(struct thermal_zone_device *, int,
1014 struct thermal_cooling_device *);
1015static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
1016 struct thermal_cooling_device *cdev,
1017 cb action)
1018{
1019 struct acpi_device *device = cdev->devdata;
1020 struct acpi_thermal *tz = thermal->devdata;
1021 struct acpi_device *dev;
1022 acpi_status status;
1023 acpi_handle handle;
1024 int i;
1025 int j;
1026 int trip = -1;
1027 int result = 0;
1028
1029 if (tz->trips.critical.flags.valid)
1030 trip++;
1031
1032 if (tz->trips.hot.flags.valid)
1033 trip++;
1034
1035 if (tz->trips.passive.flags.valid) {
1036 trip++;
1037 for (i = 0; i < tz->trips.passive.devices.count;
1038 i++) {
1039 handle = tz->trips.passive.devices.handles[i];
1040 status = acpi_bus_get_device(handle, &dev);
1041 if (ACPI_SUCCESS(status) && (dev == device)) {
1042 result = action(thermal, trip, cdev);
1043 if (result)
1044 goto failed;
1045 }
1046 }
1047 }
1048
1049 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
1050 if (!tz->trips.active[i].flags.valid)
1051 break;
1052 trip++;
1053 for (j = 0;
1054 j < tz->trips.active[i].devices.count;
1055 j++) {
1056 handle = tz->trips.active[i].devices.handles[j];
1057 status = acpi_bus_get_device(handle, &dev);
1058 if (ACPI_SUCCESS(status) && (dev == device)) {
1059 result = action(thermal, trip, cdev);
1060 if (result)
1061 goto failed;
1062 }
1063 }
1064 }
1065
1066 for (i = 0; i < tz->devices.count; i++) {
1067 handle = tz->devices.handles[i];
1068 status = acpi_bus_get_device(handle, &dev);
1069 if (ACPI_SUCCESS(status) && (dev == device)) {
1070 result = action(thermal, -1, cdev);
1071 if (result)
1072 goto failed;
1073 }
1074 }
1075
1076failed:
1077 return result;
1078}
1079
1080static int
1081acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
1082 struct thermal_cooling_device *cdev)
1083{
1084 return acpi_thermal_cooling_device_cb(thermal, cdev,
1085 thermal_zone_bind_cooling_device);
1086}
1087
1088static int
1089acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
1090 struct thermal_cooling_device *cdev)
1091{
1092 return acpi_thermal_cooling_device_cb(thermal, cdev,
1093 thermal_zone_unbind_cooling_device);
1094}
1095
1096static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
1097 .bind = acpi_thermal_bind_cooling_device,
1098 .unbind = acpi_thermal_unbind_cooling_device,
1099 .get_temp = thermal_get_temp,
1100 .get_mode = thermal_get_mode,
1101 .set_mode = thermal_set_mode,
1102 .get_trip_type = thermal_get_trip_type,
1103 .get_trip_temp = thermal_get_trip_temp,
1104};
1105
1106static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
1107{
1108 int trips = 0;
1109 int result;
1110 acpi_status status;
1111 int i;
1112
1113 if (tz->trips.critical.flags.valid)
1114 trips++;
1115
1116 if (tz->trips.hot.flags.valid)
1117 trips++;
1118
1119 if (tz->trips.passive.flags.valid)
1120 trips++;
1121
1122 for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
1123 tz->trips.active[i].flags.valid; i++, trips++);
1124 tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",
1125 trips, tz, &acpi_thermal_zone_ops);
1126 if (!tz->thermal_zone)
1127 return -ENODEV;
1128
1129 result = sysfs_create_link(&tz->device->dev.kobj,
1130 &tz->thermal_zone->device.kobj, "thermal_zone");
1131 if (result)
1132 return result;
1133
1134 result = sysfs_create_link(&tz->thermal_zone->device.kobj,
1135 &tz->device->dev.kobj, "device");
1136 if (result)
1137 return result;
1138
1139 status = acpi_attach_data(tz->device->handle,
1140 acpi_bus_private_data_handler,
1141 tz->thermal_zone);
1142 if (ACPI_FAILURE(status)) {
1143 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
1144 "Error attaching device data\n"));
1145 return -ENODEV;
1146 }
1147
1148 tz->tz_enabled = 1;
1149
1150 printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",
1151 tz->device->dev.bus_id, tz->thermal_zone->id);
1152 return 0;
1153}
1154
1155static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
1156{
1157 sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
1158 sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
1159 thermal_zone_device_unregister(tz->thermal_zone);
1160 tz->thermal_zone = NULL;
1161 acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
1162}
1163
1164
831/* -------------------------------------------------------------------------- 1165/* --------------------------------------------------------------------------
832 FS Interface (/proc) 1166 FS Interface (/proc)
833 -------------------------------------------------------------------------- */ 1167 -------------------------------------------------------------------------- */
@@ -1184,15 +1518,15 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data)
1184 acpi_thermal_check(tz); 1518 acpi_thermal_check(tz);
1185 break; 1519 break;
1186 case ACPI_THERMAL_NOTIFY_THRESHOLDS: 1520 case ACPI_THERMAL_NOTIFY_THRESHOLDS:
1187 acpi_thermal_get_trip_points(tz); 1521 acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
1188 acpi_thermal_check(tz); 1522 acpi_thermal_check(tz);
1189 acpi_bus_generate_proc_event(device, event, 0); 1523 acpi_bus_generate_proc_event(device, event, 0);
1190 acpi_bus_generate_netlink_event(device->pnp.device_class, 1524 acpi_bus_generate_netlink_event(device->pnp.device_class,
1191 device->dev.bus_id, event, 0); 1525 device->dev.bus_id, event, 0);
1192 break; 1526 break;
1193 case ACPI_THERMAL_NOTIFY_DEVICES: 1527 case ACPI_THERMAL_NOTIFY_DEVICES:
1194 if (tz->flags.devices) 1528 acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
1195 acpi_thermal_get_devices(tz); 1529 acpi_thermal_check(tz);
1196 acpi_bus_generate_proc_event(device, event, 0); 1530 acpi_bus_generate_proc_event(device, event, 0);
1197 acpi_bus_generate_netlink_event(device->pnp.device_class, 1531 acpi_bus_generate_netlink_event(device->pnp.device_class,
1198 device->dev.bus_id, event, 0); 1532 device->dev.bus_id, event, 0);
@@ -1235,11 +1569,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz)
1235 else 1569 else
1236 acpi_thermal_get_polling_frequency(tz); 1570 acpi_thermal_get_polling_frequency(tz);
1237 1571
1238 /* Get devices in this thermal zone [_TZD] (optional) */
1239 result = acpi_thermal_get_devices(tz);
1240 if (!result)
1241 tz->flags.devices = 1;
1242
1243 return 0; 1572 return 0;
1244} 1573}
1245 1574
@@ -1263,13 +1592,19 @@ static int acpi_thermal_add(struct acpi_device *device)
1263 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); 1592 strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
1264 acpi_driver_data(device) = tz; 1593 acpi_driver_data(device) = tz;
1265 mutex_init(&tz->lock); 1594 mutex_init(&tz->lock);
1595
1596
1266 result = acpi_thermal_get_info(tz); 1597 result = acpi_thermal_get_info(tz);
1267 if (result) 1598 if (result)
1268 goto end; 1599 goto free_memory;
1600
1601 result = acpi_thermal_register_thermal_zone(tz);
1602 if (result)
1603 goto free_memory;
1269 1604
1270 result = acpi_thermal_add_fs(device); 1605 result = acpi_thermal_add_fs(device);
1271 if (result) 1606 if (result)
1272 goto end; 1607 goto unregister_thermal_zone;
1273 1608
1274 init_timer(&tz->timer); 1609 init_timer(&tz->timer);
1275 1610
@@ -1280,19 +1615,21 @@ static int acpi_thermal_add(struct acpi_device *device)
1280 acpi_thermal_notify, tz); 1615 acpi_thermal_notify, tz);
1281 if (ACPI_FAILURE(status)) { 1616 if (ACPI_FAILURE(status)) {
1282 result = -ENODEV; 1617 result = -ENODEV;
1283 goto end; 1618 goto remove_fs;
1284 } 1619 }
1285 1620
1286 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", 1621 printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
1287 acpi_device_name(device), acpi_device_bid(device), 1622 acpi_device_name(device), acpi_device_bid(device),
1288 KELVIN_TO_CELSIUS(tz->temperature)); 1623 KELVIN_TO_CELSIUS(tz->temperature));
1624 goto end;
1289 1625
1290 end: 1626remove_fs:
1291 if (result) { 1627 acpi_thermal_remove_fs(device);
1292 acpi_thermal_remove_fs(device); 1628unregister_thermal_zone:
1293 kfree(tz); 1629 thermal_zone_device_unregister(tz->thermal_zone);
1294 } 1630free_memory:
1295 1631 kfree(tz);
1632end:
1296 return result; 1633 return result;
1297} 1634}
1298 1635
@@ -1332,6 +1669,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
1332 } 1669 }
1333 1670
1334 acpi_thermal_remove_fs(device); 1671 acpi_thermal_remove_fs(device);
1672 acpi_thermal_unregister_thermal_zone(tz);
1335 mutex_destroy(&tz->lock); 1673 mutex_destroy(&tz->lock);
1336 kfree(tz); 1674 kfree(tz);
1337 return 0; 1675 return 0;
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index a54ff6bce8fa..82815cff15a9 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -34,6 +34,7 @@
34#include <linux/seq_file.h> 34#include <linux/seq_file.h>
35#include <linux/input.h> 35#include <linux/input.h>
36#include <linux/backlight.h> 36#include <linux/backlight.h>
37#include <linux/thermal.h>
37#include <linux/video_output.h> 38#include <linux/video_output.h>
38#include <asm/uaccess.h> 39#include <asm/uaccess.h>
39 40
@@ -179,6 +180,7 @@ struct acpi_video_device {
179 struct acpi_device *dev; 180 struct acpi_device *dev;
180 struct acpi_video_device_brightness *brightness; 181 struct acpi_video_device_brightness *brightness;
181 struct backlight_device *backlight; 182 struct backlight_device *backlight;
183 struct thermal_cooling_device *cdev;
182 struct output_device *output_dev; 184 struct output_device *output_dev;
183}; 185};
184 186
@@ -342,6 +344,54 @@ static struct output_properties acpi_output_properties = {
342 .set_state = acpi_video_output_set, 344 .set_state = acpi_video_output_set,
343 .get_status = acpi_video_output_get, 345 .get_status = acpi_video_output_get,
344}; 346};
347
348
349/* thermal cooling device callbacks */
350static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
351{
352 struct acpi_device *device = cdev->devdata;
353 struct acpi_video_device *video = acpi_driver_data(device);
354
355 return sprintf(buf, "%d\n", video->brightness->count - 3);
356}
357
358static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
359{
360 struct acpi_device *device = cdev->devdata;
361 struct acpi_video_device *video = acpi_driver_data(device);
362 unsigned long level;
363 int state;
364
365 acpi_video_device_lcd_get_level_current(video, &level);
366 for (state = 2; state < video->brightness->count; state++)
367 if (level == video->brightness->levels[state])
368 return sprintf(buf, "%d\n",
369 video->brightness->count - state - 1);
370
371 return -EINVAL;
372}
373
374static int
375video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
376{
377 struct acpi_device *device = cdev->devdata;
378 struct acpi_video_device *video = acpi_driver_data(device);
379 int level;
380
381 if ( state >= video->brightness->count - 2)
382 return -EINVAL;
383
384 state = video->brightness->count - state;
385 level = video->brightness->levels[state -1];
386 return acpi_video_device_lcd_set_level(video, level);
387}
388
389static struct thermal_cooling_device_ops video_cooling_ops = {
390 .get_max_state = video_get_max_state,
391 .get_cur_state = video_get_cur_state,
392 .set_cur_state = video_set_cur_state,
393};
394
345/* -------------------------------------------------------------------------- 395/* --------------------------------------------------------------------------
346 Video Management 396 Video Management
347 -------------------------------------------------------------------------- */ 397 -------------------------------------------------------------------------- */
@@ -660,6 +710,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
660 kfree(obj); 710 kfree(obj);
661 711
662 if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){ 712 if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){
713 int result;
663 static int count = 0; 714 static int count = 0;
664 char *name; 715 char *name;
665 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); 716 name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
@@ -672,8 +723,25 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
672 device->backlight->props.max_brightness = device->brightness->count-3; 723 device->backlight->props.max_brightness = device->brightness->count-3;
673 device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); 724 device->backlight->props.brightness = acpi_video_get_brightness(device->backlight);
674 backlight_update_status(device->backlight); 725 backlight_update_status(device->backlight);
675
676 kfree(name); 726 kfree(name);
727
728 device->cdev = thermal_cooling_device_register("LCD",
729 device->dev, &video_cooling_ops);
730 if (device->cdev) {
731 printk(KERN_INFO PREFIX
732 "%s is registered as cooling_device%d\n",
733 device->dev->dev.bus_id, device->cdev->id);
734 result = sysfs_create_link(&device->dev->dev.kobj,
735 &device->cdev->device.kobj,
736 "thermal_cooling");
737 if (result)
738 printk(KERN_ERR PREFIX "Create sysfs link\n");
739 result = sysfs_create_link(&device->cdev->device.kobj,
740 &device->dev->dev.kobj,
741 "device");
742 if (result)
743 printk(KERN_ERR PREFIX "Create sysfs link\n");
744 }
677 } 745 }
678 if (device->cap._DCS && device->cap._DSS){ 746 if (device->cap._DCS && device->cap._DSS){
679 static int count = 0; 747 static int count = 0;
@@ -1764,6 +1832,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
1764 ACPI_DEVICE_NOTIFY, 1832 ACPI_DEVICE_NOTIFY,
1765 acpi_video_device_notify); 1833 acpi_video_device_notify);
1766 backlight_device_unregister(device->backlight); 1834 backlight_device_unregister(device->backlight);
1835 if (device->cdev) {
1836 sysfs_remove_link(&device->dev->dev.kobj,
1837 "thermal_cooling");
1838 sysfs_remove_link(&device->cdev->device.kobj,
1839 "device");
1840 thermal_cooling_device_unregister(device->cdev);
1841 device->cdev = NULL;
1842 }
1767 video_output_unregister(device->output_dev); 1843 video_output_unregister(device->output_dev);
1768 1844
1769 return 0; 1845 return 0;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b1f9a405c822..f20c30cf99e0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -251,4 +251,13 @@ config ATMEL_SSC
251 251
252 If unsure, say N. 252 If unsure, say N.
253 253
254config INTEL_MENLOW
255 tristate "Thermal Management driver for Intel menlow platform"
256 depends on ACPI_THERMAL
257 ---help---
258 ACPI thermal management enhancement driver on
259 Intel Menlow platform.
260
261 If unsure, say N.
262
254endif # MISC_DEVICES 263endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87f2685d728f..a9e8faffc1b1 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
17obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 17obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
18obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o 18obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
19obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o 19obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
20obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c
new file mode 100644
index 000000000000..f70984ab1e1b
--- /dev/null
+++ b/drivers/misc/intel_menlow.c
@@ -0,0 +1,526 @@
1/*
2 * intel_menlow.c - Intel menlow Driver for thermal management extension
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
6 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 *
24 * This driver creates the sys I/F for programming the sensors.
25 * It also implements the driver for intel menlow memory controller (hardware
26 * id is INT0002) which makes use of the platform specific ACPI methods
27 * to get/set bandwidth.
28 */
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/types.h>
34#include <linux/pci.h>
35#include <linux/pm.h>
36
37#include <linux/thermal.h>
38#include <acpi/acpi_bus.h>
39#include <acpi/acpi_drivers.h>
40
41MODULE_AUTHOR("Thomas Sujith");
42MODULE_AUTHOR("Zhang Rui");
43MODULE_DESCRIPTION("Intel Menlow platform specific driver");
44MODULE_LICENSE("GPL");
45
46/*
47 * Memory controller device control
48 */
49
50#define MEMORY_GET_BANDWIDTH "GTHS"
51#define MEMORY_SET_BANDWIDTH "STHS"
52#define MEMORY_ARG_CUR_BANDWIDTH 1
53#define MEMORY_ARG_MAX_BANDWIDTH 0
54
55static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
56 unsigned long *max_state)
57{
58 struct acpi_device *device = cdev->devdata;
59 acpi_handle handle = device->handle;
60 unsigned long value;
61 struct acpi_object_list arg_list;
62 union acpi_object arg;
63 acpi_status status = AE_OK;
64
65 arg_list.count = 1;
66 arg_list.pointer = &arg;
67 arg.type = ACPI_TYPE_INTEGER;
68 arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
69 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
70 &arg_list, &value);
71 if (ACPI_FAILURE(status))
72 return -EFAULT;
73
74 *max_state = value - 1;
75 return 0;
76}
77
78static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
79 char *buf)
80{
81 unsigned long value;
82 if (memory_get_int_max_bandwidth(cdev, &value))
83 return -EINVAL;
84
85 return sprintf(buf, "%ld\n", value);
86}
87
88static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
89 char *buf)
90{
91 struct acpi_device *device = cdev->devdata;
92 acpi_handle handle = device->handle;
93 unsigned long value;
94 struct acpi_object_list arg_list;
95 union acpi_object arg;
96 acpi_status status = AE_OK;
97
98 arg_list.count = 1;
99 arg_list.pointer = &arg;
100 arg.type = ACPI_TYPE_INTEGER;
101 arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
102 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
103 &arg_list, &value);
104 if (ACPI_FAILURE(status))
105 return -EFAULT;
106
107 return sprintf(buf, "%ld\n", value);
108}
109
110static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
111 unsigned int state)
112{
113 struct acpi_device *device = cdev->devdata;
114 acpi_handle handle = device->handle;
115 struct acpi_object_list arg_list;
116 union acpi_object arg;
117 acpi_status status;
118 int temp;
119 unsigned long max_state;
120
121 if (memory_get_int_max_bandwidth(cdev, &max_state))
122 return -EFAULT;
123
124 if (max_state < 0 || state > max_state)
125 return -EINVAL;
126
127 arg_list.count = 1;
128 arg_list.pointer = &arg;
129 arg.type = ACPI_TYPE_INTEGER;
130 arg.integer.value = state;
131
132 status =
133 acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
134 (unsigned long *)&temp);
135
136 printk(KERN_INFO
137 "Bandwidth value was %d: status is %d\n", state, status);
138 if (ACPI_FAILURE(status))
139 return -EFAULT;
140
141 return 0;
142}
143
144static struct thermal_cooling_device_ops memory_cooling_ops = {
145 .get_max_state = memory_get_max_bandwidth,
146 .get_cur_state = memory_get_cur_bandwidth,
147 .set_cur_state = memory_set_cur_bandwidth,
148};
149
150/*
151 * Memory Device Management
152 */
153static int intel_menlow_memory_add(struct acpi_device *device)
154{
155 int result = -ENODEV;
156 acpi_status status = AE_OK;
157 acpi_handle dummy;
158 struct thermal_cooling_device *cdev;
159
160 if (!device)
161 return -EINVAL;
162
163 status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
164 if (ACPI_FAILURE(status))
165 goto end;
166
167 status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
168 if (ACPI_FAILURE(status))
169 goto end;
170
171 cdev = thermal_cooling_device_register("Memory controller", device,
172 &memory_cooling_ops);
173 acpi_driver_data(device) = cdev;
174 if (!cdev)
175 result = -ENODEV;
176 else {
177 result = sysfs_create_link(&device->dev.kobj,
178 &cdev->device.kobj, "thermal_cooling");
179 if (result)
180 goto unregister;
181
182 result = sysfs_create_link(&cdev->device.kobj,
183 &device->dev.kobj, "device");
184 if (result) {
185 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
186 goto unregister;
187 }
188 }
189
190 end:
191 return result;
192
193 unregister:
194 thermal_cooling_device_unregister(cdev);
195 return result;
196
197}
198
199static int intel_menlow_memory_remove(struct acpi_device *device, int type)
200{
201 struct thermal_cooling_device *cdev = acpi_driver_data(device);
202
203 if (!device || !cdev)
204 return -EINVAL;
205
206 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
207 sysfs_remove_link(&cdev->device.kobj, "device");
208 thermal_cooling_device_unregister(cdev);
209
210 return 0;
211}
212
213const static struct acpi_device_id intel_menlow_memory_ids[] = {
214 {"INT0002", 0},
215 {"", 0},
216};
217
218static struct acpi_driver intel_menlow_memory_driver = {
219 .name = "intel_menlow_thermal_control",
220 .ids = intel_menlow_memory_ids,
221 .ops = {
222 .add = intel_menlow_memory_add,
223 .remove = intel_menlow_memory_remove,
224 },
225};
226
227/*
228 * Sensor control on menlow platform
229 */
230
231#define THERMAL_AUX0 0
232#define THERMAL_AUX1 1
233#define GET_AUX0 "GAX0"
234#define GET_AUX1 "GAX1"
235#define SET_AUX0 "SAX0"
236#define SET_AUX1 "SAX1"
237
238struct intel_menlow_attribute {
239 struct device_attribute attr;
240 struct device *device;
241 acpi_handle handle;
242 struct list_head node;
243};
244
245static LIST_HEAD(intel_menlow_attr_list);
246static DEFINE_MUTEX(intel_menlow_attr_lock);
247
248/*
249 * sensor_get_auxtrip - get the current auxtrip value from sensor
250 * @name: Thermalzone name
251 * @auxtype : AUX0/AUX1
252 * @buf: syfs buffer
253 */
254static int sensor_get_auxtrip(acpi_handle handle, int index, int *value)
255{
256 acpi_status status;
257
258 if ((index != 0 && index != 1) || !value)
259 return -EINVAL;
260
261 status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
262 NULL, (unsigned long *)value);
263 if (ACPI_FAILURE(status))
264 return -EIO;
265
266 return 0;
267}
268
269/*
270 * sensor_set_auxtrip - set the new auxtrip value to sensor
271 * @name: Thermalzone name
272 * @auxtype : AUX0/AUX1
273 * @buf: syfs buffer
274 */
275static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
276{
277 acpi_status status;
278 union acpi_object arg = {
279 ACPI_TYPE_INTEGER
280 };
281 struct acpi_object_list args = {
282 1, &arg
283 };
284 int temp;
285
286 if (index != 0 && index != 1)
287 return -EINVAL;
288
289 status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
290 NULL, (unsigned long *)&temp);
291 if (ACPI_FAILURE(status))
292 return -EIO;
293 if ((index && value < temp) || (!index && value > temp))
294 return -EINVAL;
295
296 arg.integer.value = value;
297 status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
298 &args, (unsigned long *)&temp);
299 if (ACPI_FAILURE(status))
300 return -EIO;
301
302 /* do we need to check the return value of SAX0/SAX1 ? */
303
304 return 0;
305}
306
307#define to_intel_menlow_attr(_attr) \
308 container_of(_attr, struct intel_menlow_attribute, attr)
309
310static ssize_t aux0_show(struct device *dev,
311 struct device_attribute *dev_attr, char *buf)
312{
313 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
314 int value;
315 int result;
316
317 result = sensor_get_auxtrip(attr->handle, 0, &value);
318
319 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
320}
321
322static ssize_t aux1_show(struct device *dev,
323 struct device_attribute *dev_attr, char *buf)
324{
325 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
326 int value;
327 int result;
328
329 result = sensor_get_auxtrip(attr->handle, 1, &value);
330
331 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
332}
333
334static ssize_t aux0_store(struct device *dev,
335 struct device_attribute *dev_attr,
336 const char *buf, size_t count)
337{
338 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
339 int value;
340 int result;
341
342 /*Sanity check; should be a positive integer */
343 if (!sscanf(buf, "%d", &value))
344 return -EINVAL;
345
346 if (value < 0)
347 return -EINVAL;
348
349 result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
350 return result ? result : count;
351}
352
353static ssize_t aux1_store(struct device *dev,
354 struct device_attribute *dev_attr,
355 const char *buf, size_t count)
356{
357 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
358 int value;
359 int result;
360
361 /*Sanity check; should be a positive integer */
362 if (!sscanf(buf, "%d", &value))
363 return -EINVAL;
364
365 if (value < 0)
366 return -EINVAL;
367
368 result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
369 return result ? result : count;
370}
371
372/* BIOS can enable/disable the thermal user application in dabney platform */
373#define BIOS_ENABLED "\\_TZ.GSTS"
374static ssize_t bios_enabled_show(struct device *dev,
375 struct device_attribute *attr, char *buf)
376{
377 acpi_status status;
378 unsigned long bios_enabled;
379
380 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
381 if (ACPI_FAILURE(status))
382 return -ENODEV;
383
384 return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
385}
386
387static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
388 void *store, struct device *dev,
389 acpi_handle handle)
390{
391 struct intel_menlow_attribute *attr;
392 int result;
393
394 attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
395 if (!attr)
396 return -ENOMEM;
397
398 attr->attr.attr.name = name;
399 attr->attr.attr.mode = mode;
400 attr->attr.show = show;
401 attr->attr.store = store;
402 attr->device = dev;
403 attr->handle = handle;
404
405 result = device_create_file(dev, &attr->attr);
406 if (result)
407 return result;
408
409 mutex_lock(&intel_menlow_attr_lock);
410 list_add_tail(&attr->node, &intel_menlow_attr_list);
411 mutex_unlock(&intel_menlow_attr_lock);
412
413 return 0;
414}
415
416static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
417 void *context, void **rv)
418{
419 acpi_status status;
420 acpi_handle dummy;
421 struct thermal_zone_device *thermal;
422 int result;
423
424 result = acpi_bus_get_private_data(handle, (void **)&thermal);
425 if (result)
426 return 0;
427
428 /* _TZ must have the AUX0/1 methods */
429 status = acpi_get_handle(handle, GET_AUX0, &dummy);
430 if (ACPI_FAILURE(status))
431 goto not_found;
432
433 status = acpi_get_handle(handle, SET_AUX0, &dummy);
434 if (ACPI_FAILURE(status))
435 goto not_found;
436
437 result = intel_menlow_add_one_attribute("aux0", 0644,
438 aux0_show, aux0_store,
439 &thermal->device, handle);
440 if (result)
441 return AE_ERROR;
442
443 status = acpi_get_handle(handle, GET_AUX1, &dummy);
444 if (ACPI_FAILURE(status))
445 goto not_found;
446
447 status = acpi_get_handle(handle, SET_AUX1, &dummy);
448 if (ACPI_FAILURE(status))
449 goto not_found;
450
451 result = intel_menlow_add_one_attribute("aux1", 0644,
452 aux1_show, aux1_store,
453 &thermal->device, handle);
454 if (result)
455 return AE_ERROR;
456
457 /*
458 * create the "dabney_enabled" attribute which means the user app
459 * should be loaded or not
460 */
461
462 result = intel_menlow_add_one_attribute("bios_enabled", 0444,
463 bios_enabled_show, NULL,
464 &thermal->device, handle);
465 if (result)
466 return AE_ERROR;
467
468 not_found:
469 if (status == AE_NOT_FOUND)
470 return AE_OK;
471 else
472 return status;
473}
474
475static void intel_menlow_unregister_sensor(void)
476{
477 struct intel_menlow_attribute *pos, *next;
478
479 mutex_lock(&intel_menlow_attr_lock);
480 list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
481 list_del(&pos->node);
482 device_remove_file(pos->device, &pos->attr);
483 kfree(pos);
484 }
485 mutex_unlock(&intel_menlow_attr_lock);
486
487 return;
488}
489
490static int __init intel_menlow_module_init(void)
491{
492 int result = -ENODEV;
493 acpi_status status;
494 unsigned long enable;
495
496 if (acpi_disabled)
497 return result;
498
499 /* Looking for the \_TZ.GSTS method */
500 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
501 if (ACPI_FAILURE(status) || !enable)
502 return -ENODEV;
503
504 /* Looking for ACPI device MEM0 with hardware id INT0002 */
505 result = acpi_bus_register_driver(&intel_menlow_memory_driver);
506 if (result)
507 return result;
508
509 /* Looking for sensors in each ACPI thermal zone */
510 status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
511 ACPI_UINT32_MAX,
512 intel_menlow_register_sensor, NULL, NULL);
513 if (ACPI_FAILURE(status))
514 return -ENODEV;
515
516 return 0;
517}
518
519static void __exit intel_menlow_module_exit(void)
520{
521 acpi_bus_unregister_driver(&intel_menlow_memory_driver);
522 intel_menlow_unregister_sensor();
523}
524
525module_init(intel_menlow_module_init);
526module_exit(intel_menlow_module_exit);
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
new file mode 100644
index 000000000000..9b3f61200000
--- /dev/null
+++ b/drivers/thermal/Kconfig
@@ -0,0 +1,15 @@
1#
2# Generic thermal sysfs drivers configuration
3#
4
5menuconfig THERMAL
6 bool "Generic Thermal sysfs driver"
7 default y
8 help
9 Generic Thermal Sysfs driver offers a generic mechanism for
10 thermal management. Usually it's made up of one or more thermal
11 zone and cooling device.
12 each thermal zone contains its own temperature, trip points,
13 cooling devices.
14 All platforms with ACPI thermal support can use this driver.
15 If you want this support, you should say Y here
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
new file mode 100644
index 000000000000..8ef1232de376
--- /dev/null
+++ b/drivers/thermal/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for sensor chip drivers.
3#
4
5obj-$(CONFIG_THERMAL) += thermal.o
diff --git a/drivers/thermal/thermal.c b/drivers/thermal/thermal.c
new file mode 100644
index 000000000000..3273e348fd14
--- /dev/null
+++ b/drivers/thermal/thermal.c
@@ -0,0 +1,714 @@
1/*
2 * thermal.c - Generic Thermal Management Sysfs support.
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
6 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 */
25
26#include <linux/module.h>
27#include <linux/device.h>
28#include <linux/err.h>
29#include <linux/kdev_t.h>
30#include <linux/idr.h>
31#include <linux/thermal.h>
32#include <linux/spinlock.h>
33
34MODULE_AUTHOR("Zhang Rui")
35MODULE_DESCRIPTION("Generic thermal management sysfs support");
36MODULE_LICENSE("GPL");
37
38#define PREFIX "Thermal: "
39
40struct thermal_cooling_device_instance {
41 int id;
42 char name[THERMAL_NAME_LENGTH];
43 struct thermal_zone_device *tz;
44 struct thermal_cooling_device *cdev;
45 int trip;
46 char attr_name[THERMAL_NAME_LENGTH];
47 struct device_attribute attr;
48 struct list_head node;
49};
50
51static DEFINE_IDR(thermal_tz_idr);
52static DEFINE_IDR(thermal_cdev_idr);
53static DEFINE_MUTEX(thermal_idr_lock);
54
55static LIST_HEAD(thermal_tz_list);
56static LIST_HEAD(thermal_cdev_list);
57static DEFINE_MUTEX(thermal_list_lock);
58
59static int get_idr(struct idr *idr, struct mutex *lock, int *id)
60{
61 int err;
62
63 again:
64 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
65 return -ENOMEM;
66
67 if (lock)
68 mutex_lock(lock);
69 err = idr_get_new(idr, NULL, id);
70 if (lock)
71 mutex_unlock(lock);
72 if (unlikely(err == -EAGAIN))
73 goto again;
74 else if (unlikely(err))
75 return err;
76
77 *id = *id & MAX_ID_MASK;
78 return 0;
79}
80
81static void release_idr(struct idr *idr, struct mutex *lock, int id)
82{
83 if (lock)
84 mutex_lock(lock);
85 idr_remove(idr, id);
86 if (lock)
87 mutex_unlock(lock);
88}
89
90/* sys I/F for thermal zone */
91
92#define to_thermal_zone(_dev) \
93 container_of(_dev, struct thermal_zone_device, device)
94
95static ssize_t
96type_show(struct device *dev, struct device_attribute *attr, char *buf)
97{
98 struct thermal_zone_device *tz = to_thermal_zone(dev);
99
100 return sprintf(buf, "%s\n", tz->type);
101}
102
103static ssize_t
104temp_show(struct device *dev, struct device_attribute *attr, char *buf)
105{
106 struct thermal_zone_device *tz = to_thermal_zone(dev);
107
108 if (!tz->ops->get_temp)
109 return -EPERM;
110
111 return tz->ops->get_temp(tz, buf);
112}
113
114static ssize_t
115mode_show(struct device *dev, struct device_attribute *attr, char *buf)
116{
117 struct thermal_zone_device *tz = to_thermal_zone(dev);
118
119 if (!tz->ops->get_mode)
120 return -EPERM;
121
122 return tz->ops->get_mode(tz, buf);
123}
124
125static ssize_t
126mode_store(struct device *dev, struct device_attribute *attr,
127 const char *buf, size_t count)
128{
129 struct thermal_zone_device *tz = to_thermal_zone(dev);
130 int result;
131
132 if (!tz->ops->set_mode)
133 return -EPERM;
134
135 result = tz->ops->set_mode(tz, buf);
136 if (result)
137 return result;
138
139 return count;
140}
141
142static ssize_t
143trip_point_type_show(struct device *dev, struct device_attribute *attr,
144 char *buf)
145{
146 struct thermal_zone_device *tz = to_thermal_zone(dev);
147 int trip;
148
149 if (!tz->ops->get_trip_type)
150 return -EPERM;
151
152 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
153 return -EINVAL;
154
155 return tz->ops->get_trip_type(tz, trip, buf);
156}
157
158static ssize_t
159trip_point_temp_show(struct device *dev, struct device_attribute *attr,
160 char *buf)
161{
162 struct thermal_zone_device *tz = to_thermal_zone(dev);
163 int trip;
164
165 if (!tz->ops->get_trip_temp)
166 return -EPERM;
167
168 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
169 return -EINVAL;
170
171 return tz->ops->get_trip_temp(tz, trip, buf);
172}
173
174static DEVICE_ATTR(type, 0444, type_show, NULL);
175static DEVICE_ATTR(temp, 0444, temp_show, NULL);
176static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
177
178static struct device_attribute trip_point_attrs[] = {
179 __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
180 __ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
181 __ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
182 __ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
183 __ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
184 __ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
185 __ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
186 __ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
187 __ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
188 __ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
189 __ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
190 __ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
191 __ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
192 __ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
193 __ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
194 __ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
195 __ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
196 __ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
197 __ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
198 __ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
199};
200
201#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
202do { \
203 result = device_create_file(_dev, \
204 &trip_point_attrs[_index * 2]); \
205 if (result) \
206 break; \
207 result = device_create_file(_dev, \
208 &trip_point_attrs[_index * 2 + 1]); \
209} while (0)
210
211#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
212do { \
213 device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
214 device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
215} while (0)
216
217/* sys I/F for cooling device */
218#define to_cooling_device(_dev) \
219 container_of(_dev, struct thermal_cooling_device, device)
220
221static ssize_t
222thermal_cooling_device_type_show(struct device *dev,
223 struct device_attribute *attr, char *buf)
224{
225 struct thermal_cooling_device *cdev = to_cooling_device(dev);
226
227 return sprintf(buf, "%s\n", cdev->type);
228}
229
230static ssize_t
231thermal_cooling_device_max_state_show(struct device *dev,
232 struct device_attribute *attr, char *buf)
233{
234 struct thermal_cooling_device *cdev = to_cooling_device(dev);
235
236 return cdev->ops->get_max_state(cdev, buf);
237}
238
239static ssize_t
240thermal_cooling_device_cur_state_show(struct device *dev,
241 struct device_attribute *attr, char *buf)
242{
243 struct thermal_cooling_device *cdev = to_cooling_device(dev);
244
245 return cdev->ops->get_cur_state(cdev, buf);
246}
247
248static ssize_t
249thermal_cooling_device_cur_state_store(struct device *dev,
250 struct device_attribute *attr,
251 const char *buf, size_t count)
252{
253 struct thermal_cooling_device *cdev = to_cooling_device(dev);
254 int state;
255 int result;
256
257 if (!sscanf(buf, "%d\n", &state))
258 return -EINVAL;
259
260 if (state < 0)
261 return -EINVAL;
262
263 result = cdev->ops->set_cur_state(cdev, state);
264 if (result)
265 return result;
266 return count;
267}
268
269static struct device_attribute dev_attr_cdev_type =
270 __ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
271static DEVICE_ATTR(max_state, 0444,
272 thermal_cooling_device_max_state_show, NULL);
273static DEVICE_ATTR(cur_state, 0644,
274 thermal_cooling_device_cur_state_show,
275 thermal_cooling_device_cur_state_store);
276
277static ssize_t
278thermal_cooling_device_trip_point_show(struct device *dev,
279 struct device_attribute *attr, char *buf)
280{
281 struct thermal_cooling_device_instance *instance;
282
283 instance =
284 container_of(attr, struct thermal_cooling_device_instance, attr);
285
286 if (instance->trip == THERMAL_TRIPS_NONE)
287 return sprintf(buf, "-1\n");
288 else
289 return sprintf(buf, "%d\n", instance->trip);
290}
291
292/* Device management */
293
294/**
295 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
296 * this function is usually called in the thermal zone device .bind callback.
297 * @tz: thermal zone device
298 * @trip: indicates which trip point the cooling devices is
299 * associated with in this thermal zone.
300 * @cdev: thermal cooling device
301 */
302int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
303 int trip,
304 struct thermal_cooling_device *cdev)
305{
306 struct thermal_cooling_device_instance *dev;
307 struct thermal_cooling_device_instance *pos;
308 int result;
309
310 if (trip >= tz->trips ||
311 (trip < 0 && trip != THERMAL_TRIPS_NONE))
312 return -EINVAL;
313
314 if (!tz || !cdev)
315 return -EINVAL;
316
317 dev =
318 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
319 if (!dev)
320 return -ENOMEM;
321 dev->tz = tz;
322 dev->cdev = cdev;
323 dev->trip = trip;
324 result = get_idr(&tz->idr, &tz->lock, &dev->id);
325 if (result)
326 goto free_mem;
327
328 sprintf(dev->name, "cdev%d", dev->id);
329 result =
330 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
331 if (result)
332 goto release_idr;
333
334 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
335 dev->attr.attr.name = dev->attr_name;
336 dev->attr.attr.mode = 0444;
337 dev->attr.show = thermal_cooling_device_trip_point_show;
338 result = device_create_file(&tz->device, &dev->attr);
339 if (result)
340 goto remove_symbol_link;
341
342 mutex_lock(&tz->lock);
343 list_for_each_entry(pos, &tz->cooling_devices, node)
344 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
345 result = -EEXIST;
346 break;
347 }
348 if (!result)
349 list_add_tail(&dev->node, &tz->cooling_devices);
350 mutex_unlock(&tz->lock);
351
352 if (!result)
353 return 0;
354
355 device_remove_file(&tz->device, &dev->attr);
356 remove_symbol_link:
357 sysfs_remove_link(&tz->device.kobj, dev->name);
358 release_idr:
359 release_idr(&tz->idr, &tz->lock, dev->id);
360 free_mem:
361 kfree(dev);
362 return result;
363}
364EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
365
366/**
367 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
368 * this function is usually called in the thermal zone device .unbind callback.
369 * @tz: thermal zone device
370 * @trip: indicates which trip point the cooling devices is
371 * associated with in this thermal zone.
372 * @cdev: thermal cooling device
373 */
374int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
375 int trip,
376 struct thermal_cooling_device *cdev)
377{
378 struct thermal_cooling_device_instance *pos, *next;
379
380 mutex_lock(&tz->lock);
381 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
382 if (pos->tz == tz && pos->trip == trip
383 && pos->cdev == cdev) {
384 list_del(&pos->node);
385 mutex_unlock(&tz->lock);
386 goto unbind;
387 }
388 }
389 mutex_unlock(&tz->lock);
390
391 return -ENODEV;
392
393 unbind:
394 device_remove_file(&tz->device, &pos->attr);
395 sysfs_remove_link(&tz->device.kobj, pos->name);
396 release_idr(&tz->idr, &tz->lock, pos->id);
397 kfree(pos);
398 return 0;
399}
400EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
401
402static void thermal_release(struct device *dev)
403{
404 struct thermal_zone_device *tz;
405 struct thermal_cooling_device *cdev;
406
407 if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) {
408 tz = to_thermal_zone(dev);
409 kfree(tz);
410 } else {
411 cdev = to_cooling_device(dev);
412 kfree(cdev);
413 }
414}
415
416static struct class thermal_class = {
417 .name = "thermal",
418 .dev_release = thermal_release,
419};
420
421/**
422 * thermal_cooling_device_register - register a new thermal cooling device
423 * @type: the thermal cooling device type.
424 * @devdata: device private data.
425 * @ops: standard thermal cooling devices callbacks.
426 */
427struct thermal_cooling_device *thermal_cooling_device_register(char *type,
428 void *devdata, struct thermal_cooling_device_ops *ops)
429{
430 struct thermal_cooling_device *cdev;
431 struct thermal_zone_device *pos;
432 int result;
433
434 if (strlen(type) >= THERMAL_NAME_LENGTH)
435 return NULL;
436
437 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
438 !ops->set_cur_state)
439 return NULL;
440
441 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
442 if (!cdev)
443 return NULL;
444
445 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
446 if (result) {
447 kfree(cdev);
448 return NULL;
449 }
450
451 strcpy(cdev->type, type);
452 cdev->ops = ops;
453 cdev->device.class = &thermal_class;
454 cdev->devdata = devdata;
455 sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id);
456 result = device_register(&cdev->device);
457 if (result) {
458 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
459 kfree(cdev);
460 return NULL;
461 }
462
463 /* sys I/F */
464 if (type) {
465 result = device_create_file(&cdev->device,
466 &dev_attr_cdev_type);
467 if (result)
468 goto unregister;
469 }
470
471 result = device_create_file(&cdev->device, &dev_attr_max_state);
472 if (result)
473 goto unregister;
474
475 result = device_create_file(&cdev->device, &dev_attr_cur_state);
476 if (result)
477 goto unregister;
478
479 mutex_lock(&thermal_list_lock);
480 list_add(&cdev->node, &thermal_cdev_list);
481 list_for_each_entry(pos, &thermal_tz_list, node) {
482 if (!pos->ops->bind)
483 continue;
484 result = pos->ops->bind(pos, cdev);
485 if (result)
486 break;
487
488 }
489 mutex_unlock(&thermal_list_lock);
490
491 if (!result)
492 return cdev;
493
494 unregister:
495 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
496 device_unregister(&cdev->device);
497 return NULL;
498}
499EXPORT_SYMBOL(thermal_cooling_device_register);
500
501/**
502 * thermal_cooling_device_unregister - removes the registered thermal cooling device
503 *
504 * @cdev: the thermal cooling device to remove.
505 *
506 * thermal_cooling_device_unregister() must be called when the device is no
507 * longer needed.
508 */
509void thermal_cooling_device_unregister(struct
510 thermal_cooling_device
511 *cdev)
512{
513 struct thermal_zone_device *tz;
514 struct thermal_cooling_device *pos = NULL;
515
516 if (!cdev)
517 return;
518
519 mutex_lock(&thermal_list_lock);
520 list_for_each_entry(pos, &thermal_cdev_list, node)
521 if (pos == cdev)
522 break;
523 if (pos != cdev) {
524 /* thermal cooling device not found */
525 mutex_unlock(&thermal_list_lock);
526 return;
527 }
528 list_del(&cdev->node);
529 list_for_each_entry(tz, &thermal_tz_list, node) {
530 if (!tz->ops->unbind)
531 continue;
532 tz->ops->unbind(tz, cdev);
533 }
534 mutex_unlock(&thermal_list_lock);
535 if (cdev->type[0])
536 device_remove_file(&cdev->device,
537 &dev_attr_cdev_type);
538 device_remove_file(&cdev->device, &dev_attr_max_state);
539 device_remove_file(&cdev->device, &dev_attr_cur_state);
540
541 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
542 device_unregister(&cdev->device);
543 return;
544}
545EXPORT_SYMBOL(thermal_cooling_device_unregister);
546
547/**
548 * thermal_zone_device_register - register a new thermal zone device
549 * @type: the thermal zone device type
550 * @trips: the number of trip points the thermal zone support
551 * @devdata: private device data
552 * @ops: standard thermal zone device callbacks
553 *
554 * thermal_zone_device_unregister() must be called when the device is no
555 * longer needed.
556 */
557struct thermal_zone_device *thermal_zone_device_register(char *type,
558 int trips, void *devdata,
559 struct thermal_zone_device_ops *ops)
560{
561 struct thermal_zone_device *tz;
562 struct thermal_cooling_device *pos;
563 int result;
564 int count;
565
566 if (strlen(type) >= THERMAL_NAME_LENGTH)
567 return NULL;
568
569 if (trips > THERMAL_MAX_TRIPS || trips < 0)
570 return NULL;
571
572 if (!ops || !ops->get_temp)
573 return NULL;
574
575 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
576 if (!tz)
577 return NULL;
578
579 INIT_LIST_HEAD(&tz->cooling_devices);
580 idr_init(&tz->idr);
581 mutex_init(&tz->lock);
582 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
583 if (result) {
584 kfree(tz);
585 return NULL;
586 }
587
588 strcpy(tz->type, type);
589 tz->ops = ops;
590 tz->device.class = &thermal_class;
591 tz->devdata = devdata;
592 tz->trips = trips;
593 sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
594 result = device_register(&tz->device);
595 if (result) {
596 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
597 kfree(tz);
598 return NULL;
599 }
600
601 /* sys I/F */
602 if (type) {
603 result = device_create_file(&tz->device, &dev_attr_type);
604 if (result)
605 goto unregister;
606 }
607
608 result = device_create_file(&tz->device, &dev_attr_temp);
609 if (result)
610 goto unregister;
611
612 if (ops->get_mode) {
613 result = device_create_file(&tz->device, &dev_attr_mode);
614 if (result)
615 goto unregister;
616 }
617
618 for (count = 0; count < trips; count++) {
619 TRIP_POINT_ATTR_ADD(&tz->device, count, result);
620 if (result)
621 goto unregister;
622 }
623
624 mutex_lock(&thermal_list_lock);
625 list_add_tail(&tz->node, &thermal_tz_list);
626 if (ops->bind)
627 list_for_each_entry(pos, &thermal_cdev_list, node) {
628 result = ops->bind(tz, pos);
629 if (result)
630 break;
631 }
632 mutex_unlock(&thermal_list_lock);
633
634 if (!result)
635 return tz;
636
637 unregister:
638 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
639 device_unregister(&tz->device);
640 return NULL;
641}
642EXPORT_SYMBOL(thermal_zone_device_register);
643
644/**
645 * thermal_device_unregister - removes the registered thermal zone device
646 *
647 * @tz: the thermal zone device to remove
648 */
649void thermal_zone_device_unregister(struct thermal_zone_device *tz)
650{
651 struct thermal_cooling_device *cdev;
652 struct thermal_zone_device *pos = NULL;
653 int count;
654
655 if (!tz)
656 return;
657
658 mutex_lock(&thermal_list_lock);
659 list_for_each_entry(pos, &thermal_tz_list, node)
660 if (pos == tz)
661 break;
662 if (pos != tz) {
663 /* thermal zone device not found */
664 mutex_unlock(&thermal_list_lock);
665 return;
666 }
667 list_del(&tz->node);
668 if (tz->ops->unbind)
669 list_for_each_entry(cdev, &thermal_cdev_list, node)
670 tz->ops->unbind(tz, cdev);
671 mutex_unlock(&thermal_list_lock);
672
673 if (tz->type[0])
674 device_remove_file(&tz->device, &dev_attr_type);
675 device_remove_file(&tz->device, &dev_attr_temp);
676 if (tz->ops->get_mode)
677 device_remove_file(&tz->device, &dev_attr_mode);
678
679 for (count = 0; count < tz->trips; count++)
680 TRIP_POINT_ATTR_REMOVE(&tz->device, count);
681
682 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
683 idr_destroy(&tz->idr);
684 mutex_destroy(&tz->lock);
685 device_unregister(&tz->device);
686 return;
687}
688EXPORT_SYMBOL(thermal_zone_device_unregister);
689
690static int __init thermal_init(void)
691{
692 int result = 0;
693
694 result = class_register(&thermal_class);
695 if (result) {
696 idr_destroy(&thermal_tz_idr);
697 idr_destroy(&thermal_cdev_idr);
698 mutex_destroy(&thermal_idr_lock);
699 mutex_destroy(&thermal_list_lock);
700 }
701 return result;
702}
703
704static void __exit thermal_exit(void)
705{
706 class_unregister(&thermal_class);
707 idr_destroy(&thermal_tz_idr);
708 idr_destroy(&thermal_cdev_idr);
709 mutex_destroy(&thermal_idr_lock);
710 mutex_destroy(&thermal_list_lock);
711}
712
713subsys_initcall(thermal_init);
714module_exit(thermal_exit);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index fb7171b1bd22..504af20b10c1 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -321,6 +321,8 @@ struct acpi_bus_event {
321 321
322extern struct kobject *acpi_kobj; 322extern struct kobject *acpi_kobj;
323extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); 323extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int);
324void acpi_bus_private_data_handler(acpi_handle, u32, void *);
325int acpi_bus_get_private_data(acpi_handle, void **);
324/* 326/*
325 * External Functions 327 * External Functions
326 */ 328 */
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index f6d7c508917c..0c75a0b9c565 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -4,7 +4,7 @@
4#include <linux/kernel.h> 4#include <linux/kernel.h>
5#include <linux/cpu.h> 5#include <linux/cpu.h>
6#include <linux/cpuidle.h> 6#include <linux/cpuidle.h>
7 7#include <linux/thermal.h>
8#include <asm/acpi.h> 8#include <asm/acpi.h>
9 9
10#define ACPI_PROCESSOR_BUSY_METRIC 10 10#define ACPI_PROCESSOR_BUSY_METRIC 10
@@ -219,7 +219,7 @@ struct acpi_processor {
219 struct acpi_processor_performance *performance; 219 struct acpi_processor_performance *performance;
220 struct acpi_processor_throttling throttling; 220 struct acpi_processor_throttling throttling;
221 struct acpi_processor_limit limit; 221 struct acpi_processor_limit limit;
222 222 struct thermal_cooling_device *cdev;
223 /* the _PDC objects for this processor, if any */ 223 /* the _PDC objects for this processor, if any */
224 struct acpi_object_list *pdc; 224 struct acpi_object_list *pdc;
225}; 225};
@@ -331,7 +331,7 @@ extern struct cpuidle_driver acpi_idle_driver;
331/* in processor_thermal.c */ 331/* in processor_thermal.c */
332int acpi_processor_get_limit_info(struct acpi_processor *pr); 332int acpi_processor_get_limit_info(struct acpi_processor *pr);
333extern struct file_operations acpi_processor_limit_fops; 333extern struct file_operations acpi_processor_limit_fops;
334 334extern struct thermal_cooling_device_ops processor_cooling_ops;
335#ifdef CONFIG_CPU_FREQ 335#ifdef CONFIG_CPU_FREQ
336void acpi_thermal_cpufreq_init(void); 336void acpi_thermal_cpufreq_init(void);
337void acpi_thermal_cpufreq_exit(void); 337void acpi_thermal_cpufreq_exit(void);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
new file mode 100644
index 000000000000..bba7712cadc7
--- /dev/null
+++ b/include/linux/thermal.h
@@ -0,0 +1,94 @@
1/*
2 * thermal.h ($Revision: 0 $)
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
6 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
24
25#ifndef __THERMAL_H__
26#define __THERMAL_H__
27
28#include <linux/idr.h>
29#include <linux/device.h>
30
31struct thermal_zone_device;
32struct thermal_cooling_device;
33
34struct thermal_zone_device_ops {
35 int (*bind) (struct thermal_zone_device *,
36 struct thermal_cooling_device *);
37 int (*unbind) (struct thermal_zone_device *,
38 struct thermal_cooling_device *);
39 int (*get_temp) (struct thermal_zone_device *, char *);
40 int (*get_mode) (struct thermal_zone_device *, char *);
41 int (*set_mode) (struct thermal_zone_device *, const char *);
42 int (*get_trip_type) (struct thermal_zone_device *, int, char *);
43 int (*get_trip_temp) (struct thermal_zone_device *, int, char *);
44};
45
46struct thermal_cooling_device_ops {
47 int (*get_max_state) (struct thermal_cooling_device *, char *);
48 int (*get_cur_state) (struct thermal_cooling_device *, char *);
49 int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
50};
51
52#define THERMAL_TRIPS_NONE -1
53#define THERMAL_MAX_TRIPS 10
54#define THERMAL_NAME_LENGTH 20
55struct thermal_cooling_device {
56 int id;
57 char type[THERMAL_NAME_LENGTH];
58 struct device device;
59 void *devdata;
60 struct thermal_cooling_device_ops *ops;
61 struct list_head node;
62};
63
64#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
65 ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
66#define CELSIUS_TO_KELVIN(t) ((t)*10+2732)
67
68struct thermal_zone_device {
69 int id;
70 char type[THERMAL_NAME_LENGTH];
71 struct device device;
72 void *devdata;
73 int trips;
74 struct thermal_zone_device_ops *ops;
75 struct list_head cooling_devices;
76 struct idr idr;
77 struct mutex lock; /* protect cooling devices list */
78 struct list_head node;
79};
80
81struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
82 struct thermal_zone_device_ops *);
83void thermal_zone_device_unregister(struct thermal_zone_device *);
84
85int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
86 struct thermal_cooling_device *);
87int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
88 struct thermal_cooling_device *);
89
90struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
91 struct thermal_cooling_device_ops *);
92void thermal_cooling_device_unregister(struct thermal_cooling_device *);
93
94#endif /* __THERMAL_H__ */