blob: fd1a2fc664a39445ef9b91ea9de440ce82a10fbe [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
Joe Perchesc5a01dd2012-03-21 12:55:02 -070026#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
27
Zhang Rui203d3d42008-01-17 15:51:08 +080028#include <linux/module.h>
29#include <linux/device.h>
30#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090031#include <linux/slab.h>
Zhang Rui203d3d42008-01-17 15:51:08 +080032#include <linux/kdev_t.h>
33#include <linux/idr.h>
34#include <linux/thermal.h>
35#include <linux/spinlock.h>
Matthew Garrettb1569e92008-12-03 17:55:32 +000036#include <linux/reboot.h>
R.Durgadoss4cb18722010-10-27 03:33:29 +053037#include <net/netlink.h>
38#include <net/genetlink.h>
Zhang Rui203d3d42008-01-17 15:51:08 +080039
Zhang Rui63c4ec92008-04-21 16:07:13 +080040MODULE_AUTHOR("Zhang Rui");
Zhang Rui203d3d42008-01-17 15:51:08 +080041MODULE_DESCRIPTION("Generic thermal management sysfs support");
42MODULE_LICENSE("GPL");
43
Zhang Rui203d3d42008-01-17 15:51:08 +080044struct 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
Joe Perchescaca8b82012-03-21 12:55:02 -070067again:
Zhang Rui203d3d42008-01-17 15:51:08 +080068 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
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530153 if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000154 result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530155 else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000156 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);
Joe Perches886ee542012-03-21 12:55:01 -0700342static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
Zhang Rui203d3d42008-01-17 15:51:08 +0800343
344static struct device_attribute trip_point_attrs[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 __ATTR(trip_point_0_type, 0644, trip_point_type_show,
346 trip_point_type_activate),
347 __ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
348 trip_point_temp_set),
349 __ATTR(trip_point_1_type, 0644, trip_point_type_show,
350 trip_point_type_activate),
351 __ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
352 trip_point_temp_set),
353 __ATTR(trip_point_2_type, 0644, trip_point_type_show,
354 trip_point_type_activate),
355 __ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
356 trip_point_temp_set),
357 __ATTR(trip_point_3_type, 0644, trip_point_type_show,
358 trip_point_type_activate),
359 __ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
360 trip_point_temp_set),
361 __ATTR(trip_point_4_type, 0644, trip_point_type_show,
362 trip_point_type_activate),
363 __ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
364 trip_point_temp_set),
365 __ATTR(trip_point_5_type, 0644, trip_point_type_show,
366 trip_point_type_activate),
367 __ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
368 trip_point_temp_set),
369 __ATTR(trip_point_6_type, 0644, trip_point_type_show,
370 trip_point_type_activate),
371 __ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
372 trip_point_temp_set),
373 __ATTR(trip_point_7_type, 0644, trip_point_type_show,
374 trip_point_type_activate),
375 __ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
376 trip_point_temp_set),
377 __ATTR(trip_point_8_type, 0644, trip_point_type_show,
378 trip_point_type_activate),
379 __ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
380 trip_point_temp_set),
381 __ATTR(trip_point_9_type, 0644, trip_point_type_show,
382 trip_point_type_activate),
383 __ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
384 trip_point_temp_set),
385 __ATTR(trip_point_10_type, 0644, trip_point_type_show,
386 trip_point_type_activate),
387 __ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
388 trip_point_temp_set),
389 __ATTR(trip_point_11_type, 0644, trip_point_type_show,
390 trip_point_type_activate),
391 __ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
392 trip_point_temp_set),
Zhang Rui203d3d42008-01-17 15:51:08 +0800393};
394
Zhang Rui203d3d42008-01-17 15:51:08 +0800395/* sys I/F for cooling device */
396#define to_cooling_device(_dev) \
397 container_of(_dev, struct thermal_cooling_device, device)
398
399static ssize_t
400thermal_cooling_device_type_show(struct device *dev,
401 struct device_attribute *attr, char *buf)
402{
403 struct thermal_cooling_device *cdev = to_cooling_device(dev);
404
405 return sprintf(buf, "%s\n", cdev->type);
406}
407
408static ssize_t
409thermal_cooling_device_max_state_show(struct device *dev,
410 struct device_attribute *attr, char *buf)
411{
412 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000413 unsigned long state;
414 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800415
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000416 ret = cdev->ops->get_max_state(cdev, &state);
417 if (ret)
418 return ret;
419 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800420}
421
422static ssize_t
423thermal_cooling_device_cur_state_show(struct device *dev,
424 struct device_attribute *attr, char *buf)
425{
426 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000427 unsigned long state;
428 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800429
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000430 ret = cdev->ops->get_cur_state(cdev, &state);
431 if (ret)
432 return ret;
433 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800434}
435
436static ssize_t
437thermal_cooling_device_cur_state_store(struct device *dev,
438 struct device_attribute *attr,
439 const char *buf, size_t count)
440{
441 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000442 unsigned long state;
Zhang Rui203d3d42008-01-17 15:51:08 +0800443 int result;
444
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000445 if (!sscanf(buf, "%ld\n", &state))
Zhang Rui203d3d42008-01-17 15:51:08 +0800446 return -EINVAL;
447
Roel Kluinedb94912009-12-15 22:46:50 +0100448 if ((long)state < 0)
Zhang Rui203d3d42008-01-17 15:51:08 +0800449 return -EINVAL;
450
451 result = cdev->ops->set_cur_state(cdev, state);
452 if (result)
453 return result;
454 return count;
455}
456
457static struct device_attribute dev_attr_cdev_type =
Len Brown543a9562008-02-07 16:55:08 -0500458__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800459static DEVICE_ATTR(max_state, 0444,
460 thermal_cooling_device_max_state_show, NULL);
461static DEVICE_ATTR(cur_state, 0644,
462 thermal_cooling_device_cur_state_show,
463 thermal_cooling_device_cur_state_store);
464
465static ssize_t
466thermal_cooling_device_trip_point_show(struct device *dev,
Len Brown543a9562008-02-07 16:55:08 -0500467 struct device_attribute *attr, char *buf)
Zhang Rui203d3d42008-01-17 15:51:08 +0800468{
469 struct thermal_cooling_device_instance *instance;
470
471 instance =
472 container_of(attr, struct thermal_cooling_device_instance, attr);
473
474 if (instance->trip == THERMAL_TRIPS_NONE)
475 return sprintf(buf, "-1\n");
476 else
477 return sprintf(buf, "%d\n", instance->trip);
478}
479
480/* Device management */
481
Rene Herman16d75232008-06-24 19:38:56 +0200482#if defined(CONFIG_THERMAL_HWMON)
483
Zhang Ruie68b16a2008-04-21 16:07:52 +0800484/* hwmon sys I/F */
485#include <linux/hwmon.h>
Jean Delvare31f53962011-07-28 13:48:42 -0700486
487/* thermal zone devices with the same type share one hwmon device */
488struct thermal_hwmon_device {
489 char type[THERMAL_NAME_LENGTH];
490 struct device *device;
491 int count;
492 struct list_head tz_list;
493 struct list_head node;
494};
495
496struct thermal_hwmon_attr {
497 struct device_attribute attr;
498 char name[16];
499};
500
501/* one temperature input for each thermal zone */
502struct thermal_hwmon_temp {
503 struct list_head hwmon_node;
504 struct thermal_zone_device *tz;
505 struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
506 struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
507};
508
Zhang Ruie68b16a2008-04-21 16:07:52 +0800509static LIST_HEAD(thermal_hwmon_list);
510
511static ssize_t
512name_show(struct device *dev, struct device_attribute *attr, char *buf)
513{
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700514 struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800515 return sprintf(buf, "%s\n", hwmon->type);
516}
517static DEVICE_ATTR(name, 0444, name_show, NULL);
518
519static ssize_t
520temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
521{
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000522 long temperature;
523 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800524 struct thermal_hwmon_attr *hwmon_attr
525 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700526 struct thermal_hwmon_temp *temp
527 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800528 temp_input);
Jean Delvare31f53962011-07-28 13:48:42 -0700529 struct thermal_zone_device *tz = temp->tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800530
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000531 ret = tz->ops->get_temp(tz, &temperature);
532
533 if (ret)
534 return ret;
535
536 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800537}
538
539static ssize_t
540temp_crit_show(struct device *dev, struct device_attribute *attr,
541 char *buf)
542{
543 struct thermal_hwmon_attr *hwmon_attr
544 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700545 struct thermal_hwmon_temp *temp
546 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800547 temp_crit);
Jean Delvare31f53962011-07-28 13:48:42 -0700548 struct thermal_zone_device *tz = temp->tz;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000549 long temperature;
550 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800551
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000552 ret = tz->ops->get_trip_temp(tz, 0, &temperature);
553 if (ret)
554 return ret;
555
556 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800557}
558
559
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700560static struct thermal_hwmon_device *
561thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
562{
563 struct thermal_hwmon_device *hwmon;
564
565 mutex_lock(&thermal_list_lock);
566 list_for_each_entry(hwmon, &thermal_hwmon_list, node)
567 if (!strcmp(hwmon->type, tz->type)) {
568 mutex_unlock(&thermal_list_lock);
569 return hwmon;
570 }
571 mutex_unlock(&thermal_list_lock);
572
573 return NULL;
574}
575
Jean Delvare31f53962011-07-28 13:48:42 -0700576/* Find the temperature input matching a given thermal zone */
577static struct thermal_hwmon_temp *
578thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
579 const struct thermal_zone_device *tz)
580{
581 struct thermal_hwmon_temp *temp;
582
583 mutex_lock(&thermal_list_lock);
584 list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
585 if (temp->tz == tz) {
586 mutex_unlock(&thermal_list_lock);
587 return temp;
588 }
589 mutex_unlock(&thermal_list_lock);
590
591 return NULL;
592}
593
Zhang Ruie68b16a2008-04-21 16:07:52 +0800594static int
595thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
596{
597 struct thermal_hwmon_device *hwmon;
Jean Delvare31f53962011-07-28 13:48:42 -0700598 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800599 int new_hwmon_device = 1;
600 int result;
601
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700602 hwmon = thermal_hwmon_lookup_by_type(tz);
603 if (hwmon) {
604 new_hwmon_device = 0;
605 goto register_sys_interface;
606 }
Zhang Ruie68b16a2008-04-21 16:07:52 +0800607
608 hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
609 if (!hwmon)
610 return -ENOMEM;
611
612 INIT_LIST_HEAD(&hwmon->tz_list);
613 strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
614 hwmon->device = hwmon_device_register(NULL);
615 if (IS_ERR(hwmon->device)) {
616 result = PTR_ERR(hwmon->device);
617 goto free_mem;
618 }
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700619 dev_set_drvdata(hwmon->device, hwmon);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800620 result = device_create_file(hwmon->device, &dev_attr_name);
621 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530622 goto free_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800623
624 register_sys_interface:
Jean Delvare31f53962011-07-28 13:48:42 -0700625 temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
626 if (!temp) {
627 result = -ENOMEM;
628 goto unregister_name;
629 }
630
631 temp->tz = tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800632 hwmon->count++;
633
Jean Delvare31f53962011-07-28 13:48:42 -0700634 snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800635 "temp%d_input", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700636 temp->temp_input.attr.attr.name = temp->temp_input.name;
637 temp->temp_input.attr.attr.mode = 0444;
638 temp->temp_input.attr.show = temp_input_show;
639 sysfs_attr_init(&temp->temp_input.attr.attr);
640 result = device_create_file(hwmon->device, &temp->temp_input.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800641 if (result)
Jean Delvare31f53962011-07-28 13:48:42 -0700642 goto free_temp_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800643
644 if (tz->ops->get_crit_temp) {
645 unsigned long temperature;
646 if (!tz->ops->get_crit_temp(tz, &temperature)) {
Jean Delvare31f53962011-07-28 13:48:42 -0700647 snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800648 "temp%d_crit", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700649 temp->temp_crit.attr.attr.name = temp->temp_crit.name;
650 temp->temp_crit.attr.attr.mode = 0444;
651 temp->temp_crit.attr.show = temp_crit_show;
652 sysfs_attr_init(&temp->temp_crit.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800653 result = device_create_file(hwmon->device,
Jean Delvare31f53962011-07-28 13:48:42 -0700654 &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800655 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530656 goto unregister_input;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800657 }
658 }
659
660 mutex_lock(&thermal_list_lock);
661 if (new_hwmon_device)
662 list_add_tail(&hwmon->node, &thermal_hwmon_list);
Jean Delvare31f53962011-07-28 13:48:42 -0700663 list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800664 mutex_unlock(&thermal_list_lock);
665
666 return 0;
667
Durgadoss Rb299eb52011-03-03 04:30:13 +0530668 unregister_input:
Jean Delvare31f53962011-07-28 13:48:42 -0700669 device_remove_file(hwmon->device, &temp->temp_input.attr);
670 free_temp_mem:
671 kfree(temp);
Durgadoss Rb299eb52011-03-03 04:30:13 +0530672 unregister_name:
Zhang Ruie68b16a2008-04-21 16:07:52 +0800673 if (new_hwmon_device) {
674 device_remove_file(hwmon->device, &dev_attr_name);
675 hwmon_device_unregister(hwmon->device);
676 }
677 free_mem:
678 if (new_hwmon_device)
679 kfree(hwmon);
680
681 return result;
682}
683
684static void
685thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
686{
Jean Delvare31f53962011-07-28 13:48:42 -0700687 struct thermal_hwmon_device *hwmon;
688 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800689
Jean Delvare31f53962011-07-28 13:48:42 -0700690 hwmon = thermal_hwmon_lookup_by_type(tz);
691 if (unlikely(!hwmon)) {
692 /* Should never happen... */
693 dev_dbg(&tz->device, "hwmon device lookup failed!\n");
694 return;
695 }
696
697 temp = thermal_hwmon_lookup_temp(hwmon, tz);
698 if (unlikely(!temp)) {
699 /* Should never happen... */
700 dev_dbg(&tz->device, "temperature input lookup failed!\n");
701 return;
702 }
703
704 device_remove_file(hwmon->device, &temp->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +0530705 if (tz->ops->get_crit_temp)
Jean Delvare31f53962011-07-28 13:48:42 -0700706 device_remove_file(hwmon->device, &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800707
708 mutex_lock(&thermal_list_lock);
Jean Delvare31f53962011-07-28 13:48:42 -0700709 list_del(&temp->hwmon_node);
710 kfree(temp);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800711 if (!list_empty(&hwmon->tz_list)) {
712 mutex_unlock(&thermal_list_lock);
713 return;
714 }
715 list_del(&hwmon->node);
716 mutex_unlock(&thermal_list_lock);
717
718 device_remove_file(hwmon->device, &dev_attr_name);
719 hwmon_device_unregister(hwmon->device);
720 kfree(hwmon);
721}
722#else
723static int
724thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
725{
726 return 0;
727}
728
729static void
730thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
731{
732}
733#endif
734
Matthew Garrettb1569e92008-12-03 17:55:32 +0000735static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
736 int delay)
737{
738 cancel_delayed_work(&(tz->poll_queue));
739
740 if (!delay)
741 return;
742
743 if (delay > 1000)
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +0100744 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +0000745 round_jiffies(msecs_to_jiffies(delay)));
746 else
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +0100747 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +0000748 msecs_to_jiffies(delay));
749}
750
751static void thermal_zone_device_passive(struct thermal_zone_device *tz,
752 int temp, int trip_temp, int trip)
753{
754 int trend = 0;
755 struct thermal_cooling_device_instance *instance;
756 struct thermal_cooling_device *cdev;
757 long state, max_state;
758
759 /*
760 * Above Trip?
761 * -----------
762 * Calculate the thermal trend (using the passive cooling equation)
763 * and modify the performance limit for all passive cooling devices
764 * accordingly. Note that we assume symmetry.
765 */
766 if (temp >= trip_temp) {
767 tz->passive = true;
768
769 trend = (tz->tc1 * (temp - tz->last_temperature)) +
770 (tz->tc2 * (temp - trip_temp));
771
772 /* Heating up? */
773 if (trend > 0) {
774 list_for_each_entry(instance, &tz->cooling_devices,
775 node) {
776 if (instance->trip != trip)
777 continue;
778 cdev = instance->cdev;
779 cdev->ops->get_cur_state(cdev, &state);
780 cdev->ops->get_max_state(cdev, &max_state);
781 if (state++ < max_state)
782 cdev->ops->set_cur_state(cdev, state);
783 }
784 } else if (trend < 0) { /* Cooling off? */
785 list_for_each_entry(instance, &tz->cooling_devices,
786 node) {
787 if (instance->trip != trip)
788 continue;
789 cdev = instance->cdev;
790 cdev->ops->get_cur_state(cdev, &state);
791 cdev->ops->get_max_state(cdev, &max_state);
792 if (state > 0)
793 cdev->ops->set_cur_state(cdev, --state);
794 }
795 }
796 return;
797 }
798
799 /*
800 * Below Trip?
801 * -----------
802 * Implement passive cooling hysteresis to slowly increase performance
803 * and avoid thrashing around the passive trip point. Note that we
804 * assume symmetry.
805 */
806 list_for_each_entry(instance, &tz->cooling_devices, node) {
807 if (instance->trip != trip)
808 continue;
809 cdev = instance->cdev;
810 cdev->ops->get_cur_state(cdev, &state);
811 cdev->ops->get_max_state(cdev, &max_state);
812 if (state > 0)
813 cdev->ops->set_cur_state(cdev, --state);
814 if (state == 0)
815 tz->passive = false;
816 }
817}
818
819static void thermal_zone_device_check(struct work_struct *work)
820{
821 struct thermal_zone_device *tz = container_of(work, struct
822 thermal_zone_device,
823 poll_queue.work);
824 thermal_zone_device_update(tz);
825}
Zhang Ruie68b16a2008-04-21 16:07:52 +0800826
Zhang Rui203d3d42008-01-17 15:51:08 +0800827/**
828 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +0800829 * @tz: thermal zone device
830 * @trip: indicates which trip point the cooling devices is
831 * associated with in this thermal zone.
832 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -0500833 *
834 * This function is usually called in the thermal zone device .bind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +0800835 */
836int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
837 int trip,
838 struct thermal_cooling_device *cdev)
839{
840 struct thermal_cooling_device_instance *dev;
841 struct thermal_cooling_device_instance *pos;
Thomas Sujithc7516702008-02-15 00:58:50 -0500842 struct thermal_zone_device *pos1;
843 struct thermal_cooling_device *pos2;
Zhang Rui203d3d42008-01-17 15:51:08 +0800844 int result;
845
Len Brown543a9562008-02-07 16:55:08 -0500846 if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
Zhang Rui203d3d42008-01-17 15:51:08 +0800847 return -EINVAL;
848
Thomas Sujithc7516702008-02-15 00:58:50 -0500849 list_for_each_entry(pos1, &thermal_tz_list, node) {
850 if (pos1 == tz)
851 break;
852 }
853 list_for_each_entry(pos2, &thermal_cdev_list, node) {
854 if (pos2 == cdev)
855 break;
856 }
857
858 if (tz != pos1 || cdev != pos2)
Zhang Rui203d3d42008-01-17 15:51:08 +0800859 return -EINVAL;
860
861 dev =
862 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
863 if (!dev)
864 return -ENOMEM;
865 dev->tz = tz;
866 dev->cdev = cdev;
867 dev->trip = trip;
868 result = get_idr(&tz->idr, &tz->lock, &dev->id);
869 if (result)
870 goto free_mem;
871
872 sprintf(dev->name, "cdev%d", dev->id);
873 result =
874 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
875 if (result)
876 goto release_idr;
877
878 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -0700879 sysfs_attr_init(&dev->attr.attr);
Zhang Rui203d3d42008-01-17 15:51:08 +0800880 dev->attr.attr.name = dev->attr_name;
881 dev->attr.attr.mode = 0444;
882 dev->attr.show = thermal_cooling_device_trip_point_show;
883 result = device_create_file(&tz->device, &dev->attr);
884 if (result)
885 goto remove_symbol_link;
886
887 mutex_lock(&tz->lock);
888 list_for_each_entry(pos, &tz->cooling_devices, node)
889 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
890 result = -EEXIST;
891 break;
892 }
893 if (!result)
894 list_add_tail(&dev->node, &tz->cooling_devices);
895 mutex_unlock(&tz->lock);
896
897 if (!result)
898 return 0;
899
900 device_remove_file(&tz->device, &dev->attr);
Joe Perchescaca8b82012-03-21 12:55:02 -0700901remove_symbol_link:
Zhang Rui203d3d42008-01-17 15:51:08 +0800902 sysfs_remove_link(&tz->device.kobj, dev->name);
Joe Perchescaca8b82012-03-21 12:55:02 -0700903release_idr:
Zhang Rui203d3d42008-01-17 15:51:08 +0800904 release_idr(&tz->idr, &tz->lock, dev->id);
Joe Perchescaca8b82012-03-21 12:55:02 -0700905free_mem:
Zhang Rui203d3d42008-01-17 15:51:08 +0800906 kfree(dev);
907 return result;
908}
909EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
910
911/**
912 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +0800913 * @tz: thermal zone device
914 * @trip: indicates which trip point the cooling devices is
915 * associated with in this thermal zone.
916 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -0500917 *
918 * This function is usually called in the thermal zone device .unbind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +0800919 */
920int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
921 int trip,
922 struct thermal_cooling_device *cdev)
923{
924 struct thermal_cooling_device_instance *pos, *next;
925
926 mutex_lock(&tz->lock);
927 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
Len Brown543a9562008-02-07 16:55:08 -0500928 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
Zhang Rui203d3d42008-01-17 15:51:08 +0800929 list_del(&pos->node);
930 mutex_unlock(&tz->lock);
931 goto unbind;
932 }
933 }
934 mutex_unlock(&tz->lock);
935
936 return -ENODEV;
937
Joe Perchescaca8b82012-03-21 12:55:02 -0700938unbind:
Zhang Rui203d3d42008-01-17 15:51:08 +0800939 device_remove_file(&tz->device, &pos->attr);
940 sysfs_remove_link(&tz->device.kobj, pos->name);
941 release_idr(&tz->idr, &tz->lock, pos->id);
942 kfree(pos);
943 return 0;
944}
945EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
946
947static void thermal_release(struct device *dev)
948{
949 struct thermal_zone_device *tz;
950 struct thermal_cooling_device *cdev;
951
Joe Perchescaca8b82012-03-21 12:55:02 -0700952 if (!strncmp(dev_name(dev), "thermal_zone",
953 sizeof("thermal_zone") - 1)) {
Zhang Rui203d3d42008-01-17 15:51:08 +0800954 tz = to_thermal_zone(dev);
955 kfree(tz);
956 } else {
957 cdev = to_cooling_device(dev);
958 kfree(cdev);
959 }
960}
961
962static struct class thermal_class = {
963 .name = "thermal",
964 .dev_release = thermal_release,
965};
966
967/**
968 * thermal_cooling_device_register - register a new thermal cooling device
969 * @type: the thermal cooling device type.
970 * @devdata: device private data.
971 * @ops: standard thermal cooling devices callbacks.
972 */
Joe Perchescaca8b82012-03-21 12:55:02 -0700973struct thermal_cooling_device *
974thermal_cooling_device_register(char *type, void *devdata,
975 const struct thermal_cooling_device_ops *ops)
Zhang Rui203d3d42008-01-17 15:51:08 +0800976{
977 struct thermal_cooling_device *cdev;
978 struct thermal_zone_device *pos;
979 int result;
980
981 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500982 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800983
984 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
Len Brown543a9562008-02-07 16:55:08 -0500985 !ops->set_cur_state)
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500986 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800987
988 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
989 if (!cdev)
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500990 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +0800991
992 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
993 if (result) {
994 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -0500995 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +0800996 }
997
998 strcpy(cdev->type, type);
999 cdev->ops = ops;
1000 cdev->device.class = &thermal_class;
1001 cdev->devdata = devdata;
Kay Sievers354655e2009-01-06 10:44:37 -08001002 dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001003 result = device_register(&cdev->device);
1004 if (result) {
1005 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1006 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001007 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001008 }
1009
1010 /* sys I/F */
1011 if (type) {
Len Brown543a9562008-02-07 16:55:08 -05001012 result = device_create_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001013 if (result)
1014 goto unregister;
1015 }
1016
1017 result = device_create_file(&cdev->device, &dev_attr_max_state);
1018 if (result)
1019 goto unregister;
1020
1021 result = device_create_file(&cdev->device, &dev_attr_cur_state);
1022 if (result)
1023 goto unregister;
1024
1025 mutex_lock(&thermal_list_lock);
1026 list_add(&cdev->node, &thermal_cdev_list);
1027 list_for_each_entry(pos, &thermal_tz_list, node) {
1028 if (!pos->ops->bind)
1029 continue;
1030 result = pos->ops->bind(pos, cdev);
1031 if (result)
1032 break;
1033
1034 }
1035 mutex_unlock(&thermal_list_lock);
1036
1037 if (!result)
1038 return cdev;
1039
Joe Perchescaca8b82012-03-21 12:55:02 -07001040unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001041 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1042 device_unregister(&cdev->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001043 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001044}
1045EXPORT_SYMBOL(thermal_cooling_device_register);
1046
1047/**
1048 * thermal_cooling_device_unregister - removes the registered thermal cooling device
Zhang Rui203d3d42008-01-17 15:51:08 +08001049 * @cdev: the thermal cooling device to remove.
1050 *
1051 * thermal_cooling_device_unregister() must be called when the device is no
1052 * longer needed.
1053 */
1054void thermal_cooling_device_unregister(struct
1055 thermal_cooling_device
1056 *cdev)
1057{
1058 struct thermal_zone_device *tz;
1059 struct thermal_cooling_device *pos = NULL;
1060
1061 if (!cdev)
1062 return;
1063
1064 mutex_lock(&thermal_list_lock);
1065 list_for_each_entry(pos, &thermal_cdev_list, node)
1066 if (pos == cdev)
1067 break;
1068 if (pos != cdev) {
1069 /* thermal cooling device not found */
1070 mutex_unlock(&thermal_list_lock);
1071 return;
1072 }
1073 list_del(&cdev->node);
1074 list_for_each_entry(tz, &thermal_tz_list, node) {
1075 if (!tz->ops->unbind)
1076 continue;
1077 tz->ops->unbind(tz, cdev);
1078 }
1079 mutex_unlock(&thermal_list_lock);
1080 if (cdev->type[0])
Len Brown543a9562008-02-07 16:55:08 -05001081 device_remove_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001082 device_remove_file(&cdev->device, &dev_attr_max_state);
1083 device_remove_file(&cdev->device, &dev_attr_cur_state);
1084
1085 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1086 device_unregister(&cdev->device);
1087 return;
1088}
1089EXPORT_SYMBOL(thermal_cooling_device_unregister);
1090
1091/**
Matthew Garrettb1569e92008-12-03 17:55:32 +00001092 * thermal_zone_device_update - force an update of a thermal zone's state
1093 * @ttz: the thermal zone to update
1094 */
1095
1096void thermal_zone_device_update(struct thermal_zone_device *tz)
1097{
1098 int count, ret = 0;
1099 long temp, trip_temp;
1100 enum thermal_trip_type trip_type;
1101 struct thermal_cooling_device_instance *instance;
1102 struct thermal_cooling_device *cdev;
1103
1104 mutex_lock(&tz->lock);
1105
Michael Brunner0d288162009-08-26 14:29:25 -07001106 if (tz->ops->get_temp(tz, &temp)) {
1107 /* get_temp failed - retry it later */
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001108 pr_warn("failed to read out thermal zone %d\n", tz->id);
Michael Brunner0d288162009-08-26 14:29:25 -07001109 goto leave;
1110 }
Matthew Garrettb1569e92008-12-03 17:55:32 +00001111
1112 for (count = 0; count < tz->trips; count++) {
1113 tz->ops->get_trip_type(tz, count, &trip_type);
1114 tz->ops->get_trip_temp(tz, count, &trip_temp);
1115
1116 switch (trip_type) {
1117 case THERMAL_TRIP_CRITICAL:
Vladimir Zajac29321352009-05-06 19:34:21 +02001118 if (temp >= trip_temp) {
Matthew Garrettb1569e92008-12-03 17:55:32 +00001119 if (tz->ops->notify)
1120 ret = tz->ops->notify(tz, count,
1121 trip_type);
1122 if (!ret) {
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001123 pr_emerg("Critical temperature reached (%ld C), shutting down\n",
1124 temp/1000);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001125 orderly_poweroff(true);
1126 }
1127 }
1128 break;
1129 case THERMAL_TRIP_HOT:
Vladimir Zajac29321352009-05-06 19:34:21 +02001130 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001131 if (tz->ops->notify)
1132 tz->ops->notify(tz, count, trip_type);
1133 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001134 case THERMAL_TRIP_CONFIGURABLE_HI:
1135 if (temp >= trip_temp)
1136 if (tz->ops->notify)
1137 tz->ops->notify(tz, count, trip_type);
1138 break;
1139 case THERMAL_TRIP_CONFIGURABLE_LOW:
1140 if (temp <= trip_temp)
1141 if (tz->ops->notify)
1142 tz->ops->notify(tz, count, trip_type);
1143 break;
1144 case THERMAL_TRIP_CRITICAL_LOW:
1145 if (temp <= trip_temp) {
1146 if (tz->ops->notify)
1147 ret = tz->ops->notify(tz, count,
1148 trip_type);
1149 if (!ret) {
1150 printk(KERN_EMERG
1151 "Critical temperature reached (%ld C), \
1152 shutting down.\n", temp/1000);
1153 orderly_poweroff(true);
1154 }
1155 }
1156 break;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001157 case THERMAL_TRIP_ACTIVE:
1158 list_for_each_entry(instance, &tz->cooling_devices,
1159 node) {
1160 if (instance->trip != count)
1161 continue;
1162
1163 cdev = instance->cdev;
1164
Vladimir Zajac29321352009-05-06 19:34:21 +02001165 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001166 cdev->ops->set_cur_state(cdev, 1);
1167 else
1168 cdev->ops->set_cur_state(cdev, 0);
1169 }
1170 break;
1171 case THERMAL_TRIP_PASSIVE:
Vladimir Zajac29321352009-05-06 19:34:21 +02001172 if (temp >= trip_temp || tz->passive)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001173 thermal_zone_device_passive(tz, temp,
1174 trip_temp, count);
1175 break;
1176 }
1177 }
Matthew Garrett03a971a2008-12-03 18:00:38 +00001178
1179 if (tz->forced_passive)
1180 thermal_zone_device_passive(tz, temp, tz->forced_passive,
1181 THERMAL_TRIPS_NONE);
1182
Matthew Garrettb1569e92008-12-03 17:55:32 +00001183 tz->last_temperature = temp;
Michael Brunner0d288162009-08-26 14:29:25 -07001184
Joe Perchescaca8b82012-03-21 12:55:02 -07001185leave:
Matthew Garrettb1569e92008-12-03 17:55:32 +00001186 if (tz->passive)
1187 thermal_zone_device_set_polling(tz, tz->passive_delay);
1188 else if (tz->polling_delay)
1189 thermal_zone_device_set_polling(tz, tz->polling_delay);
Frans Pop3767cb52009-10-26 08:39:04 +01001190 else
1191 thermal_zone_device_set_polling(tz, 0);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001192 mutex_unlock(&tz->lock);
1193}
1194EXPORT_SYMBOL(thermal_zone_device_update);
1195
1196/**
Zhang Rui203d3d42008-01-17 15:51:08 +08001197 * thermal_zone_device_register - register a new thermal zone device
1198 * @type: the thermal zone device type
1199 * @trips: the number of trip points the thermal zone support
1200 * @devdata: private device data
1201 * @ops: standard thermal zone device callbacks
Matthew Garrettb1569e92008-12-03 17:55:32 +00001202 * @tc1: thermal coefficient 1 for passive calculations
1203 * @tc2: thermal coefficient 2 for passive calculations
1204 * @passive_delay: number of milliseconds to wait between polls when
1205 * performing passive cooling
1206 * @polling_delay: number of milliseconds to wait between polls when checking
1207 * whether trip points have been crossed (0 for interrupt
1208 * driven systems)
Zhang Rui203d3d42008-01-17 15:51:08 +08001209 *
1210 * thermal_zone_device_unregister() must be called when the device is no
Matthew Garrettb1569e92008-12-03 17:55:32 +00001211 * longer needed. The passive cooling formula uses tc1 and tc2 as described in
1212 * section 11.1.5.1 of the ACPI specification 3.0.
Zhang Rui203d3d42008-01-17 15:51:08 +08001213 */
1214struct thermal_zone_device *thermal_zone_device_register(char *type,
Alan Cox5b275ce2010-11-11 15:27:29 +00001215 int trips, void *devdata,
1216 const struct thermal_zone_device_ops *ops,
1217 int tc1, int tc2, int passive_delay, int polling_delay)
Zhang Rui203d3d42008-01-17 15:51:08 +08001218{
1219 struct thermal_zone_device *tz;
1220 struct thermal_cooling_device *pos;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001221 enum thermal_trip_type trip_type;
Zhang Rui203d3d42008-01-17 15:51:08 +08001222 int result;
1223 int count;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001224 int passive = 0;
Zhang Rui203d3d42008-01-17 15:51:08 +08001225
1226 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001227 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001228
1229 if (trips > THERMAL_MAX_TRIPS || trips < 0)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001230 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001231
1232 if (!ops || !ops->get_temp)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001233 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001234
1235 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
1236 if (!tz)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001237 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001238
1239 INIT_LIST_HEAD(&tz->cooling_devices);
1240 idr_init(&tz->idr);
1241 mutex_init(&tz->lock);
1242 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
1243 if (result) {
1244 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001245 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001246 }
1247
1248 strcpy(tz->type, type);
1249 tz->ops = ops;
1250 tz->device.class = &thermal_class;
1251 tz->devdata = devdata;
1252 tz->trips = trips;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001253 tz->tc1 = tc1;
1254 tz->tc2 = tc2;
1255 tz->passive_delay = passive_delay;
1256 tz->polling_delay = polling_delay;
1257
Kay Sievers354655e2009-01-06 10:44:37 -08001258 dev_set_name(&tz->device, "thermal_zone%d", tz->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001259 result = device_register(&tz->device);
1260 if (result) {
1261 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1262 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001263 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001264 }
1265
1266 /* sys I/F */
1267 if (type) {
1268 result = device_create_file(&tz->device, &dev_attr_type);
1269 if (result)
1270 goto unregister;
1271 }
1272
1273 result = device_create_file(&tz->device, &dev_attr_temp);
1274 if (result)
1275 goto unregister;
1276
1277 if (ops->get_mode) {
1278 result = device_create_file(&tz->device, &dev_attr_mode);
1279 if (result)
1280 goto unregister;
1281 }
1282
1283 for (count = 0; count < trips; count++) {
Joe Perchesec797682012-03-21 12:55:02 -07001284 result = device_create_file(&tz->device,
1285 &trip_point_attrs[count * 2]);
1286 if (result)
1287 break;
1288 result = device_create_file(&tz->device,
1289 &trip_point_attrs[count * 2 + 1]);
Zhang Rui203d3d42008-01-17 15:51:08 +08001290 if (result)
1291 goto unregister;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001292 tz->ops->get_trip_type(tz, count, &trip_type);
1293 if (trip_type == THERMAL_TRIP_PASSIVE)
1294 passive = 1;
Zhang Rui203d3d42008-01-17 15:51:08 +08001295 }
1296
Matthew Garrett03a971a2008-12-03 18:00:38 +00001297 if (!passive)
1298 result = device_create_file(&tz->device,
1299 &dev_attr_passive);
1300
1301 if (result)
1302 goto unregister;
1303
Zhang Ruie68b16a2008-04-21 16:07:52 +08001304 result = thermal_add_hwmon_sysfs(tz);
1305 if (result)
1306 goto unregister;
1307
Zhang Rui203d3d42008-01-17 15:51:08 +08001308 mutex_lock(&thermal_list_lock);
1309 list_add_tail(&tz->node, &thermal_tz_list);
1310 if (ops->bind)
1311 list_for_each_entry(pos, &thermal_cdev_list, node) {
Len Brown543a9562008-02-07 16:55:08 -05001312 result = ops->bind(tz, pos);
1313 if (result)
1314 break;
Zhang Rui203d3d42008-01-17 15:51:08 +08001315 }
1316 mutex_unlock(&thermal_list_lock);
1317
Matthew Garrettb1569e92008-12-03 17:55:32 +00001318 INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
1319
1320 thermal_zone_device_update(tz);
1321
Zhang Rui203d3d42008-01-17 15:51:08 +08001322 if (!result)
1323 return tz;
1324
Joe Perchescaca8b82012-03-21 12:55:02 -07001325unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001326 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1327 device_unregister(&tz->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001328 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001329}
1330EXPORT_SYMBOL(thermal_zone_device_register);
1331
1332/**
1333 * thermal_device_unregister - removes the registered thermal zone device
Zhang Rui203d3d42008-01-17 15:51:08 +08001334 * @tz: the thermal zone device to remove
1335 */
1336void thermal_zone_device_unregister(struct thermal_zone_device *tz)
1337{
1338 struct thermal_cooling_device *cdev;
1339 struct thermal_zone_device *pos = NULL;
1340 int count;
1341
1342 if (!tz)
1343 return;
1344
1345 mutex_lock(&thermal_list_lock);
1346 list_for_each_entry(pos, &thermal_tz_list, node)
1347 if (pos == tz)
1348 break;
1349 if (pos != tz) {
1350 /* thermal zone device not found */
1351 mutex_unlock(&thermal_list_lock);
1352 return;
1353 }
1354 list_del(&tz->node);
1355 if (tz->ops->unbind)
1356 list_for_each_entry(cdev, &thermal_cdev_list, node)
1357 tz->ops->unbind(tz, cdev);
1358 mutex_unlock(&thermal_list_lock);
1359
Matthew Garrettb1569e92008-12-03 17:55:32 +00001360 thermal_zone_device_set_polling(tz, 0);
1361
Zhang Rui203d3d42008-01-17 15:51:08 +08001362 if (tz->type[0])
1363 device_remove_file(&tz->device, &dev_attr_type);
1364 device_remove_file(&tz->device, &dev_attr_temp);
1365 if (tz->ops->get_mode)
1366 device_remove_file(&tz->device, &dev_attr_mode);
1367
Joe Perchesec797682012-03-21 12:55:02 -07001368 for (count = 0; count < tz->trips; count++) {
1369 device_remove_file(&tz->device,
1370 &trip_point_attrs[count * 2]);
1371 device_remove_file(&tz->device,
1372 &trip_point_attrs[count * 2 + 1]);
1373 }
Zhang Ruie68b16a2008-04-21 16:07:52 +08001374 thermal_remove_hwmon_sysfs(tz);
Zhang Rui203d3d42008-01-17 15:51:08 +08001375 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1376 idr_destroy(&tz->idr);
1377 mutex_destroy(&tz->lock);
1378 device_unregister(&tz->device);
1379 return;
1380}
Zhang Rui203d3d42008-01-17 15:51:08 +08001381EXPORT_SYMBOL(thermal_zone_device_unregister);
1382
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001383#ifdef CONFIG_NET
1384static struct genl_family thermal_event_genl_family = {
1385 .id = GENL_ID_GENERATE,
1386 .name = THERMAL_GENL_FAMILY_NAME,
1387 .version = THERMAL_GENL_VERSION,
1388 .maxattr = THERMAL_GENL_ATTR_MAX,
1389};
1390
1391static struct genl_multicast_group thermal_event_mcgrp = {
1392 .name = THERMAL_GENL_MCAST_GROUP_NAME,
1393};
1394
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001395int thermal_generate_netlink_event(u32 orig, enum events event)
R.Durgadoss4cb18722010-10-27 03:33:29 +05301396{
1397 struct sk_buff *skb;
1398 struct nlattr *attr;
1399 struct thermal_genl_event *thermal_event;
1400 void *msg_header;
1401 int size;
1402 int result;
Fabio Estevamb11de072012-03-21 12:55:00 -07001403 static unsigned int thermal_event_seqnum;
R.Durgadoss4cb18722010-10-27 03:33:29 +05301404
1405 /* allocate memory */
Joe Perches886ee542012-03-21 12:55:01 -07001406 size = nla_total_size(sizeof(struct thermal_genl_event)) +
1407 nla_total_size(0);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301408
1409 skb = genlmsg_new(size, GFP_ATOMIC);
1410 if (!skb)
1411 return -ENOMEM;
1412
1413 /* add the genetlink message header */
1414 msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
1415 &thermal_event_genl_family, 0,
1416 THERMAL_GENL_CMD_EVENT);
1417 if (!msg_header) {
1418 nlmsg_free(skb);
1419 return -ENOMEM;
1420 }
1421
1422 /* fill the data */
Joe Perches886ee542012-03-21 12:55:01 -07001423 attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
1424 sizeof(struct thermal_genl_event));
R.Durgadoss4cb18722010-10-27 03:33:29 +05301425
1426 if (!attr) {
1427 nlmsg_free(skb);
1428 return -EINVAL;
1429 }
1430
1431 thermal_event = nla_data(attr);
1432 if (!thermal_event) {
1433 nlmsg_free(skb);
1434 return -EINVAL;
1435 }
1436
1437 memset(thermal_event, 0, sizeof(struct thermal_genl_event));
1438
1439 thermal_event->orig = orig;
1440 thermal_event->event = event;
1441
1442 /* send multicast genetlink message */
1443 result = genlmsg_end(skb, msg_header);
1444 if (result < 0) {
1445 nlmsg_free(skb);
1446 return result;
1447 }
1448
1449 result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
1450 if (result)
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001451 pr_info("failed to send netlink event:%d\n", result);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301452
1453 return result;
1454}
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001455EXPORT_SYMBOL(thermal_generate_netlink_event);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301456
1457static int genetlink_init(void)
1458{
1459 int result;
1460
1461 result = genl_register_family(&thermal_event_genl_family);
1462 if (result)
1463 return result;
1464
1465 result = genl_register_mc_group(&thermal_event_genl_family,
1466 &thermal_event_mcgrp);
1467 if (result)
1468 genl_unregister_family(&thermal_event_genl_family);
1469 return result;
1470}
1471
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001472static void genetlink_exit(void)
1473{
1474 genl_unregister_family(&thermal_event_genl_family);
1475}
1476#else /* !CONFIG_NET */
1477static inline int genetlink_init(void) { return 0; }
1478static inline void genetlink_exit(void) {}
1479#endif /* !CONFIG_NET */
1480
Zhang Rui203d3d42008-01-17 15:51:08 +08001481static int __init thermal_init(void)
1482{
1483 int result = 0;
1484
1485 result = class_register(&thermal_class);
1486 if (result) {
1487 idr_destroy(&thermal_tz_idr);
1488 idr_destroy(&thermal_cdev_idr);
1489 mutex_destroy(&thermal_idr_lock);
1490 mutex_destroy(&thermal_list_lock);
1491 }
R.Durgadoss4cb18722010-10-27 03:33:29 +05301492 result = genetlink_init();
Zhang Rui203d3d42008-01-17 15:51:08 +08001493 return result;
1494}
1495
1496static void __exit thermal_exit(void)
1497{
1498 class_unregister(&thermal_class);
1499 idr_destroy(&thermal_tz_idr);
1500 idr_destroy(&thermal_cdev_idr);
1501 mutex_destroy(&thermal_idr_lock);
1502 mutex_destroy(&thermal_list_lock);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301503 genetlink_exit();
Zhang Rui203d3d42008-01-17 15:51:08 +08001504}
1505
R.Durgadoss4cb18722010-10-27 03:33:29 +05301506fs_initcall(thermal_init);
Zhang Rui203d3d42008-01-17 15:51:08 +08001507module_exit(thermal_exit);