aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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 /drivers
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>
Diffstat (limited to 'drivers')
-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
5 files changed, 737 insertions, 0 deletions
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);