blob: 739696dbc047a95769cc816ad39ebb8facf98581 [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
Ram Chandrasekarb2f16712013-10-02 11:06:42 -060092static int __update_sensor_thresholds(struct sensor_info *sensor)
Praveen Chidambaram3af26db2013-09-17 13:51:42 -060093{
Ram Chandrasekarb2f16712013-10-02 11:06:42 -060094 long max_of_low_thresh = LONG_MIN;
95 long min_of_high_thresh = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -060096 struct sensor_threshold *pos, *var;
97 enum thermal_trip_type type;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -060098 int i, ret = 0;
99
100 if (!sensor->tz->ops->set_trip_temp ||
101 !sensor->tz->ops->activate_trip_type ||
102 !sensor->tz->ops->get_trip_type ||
103 !sensor->tz->ops->get_trip_temp) {
104 ret = -ENODEV;
105 goto update_done;
106 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600107
108 for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
109 (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
110 i++) {
111 sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
112 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
113 sensor->max_idx = i;
114 if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
115 sensor->min_idx = i;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600116 sensor->tz->ops->get_trip_temp(sensor->tz,
117 THERMAL_TRIP_CONFIGURABLE_LOW, &sensor->threshold_min);
118 sensor->tz->ops->get_trip_temp(sensor->tz,
119 THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600120 }
121
Praveen Chidambarambb646212013-07-02 13:04:58 -0600122 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600123 if (!pos->active)
124 continue;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600125 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600126 if (pos->temp > max_of_low_thresh)
127 max_of_low_thresh = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600128 }
129 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_HI) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600130 if (pos->temp < min_of_high_thresh)
131 min_of_high_thresh = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600132 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600133 }
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600134
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600135 pr_debug("sensor %d: Thresholds: max of low: %ld min of high: %ld\n",
136 sensor->sensor_id, max_of_low_thresh,
137 min_of_high_thresh);
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600138
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600139 if ((min_of_high_thresh != sensor->threshold_max) &&
140 (min_of_high_thresh != LONG_MAX)) {
141 ret = sensor->tz->ops->set_trip_temp(sensor->tz,
142 sensor->max_idx, min_of_high_thresh);
143 if (ret) {
144 pr_err("sensor %d: Unable to set high threshold %d",
145 sensor->sensor_id, ret);
146 goto update_done;
147 }
148 sensor->threshold_max = min_of_high_thresh;
149 }
150 ret = sensor->tz->ops->activate_trip_type(sensor->tz,
151 sensor->max_idx,
152 (min_of_high_thresh == LONG_MAX) ?
153 THERMAL_TRIP_ACTIVATION_DISABLED :
154 THERMAL_TRIP_ACTIVATION_ENABLED);
155 if (ret) {
156 pr_err("sensor %d: Unable to activate high threshold %d",
157 sensor->sensor_id, ret);
158 goto update_done;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600159 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600160
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600161 if ((max_of_low_thresh != sensor->threshold_min) &&
162 (max_of_low_thresh != LONG_MIN)) {
163 ret = sensor->tz->ops->set_trip_temp(sensor->tz,
164 sensor->min_idx, max_of_low_thresh);
165 if (ret) {
166 pr_err("sensor %d: Unable to set low threshold %d",
167 sensor->sensor_id, ret);
168 goto update_done;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600169 }
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600170 sensor->threshold_min = max_of_low_thresh;
171 }
172 ret = sensor->tz->ops->activate_trip_type(sensor->tz,
173 sensor->min_idx,
174 (max_of_low_thresh == LONG_MIN) ?
175 THERMAL_TRIP_ACTIVATION_DISABLED :
176 THERMAL_TRIP_ACTIVATION_ENABLED);
177 if (ret) {
178 pr_err("sensor %d: Unable to activate low threshold %d",
179 sensor->sensor_id, ret);
180 goto update_done;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600181 }
182
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600183 pr_debug("sensor %d: low: %ld high: %ld\n",
184 sensor->sensor_id,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600185 sensor->threshold_min, sensor->threshold_max);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600186
187update_done:
188 return ret;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600189}
190
191static void sensor_update_work(struct work_struct *work)
192{
193 struct sensor_info *sensor = container_of(work, struct sensor_info,
194 work);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600195 int ret = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600196 mutex_lock(&sensor->lock);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600197 ret = __update_sensor_thresholds(sensor);
198 if (ret)
199 pr_err("sensor %d: Error %d setting threshold\n",
200 sensor->sensor_id, ret);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600201 mutex_unlock(&sensor->lock);
202}
203
204/* May be called in an interrupt context.
205 * Do NOT call sensor_set_trip from this function
206 */
207int thermal_sensor_trip(struct thermal_zone_device *tz,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600208 enum thermal_trip_type trip, long temp)
Praveen Chidambarambb646212013-07-02 13:04:58 -0600209{
210 struct sensor_threshold *pos, *var;
211 int ret = -ENODEV;
212
213 if (trip != THERMAL_TRIP_CONFIGURABLE_HI &&
214 trip != THERMAL_TRIP_CONFIGURABLE_LOW)
215 return 0;
216
217 if (list_empty(&tz->sensor.threshold_list))
218 return 0;
219
220 list_for_each_entry_safe(pos, var, &tz->sensor.threshold_list, list) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600221 if ((pos->trip != trip) || (!pos->active))
Praveen Chidambarambb646212013-07-02 13:04:58 -0600222 continue;
223 if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
224 (pos->temp <= tz->sensor.threshold_min) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600225 (pos->temp >= temp)) ||
Praveen Chidambarambb646212013-07-02 13:04:58 -0600226 ((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
227 (pos->temp >= tz->sensor.threshold_max) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600228 (pos->temp <= temp))) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600229 pos->active = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600230 pos->notify(trip, temp, pos->data);
231 }
232 }
233
234 schedule_work(&tz->sensor.work);
235
236 return ret;
237}
238EXPORT_SYMBOL(thermal_sensor_trip);
239
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600240int sensor_activate_trip(uint32_t sensor_id,
241 struct sensor_threshold *threshold, bool enable)
242{
243 struct sensor_info *sensor = get_sensor(sensor_id);
244 int ret = 0;
245
246 if (!sensor || !threshold) {
247 pr_err("Sensor %d: uninitialized data\n",
248 sensor_id);
249 ret = -ENODEV;
250 goto activate_trip_exit;
251 }
252
253 mutex_lock(&sensor->lock);
254 threshold->active = (enable) ? 1 : 0;
255 ret = __update_sensor_thresholds(sensor);
256 mutex_unlock(&sensor->lock);
257
258activate_trip_exit:
259 return ret;
260}
261EXPORT_SYMBOL(sensor_activate_trip);
262
Praveen Chidambarambb646212013-07-02 13:04:58 -0600263int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
264{
265 struct sensor_threshold *pos, *var;
266 struct sensor_info *sensor = get_sensor(sensor_id);
267
268 if (!sensor)
269 return -ENODEV;
270
271 if (!threshold || !threshold->notify)
272 return -EFAULT;
273
274 mutex_lock(&sensor->lock);
275 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
276 if (pos == threshold)
277 break;
278 }
279
280 if (pos != threshold) {
281 INIT_LIST_HEAD(&threshold->list);
282 list_add(&threshold->list, &sensor->threshold_list);
283 }
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600284 threshold->active = 0; /* Do not allow active threshold right away */
Praveen Chidambarambb646212013-07-02 13:04:58 -0600285 mutex_unlock(&sensor->lock);
286
287 return 0;
288
289}
290EXPORT_SYMBOL(sensor_set_trip);
291
292int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
293{
294 struct sensor_threshold *pos, *var;
295 struct sensor_info *sensor = get_sensor(sensor_id);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600296 int ret = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600297
298 if (!sensor)
299 return -ENODEV;
300
301 mutex_lock(&sensor->lock);
302 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
303 if (pos == threshold) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600304 pos->active = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600305 list_del(&pos->list);
306 break;
307 }
308 }
309
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600310 ret = __update_sensor_thresholds(sensor);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600311 mutex_unlock(&sensor->lock);
312
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600313 return ret;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600314}
315EXPORT_SYMBOL(sensor_cancel_trip);
316
Praveen Chidambarambb646212013-07-02 13:04:58 -0600317static int tz_notify_trip(enum thermal_trip_type type, int temp, void *data)
318{
319 struct thermal_zone_device *tz = (struct thermal_zone_device *)data;
320
321 pr_debug("sensor %d tripped: type %d temp %d\n",
322 tz->sensor.sensor_id, type, temp);
323
324 return 0;
325}
326
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600327static void get_trip_threshold(struct thermal_zone_device *tz, int trip,
328 struct sensor_threshold **threshold)
329{
330 enum thermal_trip_type type;
331
332 tz->ops->get_trip_type(tz, trip, &type);
333
334 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
335 *threshold = &tz->tz_threshold[0];
336 else if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
337 *threshold = &tz->tz_threshold[1];
338 else
339 *threshold = NULL;
340}
341
Praveen Chidambarambb646212013-07-02 13:04:58 -0600342int sensor_set_trip_temp(struct thermal_zone_device *tz,
343 int trip, long temp)
344{
345 int ret = 0;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600346 struct sensor_threshold *threshold = NULL;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600347
348 if (!tz->ops->get_trip_type)
349 return -EPERM;
350
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600351 get_trip_threshold(tz, trip, &threshold);
352 if (threshold) {
353 threshold->temp = temp;
354 ret = sensor_set_trip(tz->sensor.sensor_id, threshold);
355 } else {
Praveen Chidambarambb646212013-07-02 13:04:58 -0600356 ret = tz->ops->set_trip_temp(tz, trip, temp);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600357 }
358
359 return ret;
360}
361
362int sensor_init(struct thermal_zone_device *tz)
363{
364 struct sensor_info *sensor = &tz->sensor;
365
366 sensor->sensor_id = tz->id;
367 sensor->tz = tz;
368 sensor->threshold_min = 0;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600369 sensor->threshold_max = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600370 sensor->max_idx = -1;
371 sensor->min_idx = -1;
372 mutex_init(&sensor->lock);
373 INIT_LIST_HEAD(&sensor->sensor_list);
374 INIT_LIST_HEAD(&sensor->threshold_list);
375 INIT_LIST_HEAD(&tz->tz_threshold[0].list);
376 INIT_LIST_HEAD(&tz->tz_threshold[1].list);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600377 tz->tz_threshold[0].notify = tz_notify_trip;
378 tz->tz_threshold[0].data = tz;
379 tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
380 tz->tz_threshold[1].notify = tz_notify_trip;
381 tz->tz_threshold[1].data = tz;
382 tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600383 list_add(&sensor->sensor_list, &sensor_info_list);
384 INIT_WORK(&sensor->work, sensor_update_work);
385
386 return 0;
387}
388
Zhang Rui203d3d42008-01-17 15:51:08 +0800389static int get_idr(struct idr *idr, struct mutex *lock, int *id)
390{
391 int err;
392
Joe Perchescaca8b82012-03-21 12:55:02 -0700393again:
Zhang Rui203d3d42008-01-17 15:51:08 +0800394 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
395 return -ENOMEM;
396
397 if (lock)
398 mutex_lock(lock);
399 err = idr_get_new(idr, NULL, id);
400 if (lock)
401 mutex_unlock(lock);
402 if (unlikely(err == -EAGAIN))
403 goto again;
404 else if (unlikely(err))
405 return err;
406
407 *id = *id & MAX_ID_MASK;
408 return 0;
409}
410
411static void release_idr(struct idr *idr, struct mutex *lock, int id)
412{
413 if (lock)
414 mutex_lock(lock);
415 idr_remove(idr, id);
416 if (lock)
417 mutex_unlock(lock);
418}
419
420/* sys I/F for thermal zone */
421
422#define to_thermal_zone(_dev) \
423 container_of(_dev, struct thermal_zone_device, device)
424
425static ssize_t
426type_show(struct device *dev, struct device_attribute *attr, char *buf)
427{
428 struct thermal_zone_device *tz = to_thermal_zone(dev);
429
430 return sprintf(buf, "%s\n", tz->type);
431}
432
433static ssize_t
434temp_show(struct device *dev, struct device_attribute *attr, char *buf)
435{
436 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000437 long temperature;
438 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800439
440 if (!tz->ops->get_temp)
441 return -EPERM;
442
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000443 ret = tz->ops->get_temp(tz, &temperature);
444
445 if (ret)
446 return ret;
447
448 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800449}
450
451static ssize_t
452mode_show(struct device *dev, struct device_attribute *attr, char *buf)
453{
454 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000455 enum thermal_device_mode mode;
456 int result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800457
458 if (!tz->ops->get_mode)
459 return -EPERM;
460
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000461 result = tz->ops->get_mode(tz, &mode);
462 if (result)
463 return result;
464
465 return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
466 : "disabled");
Zhang Rui203d3d42008-01-17 15:51:08 +0800467}
468
469static ssize_t
470mode_store(struct device *dev, struct device_attribute *attr,
471 const char *buf, size_t count)
472{
473 struct thermal_zone_device *tz = to_thermal_zone(dev);
474 int result;
475
476 if (!tz->ops->set_mode)
477 return -EPERM;
478
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530479 if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000480 result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530481 else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000482 result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
483 else
484 result = -EINVAL;
485
Zhang Rui203d3d42008-01-17 15:51:08 +0800486 if (result)
487 return result;
488
489 return count;
490}
491
492static ssize_t
493trip_point_type_show(struct device *dev, struct device_attribute *attr,
494 char *buf)
495{
496 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000497 enum thermal_trip_type type;
498 int trip, result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800499
500 if (!tz->ops->get_trip_type)
501 return -EPERM;
502
503 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
504 return -EINVAL;
505
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000506 result = tz->ops->get_trip_type(tz, trip, &type);
507 if (result)
508 return result;
509
510 switch (type) {
511 case THERMAL_TRIP_CRITICAL:
Amit Kucheria625120a2009-10-16 12:46:02 +0300512 return sprintf(buf, "critical\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000513 case THERMAL_TRIP_HOT:
Amit Kucheria625120a2009-10-16 12:46:02 +0300514 return sprintf(buf, "hot\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515 case THERMAL_TRIP_CONFIGURABLE_HI:
516 return sprintf(buf, "configurable_hi\n");
517 case THERMAL_TRIP_CONFIGURABLE_LOW:
518 return sprintf(buf, "configurable_low\n");
519 case THERMAL_TRIP_CRITICAL_LOW:
520 return sprintf(buf, "critical_low\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000521 case THERMAL_TRIP_PASSIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300522 return sprintf(buf, "passive\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000523 case THERMAL_TRIP_ACTIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300524 return sprintf(buf, "active\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000525 default:
Amit Kucheria625120a2009-10-16 12:46:02 +0300526 return sprintf(buf, "unknown\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000527 }
Zhang Rui203d3d42008-01-17 15:51:08 +0800528}
529
530static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531trip_point_type_activate(struct device *dev, struct device_attribute *attr,
532 const char *buf, size_t count)
533{
534 struct thermal_zone_device *tz = to_thermal_zone(dev);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600535 int trip, result = 0;
536 bool activate;
537 struct sensor_threshold *threshold = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600539 if (!tz->ops->get_trip_type ||
540 !tz->ops->activate_trip_type) {
541 result = -EPERM;
542 goto trip_activate_exit;
543 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600545 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700546 result = -EINVAL;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600547 goto trip_activate_exit;
548 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600550 if (!strcmp(buf, "enabled")) {
551 activate = true;
552 } else if (!strcmp(buf, "disabled")) {
553 activate = false;
554 } else {
555 result = -EINVAL;
556 goto trip_activate_exit;
557 }
558
559 get_trip_threshold(tz, trip, &threshold);
560 if (threshold)
561 result = sensor_activate_trip(tz->sensor.sensor_id,
562 threshold, activate);
563 else
564 result = tz->ops->activate_trip_type(tz, trip,
565 activate ? THERMAL_TRIP_ACTIVATION_ENABLED :
566 THERMAL_TRIP_ACTIVATION_DISABLED);
567
568trip_activate_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700569 if (result)
570 return result;
571
572 return count;
573}
574
575static ssize_t
Zhang Rui203d3d42008-01-17 15:51:08 +0800576trip_point_temp_show(struct device *dev, struct device_attribute *attr,
577 char *buf)
578{
579 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000580 int trip, ret;
581 long temperature;
Zhang Rui203d3d42008-01-17 15:51:08 +0800582
583 if (!tz->ops->get_trip_temp)
584 return -EPERM;
585
586 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
587 return -EINVAL;
588
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600589 ret = tz->ops->get_trip_temp(tz, trip, &temperature);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000590
591 if (ret)
592 return ret;
593
594 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800595}
596
Matthew Garrett03a971a2008-12-03 18:00:38 +0000597static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598trip_point_temp_set(struct device *dev, struct device_attribute *attr,
599 const char *buf, size_t count)
600{
601 struct thermal_zone_device *tz = to_thermal_zone(dev);
602 int trip, ret;
603 long temperature;
604
605 if (!tz->ops->set_trip_temp)
606 return -EPERM;
607
608 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
609 return -EINVAL;
610
611 if (!sscanf(buf, "%ld", &temperature))
612 return -EINVAL;
613
Praveen Chidambarambb646212013-07-02 13:04:58 -0600614 ret = sensor_set_trip_temp(tz, trip, temperature);
615
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700616 if (ret)
617 return ret;
618
619 return count;
620}
621
622static ssize_t
Matthew Garrett03a971a2008-12-03 18:00:38 +0000623passive_store(struct device *dev, struct device_attribute *attr,
624 const char *buf, size_t count)
625{
626 struct thermal_zone_device *tz = to_thermal_zone(dev);
627 struct thermal_cooling_device *cdev = NULL;
628 int state;
629
630 if (!sscanf(buf, "%d\n", &state))
631 return -EINVAL;
632
Frans Pop3d8e3ad2009-10-26 08:39:02 +0100633 /* sanity check: values below 1000 millicelcius don't make sense
634 * and can cause the system to go into a thermal heart attack
635 */
636 if (state && state < 1000)
637 return -EINVAL;
638
Matthew Garrett03a971a2008-12-03 18:00:38 +0000639 if (state && !tz->forced_passive) {
640 mutex_lock(&thermal_list_lock);
641 list_for_each_entry(cdev, &thermal_cdev_list, node) {
642 if (!strncmp("Processor", cdev->type,
643 sizeof("Processor")))
644 thermal_zone_bind_cooling_device(tz,
645 THERMAL_TRIPS_NONE,
646 cdev);
647 }
648 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100649 if (!tz->passive_delay)
650 tz->passive_delay = 1000;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000651 } else if (!state && tz->forced_passive) {
652 mutex_lock(&thermal_list_lock);
653 list_for_each_entry(cdev, &thermal_cdev_list, node) {
654 if (!strncmp("Processor", cdev->type,
655 sizeof("Processor")))
656 thermal_zone_unbind_cooling_device(tz,
657 THERMAL_TRIPS_NONE,
658 cdev);
659 }
660 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100661 tz->passive_delay = 0;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000662 }
663
664 tz->tc1 = 1;
665 tz->tc2 = 1;
666
Matthew Garrett03a971a2008-12-03 18:00:38 +0000667 tz->forced_passive = state;
668
669 thermal_zone_device_update(tz);
670
671 return count;
672}
673
674static ssize_t
675passive_show(struct device *dev, struct device_attribute *attr,
676 char *buf)
677{
678 struct thermal_zone_device *tz = to_thermal_zone(dev);
679
680 return sprintf(buf, "%d\n", tz->forced_passive);
681}
682
Zhang Rui203d3d42008-01-17 15:51:08 +0800683static DEVICE_ATTR(type, 0444, type_show, NULL);
684static DEVICE_ATTR(temp, 0444, temp_show, NULL);
685static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
Joe Perches886ee542012-03-21 12:55:01 -0700686static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
Zhang Rui203d3d42008-01-17 15:51:08 +0800687
688static struct device_attribute trip_point_attrs[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689 __ATTR(trip_point_0_type, 0644, trip_point_type_show,
690 trip_point_type_activate),
691 __ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
692 trip_point_temp_set),
693 __ATTR(trip_point_1_type, 0644, trip_point_type_show,
694 trip_point_type_activate),
695 __ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
696 trip_point_temp_set),
697 __ATTR(trip_point_2_type, 0644, trip_point_type_show,
698 trip_point_type_activate),
699 __ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
700 trip_point_temp_set),
701 __ATTR(trip_point_3_type, 0644, trip_point_type_show,
702 trip_point_type_activate),
703 __ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
704 trip_point_temp_set),
705 __ATTR(trip_point_4_type, 0644, trip_point_type_show,
706 trip_point_type_activate),
707 __ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
708 trip_point_temp_set),
709 __ATTR(trip_point_5_type, 0644, trip_point_type_show,
710 trip_point_type_activate),
711 __ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
712 trip_point_temp_set),
713 __ATTR(trip_point_6_type, 0644, trip_point_type_show,
714 trip_point_type_activate),
715 __ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
716 trip_point_temp_set),
717 __ATTR(trip_point_7_type, 0644, trip_point_type_show,
718 trip_point_type_activate),
719 __ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
720 trip_point_temp_set),
721 __ATTR(trip_point_8_type, 0644, trip_point_type_show,
722 trip_point_type_activate),
723 __ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
724 trip_point_temp_set),
725 __ATTR(trip_point_9_type, 0644, trip_point_type_show,
726 trip_point_type_activate),
727 __ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
728 trip_point_temp_set),
729 __ATTR(trip_point_10_type, 0644, trip_point_type_show,
730 trip_point_type_activate),
731 __ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
732 trip_point_temp_set),
733 __ATTR(trip_point_11_type, 0644, trip_point_type_show,
734 trip_point_type_activate),
735 __ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
736 trip_point_temp_set),
Zhang Rui203d3d42008-01-17 15:51:08 +0800737};
738
Zhang Rui203d3d42008-01-17 15:51:08 +0800739/* sys I/F for cooling device */
740#define to_cooling_device(_dev) \
741 container_of(_dev, struct thermal_cooling_device, device)
742
743static ssize_t
744thermal_cooling_device_type_show(struct device *dev,
745 struct device_attribute *attr, char *buf)
746{
747 struct thermal_cooling_device *cdev = to_cooling_device(dev);
748
749 return sprintf(buf, "%s\n", cdev->type);
750}
751
752static ssize_t
753thermal_cooling_device_max_state_show(struct device *dev,
754 struct device_attribute *attr, char *buf)
755{
756 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000757 unsigned long state;
758 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800759
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000760 ret = cdev->ops->get_max_state(cdev, &state);
761 if (ret)
762 return ret;
763 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800764}
765
766static ssize_t
767thermal_cooling_device_cur_state_show(struct device *dev,
768 struct device_attribute *attr, char *buf)
769{
770 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000771 unsigned long state;
772 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800773
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000774 ret = cdev->ops->get_cur_state(cdev, &state);
775 if (ret)
776 return ret;
777 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800778}
779
780static ssize_t
781thermal_cooling_device_cur_state_store(struct device *dev,
782 struct device_attribute *attr,
783 const char *buf, size_t count)
784{
785 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000786 unsigned long state;
Zhang Rui203d3d42008-01-17 15:51:08 +0800787 int result;
788
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000789 if (!sscanf(buf, "%ld\n", &state))
Zhang Rui203d3d42008-01-17 15:51:08 +0800790 return -EINVAL;
791
Roel Kluinedb94912009-12-15 22:46:50 +0100792 if ((long)state < 0)
Zhang Rui203d3d42008-01-17 15:51:08 +0800793 return -EINVAL;
794
795 result = cdev->ops->set_cur_state(cdev, state);
796 if (result)
797 return result;
798 return count;
799}
800
801static struct device_attribute dev_attr_cdev_type =
Len Brown543a9562008-02-07 16:55:08 -0500802__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800803static DEVICE_ATTR(max_state, 0444,
804 thermal_cooling_device_max_state_show, NULL);
805static DEVICE_ATTR(cur_state, 0644,
806 thermal_cooling_device_cur_state_show,
807 thermal_cooling_device_cur_state_store);
808
809static ssize_t
810thermal_cooling_device_trip_point_show(struct device *dev,
Len Brown543a9562008-02-07 16:55:08 -0500811 struct device_attribute *attr, char *buf)
Zhang Rui203d3d42008-01-17 15:51:08 +0800812{
813 struct thermal_cooling_device_instance *instance;
814
815 instance =
816 container_of(attr, struct thermal_cooling_device_instance, attr);
817
818 if (instance->trip == THERMAL_TRIPS_NONE)
819 return sprintf(buf, "-1\n");
820 else
821 return sprintf(buf, "%d\n", instance->trip);
822}
823
824/* Device management */
825
Rene Herman16d75232008-06-24 19:38:56 +0200826#if defined(CONFIG_THERMAL_HWMON)
827
Zhang Ruie68b16a2008-04-21 16:07:52 +0800828/* hwmon sys I/F */
829#include <linux/hwmon.h>
Jean Delvare31f53962011-07-28 13:48:42 -0700830
831/* thermal zone devices with the same type share one hwmon device */
832struct thermal_hwmon_device {
833 char type[THERMAL_NAME_LENGTH];
834 struct device *device;
835 int count;
836 struct list_head tz_list;
837 struct list_head node;
838};
839
840struct thermal_hwmon_attr {
841 struct device_attribute attr;
842 char name[16];
843};
844
845/* one temperature input for each thermal zone */
846struct thermal_hwmon_temp {
847 struct list_head hwmon_node;
848 struct thermal_zone_device *tz;
849 struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
850 struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
851};
852
Zhang Ruie68b16a2008-04-21 16:07:52 +0800853static LIST_HEAD(thermal_hwmon_list);
854
855static ssize_t
856name_show(struct device *dev, struct device_attribute *attr, char *buf)
857{
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700858 struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800859 return sprintf(buf, "%s\n", hwmon->type);
860}
861static DEVICE_ATTR(name, 0444, name_show, NULL);
862
863static ssize_t
864temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
865{
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000866 long temperature;
867 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800868 struct thermal_hwmon_attr *hwmon_attr
869 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700870 struct thermal_hwmon_temp *temp
871 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800872 temp_input);
Jean Delvare31f53962011-07-28 13:48:42 -0700873 struct thermal_zone_device *tz = temp->tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800874
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000875 ret = tz->ops->get_temp(tz, &temperature);
876
877 if (ret)
878 return ret;
879
880 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800881}
882
883static ssize_t
884temp_crit_show(struct device *dev, struct device_attribute *attr,
885 char *buf)
886{
887 struct thermal_hwmon_attr *hwmon_attr
888 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700889 struct thermal_hwmon_temp *temp
890 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800891 temp_crit);
Jean Delvare31f53962011-07-28 13:48:42 -0700892 struct thermal_zone_device *tz = temp->tz;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000893 long temperature;
894 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800895
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000896 ret = tz->ops->get_trip_temp(tz, 0, &temperature);
897 if (ret)
898 return ret;
899
900 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800901}
902
903
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700904static struct thermal_hwmon_device *
905thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
906{
907 struct thermal_hwmon_device *hwmon;
908
909 mutex_lock(&thermal_list_lock);
910 list_for_each_entry(hwmon, &thermal_hwmon_list, node)
911 if (!strcmp(hwmon->type, tz->type)) {
912 mutex_unlock(&thermal_list_lock);
913 return hwmon;
914 }
915 mutex_unlock(&thermal_list_lock);
916
917 return NULL;
918}
919
Jean Delvare31f53962011-07-28 13:48:42 -0700920/* Find the temperature input matching a given thermal zone */
921static struct thermal_hwmon_temp *
922thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
923 const struct thermal_zone_device *tz)
924{
925 struct thermal_hwmon_temp *temp;
926
927 mutex_lock(&thermal_list_lock);
928 list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
929 if (temp->tz == tz) {
930 mutex_unlock(&thermal_list_lock);
931 return temp;
932 }
933 mutex_unlock(&thermal_list_lock);
934
935 return NULL;
936}
937
Zhang Ruie68b16a2008-04-21 16:07:52 +0800938static int
939thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
940{
941 struct thermal_hwmon_device *hwmon;
Jean Delvare31f53962011-07-28 13:48:42 -0700942 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800943 int new_hwmon_device = 1;
944 int result;
945
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700946 hwmon = thermal_hwmon_lookup_by_type(tz);
947 if (hwmon) {
948 new_hwmon_device = 0;
949 goto register_sys_interface;
950 }
Zhang Ruie68b16a2008-04-21 16:07:52 +0800951
952 hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
953 if (!hwmon)
954 return -ENOMEM;
955
956 INIT_LIST_HEAD(&hwmon->tz_list);
957 strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
958 hwmon->device = hwmon_device_register(NULL);
959 if (IS_ERR(hwmon->device)) {
960 result = PTR_ERR(hwmon->device);
961 goto free_mem;
962 }
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700963 dev_set_drvdata(hwmon->device, hwmon);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800964 result = device_create_file(hwmon->device, &dev_attr_name);
965 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530966 goto free_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800967
968 register_sys_interface:
Jean Delvare31f53962011-07-28 13:48:42 -0700969 temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
970 if (!temp) {
971 result = -ENOMEM;
972 goto unregister_name;
973 }
974
975 temp->tz = tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800976 hwmon->count++;
977
Jean Delvare31f53962011-07-28 13:48:42 -0700978 snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800979 "temp%d_input", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700980 temp->temp_input.attr.attr.name = temp->temp_input.name;
981 temp->temp_input.attr.attr.mode = 0444;
982 temp->temp_input.attr.show = temp_input_show;
983 sysfs_attr_init(&temp->temp_input.attr.attr);
984 result = device_create_file(hwmon->device, &temp->temp_input.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800985 if (result)
Jean Delvare31f53962011-07-28 13:48:42 -0700986 goto free_temp_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800987
988 if (tz->ops->get_crit_temp) {
989 unsigned long temperature;
990 if (!tz->ops->get_crit_temp(tz, &temperature)) {
Jean Delvare31f53962011-07-28 13:48:42 -0700991 snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800992 "temp%d_crit", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700993 temp->temp_crit.attr.attr.name = temp->temp_crit.name;
994 temp->temp_crit.attr.attr.mode = 0444;
995 temp->temp_crit.attr.show = temp_crit_show;
996 sysfs_attr_init(&temp->temp_crit.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800997 result = device_create_file(hwmon->device,
Jean Delvare31f53962011-07-28 13:48:42 -0700998 &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800999 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +05301000 goto unregister_input;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001001 }
1002 }
1003
1004 mutex_lock(&thermal_list_lock);
1005 if (new_hwmon_device)
1006 list_add_tail(&hwmon->node, &thermal_hwmon_list);
Jean Delvare31f53962011-07-28 13:48:42 -07001007 list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001008 mutex_unlock(&thermal_list_lock);
1009
1010 return 0;
1011
Durgadoss Rb299eb52011-03-03 04:30:13 +05301012 unregister_input:
Jean Delvare31f53962011-07-28 13:48:42 -07001013 device_remove_file(hwmon->device, &temp->temp_input.attr);
1014 free_temp_mem:
1015 kfree(temp);
Durgadoss Rb299eb52011-03-03 04:30:13 +05301016 unregister_name:
Zhang Ruie68b16a2008-04-21 16:07:52 +08001017 if (new_hwmon_device) {
1018 device_remove_file(hwmon->device, &dev_attr_name);
1019 hwmon_device_unregister(hwmon->device);
1020 }
1021 free_mem:
1022 if (new_hwmon_device)
1023 kfree(hwmon);
1024
1025 return result;
1026}
1027
1028static void
1029thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1030{
Jean Delvare31f53962011-07-28 13:48:42 -07001031 struct thermal_hwmon_device *hwmon;
1032 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001033
Jean Delvare31f53962011-07-28 13:48:42 -07001034 hwmon = thermal_hwmon_lookup_by_type(tz);
1035 if (unlikely(!hwmon)) {
1036 /* Should never happen... */
1037 dev_dbg(&tz->device, "hwmon device lookup failed!\n");
1038 return;
1039 }
1040
1041 temp = thermal_hwmon_lookup_temp(hwmon, tz);
1042 if (unlikely(!temp)) {
1043 /* Should never happen... */
1044 dev_dbg(&tz->device, "temperature input lookup failed!\n");
1045 return;
1046 }
1047
1048 device_remove_file(hwmon->device, &temp->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +05301049 if (tz->ops->get_crit_temp)
Jean Delvare31f53962011-07-28 13:48:42 -07001050 device_remove_file(hwmon->device, &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001051
1052 mutex_lock(&thermal_list_lock);
Jean Delvare31f53962011-07-28 13:48:42 -07001053 list_del(&temp->hwmon_node);
1054 kfree(temp);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001055 if (!list_empty(&hwmon->tz_list)) {
1056 mutex_unlock(&thermal_list_lock);
1057 return;
1058 }
1059 list_del(&hwmon->node);
1060 mutex_unlock(&thermal_list_lock);
1061
1062 device_remove_file(hwmon->device, &dev_attr_name);
1063 hwmon_device_unregister(hwmon->device);
1064 kfree(hwmon);
1065}
1066#else
1067static int
1068thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
1069{
1070 return 0;
1071}
1072
1073static void
1074thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1075{
1076}
1077#endif
1078
Matthew Garrettb1569e92008-12-03 17:55:32 +00001079static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
1080 int delay)
1081{
1082 cancel_delayed_work(&(tz->poll_queue));
1083
1084 if (!delay)
1085 return;
1086
1087 if (delay > 1000)
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001088 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001089 round_jiffies(msecs_to_jiffies(delay)));
1090 else
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001091 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001092 msecs_to_jiffies(delay));
1093}
1094
1095static void thermal_zone_device_passive(struct thermal_zone_device *tz,
1096 int temp, int trip_temp, int trip)
1097{
1098 int trend = 0;
1099 struct thermal_cooling_device_instance *instance;
1100 struct thermal_cooling_device *cdev;
1101 long state, max_state;
1102
1103 /*
1104 * Above Trip?
1105 * -----------
1106 * Calculate the thermal trend (using the passive cooling equation)
1107 * and modify the performance limit for all passive cooling devices
1108 * accordingly. Note that we assume symmetry.
1109 */
1110 if (temp >= trip_temp) {
1111 tz->passive = true;
1112
1113 trend = (tz->tc1 * (temp - tz->last_temperature)) +
1114 (tz->tc2 * (temp - trip_temp));
1115
1116 /* Heating up? */
1117 if (trend > 0) {
1118 list_for_each_entry(instance, &tz->cooling_devices,
1119 node) {
1120 if (instance->trip != trip)
1121 continue;
1122 cdev = instance->cdev;
1123 cdev->ops->get_cur_state(cdev, &state);
1124 cdev->ops->get_max_state(cdev, &max_state);
1125 if (state++ < max_state)
1126 cdev->ops->set_cur_state(cdev, state);
1127 }
1128 } else if (trend < 0) { /* Cooling off? */
1129 list_for_each_entry(instance, &tz->cooling_devices,
1130 node) {
1131 if (instance->trip != trip)
1132 continue;
1133 cdev = instance->cdev;
1134 cdev->ops->get_cur_state(cdev, &state);
1135 cdev->ops->get_max_state(cdev, &max_state);
1136 if (state > 0)
1137 cdev->ops->set_cur_state(cdev, --state);
1138 }
1139 }
1140 return;
1141 }
1142
1143 /*
1144 * Below Trip?
1145 * -----------
1146 * Implement passive cooling hysteresis to slowly increase performance
1147 * and avoid thrashing around the passive trip point. Note that we
1148 * assume symmetry.
1149 */
1150 list_for_each_entry(instance, &tz->cooling_devices, node) {
1151 if (instance->trip != trip)
1152 continue;
1153 cdev = instance->cdev;
1154 cdev->ops->get_cur_state(cdev, &state);
1155 cdev->ops->get_max_state(cdev, &max_state);
1156 if (state > 0)
1157 cdev->ops->set_cur_state(cdev, --state);
1158 if (state == 0)
1159 tz->passive = false;
1160 }
1161}
1162
1163static void thermal_zone_device_check(struct work_struct *work)
1164{
1165 struct thermal_zone_device *tz = container_of(work, struct
1166 thermal_zone_device,
1167 poll_queue.work);
1168 thermal_zone_device_update(tz);
1169}
Zhang Ruie68b16a2008-04-21 16:07:52 +08001170
Zhang Rui203d3d42008-01-17 15:51:08 +08001171/**
1172 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001173 * @tz: thermal zone device
1174 * @trip: indicates which trip point the cooling devices is
1175 * associated with in this thermal zone.
1176 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001177 *
1178 * This function is usually called in the thermal zone device .bind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001179 */
1180int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
1181 int trip,
1182 struct thermal_cooling_device *cdev)
1183{
1184 struct thermal_cooling_device_instance *dev;
1185 struct thermal_cooling_device_instance *pos;
Thomas Sujithc7516702008-02-15 00:58:50 -05001186 struct thermal_zone_device *pos1;
1187 struct thermal_cooling_device *pos2;
Zhang Rui203d3d42008-01-17 15:51:08 +08001188 int result;
1189
Len Brown543a9562008-02-07 16:55:08 -05001190 if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
Zhang Rui203d3d42008-01-17 15:51:08 +08001191 return -EINVAL;
1192
Thomas Sujithc7516702008-02-15 00:58:50 -05001193 list_for_each_entry(pos1, &thermal_tz_list, node) {
1194 if (pos1 == tz)
1195 break;
1196 }
1197 list_for_each_entry(pos2, &thermal_cdev_list, node) {
1198 if (pos2 == cdev)
1199 break;
1200 }
1201
1202 if (tz != pos1 || cdev != pos2)
Zhang Rui203d3d42008-01-17 15:51:08 +08001203 return -EINVAL;
1204
1205 dev =
1206 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
1207 if (!dev)
1208 return -ENOMEM;
1209 dev->tz = tz;
1210 dev->cdev = cdev;
1211 dev->trip = trip;
1212 result = get_idr(&tz->idr, &tz->lock, &dev->id);
1213 if (result)
1214 goto free_mem;
1215
1216 sprintf(dev->name, "cdev%d", dev->id);
1217 result =
1218 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
1219 if (result)
1220 goto release_idr;
1221
1222 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -07001223 sysfs_attr_init(&dev->attr.attr);
Zhang Rui203d3d42008-01-17 15:51:08 +08001224 dev->attr.attr.name = dev->attr_name;
1225 dev->attr.attr.mode = 0444;
1226 dev->attr.show = thermal_cooling_device_trip_point_show;
1227 result = device_create_file(&tz->device, &dev->attr);
1228 if (result)
1229 goto remove_symbol_link;
1230
1231 mutex_lock(&tz->lock);
1232 list_for_each_entry(pos, &tz->cooling_devices, node)
1233 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
1234 result = -EEXIST;
1235 break;
1236 }
1237 if (!result)
1238 list_add_tail(&dev->node, &tz->cooling_devices);
1239 mutex_unlock(&tz->lock);
1240
1241 if (!result)
1242 return 0;
1243
1244 device_remove_file(&tz->device, &dev->attr);
Joe Perchescaca8b82012-03-21 12:55:02 -07001245remove_symbol_link:
Zhang Rui203d3d42008-01-17 15:51:08 +08001246 sysfs_remove_link(&tz->device.kobj, dev->name);
Joe Perchescaca8b82012-03-21 12:55:02 -07001247release_idr:
Zhang Rui203d3d42008-01-17 15:51:08 +08001248 release_idr(&tz->idr, &tz->lock, dev->id);
Joe Perchescaca8b82012-03-21 12:55:02 -07001249free_mem:
Zhang Rui203d3d42008-01-17 15:51:08 +08001250 kfree(dev);
1251 return result;
1252}
1253EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
1254
1255/**
1256 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001257 * @tz: thermal zone device
1258 * @trip: indicates which trip point the cooling devices is
1259 * associated with in this thermal zone.
1260 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001261 *
1262 * This function is usually called in the thermal zone device .unbind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001263 */
1264int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
1265 int trip,
1266 struct thermal_cooling_device *cdev)
1267{
1268 struct thermal_cooling_device_instance *pos, *next;
1269
1270 mutex_lock(&tz->lock);
1271 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
Len Brown543a9562008-02-07 16:55:08 -05001272 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001273 list_del(&pos->node);
1274 mutex_unlock(&tz->lock);
1275 goto unbind;
1276 }
1277 }
1278 mutex_unlock(&tz->lock);
1279
1280 return -ENODEV;
1281
Joe Perchescaca8b82012-03-21 12:55:02 -07001282unbind:
Zhang Rui203d3d42008-01-17 15:51:08 +08001283 device_remove_file(&tz->device, &pos->attr);
1284 sysfs_remove_link(&tz->device.kobj, pos->name);
1285 release_idr(&tz->idr, &tz->lock, pos->id);
1286 kfree(pos);
1287 return 0;
1288}
1289EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
1290
1291static void thermal_release(struct device *dev)
1292{
1293 struct thermal_zone_device *tz;
1294 struct thermal_cooling_device *cdev;
1295
Joe Perchescaca8b82012-03-21 12:55:02 -07001296 if (!strncmp(dev_name(dev), "thermal_zone",
1297 sizeof("thermal_zone") - 1)) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001298 tz = to_thermal_zone(dev);
1299 kfree(tz);
1300 } else {
1301 cdev = to_cooling_device(dev);
1302 kfree(cdev);
1303 }
1304}
1305
1306static struct class thermal_class = {
1307 .name = "thermal",
1308 .dev_release = thermal_release,
1309};
1310
1311/**
1312 * thermal_cooling_device_register - register a new thermal cooling device
1313 * @type: the thermal cooling device type.
1314 * @devdata: device private data.
1315 * @ops: standard thermal cooling devices callbacks.
1316 */
Joe Perchescaca8b82012-03-21 12:55:02 -07001317struct thermal_cooling_device *
1318thermal_cooling_device_register(char *type, void *devdata,
1319 const struct thermal_cooling_device_ops *ops)
Zhang Rui203d3d42008-01-17 15:51:08 +08001320{
1321 struct thermal_cooling_device *cdev;
1322 struct thermal_zone_device *pos;
1323 int result;
1324
1325 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001326 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001327
1328 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
Len Brown543a9562008-02-07 16:55:08 -05001329 !ops->set_cur_state)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001330 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001331
1332 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
1333 if (!cdev)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001334 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001335
1336 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
1337 if (result) {
1338 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001339 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001340 }
1341
1342 strcpy(cdev->type, type);
1343 cdev->ops = ops;
1344 cdev->device.class = &thermal_class;
1345 cdev->devdata = devdata;
Kay Sievers354655e2009-01-06 10:44:37 -08001346 dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001347 result = device_register(&cdev->device);
1348 if (result) {
1349 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1350 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001351 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001352 }
1353
1354 /* sys I/F */
1355 if (type) {
Len Brown543a9562008-02-07 16:55:08 -05001356 result = device_create_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001357 if (result)
1358 goto unregister;
1359 }
1360
1361 result = device_create_file(&cdev->device, &dev_attr_max_state);
1362 if (result)
1363 goto unregister;
1364
1365 result = device_create_file(&cdev->device, &dev_attr_cur_state);
1366 if (result)
1367 goto unregister;
1368
1369 mutex_lock(&thermal_list_lock);
1370 list_add(&cdev->node, &thermal_cdev_list);
1371 list_for_each_entry(pos, &thermal_tz_list, node) {
1372 if (!pos->ops->bind)
1373 continue;
1374 result = pos->ops->bind(pos, cdev);
1375 if (result)
1376 break;
1377
1378 }
1379 mutex_unlock(&thermal_list_lock);
1380
1381 if (!result)
1382 return cdev;
1383
Joe Perchescaca8b82012-03-21 12:55:02 -07001384unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001385 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1386 device_unregister(&cdev->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001387 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001388}
1389EXPORT_SYMBOL(thermal_cooling_device_register);
1390
1391/**
1392 * thermal_cooling_device_unregister - removes the registered thermal cooling device
Zhang Rui203d3d42008-01-17 15:51:08 +08001393 * @cdev: the thermal cooling device to remove.
1394 *
1395 * thermal_cooling_device_unregister() must be called when the device is no
1396 * longer needed.
1397 */
1398void thermal_cooling_device_unregister(struct
1399 thermal_cooling_device
1400 *cdev)
1401{
1402 struct thermal_zone_device *tz;
1403 struct thermal_cooling_device *pos = NULL;
1404
1405 if (!cdev)
1406 return;
1407
1408 mutex_lock(&thermal_list_lock);
1409 list_for_each_entry(pos, &thermal_cdev_list, node)
1410 if (pos == cdev)
1411 break;
1412 if (pos != cdev) {
1413 /* thermal cooling device not found */
1414 mutex_unlock(&thermal_list_lock);
1415 return;
1416 }
1417 list_del(&cdev->node);
1418 list_for_each_entry(tz, &thermal_tz_list, node) {
1419 if (!tz->ops->unbind)
1420 continue;
1421 tz->ops->unbind(tz, cdev);
1422 }
1423 mutex_unlock(&thermal_list_lock);
1424 if (cdev->type[0])
Len Brown543a9562008-02-07 16:55:08 -05001425 device_remove_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001426 device_remove_file(&cdev->device, &dev_attr_max_state);
1427 device_remove_file(&cdev->device, &dev_attr_cur_state);
1428
1429 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1430 device_unregister(&cdev->device);
1431 return;
1432}
1433EXPORT_SYMBOL(thermal_cooling_device_unregister);
1434
1435/**
Matthew Garrettb1569e92008-12-03 17:55:32 +00001436 * thermal_zone_device_update - force an update of a thermal zone's state
1437 * @ttz: the thermal zone to update
1438 */
1439
1440void thermal_zone_device_update(struct thermal_zone_device *tz)
1441{
1442 int count, ret = 0;
1443 long temp, trip_temp;
1444 enum thermal_trip_type trip_type;
1445 struct thermal_cooling_device_instance *instance;
1446 struct thermal_cooling_device *cdev;
1447
1448 mutex_lock(&tz->lock);
1449
Michael Brunner0d288162009-08-26 14:29:25 -07001450 if (tz->ops->get_temp(tz, &temp)) {
1451 /* get_temp failed - retry it later */
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001452 pr_warn("failed to read out thermal zone %d\n", tz->id);
Michael Brunner0d288162009-08-26 14:29:25 -07001453 goto leave;
1454 }
Matthew Garrettb1569e92008-12-03 17:55:32 +00001455
1456 for (count = 0; count < tz->trips; count++) {
1457 tz->ops->get_trip_type(tz, count, &trip_type);
1458 tz->ops->get_trip_temp(tz, count, &trip_temp);
1459
1460 switch (trip_type) {
1461 case THERMAL_TRIP_CRITICAL:
Vladimir Zajac29321352009-05-06 19:34:21 +02001462 if (temp >= trip_temp) {
Matthew Garrettb1569e92008-12-03 17:55:32 +00001463 if (tz->ops->notify)
1464 ret = tz->ops->notify(tz, count,
1465 trip_type);
1466 if (!ret) {
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001467 pr_emerg("Critical temperature reached (%ld C), shutting down\n",
1468 temp/1000);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001469 orderly_poweroff(true);
1470 }
1471 }
1472 break;
1473 case THERMAL_TRIP_HOT:
Vladimir Zajac29321352009-05-06 19:34:21 +02001474 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001475 if (tz->ops->notify)
1476 tz->ops->notify(tz, count, trip_type);
1477 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001478 case THERMAL_TRIP_CONFIGURABLE_HI:
1479 if (temp >= trip_temp)
1480 if (tz->ops->notify)
1481 tz->ops->notify(tz, count, trip_type);
1482 break;
1483 case THERMAL_TRIP_CONFIGURABLE_LOW:
1484 if (temp <= trip_temp)
1485 if (tz->ops->notify)
1486 tz->ops->notify(tz, count, trip_type);
1487 break;
1488 case THERMAL_TRIP_CRITICAL_LOW:
1489 if (temp <= trip_temp) {
1490 if (tz->ops->notify)
1491 ret = tz->ops->notify(tz, count,
1492 trip_type);
1493 if (!ret) {
1494 printk(KERN_EMERG
1495 "Critical temperature reached (%ld C), \
1496 shutting down.\n", temp/1000);
1497 orderly_poweroff(true);
1498 }
1499 }
1500 break;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001501 case THERMAL_TRIP_ACTIVE:
1502 list_for_each_entry(instance, &tz->cooling_devices,
1503 node) {
1504 if (instance->trip != count)
1505 continue;
1506
1507 cdev = instance->cdev;
1508
Vladimir Zajac29321352009-05-06 19:34:21 +02001509 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001510 cdev->ops->set_cur_state(cdev, 1);
1511 else
1512 cdev->ops->set_cur_state(cdev, 0);
1513 }
1514 break;
1515 case THERMAL_TRIP_PASSIVE:
Vladimir Zajac29321352009-05-06 19:34:21 +02001516 if (temp >= trip_temp || tz->passive)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001517 thermal_zone_device_passive(tz, temp,
1518 trip_temp, count);
1519 break;
1520 }
1521 }
Matthew Garrett03a971a2008-12-03 18:00:38 +00001522
1523 if (tz->forced_passive)
1524 thermal_zone_device_passive(tz, temp, tz->forced_passive,
1525 THERMAL_TRIPS_NONE);
1526
Matthew Garrettb1569e92008-12-03 17:55:32 +00001527 tz->last_temperature = temp;
Michael Brunner0d288162009-08-26 14:29:25 -07001528
Joe Perchescaca8b82012-03-21 12:55:02 -07001529leave:
Matthew Garrettb1569e92008-12-03 17:55:32 +00001530 if (tz->passive)
1531 thermal_zone_device_set_polling(tz, tz->passive_delay);
1532 else if (tz->polling_delay)
1533 thermal_zone_device_set_polling(tz, tz->polling_delay);
Frans Pop3767cb52009-10-26 08:39:04 +01001534 else
1535 thermal_zone_device_set_polling(tz, 0);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001536 mutex_unlock(&tz->lock);
1537}
1538EXPORT_SYMBOL(thermal_zone_device_update);
1539
1540/**
Zhang Rui203d3d42008-01-17 15:51:08 +08001541 * thermal_zone_device_register - register a new thermal zone device
1542 * @type: the thermal zone device type
1543 * @trips: the number of trip points the thermal zone support
1544 * @devdata: private device data
1545 * @ops: standard thermal zone device callbacks
Matthew Garrettb1569e92008-12-03 17:55:32 +00001546 * @tc1: thermal coefficient 1 for passive calculations
1547 * @tc2: thermal coefficient 2 for passive calculations
1548 * @passive_delay: number of milliseconds to wait between polls when
1549 * performing passive cooling
1550 * @polling_delay: number of milliseconds to wait between polls when checking
1551 * whether trip points have been crossed (0 for interrupt
1552 * driven systems)
Zhang Rui203d3d42008-01-17 15:51:08 +08001553 *
1554 * thermal_zone_device_unregister() must be called when the device is no
Matthew Garrettb1569e92008-12-03 17:55:32 +00001555 * longer needed. The passive cooling formula uses tc1 and tc2 as described in
1556 * section 11.1.5.1 of the ACPI specification 3.0.
Zhang Rui203d3d42008-01-17 15:51:08 +08001557 */
1558struct thermal_zone_device *thermal_zone_device_register(char *type,
Alan Cox5b275ce2010-11-11 15:27:29 +00001559 int trips, void *devdata,
1560 const struct thermal_zone_device_ops *ops,
1561 int tc1, int tc2, int passive_delay, int polling_delay)
Zhang Rui203d3d42008-01-17 15:51:08 +08001562{
1563 struct thermal_zone_device *tz;
1564 struct thermal_cooling_device *pos;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001565 enum thermal_trip_type trip_type;
Zhang Rui203d3d42008-01-17 15:51:08 +08001566 int result;
1567 int count;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001568 int passive = 0;
Zhang Rui203d3d42008-01-17 15:51:08 +08001569
1570 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001571 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001572
1573 if (trips > THERMAL_MAX_TRIPS || trips < 0)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001574 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001575
1576 if (!ops || !ops->get_temp)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001577 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001578
1579 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
1580 if (!tz)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001581 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001582
1583 INIT_LIST_HEAD(&tz->cooling_devices);
1584 idr_init(&tz->idr);
1585 mutex_init(&tz->lock);
1586 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
1587 if (result) {
1588 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001589 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001590 }
1591
1592 strcpy(tz->type, type);
1593 tz->ops = ops;
1594 tz->device.class = &thermal_class;
1595 tz->devdata = devdata;
1596 tz->trips = trips;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001597 tz->tc1 = tc1;
1598 tz->tc2 = tc2;
1599 tz->passive_delay = passive_delay;
1600 tz->polling_delay = polling_delay;
1601
Kay Sievers354655e2009-01-06 10:44:37 -08001602 dev_set_name(&tz->device, "thermal_zone%d", tz->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001603 result = device_register(&tz->device);
1604 if (result) {
1605 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1606 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001607 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001608 }
1609
1610 /* sys I/F */
1611 if (type) {
1612 result = device_create_file(&tz->device, &dev_attr_type);
1613 if (result)
1614 goto unregister;
1615 }
1616
1617 result = device_create_file(&tz->device, &dev_attr_temp);
1618 if (result)
1619 goto unregister;
1620
1621 if (ops->get_mode) {
1622 result = device_create_file(&tz->device, &dev_attr_mode);
1623 if (result)
1624 goto unregister;
1625 }
1626
1627 for (count = 0; count < trips; count++) {
Joe Perchesec797682012-03-21 12:55:02 -07001628 result = device_create_file(&tz->device,
1629 &trip_point_attrs[count * 2]);
1630 if (result)
1631 break;
1632 result = device_create_file(&tz->device,
1633 &trip_point_attrs[count * 2 + 1]);
Zhang Rui203d3d42008-01-17 15:51:08 +08001634 if (result)
1635 goto unregister;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001636 tz->ops->get_trip_type(tz, count, &trip_type);
1637 if (trip_type == THERMAL_TRIP_PASSIVE)
1638 passive = 1;
Zhang Rui203d3d42008-01-17 15:51:08 +08001639 }
1640
Matthew Garrett03a971a2008-12-03 18:00:38 +00001641 if (!passive)
1642 result = device_create_file(&tz->device,
1643 &dev_attr_passive);
1644
1645 if (result)
1646 goto unregister;
1647
Zhang Ruie68b16a2008-04-21 16:07:52 +08001648 result = thermal_add_hwmon_sysfs(tz);
1649 if (result)
1650 goto unregister;
1651
Zhang Rui203d3d42008-01-17 15:51:08 +08001652 mutex_lock(&thermal_list_lock);
1653 list_add_tail(&tz->node, &thermal_tz_list);
1654 if (ops->bind)
1655 list_for_each_entry(pos, &thermal_cdev_list, node) {
Len Brown543a9562008-02-07 16:55:08 -05001656 result = ops->bind(tz, pos);
1657 if (result)
1658 break;
Zhang Rui203d3d42008-01-17 15:51:08 +08001659 }
Praveen Chidambarambb646212013-07-02 13:04:58 -06001660 sensor_init(tz);
Zhang Rui203d3d42008-01-17 15:51:08 +08001661 mutex_unlock(&thermal_list_lock);
1662
Matthew Garrettb1569e92008-12-03 17:55:32 +00001663 INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
1664
1665 thermal_zone_device_update(tz);
1666
Zhang Rui203d3d42008-01-17 15:51:08 +08001667 if (!result)
1668 return tz;
1669
Joe Perchescaca8b82012-03-21 12:55:02 -07001670unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001671 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1672 device_unregister(&tz->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001673 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001674}
1675EXPORT_SYMBOL(thermal_zone_device_register);
1676
1677/**
1678 * thermal_device_unregister - removes the registered thermal zone device
Zhang Rui203d3d42008-01-17 15:51:08 +08001679 * @tz: the thermal zone device to remove
1680 */
1681void thermal_zone_device_unregister(struct thermal_zone_device *tz)
1682{
1683 struct thermal_cooling_device *cdev;
1684 struct thermal_zone_device *pos = NULL;
1685 int count;
1686
1687 if (!tz)
1688 return;
1689
1690 mutex_lock(&thermal_list_lock);
1691 list_for_each_entry(pos, &thermal_tz_list, node)
1692 if (pos == tz)
1693 break;
1694 if (pos != tz) {
1695 /* thermal zone device not found */
1696 mutex_unlock(&thermal_list_lock);
1697 return;
1698 }
1699 list_del(&tz->node);
1700 if (tz->ops->unbind)
1701 list_for_each_entry(cdev, &thermal_cdev_list, node)
1702 tz->ops->unbind(tz, cdev);
1703 mutex_unlock(&thermal_list_lock);
1704
Matthew Garrettb1569e92008-12-03 17:55:32 +00001705 thermal_zone_device_set_polling(tz, 0);
1706
Zhang Rui203d3d42008-01-17 15:51:08 +08001707 if (tz->type[0])
1708 device_remove_file(&tz->device, &dev_attr_type);
1709 device_remove_file(&tz->device, &dev_attr_temp);
1710 if (tz->ops->get_mode)
1711 device_remove_file(&tz->device, &dev_attr_mode);
1712
Joe Perchesec797682012-03-21 12:55:02 -07001713 for (count = 0; count < tz->trips; count++) {
1714 device_remove_file(&tz->device,
1715 &trip_point_attrs[count * 2]);
1716 device_remove_file(&tz->device,
1717 &trip_point_attrs[count * 2 + 1]);
1718 }
Zhang Ruie68b16a2008-04-21 16:07:52 +08001719 thermal_remove_hwmon_sysfs(tz);
Praveen Chidambarambb646212013-07-02 13:04:58 -06001720 flush_work(&tz->sensor.work);
1721 mutex_lock(&thermal_list_lock);
1722 list_del(&tz->sensor.sensor_list);
1723 mutex_unlock(&thermal_list_lock);
Zhang Rui203d3d42008-01-17 15:51:08 +08001724 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1725 idr_destroy(&tz->idr);
1726 mutex_destroy(&tz->lock);
1727 device_unregister(&tz->device);
1728 return;
1729}
Zhang Rui203d3d42008-01-17 15:51:08 +08001730EXPORT_SYMBOL(thermal_zone_device_unregister);
1731
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001732#ifdef CONFIG_NET
1733static struct genl_family thermal_event_genl_family = {
1734 .id = GENL_ID_GENERATE,
1735 .name = THERMAL_GENL_FAMILY_NAME,
1736 .version = THERMAL_GENL_VERSION,
1737 .maxattr = THERMAL_GENL_ATTR_MAX,
1738};
1739
1740static struct genl_multicast_group thermal_event_mcgrp = {
1741 .name = THERMAL_GENL_MCAST_GROUP_NAME,
1742};
1743
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001744int thermal_generate_netlink_event(u32 orig, enum events event)
R.Durgadoss4cb18722010-10-27 03:33:29 +05301745{
1746 struct sk_buff *skb;
1747 struct nlattr *attr;
1748 struct thermal_genl_event *thermal_event;
1749 void *msg_header;
1750 int size;
1751 int result;
Fabio Estevamb11de072012-03-21 12:55:00 -07001752 static unsigned int thermal_event_seqnum;
R.Durgadoss4cb18722010-10-27 03:33:29 +05301753
1754 /* allocate memory */
Joe Perches886ee542012-03-21 12:55:01 -07001755 size = nla_total_size(sizeof(struct thermal_genl_event)) +
1756 nla_total_size(0);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301757
1758 skb = genlmsg_new(size, GFP_ATOMIC);
1759 if (!skb)
1760 return -ENOMEM;
1761
1762 /* add the genetlink message header */
1763 msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
1764 &thermal_event_genl_family, 0,
1765 THERMAL_GENL_CMD_EVENT);
1766 if (!msg_header) {
1767 nlmsg_free(skb);
1768 return -ENOMEM;
1769 }
1770
1771 /* fill the data */
Joe Perches886ee542012-03-21 12:55:01 -07001772 attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
1773 sizeof(struct thermal_genl_event));
R.Durgadoss4cb18722010-10-27 03:33:29 +05301774
1775 if (!attr) {
1776 nlmsg_free(skb);
1777 return -EINVAL;
1778 }
1779
1780 thermal_event = nla_data(attr);
1781 if (!thermal_event) {
1782 nlmsg_free(skb);
1783 return -EINVAL;
1784 }
1785
1786 memset(thermal_event, 0, sizeof(struct thermal_genl_event));
1787
1788 thermal_event->orig = orig;
1789 thermal_event->event = event;
1790
1791 /* send multicast genetlink message */
1792 result = genlmsg_end(skb, msg_header);
1793 if (result < 0) {
1794 nlmsg_free(skb);
1795 return result;
1796 }
1797
1798 result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
1799 if (result)
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001800 pr_info("failed to send netlink event:%d\n", result);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301801
1802 return result;
1803}
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001804EXPORT_SYMBOL(thermal_generate_netlink_event);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301805
1806static int genetlink_init(void)
1807{
1808 int result;
1809
1810 result = genl_register_family(&thermal_event_genl_family);
1811 if (result)
1812 return result;
1813
1814 result = genl_register_mc_group(&thermal_event_genl_family,
1815 &thermal_event_mcgrp);
1816 if (result)
1817 genl_unregister_family(&thermal_event_genl_family);
1818 return result;
1819}
1820
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001821static void genetlink_exit(void)
1822{
1823 genl_unregister_family(&thermal_event_genl_family);
1824}
1825#else /* !CONFIG_NET */
1826static inline int genetlink_init(void) { return 0; }
1827static inline void genetlink_exit(void) {}
1828#endif /* !CONFIG_NET */
1829
Zhang Rui203d3d42008-01-17 15:51:08 +08001830static int __init thermal_init(void)
1831{
1832 int result = 0;
1833
1834 result = class_register(&thermal_class);
1835 if (result) {
1836 idr_destroy(&thermal_tz_idr);
1837 idr_destroy(&thermal_cdev_idr);
1838 mutex_destroy(&thermal_idr_lock);
1839 mutex_destroy(&thermal_list_lock);
1840 }
R.Durgadoss4cb18722010-10-27 03:33:29 +05301841 result = genetlink_init();
Zhang Rui203d3d42008-01-17 15:51:08 +08001842 return result;
1843}
1844
1845static void __exit thermal_exit(void)
1846{
1847 class_unregister(&thermal_class);
1848 idr_destroy(&thermal_tz_idr);
1849 idr_destroy(&thermal_cdev_idr);
1850 mutex_destroy(&thermal_idr_lock);
1851 mutex_destroy(&thermal_list_lock);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301852 genetlink_exit();
Zhang Rui203d3d42008-01-17 15:51:08 +08001853}
1854
R.Durgadoss4cb18722010-10-27 03:33:29 +05301855fs_initcall(thermal_init);
Zhang Rui203d3d42008-01-17 15:51:08 +08001856module_exit(thermal_exit);