blob: 7970e8911e277f6b5fd76160996a5313bd92881d [file] [log] [blame]
Zhang Rui203d3d42008-01-17 15:51:08 +08001/*
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090029#include <linux/slab.h>
Zhang Rui203d3d42008-01-17 15:51:08 +080030#include <linux/kdev_t.h>
31#include <linux/idr.h>
32#include <linux/thermal.h>
33#include <linux/spinlock.h>
Matthew Garrettb1569e92008-12-03 17:55:32 +000034#include <linux/reboot.h>
R.Durgadoss4cb18722010-10-27 03:33:29 +053035#include <net/netlink.h>
36#include <net/genetlink.h>
Zhang Rui203d3d42008-01-17 15:51:08 +080037
Zhang Rui63c4ec92008-04-21 16:07:13 +080038MODULE_AUTHOR("Zhang Rui");
Zhang Rui203d3d42008-01-17 15:51:08 +080039MODULE_DESCRIPTION("Generic thermal management sysfs support");
40MODULE_LICENSE("GPL");
41
42#define PREFIX "Thermal: "
43
44struct thermal_cooling_device_instance {
45 int id;
46 char name[THERMAL_NAME_LENGTH];
47 struct thermal_zone_device *tz;
48 struct thermal_cooling_device *cdev;
49 int trip;
50 char attr_name[THERMAL_NAME_LENGTH];
51 struct device_attribute attr;
52 struct list_head node;
53};
54
55static DEFINE_IDR(thermal_tz_idr);
56static DEFINE_IDR(thermal_cdev_idr);
57static DEFINE_MUTEX(thermal_idr_lock);
58
59static LIST_HEAD(thermal_tz_list);
60static LIST_HEAD(thermal_cdev_list);
61static DEFINE_MUTEX(thermal_list_lock);
62
63static int get_idr(struct idr *idr, struct mutex *lock, int *id)
64{
65 int err;
66
67 again:
68 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
69 return -ENOMEM;
70
71 if (lock)
72 mutex_lock(lock);
73 err = idr_get_new(idr, NULL, id);
74 if (lock)
75 mutex_unlock(lock);
76 if (unlikely(err == -EAGAIN))
77 goto again;
78 else if (unlikely(err))
79 return err;
80
81 *id = *id & MAX_ID_MASK;
82 return 0;
83}
84
85static void release_idr(struct idr *idr, struct mutex *lock, int id)
86{
87 if (lock)
88 mutex_lock(lock);
89 idr_remove(idr, id);
90 if (lock)
91 mutex_unlock(lock);
92}
93
94/* sys I/F for thermal zone */
95
96#define to_thermal_zone(_dev) \
97 container_of(_dev, struct thermal_zone_device, device)
98
99static ssize_t
100type_show(struct device *dev, struct device_attribute *attr, char *buf)
101{
102 struct thermal_zone_device *tz = to_thermal_zone(dev);
103
104 return sprintf(buf, "%s\n", tz->type);
105}
106
107static ssize_t
108temp_show(struct device *dev, struct device_attribute *attr, char *buf)
109{
110 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000111 long temperature;
112 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800113
114 if (!tz->ops->get_temp)
115 return -EPERM;
116
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000117 ret = tz->ops->get_temp(tz, &temperature);
118
119 if (ret)
120 return ret;
121
122 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800123}
124
125static ssize_t
126mode_show(struct device *dev, struct device_attribute *attr, char *buf)
127{
128 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000129 enum thermal_device_mode mode;
130 int result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800131
132 if (!tz->ops->get_mode)
133 return -EPERM;
134
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000135 result = tz->ops->get_mode(tz, &mode);
136 if (result)
137 return result;
138
139 return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
140 : "disabled");
Zhang Rui203d3d42008-01-17 15:51:08 +0800141}
142
143static ssize_t
144mode_store(struct device *dev, struct device_attribute *attr,
145 const char *buf, size_t count)
146{
147 struct thermal_zone_device *tz = to_thermal_zone(dev);
148 int result;
149
150 if (!tz->ops->set_mode)
151 return -EPERM;
152
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000153 if (!strncmp(buf, "enabled", sizeof("enabled")))
154 result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
155 else if (!strncmp(buf, "disabled", sizeof("disabled")))
156 result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
157 else
158 result = -EINVAL;
159
Zhang Rui203d3d42008-01-17 15:51:08 +0800160 if (result)
161 return result;
162
163 return count;
164}
165
166static ssize_t
167trip_point_type_show(struct device *dev, struct device_attribute *attr,
168 char *buf)
169{
170 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000171 enum thermal_trip_type type;
172 int trip, result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800173
174 if (!tz->ops->get_trip_type)
175 return -EPERM;
176
177 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
178 return -EINVAL;
179
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000180 result = tz->ops->get_trip_type(tz, trip, &type);
181 if (result)
182 return result;
183
184 switch (type) {
185 case THERMAL_TRIP_CRITICAL:
Amit Kucheria625120a2009-10-16 12:46:02 +0300186 return sprintf(buf, "critical\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000187 case THERMAL_TRIP_HOT:
Amit Kucheria625120a2009-10-16 12:46:02 +0300188 return sprintf(buf, "hot\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 case THERMAL_TRIP_CONFIGURABLE_HI:
190 return sprintf(buf, "configurable_hi\n");
191 case THERMAL_TRIP_CONFIGURABLE_LOW:
192 return sprintf(buf, "configurable_low\n");
193 case THERMAL_TRIP_CRITICAL_LOW:
194 return sprintf(buf, "critical_low\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000195 case THERMAL_TRIP_PASSIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300196 return sprintf(buf, "passive\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000197 case THERMAL_TRIP_ACTIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300198 return sprintf(buf, "active\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000199 default:
Amit Kucheria625120a2009-10-16 12:46:02 +0300200 return sprintf(buf, "unknown\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000201 }
Zhang Rui203d3d42008-01-17 15:51:08 +0800202}
203
204static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205trip_point_type_activate(struct device *dev, struct device_attribute *attr,
206 const char *buf, size_t count)
207{
208 struct thermal_zone_device *tz = to_thermal_zone(dev);
209 int trip, result;
210
211 if (!tz->ops->activate_trip_type)
212 return -EPERM;
213
214 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
215 return -EINVAL;
216
217 if (!strncmp(buf, "enabled", sizeof("enabled")))
218 result = tz->ops->activate_trip_type(tz, trip,
219 THERMAL_TRIP_ACTIVATION_ENABLED);
220 else if (!strncmp(buf, "disabled", sizeof("disabled")))
221 result = tz->ops->activate_trip_type(tz, trip,
222 THERMAL_TRIP_ACTIVATION_DISABLED);
223 else
224 result = -EINVAL;
225
226 if (result)
227 return result;
228
229 return count;
230}
231
232static ssize_t
Zhang Rui203d3d42008-01-17 15:51:08 +0800233trip_point_temp_show(struct device *dev, struct device_attribute *attr,
234 char *buf)
235{
236 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000237 int trip, ret;
238 long temperature;
Zhang Rui203d3d42008-01-17 15:51:08 +0800239
240 if (!tz->ops->get_trip_temp)
241 return -EPERM;
242
243 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
244 return -EINVAL;
245
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000246 ret = tz->ops->get_trip_temp(tz, trip, &temperature);
247
248 if (ret)
249 return ret;
250
251 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800252}
253
Matthew Garrett03a971a2008-12-03 18:00:38 +0000254static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255trip_point_temp_set(struct device *dev, struct device_attribute *attr,
256 const char *buf, size_t count)
257{
258 struct thermal_zone_device *tz = to_thermal_zone(dev);
259 int trip, ret;
260 long temperature;
261
262 if (!tz->ops->set_trip_temp)
263 return -EPERM;
264
265 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
266 return -EINVAL;
267
268 if (!sscanf(buf, "%ld", &temperature))
269 return -EINVAL;
270
271 ret = tz->ops->set_trip_temp(tz, trip, temperature);
272 if (ret)
273 return ret;
274
275 return count;
276}
277
278static ssize_t
Matthew Garrett03a971a2008-12-03 18:00:38 +0000279passive_store(struct device *dev, struct device_attribute *attr,
280 const char *buf, size_t count)
281{
282 struct thermal_zone_device *tz = to_thermal_zone(dev);
283 struct thermal_cooling_device *cdev = NULL;
284 int state;
285
286 if (!sscanf(buf, "%d\n", &state))
287 return -EINVAL;
288
Frans Pop3d8e3ad2009-10-26 08:39:02 +0100289 /* sanity check: values below 1000 millicelcius don't make sense
290 * and can cause the system to go into a thermal heart attack
291 */
292 if (state && state < 1000)
293 return -EINVAL;
294
Matthew Garrett03a971a2008-12-03 18:00:38 +0000295 if (state && !tz->forced_passive) {
296 mutex_lock(&thermal_list_lock);
297 list_for_each_entry(cdev, &thermal_cdev_list, node) {
298 if (!strncmp("Processor", cdev->type,
299 sizeof("Processor")))
300 thermal_zone_bind_cooling_device(tz,
301 THERMAL_TRIPS_NONE,
302 cdev);
303 }
304 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100305 if (!tz->passive_delay)
306 tz->passive_delay = 1000;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000307 } else if (!state && tz->forced_passive) {
308 mutex_lock(&thermal_list_lock);
309 list_for_each_entry(cdev, &thermal_cdev_list, node) {
310 if (!strncmp("Processor", cdev->type,
311 sizeof("Processor")))
312 thermal_zone_unbind_cooling_device(tz,
313 THERMAL_TRIPS_NONE,
314 cdev);
315 }
316 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100317 tz->passive_delay = 0;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000318 }
319
320 tz->tc1 = 1;
321 tz->tc2 = 1;
322
Matthew Garrett03a971a2008-12-03 18:00:38 +0000323 tz->forced_passive = state;
324
325 thermal_zone_device_update(tz);
326
327 return count;
328}
329
330static ssize_t
331passive_show(struct device *dev, struct device_attribute *attr,
332 char *buf)
333{
334 struct thermal_zone_device *tz = to_thermal_zone(dev);
335
336 return sprintf(buf, "%d\n", tz->forced_passive);
337}
338
Zhang Rui203d3d42008-01-17 15:51:08 +0800339static DEVICE_ATTR(type, 0444, type_show, NULL);
340static DEVICE_ATTR(temp, 0444, temp_show, NULL);
341static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
Matthew Garrett03a971a2008-12-03 18:00:38 +0000342static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, \
343 passive_store);
Zhang Rui203d3d42008-01-17 15:51:08 +0800344
345static struct device_attribute trip_point_attrs[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 __ATTR(trip_point_0_type, 0644, trip_point_type_show,
347 trip_point_type_activate),
348 __ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
349 trip_point_temp_set),
350 __ATTR(trip_point_1_type, 0644, trip_point_type_show,
351 trip_point_type_activate),
352 __ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
353 trip_point_temp_set),
354 __ATTR(trip_point_2_type, 0644, trip_point_type_show,
355 trip_point_type_activate),
356 __ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
357 trip_point_temp_set),
358 __ATTR(trip_point_3_type, 0644, trip_point_type_show,
359 trip_point_type_activate),
360 __ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
361 trip_point_temp_set),
362 __ATTR(trip_point_4_type, 0644, trip_point_type_show,
363 trip_point_type_activate),
364 __ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
365 trip_point_temp_set),
366 __ATTR(trip_point_5_type, 0644, trip_point_type_show,
367 trip_point_type_activate),
368 __ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
369 trip_point_temp_set),
370 __ATTR(trip_point_6_type, 0644, trip_point_type_show,
371 trip_point_type_activate),
372 __ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
373 trip_point_temp_set),
374 __ATTR(trip_point_7_type, 0644, trip_point_type_show,
375 trip_point_type_activate),
376 __ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
377 trip_point_temp_set),
378 __ATTR(trip_point_8_type, 0644, trip_point_type_show,
379 trip_point_type_activate),
380 __ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
381 trip_point_temp_set),
382 __ATTR(trip_point_9_type, 0644, trip_point_type_show,
383 trip_point_type_activate),
384 __ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
385 trip_point_temp_set),
386 __ATTR(trip_point_10_type, 0644, trip_point_type_show,
387 trip_point_type_activate),
388 __ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
389 trip_point_temp_set),
390 __ATTR(trip_point_11_type, 0644, trip_point_type_show,
391 trip_point_type_activate),
392 __ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
393 trip_point_temp_set),
Zhang Rui203d3d42008-01-17 15:51:08 +0800394};
395
396#define TRIP_POINT_ATTR_ADD(_dev, _index, result) \
397do { \
398 result = device_create_file(_dev, \
399 &trip_point_attrs[_index * 2]); \
400 if (result) \
401 break; \
402 result = device_create_file(_dev, \
403 &trip_point_attrs[_index * 2 + 1]); \
404} while (0)
405
406#define TRIP_POINT_ATTR_REMOVE(_dev, _index) \
407do { \
408 device_remove_file(_dev, &trip_point_attrs[_index * 2]); \
409 device_remove_file(_dev, &trip_point_attrs[_index * 2 + 1]); \
410} while (0)
411
412/* sys I/F for cooling device */
413#define to_cooling_device(_dev) \
414 container_of(_dev, struct thermal_cooling_device, device)
415
416static ssize_t
417thermal_cooling_device_type_show(struct device *dev,
418 struct device_attribute *attr, char *buf)
419{
420 struct thermal_cooling_device *cdev = to_cooling_device(dev);
421
422 return sprintf(buf, "%s\n", cdev->type);
423}
424
425static ssize_t
426thermal_cooling_device_max_state_show(struct device *dev,
427 struct device_attribute *attr, char *buf)
428{
429 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000430 unsigned long state;
431 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800432
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000433 ret = cdev->ops->get_max_state(cdev, &state);
434 if (ret)
435 return ret;
436 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800437}
438
439static ssize_t
440thermal_cooling_device_cur_state_show(struct device *dev,
441 struct device_attribute *attr, char *buf)
442{
443 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000444 unsigned long state;
445 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800446
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000447 ret = cdev->ops->get_cur_state(cdev, &state);
448 if (ret)
449 return ret;
450 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800451}
452
453static ssize_t
454thermal_cooling_device_cur_state_store(struct device *dev,
455 struct device_attribute *attr,
456 const char *buf, size_t count)
457{
458 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000459 unsigned long state;
Zhang Rui203d3d42008-01-17 15:51:08 +0800460 int result;
461
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000462 if (!sscanf(buf, "%ld\n", &state))
Zhang Rui203d3d42008-01-17 15:51:08 +0800463 return -EINVAL;
464
Roel Kluinedb94912009-12-15 22:46:50 +0100465 if ((long)state < 0)
Zhang Rui203d3d42008-01-17 15:51:08 +0800466 return -EINVAL;
467
468 result = cdev->ops->set_cur_state(cdev, state);
469 if (result)
470 return result;
471 return count;
472}
473
474static struct device_attribute dev_attr_cdev_type =
Len Brown543a9562008-02-07 16:55:08 -0500475__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800476static DEVICE_ATTR(max_state, 0444,
477 thermal_cooling_device_max_state_show, NULL);
478static DEVICE_ATTR(cur_state, 0644,
479 thermal_cooling_device_cur_state_show,
480 thermal_cooling_device_cur_state_store);
481
482static ssize_t
483thermal_cooling_device_trip_point_show(struct device *dev,
Len Brown543a9562008-02-07 16:55:08 -0500484 struct device_attribute *attr, char *buf)
Zhang Rui203d3d42008-01-17 15:51:08 +0800485{
486 struct thermal_cooling_device_instance *instance;
487
488 instance =
489 container_of(attr, struct thermal_cooling_device_instance, attr);
490
491 if (instance->trip == THERMAL_TRIPS_NONE)
492 return sprintf(buf, "-1\n");
493 else
494 return sprintf(buf, "%d\n", instance->trip);
495}
496
497/* Device management */
498
Rene Herman16d75232008-06-24 19:38:56 +0200499#if defined(CONFIG_THERMAL_HWMON)
500
Zhang Ruie68b16a2008-04-21 16:07:52 +0800501/* hwmon sys I/F */
502#include <linux/hwmon.h>
503static LIST_HEAD(thermal_hwmon_list);
504
505static ssize_t
506name_show(struct device *dev, struct device_attribute *attr, char *buf)
507{
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700508 struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800509 return sprintf(buf, "%s\n", hwmon->type);
510}
511static DEVICE_ATTR(name, 0444, name_show, NULL);
512
513static ssize_t
514temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
515{
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000516 long temperature;
517 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800518 struct thermal_hwmon_attr *hwmon_attr
519 = container_of(attr, struct thermal_hwmon_attr, attr);
520 struct thermal_zone_device *tz
521 = container_of(hwmon_attr, struct thermal_zone_device,
522 temp_input);
523
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000524 ret = tz->ops->get_temp(tz, &temperature);
525
526 if (ret)
527 return ret;
528
529 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800530}
531
532static ssize_t
533temp_crit_show(struct device *dev, struct device_attribute *attr,
534 char *buf)
535{
536 struct thermal_hwmon_attr *hwmon_attr
537 = container_of(attr, struct thermal_hwmon_attr, attr);
538 struct thermal_zone_device *tz
539 = container_of(hwmon_attr, struct thermal_zone_device,
540 temp_crit);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000541 long temperature;
542 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800543
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000544 ret = tz->ops->get_trip_temp(tz, 0, &temperature);
545 if (ret)
546 return ret;
547
548 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800549}
550
551
552static int
553thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
554{
555 struct thermal_hwmon_device *hwmon;
556 int new_hwmon_device = 1;
557 int result;
558
559 mutex_lock(&thermal_list_lock);
560 list_for_each_entry(hwmon, &thermal_hwmon_list, node)
561 if (!strcmp(hwmon->type, tz->type)) {
562 new_hwmon_device = 0;
563 mutex_unlock(&thermal_list_lock);
564 goto register_sys_interface;
565 }
566 mutex_unlock(&thermal_list_lock);
567
568 hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
569 if (!hwmon)
570 return -ENOMEM;
571
572 INIT_LIST_HEAD(&hwmon->tz_list);
573 strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
574 hwmon->device = hwmon_device_register(NULL);
575 if (IS_ERR(hwmon->device)) {
576 result = PTR_ERR(hwmon->device);
577 goto free_mem;
578 }
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700579 dev_set_drvdata(hwmon->device, hwmon);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800580 result = device_create_file(hwmon->device, &dev_attr_name);
581 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530582 goto free_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800583
584 register_sys_interface:
585 tz->hwmon = hwmon;
586 hwmon->count++;
587
588 snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH,
589 "temp%d_input", hwmon->count);
590 tz->temp_input.attr.attr.name = tz->temp_input.name;
591 tz->temp_input.attr.attr.mode = 0444;
592 tz->temp_input.attr.show = temp_input_show;
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -0700593 sysfs_attr_init(&tz->temp_input.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800594 result = device_create_file(hwmon->device, &tz->temp_input.attr);
595 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530596 goto unregister_name;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800597
598 if (tz->ops->get_crit_temp) {
599 unsigned long temperature;
600 if (!tz->ops->get_crit_temp(tz, &temperature)) {
601 snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH,
602 "temp%d_crit", hwmon->count);
603 tz->temp_crit.attr.attr.name = tz->temp_crit.name;
604 tz->temp_crit.attr.attr.mode = 0444;
605 tz->temp_crit.attr.show = temp_crit_show;
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -0700606 sysfs_attr_init(&tz->temp_crit.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800607 result = device_create_file(hwmon->device,
608 &tz->temp_crit.attr);
609 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530610 goto unregister_input;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800611 }
612 }
613
614 mutex_lock(&thermal_list_lock);
615 if (new_hwmon_device)
616 list_add_tail(&hwmon->node, &thermal_hwmon_list);
617 list_add_tail(&tz->hwmon_node, &hwmon->tz_list);
618 mutex_unlock(&thermal_list_lock);
619
620 return 0;
621
Durgadoss Rb299eb52011-03-03 04:30:13 +0530622 unregister_input:
Zhang Ruie68b16a2008-04-21 16:07:52 +0800623 device_remove_file(hwmon->device, &tz->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +0530624 unregister_name:
Zhang Ruie68b16a2008-04-21 16:07:52 +0800625 if (new_hwmon_device) {
626 device_remove_file(hwmon->device, &dev_attr_name);
627 hwmon_device_unregister(hwmon->device);
628 }
629 free_mem:
630 if (new_hwmon_device)
631 kfree(hwmon);
632
633 return result;
634}
635
636static void
637thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
638{
639 struct thermal_hwmon_device *hwmon = tz->hwmon;
640
641 tz->hwmon = NULL;
642 device_remove_file(hwmon->device, &tz->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +0530643 if (tz->ops->get_crit_temp)
644 device_remove_file(hwmon->device, &tz->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800645
646 mutex_lock(&thermal_list_lock);
647 list_del(&tz->hwmon_node);
648 if (!list_empty(&hwmon->tz_list)) {
649 mutex_unlock(&thermal_list_lock);
650 return;
651 }
652 list_del(&hwmon->node);
653 mutex_unlock(&thermal_list_lock);
654
655 device_remove_file(hwmon->device, &dev_attr_name);
656 hwmon_device_unregister(hwmon->device);
657 kfree(hwmon);
658}
659#else
660static int
661thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
662{
663 return 0;
664}
665
666static void
667thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
668{
669}
670#endif
671
Matthew Garrettb1569e92008-12-03 17:55:32 +0000672static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
673 int delay)
674{
675 cancel_delayed_work(&(tz->poll_queue));
676
677 if (!delay)
678 return;
679
680 if (delay > 1000)
681 schedule_delayed_work(&(tz->poll_queue),
682 round_jiffies(msecs_to_jiffies(delay)));
683 else
684 schedule_delayed_work(&(tz->poll_queue),
685 msecs_to_jiffies(delay));
686}
687
688static void thermal_zone_device_passive(struct thermal_zone_device *tz,
689 int temp, int trip_temp, int trip)
690{
691 int trend = 0;
692 struct thermal_cooling_device_instance *instance;
693 struct thermal_cooling_device *cdev;
694 long state, max_state;
695
696 /*
697 * Above Trip?
698 * -----------
699 * Calculate the thermal trend (using the passive cooling equation)
700 * and modify the performance limit for all passive cooling devices
701 * accordingly. Note that we assume symmetry.
702 */
703 if (temp >= trip_temp) {
704 tz->passive = true;
705
706 trend = (tz->tc1 * (temp - tz->last_temperature)) +
707 (tz->tc2 * (temp - trip_temp));
708
709 /* Heating up? */
710 if (trend > 0) {
711 list_for_each_entry(instance, &tz->cooling_devices,
712 node) {
713 if (instance->trip != trip)
714 continue;
715 cdev = instance->cdev;
716 cdev->ops->get_cur_state(cdev, &state);
717 cdev->ops->get_max_state(cdev, &max_state);
718 if (state++ < max_state)
719 cdev->ops->set_cur_state(cdev, state);
720 }
721 } else if (trend < 0) { /* Cooling off? */
722 list_for_each_entry(instance, &tz->cooling_devices,
723 node) {
724 if (instance->trip != trip)
725 continue;
726 cdev = instance->cdev;
727 cdev->ops->get_cur_state(cdev, &state);
728 cdev->ops->get_max_state(cdev, &max_state);
729 if (state > 0)
730 cdev->ops->set_cur_state(cdev, --state);
731 }
732 }
733 return;
734 }
735
736 /*
737 * Below Trip?
738 * -----------
739 * Implement passive cooling hysteresis to slowly increase performance
740 * and avoid thrashing around the passive trip point. Note that we
741 * assume symmetry.
742 */
743 list_for_each_entry(instance, &tz->cooling_devices, node) {
744 if (instance->trip != trip)
745 continue;
746 cdev = instance->cdev;
747 cdev->ops->get_cur_state(cdev, &state);
748 cdev->ops->get_max_state(cdev, &max_state);
749 if (state > 0)
750 cdev->ops->set_cur_state(cdev, --state);
751 if (state == 0)
752 tz->passive = false;
753 }
754}
755
756static void thermal_zone_device_check(struct work_struct *work)
757{
758 struct thermal_zone_device *tz = container_of(work, struct
759 thermal_zone_device,
760 poll_queue.work);
761 thermal_zone_device_update(tz);
762}
Zhang Ruie68b16a2008-04-21 16:07:52 +0800763
Zhang Rui203d3d42008-01-17 15:51:08 +0800764/**
765 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +0800766 * @tz: thermal zone device
767 * @trip: indicates which trip point the cooling devices is
768 * associated with in this thermal zone.
769 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -0500770 *
771 * This function is usually called in the thermal zone device .bind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +0800772 */
773int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
774 int trip,
775 struct thermal_cooling_device *cdev)
776{
777 struct thermal_cooling_device_instance *dev;
778 struct thermal_cooling_device_instance *pos;
Thomas Sujithc7516702008-02-15 00:58:50 -0500779 struct thermal_zone_device *pos1;
780 struct thermal_cooling_device *pos2;
Zhang Rui203d3d42008-01-17 15:51:08 +0800781 int result;
782
Len Brown543a9562008-02-07 16:55:08 -0500783 if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
Zhang Rui203d3d42008-01-17 15:51:08 +0800784 return -EINVAL;
785
Thomas Sujithc7516702008-02-15 00:58:50 -0500786 list_for_each_entry(pos1, &thermal_tz_list, node) {
787 if (pos1 == tz)
788 break;
789 }
790 list_for_each_entry(pos2, &thermal_cdev_list, node) {
791 if (pos2 == cdev)
792 break;
793 }
794
795 if (tz != pos1 || cdev != pos2)
Zhang Rui203d3d42008-01-17 15:51:08 +0800796 return -EINVAL;
797
798 dev =
799 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
800 if (!dev)
801 return -ENOMEM;
802 dev->tz = tz;
803 dev->cdev = cdev;
804 dev->trip = trip;
805 result = get_idr(&tz->idr, &tz->lock, &dev->id);
806 if (result)
807 goto free_mem;
808
809 sprintf(dev->name, "cdev%d", dev->id);
810 result =
811 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
812 if (result)
813 goto release_idr;
814
815 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -0700816 sysfs_attr_init(&dev->attr.attr);
Zhang Rui203d3d42008-01-17 15:51:08 +0800817 dev->attr.attr.name = dev->attr_name;
818 dev->attr.attr.mode = 0444;
819 dev->attr.show = thermal_cooling_device_trip_point_show;
820 result = device_create_file(&tz->device, &dev->attr);
821 if (result)
822 goto remove_symbol_link;
823
824 mutex_lock(&tz->lock);
825 list_for_each_entry(pos, &tz->cooling_devices, node)
826 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
827 result = -EEXIST;
828 break;
829 }
830 if (!result)
831 list_add_tail(&dev->node, &tz->cooling_devices);
832 mutex_unlock(&tz->lock);
833
834 if (!result)
835 return 0;
836
837 device_remove_file(&tz->device, &dev->attr);
838 remove_symbol_link:
839 sysfs_remove_link(&tz->device.kobj, dev->name);
840 release_idr:
841 release_idr(&tz->idr, &tz->lock, dev->id);
842 free_mem:
843 kfree(dev);
844 return result;
845}
Len Brown543a9562008-02-07 16:55:08 -0500846
Zhang Rui203d3d42008-01-17 15:51:08 +0800847EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
848
849/**
850 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +0800851 * @tz: thermal zone device
852 * @trip: indicates which trip point the cooling devices is
853 * associated with in this thermal zone.
854 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -0500855 *
856 * This function is usually called in the thermal zone device .unbind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +0800857 */
858int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
859 int trip,
860 struct thermal_cooling_device *cdev)
861{
862 struct thermal_cooling_device_instance *pos, *next;
863
864 mutex_lock(&tz->lock);
865 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
Len Brown543a9562008-02-07 16:55:08 -0500866 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
Zhang Rui203d3d42008-01-17 15:51:08 +0800867 list_del(&pos->node);
868 mutex_unlock(&tz->lock);
869 goto unbind;
870 }
871 }
872 mutex_unlock(&tz->lock);
873
874 return -ENODEV;
875
876 unbind:
877 device_remove_file(&tz->device, &pos->attr);
878 sysfs_remove_link(&tz->device.kobj, pos->name);
879 release_idr(&tz->idr, &tz->lock, pos->id);
880 kfree(pos);
881 return 0;
882}
Len Brown543a9562008-02-07 16:55:08 -0500883
Zhang Rui203d3d42008-01-17 15:51:08 +0800884EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
885
886static void thermal_release(struct device *dev)
887{
888 struct thermal_zone_device *tz;
889 struct thermal_cooling_device *cdev;
890
Kay Sievers354655e2009-01-06 10:44:37 -0800891 if (!strncmp(dev_name(dev), "thermal_zone", sizeof "thermal_zone" - 1)) {
Zhang Rui203d3d42008-01-17 15:51:08 +0800892 tz = to_thermal_zone(dev);
893 kfree(tz);
894 } else {
895 cdev = to_cooling_device(dev);
896 kfree(cdev);
897 }
898}
899
900static struct class thermal_class = {
901 .name = "thermal",
902 .dev_release = thermal_release,
903};
904
905/**
906 * thermal_cooling_device_register - register a new thermal cooling device
907 * @type: the thermal cooling device type.
908 * @devdata: device private data.
909 * @ops: standard thermal cooling devices callbacks.
910 */
Alan Cox5b275ce2010-11-11 15:27:29 +0000911struct thermal_cooling_device *thermal_cooling_device_register(
912 char *type, void *devdata, const struct thermal_cooling_device_ops *ops)
Zhang Rui203d3d42008-01-17 15:51:08 +0800913{
914 struct thermal_cooling_device *cdev;
915 struct thermal_zone_device *pos;
916 int result;
917
918 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500919 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800920
921 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
Len Brown543a9562008-02-07 16:55:08 -0500922 !ops->set_cur_state)
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500923 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800924
925 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
926 if (!cdev)
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500927 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +0800928
929 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
930 if (result) {
931 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500932 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +0800933 }
934
935 strcpy(cdev->type, type);
936 cdev->ops = ops;
937 cdev->device.class = &thermal_class;
938 cdev->devdata = devdata;
Kay Sievers354655e2009-01-06 10:44:37 -0800939 dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
Zhang Rui203d3d42008-01-17 15:51:08 +0800940 result = device_register(&cdev->device);
941 if (result) {
942 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
943 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500944 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +0800945 }
946
947 /* sys I/F */
948 if (type) {
Len Brown543a9562008-02-07 16:55:08 -0500949 result = device_create_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +0800950 if (result)
951 goto unregister;
952 }
953
954 result = device_create_file(&cdev->device, &dev_attr_max_state);
955 if (result)
956 goto unregister;
957
958 result = device_create_file(&cdev->device, &dev_attr_cur_state);
959 if (result)
960 goto unregister;
961
962 mutex_lock(&thermal_list_lock);
963 list_add(&cdev->node, &thermal_cdev_list);
964 list_for_each_entry(pos, &thermal_tz_list, node) {
965 if (!pos->ops->bind)
966 continue;
967 result = pos->ops->bind(pos, cdev);
968 if (result)
969 break;
970
971 }
972 mutex_unlock(&thermal_list_lock);
973
974 if (!result)
975 return cdev;
976
977 unregister:
978 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
979 device_unregister(&cdev->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500980 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +0800981}
Len Brown543a9562008-02-07 16:55:08 -0500982
Zhang Rui203d3d42008-01-17 15:51:08 +0800983EXPORT_SYMBOL(thermal_cooling_device_register);
984
985/**
986 * thermal_cooling_device_unregister - removes the registered thermal cooling device
Zhang Rui203d3d42008-01-17 15:51:08 +0800987 * @cdev: the thermal cooling device to remove.
988 *
989 * thermal_cooling_device_unregister() must be called when the device is no
990 * longer needed.
991 */
992void thermal_cooling_device_unregister(struct
993 thermal_cooling_device
994 *cdev)
995{
996 struct thermal_zone_device *tz;
997 struct thermal_cooling_device *pos = NULL;
998
999 if (!cdev)
1000 return;
1001
1002 mutex_lock(&thermal_list_lock);
1003 list_for_each_entry(pos, &thermal_cdev_list, node)
1004 if (pos == cdev)
1005 break;
1006 if (pos != cdev) {
1007 /* thermal cooling device not found */
1008 mutex_unlock(&thermal_list_lock);
1009 return;
1010 }
1011 list_del(&cdev->node);
1012 list_for_each_entry(tz, &thermal_tz_list, node) {
1013 if (!tz->ops->unbind)
1014 continue;
1015 tz->ops->unbind(tz, cdev);
1016 }
1017 mutex_unlock(&thermal_list_lock);
1018 if (cdev->type[0])
Len Brown543a9562008-02-07 16:55:08 -05001019 device_remove_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001020 device_remove_file(&cdev->device, &dev_attr_max_state);
1021 device_remove_file(&cdev->device, &dev_attr_cur_state);
1022
1023 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1024 device_unregister(&cdev->device);
1025 return;
1026}
Len Brown543a9562008-02-07 16:55:08 -05001027
Zhang Rui203d3d42008-01-17 15:51:08 +08001028EXPORT_SYMBOL(thermal_cooling_device_unregister);
1029
1030/**
Matthew Garrettb1569e92008-12-03 17:55:32 +00001031 * thermal_zone_device_update - force an update of a thermal zone's state
1032 * @ttz: the thermal zone to update
1033 */
1034
1035void thermal_zone_device_update(struct thermal_zone_device *tz)
1036{
1037 int count, ret = 0;
1038 long temp, trip_temp;
1039 enum thermal_trip_type trip_type;
1040 struct thermal_cooling_device_instance *instance;
1041 struct thermal_cooling_device *cdev;
1042
1043 mutex_lock(&tz->lock);
1044
Michael Brunner0d288162009-08-26 14:29:25 -07001045 if (tz->ops->get_temp(tz, &temp)) {
1046 /* get_temp failed - retry it later */
1047 printk(KERN_WARNING PREFIX "failed to read out thermal zone "
1048 "%d\n", tz->id);
1049 goto leave;
1050 }
Matthew Garrettb1569e92008-12-03 17:55:32 +00001051
1052 for (count = 0; count < tz->trips; count++) {
1053 tz->ops->get_trip_type(tz, count, &trip_type);
1054 tz->ops->get_trip_temp(tz, count, &trip_temp);
1055
1056 switch (trip_type) {
1057 case THERMAL_TRIP_CRITICAL:
Vladimir Zajac29321352009-05-06 19:34:21 +02001058 if (temp >= trip_temp) {
Matthew Garrettb1569e92008-12-03 17:55:32 +00001059 if (tz->ops->notify)
1060 ret = tz->ops->notify(tz, count,
1061 trip_type);
1062 if (!ret) {
1063 printk(KERN_EMERG
1064 "Critical temperature reached (%ld C), shutting down.\n",
1065 temp/1000);
1066 orderly_poweroff(true);
1067 }
1068 }
1069 break;
1070 case THERMAL_TRIP_HOT:
Vladimir Zajac29321352009-05-06 19:34:21 +02001071 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001072 if (tz->ops->notify)
1073 tz->ops->notify(tz, count, trip_type);
1074 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001075 case THERMAL_TRIP_CONFIGURABLE_HI:
1076 if (temp >= trip_temp)
1077 if (tz->ops->notify)
1078 tz->ops->notify(tz, count, trip_type);
1079 break;
1080 case THERMAL_TRIP_CONFIGURABLE_LOW:
1081 if (temp <= trip_temp)
1082 if (tz->ops->notify)
1083 tz->ops->notify(tz, count, trip_type);
1084 break;
1085 case THERMAL_TRIP_CRITICAL_LOW:
1086 if (temp <= trip_temp) {
1087 if (tz->ops->notify)
1088 ret = tz->ops->notify(tz, count,
1089 trip_type);
1090 if (!ret) {
1091 printk(KERN_EMERG
1092 "Critical temperature reached (%ld C), \
1093 shutting down.\n", temp/1000);
1094 orderly_poweroff(true);
1095 }
1096 }
1097 break;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001098 case THERMAL_TRIP_ACTIVE:
1099 list_for_each_entry(instance, &tz->cooling_devices,
1100 node) {
1101 if (instance->trip != count)
1102 continue;
1103
1104 cdev = instance->cdev;
1105
Vladimir Zajac29321352009-05-06 19:34:21 +02001106 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001107 cdev->ops->set_cur_state(cdev, 1);
1108 else
1109 cdev->ops->set_cur_state(cdev, 0);
1110 }
1111 break;
1112 case THERMAL_TRIP_PASSIVE:
Vladimir Zajac29321352009-05-06 19:34:21 +02001113 if (temp >= trip_temp || tz->passive)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001114 thermal_zone_device_passive(tz, temp,
1115 trip_temp, count);
1116 break;
1117 }
1118 }
Matthew Garrett03a971a2008-12-03 18:00:38 +00001119
1120 if (tz->forced_passive)
1121 thermal_zone_device_passive(tz, temp, tz->forced_passive,
1122 THERMAL_TRIPS_NONE);
1123
Matthew Garrettb1569e92008-12-03 17:55:32 +00001124 tz->last_temperature = temp;
Michael Brunner0d288162009-08-26 14:29:25 -07001125
1126 leave:
Matthew Garrettb1569e92008-12-03 17:55:32 +00001127 if (tz->passive)
1128 thermal_zone_device_set_polling(tz, tz->passive_delay);
1129 else if (tz->polling_delay)
1130 thermal_zone_device_set_polling(tz, tz->polling_delay);
Frans Pop3767cb52009-10-26 08:39:04 +01001131 else
1132 thermal_zone_device_set_polling(tz, 0);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001133 mutex_unlock(&tz->lock);
1134}
1135EXPORT_SYMBOL(thermal_zone_device_update);
1136
1137/**
Zhang Rui203d3d42008-01-17 15:51:08 +08001138 * thermal_zone_device_register - register a new thermal zone device
1139 * @type: the thermal zone device type
1140 * @trips: the number of trip points the thermal zone support
1141 * @devdata: private device data
1142 * @ops: standard thermal zone device callbacks
Matthew Garrettb1569e92008-12-03 17:55:32 +00001143 * @tc1: thermal coefficient 1 for passive calculations
1144 * @tc2: thermal coefficient 2 for passive calculations
1145 * @passive_delay: number of milliseconds to wait between polls when
1146 * performing passive cooling
1147 * @polling_delay: number of milliseconds to wait between polls when checking
1148 * whether trip points have been crossed (0 for interrupt
1149 * driven systems)
Zhang Rui203d3d42008-01-17 15:51:08 +08001150 *
1151 * thermal_zone_device_unregister() must be called when the device is no
Matthew Garrettb1569e92008-12-03 17:55:32 +00001152 * longer needed. The passive cooling formula uses tc1 and tc2 as described in
1153 * section 11.1.5.1 of the ACPI specification 3.0.
Zhang Rui203d3d42008-01-17 15:51:08 +08001154 */
1155struct thermal_zone_device *thermal_zone_device_register(char *type,
Alan Cox5b275ce2010-11-11 15:27:29 +00001156 int trips, void *devdata,
1157 const struct thermal_zone_device_ops *ops,
1158 int tc1, int tc2, int passive_delay, int polling_delay)
Zhang Rui203d3d42008-01-17 15:51:08 +08001159{
1160 struct thermal_zone_device *tz;
1161 struct thermal_cooling_device *pos;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001162 enum thermal_trip_type trip_type;
Zhang Rui203d3d42008-01-17 15:51:08 +08001163 int result;
1164 int count;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001165 int passive = 0;
Zhang Rui203d3d42008-01-17 15:51:08 +08001166
1167 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001168 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001169
1170 if (trips > THERMAL_MAX_TRIPS || trips < 0)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001171 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001172
1173 if (!ops || !ops->get_temp)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001174 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001175
1176 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
1177 if (!tz)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001178 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001179
1180 INIT_LIST_HEAD(&tz->cooling_devices);
1181 idr_init(&tz->idr);
1182 mutex_init(&tz->lock);
1183 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
1184 if (result) {
1185 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001186 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001187 }
1188
1189 strcpy(tz->type, type);
1190 tz->ops = ops;
1191 tz->device.class = &thermal_class;
1192 tz->devdata = devdata;
1193 tz->trips = trips;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001194 tz->tc1 = tc1;
1195 tz->tc2 = tc2;
1196 tz->passive_delay = passive_delay;
1197 tz->polling_delay = polling_delay;
1198
Kay Sievers354655e2009-01-06 10:44:37 -08001199 dev_set_name(&tz->device, "thermal_zone%d", tz->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001200 result = device_register(&tz->device);
1201 if (result) {
1202 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1203 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001204 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001205 }
1206
1207 /* sys I/F */
1208 if (type) {
1209 result = device_create_file(&tz->device, &dev_attr_type);
1210 if (result)
1211 goto unregister;
1212 }
1213
1214 result = device_create_file(&tz->device, &dev_attr_temp);
1215 if (result)
1216 goto unregister;
1217
1218 if (ops->get_mode) {
1219 result = device_create_file(&tz->device, &dev_attr_mode);
1220 if (result)
1221 goto unregister;
1222 }
1223
1224 for (count = 0; count < trips; count++) {
1225 TRIP_POINT_ATTR_ADD(&tz->device, count, result);
1226 if (result)
1227 goto unregister;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001228 tz->ops->get_trip_type(tz, count, &trip_type);
1229 if (trip_type == THERMAL_TRIP_PASSIVE)
1230 passive = 1;
Zhang Rui203d3d42008-01-17 15:51:08 +08001231 }
1232
Matthew Garrett03a971a2008-12-03 18:00:38 +00001233 if (!passive)
1234 result = device_create_file(&tz->device,
1235 &dev_attr_passive);
1236
1237 if (result)
1238 goto unregister;
1239
Zhang Ruie68b16a2008-04-21 16:07:52 +08001240 result = thermal_add_hwmon_sysfs(tz);
1241 if (result)
1242 goto unregister;
1243
Zhang Rui203d3d42008-01-17 15:51:08 +08001244 mutex_lock(&thermal_list_lock);
1245 list_add_tail(&tz->node, &thermal_tz_list);
1246 if (ops->bind)
1247 list_for_each_entry(pos, &thermal_cdev_list, node) {
Len Brown543a9562008-02-07 16:55:08 -05001248 result = ops->bind(tz, pos);
1249 if (result)
1250 break;
Zhang Rui203d3d42008-01-17 15:51:08 +08001251 }
1252 mutex_unlock(&thermal_list_lock);
1253
Matthew Garrettb1569e92008-12-03 17:55:32 +00001254 INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
1255
1256 thermal_zone_device_update(tz);
1257
Zhang Rui203d3d42008-01-17 15:51:08 +08001258 if (!result)
1259 return tz;
1260
1261 unregister:
1262 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1263 device_unregister(&tz->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001264 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001265}
Len Brown543a9562008-02-07 16:55:08 -05001266
Zhang Rui203d3d42008-01-17 15:51:08 +08001267EXPORT_SYMBOL(thermal_zone_device_register);
1268
1269/**
1270 * thermal_device_unregister - removes the registered thermal zone device
Zhang Rui203d3d42008-01-17 15:51:08 +08001271 * @tz: the thermal zone device to remove
1272 */
1273void thermal_zone_device_unregister(struct thermal_zone_device *tz)
1274{
1275 struct thermal_cooling_device *cdev;
1276 struct thermal_zone_device *pos = NULL;
1277 int count;
1278
1279 if (!tz)
1280 return;
1281
1282 mutex_lock(&thermal_list_lock);
1283 list_for_each_entry(pos, &thermal_tz_list, node)
1284 if (pos == tz)
1285 break;
1286 if (pos != tz) {
1287 /* thermal zone device not found */
1288 mutex_unlock(&thermal_list_lock);
1289 return;
1290 }
1291 list_del(&tz->node);
1292 if (tz->ops->unbind)
1293 list_for_each_entry(cdev, &thermal_cdev_list, node)
1294 tz->ops->unbind(tz, cdev);
1295 mutex_unlock(&thermal_list_lock);
1296
Matthew Garrettb1569e92008-12-03 17:55:32 +00001297 thermal_zone_device_set_polling(tz, 0);
1298
Zhang Rui203d3d42008-01-17 15:51:08 +08001299 if (tz->type[0])
1300 device_remove_file(&tz->device, &dev_attr_type);
1301 device_remove_file(&tz->device, &dev_attr_temp);
1302 if (tz->ops->get_mode)
1303 device_remove_file(&tz->device, &dev_attr_mode);
1304
1305 for (count = 0; count < tz->trips; count++)
1306 TRIP_POINT_ATTR_REMOVE(&tz->device, count);
1307
Zhang Ruie68b16a2008-04-21 16:07:52 +08001308 thermal_remove_hwmon_sysfs(tz);
Zhang Rui203d3d42008-01-17 15:51:08 +08001309 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1310 idr_destroy(&tz->idr);
1311 mutex_destroy(&tz->lock);
1312 device_unregister(&tz->device);
1313 return;
1314}
Len Brown543a9562008-02-07 16:55:08 -05001315
Zhang Rui203d3d42008-01-17 15:51:08 +08001316EXPORT_SYMBOL(thermal_zone_device_unregister);
1317
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001318#ifdef CONFIG_NET
Bryan Huntsmand0c5dd52011-10-04 15:49:21 -07001319static unsigned int thermal_event_seqnum;
1320
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001321static struct genl_family thermal_event_genl_family = {
1322 .id = GENL_ID_GENERATE,
1323 .name = THERMAL_GENL_FAMILY_NAME,
1324 .version = THERMAL_GENL_VERSION,
1325 .maxattr = THERMAL_GENL_ATTR_MAX,
1326};
1327
1328static struct genl_multicast_group thermal_event_mcgrp = {
1329 .name = THERMAL_GENL_MCAST_GROUP_NAME,
1330};
1331
R.Durgadoss4cb18722010-10-27 03:33:29 +05301332int generate_netlink_event(u32 orig, enum events event)
1333{
1334 struct sk_buff *skb;
1335 struct nlattr *attr;
1336 struct thermal_genl_event *thermal_event;
1337 void *msg_header;
1338 int size;
1339 int result;
1340
1341 /* allocate memory */
1342 size = nla_total_size(sizeof(struct thermal_genl_event)) + \
1343 nla_total_size(0);
1344
1345 skb = genlmsg_new(size, GFP_ATOMIC);
1346 if (!skb)
1347 return -ENOMEM;
1348
1349 /* add the genetlink message header */
1350 msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
1351 &thermal_event_genl_family, 0,
1352 THERMAL_GENL_CMD_EVENT);
1353 if (!msg_header) {
1354 nlmsg_free(skb);
1355 return -ENOMEM;
1356 }
1357
1358 /* fill the data */
1359 attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT, \
1360 sizeof(struct thermal_genl_event));
1361
1362 if (!attr) {
1363 nlmsg_free(skb);
1364 return -EINVAL;
1365 }
1366
1367 thermal_event = nla_data(attr);
1368 if (!thermal_event) {
1369 nlmsg_free(skb);
1370 return -EINVAL;
1371 }
1372
1373 memset(thermal_event, 0, sizeof(struct thermal_genl_event));
1374
1375 thermal_event->orig = orig;
1376 thermal_event->event = event;
1377
1378 /* send multicast genetlink message */
1379 result = genlmsg_end(skb, msg_header);
1380 if (result < 0) {
1381 nlmsg_free(skb);
1382 return result;
1383 }
1384
1385 result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
1386 if (result)
1387 printk(KERN_INFO "failed to send netlink event:%d", result);
1388
1389 return result;
1390}
1391EXPORT_SYMBOL(generate_netlink_event);
1392
1393static int genetlink_init(void)
1394{
1395 int result;
1396
1397 result = genl_register_family(&thermal_event_genl_family);
1398 if (result)
1399 return result;
1400
1401 result = genl_register_mc_group(&thermal_event_genl_family,
1402 &thermal_event_mcgrp);
1403 if (result)
1404 genl_unregister_family(&thermal_event_genl_family);
1405 return result;
1406}
1407
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001408static void genetlink_exit(void)
1409{
1410 genl_unregister_family(&thermal_event_genl_family);
1411}
1412#else /* !CONFIG_NET */
1413static inline int genetlink_init(void) { return 0; }
1414static inline void genetlink_exit(void) {}
1415#endif /* !CONFIG_NET */
1416
Zhang Rui203d3d42008-01-17 15:51:08 +08001417static int __init thermal_init(void)
1418{
1419 int result = 0;
1420
1421 result = class_register(&thermal_class);
1422 if (result) {
1423 idr_destroy(&thermal_tz_idr);
1424 idr_destroy(&thermal_cdev_idr);
1425 mutex_destroy(&thermal_idr_lock);
1426 mutex_destroy(&thermal_list_lock);
1427 }
R.Durgadoss4cb18722010-10-27 03:33:29 +05301428 result = genetlink_init();
Zhang Rui203d3d42008-01-17 15:51:08 +08001429 return result;
1430}
1431
1432static void __exit thermal_exit(void)
1433{
1434 class_unregister(&thermal_class);
1435 idr_destroy(&thermal_tz_idr);
1436 idr_destroy(&thermal_cdev_idr);
1437 mutex_destroy(&thermal_idr_lock);
1438 mutex_destroy(&thermal_list_lock);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301439 genetlink_exit();
Zhang Rui203d3d42008-01-17 15:51:08 +08001440}
1441
R.Durgadoss4cb18722010-10-27 03:33:29 +05301442fs_initcall(thermal_init);
Zhang Rui203d3d42008-01-17 15:51:08 +08001443module_exit(thermal_exit);