blob: 8d9da6b37acb3bdd029ef645a0a10998897f433d [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>
Praveen Chidambarambb646212013-07-02 13:04:58 -06007 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
Zhang Rui203d3d42008-01-17 15:51:08 +08008 *
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 *
24 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 */
26
Joe Perchesc5a01dd2012-03-21 12:55:02 -070027#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
Zhang Rui203d3d42008-01-17 15:51:08 +080029#include <linux/module.h>
30#include <linux/device.h>
31#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090032#include <linux/slab.h>
Zhang Rui203d3d42008-01-17 15:51:08 +080033#include <linux/kdev_t.h>
34#include <linux/idr.h>
35#include <linux/thermal.h>
36#include <linux/spinlock.h>
Matthew Garrettb1569e92008-12-03 17:55:32 +000037#include <linux/reboot.h>
R.Durgadoss4cb18722010-10-27 03:33:29 +053038#include <net/netlink.h>
39#include <net/genetlink.h>
Zhang Rui203d3d42008-01-17 15:51:08 +080040
Zhang Rui63c4ec92008-04-21 16:07:13 +080041MODULE_AUTHOR("Zhang Rui");
Zhang Rui203d3d42008-01-17 15:51:08 +080042MODULE_DESCRIPTION("Generic thermal management sysfs support");
43MODULE_LICENSE("GPL");
44
Zhang Rui203d3d42008-01-17 15:51:08 +080045struct thermal_cooling_device_instance {
46 int id;
47 char name[THERMAL_NAME_LENGTH];
48 struct thermal_zone_device *tz;
49 struct thermal_cooling_device *cdev;
50 int trip;
51 char attr_name[THERMAL_NAME_LENGTH];
52 struct device_attribute attr;
53 struct list_head node;
54};
55
56static DEFINE_IDR(thermal_tz_idr);
57static DEFINE_IDR(thermal_cdev_idr);
58static DEFINE_MUTEX(thermal_idr_lock);
59
60static LIST_HEAD(thermal_tz_list);
61static LIST_HEAD(thermal_cdev_list);
62static DEFINE_MUTEX(thermal_list_lock);
63
Praveen Chidambarambb646212013-07-02 13:04:58 -060064static LIST_HEAD(sensor_info_list);
65static DEFINE_MUTEX(sensor_list_lock);
66
67static struct sensor_info *get_sensor(uint32_t sensor_id)
68{
69 struct sensor_info *pos, *var;
70
71 list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
72 if (pos->sensor_id == sensor_id)
73 break;
74 }
75
76 return pos;
77}
78
79int sensor_get_id(char *name)
80{
81 struct sensor_info *pos, *var;
82
83 list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
84 if (!strcmp(pos->tz->type, name))
85 return pos->sensor_id;
86 }
87
88 return -ENODEV;
89}
90EXPORT_SYMBOL(sensor_get_id);
91
Praveen Chidambaram3af26db2013-09-17 13:51:42 -060092static long get_min(struct sensor_info *sensor, long temp)
93{
94 long min = LONG_MIN;
95 struct sensor_threshold *pos, *var;
96
97 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
98 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW)
99 if (pos->temp < temp && pos->temp > min)
100 min = pos->temp;
101 }
102
103 return min;
104}
105
Praveen Chidambarambb646212013-07-02 13:04:58 -0600106static void __update_sensor_thresholds(struct sensor_info *sensor)
107{
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600108 long min = LONG_MIN;
109 long max = LONG_MAX;
110 long max_of_min = LONG_MIN;
111 long min_of_max = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600112 struct sensor_threshold *pos, *var;
113 enum thermal_trip_type type;
114 int i;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600115 long curr_temp;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600116
117 for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
118 (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
119 i++) {
120 sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
121 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
122 sensor->max_idx = i;
123 if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
124 sensor->min_idx = i;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600125 sensor->tz->ops->get_trip_temp(sensor->tz,
126 THERMAL_TRIP_CONFIGURABLE_LOW, &sensor->threshold_min);
127 sensor->tz->ops->get_trip_temp(sensor->tz,
128 THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600129 }
130
Praveen Chidambarambb646212013-07-02 13:04:58 -0600131 sensor->tz->ops->get_temp(sensor->tz, &curr_temp);
132 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600133 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW) {
134 if (pos->temp > max_of_min)
135 max_of_min = pos->temp;
136 if (pos->temp < curr_temp && pos->temp > min)
Praveen Chidambarambb646212013-07-02 13:04:58 -0600137 min = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600138 }
139 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_HI) {
140 if (pos->temp < min_of_max)
141 min_of_max = pos->temp;
142 if (pos->temp > curr_temp && pos->temp < max)
Praveen Chidambarambb646212013-07-02 13:04:58 -0600143 max = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600144 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600145 }
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600146
147 pr_debug("sensor %d: min of max: %ld max of min: %ld\n",
148 sensor->sensor_id, max_of_min, min_of_max);
149
150 /* If we haven't found a max and min bounding the curr_temp,
151 * use the min of max and max of min instead.
152 */
153 if (max == LONG_MAX)
154 max = min_of_max;
155 if (min == LONG_MIN) {
156 min = get_min(sensor, max);
157 if (min == LONG_MIN)
158 min = max_of_min;
159 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600160
161 if (sensor->tz->ops->set_trip_temp) {
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600162 if (max != sensor->threshold_max) {
Praveen Chidambarambb646212013-07-02 13:04:58 -0600163 sensor->tz->ops->set_trip_temp(sensor->tz,
164 sensor->max_idx, max);
165 sensor->threshold_max = max;
166 }
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600167 if (min != sensor->threshold_min) {
Praveen Chidambarambb646212013-07-02 13:04:58 -0600168 sensor->tz->ops->set_trip_temp(sensor->tz,
169 sensor->min_idx, min);
170 sensor->threshold_min = min;
171 }
172 }
173
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600174 pr_debug("sensor %d: curr_temp: %ld min: %ld max: %ld\n",
175 sensor->sensor_id, curr_temp,
176 sensor->threshold_min, sensor->threshold_max);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600177}
178
179static void sensor_update_work(struct work_struct *work)
180{
181 struct sensor_info *sensor = container_of(work, struct sensor_info,
182 work);
183 mutex_lock(&sensor->lock);
184 __update_sensor_thresholds(sensor);
185 mutex_unlock(&sensor->lock);
186}
187
188/* May be called in an interrupt context.
189 * Do NOT call sensor_set_trip from this function
190 */
191int thermal_sensor_trip(struct thermal_zone_device *tz,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600192 enum thermal_trip_type trip, long temp)
Praveen Chidambarambb646212013-07-02 13:04:58 -0600193{
194 struct sensor_threshold *pos, *var;
195 int ret = -ENODEV;
196
197 if (trip != THERMAL_TRIP_CONFIGURABLE_HI &&
198 trip != THERMAL_TRIP_CONFIGURABLE_LOW)
199 return 0;
200
201 if (list_empty(&tz->sensor.threshold_list))
202 return 0;
203
204 list_for_each_entry_safe(pos, var, &tz->sensor.threshold_list, list) {
205 if (pos->trip != trip)
206 continue;
207 if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
208 (pos->temp <= tz->sensor.threshold_min) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600209 (pos->temp >= temp)) ||
Praveen Chidambarambb646212013-07-02 13:04:58 -0600210 ((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
211 (pos->temp >= tz->sensor.threshold_max) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600212 (pos->temp <= temp))) {
Praveen Chidambarambb646212013-07-02 13:04:58 -0600213 pos->notify(trip, temp, pos->data);
214 }
215 }
216
217 schedule_work(&tz->sensor.work);
218
219 return ret;
220}
221EXPORT_SYMBOL(thermal_sensor_trip);
222
223int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
224{
225 struct sensor_threshold *pos, *var;
226 struct sensor_info *sensor = get_sensor(sensor_id);
227
228 if (!sensor)
229 return -ENODEV;
230
231 if (!threshold || !threshold->notify)
232 return -EFAULT;
233
234 mutex_lock(&sensor->lock);
235 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
236 if (pos == threshold)
237 break;
238 }
239
240 if (pos != threshold) {
241 INIT_LIST_HEAD(&threshold->list);
242 list_add(&threshold->list, &sensor->threshold_list);
243 }
244
245 __update_sensor_thresholds(sensor);
246 mutex_unlock(&sensor->lock);
247
248 return 0;
249
250}
251EXPORT_SYMBOL(sensor_set_trip);
252
253int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
254{
255 struct sensor_threshold *pos, *var;
256 struct sensor_info *sensor = get_sensor(sensor_id);
257
258 if (!sensor)
259 return -ENODEV;
260
261 mutex_lock(&sensor->lock);
262 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
263 if (pos == threshold) {
264 list_del(&pos->list);
265 break;
266 }
267 }
268
269 __update_sensor_thresholds(sensor);
270 mutex_unlock(&sensor->lock);
271
272 return 0;
273}
274EXPORT_SYMBOL(sensor_cancel_trip);
275
Praveen Chidambarambb646212013-07-02 13:04:58 -0600276static int tz_notify_trip(enum thermal_trip_type type, int temp, void *data)
277{
278 struct thermal_zone_device *tz = (struct thermal_zone_device *)data;
279
280 pr_debug("sensor %d tripped: type %d temp %d\n",
281 tz->sensor.sensor_id, type, temp);
282
283 return 0;
284}
285
286int sensor_set_trip_temp(struct thermal_zone_device *tz,
287 int trip, long temp)
288{
289 int ret = 0;
290 enum thermal_trip_type type;
291
292 if (!tz->ops->get_trip_type)
293 return -EPERM;
294
295 tz->ops->get_trip_type(tz, trip, &type);
296 switch (type) {
297 case THERMAL_TRIP_CONFIGURABLE_HI:
298 tz->tz_threshold[0].temp = temp;
299 tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
300 tz->tz_threshold[0].notify = tz_notify_trip;
301 tz->tz_threshold[0].data = tz;
302 ret = sensor_set_trip(tz->sensor.sensor_id,
303 &tz->tz_threshold[0]);
304 break;
305 case THERMAL_TRIP_CONFIGURABLE_LOW:
306 tz->tz_threshold[1].temp = temp;
307 tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
308 tz->tz_threshold[1].notify = tz_notify_trip;
309 tz->tz_threshold[1].data = tz;
310 ret = sensor_set_trip(tz->sensor.sensor_id,
311 &tz->tz_threshold[1]);
312 break;
313 default:
314 ret = tz->ops->set_trip_temp(tz, trip, temp);
315 break;
316 }
317
318 return ret;
319}
320
321int sensor_init(struct thermal_zone_device *tz)
322{
323 struct sensor_info *sensor = &tz->sensor;
324
325 sensor->sensor_id = tz->id;
326 sensor->tz = tz;
327 sensor->threshold_min = 0;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600328 sensor->threshold_max = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600329 sensor->max_idx = -1;
330 sensor->min_idx = -1;
331 mutex_init(&sensor->lock);
332 INIT_LIST_HEAD(&sensor->sensor_list);
333 INIT_LIST_HEAD(&sensor->threshold_list);
334 INIT_LIST_HEAD(&tz->tz_threshold[0].list);
335 INIT_LIST_HEAD(&tz->tz_threshold[1].list);
336 tz->tz_threshold[0].notify = NULL;
337 tz->tz_threshold[0].data = NULL;
338 tz->tz_threshold[1].notify = NULL;
339 tz->tz_threshold[1].data = NULL;
340 list_add(&sensor->sensor_list, &sensor_info_list);
341 INIT_WORK(&sensor->work, sensor_update_work);
342
343 return 0;
344}
345
Zhang Rui203d3d42008-01-17 15:51:08 +0800346static int get_idr(struct idr *idr, struct mutex *lock, int *id)
347{
348 int err;
349
Joe Perchescaca8b82012-03-21 12:55:02 -0700350again:
Zhang Rui203d3d42008-01-17 15:51:08 +0800351 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
352 return -ENOMEM;
353
354 if (lock)
355 mutex_lock(lock);
356 err = idr_get_new(idr, NULL, id);
357 if (lock)
358 mutex_unlock(lock);
359 if (unlikely(err == -EAGAIN))
360 goto again;
361 else if (unlikely(err))
362 return err;
363
364 *id = *id & MAX_ID_MASK;
365 return 0;
366}
367
368static void release_idr(struct idr *idr, struct mutex *lock, int id)
369{
370 if (lock)
371 mutex_lock(lock);
372 idr_remove(idr, id);
373 if (lock)
374 mutex_unlock(lock);
375}
376
377/* sys I/F for thermal zone */
378
379#define to_thermal_zone(_dev) \
380 container_of(_dev, struct thermal_zone_device, device)
381
382static ssize_t
383type_show(struct device *dev, struct device_attribute *attr, char *buf)
384{
385 struct thermal_zone_device *tz = to_thermal_zone(dev);
386
387 return sprintf(buf, "%s\n", tz->type);
388}
389
390static ssize_t
391temp_show(struct device *dev, struct device_attribute *attr, char *buf)
392{
393 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000394 long temperature;
395 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800396
397 if (!tz->ops->get_temp)
398 return -EPERM;
399
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000400 ret = tz->ops->get_temp(tz, &temperature);
401
402 if (ret)
403 return ret;
404
405 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800406}
407
408static ssize_t
409mode_show(struct device *dev, struct device_attribute *attr, char *buf)
410{
411 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000412 enum thermal_device_mode mode;
413 int result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800414
415 if (!tz->ops->get_mode)
416 return -EPERM;
417
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000418 result = tz->ops->get_mode(tz, &mode);
419 if (result)
420 return result;
421
422 return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
423 : "disabled");
Zhang Rui203d3d42008-01-17 15:51:08 +0800424}
425
426static ssize_t
427mode_store(struct device *dev, struct device_attribute *attr,
428 const char *buf, size_t count)
429{
430 struct thermal_zone_device *tz = to_thermal_zone(dev);
431 int result;
432
433 if (!tz->ops->set_mode)
434 return -EPERM;
435
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530436 if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000437 result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530438 else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000439 result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
440 else
441 result = -EINVAL;
442
Zhang Rui203d3d42008-01-17 15:51:08 +0800443 if (result)
444 return result;
445
446 return count;
447}
448
449static ssize_t
450trip_point_type_show(struct device *dev, struct device_attribute *attr,
451 char *buf)
452{
453 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000454 enum thermal_trip_type type;
455 int trip, result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800456
457 if (!tz->ops->get_trip_type)
458 return -EPERM;
459
460 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
461 return -EINVAL;
462
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000463 result = tz->ops->get_trip_type(tz, trip, &type);
464 if (result)
465 return result;
466
467 switch (type) {
468 case THERMAL_TRIP_CRITICAL:
Amit Kucheria625120a2009-10-16 12:46:02 +0300469 return sprintf(buf, "critical\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000470 case THERMAL_TRIP_HOT:
Amit Kucheria625120a2009-10-16 12:46:02 +0300471 return sprintf(buf, "hot\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472 case THERMAL_TRIP_CONFIGURABLE_HI:
473 return sprintf(buf, "configurable_hi\n");
474 case THERMAL_TRIP_CONFIGURABLE_LOW:
475 return sprintf(buf, "configurable_low\n");
476 case THERMAL_TRIP_CRITICAL_LOW:
477 return sprintf(buf, "critical_low\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000478 case THERMAL_TRIP_PASSIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300479 return sprintf(buf, "passive\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000480 case THERMAL_TRIP_ACTIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300481 return sprintf(buf, "active\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000482 default:
Amit Kucheria625120a2009-10-16 12:46:02 +0300483 return sprintf(buf, "unknown\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000484 }
Zhang Rui203d3d42008-01-17 15:51:08 +0800485}
486
487static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700488trip_point_type_activate(struct device *dev, struct device_attribute *attr,
489 const char *buf, size_t count)
490{
491 struct thermal_zone_device *tz = to_thermal_zone(dev);
492 int trip, result;
493
494 if (!tz->ops->activate_trip_type)
495 return -EPERM;
496
497 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
498 return -EINVAL;
499
500 if (!strncmp(buf, "enabled", sizeof("enabled")))
501 result = tz->ops->activate_trip_type(tz, trip,
502 THERMAL_TRIP_ACTIVATION_ENABLED);
503 else if (!strncmp(buf, "disabled", sizeof("disabled")))
504 result = tz->ops->activate_trip_type(tz, trip,
505 THERMAL_TRIP_ACTIVATION_DISABLED);
506 else
507 result = -EINVAL;
508
509 if (result)
510 return result;
511
512 return count;
513}
514
515static ssize_t
Zhang Rui203d3d42008-01-17 15:51:08 +0800516trip_point_temp_show(struct device *dev, struct device_attribute *attr,
517 char *buf)
518{
519 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000520 int trip, ret;
521 long temperature;
Zhang Rui203d3d42008-01-17 15:51:08 +0800522
523 if (!tz->ops->get_trip_temp)
524 return -EPERM;
525
526 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
527 return -EINVAL;
528
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600529 ret = tz->ops->get_trip_temp(tz, trip, &temperature);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000530
531 if (ret)
532 return ret;
533
534 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800535}
536
Matthew Garrett03a971a2008-12-03 18:00:38 +0000537static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538trip_point_temp_set(struct device *dev, struct device_attribute *attr,
539 const char *buf, size_t count)
540{
541 struct thermal_zone_device *tz = to_thermal_zone(dev);
542 int trip, ret;
543 long temperature;
544
545 if (!tz->ops->set_trip_temp)
546 return -EPERM;
547
548 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
549 return -EINVAL;
550
551 if (!sscanf(buf, "%ld", &temperature))
552 return -EINVAL;
553
Praveen Chidambarambb646212013-07-02 13:04:58 -0600554 ret = sensor_set_trip_temp(tz, trip, temperature);
555
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 if (ret)
557 return ret;
558
559 return count;
560}
561
562static ssize_t
Matthew Garrett03a971a2008-12-03 18:00:38 +0000563passive_store(struct device *dev, struct device_attribute *attr,
564 const char *buf, size_t count)
565{
566 struct thermal_zone_device *tz = to_thermal_zone(dev);
567 struct thermal_cooling_device *cdev = NULL;
568 int state;
569
570 if (!sscanf(buf, "%d\n", &state))
571 return -EINVAL;
572
Frans Pop3d8e3ad2009-10-26 08:39:02 +0100573 /* sanity check: values below 1000 millicelcius don't make sense
574 * and can cause the system to go into a thermal heart attack
575 */
576 if (state && state < 1000)
577 return -EINVAL;
578
Matthew Garrett03a971a2008-12-03 18:00:38 +0000579 if (state && !tz->forced_passive) {
580 mutex_lock(&thermal_list_lock);
581 list_for_each_entry(cdev, &thermal_cdev_list, node) {
582 if (!strncmp("Processor", cdev->type,
583 sizeof("Processor")))
584 thermal_zone_bind_cooling_device(tz,
585 THERMAL_TRIPS_NONE,
586 cdev);
587 }
588 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100589 if (!tz->passive_delay)
590 tz->passive_delay = 1000;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000591 } else if (!state && tz->forced_passive) {
592 mutex_lock(&thermal_list_lock);
593 list_for_each_entry(cdev, &thermal_cdev_list, node) {
594 if (!strncmp("Processor", cdev->type,
595 sizeof("Processor")))
596 thermal_zone_unbind_cooling_device(tz,
597 THERMAL_TRIPS_NONE,
598 cdev);
599 }
600 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100601 tz->passive_delay = 0;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000602 }
603
604 tz->tc1 = 1;
605 tz->tc2 = 1;
606
Matthew Garrett03a971a2008-12-03 18:00:38 +0000607 tz->forced_passive = state;
608
609 thermal_zone_device_update(tz);
610
611 return count;
612}
613
614static ssize_t
615passive_show(struct device *dev, struct device_attribute *attr,
616 char *buf)
617{
618 struct thermal_zone_device *tz = to_thermal_zone(dev);
619
620 return sprintf(buf, "%d\n", tz->forced_passive);
621}
622
Zhang Rui203d3d42008-01-17 15:51:08 +0800623static DEVICE_ATTR(type, 0444, type_show, NULL);
624static DEVICE_ATTR(temp, 0444, temp_show, NULL);
625static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
Joe Perches886ee542012-03-21 12:55:01 -0700626static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
Zhang Rui203d3d42008-01-17 15:51:08 +0800627
628static struct device_attribute trip_point_attrs[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700629 __ATTR(trip_point_0_type, 0644, trip_point_type_show,
630 trip_point_type_activate),
631 __ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
632 trip_point_temp_set),
633 __ATTR(trip_point_1_type, 0644, trip_point_type_show,
634 trip_point_type_activate),
635 __ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
636 trip_point_temp_set),
637 __ATTR(trip_point_2_type, 0644, trip_point_type_show,
638 trip_point_type_activate),
639 __ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
640 trip_point_temp_set),
641 __ATTR(trip_point_3_type, 0644, trip_point_type_show,
642 trip_point_type_activate),
643 __ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
644 trip_point_temp_set),
645 __ATTR(trip_point_4_type, 0644, trip_point_type_show,
646 trip_point_type_activate),
647 __ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
648 trip_point_temp_set),
649 __ATTR(trip_point_5_type, 0644, trip_point_type_show,
650 trip_point_type_activate),
651 __ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
652 trip_point_temp_set),
653 __ATTR(trip_point_6_type, 0644, trip_point_type_show,
654 trip_point_type_activate),
655 __ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
656 trip_point_temp_set),
657 __ATTR(trip_point_7_type, 0644, trip_point_type_show,
658 trip_point_type_activate),
659 __ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
660 trip_point_temp_set),
661 __ATTR(trip_point_8_type, 0644, trip_point_type_show,
662 trip_point_type_activate),
663 __ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
664 trip_point_temp_set),
665 __ATTR(trip_point_9_type, 0644, trip_point_type_show,
666 trip_point_type_activate),
667 __ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
668 trip_point_temp_set),
669 __ATTR(trip_point_10_type, 0644, trip_point_type_show,
670 trip_point_type_activate),
671 __ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
672 trip_point_temp_set),
673 __ATTR(trip_point_11_type, 0644, trip_point_type_show,
674 trip_point_type_activate),
675 __ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
676 trip_point_temp_set),
Zhang Rui203d3d42008-01-17 15:51:08 +0800677};
678
Zhang Rui203d3d42008-01-17 15:51:08 +0800679/* sys I/F for cooling device */
680#define to_cooling_device(_dev) \
681 container_of(_dev, struct thermal_cooling_device, device)
682
683static ssize_t
684thermal_cooling_device_type_show(struct device *dev,
685 struct device_attribute *attr, char *buf)
686{
687 struct thermal_cooling_device *cdev = to_cooling_device(dev);
688
689 return sprintf(buf, "%s\n", cdev->type);
690}
691
692static ssize_t
693thermal_cooling_device_max_state_show(struct device *dev,
694 struct device_attribute *attr, char *buf)
695{
696 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000697 unsigned long state;
698 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800699
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000700 ret = cdev->ops->get_max_state(cdev, &state);
701 if (ret)
702 return ret;
703 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800704}
705
706static ssize_t
707thermal_cooling_device_cur_state_show(struct device *dev,
708 struct device_attribute *attr, char *buf)
709{
710 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000711 unsigned long state;
712 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800713
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000714 ret = cdev->ops->get_cur_state(cdev, &state);
715 if (ret)
716 return ret;
717 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800718}
719
720static ssize_t
721thermal_cooling_device_cur_state_store(struct device *dev,
722 struct device_attribute *attr,
723 const char *buf, size_t count)
724{
725 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000726 unsigned long state;
Zhang Rui203d3d42008-01-17 15:51:08 +0800727 int result;
728
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000729 if (!sscanf(buf, "%ld\n", &state))
Zhang Rui203d3d42008-01-17 15:51:08 +0800730 return -EINVAL;
731
Roel Kluinedb94912009-12-15 22:46:50 +0100732 if ((long)state < 0)
Zhang Rui203d3d42008-01-17 15:51:08 +0800733 return -EINVAL;
734
735 result = cdev->ops->set_cur_state(cdev, state);
736 if (result)
737 return result;
738 return count;
739}
740
741static struct device_attribute dev_attr_cdev_type =
Len Brown543a9562008-02-07 16:55:08 -0500742__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800743static DEVICE_ATTR(max_state, 0444,
744 thermal_cooling_device_max_state_show, NULL);
745static DEVICE_ATTR(cur_state, 0644,
746 thermal_cooling_device_cur_state_show,
747 thermal_cooling_device_cur_state_store);
748
749static ssize_t
750thermal_cooling_device_trip_point_show(struct device *dev,
Len Brown543a9562008-02-07 16:55:08 -0500751 struct device_attribute *attr, char *buf)
Zhang Rui203d3d42008-01-17 15:51:08 +0800752{
753 struct thermal_cooling_device_instance *instance;
754
755 instance =
756 container_of(attr, struct thermal_cooling_device_instance, attr);
757
758 if (instance->trip == THERMAL_TRIPS_NONE)
759 return sprintf(buf, "-1\n");
760 else
761 return sprintf(buf, "%d\n", instance->trip);
762}
763
764/* Device management */
765
Rene Herman16d75232008-06-24 19:38:56 +0200766#if defined(CONFIG_THERMAL_HWMON)
767
Zhang Ruie68b16a2008-04-21 16:07:52 +0800768/* hwmon sys I/F */
769#include <linux/hwmon.h>
Jean Delvare31f53962011-07-28 13:48:42 -0700770
771/* thermal zone devices with the same type share one hwmon device */
772struct thermal_hwmon_device {
773 char type[THERMAL_NAME_LENGTH];
774 struct device *device;
775 int count;
776 struct list_head tz_list;
777 struct list_head node;
778};
779
780struct thermal_hwmon_attr {
781 struct device_attribute attr;
782 char name[16];
783};
784
785/* one temperature input for each thermal zone */
786struct thermal_hwmon_temp {
787 struct list_head hwmon_node;
788 struct thermal_zone_device *tz;
789 struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
790 struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
791};
792
Zhang Ruie68b16a2008-04-21 16:07:52 +0800793static LIST_HEAD(thermal_hwmon_list);
794
795static ssize_t
796name_show(struct device *dev, struct device_attribute *attr, char *buf)
797{
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700798 struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800799 return sprintf(buf, "%s\n", hwmon->type);
800}
801static DEVICE_ATTR(name, 0444, name_show, NULL);
802
803static ssize_t
804temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
805{
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000806 long temperature;
807 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800808 struct thermal_hwmon_attr *hwmon_attr
809 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700810 struct thermal_hwmon_temp *temp
811 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800812 temp_input);
Jean Delvare31f53962011-07-28 13:48:42 -0700813 struct thermal_zone_device *tz = temp->tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800814
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000815 ret = tz->ops->get_temp(tz, &temperature);
816
817 if (ret)
818 return ret;
819
820 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800821}
822
823static ssize_t
824temp_crit_show(struct device *dev, struct device_attribute *attr,
825 char *buf)
826{
827 struct thermal_hwmon_attr *hwmon_attr
828 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700829 struct thermal_hwmon_temp *temp
830 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800831 temp_crit);
Jean Delvare31f53962011-07-28 13:48:42 -0700832 struct thermal_zone_device *tz = temp->tz;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000833 long temperature;
834 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800835
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000836 ret = tz->ops->get_trip_temp(tz, 0, &temperature);
837 if (ret)
838 return ret;
839
840 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800841}
842
843
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700844static struct thermal_hwmon_device *
845thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
846{
847 struct thermal_hwmon_device *hwmon;
848
849 mutex_lock(&thermal_list_lock);
850 list_for_each_entry(hwmon, &thermal_hwmon_list, node)
851 if (!strcmp(hwmon->type, tz->type)) {
852 mutex_unlock(&thermal_list_lock);
853 return hwmon;
854 }
855 mutex_unlock(&thermal_list_lock);
856
857 return NULL;
858}
859
Jean Delvare31f53962011-07-28 13:48:42 -0700860/* Find the temperature input matching a given thermal zone */
861static struct thermal_hwmon_temp *
862thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
863 const struct thermal_zone_device *tz)
864{
865 struct thermal_hwmon_temp *temp;
866
867 mutex_lock(&thermal_list_lock);
868 list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
869 if (temp->tz == tz) {
870 mutex_unlock(&thermal_list_lock);
871 return temp;
872 }
873 mutex_unlock(&thermal_list_lock);
874
875 return NULL;
876}
877
Zhang Ruie68b16a2008-04-21 16:07:52 +0800878static int
879thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
880{
881 struct thermal_hwmon_device *hwmon;
Jean Delvare31f53962011-07-28 13:48:42 -0700882 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800883 int new_hwmon_device = 1;
884 int result;
885
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700886 hwmon = thermal_hwmon_lookup_by_type(tz);
887 if (hwmon) {
888 new_hwmon_device = 0;
889 goto register_sys_interface;
890 }
Zhang Ruie68b16a2008-04-21 16:07:52 +0800891
892 hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
893 if (!hwmon)
894 return -ENOMEM;
895
896 INIT_LIST_HEAD(&hwmon->tz_list);
897 strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
898 hwmon->device = hwmon_device_register(NULL);
899 if (IS_ERR(hwmon->device)) {
900 result = PTR_ERR(hwmon->device);
901 goto free_mem;
902 }
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700903 dev_set_drvdata(hwmon->device, hwmon);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800904 result = device_create_file(hwmon->device, &dev_attr_name);
905 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530906 goto free_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800907
908 register_sys_interface:
Jean Delvare31f53962011-07-28 13:48:42 -0700909 temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
910 if (!temp) {
911 result = -ENOMEM;
912 goto unregister_name;
913 }
914
915 temp->tz = tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800916 hwmon->count++;
917
Jean Delvare31f53962011-07-28 13:48:42 -0700918 snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800919 "temp%d_input", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700920 temp->temp_input.attr.attr.name = temp->temp_input.name;
921 temp->temp_input.attr.attr.mode = 0444;
922 temp->temp_input.attr.show = temp_input_show;
923 sysfs_attr_init(&temp->temp_input.attr.attr);
924 result = device_create_file(hwmon->device, &temp->temp_input.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800925 if (result)
Jean Delvare31f53962011-07-28 13:48:42 -0700926 goto free_temp_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800927
928 if (tz->ops->get_crit_temp) {
929 unsigned long temperature;
930 if (!tz->ops->get_crit_temp(tz, &temperature)) {
Jean Delvare31f53962011-07-28 13:48:42 -0700931 snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800932 "temp%d_crit", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700933 temp->temp_crit.attr.attr.name = temp->temp_crit.name;
934 temp->temp_crit.attr.attr.mode = 0444;
935 temp->temp_crit.attr.show = temp_crit_show;
936 sysfs_attr_init(&temp->temp_crit.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800937 result = device_create_file(hwmon->device,
Jean Delvare31f53962011-07-28 13:48:42 -0700938 &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800939 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530940 goto unregister_input;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800941 }
942 }
943
944 mutex_lock(&thermal_list_lock);
945 if (new_hwmon_device)
946 list_add_tail(&hwmon->node, &thermal_hwmon_list);
Jean Delvare31f53962011-07-28 13:48:42 -0700947 list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800948 mutex_unlock(&thermal_list_lock);
949
950 return 0;
951
Durgadoss Rb299eb52011-03-03 04:30:13 +0530952 unregister_input:
Jean Delvare31f53962011-07-28 13:48:42 -0700953 device_remove_file(hwmon->device, &temp->temp_input.attr);
954 free_temp_mem:
955 kfree(temp);
Durgadoss Rb299eb52011-03-03 04:30:13 +0530956 unregister_name:
Zhang Ruie68b16a2008-04-21 16:07:52 +0800957 if (new_hwmon_device) {
958 device_remove_file(hwmon->device, &dev_attr_name);
959 hwmon_device_unregister(hwmon->device);
960 }
961 free_mem:
962 if (new_hwmon_device)
963 kfree(hwmon);
964
965 return result;
966}
967
968static void
969thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
970{
Jean Delvare31f53962011-07-28 13:48:42 -0700971 struct thermal_hwmon_device *hwmon;
972 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800973
Jean Delvare31f53962011-07-28 13:48:42 -0700974 hwmon = thermal_hwmon_lookup_by_type(tz);
975 if (unlikely(!hwmon)) {
976 /* Should never happen... */
977 dev_dbg(&tz->device, "hwmon device lookup failed!\n");
978 return;
979 }
980
981 temp = thermal_hwmon_lookup_temp(hwmon, tz);
982 if (unlikely(!temp)) {
983 /* Should never happen... */
984 dev_dbg(&tz->device, "temperature input lookup failed!\n");
985 return;
986 }
987
988 device_remove_file(hwmon->device, &temp->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +0530989 if (tz->ops->get_crit_temp)
Jean Delvare31f53962011-07-28 13:48:42 -0700990 device_remove_file(hwmon->device, &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800991
992 mutex_lock(&thermal_list_lock);
Jean Delvare31f53962011-07-28 13:48:42 -0700993 list_del(&temp->hwmon_node);
994 kfree(temp);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800995 if (!list_empty(&hwmon->tz_list)) {
996 mutex_unlock(&thermal_list_lock);
997 return;
998 }
999 list_del(&hwmon->node);
1000 mutex_unlock(&thermal_list_lock);
1001
1002 device_remove_file(hwmon->device, &dev_attr_name);
1003 hwmon_device_unregister(hwmon->device);
1004 kfree(hwmon);
1005}
1006#else
1007static int
1008thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
1009{
1010 return 0;
1011}
1012
1013static void
1014thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1015{
1016}
1017#endif
1018
Matthew Garrettb1569e92008-12-03 17:55:32 +00001019static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
1020 int delay)
1021{
1022 cancel_delayed_work(&(tz->poll_queue));
1023
1024 if (!delay)
1025 return;
1026
1027 if (delay > 1000)
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001028 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001029 round_jiffies(msecs_to_jiffies(delay)));
1030 else
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001031 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001032 msecs_to_jiffies(delay));
1033}
1034
1035static void thermal_zone_device_passive(struct thermal_zone_device *tz,
1036 int temp, int trip_temp, int trip)
1037{
1038 int trend = 0;
1039 struct thermal_cooling_device_instance *instance;
1040 struct thermal_cooling_device *cdev;
1041 long state, max_state;
1042
1043 /*
1044 * Above Trip?
1045 * -----------
1046 * Calculate the thermal trend (using the passive cooling equation)
1047 * and modify the performance limit for all passive cooling devices
1048 * accordingly. Note that we assume symmetry.
1049 */
1050 if (temp >= trip_temp) {
1051 tz->passive = true;
1052
1053 trend = (tz->tc1 * (temp - tz->last_temperature)) +
1054 (tz->tc2 * (temp - trip_temp));
1055
1056 /* Heating up? */
1057 if (trend > 0) {
1058 list_for_each_entry(instance, &tz->cooling_devices,
1059 node) {
1060 if (instance->trip != trip)
1061 continue;
1062 cdev = instance->cdev;
1063 cdev->ops->get_cur_state(cdev, &state);
1064 cdev->ops->get_max_state(cdev, &max_state);
1065 if (state++ < max_state)
1066 cdev->ops->set_cur_state(cdev, state);
1067 }
1068 } else if (trend < 0) { /* Cooling off? */
1069 list_for_each_entry(instance, &tz->cooling_devices,
1070 node) {
1071 if (instance->trip != trip)
1072 continue;
1073 cdev = instance->cdev;
1074 cdev->ops->get_cur_state(cdev, &state);
1075 cdev->ops->get_max_state(cdev, &max_state);
1076 if (state > 0)
1077 cdev->ops->set_cur_state(cdev, --state);
1078 }
1079 }
1080 return;
1081 }
1082
1083 /*
1084 * Below Trip?
1085 * -----------
1086 * Implement passive cooling hysteresis to slowly increase performance
1087 * and avoid thrashing around the passive trip point. Note that we
1088 * assume symmetry.
1089 */
1090 list_for_each_entry(instance, &tz->cooling_devices, node) {
1091 if (instance->trip != trip)
1092 continue;
1093 cdev = instance->cdev;
1094 cdev->ops->get_cur_state(cdev, &state);
1095 cdev->ops->get_max_state(cdev, &max_state);
1096 if (state > 0)
1097 cdev->ops->set_cur_state(cdev, --state);
1098 if (state == 0)
1099 tz->passive = false;
1100 }
1101}
1102
1103static void thermal_zone_device_check(struct work_struct *work)
1104{
1105 struct thermal_zone_device *tz = container_of(work, struct
1106 thermal_zone_device,
1107 poll_queue.work);
1108 thermal_zone_device_update(tz);
1109}
Zhang Ruie68b16a2008-04-21 16:07:52 +08001110
Zhang Rui203d3d42008-01-17 15:51:08 +08001111/**
1112 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001113 * @tz: thermal zone device
1114 * @trip: indicates which trip point the cooling devices is
1115 * associated with in this thermal zone.
1116 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001117 *
1118 * This function is usually called in the thermal zone device .bind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001119 */
1120int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
1121 int trip,
1122 struct thermal_cooling_device *cdev)
1123{
1124 struct thermal_cooling_device_instance *dev;
1125 struct thermal_cooling_device_instance *pos;
Thomas Sujithc7516702008-02-15 00:58:50 -05001126 struct thermal_zone_device *pos1;
1127 struct thermal_cooling_device *pos2;
Zhang Rui203d3d42008-01-17 15:51:08 +08001128 int result;
1129
Len Brown543a9562008-02-07 16:55:08 -05001130 if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
Zhang Rui203d3d42008-01-17 15:51:08 +08001131 return -EINVAL;
1132
Thomas Sujithc7516702008-02-15 00:58:50 -05001133 list_for_each_entry(pos1, &thermal_tz_list, node) {
1134 if (pos1 == tz)
1135 break;
1136 }
1137 list_for_each_entry(pos2, &thermal_cdev_list, node) {
1138 if (pos2 == cdev)
1139 break;
1140 }
1141
1142 if (tz != pos1 || cdev != pos2)
Zhang Rui203d3d42008-01-17 15:51:08 +08001143 return -EINVAL;
1144
1145 dev =
1146 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
1147 if (!dev)
1148 return -ENOMEM;
1149 dev->tz = tz;
1150 dev->cdev = cdev;
1151 dev->trip = trip;
1152 result = get_idr(&tz->idr, &tz->lock, &dev->id);
1153 if (result)
1154 goto free_mem;
1155
1156 sprintf(dev->name, "cdev%d", dev->id);
1157 result =
1158 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
1159 if (result)
1160 goto release_idr;
1161
1162 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -07001163 sysfs_attr_init(&dev->attr.attr);
Zhang Rui203d3d42008-01-17 15:51:08 +08001164 dev->attr.attr.name = dev->attr_name;
1165 dev->attr.attr.mode = 0444;
1166 dev->attr.show = thermal_cooling_device_trip_point_show;
1167 result = device_create_file(&tz->device, &dev->attr);
1168 if (result)
1169 goto remove_symbol_link;
1170
1171 mutex_lock(&tz->lock);
1172 list_for_each_entry(pos, &tz->cooling_devices, node)
1173 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
1174 result = -EEXIST;
1175 break;
1176 }
1177 if (!result)
1178 list_add_tail(&dev->node, &tz->cooling_devices);
1179 mutex_unlock(&tz->lock);
1180
1181 if (!result)
1182 return 0;
1183
1184 device_remove_file(&tz->device, &dev->attr);
Joe Perchescaca8b82012-03-21 12:55:02 -07001185remove_symbol_link:
Zhang Rui203d3d42008-01-17 15:51:08 +08001186 sysfs_remove_link(&tz->device.kobj, dev->name);
Joe Perchescaca8b82012-03-21 12:55:02 -07001187release_idr:
Zhang Rui203d3d42008-01-17 15:51:08 +08001188 release_idr(&tz->idr, &tz->lock, dev->id);
Joe Perchescaca8b82012-03-21 12:55:02 -07001189free_mem:
Zhang Rui203d3d42008-01-17 15:51:08 +08001190 kfree(dev);
1191 return result;
1192}
1193EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
1194
1195/**
1196 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001197 * @tz: thermal zone device
1198 * @trip: indicates which trip point the cooling devices is
1199 * associated with in this thermal zone.
1200 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001201 *
1202 * This function is usually called in the thermal zone device .unbind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001203 */
1204int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
1205 int trip,
1206 struct thermal_cooling_device *cdev)
1207{
1208 struct thermal_cooling_device_instance *pos, *next;
1209
1210 mutex_lock(&tz->lock);
1211 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
Len Brown543a9562008-02-07 16:55:08 -05001212 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001213 list_del(&pos->node);
1214 mutex_unlock(&tz->lock);
1215 goto unbind;
1216 }
1217 }
1218 mutex_unlock(&tz->lock);
1219
1220 return -ENODEV;
1221
Joe Perchescaca8b82012-03-21 12:55:02 -07001222unbind:
Zhang Rui203d3d42008-01-17 15:51:08 +08001223 device_remove_file(&tz->device, &pos->attr);
1224 sysfs_remove_link(&tz->device.kobj, pos->name);
1225 release_idr(&tz->idr, &tz->lock, pos->id);
1226 kfree(pos);
1227 return 0;
1228}
1229EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
1230
1231static void thermal_release(struct device *dev)
1232{
1233 struct thermal_zone_device *tz;
1234 struct thermal_cooling_device *cdev;
1235
Joe Perchescaca8b82012-03-21 12:55:02 -07001236 if (!strncmp(dev_name(dev), "thermal_zone",
1237 sizeof("thermal_zone") - 1)) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001238 tz = to_thermal_zone(dev);
1239 kfree(tz);
1240 } else {
1241 cdev = to_cooling_device(dev);
1242 kfree(cdev);
1243 }
1244}
1245
1246static struct class thermal_class = {
1247 .name = "thermal",
1248 .dev_release = thermal_release,
1249};
1250
1251/**
1252 * thermal_cooling_device_register - register a new thermal cooling device
1253 * @type: the thermal cooling device type.
1254 * @devdata: device private data.
1255 * @ops: standard thermal cooling devices callbacks.
1256 */
Joe Perchescaca8b82012-03-21 12:55:02 -07001257struct thermal_cooling_device *
1258thermal_cooling_device_register(char *type, void *devdata,
1259 const struct thermal_cooling_device_ops *ops)
Zhang Rui203d3d42008-01-17 15:51:08 +08001260{
1261 struct thermal_cooling_device *cdev;
1262 struct thermal_zone_device *pos;
1263 int result;
1264
1265 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001266 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001267
1268 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
Len Brown543a9562008-02-07 16:55:08 -05001269 !ops->set_cur_state)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001270 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001271
1272 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
1273 if (!cdev)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001274 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001275
1276 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
1277 if (result) {
1278 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001279 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001280 }
1281
1282 strcpy(cdev->type, type);
1283 cdev->ops = ops;
1284 cdev->device.class = &thermal_class;
1285 cdev->devdata = devdata;
Kay Sievers354655e2009-01-06 10:44:37 -08001286 dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001287 result = device_register(&cdev->device);
1288 if (result) {
1289 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1290 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001291 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001292 }
1293
1294 /* sys I/F */
1295 if (type) {
Len Brown543a9562008-02-07 16:55:08 -05001296 result = device_create_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001297 if (result)
1298 goto unregister;
1299 }
1300
1301 result = device_create_file(&cdev->device, &dev_attr_max_state);
1302 if (result)
1303 goto unregister;
1304
1305 result = device_create_file(&cdev->device, &dev_attr_cur_state);
1306 if (result)
1307 goto unregister;
1308
1309 mutex_lock(&thermal_list_lock);
1310 list_add(&cdev->node, &thermal_cdev_list);
1311 list_for_each_entry(pos, &thermal_tz_list, node) {
1312 if (!pos->ops->bind)
1313 continue;
1314 result = pos->ops->bind(pos, cdev);
1315 if (result)
1316 break;
1317
1318 }
1319 mutex_unlock(&thermal_list_lock);
1320
1321 if (!result)
1322 return cdev;
1323
Joe Perchescaca8b82012-03-21 12:55:02 -07001324unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001325 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1326 device_unregister(&cdev->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001327 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001328}
1329EXPORT_SYMBOL(thermal_cooling_device_register);
1330
1331/**
1332 * thermal_cooling_device_unregister - removes the registered thermal cooling device
Zhang Rui203d3d42008-01-17 15:51:08 +08001333 * @cdev: the thermal cooling device to remove.
1334 *
1335 * thermal_cooling_device_unregister() must be called when the device is no
1336 * longer needed.
1337 */
1338void thermal_cooling_device_unregister(struct
1339 thermal_cooling_device
1340 *cdev)
1341{
1342 struct thermal_zone_device *tz;
1343 struct thermal_cooling_device *pos = NULL;
1344
1345 if (!cdev)
1346 return;
1347
1348 mutex_lock(&thermal_list_lock);
1349 list_for_each_entry(pos, &thermal_cdev_list, node)
1350 if (pos == cdev)
1351 break;
1352 if (pos != cdev) {
1353 /* thermal cooling device not found */
1354 mutex_unlock(&thermal_list_lock);
1355 return;
1356 }
1357 list_del(&cdev->node);
1358 list_for_each_entry(tz, &thermal_tz_list, node) {
1359 if (!tz->ops->unbind)
1360 continue;
1361 tz->ops->unbind(tz, cdev);
1362 }
1363 mutex_unlock(&thermal_list_lock);
1364 if (cdev->type[0])
Len Brown543a9562008-02-07 16:55:08 -05001365 device_remove_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001366 device_remove_file(&cdev->device, &dev_attr_max_state);
1367 device_remove_file(&cdev->device, &dev_attr_cur_state);
1368
1369 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1370 device_unregister(&cdev->device);
1371 return;
1372}
1373EXPORT_SYMBOL(thermal_cooling_device_unregister);
1374
1375/**
Matthew Garrettb1569e92008-12-03 17:55:32 +00001376 * thermal_zone_device_update - force an update of a thermal zone's state
1377 * @ttz: the thermal zone to update
1378 */
1379
1380void thermal_zone_device_update(struct thermal_zone_device *tz)
1381{
1382 int count, ret = 0;
1383 long temp, trip_temp;
1384 enum thermal_trip_type trip_type;
1385 struct thermal_cooling_device_instance *instance;
1386 struct thermal_cooling_device *cdev;
1387
1388 mutex_lock(&tz->lock);
1389
Michael Brunner0d288162009-08-26 14:29:25 -07001390 if (tz->ops->get_temp(tz, &temp)) {
1391 /* get_temp failed - retry it later */
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001392 pr_warn("failed to read out thermal zone %d\n", tz->id);
Michael Brunner0d288162009-08-26 14:29:25 -07001393 goto leave;
1394 }
Matthew Garrettb1569e92008-12-03 17:55:32 +00001395
1396 for (count = 0; count < tz->trips; count++) {
1397 tz->ops->get_trip_type(tz, count, &trip_type);
1398 tz->ops->get_trip_temp(tz, count, &trip_temp);
1399
1400 switch (trip_type) {
1401 case THERMAL_TRIP_CRITICAL:
Vladimir Zajac29321352009-05-06 19:34:21 +02001402 if (temp >= trip_temp) {
Matthew Garrettb1569e92008-12-03 17:55:32 +00001403 if (tz->ops->notify)
1404 ret = tz->ops->notify(tz, count,
1405 trip_type);
1406 if (!ret) {
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001407 pr_emerg("Critical temperature reached (%ld C), shutting down\n",
1408 temp/1000);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001409 orderly_poweroff(true);
1410 }
1411 }
1412 break;
1413 case THERMAL_TRIP_HOT:
Vladimir Zajac29321352009-05-06 19:34:21 +02001414 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001415 if (tz->ops->notify)
1416 tz->ops->notify(tz, count, trip_type);
1417 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001418 case THERMAL_TRIP_CONFIGURABLE_HI:
1419 if (temp >= trip_temp)
1420 if (tz->ops->notify)
1421 tz->ops->notify(tz, count, trip_type);
1422 break;
1423 case THERMAL_TRIP_CONFIGURABLE_LOW:
1424 if (temp <= trip_temp)
1425 if (tz->ops->notify)
1426 tz->ops->notify(tz, count, trip_type);
1427 break;
1428 case THERMAL_TRIP_CRITICAL_LOW:
1429 if (temp <= trip_temp) {
1430 if (tz->ops->notify)
1431 ret = tz->ops->notify(tz, count,
1432 trip_type);
1433 if (!ret) {
1434 printk(KERN_EMERG
1435 "Critical temperature reached (%ld C), \
1436 shutting down.\n", temp/1000);
1437 orderly_poweroff(true);
1438 }
1439 }
1440 break;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001441 case THERMAL_TRIP_ACTIVE:
1442 list_for_each_entry(instance, &tz->cooling_devices,
1443 node) {
1444 if (instance->trip != count)
1445 continue;
1446
1447 cdev = instance->cdev;
1448
Vladimir Zajac29321352009-05-06 19:34:21 +02001449 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001450 cdev->ops->set_cur_state(cdev, 1);
1451 else
1452 cdev->ops->set_cur_state(cdev, 0);
1453 }
1454 break;
1455 case THERMAL_TRIP_PASSIVE:
Vladimir Zajac29321352009-05-06 19:34:21 +02001456 if (temp >= trip_temp || tz->passive)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001457 thermal_zone_device_passive(tz, temp,
1458 trip_temp, count);
1459 break;
1460 }
1461 }
Matthew Garrett03a971a2008-12-03 18:00:38 +00001462
1463 if (tz->forced_passive)
1464 thermal_zone_device_passive(tz, temp, tz->forced_passive,
1465 THERMAL_TRIPS_NONE);
1466
Matthew Garrettb1569e92008-12-03 17:55:32 +00001467 tz->last_temperature = temp;
Michael Brunner0d288162009-08-26 14:29:25 -07001468
Joe Perchescaca8b82012-03-21 12:55:02 -07001469leave:
Matthew Garrettb1569e92008-12-03 17:55:32 +00001470 if (tz->passive)
1471 thermal_zone_device_set_polling(tz, tz->passive_delay);
1472 else if (tz->polling_delay)
1473 thermal_zone_device_set_polling(tz, tz->polling_delay);
Frans Pop3767cb52009-10-26 08:39:04 +01001474 else
1475 thermal_zone_device_set_polling(tz, 0);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001476 mutex_unlock(&tz->lock);
1477}
1478EXPORT_SYMBOL(thermal_zone_device_update);
1479
1480/**
Zhang Rui203d3d42008-01-17 15:51:08 +08001481 * thermal_zone_device_register - register a new thermal zone device
1482 * @type: the thermal zone device type
1483 * @trips: the number of trip points the thermal zone support
1484 * @devdata: private device data
1485 * @ops: standard thermal zone device callbacks
Matthew Garrettb1569e92008-12-03 17:55:32 +00001486 * @tc1: thermal coefficient 1 for passive calculations
1487 * @tc2: thermal coefficient 2 for passive calculations
1488 * @passive_delay: number of milliseconds to wait between polls when
1489 * performing passive cooling
1490 * @polling_delay: number of milliseconds to wait between polls when checking
1491 * whether trip points have been crossed (0 for interrupt
1492 * driven systems)
Zhang Rui203d3d42008-01-17 15:51:08 +08001493 *
1494 * thermal_zone_device_unregister() must be called when the device is no
Matthew Garrettb1569e92008-12-03 17:55:32 +00001495 * longer needed. The passive cooling formula uses tc1 and tc2 as described in
1496 * section 11.1.5.1 of the ACPI specification 3.0.
Zhang Rui203d3d42008-01-17 15:51:08 +08001497 */
1498struct thermal_zone_device *thermal_zone_device_register(char *type,
Alan Cox5b275ce2010-11-11 15:27:29 +00001499 int trips, void *devdata,
1500 const struct thermal_zone_device_ops *ops,
1501 int tc1, int tc2, int passive_delay, int polling_delay)
Zhang Rui203d3d42008-01-17 15:51:08 +08001502{
1503 struct thermal_zone_device *tz;
1504 struct thermal_cooling_device *pos;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001505 enum thermal_trip_type trip_type;
Zhang Rui203d3d42008-01-17 15:51:08 +08001506 int result;
1507 int count;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001508 int passive = 0;
Zhang Rui203d3d42008-01-17 15:51:08 +08001509
1510 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001511 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001512
1513 if (trips > THERMAL_MAX_TRIPS || trips < 0)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001514 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001515
1516 if (!ops || !ops->get_temp)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001517 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001518
1519 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
1520 if (!tz)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001521 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001522
1523 INIT_LIST_HEAD(&tz->cooling_devices);
1524 idr_init(&tz->idr);
1525 mutex_init(&tz->lock);
1526 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
1527 if (result) {
1528 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001529 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001530 }
1531
1532 strcpy(tz->type, type);
1533 tz->ops = ops;
1534 tz->device.class = &thermal_class;
1535 tz->devdata = devdata;
1536 tz->trips = trips;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001537 tz->tc1 = tc1;
1538 tz->tc2 = tc2;
1539 tz->passive_delay = passive_delay;
1540 tz->polling_delay = polling_delay;
1541
Kay Sievers354655e2009-01-06 10:44:37 -08001542 dev_set_name(&tz->device, "thermal_zone%d", tz->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001543 result = device_register(&tz->device);
1544 if (result) {
1545 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1546 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001547 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001548 }
1549
1550 /* sys I/F */
1551 if (type) {
1552 result = device_create_file(&tz->device, &dev_attr_type);
1553 if (result)
1554 goto unregister;
1555 }
1556
1557 result = device_create_file(&tz->device, &dev_attr_temp);
1558 if (result)
1559 goto unregister;
1560
1561 if (ops->get_mode) {
1562 result = device_create_file(&tz->device, &dev_attr_mode);
1563 if (result)
1564 goto unregister;
1565 }
1566
1567 for (count = 0; count < trips; count++) {
Joe Perchesec797682012-03-21 12:55:02 -07001568 result = device_create_file(&tz->device,
1569 &trip_point_attrs[count * 2]);
1570 if (result)
1571 break;
1572 result = device_create_file(&tz->device,
1573 &trip_point_attrs[count * 2 + 1]);
Zhang Rui203d3d42008-01-17 15:51:08 +08001574 if (result)
1575 goto unregister;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001576 tz->ops->get_trip_type(tz, count, &trip_type);
1577 if (trip_type == THERMAL_TRIP_PASSIVE)
1578 passive = 1;
Zhang Rui203d3d42008-01-17 15:51:08 +08001579 }
1580
Matthew Garrett03a971a2008-12-03 18:00:38 +00001581 if (!passive)
1582 result = device_create_file(&tz->device,
1583 &dev_attr_passive);
1584
1585 if (result)
1586 goto unregister;
1587
Zhang Ruie68b16a2008-04-21 16:07:52 +08001588 result = thermal_add_hwmon_sysfs(tz);
1589 if (result)
1590 goto unregister;
1591
Zhang Rui203d3d42008-01-17 15:51:08 +08001592 mutex_lock(&thermal_list_lock);
1593 list_add_tail(&tz->node, &thermal_tz_list);
1594 if (ops->bind)
1595 list_for_each_entry(pos, &thermal_cdev_list, node) {
Len Brown543a9562008-02-07 16:55:08 -05001596 result = ops->bind(tz, pos);
1597 if (result)
1598 break;
Zhang Rui203d3d42008-01-17 15:51:08 +08001599 }
Praveen Chidambarambb646212013-07-02 13:04:58 -06001600 sensor_init(tz);
Zhang Rui203d3d42008-01-17 15:51:08 +08001601 mutex_unlock(&thermal_list_lock);
1602
Matthew Garrettb1569e92008-12-03 17:55:32 +00001603 INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
1604
1605 thermal_zone_device_update(tz);
1606
Zhang Rui203d3d42008-01-17 15:51:08 +08001607 if (!result)
1608 return tz;
1609
Joe Perchescaca8b82012-03-21 12:55:02 -07001610unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001611 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1612 device_unregister(&tz->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001613 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001614}
1615EXPORT_SYMBOL(thermal_zone_device_register);
1616
1617/**
1618 * thermal_device_unregister - removes the registered thermal zone device
Zhang Rui203d3d42008-01-17 15:51:08 +08001619 * @tz: the thermal zone device to remove
1620 */
1621void thermal_zone_device_unregister(struct thermal_zone_device *tz)
1622{
1623 struct thermal_cooling_device *cdev;
1624 struct thermal_zone_device *pos = NULL;
1625 int count;
1626
1627 if (!tz)
1628 return;
1629
1630 mutex_lock(&thermal_list_lock);
1631 list_for_each_entry(pos, &thermal_tz_list, node)
1632 if (pos == tz)
1633 break;
1634 if (pos != tz) {
1635 /* thermal zone device not found */
1636 mutex_unlock(&thermal_list_lock);
1637 return;
1638 }
1639 list_del(&tz->node);
1640 if (tz->ops->unbind)
1641 list_for_each_entry(cdev, &thermal_cdev_list, node)
1642 tz->ops->unbind(tz, cdev);
1643 mutex_unlock(&thermal_list_lock);
1644
Matthew Garrettb1569e92008-12-03 17:55:32 +00001645 thermal_zone_device_set_polling(tz, 0);
1646
Zhang Rui203d3d42008-01-17 15:51:08 +08001647 if (tz->type[0])
1648 device_remove_file(&tz->device, &dev_attr_type);
1649 device_remove_file(&tz->device, &dev_attr_temp);
1650 if (tz->ops->get_mode)
1651 device_remove_file(&tz->device, &dev_attr_mode);
1652
Joe Perchesec797682012-03-21 12:55:02 -07001653 for (count = 0; count < tz->trips; count++) {
1654 device_remove_file(&tz->device,
1655 &trip_point_attrs[count * 2]);
1656 device_remove_file(&tz->device,
1657 &trip_point_attrs[count * 2 + 1]);
1658 }
Zhang Ruie68b16a2008-04-21 16:07:52 +08001659 thermal_remove_hwmon_sysfs(tz);
Praveen Chidambarambb646212013-07-02 13:04:58 -06001660 flush_work(&tz->sensor.work);
1661 mutex_lock(&thermal_list_lock);
1662 list_del(&tz->sensor.sensor_list);
1663 mutex_unlock(&thermal_list_lock);
Zhang Rui203d3d42008-01-17 15:51:08 +08001664 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1665 idr_destroy(&tz->idr);
1666 mutex_destroy(&tz->lock);
1667 device_unregister(&tz->device);
1668 return;
1669}
Zhang Rui203d3d42008-01-17 15:51:08 +08001670EXPORT_SYMBOL(thermal_zone_device_unregister);
1671
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001672#ifdef CONFIG_NET
1673static struct genl_family thermal_event_genl_family = {
1674 .id = GENL_ID_GENERATE,
1675 .name = THERMAL_GENL_FAMILY_NAME,
1676 .version = THERMAL_GENL_VERSION,
1677 .maxattr = THERMAL_GENL_ATTR_MAX,
1678};
1679
1680static struct genl_multicast_group thermal_event_mcgrp = {
1681 .name = THERMAL_GENL_MCAST_GROUP_NAME,
1682};
1683
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001684int thermal_generate_netlink_event(u32 orig, enum events event)
R.Durgadoss4cb18722010-10-27 03:33:29 +05301685{
1686 struct sk_buff *skb;
1687 struct nlattr *attr;
1688 struct thermal_genl_event *thermal_event;
1689 void *msg_header;
1690 int size;
1691 int result;
Fabio Estevamb11de072012-03-21 12:55:00 -07001692 static unsigned int thermal_event_seqnum;
R.Durgadoss4cb18722010-10-27 03:33:29 +05301693
1694 /* allocate memory */
Joe Perches886ee542012-03-21 12:55:01 -07001695 size = nla_total_size(sizeof(struct thermal_genl_event)) +
1696 nla_total_size(0);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301697
1698 skb = genlmsg_new(size, GFP_ATOMIC);
1699 if (!skb)
1700 return -ENOMEM;
1701
1702 /* add the genetlink message header */
1703 msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
1704 &thermal_event_genl_family, 0,
1705 THERMAL_GENL_CMD_EVENT);
1706 if (!msg_header) {
1707 nlmsg_free(skb);
1708 return -ENOMEM;
1709 }
1710
1711 /* fill the data */
Joe Perches886ee542012-03-21 12:55:01 -07001712 attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
1713 sizeof(struct thermal_genl_event));
R.Durgadoss4cb18722010-10-27 03:33:29 +05301714
1715 if (!attr) {
1716 nlmsg_free(skb);
1717 return -EINVAL;
1718 }
1719
1720 thermal_event = nla_data(attr);
1721 if (!thermal_event) {
1722 nlmsg_free(skb);
1723 return -EINVAL;
1724 }
1725
1726 memset(thermal_event, 0, sizeof(struct thermal_genl_event));
1727
1728 thermal_event->orig = orig;
1729 thermal_event->event = event;
1730
1731 /* send multicast genetlink message */
1732 result = genlmsg_end(skb, msg_header);
1733 if (result < 0) {
1734 nlmsg_free(skb);
1735 return result;
1736 }
1737
1738 result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
1739 if (result)
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001740 pr_info("failed to send netlink event:%d\n", result);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301741
1742 return result;
1743}
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001744EXPORT_SYMBOL(thermal_generate_netlink_event);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301745
1746static int genetlink_init(void)
1747{
1748 int result;
1749
1750 result = genl_register_family(&thermal_event_genl_family);
1751 if (result)
1752 return result;
1753
1754 result = genl_register_mc_group(&thermal_event_genl_family,
1755 &thermal_event_mcgrp);
1756 if (result)
1757 genl_unregister_family(&thermal_event_genl_family);
1758 return result;
1759}
1760
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001761static void genetlink_exit(void)
1762{
1763 genl_unregister_family(&thermal_event_genl_family);
1764}
1765#else /* !CONFIG_NET */
1766static inline int genetlink_init(void) { return 0; }
1767static inline void genetlink_exit(void) {}
1768#endif /* !CONFIG_NET */
1769
Zhang Rui203d3d42008-01-17 15:51:08 +08001770static int __init thermal_init(void)
1771{
1772 int result = 0;
1773
1774 result = class_register(&thermal_class);
1775 if (result) {
1776 idr_destroy(&thermal_tz_idr);
1777 idr_destroy(&thermal_cdev_idr);
1778 mutex_destroy(&thermal_idr_lock);
1779 mutex_destroy(&thermal_list_lock);
1780 }
R.Durgadoss4cb18722010-10-27 03:33:29 +05301781 result = genetlink_init();
Zhang Rui203d3d42008-01-17 15:51:08 +08001782 return result;
1783}
1784
1785static void __exit thermal_exit(void)
1786{
1787 class_unregister(&thermal_class);
1788 idr_destroy(&thermal_tz_idr);
1789 idr_destroy(&thermal_cdev_idr);
1790 mutex_destroy(&thermal_idr_lock);
1791 mutex_destroy(&thermal_list_lock);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301792 genetlink_exit();
Zhang Rui203d3d42008-01-17 15:51:08 +08001793}
1794
R.Durgadoss4cb18722010-10-27 03:33:29 +05301795fs_initcall(thermal_init);
Zhang Rui203d3d42008-01-17 15:51:08 +08001796module_exit(thermal_exit);