aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhang Rui <rui.zhang@intel.com>2008-01-17 02:51:08 -0500
committerLen Brown <len.brown@intel.com>2008-02-01 23:12:19 -0500
commit203d3d4aa482339b4816f131f713e1b8ee37f6dd (patch)
treede7f27619e88ca6bf62bbba21fb54f213a98394a
parentaa6299926950c8dfe2fea638276cad6def092bc9 (diff)
the generic thermal sysfs driver
The Generic Thermal sysfs driver for thermal management. Signed-off-by: Zhang Rui <rui.zhang@intel.com> Signed-off-by: Thomas Sujith <sujith.thomas@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--Documentation/thermal/sysfs-api.txt246
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/thermal/Kconfig15
-rw-r--r--drivers/thermal/Makefile5
-rw-r--r--drivers/thermal/thermal.c714
-rw-r--r--include/linux/thermal.h90
7 files changed, 1073 insertions, 0 deletions
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
new file mode 100644
index 000000000000..5776e090359d
--- /dev/null
+++ b/Documentation/thermal/sysfs-api.txt
@@ -0,0 +1,246 @@
1Generic Thermal Sysfs driver How To
2=========================
3
4Written by Sujith Thomas <sujith.thomas@intel.com>, Zhang Rui <rui.zhang@intel.com>
5
6Updated: 2 January 2008
7
8Copyright (c) 2008 Intel Corporation
9
10
110. Introduction
12
13The generic thermal sysfs provides a set of interfaces for thermal zone devices (sensors)
14and thermal cooling devices (fan, processor...) to register with the thermal management
15solution and to be a part of it.
16
17This how-to focusses on enabling new thermal zone and cooling devices to participate
18in thermal management.
19This solution is platform independent and any type of thermal zone devices and
20cooling devices should be able to make use of the infrastructure.
21
22The main task of the thermal sysfs driver is to expose thermal zone attributes as well
23as cooling device attributes to the user space.
24An intelligent thermal management application can make decisions based on inputs
25from thermal zone attributes (the current temperature and trip point temperature)
26and throttle appropriate devices.
27
28[0-*] denotes any positive number starting from 0
29[1-*] denotes any positive number starting from 1
30
311. thermal sysfs driver interface functions
32
331.1 thermal zone device interface
341.1.1 struct thermal_zone_device *thermal_zone_device_register(char *name, int trips,
35 void *devdata, struct thermal_zone_device_ops *ops)
36
37 This interface function adds a new thermal zone device (sensor) to
38 /sys/class/thermal folder as thermal_zone[0-*].
39 It tries to bind all the thermal cooling devices registered at the same time.
40
41 name: the thermal zone name.
42 trips: the total number of trip points this thermal zone supports.
43 devdata: device private data
44 ops: thermal zone device callbacks.
45 .bind: bind the thermal zone device with a thermal cooling device.
46 .unbind: unbing the thermal zone device with a thermal cooling device.
47 .get_temp: get the current temperature of the thermal zone.
48 .get_mode: get the current mode (user/kernel) of the thermal zone.
49 "kernel" means thermal management is done in kernel.
50 "user" will prevent kernel thermal driver actions upon trip points
51 so that user applications can take charge of thermal management.
52 .set_mode: set the mode (user/kernel) of the thermal zone.
53 .get_trip_type: get the type of certain trip point.
54 .get_trip_temp: get the temperature above which the certain trip point
55 will be fired.
56
571.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz)
58
59 This interface function removes the thermal zone device.
60 It deletes the corresponding entry form /sys/class/thermal folder and unbind all
61 the thermal cooling devices it uses.
62
631.2 thermal cooling device interface
641.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
65 void *devdata, struct thermal_cooling_device_ops *)
66
67 This interface function adds a new thermal cooling device (fan/processor/...) to
68 /sys/class/thermal/ folder as cooling_device[0-*].
69 It tries to bind itself to all the thermal zone devices register at the same time.
70 name: the cooling device name.
71 devdata: device private data.
72 ops: thermal cooling devices callbacks.
73 .get_max_state: get the Maximum throttle state of the cooling device.
74 .get_cur_state: get the Current throttle state of the cooling device.
75 .set_cur_state: set the Current throttle state of the cooling device.
76
771.2.2 void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
78
79 This interface function remove the thermal cooling device.
80 It deletes the corresponding entry form /sys/class/thermal folder and unbind
81 itself from all the thermal zone devices using it.
82
831.3 interface for binding a thermal zone device with a thermal cooling device
841.3.1 int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
85 int trip, struct thermal_cooling_device *cdev);
86
87 This interface function bind a thermal cooling device to the certain trip point
88 of a thermal zone device.
89 This function is usually called in the thermal zone device .bind callback.
90 tz: the thermal zone device
91 cdev: thermal cooling device
92 trip: indicates which trip point the cooling devices is associated with
93 in this thermal zone.
94
951.3.2 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
96 int trip, struct thermal_cooling_device *cdev);
97
98 This interface function unbind a thermal cooling device from the certain trip point
99 of a thermal zone device.
100 This function is usually called in the thermal zone device .unbind callback.
101 tz: the thermal zone device
102 cdev: thermal cooling device
103 trip: indicates which trip point the cooling devices is associated with
104 in this thermal zone.
105
1062. sysfs attributes structure
107
108RO read only value
109RW read/write value
110
111All thermal sysfs attributes will be represented under /sys/class/thermal
112/sys/class/thermal/
113
114Thermal zone device sys I/F, created once it's registered:
115|thermal_zone[0-*]:
116 |-----type: Type of the thermal zone
117 |-----temp: Current temperature
118 |-----mode: Working mode of the thermal zone
119 |-----trip_point_[0-*]_temp: Trip point temperature
120 |-----trip_point_[0-*]_type: Trip point type
121
122Thermal cooling device sys I/F, created once it's registered:
123|cooling_device[0-*]:
124 |-----type : Type of the cooling device(processor/fan/...)
125 |-----max_state: Maximum cooling state of the cooling device
126 |-----cur_state: Current cooling state of the cooling device
127
128
129These two dynamic attributes are created/removed in pairs.
130They represent the relationship between a thermal zone and its associated cooling device.
131They are created/removed for each
132thermal_zone_bind_cooling_device/thermal_zone_unbind_cooling_device successful exection.
133
134|thermal_zone[0-*]
135 |-----cdev[0-*]: The [0-*]th cooling device in the current thermal zone
136 |-----cdev[0-*]_trip_point: Trip point that cdev[0-*] is associated with
137
138
139***************************
140* Thermal zone attributes *
141***************************
142
143type Strings which represent the thermal zone type.
144 This is given by thermal zone driver as part of registration.
145 Eg: "ACPI thermal zone" indicates it's a ACPI thermal device
146 RO
147 Optional
148
149temp Current temperature as reported by thermal zone (sensor)
150 Unit: degree celsius
151 RO
152 Required
153
154mode One of the predifned values in [kernel, user]
155 This file gives information about the algorithm
156 that is currently managing the thermal zone.
157 It can be either default kernel based algorithm
158 or user space application.
159 RW
160 Optional
161 kernel = Thermal management in kernel thermal zone driver.
162 user = Preventing kernel thermal zone driver actions upon
163 trip points so that user application can take full
164 charge of the thermal management.
165
166trip_point_[0-*]_temp The temperature above which trip point will be fired
167 Unit: degree celsius
168 RO
169 Optional
170
171trip_point_[0-*]_type Strings which indicate the type of the trip point
172 Eg. it can be one of critical, hot, passive,
173 active[0-*] for ACPI thermal zone.
174 RO
175 Optional
176
177cdev[0-*] Sysfs link to the thermal cooling device node where the sys I/F
178 for cooling device throttling control represents.
179 RO
180 Optional
181
182cdev[0-*]_trip_point The trip point with which cdev[0-*] is assocated in this thermal zone
183 -1 means the cooling device is not associated with any trip point.
184 RO
185 Optional
186
187******************************
188* Cooling device attributes *
189******************************
190
191type String which represents the type of device
192 eg: For generic ACPI: this should be "Fan",
193 "Processor" or "LCD"
194 eg. For memory controller device on intel_menlow platform:
195 this should be "Memory controller"
196 RO
197 Optional
198
199max_state The maximum permissible cooling state of this cooling device.
200 RO
201 Required
202
203cur_state The current cooling state of this cooling device.
204 the value can any integer numbers between 0 and max_state,
205 cur_state == 0 means no cooling
206 cur_state == max_state means the maximum cooling.
207 RW
208 Required
209
2103. A simple implementation
211
212ACPI thermal zone may support multiple trip points like critical/hot/passive/active.
213If an ACPI thermal zone supports critical, passive, active[0] and active[1] at the same time,
214it may register itself as a thermale_zone_device (thermal_zone1) with 4 trip points in all.
215It has one processor and one fan, which are both registered as thermal_cooling_device.
216If the processor is listed in _PSL method, and the fan is listed in _AL0 method,
217the sys I/F structure will be built like this:
218
219/sys/class/thermal:
220
221|thermal_zone1:
222 |-----type: ACPI thermal zone
223 |-----temp: 37
224 |-----mode: kernel
225 |-----trip_point_0_temp: 100
226 |-----trip_point_0_type: critical
227 |-----trip_point_1_temp: 80
228 |-----trip_point_1_type: passive
229 |-----trip_point_2_temp: 70
230 |-----trip_point_2_type: active[0]
231 |-----trip_point_3_temp: 60
232 |-----trip_point_3_type: active[1]
233 |-----cdev0: --->/sys/class/thermal/cooling_device0
234 |-----cdev0_trip_point: 1 /* cdev0 can be used for passive */
235 |-----cdev1: --->/sys/class/thermal/cooling_device3
236 |-----cdev1_trip_point: 2 /* cdev1 can be used for active[0]*/
237
238|cooling_device0:
239 |-----type: Processor
240 |-----max_state: 8
241 |-----cur_state: 0
242
243|cooling_device3:
244 |-----type: Fan
245 |-----max_state: 2
246 |-----cur_state: 0
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 08d4ae201597..8e238cfc0795 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -58,6 +58,8 @@ source "drivers/power/Kconfig"
58 58
59source "drivers/hwmon/Kconfig" 59source "drivers/hwmon/Kconfig"
60 60
61source "drivers/thermal/Kconfig"
62
61source "drivers/watchdog/Kconfig" 63source "drivers/watchdog/Kconfig"
62 64
63source "drivers/ssb/Kconfig" 65source "drivers/ssb/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 0ee9a8a4095e..a516b8b19127 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -64,6 +64,7 @@ obj-y += i2c/
64obj-$(CONFIG_W1) += w1/ 64obj-$(CONFIG_W1) += w1/
65obj-$(CONFIG_POWER_SUPPLY) += power/ 65obj-$(CONFIG_POWER_SUPPLY) += power/
66obj-$(CONFIG_HWMON) += hwmon/ 66obj-$(CONFIG_HWMON) += hwmon/
67obj-$(CONFIG_THERMAL) += thermal/
67obj-$(CONFIG_WATCHDOG) += watchdog/ 68obj-$(CONFIG_WATCHDOG) += watchdog/
68obj-$(CONFIG_PHONE) += telephony/ 69obj-$(CONFIG_PHONE) += telephony/
69obj-$(CONFIG_MD) += md/ 70obj-$(CONFIG_MD) += md/
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
new file mode 100644
index 000000000000..9b3f61200000
--- /dev/null
+++ b/drivers/thermal/Kconfig
@@ -0,0 +1,15 @@
1#
2# Generic thermal sysfs drivers configuration
3#
4
5menuconfig THERMAL
6 bool "Generic Thermal sysfs driver"
7 default y
8 help
9 Generic Thermal Sysfs driver offers a generic mechanism for
10 thermal management. Usually it's made up of one or more thermal
11 zone and cooling device.
12 each thermal zone contains its own temperature, trip points,
13 cooling devices.
14 All platforms with ACPI thermal support can use this driver.
15 If you want this support, you should say Y here
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
new file mode 100644
index 000000000000..8ef1232de376
--- /dev/null
+++ b/drivers/thermal/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for sensor chip drivers.
3#
4
5obj-$(CONFIG_THERMAL) += thermal.o
diff --git a/drivers/thermal/thermal.c b/drivers/thermal/thermal.c
new file mode 100644
index 000000000000..3273e348fd14
--- /dev/null
+++ b/drivers/thermal/thermal.c
@@ -0,0 +1,714 @@
1/*
2 * thermal.c - Generic Thermal Management Sysfs support.
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
6 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 */
25
26#include <linux/module.h>
27#include <linux/device.h>
28#include <linux/err.h>
29#include <linux/kdev_t.h>
30#include <linux/idr.h>
31#include <linux/thermal.h>
32#include <linux/spinlock.h>
33
34MODULE_AUTHOR("Zhang Rui")
35MODULE_DESCRIPTION("Generic thermal management sysfs support");
36MODULE_LICENSE("GPL");
37
38#define PREFIX "Thermal: "
39
40struct thermal_cooling_device_instance {
41 int id;
42 char name[THERMAL_NAME_LENGTH];
43 struct thermal_zone_device *tz;
44 struct thermal_cooling_device *cdev;
45 int trip;
46 char attr_name[THERMAL_NAME_LENGTH];
47 struct device_attribute attr;
48 struct list_head node;
49};
50
51static DEFINE_IDR(thermal_tz_idr);
52static DEFINE_IDR(thermal_cdev_idr);
53static DEFINE_MUTEX(thermal_idr_lock);
54
55static LIST_HEAD(thermal_tz_list);
56static LIST_HEAD(thermal_cdev_list);
57static DEFINE_MUTEX(thermal_list_lock);
58
59static int get_idr(struct idr *idr, struct mutex *lock, int *id)
60{
61 int err;
62
63 again:
64 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
65 return -ENOMEM;
66
67 if (lock)
68 mutex_lock(lock);
69 err = idr_get_new(idr, NULL, id);
70 if (lock)
71 mutex_unlock(lock);
72 if (unlikely(err == -EAGAIN))
73 goto again;
74 else if (unlikely(err))
75 return err;
76
77 *id = *id & MAX_ID_MASK;
78 return 0;
79}
80
81static void release_idr(struct idr *idr, struct mutex *lock, int id)
82{
83 if (lock)
84 mutex_lock(lock);
85 idr_remove(idr, id);
86 if (lock)
87 mutex_unlock(lock);
88}
89
90/* sys I/F for thermal zone */
91
92#define to_thermal_zone(_dev) \
93 container_of(_dev, struct thermal_zone_device, device)
94
95static ssize_t
96type_show(struct device *dev, struct device_attribute *attr, char *buf)
97{
98 struct thermal_zone_device *tz = to_thermal_zone(dev);
99
100 return sprintf(buf, "%s\n", tz->type);
101}
102
103static ssize_t
104temp_show(struct device *dev, struct device_attribute *attr, char *buf)
105{
106 struct thermal_zone_device *tz = to_thermal_zone(dev);
107
108 if (!tz->ops->get_temp)
109 return -EPERM;
110
111 return tz->ops->get_temp(tz, buf);
112}
113
114static ssize_t
115mode_show(struct device *dev, struct device_attribute *attr, char *buf)
116{
117 struct thermal_zone_device *tz = to_thermal_zone(dev);
118
119 if (!tz->ops->get_mode)
120 return -EPERM;
121
122 return tz->ops->get_mode(tz, buf);
123}
124
125static ssize_t
126mode_store(struct device *dev, struct device_attribute *attr,
127 const char *buf, size_t count)
128{
129 struct thermal_zone_device *tz = to_thermal_zone(dev);
130 int result;
131
132 if (!tz->ops->set_mode)
133 return -EPERM;
134
135 result = tz->ops->set_mode(tz, buf);
136 if (result)
137 return result;
138
139 return count;
140}
141
142static ssize_t
143trip_point_type_show(struct device *dev, struct device_attribute *attr,
144 char *buf)
145{
146 struct thermal_zone_device *tz = to_thermal_zone(dev);
147 int trip;
148
149 if (!tz->ops->get_trip_type)
150 return -EPERM;
151
152 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
153 return -EINVAL;
154
155 return tz->ops->get_trip_type(tz, trip, buf);
156}
157
158static ssize_t
159trip_point_temp_show(struct device *dev, struct device_attribute *attr,
160 char *buf)
161{
162 struct thermal_zone_device *tz = to_thermal_zone(dev);
163 int trip;
164
165 if (!tz->ops->get_trip_temp)
166 return -EPERM;
167
168 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
169 return -EINVAL;
170
171 return tz->ops->get_trip_temp(tz, trip, buf);
172}
173
174static DEVICE_ATTR(type, 0444, type_show, NULL);
175static DEVICE_ATTR(temp, 0444, temp_show, NULL);
176static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
177
178static struct device_attribute trip_point_attrs[] = {
179 __ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
180 __ATTR(trip_point_0_temp, 0444, trip_point_temp_show, NULL),
181 __ATTR(trip_point_1_type, 0444, trip_point_type_show, NULL),
182 __ATTR(trip_point_1_temp, 0444, trip_point_temp_show, NULL),
183 __ATTR(trip_point_2_type, 0444, trip_point_type_show, NULL),
184 __ATTR(trip_point_2_temp, 0444, trip_point_temp_show, NULL),
185 __ATTR(trip_point_3_type, 0444, trip_point_type_show, NULL),
186 __ATTR(trip_point_3_temp, 0444, trip_point_temp_show, NULL),
187 __ATTR(trip_point_4_type, 0444, trip_point_type_show, NULL),
188 __ATTR(trip_point_4_temp, 0444, trip_point_temp_show, NULL),
189 __ATTR(trip_point_5_type, 0444, trip_point_type_show, NULL),
190 __ATTR(trip_point_5_temp, 0444, trip_point_temp_show, NULL),
191 __ATTR(trip_point_6_type, 0444, trip_point_type_show, NULL),
192 __ATTR(trip_point_6_temp, 0444, trip_point_temp_show, NULL),
193 __ATTR(trip_point_7_type, 0444, trip_point_type_show, NULL),
194 __ATTR(trip_point_7_temp, 0444, trip_point_temp_show, NULL),
195 __ATTR(trip_point_8_type, 0444, trip_point_type_show, NULL),
196 __ATTR(trip_point_8_temp, 0444, trip_point_temp_show, NULL),
197 __ATTR(trip_point_9_type, 0444, trip_point_type_show, NULL),
198 __ATTR(trip_point_9_temp, 0444, trip_point_temp_show, NULL),
199};
200
201#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
202do { \
203 result = device_create_file(_dev, \
204 &trip_point_attrs[_index * 2]); \
205 if (result) \
206 break; \
207 result = device_create_file(_dev, \
208 &trip_point_attrs[_index * 2 + 1]); \
209} while (0)
210
211#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
212do { \
213 device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
214 device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
215} while (0)
216
217/* sys I/F for cooling device */
218#define to_cooling_device(_dev) \
219 container_of(_dev, struct thermal_cooling_device, device)
220
221static ssize_t
222thermal_cooling_device_type_show(struct device *dev,
223 struct device_attribute *attr, char *buf)
224{
225 struct thermal_cooling_device *cdev = to_cooling_device(dev);
226
227 return sprintf(buf, "%s\n", cdev->type);
228}
229
230static ssize_t
231thermal_cooling_device_max_state_show(struct device *dev,
232 struct device_attribute *attr, char *buf)
233{
234 struct thermal_cooling_device *cdev = to_cooling_device(dev);
235
236 return cdev->ops->get_max_state(cdev, buf);
237}
238
239static ssize_t
240thermal_cooling_device_cur_state_show(struct device *dev,
241 struct device_attribute *attr, char *buf)
242{
243 struct thermal_cooling_device *cdev = to_cooling_device(dev);
244
245 return cdev->ops->get_cur_state(cdev, buf);
246}
247
248static ssize_t
249thermal_cooling_device_cur_state_store(struct device *dev,
250 struct device_attribute *attr,
251 const char *buf, size_t count)
252{
253 struct thermal_cooling_device *cdev = to_cooling_device(dev);
254 int state;
255 int result;
256
257 if (!sscanf(buf, "%d\n", &state))
258 return -EINVAL;
259
260 if (state < 0)
261 return -EINVAL;
262
263 result = cdev->ops->set_cur_state(cdev, state);
264 if (result)
265 return result;
266 return count;
267}
268
269static struct device_attribute dev_attr_cdev_type =
270 __ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
271static DEVICE_ATTR(max_state, 0444,
272 thermal_cooling_device_max_state_show, NULL);
273static DEVICE_ATTR(cur_state, 0644,
274 thermal_cooling_device_cur_state_show,
275 thermal_cooling_device_cur_state_store);
276
277static ssize_t
278thermal_cooling_device_trip_point_show(struct device *dev,
279 struct device_attribute *attr, char *buf)
280{
281 struct thermal_cooling_device_instance *instance;
282
283 instance =
284 container_of(attr, struct thermal_cooling_device_instance, attr);
285
286 if (instance->trip == THERMAL_TRIPS_NONE)
287 return sprintf(buf, "-1\n");
288 else
289 return sprintf(buf, "%d\n", instance->trip);
290}
291
292/* Device management */
293
294/**
295 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
296 * this function is usually called in the thermal zone device .bind callback.
297 * @tz: thermal zone device
298 * @trip: indicates which trip point the cooling devices is
299 * associated with in this thermal zone.
300 * @cdev: thermal cooling device
301 */
302int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
303 int trip,
304 struct thermal_cooling_device *cdev)
305{
306 struct thermal_cooling_device_instance *dev;
307 struct thermal_cooling_device_instance *pos;
308 int result;
309
310 if (trip >= tz->trips ||
311 (trip < 0 && trip != THERMAL_TRIPS_NONE))
312 return -EINVAL;
313
314 if (!tz || !cdev)
315 return -EINVAL;
316
317 dev =
318 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
319 if (!dev)
320 return -ENOMEM;
321 dev->tz = tz;
322 dev->cdev = cdev;
323 dev->trip = trip;
324 result = get_idr(&tz->idr, &tz->lock, &dev->id);
325 if (result)
326 goto free_mem;
327
328 sprintf(dev->name, "cdev%d", dev->id);
329 result =
330 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
331 if (result)
332 goto release_idr;
333
334 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
335 dev->attr.attr.name = dev->attr_name;
336 dev->attr.attr.mode = 0444;
337 dev->attr.show = thermal_cooling_device_trip_point_show;
338 result = device_create_file(&tz->device, &dev->attr);
339 if (result)
340 goto remove_symbol_link;
341
342 mutex_lock(&tz->lock);
343 list_for_each_entry(pos, &tz->cooling_devices, node)
344 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
345 result = -EEXIST;
346 break;
347 }
348 if (!result)
349 list_add_tail(&dev->node, &tz->cooling_devices);
350 mutex_unlock(&tz->lock);
351
352 if (!result)
353 return 0;
354
355 device_remove_file(&tz->device, &dev->attr);
356 remove_symbol_link:
357 sysfs_remove_link(&tz->device.kobj, dev->name);
358 release_idr:
359 release_idr(&tz->idr, &tz->lock, dev->id);
360 free_mem:
361 kfree(dev);
362 return result;
363}
364EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
365
366/**
367 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
368 * this function is usually called in the thermal zone device .unbind callback.
369 * @tz: thermal zone device
370 * @trip: indicates which trip point the cooling devices is
371 * associated with in this thermal zone.
372 * @cdev: thermal cooling device
373 */
374int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
375 int trip,
376 struct thermal_cooling_device *cdev)
377{
378 struct thermal_cooling_device_instance *pos, *next;
379
380 mutex_lock(&tz->lock);
381 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
382 if (pos->tz == tz && pos->trip == trip
383 && pos->cdev == cdev) {
384 list_del(&pos->node);
385 mutex_unlock(&tz->lock);
386 goto unbind;
387 }
388 }
389 mutex_unlock(&tz->lock);
390
391 return -ENODEV;
392
393 unbind:
394 device_remove_file(&tz->device, &pos->attr);
395 sysfs_remove_link(&tz->device.kobj, pos->name);
396 release_idr(&tz->idr, &tz->lock, pos->id);
397 kfree(pos);
398 return 0;
399}
400EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
401
402static void thermal_release(struct device *dev)
403{
404 struct thermal_zone_device *tz;
405 struct thermal_cooling_device *cdev;
406
407 if (!strncmp(dev->bus_id, "thermal_zone", sizeof "thermal_zone" - 1)) {
408 tz = to_thermal_zone(dev);
409 kfree(tz);
410 } else {
411 cdev = to_cooling_device(dev);
412 kfree(cdev);
413 }
414}
415
416static struct class thermal_class = {
417 .name = "thermal",
418 .dev_release = thermal_release,
419};
420
421/**
422 * thermal_cooling_device_register - register a new thermal cooling device
423 * @type: the thermal cooling device type.
424 * @devdata: device private data.
425 * @ops: standard thermal cooling devices callbacks.
426 */
427struct thermal_cooling_device *thermal_cooling_device_register(char *type,
428 void *devdata, struct thermal_cooling_device_ops *ops)
429{
430 struct thermal_cooling_device *cdev;
431 struct thermal_zone_device *pos;
432 int result;
433
434 if (strlen(type) >= THERMAL_NAME_LENGTH)
435 return NULL;
436
437 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
438 !ops->set_cur_state)
439 return NULL;
440
441 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
442 if (!cdev)
443 return NULL;
444
445 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
446 if (result) {
447 kfree(cdev);
448 return NULL;
449 }
450
451 strcpy(cdev->type, type);
452 cdev->ops = ops;
453 cdev->device.class = &thermal_class;
454 cdev->devdata = devdata;
455 sprintf(cdev->device.bus_id, "cooling_device%d", cdev->id);
456 result = device_register(&cdev->device);
457 if (result) {
458 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
459 kfree(cdev);
460 return NULL;
461 }
462
463 /* sys I/F */
464 if (type) {
465 result = device_create_file(&cdev->device,
466 &dev_attr_cdev_type);
467 if (result)
468 goto unregister;
469 }
470
471 result = device_create_file(&cdev->device, &dev_attr_max_state);
472 if (result)
473 goto unregister;
474
475 result = device_create_file(&cdev->device, &dev_attr_cur_state);
476 if (result)
477 goto unregister;
478
479 mutex_lock(&thermal_list_lock);
480 list_add(&cdev->node, &thermal_cdev_list);
481 list_for_each_entry(pos, &thermal_tz_list, node) {
482 if (!pos->ops->bind)
483 continue;
484 result = pos->ops->bind(pos, cdev);
485 if (result)
486 break;
487
488 }
489 mutex_unlock(&thermal_list_lock);
490
491 if (!result)
492 return cdev;
493
494 unregister:
495 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
496 device_unregister(&cdev->device);
497 return NULL;
498}
499EXPORT_SYMBOL(thermal_cooling_device_register);
500
501/**
502 * thermal_cooling_device_unregister - removes the registered thermal cooling device
503 *
504 * @cdev: the thermal cooling device to remove.
505 *
506 * thermal_cooling_device_unregister() must be called when the device is no
507 * longer needed.
508 */
509void thermal_cooling_device_unregister(struct
510 thermal_cooling_device
511 *cdev)
512{
513 struct thermal_zone_device *tz;
514 struct thermal_cooling_device *pos = NULL;
515
516 if (!cdev)
517 return;
518
519 mutex_lock(&thermal_list_lock);
520 list_for_each_entry(pos, &thermal_cdev_list, node)
521 if (pos == cdev)
522 break;
523 if (pos != cdev) {
524 /* thermal cooling device not found */
525 mutex_unlock(&thermal_list_lock);
526 return;
527 }
528 list_del(&cdev->node);
529 list_for_each_entry(tz, &thermal_tz_list, node) {
530 if (!tz->ops->unbind)
531 continue;
532 tz->ops->unbind(tz, cdev);
533 }
534 mutex_unlock(&thermal_list_lock);
535 if (cdev->type[0])
536 device_remove_file(&cdev->device,
537 &dev_attr_cdev_type);
538 device_remove_file(&cdev->device, &dev_attr_max_state);
539 device_remove_file(&cdev->device, &dev_attr_cur_state);
540
541 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
542 device_unregister(&cdev->device);
543 return;
544}
545EXPORT_SYMBOL(thermal_cooling_device_unregister);
546
547/**
548 * thermal_zone_device_register - register a new thermal zone device
549 * @type: the thermal zone device type
550 * @trips: the number of trip points the thermal zone support
551 * @devdata: private device data
552 * @ops: standard thermal zone device callbacks
553 *
554 * thermal_zone_device_unregister() must be called when the device is no
555 * longer needed.
556 */
557struct thermal_zone_device *thermal_zone_device_register(char *type,
558 int trips, void *devdata,
559 struct thermal_zone_device_ops *ops)
560{
561 struct thermal_zone_device *tz;
562 struct thermal_cooling_device *pos;
563 int result;
564 int count;
565
566 if (strlen(type) >= THERMAL_NAME_LENGTH)
567 return NULL;
568
569 if (trips > THERMAL_MAX_TRIPS || trips < 0)
570 return NULL;
571
572 if (!ops || !ops->get_temp)
573 return NULL;
574
575 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
576 if (!tz)
577 return NULL;
578
579 INIT_LIST_HEAD(&tz->cooling_devices);
580 idr_init(&tz->idr);
581 mutex_init(&tz->lock);
582 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
583 if (result) {
584 kfree(tz);
585 return NULL;
586 }
587
588 strcpy(tz->type, type);
589 tz->ops = ops;
590 tz->device.class = &thermal_class;
591 tz->devdata = devdata;
592 tz->trips = trips;
593 sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
594 result = device_register(&tz->device);
595 if (result) {
596 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
597 kfree(tz);
598 return NULL;
599 }
600
601 /* sys I/F */
602 if (type) {
603 result = device_create_file(&tz->device, &dev_attr_type);
604 if (result)
605 goto unregister;
606 }
607
608 result = device_create_file(&tz->device, &dev_attr_temp);
609 if (result)
610 goto unregister;
611
612 if (ops->get_mode) {
613 result = device_create_file(&tz->device, &dev_attr_mode);
614 if (result)
615 goto unregister;
616 }
617
618 for (count = 0; count < trips; count++) {
619 TRIP_POINT_ATTR_ADD(&tz->device, count, result);
620 if (result)
621 goto unregister;
622 }
623
624 mutex_lock(&thermal_list_lock);
625 list_add_tail(&tz->node, &thermal_tz_list);
626 if (ops->bind)
627 list_for_each_entry(pos, &thermal_cdev_list, node) {
628 result = ops->bind(tz, pos);
629 if (result)
630 break;
631 }
632 mutex_unlock(&thermal_list_lock);
633
634 if (!result)
635 return tz;
636
637 unregister:
638 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
639 device_unregister(&tz->device);
640 return NULL;
641}
642EXPORT_SYMBOL(thermal_zone_device_register);
643
644/**
645 * thermal_device_unregister - removes the registered thermal zone device
646 *
647 * @tz: the thermal zone device to remove
648 */
649void thermal_zone_device_unregister(struct thermal_zone_device *tz)
650{
651 struct thermal_cooling_device *cdev;
652 struct thermal_zone_device *pos = NULL;
653 int count;
654
655 if (!tz)
656 return;
657
658 mutex_lock(&thermal_list_lock);
659 list_for_each_entry(pos, &thermal_tz_list, node)
660 if (pos == tz)
661 break;
662 if (pos != tz) {
663 /* thermal zone device not found */
664 mutex_unlock(&thermal_list_lock);
665 return;
666 }
667 list_del(&tz->node);
668 if (tz->ops->unbind)
669 list_for_each_entry(cdev, &thermal_cdev_list, node)
670 tz->ops->unbind(tz, cdev);
671 mutex_unlock(&thermal_list_lock);
672
673 if (tz->type[0])
674 device_remove_file(&tz->device, &dev_attr_type);
675 device_remove_file(&tz->device, &dev_attr_temp);
676 if (tz->ops->get_mode)
677 device_remove_file(&tz->device, &dev_attr_mode);
678
679 for (count = 0; count < tz->trips; count++)
680 TRIP_POINT_ATTR_REMOVE(&tz->device, count);
681
682 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
683 idr_destroy(&tz->idr);
684 mutex_destroy(&tz->lock);
685 device_unregister(&tz->device);
686 return;
687}
688EXPORT_SYMBOL(thermal_zone_device_unregister);
689
690static int __init thermal_init(void)
691{
692 int result = 0;
693
694 result = class_register(&thermal_class);
695 if (result) {
696 idr_destroy(&thermal_tz_idr);
697 idr_destroy(&thermal_cdev_idr);
698 mutex_destroy(&thermal_idr_lock);
699 mutex_destroy(&thermal_list_lock);
700 }
701 return result;
702}
703
704static void __exit thermal_exit(void)
705{
706 class_unregister(&thermal_class);
707 idr_destroy(&thermal_tz_idr);
708 idr_destroy(&thermal_cdev_idr);
709 mutex_destroy(&thermal_idr_lock);
710 mutex_destroy(&thermal_list_lock);
711}
712
713subsys_initcall(thermal_init);
714module_exit(thermal_exit);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
new file mode 100644
index 000000000000..e4b76c7afb51
--- /dev/null
+++ b/include/linux/thermal.h
@@ -0,0 +1,90 @@
1/*
2 * thermal.h ($Revision: 0 $)
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
6 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
24
25#ifndef __THERMAL_H__
26#define __THERMAL_H__
27
28#include <linux/idr.h>
29#include <linux/device.h>
30
31struct thermal_zone_device;
32struct thermal_cooling_device;
33
34struct thermal_zone_device_ops {
35 int (*bind) (struct thermal_zone_device *,
36 struct thermal_cooling_device *);
37 int (*unbind) (struct thermal_zone_device *,
38 struct thermal_cooling_device *);
39 int (*get_temp) (struct thermal_zone_device *, char *);
40 int (*get_mode) (struct thermal_zone_device *, char *);
41 int (*set_mode) (struct thermal_zone_device *, const char *);
42 int (*get_trip_type) (struct thermal_zone_device *, int, char *);
43 int (*get_trip_temp) (struct thermal_zone_device *, int, char *);
44};
45
46struct thermal_cooling_device_ops {
47 int (*get_max_state) (struct thermal_cooling_device *, char *);
48 int (*get_cur_state) (struct thermal_cooling_device *, char *);
49 int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
50};
51
52#define THERMAL_TRIPS_NONE -1
53#define THERMAL_MAX_TRIPS 10
54#define THERMAL_NAME_LENGTH 20
55struct thermal_cooling_device {
56 int id;
57 char type[THERMAL_NAME_LENGTH];
58 struct device device;
59 void *devdata;
60 struct thermal_cooling_device_ops *ops;
61 struct list_head node;
62};
63
64struct thermal_zone_device {
65 int id;
66 char type[THERMAL_NAME_LENGTH];
67 struct device device;
68 void *devdata;
69 int trips;
70 struct thermal_zone_device_ops *ops;
71 struct list_head cooling_devices;
72 struct idr idr;
73 struct mutex lock; /* protect cooling devices list */
74 struct list_head node;
75};
76
77struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
78 struct thermal_zone_device_ops *);
79void thermal_zone_device_unregister(struct thermal_zone_device *);
80
81int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
82 struct thermal_cooling_device *);
83int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
84 struct thermal_cooling_device *);
85
86struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
87 struct thermal_cooling_device_ops *);
88void thermal_cooling_device_unregister(struct thermal_cooling_device *);
89
90#endif /* __THERMAL_H__ */