blob: 21863e8c19ce047473247feb7fcdf0cea1bb3fc5 [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)
Anji Jonnala89f84ab2013-11-22 21:36:18 +053073 return pos;
Praveen Chidambarambb646212013-07-02 13:04:58 -060074 }
75
Anji Jonnala89f84ab2013-11-22 21:36:18 +053076 return NULL;
Praveen Chidambarambb646212013-07-02 13:04:58 -060077}
78
79int sensor_get_id(char *name)
80{
81 struct sensor_info *pos, *var;
82
Anji Jonnala89f84ab2013-11-22 21:36:18 +053083 if (!name)
84 return -ENODEV;
85
Praveen Chidambarambb646212013-07-02 13:04:58 -060086 list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
87 if (!strcmp(pos->tz->type, name))
88 return pos->sensor_id;
89 }
90
91 return -ENODEV;
92}
93EXPORT_SYMBOL(sensor_get_id);
94
Ram Chandrasekarb2f16712013-10-02 11:06:42 -060095static int __update_sensor_thresholds(struct sensor_info *sensor)
Praveen Chidambaram3af26db2013-09-17 13:51:42 -060096{
Ram Chandrasekarb2f16712013-10-02 11:06:42 -060097 long max_of_low_thresh = LONG_MIN;
98 long min_of_high_thresh = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -060099 struct sensor_threshold *pos, *var;
100 enum thermal_trip_type type;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600101 int i, ret = 0;
102
103 if (!sensor->tz->ops->set_trip_temp ||
104 !sensor->tz->ops->activate_trip_type ||
105 !sensor->tz->ops->get_trip_type ||
106 !sensor->tz->ops->get_trip_temp) {
107 ret = -ENODEV;
108 goto update_done;
109 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600110
111 for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
112 (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
113 i++) {
114 sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
115 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
116 sensor->max_idx = i;
117 if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
118 sensor->min_idx = i;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600119 sensor->tz->ops->get_trip_temp(sensor->tz,
120 THERMAL_TRIP_CONFIGURABLE_LOW, &sensor->threshold_min);
121 sensor->tz->ops->get_trip_temp(sensor->tz,
122 THERMAL_TRIP_CONFIGURABLE_HI, &sensor->threshold_max);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600123 }
124
Praveen Chidambarambb646212013-07-02 13:04:58 -0600125 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600126 if (!pos->active)
127 continue;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600128 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600129 if (pos->temp > max_of_low_thresh)
130 max_of_low_thresh = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600131 }
132 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_HI) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600133 if (pos->temp < min_of_high_thresh)
134 min_of_high_thresh = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600135 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600136 }
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600137
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600138 pr_debug("sensor %d: Thresholds: max of low: %ld min of high: %ld\n",
139 sensor->sensor_id, max_of_low_thresh,
140 min_of_high_thresh);
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600141
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600142 if ((min_of_high_thresh != sensor->threshold_max) &&
143 (min_of_high_thresh != LONG_MAX)) {
144 ret = sensor->tz->ops->set_trip_temp(sensor->tz,
145 sensor->max_idx, min_of_high_thresh);
146 if (ret) {
147 pr_err("sensor %d: Unable to set high threshold %d",
148 sensor->sensor_id, ret);
149 goto update_done;
150 }
151 sensor->threshold_max = min_of_high_thresh;
152 }
153 ret = sensor->tz->ops->activate_trip_type(sensor->tz,
154 sensor->max_idx,
155 (min_of_high_thresh == LONG_MAX) ?
156 THERMAL_TRIP_ACTIVATION_DISABLED :
157 THERMAL_TRIP_ACTIVATION_ENABLED);
158 if (ret) {
159 pr_err("sensor %d: Unable to activate high threshold %d",
160 sensor->sensor_id, ret);
161 goto update_done;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600162 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600163
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600164 if ((max_of_low_thresh != sensor->threshold_min) &&
165 (max_of_low_thresh != LONG_MIN)) {
166 ret = sensor->tz->ops->set_trip_temp(sensor->tz,
167 sensor->min_idx, max_of_low_thresh);
168 if (ret) {
169 pr_err("sensor %d: Unable to set low threshold %d",
170 sensor->sensor_id, ret);
171 goto update_done;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600172 }
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600173 sensor->threshold_min = max_of_low_thresh;
174 }
175 ret = sensor->tz->ops->activate_trip_type(sensor->tz,
176 sensor->min_idx,
177 (max_of_low_thresh == LONG_MIN) ?
178 THERMAL_TRIP_ACTIVATION_DISABLED :
179 THERMAL_TRIP_ACTIVATION_ENABLED);
180 if (ret) {
181 pr_err("sensor %d: Unable to activate low threshold %d",
182 sensor->sensor_id, ret);
183 goto update_done;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600184 }
185
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600186 pr_debug("sensor %d: low: %ld high: %ld\n",
187 sensor->sensor_id,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600188 sensor->threshold_min, sensor->threshold_max);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600189
190update_done:
191 return ret;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600192}
193
194static void sensor_update_work(struct work_struct *work)
195{
196 struct sensor_info *sensor = container_of(work, struct sensor_info,
197 work);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600198 int ret = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600199 mutex_lock(&sensor->lock);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600200 ret = __update_sensor_thresholds(sensor);
201 if (ret)
202 pr_err("sensor %d: Error %d setting threshold\n",
203 sensor->sensor_id, ret);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600204 mutex_unlock(&sensor->lock);
205}
206
207/* May be called in an interrupt context.
208 * Do NOT call sensor_set_trip from this function
209 */
210int thermal_sensor_trip(struct thermal_zone_device *tz,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600211 enum thermal_trip_type trip, long temp)
Praveen Chidambarambb646212013-07-02 13:04:58 -0600212{
213 struct sensor_threshold *pos, *var;
214 int ret = -ENODEV;
215
216 if (trip != THERMAL_TRIP_CONFIGURABLE_HI &&
217 trip != THERMAL_TRIP_CONFIGURABLE_LOW)
218 return 0;
219
220 if (list_empty(&tz->sensor.threshold_list))
221 return 0;
222
223 list_for_each_entry_safe(pos, var, &tz->sensor.threshold_list, list) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600224 if ((pos->trip != trip) || (!pos->active))
Praveen Chidambarambb646212013-07-02 13:04:58 -0600225 continue;
226 if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
227 (pos->temp <= tz->sensor.threshold_min) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600228 (pos->temp >= temp)) ||
Praveen Chidambarambb646212013-07-02 13:04:58 -0600229 ((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
230 (pos->temp >= tz->sensor.threshold_max) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600231 (pos->temp <= temp))) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600232 pos->active = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600233 pos->notify(trip, temp, pos->data);
234 }
235 }
236
237 schedule_work(&tz->sensor.work);
238
239 return ret;
240}
241EXPORT_SYMBOL(thermal_sensor_trip);
242
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600243int sensor_activate_trip(uint32_t sensor_id,
244 struct sensor_threshold *threshold, bool enable)
245{
246 struct sensor_info *sensor = get_sensor(sensor_id);
247 int ret = 0;
248
249 if (!sensor || !threshold) {
250 pr_err("Sensor %d: uninitialized data\n",
251 sensor_id);
252 ret = -ENODEV;
253 goto activate_trip_exit;
254 }
255
256 mutex_lock(&sensor->lock);
257 threshold->active = (enable) ? 1 : 0;
258 ret = __update_sensor_thresholds(sensor);
259 mutex_unlock(&sensor->lock);
260
261activate_trip_exit:
262 return ret;
263}
264EXPORT_SYMBOL(sensor_activate_trip);
265
Praveen Chidambarambb646212013-07-02 13:04:58 -0600266int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
267{
268 struct sensor_threshold *pos, *var;
269 struct sensor_info *sensor = get_sensor(sensor_id);
270
271 if (!sensor)
272 return -ENODEV;
273
274 if (!threshold || !threshold->notify)
275 return -EFAULT;
276
277 mutex_lock(&sensor->lock);
278 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
279 if (pos == threshold)
280 break;
281 }
282
283 if (pos != threshold) {
284 INIT_LIST_HEAD(&threshold->list);
285 list_add(&threshold->list, &sensor->threshold_list);
286 }
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600287 threshold->active = 0; /* Do not allow active threshold right away */
Praveen Chidambarambb646212013-07-02 13:04:58 -0600288 mutex_unlock(&sensor->lock);
289
290 return 0;
291
292}
293EXPORT_SYMBOL(sensor_set_trip);
294
295int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
296{
297 struct sensor_threshold *pos, *var;
298 struct sensor_info *sensor = get_sensor(sensor_id);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600299 int ret = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600300
301 if (!sensor)
302 return -ENODEV;
303
304 mutex_lock(&sensor->lock);
305 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
306 if (pos == threshold) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600307 pos->active = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600308 list_del(&pos->list);
309 break;
310 }
311 }
312
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600313 ret = __update_sensor_thresholds(sensor);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600314 mutex_unlock(&sensor->lock);
315
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600316 return ret;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600317}
318EXPORT_SYMBOL(sensor_cancel_trip);
319
Praveen Chidambarambb646212013-07-02 13:04:58 -0600320static int tz_notify_trip(enum thermal_trip_type type, int temp, void *data)
321{
322 struct thermal_zone_device *tz = (struct thermal_zone_device *)data;
323
324 pr_debug("sensor %d tripped: type %d temp %d\n",
325 tz->sensor.sensor_id, type, temp);
326
327 return 0;
328}
329
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600330static void get_trip_threshold(struct thermal_zone_device *tz, int trip,
331 struct sensor_threshold **threshold)
332{
333 enum thermal_trip_type type;
334
335 tz->ops->get_trip_type(tz, trip, &type);
336
337 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
338 *threshold = &tz->tz_threshold[0];
339 else if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
340 *threshold = &tz->tz_threshold[1];
341 else
342 *threshold = NULL;
343}
344
Praveen Chidambarambb646212013-07-02 13:04:58 -0600345int sensor_set_trip_temp(struct thermal_zone_device *tz,
346 int trip, long temp)
347{
348 int ret = 0;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600349 struct sensor_threshold *threshold = NULL;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600350
351 if (!tz->ops->get_trip_type)
352 return -EPERM;
353
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600354 get_trip_threshold(tz, trip, &threshold);
355 if (threshold) {
356 threshold->temp = temp;
357 ret = sensor_set_trip(tz->sensor.sensor_id, threshold);
358 } else {
Praveen Chidambarambb646212013-07-02 13:04:58 -0600359 ret = tz->ops->set_trip_temp(tz, trip, temp);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600360 }
361
362 return ret;
363}
364
365int sensor_init(struct thermal_zone_device *tz)
366{
367 struct sensor_info *sensor = &tz->sensor;
368
369 sensor->sensor_id = tz->id;
370 sensor->tz = tz;
371 sensor->threshold_min = 0;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600372 sensor->threshold_max = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600373 sensor->max_idx = -1;
374 sensor->min_idx = -1;
375 mutex_init(&sensor->lock);
376 INIT_LIST_HEAD(&sensor->sensor_list);
377 INIT_LIST_HEAD(&sensor->threshold_list);
378 INIT_LIST_HEAD(&tz->tz_threshold[0].list);
379 INIT_LIST_HEAD(&tz->tz_threshold[1].list);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600380 tz->tz_threshold[0].notify = tz_notify_trip;
381 tz->tz_threshold[0].data = tz;
382 tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
383 tz->tz_threshold[1].notify = tz_notify_trip;
384 tz->tz_threshold[1].data = tz;
385 tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600386 list_add(&sensor->sensor_list, &sensor_info_list);
387 INIT_WORK(&sensor->work, sensor_update_work);
388
389 return 0;
390}
391
Zhang Rui203d3d42008-01-17 15:51:08 +0800392static int get_idr(struct idr *idr, struct mutex *lock, int *id)
393{
394 int err;
395
Joe Perchescaca8b82012-03-21 12:55:02 -0700396again:
Zhang Rui203d3d42008-01-17 15:51:08 +0800397 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
398 return -ENOMEM;
399
400 if (lock)
401 mutex_lock(lock);
402 err = idr_get_new(idr, NULL, id);
403 if (lock)
404 mutex_unlock(lock);
405 if (unlikely(err == -EAGAIN))
406 goto again;
407 else if (unlikely(err))
408 return err;
409
410 *id = *id & MAX_ID_MASK;
411 return 0;
412}
413
414static void release_idr(struct idr *idr, struct mutex *lock, int id)
415{
416 if (lock)
417 mutex_lock(lock);
418 idr_remove(idr, id);
419 if (lock)
420 mutex_unlock(lock);
421}
422
423/* sys I/F for thermal zone */
424
425#define to_thermal_zone(_dev) \
426 container_of(_dev, struct thermal_zone_device, device)
427
428static ssize_t
429type_show(struct device *dev, struct device_attribute *attr, char *buf)
430{
431 struct thermal_zone_device *tz = to_thermal_zone(dev);
432
433 return sprintf(buf, "%s\n", tz->type);
434}
435
436static ssize_t
437temp_show(struct device *dev, struct device_attribute *attr, char *buf)
438{
439 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000440 long temperature;
441 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800442
443 if (!tz->ops->get_temp)
444 return -EPERM;
445
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000446 ret = tz->ops->get_temp(tz, &temperature);
447
448 if (ret)
449 return ret;
450
451 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800452}
453
454static ssize_t
455mode_show(struct device *dev, struct device_attribute *attr, char *buf)
456{
457 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000458 enum thermal_device_mode mode;
459 int result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800460
461 if (!tz->ops->get_mode)
462 return -EPERM;
463
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000464 result = tz->ops->get_mode(tz, &mode);
465 if (result)
466 return result;
467
468 return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
469 : "disabled");
Zhang Rui203d3d42008-01-17 15:51:08 +0800470}
471
472static ssize_t
473mode_store(struct device *dev, struct device_attribute *attr,
474 const char *buf, size_t count)
475{
476 struct thermal_zone_device *tz = to_thermal_zone(dev);
477 int result;
478
479 if (!tz->ops->set_mode)
480 return -EPERM;
481
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530482 if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000483 result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530484 else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000485 result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
486 else
487 result = -EINVAL;
488
Zhang Rui203d3d42008-01-17 15:51:08 +0800489 if (result)
490 return result;
491
492 return count;
493}
494
495static ssize_t
496trip_point_type_show(struct device *dev, struct device_attribute *attr,
497 char *buf)
498{
499 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000500 enum thermal_trip_type type;
501 int trip, result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800502
503 if (!tz->ops->get_trip_type)
504 return -EPERM;
505
506 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
507 return -EINVAL;
508
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000509 result = tz->ops->get_trip_type(tz, trip, &type);
510 if (result)
511 return result;
512
513 switch (type) {
514 case THERMAL_TRIP_CRITICAL:
Amit Kucheria625120a2009-10-16 12:46:02 +0300515 return sprintf(buf, "critical\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000516 case THERMAL_TRIP_HOT:
Amit Kucheria625120a2009-10-16 12:46:02 +0300517 return sprintf(buf, "hot\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 case THERMAL_TRIP_CONFIGURABLE_HI:
519 return sprintf(buf, "configurable_hi\n");
520 case THERMAL_TRIP_CONFIGURABLE_LOW:
521 return sprintf(buf, "configurable_low\n");
522 case THERMAL_TRIP_CRITICAL_LOW:
523 return sprintf(buf, "critical_low\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000524 case THERMAL_TRIP_PASSIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300525 return sprintf(buf, "passive\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000526 case THERMAL_TRIP_ACTIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300527 return sprintf(buf, "active\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000528 default:
Amit Kucheria625120a2009-10-16 12:46:02 +0300529 return sprintf(buf, "unknown\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000530 }
Zhang Rui203d3d42008-01-17 15:51:08 +0800531}
532
533static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534trip_point_type_activate(struct device *dev, struct device_attribute *attr,
535 const char *buf, size_t count)
536{
537 struct thermal_zone_device *tz = to_thermal_zone(dev);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600538 int trip, result = 0;
539 bool activate;
540 struct sensor_threshold *threshold = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600542 if (!tz->ops->get_trip_type ||
543 !tz->ops->activate_trip_type) {
544 result = -EPERM;
545 goto trip_activate_exit;
546 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600548 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549 result = -EINVAL;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600550 goto trip_activate_exit;
551 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600553 if (!strcmp(buf, "enabled")) {
554 activate = true;
555 } else if (!strcmp(buf, "disabled")) {
556 activate = false;
557 } else {
558 result = -EINVAL;
559 goto trip_activate_exit;
560 }
561
562 get_trip_threshold(tz, trip, &threshold);
563 if (threshold)
564 result = sensor_activate_trip(tz->sensor.sensor_id,
565 threshold, activate);
566 else
567 result = tz->ops->activate_trip_type(tz, trip,
568 activate ? THERMAL_TRIP_ACTIVATION_ENABLED :
569 THERMAL_TRIP_ACTIVATION_DISABLED);
570
571trip_activate_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 if (result)
573 return result;
574
575 return count;
576}
577
578static ssize_t
Zhang Rui203d3d42008-01-17 15:51:08 +0800579trip_point_temp_show(struct device *dev, struct device_attribute *attr,
580 char *buf)
581{
582 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000583 int trip, ret;
584 long temperature;
Zhang Rui203d3d42008-01-17 15:51:08 +0800585
586 if (!tz->ops->get_trip_temp)
587 return -EPERM;
588
589 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
590 return -EINVAL;
591
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600592 ret = tz->ops->get_trip_temp(tz, trip, &temperature);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000593
594 if (ret)
595 return ret;
596
597 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800598}
599
Matthew Garrett03a971a2008-12-03 18:00:38 +0000600static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601trip_point_temp_set(struct device *dev, struct device_attribute *attr,
602 const char *buf, size_t count)
603{
604 struct thermal_zone_device *tz = to_thermal_zone(dev);
605 int trip, ret;
606 long temperature;
607
608 if (!tz->ops->set_trip_temp)
609 return -EPERM;
610
611 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
612 return -EINVAL;
613
614 if (!sscanf(buf, "%ld", &temperature))
615 return -EINVAL;
616
Praveen Chidambarambb646212013-07-02 13:04:58 -0600617 ret = sensor_set_trip_temp(tz, trip, temperature);
618
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700619 if (ret)
620 return ret;
621
622 return count;
623}
624
625static ssize_t
Matthew Garrett03a971a2008-12-03 18:00:38 +0000626passive_store(struct device *dev, struct device_attribute *attr,
627 const char *buf, size_t count)
628{
629 struct thermal_zone_device *tz = to_thermal_zone(dev);
630 struct thermal_cooling_device *cdev = NULL;
631 int state;
632
633 if (!sscanf(buf, "%d\n", &state))
634 return -EINVAL;
635
Frans Pop3d8e3ad2009-10-26 08:39:02 +0100636 /* sanity check: values below 1000 millicelcius don't make sense
637 * and can cause the system to go into a thermal heart attack
638 */
639 if (state && state < 1000)
640 return -EINVAL;
641
Matthew Garrett03a971a2008-12-03 18:00:38 +0000642 if (state && !tz->forced_passive) {
643 mutex_lock(&thermal_list_lock);
644 list_for_each_entry(cdev, &thermal_cdev_list, node) {
645 if (!strncmp("Processor", cdev->type,
646 sizeof("Processor")))
647 thermal_zone_bind_cooling_device(tz,
648 THERMAL_TRIPS_NONE,
649 cdev);
650 }
651 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100652 if (!tz->passive_delay)
653 tz->passive_delay = 1000;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000654 } else if (!state && tz->forced_passive) {
655 mutex_lock(&thermal_list_lock);
656 list_for_each_entry(cdev, &thermal_cdev_list, node) {
657 if (!strncmp("Processor", cdev->type,
658 sizeof("Processor")))
659 thermal_zone_unbind_cooling_device(tz,
660 THERMAL_TRIPS_NONE,
661 cdev);
662 }
663 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100664 tz->passive_delay = 0;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000665 }
666
667 tz->tc1 = 1;
668 tz->tc2 = 1;
669
Matthew Garrett03a971a2008-12-03 18:00:38 +0000670 tz->forced_passive = state;
671
672 thermal_zone_device_update(tz);
673
674 return count;
675}
676
677static ssize_t
678passive_show(struct device *dev, struct device_attribute *attr,
679 char *buf)
680{
681 struct thermal_zone_device *tz = to_thermal_zone(dev);
682
683 return sprintf(buf, "%d\n", tz->forced_passive);
684}
685
Zhang Rui203d3d42008-01-17 15:51:08 +0800686static DEVICE_ATTR(type, 0444, type_show, NULL);
687static DEVICE_ATTR(temp, 0444, temp_show, NULL);
688static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
Joe Perches886ee542012-03-21 12:55:01 -0700689static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
Zhang Rui203d3d42008-01-17 15:51:08 +0800690
691static struct device_attribute trip_point_attrs[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692 __ATTR(trip_point_0_type, 0644, trip_point_type_show,
693 trip_point_type_activate),
694 __ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
695 trip_point_temp_set),
696 __ATTR(trip_point_1_type, 0644, trip_point_type_show,
697 trip_point_type_activate),
698 __ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
699 trip_point_temp_set),
700 __ATTR(trip_point_2_type, 0644, trip_point_type_show,
701 trip_point_type_activate),
702 __ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
703 trip_point_temp_set),
704 __ATTR(trip_point_3_type, 0644, trip_point_type_show,
705 trip_point_type_activate),
706 __ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
707 trip_point_temp_set),
708 __ATTR(trip_point_4_type, 0644, trip_point_type_show,
709 trip_point_type_activate),
710 __ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
711 trip_point_temp_set),
712 __ATTR(trip_point_5_type, 0644, trip_point_type_show,
713 trip_point_type_activate),
714 __ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
715 trip_point_temp_set),
716 __ATTR(trip_point_6_type, 0644, trip_point_type_show,
717 trip_point_type_activate),
718 __ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
719 trip_point_temp_set),
720 __ATTR(trip_point_7_type, 0644, trip_point_type_show,
721 trip_point_type_activate),
722 __ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
723 trip_point_temp_set),
724 __ATTR(trip_point_8_type, 0644, trip_point_type_show,
725 trip_point_type_activate),
726 __ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
727 trip_point_temp_set),
728 __ATTR(trip_point_9_type, 0644, trip_point_type_show,
729 trip_point_type_activate),
730 __ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
731 trip_point_temp_set),
732 __ATTR(trip_point_10_type, 0644, trip_point_type_show,
733 trip_point_type_activate),
734 __ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
735 trip_point_temp_set),
736 __ATTR(trip_point_11_type, 0644, trip_point_type_show,
737 trip_point_type_activate),
738 __ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
739 trip_point_temp_set),
Zhang Rui203d3d42008-01-17 15:51:08 +0800740};
741
Zhang Rui203d3d42008-01-17 15:51:08 +0800742/* sys I/F for cooling device */
743#define to_cooling_device(_dev) \
744 container_of(_dev, struct thermal_cooling_device, device)
745
746static ssize_t
747thermal_cooling_device_type_show(struct device *dev,
748 struct device_attribute *attr, char *buf)
749{
750 struct thermal_cooling_device *cdev = to_cooling_device(dev);
751
752 return sprintf(buf, "%s\n", cdev->type);
753}
754
755static ssize_t
756thermal_cooling_device_max_state_show(struct device *dev,
757 struct device_attribute *attr, char *buf)
758{
759 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000760 unsigned long state;
761 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800762
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000763 ret = cdev->ops->get_max_state(cdev, &state);
764 if (ret)
765 return ret;
766 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800767}
768
769static ssize_t
770thermal_cooling_device_cur_state_show(struct device *dev,
771 struct device_attribute *attr, char *buf)
772{
773 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000774 unsigned long state;
775 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800776
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000777 ret = cdev->ops->get_cur_state(cdev, &state);
778 if (ret)
779 return ret;
780 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800781}
782
783static ssize_t
784thermal_cooling_device_cur_state_store(struct device *dev,
785 struct device_attribute *attr,
786 const char *buf, size_t count)
787{
788 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000789 unsigned long state;
Zhang Rui203d3d42008-01-17 15:51:08 +0800790 int result;
791
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000792 if (!sscanf(buf, "%ld\n", &state))
Zhang Rui203d3d42008-01-17 15:51:08 +0800793 return -EINVAL;
794
Roel Kluinedb94912009-12-15 22:46:50 +0100795 if ((long)state < 0)
Zhang Rui203d3d42008-01-17 15:51:08 +0800796 return -EINVAL;
797
798 result = cdev->ops->set_cur_state(cdev, state);
799 if (result)
800 return result;
801 return count;
802}
803
804static struct device_attribute dev_attr_cdev_type =
Len Brown543a9562008-02-07 16:55:08 -0500805__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800806static DEVICE_ATTR(max_state, 0444,
807 thermal_cooling_device_max_state_show, NULL);
808static DEVICE_ATTR(cur_state, 0644,
809 thermal_cooling_device_cur_state_show,
810 thermal_cooling_device_cur_state_store);
811
812static ssize_t
813thermal_cooling_device_trip_point_show(struct device *dev,
Len Brown543a9562008-02-07 16:55:08 -0500814 struct device_attribute *attr, char *buf)
Zhang Rui203d3d42008-01-17 15:51:08 +0800815{
816 struct thermal_cooling_device_instance *instance;
817
818 instance =
819 container_of(attr, struct thermal_cooling_device_instance, attr);
820
821 if (instance->trip == THERMAL_TRIPS_NONE)
822 return sprintf(buf, "-1\n");
823 else
824 return sprintf(buf, "%d\n", instance->trip);
825}
826
827/* Device management */
828
Rene Herman16d75232008-06-24 19:38:56 +0200829#if defined(CONFIG_THERMAL_HWMON)
830
Zhang Ruie68b16a2008-04-21 16:07:52 +0800831/* hwmon sys I/F */
832#include <linux/hwmon.h>
Jean Delvare31f53962011-07-28 13:48:42 -0700833
834/* thermal zone devices with the same type share one hwmon device */
835struct thermal_hwmon_device {
836 char type[THERMAL_NAME_LENGTH];
837 struct device *device;
838 int count;
839 struct list_head tz_list;
840 struct list_head node;
841};
842
843struct thermal_hwmon_attr {
844 struct device_attribute attr;
845 char name[16];
846};
847
848/* one temperature input for each thermal zone */
849struct thermal_hwmon_temp {
850 struct list_head hwmon_node;
851 struct thermal_zone_device *tz;
852 struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
853 struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
854};
855
Zhang Ruie68b16a2008-04-21 16:07:52 +0800856static LIST_HEAD(thermal_hwmon_list);
857
858static ssize_t
859name_show(struct device *dev, struct device_attribute *attr, char *buf)
860{
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700861 struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800862 return sprintf(buf, "%s\n", hwmon->type);
863}
864static DEVICE_ATTR(name, 0444, name_show, NULL);
865
866static ssize_t
867temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
868{
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000869 long temperature;
870 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800871 struct thermal_hwmon_attr *hwmon_attr
872 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700873 struct thermal_hwmon_temp *temp
874 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800875 temp_input);
Jean Delvare31f53962011-07-28 13:48:42 -0700876 struct thermal_zone_device *tz = temp->tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800877
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000878 ret = tz->ops->get_temp(tz, &temperature);
879
880 if (ret)
881 return ret;
882
883 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800884}
885
886static ssize_t
887temp_crit_show(struct device *dev, struct device_attribute *attr,
888 char *buf)
889{
890 struct thermal_hwmon_attr *hwmon_attr
891 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700892 struct thermal_hwmon_temp *temp
893 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800894 temp_crit);
Jean Delvare31f53962011-07-28 13:48:42 -0700895 struct thermal_zone_device *tz = temp->tz;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000896 long temperature;
897 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800898
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000899 ret = tz->ops->get_trip_temp(tz, 0, &temperature);
900 if (ret)
901 return ret;
902
903 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800904}
905
906
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700907static struct thermal_hwmon_device *
908thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
909{
910 struct thermal_hwmon_device *hwmon;
911
912 mutex_lock(&thermal_list_lock);
913 list_for_each_entry(hwmon, &thermal_hwmon_list, node)
914 if (!strcmp(hwmon->type, tz->type)) {
915 mutex_unlock(&thermal_list_lock);
916 return hwmon;
917 }
918 mutex_unlock(&thermal_list_lock);
919
920 return NULL;
921}
922
Jean Delvare31f53962011-07-28 13:48:42 -0700923/* Find the temperature input matching a given thermal zone */
924static struct thermal_hwmon_temp *
925thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
926 const struct thermal_zone_device *tz)
927{
928 struct thermal_hwmon_temp *temp;
929
930 mutex_lock(&thermal_list_lock);
931 list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
932 if (temp->tz == tz) {
933 mutex_unlock(&thermal_list_lock);
934 return temp;
935 }
936 mutex_unlock(&thermal_list_lock);
937
938 return NULL;
939}
940
Zhang Ruie68b16a2008-04-21 16:07:52 +0800941static int
942thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
943{
944 struct thermal_hwmon_device *hwmon;
Jean Delvare31f53962011-07-28 13:48:42 -0700945 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800946 int new_hwmon_device = 1;
947 int result;
948
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700949 hwmon = thermal_hwmon_lookup_by_type(tz);
950 if (hwmon) {
951 new_hwmon_device = 0;
952 goto register_sys_interface;
953 }
Zhang Ruie68b16a2008-04-21 16:07:52 +0800954
955 hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
956 if (!hwmon)
957 return -ENOMEM;
958
959 INIT_LIST_HEAD(&hwmon->tz_list);
960 strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
961 hwmon->device = hwmon_device_register(NULL);
962 if (IS_ERR(hwmon->device)) {
963 result = PTR_ERR(hwmon->device);
964 goto free_mem;
965 }
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700966 dev_set_drvdata(hwmon->device, hwmon);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800967 result = device_create_file(hwmon->device, &dev_attr_name);
968 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530969 goto free_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800970
971 register_sys_interface:
Jean Delvare31f53962011-07-28 13:48:42 -0700972 temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
973 if (!temp) {
974 result = -ENOMEM;
975 goto unregister_name;
976 }
977
978 temp->tz = tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800979 hwmon->count++;
980
Jean Delvare31f53962011-07-28 13:48:42 -0700981 snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800982 "temp%d_input", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700983 temp->temp_input.attr.attr.name = temp->temp_input.name;
984 temp->temp_input.attr.attr.mode = 0444;
985 temp->temp_input.attr.show = temp_input_show;
986 sysfs_attr_init(&temp->temp_input.attr.attr);
987 result = device_create_file(hwmon->device, &temp->temp_input.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800988 if (result)
Jean Delvare31f53962011-07-28 13:48:42 -0700989 goto free_temp_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800990
991 if (tz->ops->get_crit_temp) {
992 unsigned long temperature;
993 if (!tz->ops->get_crit_temp(tz, &temperature)) {
Jean Delvare31f53962011-07-28 13:48:42 -0700994 snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800995 "temp%d_crit", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -0700996 temp->temp_crit.attr.attr.name = temp->temp_crit.name;
997 temp->temp_crit.attr.attr.mode = 0444;
998 temp->temp_crit.attr.show = temp_crit_show;
999 sysfs_attr_init(&temp->temp_crit.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001000 result = device_create_file(hwmon->device,
Jean Delvare31f53962011-07-28 13:48:42 -07001001 &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001002 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +05301003 goto unregister_input;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001004 }
1005 }
1006
1007 mutex_lock(&thermal_list_lock);
1008 if (new_hwmon_device)
1009 list_add_tail(&hwmon->node, &thermal_hwmon_list);
Jean Delvare31f53962011-07-28 13:48:42 -07001010 list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001011 mutex_unlock(&thermal_list_lock);
1012
1013 return 0;
1014
Durgadoss Rb299eb52011-03-03 04:30:13 +05301015 unregister_input:
Jean Delvare31f53962011-07-28 13:48:42 -07001016 device_remove_file(hwmon->device, &temp->temp_input.attr);
1017 free_temp_mem:
1018 kfree(temp);
Durgadoss Rb299eb52011-03-03 04:30:13 +05301019 unregister_name:
Zhang Ruie68b16a2008-04-21 16:07:52 +08001020 if (new_hwmon_device) {
1021 device_remove_file(hwmon->device, &dev_attr_name);
1022 hwmon_device_unregister(hwmon->device);
1023 }
1024 free_mem:
1025 if (new_hwmon_device)
1026 kfree(hwmon);
1027
1028 return result;
1029}
1030
1031static void
1032thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1033{
Jean Delvare31f53962011-07-28 13:48:42 -07001034 struct thermal_hwmon_device *hwmon;
1035 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001036
Jean Delvare31f53962011-07-28 13:48:42 -07001037 hwmon = thermal_hwmon_lookup_by_type(tz);
1038 if (unlikely(!hwmon)) {
1039 /* Should never happen... */
1040 dev_dbg(&tz->device, "hwmon device lookup failed!\n");
1041 return;
1042 }
1043
1044 temp = thermal_hwmon_lookup_temp(hwmon, tz);
1045 if (unlikely(!temp)) {
1046 /* Should never happen... */
1047 dev_dbg(&tz->device, "temperature input lookup failed!\n");
1048 return;
1049 }
1050
1051 device_remove_file(hwmon->device, &temp->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +05301052 if (tz->ops->get_crit_temp)
Jean Delvare31f53962011-07-28 13:48:42 -07001053 device_remove_file(hwmon->device, &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001054
1055 mutex_lock(&thermal_list_lock);
Jean Delvare31f53962011-07-28 13:48:42 -07001056 list_del(&temp->hwmon_node);
1057 kfree(temp);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001058 if (!list_empty(&hwmon->tz_list)) {
1059 mutex_unlock(&thermal_list_lock);
1060 return;
1061 }
1062 list_del(&hwmon->node);
1063 mutex_unlock(&thermal_list_lock);
1064
1065 device_remove_file(hwmon->device, &dev_attr_name);
1066 hwmon_device_unregister(hwmon->device);
1067 kfree(hwmon);
1068}
1069#else
1070static int
1071thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
1072{
1073 return 0;
1074}
1075
1076static void
1077thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1078{
1079}
1080#endif
1081
Matthew Garrettb1569e92008-12-03 17:55:32 +00001082static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
1083 int delay)
1084{
1085 cancel_delayed_work(&(tz->poll_queue));
1086
1087 if (!delay)
1088 return;
1089
1090 if (delay > 1000)
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 round_jiffies(msecs_to_jiffies(delay)));
1093 else
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001094 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001095 msecs_to_jiffies(delay));
1096}
1097
1098static void thermal_zone_device_passive(struct thermal_zone_device *tz,
1099 int temp, int trip_temp, int trip)
1100{
1101 int trend = 0;
1102 struct thermal_cooling_device_instance *instance;
1103 struct thermal_cooling_device *cdev;
1104 long state, max_state;
1105
1106 /*
1107 * Above Trip?
1108 * -----------
1109 * Calculate the thermal trend (using the passive cooling equation)
1110 * and modify the performance limit for all passive cooling devices
1111 * accordingly. Note that we assume symmetry.
1112 */
1113 if (temp >= trip_temp) {
1114 tz->passive = true;
1115
1116 trend = (tz->tc1 * (temp - tz->last_temperature)) +
1117 (tz->tc2 * (temp - trip_temp));
1118
1119 /* Heating up? */
1120 if (trend > 0) {
1121 list_for_each_entry(instance, &tz->cooling_devices,
1122 node) {
1123 if (instance->trip != trip)
1124 continue;
1125 cdev = instance->cdev;
1126 cdev->ops->get_cur_state(cdev, &state);
1127 cdev->ops->get_max_state(cdev, &max_state);
1128 if (state++ < max_state)
1129 cdev->ops->set_cur_state(cdev, state);
1130 }
1131 } else if (trend < 0) { /* Cooling off? */
1132 list_for_each_entry(instance, &tz->cooling_devices,
1133 node) {
1134 if (instance->trip != trip)
1135 continue;
1136 cdev = instance->cdev;
1137 cdev->ops->get_cur_state(cdev, &state);
1138 cdev->ops->get_max_state(cdev, &max_state);
1139 if (state > 0)
1140 cdev->ops->set_cur_state(cdev, --state);
1141 }
1142 }
1143 return;
1144 }
1145
1146 /*
1147 * Below Trip?
1148 * -----------
1149 * Implement passive cooling hysteresis to slowly increase performance
1150 * and avoid thrashing around the passive trip point. Note that we
1151 * assume symmetry.
1152 */
1153 list_for_each_entry(instance, &tz->cooling_devices, node) {
1154 if (instance->trip != trip)
1155 continue;
1156 cdev = instance->cdev;
1157 cdev->ops->get_cur_state(cdev, &state);
1158 cdev->ops->get_max_state(cdev, &max_state);
1159 if (state > 0)
1160 cdev->ops->set_cur_state(cdev, --state);
1161 if (state == 0)
1162 tz->passive = false;
1163 }
1164}
1165
1166static void thermal_zone_device_check(struct work_struct *work)
1167{
1168 struct thermal_zone_device *tz = container_of(work, struct
1169 thermal_zone_device,
1170 poll_queue.work);
1171 thermal_zone_device_update(tz);
1172}
Zhang Ruie68b16a2008-04-21 16:07:52 +08001173
Zhang Rui203d3d42008-01-17 15:51:08 +08001174/**
1175 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001176 * @tz: thermal zone device
1177 * @trip: indicates which trip point the cooling devices is
1178 * associated with in this thermal zone.
1179 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001180 *
1181 * This function is usually called in the thermal zone device .bind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001182 */
1183int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
1184 int trip,
1185 struct thermal_cooling_device *cdev)
1186{
1187 struct thermal_cooling_device_instance *dev;
1188 struct thermal_cooling_device_instance *pos;
Thomas Sujithc7516702008-02-15 00:58:50 -05001189 struct thermal_zone_device *pos1;
1190 struct thermal_cooling_device *pos2;
Zhang Rui203d3d42008-01-17 15:51:08 +08001191 int result;
1192
Len Brown543a9562008-02-07 16:55:08 -05001193 if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
Zhang Rui203d3d42008-01-17 15:51:08 +08001194 return -EINVAL;
1195
Thomas Sujithc7516702008-02-15 00:58:50 -05001196 list_for_each_entry(pos1, &thermal_tz_list, node) {
1197 if (pos1 == tz)
1198 break;
1199 }
1200 list_for_each_entry(pos2, &thermal_cdev_list, node) {
1201 if (pos2 == cdev)
1202 break;
1203 }
1204
1205 if (tz != pos1 || cdev != pos2)
Zhang Rui203d3d42008-01-17 15:51:08 +08001206 return -EINVAL;
1207
1208 dev =
1209 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
1210 if (!dev)
1211 return -ENOMEM;
1212 dev->tz = tz;
1213 dev->cdev = cdev;
1214 dev->trip = trip;
1215 result = get_idr(&tz->idr, &tz->lock, &dev->id);
1216 if (result)
1217 goto free_mem;
1218
1219 sprintf(dev->name, "cdev%d", dev->id);
1220 result =
1221 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
1222 if (result)
1223 goto release_idr;
1224
1225 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -07001226 sysfs_attr_init(&dev->attr.attr);
Zhang Rui203d3d42008-01-17 15:51:08 +08001227 dev->attr.attr.name = dev->attr_name;
1228 dev->attr.attr.mode = 0444;
1229 dev->attr.show = thermal_cooling_device_trip_point_show;
1230 result = device_create_file(&tz->device, &dev->attr);
1231 if (result)
1232 goto remove_symbol_link;
1233
1234 mutex_lock(&tz->lock);
1235 list_for_each_entry(pos, &tz->cooling_devices, node)
1236 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
1237 result = -EEXIST;
1238 break;
1239 }
1240 if (!result)
1241 list_add_tail(&dev->node, &tz->cooling_devices);
1242 mutex_unlock(&tz->lock);
1243
1244 if (!result)
1245 return 0;
1246
1247 device_remove_file(&tz->device, &dev->attr);
Joe Perchescaca8b82012-03-21 12:55:02 -07001248remove_symbol_link:
Zhang Rui203d3d42008-01-17 15:51:08 +08001249 sysfs_remove_link(&tz->device.kobj, dev->name);
Joe Perchescaca8b82012-03-21 12:55:02 -07001250release_idr:
Zhang Rui203d3d42008-01-17 15:51:08 +08001251 release_idr(&tz->idr, &tz->lock, dev->id);
Joe Perchescaca8b82012-03-21 12:55:02 -07001252free_mem:
Zhang Rui203d3d42008-01-17 15:51:08 +08001253 kfree(dev);
1254 return result;
1255}
1256EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
1257
1258/**
1259 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001260 * @tz: thermal zone device
1261 * @trip: indicates which trip point the cooling devices is
1262 * associated with in this thermal zone.
1263 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001264 *
1265 * This function is usually called in the thermal zone device .unbind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001266 */
1267int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
1268 int trip,
1269 struct thermal_cooling_device *cdev)
1270{
1271 struct thermal_cooling_device_instance *pos, *next;
1272
1273 mutex_lock(&tz->lock);
1274 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
Len Brown543a9562008-02-07 16:55:08 -05001275 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001276 list_del(&pos->node);
1277 mutex_unlock(&tz->lock);
1278 goto unbind;
1279 }
1280 }
1281 mutex_unlock(&tz->lock);
1282
1283 return -ENODEV;
1284
Joe Perchescaca8b82012-03-21 12:55:02 -07001285unbind:
Zhang Rui203d3d42008-01-17 15:51:08 +08001286 device_remove_file(&tz->device, &pos->attr);
1287 sysfs_remove_link(&tz->device.kobj, pos->name);
1288 release_idr(&tz->idr, &tz->lock, pos->id);
1289 kfree(pos);
1290 return 0;
1291}
1292EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
1293
1294static void thermal_release(struct device *dev)
1295{
1296 struct thermal_zone_device *tz;
1297 struct thermal_cooling_device *cdev;
1298
Joe Perchescaca8b82012-03-21 12:55:02 -07001299 if (!strncmp(dev_name(dev), "thermal_zone",
1300 sizeof("thermal_zone") - 1)) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001301 tz = to_thermal_zone(dev);
1302 kfree(tz);
1303 } else {
1304 cdev = to_cooling_device(dev);
1305 kfree(cdev);
1306 }
1307}
1308
1309static struct class thermal_class = {
1310 .name = "thermal",
1311 .dev_release = thermal_release,
1312};
1313
1314/**
1315 * thermal_cooling_device_register - register a new thermal cooling device
1316 * @type: the thermal cooling device type.
1317 * @devdata: device private data.
1318 * @ops: standard thermal cooling devices callbacks.
1319 */
Joe Perchescaca8b82012-03-21 12:55:02 -07001320struct thermal_cooling_device *
1321thermal_cooling_device_register(char *type, void *devdata,
1322 const struct thermal_cooling_device_ops *ops)
Zhang Rui203d3d42008-01-17 15:51:08 +08001323{
1324 struct thermal_cooling_device *cdev;
1325 struct thermal_zone_device *pos;
1326 int result;
1327
1328 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001329 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001330
1331 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
Len Brown543a9562008-02-07 16:55:08 -05001332 !ops->set_cur_state)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001333 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001334
1335 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
1336 if (!cdev)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001337 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001338
1339 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
1340 if (result) {
1341 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001342 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001343 }
1344
1345 strcpy(cdev->type, type);
1346 cdev->ops = ops;
1347 cdev->device.class = &thermal_class;
1348 cdev->devdata = devdata;
Kay Sievers354655e2009-01-06 10:44:37 -08001349 dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001350 result = device_register(&cdev->device);
1351 if (result) {
1352 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1353 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001354 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001355 }
1356
1357 /* sys I/F */
1358 if (type) {
Len Brown543a9562008-02-07 16:55:08 -05001359 result = device_create_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001360 if (result)
1361 goto unregister;
1362 }
1363
1364 result = device_create_file(&cdev->device, &dev_attr_max_state);
1365 if (result)
1366 goto unregister;
1367
1368 result = device_create_file(&cdev->device, &dev_attr_cur_state);
1369 if (result)
1370 goto unregister;
1371
1372 mutex_lock(&thermal_list_lock);
1373 list_add(&cdev->node, &thermal_cdev_list);
1374 list_for_each_entry(pos, &thermal_tz_list, node) {
1375 if (!pos->ops->bind)
1376 continue;
1377 result = pos->ops->bind(pos, cdev);
1378 if (result)
1379 break;
1380
1381 }
1382 mutex_unlock(&thermal_list_lock);
1383
1384 if (!result)
1385 return cdev;
1386
Joe Perchescaca8b82012-03-21 12:55:02 -07001387unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001388 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1389 device_unregister(&cdev->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001390 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001391}
1392EXPORT_SYMBOL(thermal_cooling_device_register);
1393
1394/**
1395 * thermal_cooling_device_unregister - removes the registered thermal cooling device
Zhang Rui203d3d42008-01-17 15:51:08 +08001396 * @cdev: the thermal cooling device to remove.
1397 *
1398 * thermal_cooling_device_unregister() must be called when the device is no
1399 * longer needed.
1400 */
1401void thermal_cooling_device_unregister(struct
1402 thermal_cooling_device
1403 *cdev)
1404{
1405 struct thermal_zone_device *tz;
1406 struct thermal_cooling_device *pos = NULL;
1407
1408 if (!cdev)
1409 return;
1410
1411 mutex_lock(&thermal_list_lock);
1412 list_for_each_entry(pos, &thermal_cdev_list, node)
1413 if (pos == cdev)
1414 break;
1415 if (pos != cdev) {
1416 /* thermal cooling device not found */
1417 mutex_unlock(&thermal_list_lock);
1418 return;
1419 }
1420 list_del(&cdev->node);
1421 list_for_each_entry(tz, &thermal_tz_list, node) {
1422 if (!tz->ops->unbind)
1423 continue;
1424 tz->ops->unbind(tz, cdev);
1425 }
1426 mutex_unlock(&thermal_list_lock);
1427 if (cdev->type[0])
Len Brown543a9562008-02-07 16:55:08 -05001428 device_remove_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001429 device_remove_file(&cdev->device, &dev_attr_max_state);
1430 device_remove_file(&cdev->device, &dev_attr_cur_state);
1431
1432 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1433 device_unregister(&cdev->device);
1434 return;
1435}
1436EXPORT_SYMBOL(thermal_cooling_device_unregister);
1437
1438/**
Matthew Garrettb1569e92008-12-03 17:55:32 +00001439 * thermal_zone_device_update - force an update of a thermal zone's state
1440 * @ttz: the thermal zone to update
1441 */
1442
1443void thermal_zone_device_update(struct thermal_zone_device *tz)
1444{
1445 int count, ret = 0;
1446 long temp, trip_temp;
1447 enum thermal_trip_type trip_type;
1448 struct thermal_cooling_device_instance *instance;
1449 struct thermal_cooling_device *cdev;
1450
1451 mutex_lock(&tz->lock);
1452
Michael Brunner0d288162009-08-26 14:29:25 -07001453 if (tz->ops->get_temp(tz, &temp)) {
1454 /* get_temp failed - retry it later */
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001455 pr_warn("failed to read out thermal zone %d\n", tz->id);
Michael Brunner0d288162009-08-26 14:29:25 -07001456 goto leave;
1457 }
Matthew Garrettb1569e92008-12-03 17:55:32 +00001458
1459 for (count = 0; count < tz->trips; count++) {
1460 tz->ops->get_trip_type(tz, count, &trip_type);
1461 tz->ops->get_trip_temp(tz, count, &trip_temp);
1462
1463 switch (trip_type) {
1464 case THERMAL_TRIP_CRITICAL:
Vladimir Zajac29321352009-05-06 19:34:21 +02001465 if (temp >= trip_temp) {
Matthew Garrettb1569e92008-12-03 17:55:32 +00001466 if (tz->ops->notify)
1467 ret = tz->ops->notify(tz, count,
1468 trip_type);
1469 if (!ret) {
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001470 pr_emerg("Critical temperature reached (%ld C), shutting down\n",
1471 temp/1000);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001472 orderly_poweroff(true);
1473 }
1474 }
1475 break;
1476 case THERMAL_TRIP_HOT:
Vladimir Zajac29321352009-05-06 19:34:21 +02001477 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001478 if (tz->ops->notify)
1479 tz->ops->notify(tz, count, trip_type);
1480 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001481 case THERMAL_TRIP_CONFIGURABLE_HI:
1482 if (temp >= trip_temp)
1483 if (tz->ops->notify)
1484 tz->ops->notify(tz, count, trip_type);
1485 break;
1486 case THERMAL_TRIP_CONFIGURABLE_LOW:
1487 if (temp <= trip_temp)
1488 if (tz->ops->notify)
1489 tz->ops->notify(tz, count, trip_type);
1490 break;
1491 case THERMAL_TRIP_CRITICAL_LOW:
1492 if (temp <= trip_temp) {
1493 if (tz->ops->notify)
1494 ret = tz->ops->notify(tz, count,
1495 trip_type);
1496 if (!ret) {
1497 printk(KERN_EMERG
1498 "Critical temperature reached (%ld C), \
1499 shutting down.\n", temp/1000);
1500 orderly_poweroff(true);
1501 }
1502 }
1503 break;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001504 case THERMAL_TRIP_ACTIVE:
1505 list_for_each_entry(instance, &tz->cooling_devices,
1506 node) {
1507 if (instance->trip != count)
1508 continue;
1509
1510 cdev = instance->cdev;
1511
Vladimir Zajac29321352009-05-06 19:34:21 +02001512 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001513 cdev->ops->set_cur_state(cdev, 1);
1514 else
1515 cdev->ops->set_cur_state(cdev, 0);
1516 }
1517 break;
1518 case THERMAL_TRIP_PASSIVE:
Vladimir Zajac29321352009-05-06 19:34:21 +02001519 if (temp >= trip_temp || tz->passive)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001520 thermal_zone_device_passive(tz, temp,
1521 trip_temp, count);
1522 break;
1523 }
1524 }
Matthew Garrett03a971a2008-12-03 18:00:38 +00001525
1526 if (tz->forced_passive)
1527 thermal_zone_device_passive(tz, temp, tz->forced_passive,
1528 THERMAL_TRIPS_NONE);
1529
Matthew Garrettb1569e92008-12-03 17:55:32 +00001530 tz->last_temperature = temp;
Michael Brunner0d288162009-08-26 14:29:25 -07001531
Joe Perchescaca8b82012-03-21 12:55:02 -07001532leave:
Matthew Garrettb1569e92008-12-03 17:55:32 +00001533 if (tz->passive)
1534 thermal_zone_device_set_polling(tz, tz->passive_delay);
1535 else if (tz->polling_delay)
1536 thermal_zone_device_set_polling(tz, tz->polling_delay);
Frans Pop3767cb52009-10-26 08:39:04 +01001537 else
1538 thermal_zone_device_set_polling(tz, 0);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001539 mutex_unlock(&tz->lock);
1540}
1541EXPORT_SYMBOL(thermal_zone_device_update);
1542
1543/**
Zhang Rui203d3d42008-01-17 15:51:08 +08001544 * thermal_zone_device_register - register a new thermal zone device
1545 * @type: the thermal zone device type
1546 * @trips: the number of trip points the thermal zone support
1547 * @devdata: private device data
1548 * @ops: standard thermal zone device callbacks
Matthew Garrettb1569e92008-12-03 17:55:32 +00001549 * @tc1: thermal coefficient 1 for passive calculations
1550 * @tc2: thermal coefficient 2 for passive calculations
1551 * @passive_delay: number of milliseconds to wait between polls when
1552 * performing passive cooling
1553 * @polling_delay: number of milliseconds to wait between polls when checking
1554 * whether trip points have been crossed (0 for interrupt
1555 * driven systems)
Zhang Rui203d3d42008-01-17 15:51:08 +08001556 *
1557 * thermal_zone_device_unregister() must be called when the device is no
Matthew Garrettb1569e92008-12-03 17:55:32 +00001558 * longer needed. The passive cooling formula uses tc1 and tc2 as described in
1559 * section 11.1.5.1 of the ACPI specification 3.0.
Zhang Rui203d3d42008-01-17 15:51:08 +08001560 */
1561struct thermal_zone_device *thermal_zone_device_register(char *type,
Alan Cox5b275ce2010-11-11 15:27:29 +00001562 int trips, void *devdata,
1563 const struct thermal_zone_device_ops *ops,
1564 int tc1, int tc2, int passive_delay, int polling_delay)
Zhang Rui203d3d42008-01-17 15:51:08 +08001565{
1566 struct thermal_zone_device *tz;
1567 struct thermal_cooling_device *pos;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001568 enum thermal_trip_type trip_type;
Zhang Rui203d3d42008-01-17 15:51:08 +08001569 int result;
1570 int count;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001571 int passive = 0;
Zhang Rui203d3d42008-01-17 15:51:08 +08001572
1573 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001574 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001575
1576 if (trips > THERMAL_MAX_TRIPS || trips < 0)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001577 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001578
1579 if (!ops || !ops->get_temp)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001580 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001581
1582 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
1583 if (!tz)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001584 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001585
1586 INIT_LIST_HEAD(&tz->cooling_devices);
1587 idr_init(&tz->idr);
1588 mutex_init(&tz->lock);
1589 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
1590 if (result) {
1591 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001592 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001593 }
1594
1595 strcpy(tz->type, type);
1596 tz->ops = ops;
1597 tz->device.class = &thermal_class;
1598 tz->devdata = devdata;
1599 tz->trips = trips;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001600 tz->tc1 = tc1;
1601 tz->tc2 = tc2;
1602 tz->passive_delay = passive_delay;
1603 tz->polling_delay = polling_delay;
1604
Kay Sievers354655e2009-01-06 10:44:37 -08001605 dev_set_name(&tz->device, "thermal_zone%d", tz->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001606 result = device_register(&tz->device);
1607 if (result) {
1608 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1609 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001610 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001611 }
1612
1613 /* sys I/F */
1614 if (type) {
1615 result = device_create_file(&tz->device, &dev_attr_type);
1616 if (result)
1617 goto unregister;
1618 }
1619
1620 result = device_create_file(&tz->device, &dev_attr_temp);
1621 if (result)
1622 goto unregister;
1623
1624 if (ops->get_mode) {
1625 result = device_create_file(&tz->device, &dev_attr_mode);
1626 if (result)
1627 goto unregister;
1628 }
1629
1630 for (count = 0; count < trips; count++) {
Joe Perchesec797682012-03-21 12:55:02 -07001631 result = device_create_file(&tz->device,
1632 &trip_point_attrs[count * 2]);
1633 if (result)
1634 break;
1635 result = device_create_file(&tz->device,
1636 &trip_point_attrs[count * 2 + 1]);
Zhang Rui203d3d42008-01-17 15:51:08 +08001637 if (result)
1638 goto unregister;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001639 tz->ops->get_trip_type(tz, count, &trip_type);
1640 if (trip_type == THERMAL_TRIP_PASSIVE)
1641 passive = 1;
Zhang Rui203d3d42008-01-17 15:51:08 +08001642 }
1643
Matthew Garrett03a971a2008-12-03 18:00:38 +00001644 if (!passive)
1645 result = device_create_file(&tz->device,
1646 &dev_attr_passive);
1647
1648 if (result)
1649 goto unregister;
1650
Zhang Ruie68b16a2008-04-21 16:07:52 +08001651 result = thermal_add_hwmon_sysfs(tz);
1652 if (result)
1653 goto unregister;
1654
Zhang Rui203d3d42008-01-17 15:51:08 +08001655 mutex_lock(&thermal_list_lock);
1656 list_add_tail(&tz->node, &thermal_tz_list);
1657 if (ops->bind)
1658 list_for_each_entry(pos, &thermal_cdev_list, node) {
Len Brown543a9562008-02-07 16:55:08 -05001659 result = ops->bind(tz, pos);
1660 if (result)
1661 break;
Zhang Rui203d3d42008-01-17 15:51:08 +08001662 }
Praveen Chidambarambb646212013-07-02 13:04:58 -06001663 sensor_init(tz);
Zhang Rui203d3d42008-01-17 15:51:08 +08001664 mutex_unlock(&thermal_list_lock);
1665
Matthew Garrettb1569e92008-12-03 17:55:32 +00001666 INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
1667
1668 thermal_zone_device_update(tz);
1669
Zhang Rui203d3d42008-01-17 15:51:08 +08001670 if (!result)
1671 return tz;
1672
Joe Perchescaca8b82012-03-21 12:55:02 -07001673unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001674 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1675 device_unregister(&tz->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001676 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001677}
1678EXPORT_SYMBOL(thermal_zone_device_register);
1679
1680/**
1681 * thermal_device_unregister - removes the registered thermal zone device
Zhang Rui203d3d42008-01-17 15:51:08 +08001682 * @tz: the thermal zone device to remove
1683 */
1684void thermal_zone_device_unregister(struct thermal_zone_device *tz)
1685{
1686 struct thermal_cooling_device *cdev;
1687 struct thermal_zone_device *pos = NULL;
1688 int count;
1689
1690 if (!tz)
1691 return;
1692
1693 mutex_lock(&thermal_list_lock);
1694 list_for_each_entry(pos, &thermal_tz_list, node)
1695 if (pos == tz)
1696 break;
1697 if (pos != tz) {
1698 /* thermal zone device not found */
1699 mutex_unlock(&thermal_list_lock);
1700 return;
1701 }
1702 list_del(&tz->node);
1703 if (tz->ops->unbind)
1704 list_for_each_entry(cdev, &thermal_cdev_list, node)
1705 tz->ops->unbind(tz, cdev);
1706 mutex_unlock(&thermal_list_lock);
1707
Matthew Garrettb1569e92008-12-03 17:55:32 +00001708 thermal_zone_device_set_polling(tz, 0);
1709
Zhang Rui203d3d42008-01-17 15:51:08 +08001710 if (tz->type[0])
1711 device_remove_file(&tz->device, &dev_attr_type);
1712 device_remove_file(&tz->device, &dev_attr_temp);
1713 if (tz->ops->get_mode)
1714 device_remove_file(&tz->device, &dev_attr_mode);
1715
Joe Perchesec797682012-03-21 12:55:02 -07001716 for (count = 0; count < tz->trips; count++) {
1717 device_remove_file(&tz->device,
1718 &trip_point_attrs[count * 2]);
1719 device_remove_file(&tz->device,
1720 &trip_point_attrs[count * 2 + 1]);
1721 }
Zhang Ruie68b16a2008-04-21 16:07:52 +08001722 thermal_remove_hwmon_sysfs(tz);
Praveen Chidambarambb646212013-07-02 13:04:58 -06001723 flush_work(&tz->sensor.work);
1724 mutex_lock(&thermal_list_lock);
1725 list_del(&tz->sensor.sensor_list);
1726 mutex_unlock(&thermal_list_lock);
Zhang Rui203d3d42008-01-17 15:51:08 +08001727 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1728 idr_destroy(&tz->idr);
1729 mutex_destroy(&tz->lock);
1730 device_unregister(&tz->device);
1731 return;
1732}
Zhang Rui203d3d42008-01-17 15:51:08 +08001733EXPORT_SYMBOL(thermal_zone_device_unregister);
1734
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001735#ifdef CONFIG_NET
1736static struct genl_family thermal_event_genl_family = {
1737 .id = GENL_ID_GENERATE,
1738 .name = THERMAL_GENL_FAMILY_NAME,
1739 .version = THERMAL_GENL_VERSION,
1740 .maxattr = THERMAL_GENL_ATTR_MAX,
1741};
1742
1743static struct genl_multicast_group thermal_event_mcgrp = {
1744 .name = THERMAL_GENL_MCAST_GROUP_NAME,
1745};
1746
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001747int thermal_generate_netlink_event(u32 orig, enum events event)
R.Durgadoss4cb18722010-10-27 03:33:29 +05301748{
1749 struct sk_buff *skb;
1750 struct nlattr *attr;
1751 struct thermal_genl_event *thermal_event;
1752 void *msg_header;
1753 int size;
1754 int result;
Fabio Estevamb11de072012-03-21 12:55:00 -07001755 static unsigned int thermal_event_seqnum;
R.Durgadoss4cb18722010-10-27 03:33:29 +05301756
1757 /* allocate memory */
Joe Perches886ee542012-03-21 12:55:01 -07001758 size = nla_total_size(sizeof(struct thermal_genl_event)) +
1759 nla_total_size(0);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301760
1761 skb = genlmsg_new(size, GFP_ATOMIC);
1762 if (!skb)
1763 return -ENOMEM;
1764
1765 /* add the genetlink message header */
1766 msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
1767 &thermal_event_genl_family, 0,
1768 THERMAL_GENL_CMD_EVENT);
1769 if (!msg_header) {
1770 nlmsg_free(skb);
1771 return -ENOMEM;
1772 }
1773
1774 /* fill the data */
Joe Perches886ee542012-03-21 12:55:01 -07001775 attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
1776 sizeof(struct thermal_genl_event));
R.Durgadoss4cb18722010-10-27 03:33:29 +05301777
1778 if (!attr) {
1779 nlmsg_free(skb);
1780 return -EINVAL;
1781 }
1782
1783 thermal_event = nla_data(attr);
1784 if (!thermal_event) {
1785 nlmsg_free(skb);
1786 return -EINVAL;
1787 }
1788
1789 memset(thermal_event, 0, sizeof(struct thermal_genl_event));
1790
1791 thermal_event->orig = orig;
1792 thermal_event->event = event;
1793
1794 /* send multicast genetlink message */
1795 result = genlmsg_end(skb, msg_header);
1796 if (result < 0) {
1797 nlmsg_free(skb);
1798 return result;
1799 }
1800
1801 result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
1802 if (result)
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001803 pr_info("failed to send netlink event:%d\n", result);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301804
1805 return result;
1806}
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001807EXPORT_SYMBOL(thermal_generate_netlink_event);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301808
1809static int genetlink_init(void)
1810{
1811 int result;
1812
1813 result = genl_register_family(&thermal_event_genl_family);
1814 if (result)
1815 return result;
1816
1817 result = genl_register_mc_group(&thermal_event_genl_family,
1818 &thermal_event_mcgrp);
1819 if (result)
1820 genl_unregister_family(&thermal_event_genl_family);
1821 return result;
1822}
1823
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001824static void genetlink_exit(void)
1825{
1826 genl_unregister_family(&thermal_event_genl_family);
1827}
1828#else /* !CONFIG_NET */
1829static inline int genetlink_init(void) { return 0; }
1830static inline void genetlink_exit(void) {}
1831#endif /* !CONFIG_NET */
1832
Zhang Rui203d3d42008-01-17 15:51:08 +08001833static int __init thermal_init(void)
1834{
1835 int result = 0;
1836
1837 result = class_register(&thermal_class);
1838 if (result) {
1839 idr_destroy(&thermal_tz_idr);
1840 idr_destroy(&thermal_cdev_idr);
1841 mutex_destroy(&thermal_idr_lock);
1842 mutex_destroy(&thermal_list_lock);
1843 }
R.Durgadoss4cb18722010-10-27 03:33:29 +05301844 result = genetlink_init();
Zhang Rui203d3d42008-01-17 15:51:08 +08001845 return result;
1846}
1847
1848static void __exit thermal_exit(void)
1849{
1850 class_unregister(&thermal_class);
1851 idr_destroy(&thermal_tz_idr);
1852 idr_destroy(&thermal_cdev_idr);
1853 mutex_destroy(&thermal_idr_lock);
1854 mutex_destroy(&thermal_list_lock);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301855 genetlink_exit();
Zhang Rui203d3d42008-01-17 15:51:08 +08001856}
1857
R.Durgadoss4cb18722010-10-27 03:33:29 +05301858fs_initcall(thermal_init);
Zhang Rui203d3d42008-01-17 15:51:08 +08001859module_exit(thermal_exit);