diff options
-rw-r--r-- | Documentation/thermal/sysfs-api.txt | 246 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 25 | ||||
-rw-r--r-- | drivers/acpi/fan.c | 92 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 23 | ||||
-rw-r--r-- | drivers/acpi/processor_thermal.c | 134 | ||||
-rw-r--r-- | drivers/acpi/thermal.c | 646 | ||||
-rw-r--r-- | drivers/acpi/video.c | 78 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/intel_menlow.c | 526 | ||||
-rw-r--r-- | drivers/thermal/Kconfig | 15 | ||||
-rw-r--r-- | drivers/thermal/Makefile | 5 | ||||
-rw-r--r-- | drivers/thermal/thermal.c | 714 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 2 | ||||
-rw-r--r-- | include/acpi/processor.h | 6 | ||||
-rw-r--r-- | include/linux/thermal.h | 94 |
19 files changed, 2450 insertions, 170 deletions
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt new file mode 100644 index 000000000000..5776e090359d --- /dev/null +++ b/Documentation/thermal/sysfs-api.txt | |||
@@ -0,0 +1,246 @@ | |||
1 | Generic Thermal Sysfs driver How To | ||
2 | ========================= | ||
3 | |||
4 | Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com> | ||
5 | |||
6 | Updated: 2 January 2008 | ||
7 | |||
8 | Copyright (c) 2008 Intel Corporation | ||
9 | |||
10 | |||
11 | 0. Introduction | ||
12 | |||
13 | The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors) | ||
14 | and thermal cooling devices (fan, processor...) to register with the thermal management | ||
15 | solution and to be a part of it. | ||
16 | |||
17 | This how-to focusses on enabling new thermal zone and cooling devices to participate | ||
18 | in thermal management. | ||
19 | This solution is platform independent and any type of thermal zone devices and | ||
20 | cooling devices should be able to make use of the infrastructure. | ||
21 | |||
22 | The main task of the thermal sysfs driver is to expose thermal zone attributes as well | ||
23 | as cooling device attributes to the user space. | ||
24 | An intelligent thermal management application can make decisions based on inputs | ||
25 | from thermal zone attributes (the current temperature and trip point temperature) | ||
26 | and throttle appropriate devices. | ||
27 | |||
28 | [0-*] denotes any positive number starting from 0 | ||
29 | [1-*] denotes any positive number starting from 1 | ||
30 | |||
31 | 1. thermal sysfs driver interface functions | ||
32 | |||
33 | 1.1 thermal zone device interface | ||
34 | 1.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 | |||
57 | 1.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 | |||
63 | 1.2 thermal cooling device interface | ||
64 | 1.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 | |||
77 | 1.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 | |||
83 | 1.3 interface for binding a thermal zone device with a thermal cooling device | ||
84 | 1.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 | |||
95 | 1.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 | |||
106 | 2. sysfs attributes structure | ||
107 | |||
108 | RO read only value | ||
109 | RW read/write value | ||
110 | |||
111 | All thermal sysfs attributes will be represented under /sys/class/thermal | ||
112 | /sys/class/thermal/ | ||
113 | |||
114 | Thermal 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 | |||
122 | Thermal 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 | |||
129 | These two dynamic attributes are created/removed in pairs. | ||
130 | They represent the relationship between a thermal zone and its associated cooling device. | ||
131 | They are created/removed for each | ||
132 | thermal_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 | |||
143 | type 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 | |||
149 | temp Current temperature as reported by thermal zone (sensor) | ||
150 | Unit: degree celsius | ||
151 | RO | ||
152 | Required | ||
153 | |||
154 | mode 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 | |||
166 | trip_point_[0-*]_temp The temperature above which trip point will be fired | ||
167 | Unit: degree celsius | ||
168 | RO | ||
169 | Optional | ||
170 | |||
171 | trip_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 | |||
177 | cdev[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 | |||
182 | cdev[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 | |||
191 | type 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 | |||
199 | max_state The maximum permissible cooling state of this cooling device. | ||
200 | RO | ||
201 | Required | ||
202 | |||
203 | cur_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 | |||
210 | 3. A simple implementation | ||
211 | |||
212 | ACPI thermal zone may support multiple trip points like critical/hot/passive/active. | ||
213 | If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time, | ||
214 | it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all. | ||
215 | It has one processor and one fan, which are both registered as thermal_cooling_device. | ||
216 | If the processor is listed in _PSL method, and the fan is listed in _AL0 method, | ||
217 | the 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 | ||
61 | source "drivers/hwmon/Kconfig" | 61 | source "drivers/hwmon/Kconfig" |
62 | 62 | ||
63 | source "drivers/thermal/Kconfig" | ||
64 | |||
63 | source "drivers/watchdog/Kconfig" | 65 | source "drivers/watchdog/Kconfig" |
64 | 66 | ||
65 | source "drivers/ssb/Kconfig" | 67 | source "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/ | |||
65 | obj-$(CONFIG_W1) += w1/ | 65 | obj-$(CONFIG_W1) += w1/ |
66 | obj-$(CONFIG_POWER_SUPPLY) += power/ | 66 | obj-$(CONFIG_POWER_SUPPLY) += power/ |
67 | obj-$(CONFIG_HWMON) += hwmon/ | 67 | obj-$(CONFIG_HWMON) += hwmon/ |
68 | obj-$(CONFIG_THERMAL) += thermal/ | ||
68 | obj-$(CONFIG_WATCHDOG) += watchdog/ | 69 | obj-$(CONFIG_WATCHDOG) += watchdog/ |
69 | obj-$(CONFIG_PHONE) += telephony/ | 70 | obj-$(CONFIG_PHONE) += telephony/ |
70 | obj-$(CONFIG_MD) += md/ | 71 | obj-$(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 | |||
186 | config ACPI_THERMAL | 186 | config 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 | ||
123 | EXPORT_SYMBOL(acpi_bus_get_status); | 123 | EXPORT_SYMBOL(acpi_bus_get_status); |
124 | 124 | ||
125 | void acpi_bus_private_data_handler(acpi_handle handle, | ||
126 | u32 function, void *context) | ||
127 | { | ||
128 | return; | ||
129 | } | ||
130 | EXPORT_SYMBOL(acpi_bus_private_data_handler); | ||
131 | |||
132 | int 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 | } | ||
148 | EXPORT_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 */ | ||
72 | static 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 | |||
78 | static 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 | |||
95 | static int | ||
96 | fan_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 | |||
110 | static 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 | ||
75 | static struct proc_dir_entry *acpi_fan_dir; | 121 | static 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 | ||
221 | static int acpi_fan_add_fs(struct acpi_device *device) | ||
222 | { | ||
223 | return 0; | ||
224 | } | ||
174 | 225 | ||
226 | static 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) | |||
179 | static int acpi_fan_add(struct acpi_device *device) | 235 | static 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 | ||
217 | static int acpi_fan_remove(struct acpi_device *device, int type) | 288 | static 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 | |||
96 | static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; | 100 | static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; |
97 | static unsigned int acpi_thermal_cpufreq_is_init = 0; | 101 | static 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 | ||
164 | static 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 | |||
172 | static 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 | |||
180 | static 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 | |||
158 | void acpi_thermal_cpufreq_init(void) | 190 | void 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 */ |
214 | static int cpufreq_get_max_state(unsigned int cpu) | ||
215 | { | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int cpufreq_get_cur_state(unsigned int cpu) | ||
220 | { | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int cpufreq_set_cur_state(unsigned int cpu, int state) | ||
225 | { | ||
226 | return 0; | ||
227 | } | ||
182 | 228 | ||
183 | static int acpi_thermal_cpufreq_increase(unsigned int cpu) | 229 | static 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 */ | ||
360 | static 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 | } | ||
374 | static int | ||
375 | processor_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 | |||
386 | static int | ||
387 | processor_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 | |||
403 | static int | ||
404 | processor_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 | |||
431 | struct 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 | ||
315 | static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) | 439 | static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) |
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 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 |
72 | ACPI_MODULE_NAME("thermal"); | 69 | ACPI_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 | ||
324 | static 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 { | 344 | do { \ |
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 | |||
351 | static 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 | ||
477 | static int acpi_thermal_get_devices(struct acpi_thermal *tz) | 535 | static 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 | ||
493 | static int acpi_thermal_critical(struct acpi_thermal *tz) | 540 | static 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 */ | ||
882 | static 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 | |||
892 | static const char enabled[] = "kernel"; | ||
893 | static const char disabled[] = "user"; | ||
894 | static 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 | |||
906 | static 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 | |||
935 | static 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 | |||
972 | static 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 | |||
1013 | typedef int (*cb)(struct thermal_zone_device *, int, | ||
1014 | struct thermal_cooling_device *); | ||
1015 | static 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 | |||
1076 | failed: | ||
1077 | return result; | ||
1078 | } | ||
1079 | |||
1080 | static int | ||
1081 | acpi_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 | |||
1088 | static int | ||
1089 | acpi_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 | |||
1096 | static 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 | |||
1106 | static 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 | |||
1155 | static 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: | 1626 | remove_fs: |
1291 | if (result) { | 1627 | acpi_thermal_remove_fs(device); |
1292 | acpi_thermal_remove_fs(device); | 1628 | unregister_thermal_zone: |
1293 | kfree(tz); | 1629 | thermal_zone_device_unregister(tz->thermal_zone); |
1294 | } | 1630 | free_memory: |
1295 | 1631 | kfree(tz); | |
1632 | end: | ||
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 */ | ||
350 | static 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 | |||
358 | static 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 | |||
374 | static int | ||
375 | video_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 | |||
389 | static 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 | ||
254 | config 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 | |||
254 | endif # MISC_DEVICES | 263 | endif # 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 | |||
17 | obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o | 17 | obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o |
18 | obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o | 18 | obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o |
19 | obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o | 19 | obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o |
20 | obj-$(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 | |||
41 | MODULE_AUTHOR("Thomas Sujith"); | ||
42 | MODULE_AUTHOR("Zhang Rui"); | ||
43 | MODULE_DESCRIPTION("Intel Menlow platform specific driver"); | ||
44 | MODULE_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 | |||
55 | static 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 | |||
78 | static 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 | |||
88 | static 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 | |||
110 | static 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 | |||
144 | static 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 | */ | ||
153 | static 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 | |||
199 | static 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 | |||
213 | const static struct acpi_device_id intel_menlow_memory_ids[] = { | ||
214 | {"INT0002", 0}, | ||
215 | {"", 0}, | ||
216 | }; | ||
217 | |||
218 | static 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 | |||
238 | struct intel_menlow_attribute { | ||
239 | struct device_attribute attr; | ||
240 | struct device *device; | ||
241 | acpi_handle handle; | ||
242 | struct list_head node; | ||
243 | }; | ||
244 | |||
245 | static LIST_HEAD(intel_menlow_attr_list); | ||
246 | static 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 | */ | ||
254 | static 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 | */ | ||
275 | static 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 | |||
310 | static 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 | |||
322 | static 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 | |||
334 | static 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 | |||
353 | static 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" | ||
374 | static 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 | |||
387 | static 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 | |||
416 | static 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 | |||
475 | static 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 | |||
490 | static 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 | |||
519 | static void __exit intel_menlow_module_exit(void) | ||
520 | { | ||
521 | acpi_bus_unregister_driver(&intel_menlow_memory_driver); | ||
522 | intel_menlow_unregister_sensor(); | ||
523 | } | ||
524 | |||
525 | module_init(intel_menlow_module_init); | ||
526 | module_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 | |||
5 | menuconfig 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 | |||
5 | obj-$(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 | |||
34 | MODULE_AUTHOR("Zhang Rui") | ||
35 | MODULE_DESCRIPTION("Generic thermal management sysfs support"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | #define PREFIX "Thermal: " | ||
39 | |||
40 | struct 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 | |||
51 | static DEFINE_IDR(thermal_tz_idr); | ||
52 | static DEFINE_IDR(thermal_cdev_idr); | ||
53 | static DEFINE_MUTEX(thermal_idr_lock); | ||
54 | |||
55 | static LIST_HEAD(thermal_tz_list); | ||
56 | static LIST_HEAD(thermal_cdev_list); | ||
57 | static DEFINE_MUTEX(thermal_list_lock); | ||
58 | |||
59 | static 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 | |||
81 | static 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 | |||
95 | static ssize_t | ||
96 | type_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 | |||
103 | static ssize_t | ||
104 | temp_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 | |||
114 | static ssize_t | ||
115 | mode_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 | |||
125 | static ssize_t | ||
126 | mode_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 | |||
142 | static ssize_t | ||
143 | trip_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 | |||
158 | static ssize_t | ||
159 | trip_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 | |||
174 | static DEVICE_ATTR(type, 0444, type_show, NULL); | ||
175 | static DEVICE_ATTR(temp, 0444, temp_show, NULL); | ||
176 | static DEVICE_ATTR(mode, 0644, mode_show, mode_store); | ||
177 | |||
178 | static 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) \ | ||
202 | do { \ | ||
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) \ | ||
212 | do { \ | ||
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 | |||
221 | static ssize_t | ||
222 | thermal_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 | |||
230 | static ssize_t | ||
231 | thermal_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 | |||
239 | static ssize_t | ||
240 | thermal_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 | |||
248 | static ssize_t | ||
249 | thermal_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 | |||
269 | static struct device_attribute dev_attr_cdev_type = | ||
270 | __ATTR(type, 0444, thermal_cooling_device_type_show, NULL); | ||
271 | static DEVICE_ATTR(max_state, 0444, | ||
272 | thermal_cooling_device_max_state_show, NULL); | ||
273 | static DEVICE_ATTR(cur_state, 0644, | ||
274 | thermal_cooling_device_cur_state_show, | ||
275 | thermal_cooling_device_cur_state_store); | ||
276 | |||
277 | static ssize_t | ||
278 | thermal_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 | */ | ||
302 | int 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 | } | ||
364 | EXPORT_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 | */ | ||
374 | int 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 | } | ||
400 | EXPORT_SYMBOL(thermal_zone_unbind_cooling_device); | ||
401 | |||
402 | static 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 | |||
416 | static 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 | */ | ||
427 | struct 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 | } | ||
499 | EXPORT_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 | */ | ||
509 | void 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 | } | ||
545 | EXPORT_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 | */ | ||
557 | struct 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 | } | ||
642 | EXPORT_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 | */ | ||
649 | void 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 | } | ||
688 | EXPORT_SYMBOL(thermal_zone_device_unregister); | ||
689 | |||
690 | static 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 | |||
704 | static 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 | |||
713 | subsys_initcall(thermal_init); | ||
714 | module_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 | ||
322 | extern struct kobject *acpi_kobj; | 322 | extern struct kobject *acpi_kobj; |
323 | extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); | 323 | extern int acpi_bus_generate_netlink_event(const char*, const char*, u8, int); |
324 | void acpi_bus_private_data_handler(acpi_handle, u32, void *); | ||
325 | int 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 */ |
332 | int acpi_processor_get_limit_info(struct acpi_processor *pr); | 332 | int acpi_processor_get_limit_info(struct acpi_processor *pr); |
333 | extern struct file_operations acpi_processor_limit_fops; | 333 | extern struct file_operations acpi_processor_limit_fops; |
334 | 334 | extern struct thermal_cooling_device_ops processor_cooling_ops; | |
335 | #ifdef CONFIG_CPU_FREQ | 335 | #ifdef CONFIG_CPU_FREQ |
336 | void acpi_thermal_cpufreq_init(void); | 336 | void acpi_thermal_cpufreq_init(void); |
337 | void acpi_thermal_cpufreq_exit(void); | 337 | void 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 | |||
31 | struct thermal_zone_device; | ||
32 | struct thermal_cooling_device; | ||
33 | |||
34 | struct 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 | |||
46 | struct 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 | ||
55 | struct 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 | |||
68 | struct 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 | |||
81 | struct thermal_zone_device *thermal_zone_device_register(char *, int, void *, | ||
82 | struct thermal_zone_device_ops *); | ||
83 | void thermal_zone_device_unregister(struct thermal_zone_device *); | ||
84 | |||
85 | int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, | ||
86 | struct thermal_cooling_device *); | ||
87 | int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, | ||
88 | struct thermal_cooling_device *); | ||
89 | |||
90 | struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, | ||
91 | struct thermal_cooling_device_ops *); | ||
92 | void thermal_cooling_device_unregister(struct thermal_cooling_device *); | ||
93 | |||
94 | #endif /* __THERMAL_H__ */ | ||