blob: 8cd70ac7902fa055dcf81af0991e711896da807c [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 Chandrasekarfbc66242014-01-29 14:45:31 -070095static void init_sensor_trip(struct sensor_info *sensor)
96{
97 int ret = 0, i = 0;
98 enum thermal_trip_type type;
99
100 for (i = 0; ((sensor->max_idx == -1) ||
101 (sensor->min_idx == -1)) &&
102 (sensor->tz->ops->get_trip_type) &&
103 (i < sensor->tz->trips); i++) {
104
105 sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
106 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
107 sensor->max_idx = i;
108 if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
109 sensor->min_idx = i;
110 type = 0;
111 }
112
113 ret = sensor->tz->ops->get_trip_temp(sensor->tz,
114 sensor->min_idx, &sensor->threshold_min);
115 if (ret)
116 pr_err("Unable to get MIN trip temp. sensor:%d err:%d\n",
117 sensor->sensor_id, ret);
118
119 ret = sensor->tz->ops->get_trip_temp(sensor->tz,
120 sensor->max_idx, &sensor->threshold_max);
121 if (ret)
122 pr_err("Unable to get MAX trip temp. sensor:%d err:%d\n",
123 sensor->sensor_id, ret);
124}
125
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600126static int __update_sensor_thresholds(struct sensor_info *sensor)
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600127{
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600128 long max_of_low_thresh = LONG_MIN;
129 long min_of_high_thresh = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600130 struct sensor_threshold *pos, *var;
Ram Chandrasekarfbc66242014-01-29 14:45:31 -0700131 int ret = 0;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600132
133 if (!sensor->tz->ops->set_trip_temp ||
134 !sensor->tz->ops->activate_trip_type ||
135 !sensor->tz->ops->get_trip_type ||
136 !sensor->tz->ops->get_trip_temp) {
137 ret = -ENODEV;
138 goto update_done;
139 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600140
Ram Chandrasekarfbc66242014-01-29 14:45:31 -0700141 if ((sensor->max_idx == -1) || (sensor->min_idx == -1))
142 init_sensor_trip(sensor);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600143
Praveen Chidambarambb646212013-07-02 13:04:58 -0600144 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600145 if (!pos->active)
146 continue;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600147 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600148 if (pos->temp > max_of_low_thresh)
149 max_of_low_thresh = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600150 }
151 if (pos->trip == THERMAL_TRIP_CONFIGURABLE_HI) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600152 if (pos->temp < min_of_high_thresh)
153 min_of_high_thresh = pos->temp;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600154 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600155 }
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600156
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600157 pr_debug("sensor %d: Thresholds: max of low: %ld min of high: %ld\n",
158 sensor->sensor_id, max_of_low_thresh,
159 min_of_high_thresh);
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600160
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600161 if ((min_of_high_thresh != sensor->threshold_max) &&
162 (min_of_high_thresh != LONG_MAX)) {
163 ret = sensor->tz->ops->set_trip_temp(sensor->tz,
164 sensor->max_idx, min_of_high_thresh);
165 if (ret) {
166 pr_err("sensor %d: Unable to set high threshold %d",
167 sensor->sensor_id, ret);
168 goto update_done;
169 }
170 sensor->threshold_max = min_of_high_thresh;
171 }
172 ret = sensor->tz->ops->activate_trip_type(sensor->tz,
173 sensor->max_idx,
174 (min_of_high_thresh == LONG_MAX) ?
175 THERMAL_TRIP_ACTIVATION_DISABLED :
176 THERMAL_TRIP_ACTIVATION_ENABLED);
177 if (ret) {
178 pr_err("sensor %d: Unable to activate high threshold %d",
179 sensor->sensor_id, ret);
180 goto update_done;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600181 }
Praveen Chidambarambb646212013-07-02 13:04:58 -0600182
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600183 if ((max_of_low_thresh != sensor->threshold_min) &&
184 (max_of_low_thresh != LONG_MIN)) {
185 ret = sensor->tz->ops->set_trip_temp(sensor->tz,
186 sensor->min_idx, max_of_low_thresh);
187 if (ret) {
188 pr_err("sensor %d: Unable to set low threshold %d",
189 sensor->sensor_id, ret);
190 goto update_done;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600191 }
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600192 sensor->threshold_min = max_of_low_thresh;
193 }
194 ret = sensor->tz->ops->activate_trip_type(sensor->tz,
195 sensor->min_idx,
196 (max_of_low_thresh == LONG_MIN) ?
197 THERMAL_TRIP_ACTIVATION_DISABLED :
198 THERMAL_TRIP_ACTIVATION_ENABLED);
199 if (ret) {
200 pr_err("sensor %d: Unable to activate low threshold %d",
201 sensor->sensor_id, ret);
202 goto update_done;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600203 }
204
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600205 pr_debug("sensor %d: low: %ld high: %ld\n",
206 sensor->sensor_id,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600207 sensor->threshold_min, sensor->threshold_max);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600208
209update_done:
210 return ret;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600211}
212
213static void sensor_update_work(struct work_struct *work)
214{
215 struct sensor_info *sensor = container_of(work, struct sensor_info,
216 work);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600217 int ret = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600218 mutex_lock(&sensor->lock);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600219 ret = __update_sensor_thresholds(sensor);
220 if (ret)
221 pr_err("sensor %d: Error %d setting threshold\n",
222 sensor->sensor_id, ret);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600223 mutex_unlock(&sensor->lock);
224}
225
226/* May be called in an interrupt context.
227 * Do NOT call sensor_set_trip from this function
228 */
229int thermal_sensor_trip(struct thermal_zone_device *tz,
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600230 enum thermal_trip_type trip, long temp)
Praveen Chidambarambb646212013-07-02 13:04:58 -0600231{
232 struct sensor_threshold *pos, *var;
233 int ret = -ENODEV;
234
235 if (trip != THERMAL_TRIP_CONFIGURABLE_HI &&
236 trip != THERMAL_TRIP_CONFIGURABLE_LOW)
237 return 0;
238
239 if (list_empty(&tz->sensor.threshold_list))
240 return 0;
241
242 list_for_each_entry_safe(pos, var, &tz->sensor.threshold_list, list) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600243 if ((pos->trip != trip) || (!pos->active))
Praveen Chidambarambb646212013-07-02 13:04:58 -0600244 continue;
245 if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
246 (pos->temp <= tz->sensor.threshold_min) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600247 (pos->temp >= temp)) ||
Praveen Chidambarambb646212013-07-02 13:04:58 -0600248 ((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
249 (pos->temp >= tz->sensor.threshold_max) &&
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600250 (pos->temp <= temp))) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600251 pos->active = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600252 pos->notify(trip, temp, pos->data);
253 }
254 }
255
256 schedule_work(&tz->sensor.work);
257
258 return ret;
259}
260EXPORT_SYMBOL(thermal_sensor_trip);
261
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600262int sensor_activate_trip(uint32_t sensor_id,
263 struct sensor_threshold *threshold, bool enable)
264{
265 struct sensor_info *sensor = get_sensor(sensor_id);
266 int ret = 0;
267
268 if (!sensor || !threshold) {
269 pr_err("Sensor %d: uninitialized data\n",
270 sensor_id);
271 ret = -ENODEV;
272 goto activate_trip_exit;
273 }
274
275 mutex_lock(&sensor->lock);
276 threshold->active = (enable) ? 1 : 0;
277 ret = __update_sensor_thresholds(sensor);
278 mutex_unlock(&sensor->lock);
279
280activate_trip_exit:
281 return ret;
282}
283EXPORT_SYMBOL(sensor_activate_trip);
284
Praveen Chidambarambb646212013-07-02 13:04:58 -0600285int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
286{
287 struct sensor_threshold *pos, *var;
288 struct sensor_info *sensor = get_sensor(sensor_id);
289
290 if (!sensor)
291 return -ENODEV;
292
293 if (!threshold || !threshold->notify)
294 return -EFAULT;
295
296 mutex_lock(&sensor->lock);
297 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
298 if (pos == threshold)
299 break;
300 }
301
302 if (pos != threshold) {
303 INIT_LIST_HEAD(&threshold->list);
304 list_add(&threshold->list, &sensor->threshold_list);
305 }
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600306 threshold->active = 0; /* Do not allow active threshold right away */
Praveen Chidambarambb646212013-07-02 13:04:58 -0600307 mutex_unlock(&sensor->lock);
308
309 return 0;
310
311}
312EXPORT_SYMBOL(sensor_set_trip);
313
314int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
315{
316 struct sensor_threshold *pos, *var;
317 struct sensor_info *sensor = get_sensor(sensor_id);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600318 int ret = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600319
320 if (!sensor)
321 return -ENODEV;
322
323 mutex_lock(&sensor->lock);
324 list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
325 if (pos == threshold) {
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600326 pos->active = 0;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600327 list_del(&pos->list);
328 break;
329 }
330 }
331
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600332 ret = __update_sensor_thresholds(sensor);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600333 mutex_unlock(&sensor->lock);
334
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600335 return ret;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600336}
337EXPORT_SYMBOL(sensor_cancel_trip);
338
Praveen Chidambarambb646212013-07-02 13:04:58 -0600339static int tz_notify_trip(enum thermal_trip_type type, int temp, void *data)
340{
341 struct thermal_zone_device *tz = (struct thermal_zone_device *)data;
342
343 pr_debug("sensor %d tripped: type %d temp %d\n",
344 tz->sensor.sensor_id, type, temp);
345
346 return 0;
347}
348
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600349static void get_trip_threshold(struct thermal_zone_device *tz, int trip,
350 struct sensor_threshold **threshold)
351{
352 enum thermal_trip_type type;
353
354 tz->ops->get_trip_type(tz, trip, &type);
355
356 if (type == THERMAL_TRIP_CONFIGURABLE_HI)
357 *threshold = &tz->tz_threshold[0];
358 else if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
359 *threshold = &tz->tz_threshold[1];
360 else
361 *threshold = NULL;
362}
363
Praveen Chidambarambb646212013-07-02 13:04:58 -0600364int sensor_set_trip_temp(struct thermal_zone_device *tz,
365 int trip, long temp)
366{
367 int ret = 0;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600368 struct sensor_threshold *threshold = NULL;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600369
370 if (!tz->ops->get_trip_type)
371 return -EPERM;
372
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600373 get_trip_threshold(tz, trip, &threshold);
374 if (threshold) {
375 threshold->temp = temp;
376 ret = sensor_set_trip(tz->sensor.sensor_id, threshold);
377 } else {
Praveen Chidambarambb646212013-07-02 13:04:58 -0600378 ret = tz->ops->set_trip_temp(tz, trip, temp);
Praveen Chidambarambb646212013-07-02 13:04:58 -0600379 }
380
381 return ret;
382}
383
384int sensor_init(struct thermal_zone_device *tz)
385{
386 struct sensor_info *sensor = &tz->sensor;
387
388 sensor->sensor_id = tz->id;
389 sensor->tz = tz;
Ram Chandrasekarfbc66242014-01-29 14:45:31 -0700390 sensor->threshold_min = LONG_MIN;
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600391 sensor->threshold_max = LONG_MAX;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600392 sensor->max_idx = -1;
393 sensor->min_idx = -1;
394 mutex_init(&sensor->lock);
395 INIT_LIST_HEAD(&sensor->sensor_list);
396 INIT_LIST_HEAD(&sensor->threshold_list);
397 INIT_LIST_HEAD(&tz->tz_threshold[0].list);
398 INIT_LIST_HEAD(&tz->tz_threshold[1].list);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600399 tz->tz_threshold[0].notify = tz_notify_trip;
400 tz->tz_threshold[0].data = tz;
401 tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
402 tz->tz_threshold[1].notify = tz_notify_trip;
403 tz->tz_threshold[1].data = tz;
404 tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
Praveen Chidambarambb646212013-07-02 13:04:58 -0600405 list_add(&sensor->sensor_list, &sensor_info_list);
406 INIT_WORK(&sensor->work, sensor_update_work);
407
408 return 0;
409}
410
Zhang Rui203d3d42008-01-17 15:51:08 +0800411static int get_idr(struct idr *idr, struct mutex *lock, int *id)
412{
413 int err;
414
Joe Perchescaca8b82012-03-21 12:55:02 -0700415again:
Zhang Rui203d3d42008-01-17 15:51:08 +0800416 if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
417 return -ENOMEM;
418
419 if (lock)
420 mutex_lock(lock);
421 err = idr_get_new(idr, NULL, id);
422 if (lock)
423 mutex_unlock(lock);
424 if (unlikely(err == -EAGAIN))
425 goto again;
426 else if (unlikely(err))
427 return err;
428
429 *id = *id & MAX_ID_MASK;
430 return 0;
431}
432
433static void release_idr(struct idr *idr, struct mutex *lock, int id)
434{
435 if (lock)
436 mutex_lock(lock);
437 idr_remove(idr, id);
438 if (lock)
439 mutex_unlock(lock);
440}
441
442/* sys I/F for thermal zone */
443
444#define to_thermal_zone(_dev) \
445 container_of(_dev, struct thermal_zone_device, device)
446
447static ssize_t
448type_show(struct device *dev, struct device_attribute *attr, char *buf)
449{
450 struct thermal_zone_device *tz = to_thermal_zone(dev);
451
452 return sprintf(buf, "%s\n", tz->type);
453}
454
455static ssize_t
456temp_show(struct device *dev, struct device_attribute *attr, char *buf)
457{
458 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000459 long temperature;
460 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800461
462 if (!tz->ops->get_temp)
463 return -EPERM;
464
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000465 ret = tz->ops->get_temp(tz, &temperature);
466
467 if (ret)
468 return ret;
469
470 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800471}
472
473static ssize_t
474mode_show(struct device *dev, struct device_attribute *attr, char *buf)
475{
476 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000477 enum thermal_device_mode mode;
478 int result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800479
480 if (!tz->ops->get_mode)
481 return -EPERM;
482
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000483 result = tz->ops->get_mode(tz, &mode);
484 if (result)
485 return result;
486
487 return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
488 : "disabled");
Zhang Rui203d3d42008-01-17 15:51:08 +0800489}
490
491static ssize_t
492mode_store(struct device *dev, struct device_attribute *attr,
493 const char *buf, size_t count)
494{
495 struct thermal_zone_device *tz = to_thermal_zone(dev);
496 int result;
497
498 if (!tz->ops->set_mode)
499 return -EPERM;
500
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530501 if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000502 result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
Amit Daniel Kachhapf1f0e2a2012-03-21 16:40:01 +0530503 else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000504 result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
505 else
506 result = -EINVAL;
507
Zhang Rui203d3d42008-01-17 15:51:08 +0800508 if (result)
509 return result;
510
511 return count;
512}
513
514static ssize_t
515trip_point_type_show(struct device *dev, struct device_attribute *attr,
516 char *buf)
517{
518 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000519 enum thermal_trip_type type;
520 int trip, result;
Zhang Rui203d3d42008-01-17 15:51:08 +0800521
522 if (!tz->ops->get_trip_type)
523 return -EPERM;
524
525 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
526 return -EINVAL;
527
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000528 result = tz->ops->get_trip_type(tz, trip, &type);
529 if (result)
530 return result;
531
532 switch (type) {
533 case THERMAL_TRIP_CRITICAL:
Amit Kucheria625120a2009-10-16 12:46:02 +0300534 return sprintf(buf, "critical\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000535 case THERMAL_TRIP_HOT:
Amit Kucheria625120a2009-10-16 12:46:02 +0300536 return sprintf(buf, "hot\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 case THERMAL_TRIP_CONFIGURABLE_HI:
538 return sprintf(buf, "configurable_hi\n");
539 case THERMAL_TRIP_CONFIGURABLE_LOW:
540 return sprintf(buf, "configurable_low\n");
541 case THERMAL_TRIP_CRITICAL_LOW:
542 return sprintf(buf, "critical_low\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000543 case THERMAL_TRIP_PASSIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300544 return sprintf(buf, "passive\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000545 case THERMAL_TRIP_ACTIVE:
Amit Kucheria625120a2009-10-16 12:46:02 +0300546 return sprintf(buf, "active\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000547 default:
Amit Kucheria625120a2009-10-16 12:46:02 +0300548 return sprintf(buf, "unknown\n");
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000549 }
Zhang Rui203d3d42008-01-17 15:51:08 +0800550}
551
552static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553trip_point_type_activate(struct device *dev, struct device_attribute *attr,
554 const char *buf, size_t count)
555{
556 struct thermal_zone_device *tz = to_thermal_zone(dev);
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600557 int trip, result = 0;
558 bool activate;
559 struct sensor_threshold *threshold = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600561 if (!tz->ops->get_trip_type ||
562 !tz->ops->activate_trip_type) {
563 result = -EPERM;
564 goto trip_activate_exit;
565 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600567 if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 result = -EINVAL;
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600569 goto trip_activate_exit;
570 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571
Ram Chandrasekarb2f16712013-10-02 11:06:42 -0600572 if (!strcmp(buf, "enabled")) {
573 activate = true;
574 } else if (!strcmp(buf, "disabled")) {
575 activate = false;
576 } else {
577 result = -EINVAL;
578 goto trip_activate_exit;
579 }
580
581 get_trip_threshold(tz, trip, &threshold);
582 if (threshold)
583 result = sensor_activate_trip(tz->sensor.sensor_id,
584 threshold, activate);
585 else
586 result = tz->ops->activate_trip_type(tz, trip,
587 activate ? THERMAL_TRIP_ACTIVATION_ENABLED :
588 THERMAL_TRIP_ACTIVATION_DISABLED);
589
590trip_activate_exit:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591 if (result)
592 return result;
593
594 return count;
595}
596
597static ssize_t
Zhang Rui203d3d42008-01-17 15:51:08 +0800598trip_point_temp_show(struct device *dev, struct device_attribute *attr,
599 char *buf)
600{
601 struct thermal_zone_device *tz = to_thermal_zone(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000602 int trip, ret;
603 long temperature;
Zhang Rui203d3d42008-01-17 15:51:08 +0800604
605 if (!tz->ops->get_trip_temp)
606 return -EPERM;
607
608 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
609 return -EINVAL;
610
Praveen Chidambaram3af26db2013-09-17 13:51:42 -0600611 ret = tz->ops->get_trip_temp(tz, trip, &temperature);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000612
613 if (ret)
614 return ret;
615
616 return sprintf(buf, "%ld\n", temperature);
Zhang Rui203d3d42008-01-17 15:51:08 +0800617}
618
Matthew Garrett03a971a2008-12-03 18:00:38 +0000619static ssize_t
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700620trip_point_temp_set(struct device *dev, struct device_attribute *attr,
621 const char *buf, size_t count)
622{
623 struct thermal_zone_device *tz = to_thermal_zone(dev);
624 int trip, ret;
625 long temperature;
626
627 if (!tz->ops->set_trip_temp)
628 return -EPERM;
629
630 if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
631 return -EINVAL;
632
633 if (!sscanf(buf, "%ld", &temperature))
634 return -EINVAL;
635
Praveen Chidambarambb646212013-07-02 13:04:58 -0600636 ret = sensor_set_trip_temp(tz, trip, temperature);
637
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638 if (ret)
639 return ret;
640
641 return count;
642}
643
644static ssize_t
Matthew Garrett03a971a2008-12-03 18:00:38 +0000645passive_store(struct device *dev, struct device_attribute *attr,
646 const char *buf, size_t count)
647{
648 struct thermal_zone_device *tz = to_thermal_zone(dev);
649 struct thermal_cooling_device *cdev = NULL;
650 int state;
651
652 if (!sscanf(buf, "%d\n", &state))
653 return -EINVAL;
654
Frans Pop3d8e3ad2009-10-26 08:39:02 +0100655 /* sanity check: values below 1000 millicelcius don't make sense
656 * and can cause the system to go into a thermal heart attack
657 */
658 if (state && state < 1000)
659 return -EINVAL;
660
Matthew Garrett03a971a2008-12-03 18:00:38 +0000661 if (state && !tz->forced_passive) {
662 mutex_lock(&thermal_list_lock);
663 list_for_each_entry(cdev, &thermal_cdev_list, node) {
664 if (!strncmp("Processor", cdev->type,
665 sizeof("Processor")))
666 thermal_zone_bind_cooling_device(tz,
667 THERMAL_TRIPS_NONE,
668 cdev);
669 }
670 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100671 if (!tz->passive_delay)
672 tz->passive_delay = 1000;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000673 } else if (!state && tz->forced_passive) {
674 mutex_lock(&thermal_list_lock);
675 list_for_each_entry(cdev, &thermal_cdev_list, node) {
676 if (!strncmp("Processor", cdev->type,
677 sizeof("Processor")))
678 thermal_zone_unbind_cooling_device(tz,
679 THERMAL_TRIPS_NONE,
680 cdev);
681 }
682 mutex_unlock(&thermal_list_lock);
Frans Pope4143b02009-10-26 08:39:03 +0100683 tz->passive_delay = 0;
Matthew Garrett03a971a2008-12-03 18:00:38 +0000684 }
685
686 tz->tc1 = 1;
687 tz->tc2 = 1;
688
Matthew Garrett03a971a2008-12-03 18:00:38 +0000689 tz->forced_passive = state;
690
691 thermal_zone_device_update(tz);
692
693 return count;
694}
695
696static ssize_t
697passive_show(struct device *dev, struct device_attribute *attr,
698 char *buf)
699{
700 struct thermal_zone_device *tz = to_thermal_zone(dev);
701
702 return sprintf(buf, "%d\n", tz->forced_passive);
703}
704
Zhang Rui203d3d42008-01-17 15:51:08 +0800705static DEVICE_ATTR(type, 0444, type_show, NULL);
706static DEVICE_ATTR(temp, 0444, temp_show, NULL);
707static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
Joe Perches886ee542012-03-21 12:55:01 -0700708static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
Zhang Rui203d3d42008-01-17 15:51:08 +0800709
710static struct device_attribute trip_point_attrs[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711 __ATTR(trip_point_0_type, 0644, trip_point_type_show,
712 trip_point_type_activate),
713 __ATTR(trip_point_0_temp, 0644, trip_point_temp_show,
714 trip_point_temp_set),
715 __ATTR(trip_point_1_type, 0644, trip_point_type_show,
716 trip_point_type_activate),
717 __ATTR(trip_point_1_temp, 0644, trip_point_temp_show,
718 trip_point_temp_set),
719 __ATTR(trip_point_2_type, 0644, trip_point_type_show,
720 trip_point_type_activate),
721 __ATTR(trip_point_2_temp, 0644, trip_point_temp_show,
722 trip_point_temp_set),
723 __ATTR(trip_point_3_type, 0644, trip_point_type_show,
724 trip_point_type_activate),
725 __ATTR(trip_point_3_temp, 0644, trip_point_temp_show,
726 trip_point_temp_set),
727 __ATTR(trip_point_4_type, 0644, trip_point_type_show,
728 trip_point_type_activate),
729 __ATTR(trip_point_4_temp, 0644, trip_point_temp_show,
730 trip_point_temp_set),
731 __ATTR(trip_point_5_type, 0644, trip_point_type_show,
732 trip_point_type_activate),
733 __ATTR(trip_point_5_temp, 0644, trip_point_temp_show,
734 trip_point_temp_set),
735 __ATTR(trip_point_6_type, 0644, trip_point_type_show,
736 trip_point_type_activate),
737 __ATTR(trip_point_6_temp, 0644, trip_point_temp_show,
738 trip_point_temp_set),
739 __ATTR(trip_point_7_type, 0644, trip_point_type_show,
740 trip_point_type_activate),
741 __ATTR(trip_point_7_temp, 0644, trip_point_temp_show,
742 trip_point_temp_set),
743 __ATTR(trip_point_8_type, 0644, trip_point_type_show,
744 trip_point_type_activate),
745 __ATTR(trip_point_8_temp, 0644, trip_point_temp_show,
746 trip_point_temp_set),
747 __ATTR(trip_point_9_type, 0644, trip_point_type_show,
748 trip_point_type_activate),
749 __ATTR(trip_point_9_temp, 0644, trip_point_temp_show,
750 trip_point_temp_set),
751 __ATTR(trip_point_10_type, 0644, trip_point_type_show,
752 trip_point_type_activate),
753 __ATTR(trip_point_10_temp, 0644, trip_point_temp_show,
754 trip_point_temp_set),
755 __ATTR(trip_point_11_type, 0644, trip_point_type_show,
756 trip_point_type_activate),
757 __ATTR(trip_point_11_temp, 0644, trip_point_temp_show,
758 trip_point_temp_set),
Zhang Rui203d3d42008-01-17 15:51:08 +0800759};
760
Zhang Rui203d3d42008-01-17 15:51:08 +0800761/* sys I/F for cooling device */
762#define to_cooling_device(_dev) \
763 container_of(_dev, struct thermal_cooling_device, device)
764
765static ssize_t
766thermal_cooling_device_type_show(struct device *dev,
767 struct device_attribute *attr, char *buf)
768{
769 struct thermal_cooling_device *cdev = to_cooling_device(dev);
770
771 return sprintf(buf, "%s\n", cdev->type);
772}
773
774static ssize_t
775thermal_cooling_device_max_state_show(struct device *dev,
776 struct device_attribute *attr, char *buf)
777{
778 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000779 unsigned long state;
780 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800781
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000782 ret = cdev->ops->get_max_state(cdev, &state);
783 if (ret)
784 return ret;
785 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800786}
787
788static ssize_t
789thermal_cooling_device_cur_state_show(struct device *dev,
790 struct device_attribute *attr, char *buf)
791{
792 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000793 unsigned long state;
794 int ret;
Zhang Rui203d3d42008-01-17 15:51:08 +0800795
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000796 ret = cdev->ops->get_cur_state(cdev, &state);
797 if (ret)
798 return ret;
799 return sprintf(buf, "%ld\n", state);
Zhang Rui203d3d42008-01-17 15:51:08 +0800800}
801
802static ssize_t
803thermal_cooling_device_cur_state_store(struct device *dev,
804 struct device_attribute *attr,
805 const char *buf, size_t count)
806{
807 struct thermal_cooling_device *cdev = to_cooling_device(dev);
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000808 unsigned long state;
Zhang Rui203d3d42008-01-17 15:51:08 +0800809 int result;
810
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000811 if (!sscanf(buf, "%ld\n", &state))
Zhang Rui203d3d42008-01-17 15:51:08 +0800812 return -EINVAL;
813
Roel Kluinedb94912009-12-15 22:46:50 +0100814 if ((long)state < 0)
Zhang Rui203d3d42008-01-17 15:51:08 +0800815 return -EINVAL;
816
817 result = cdev->ops->set_cur_state(cdev, state);
818 if (result)
819 return result;
820 return count;
821}
822
823static struct device_attribute dev_attr_cdev_type =
Len Brown543a9562008-02-07 16:55:08 -0500824__ATTR(type, 0444, thermal_cooling_device_type_show, NULL);
Zhang Rui203d3d42008-01-17 15:51:08 +0800825static DEVICE_ATTR(max_state, 0444,
826 thermal_cooling_device_max_state_show, NULL);
827static DEVICE_ATTR(cur_state, 0644,
828 thermal_cooling_device_cur_state_show,
829 thermal_cooling_device_cur_state_store);
830
831static ssize_t
832thermal_cooling_device_trip_point_show(struct device *dev,
Len Brown543a9562008-02-07 16:55:08 -0500833 struct device_attribute *attr, char *buf)
Zhang Rui203d3d42008-01-17 15:51:08 +0800834{
835 struct thermal_cooling_device_instance *instance;
836
837 instance =
838 container_of(attr, struct thermal_cooling_device_instance, attr);
839
840 if (instance->trip == THERMAL_TRIPS_NONE)
841 return sprintf(buf, "-1\n");
842 else
843 return sprintf(buf, "%d\n", instance->trip);
844}
845
846/* Device management */
847
Rene Herman16d75232008-06-24 19:38:56 +0200848#if defined(CONFIG_THERMAL_HWMON)
849
Zhang Ruie68b16a2008-04-21 16:07:52 +0800850/* hwmon sys I/F */
851#include <linux/hwmon.h>
Jean Delvare31f53962011-07-28 13:48:42 -0700852
853/* thermal zone devices with the same type share one hwmon device */
854struct thermal_hwmon_device {
855 char type[THERMAL_NAME_LENGTH];
856 struct device *device;
857 int count;
858 struct list_head tz_list;
859 struct list_head node;
860};
861
862struct thermal_hwmon_attr {
863 struct device_attribute attr;
864 char name[16];
865};
866
867/* one temperature input for each thermal zone */
868struct thermal_hwmon_temp {
869 struct list_head hwmon_node;
870 struct thermal_zone_device *tz;
871 struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
872 struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
873};
874
Zhang Ruie68b16a2008-04-21 16:07:52 +0800875static LIST_HEAD(thermal_hwmon_list);
876
877static ssize_t
878name_show(struct device *dev, struct device_attribute *attr, char *buf)
879{
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700880 struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800881 return sprintf(buf, "%s\n", hwmon->type);
882}
883static DEVICE_ATTR(name, 0444, name_show, NULL);
884
885static ssize_t
886temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
887{
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000888 long temperature;
889 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800890 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_input);
Jean Delvare31f53962011-07-28 13:48:42 -0700895 struct thermal_zone_device *tz = temp->tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800896
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000897 ret = tz->ops->get_temp(tz, &temperature);
898
899 if (ret)
900 return ret;
901
902 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800903}
904
905static ssize_t
906temp_crit_show(struct device *dev, struct device_attribute *attr,
907 char *buf)
908{
909 struct thermal_hwmon_attr *hwmon_attr
910 = container_of(attr, struct thermal_hwmon_attr, attr);
Jean Delvare31f53962011-07-28 13:48:42 -0700911 struct thermal_hwmon_temp *temp
912 = container_of(hwmon_attr, struct thermal_hwmon_temp,
Zhang Ruie68b16a2008-04-21 16:07:52 +0800913 temp_crit);
Jean Delvare31f53962011-07-28 13:48:42 -0700914 struct thermal_zone_device *tz = temp->tz;
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000915 long temperature;
916 int ret;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800917
Matthew Garrett6503e5d2008-11-27 17:48:13 +0000918 ret = tz->ops->get_trip_temp(tz, 0, &temperature);
919 if (ret)
920 return ret;
921
922 return sprintf(buf, "%ld\n", temperature);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800923}
924
925
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700926static struct thermal_hwmon_device *
927thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
928{
929 struct thermal_hwmon_device *hwmon;
930
931 mutex_lock(&thermal_list_lock);
932 list_for_each_entry(hwmon, &thermal_hwmon_list, node)
933 if (!strcmp(hwmon->type, tz->type)) {
934 mutex_unlock(&thermal_list_lock);
935 return hwmon;
936 }
937 mutex_unlock(&thermal_list_lock);
938
939 return NULL;
940}
941
Jean Delvare31f53962011-07-28 13:48:42 -0700942/* Find the temperature input matching a given thermal zone */
943static struct thermal_hwmon_temp *
944thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
945 const struct thermal_zone_device *tz)
946{
947 struct thermal_hwmon_temp *temp;
948
949 mutex_lock(&thermal_list_lock);
950 list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
951 if (temp->tz == tz) {
952 mutex_unlock(&thermal_list_lock);
953 return temp;
954 }
955 mutex_unlock(&thermal_list_lock);
956
957 return NULL;
958}
959
Zhang Ruie68b16a2008-04-21 16:07:52 +0800960static int
961thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
962{
963 struct thermal_hwmon_device *hwmon;
Jean Delvare31f53962011-07-28 13:48:42 -0700964 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800965 int new_hwmon_device = 1;
966 int result;
967
Jean Delvare0d97d7a2011-07-28 13:48:41 -0700968 hwmon = thermal_hwmon_lookup_by_type(tz);
969 if (hwmon) {
970 new_hwmon_device = 0;
971 goto register_sys_interface;
972 }
Zhang Ruie68b16a2008-04-21 16:07:52 +0800973
974 hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
975 if (!hwmon)
976 return -ENOMEM;
977
978 INIT_LIST_HEAD(&hwmon->tz_list);
979 strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
980 hwmon->device = hwmon_device_register(NULL);
981 if (IS_ERR(hwmon->device)) {
982 result = PTR_ERR(hwmon->device);
983 goto free_mem;
984 }
Greg Kroah-Hartman0e968a32009-04-30 14:43:31 -0700985 dev_set_drvdata(hwmon->device, hwmon);
Zhang Ruie68b16a2008-04-21 16:07:52 +0800986 result = device_create_file(hwmon->device, &dev_attr_name);
987 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +0530988 goto free_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800989
990 register_sys_interface:
Jean Delvare31f53962011-07-28 13:48:42 -0700991 temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
992 if (!temp) {
993 result = -ENOMEM;
994 goto unregister_name;
995 }
996
997 temp->tz = tz;
Zhang Ruie68b16a2008-04-21 16:07:52 +0800998 hwmon->count++;
999
Jean Delvare31f53962011-07-28 13:48:42 -07001000 snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +08001001 "temp%d_input", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -07001002 temp->temp_input.attr.attr.name = temp->temp_input.name;
1003 temp->temp_input.attr.attr.mode = 0444;
1004 temp->temp_input.attr.show = temp_input_show;
1005 sysfs_attr_init(&temp->temp_input.attr.attr);
1006 result = device_create_file(hwmon->device, &temp->temp_input.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001007 if (result)
Jean Delvare31f53962011-07-28 13:48:42 -07001008 goto free_temp_mem;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001009
1010 if (tz->ops->get_crit_temp) {
1011 unsigned long temperature;
1012 if (!tz->ops->get_crit_temp(tz, &temperature)) {
Jean Delvare31f53962011-07-28 13:48:42 -07001013 snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
Zhang Ruie68b16a2008-04-21 16:07:52 +08001014 "temp%d_crit", hwmon->count);
Jean Delvare31f53962011-07-28 13:48:42 -07001015 temp->temp_crit.attr.attr.name = temp->temp_crit.name;
1016 temp->temp_crit.attr.attr.mode = 0444;
1017 temp->temp_crit.attr.show = temp_crit_show;
1018 sysfs_attr_init(&temp->temp_crit.attr.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001019 result = device_create_file(hwmon->device,
Jean Delvare31f53962011-07-28 13:48:42 -07001020 &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001021 if (result)
Durgadoss Rb299eb52011-03-03 04:30:13 +05301022 goto unregister_input;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001023 }
1024 }
1025
1026 mutex_lock(&thermal_list_lock);
1027 if (new_hwmon_device)
1028 list_add_tail(&hwmon->node, &thermal_hwmon_list);
Jean Delvare31f53962011-07-28 13:48:42 -07001029 list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001030 mutex_unlock(&thermal_list_lock);
1031
1032 return 0;
1033
Durgadoss Rb299eb52011-03-03 04:30:13 +05301034 unregister_input:
Jean Delvare31f53962011-07-28 13:48:42 -07001035 device_remove_file(hwmon->device, &temp->temp_input.attr);
1036 free_temp_mem:
1037 kfree(temp);
Durgadoss Rb299eb52011-03-03 04:30:13 +05301038 unregister_name:
Zhang Ruie68b16a2008-04-21 16:07:52 +08001039 if (new_hwmon_device) {
1040 device_remove_file(hwmon->device, &dev_attr_name);
1041 hwmon_device_unregister(hwmon->device);
1042 }
1043 free_mem:
1044 if (new_hwmon_device)
1045 kfree(hwmon);
1046
1047 return result;
1048}
1049
1050static void
1051thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1052{
Jean Delvare31f53962011-07-28 13:48:42 -07001053 struct thermal_hwmon_device *hwmon;
1054 struct thermal_hwmon_temp *temp;
Zhang Ruie68b16a2008-04-21 16:07:52 +08001055
Jean Delvare31f53962011-07-28 13:48:42 -07001056 hwmon = thermal_hwmon_lookup_by_type(tz);
1057 if (unlikely(!hwmon)) {
1058 /* Should never happen... */
1059 dev_dbg(&tz->device, "hwmon device lookup failed!\n");
1060 return;
1061 }
1062
1063 temp = thermal_hwmon_lookup_temp(hwmon, tz);
1064 if (unlikely(!temp)) {
1065 /* Should never happen... */
1066 dev_dbg(&tz->device, "temperature input lookup failed!\n");
1067 return;
1068 }
1069
1070 device_remove_file(hwmon->device, &temp->temp_input.attr);
Durgadoss Rb299eb52011-03-03 04:30:13 +05301071 if (tz->ops->get_crit_temp)
Jean Delvare31f53962011-07-28 13:48:42 -07001072 device_remove_file(hwmon->device, &temp->temp_crit.attr);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001073
1074 mutex_lock(&thermal_list_lock);
Jean Delvare31f53962011-07-28 13:48:42 -07001075 list_del(&temp->hwmon_node);
1076 kfree(temp);
Zhang Ruie68b16a2008-04-21 16:07:52 +08001077 if (!list_empty(&hwmon->tz_list)) {
1078 mutex_unlock(&thermal_list_lock);
1079 return;
1080 }
1081 list_del(&hwmon->node);
1082 mutex_unlock(&thermal_list_lock);
1083
1084 device_remove_file(hwmon->device, &dev_attr_name);
1085 hwmon_device_unregister(hwmon->device);
1086 kfree(hwmon);
1087}
1088#else
1089static int
1090thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
1091{
1092 return 0;
1093}
1094
1095static void
1096thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
1097{
1098}
1099#endif
1100
Matthew Garrettb1569e92008-12-03 17:55:32 +00001101static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
1102 int delay)
1103{
1104 cancel_delayed_work(&(tz->poll_queue));
1105
1106 if (!delay)
1107 return;
1108
1109 if (delay > 1000)
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001110 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001111 round_jiffies(msecs_to_jiffies(delay)));
1112 else
Rafael J. Wysocki51e20d02011-11-06 14:21:38 +01001113 queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
Matthew Garrettb1569e92008-12-03 17:55:32 +00001114 msecs_to_jiffies(delay));
1115}
1116
1117static void thermal_zone_device_passive(struct thermal_zone_device *tz,
1118 int temp, int trip_temp, int trip)
1119{
1120 int trend = 0;
1121 struct thermal_cooling_device_instance *instance;
1122 struct thermal_cooling_device *cdev;
1123 long state, max_state;
1124
1125 /*
1126 * Above Trip?
1127 * -----------
1128 * Calculate the thermal trend (using the passive cooling equation)
1129 * and modify the performance limit for all passive cooling devices
1130 * accordingly. Note that we assume symmetry.
1131 */
1132 if (temp >= trip_temp) {
1133 tz->passive = true;
1134
1135 trend = (tz->tc1 * (temp - tz->last_temperature)) +
1136 (tz->tc2 * (temp - trip_temp));
1137
1138 /* Heating up? */
1139 if (trend > 0) {
1140 list_for_each_entry(instance, &tz->cooling_devices,
1141 node) {
1142 if (instance->trip != trip)
1143 continue;
1144 cdev = instance->cdev;
1145 cdev->ops->get_cur_state(cdev, &state);
1146 cdev->ops->get_max_state(cdev, &max_state);
1147 if (state++ < max_state)
1148 cdev->ops->set_cur_state(cdev, state);
1149 }
1150 } else if (trend < 0) { /* Cooling off? */
1151 list_for_each_entry(instance, &tz->cooling_devices,
1152 node) {
1153 if (instance->trip != trip)
1154 continue;
1155 cdev = instance->cdev;
1156 cdev->ops->get_cur_state(cdev, &state);
1157 cdev->ops->get_max_state(cdev, &max_state);
1158 if (state > 0)
1159 cdev->ops->set_cur_state(cdev, --state);
1160 }
1161 }
1162 return;
1163 }
1164
1165 /*
1166 * Below Trip?
1167 * -----------
1168 * Implement passive cooling hysteresis to slowly increase performance
1169 * and avoid thrashing around the passive trip point. Note that we
1170 * assume symmetry.
1171 */
1172 list_for_each_entry(instance, &tz->cooling_devices, node) {
1173 if (instance->trip != trip)
1174 continue;
1175 cdev = instance->cdev;
1176 cdev->ops->get_cur_state(cdev, &state);
1177 cdev->ops->get_max_state(cdev, &max_state);
1178 if (state > 0)
1179 cdev->ops->set_cur_state(cdev, --state);
1180 if (state == 0)
1181 tz->passive = false;
1182 }
1183}
1184
1185static void thermal_zone_device_check(struct work_struct *work)
1186{
1187 struct thermal_zone_device *tz = container_of(work, struct
1188 thermal_zone_device,
1189 poll_queue.work);
1190 thermal_zone_device_update(tz);
1191}
Zhang Ruie68b16a2008-04-21 16:07:52 +08001192
Zhang Rui203d3d42008-01-17 15:51:08 +08001193/**
1194 * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001195 * @tz: thermal zone device
1196 * @trip: indicates which trip point the cooling devices is
1197 * associated with in this thermal zone.
1198 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001199 *
1200 * This function is usually called in the thermal zone device .bind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001201 */
1202int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
1203 int trip,
1204 struct thermal_cooling_device *cdev)
1205{
1206 struct thermal_cooling_device_instance *dev;
1207 struct thermal_cooling_device_instance *pos;
Thomas Sujithc7516702008-02-15 00:58:50 -05001208 struct thermal_zone_device *pos1;
1209 struct thermal_cooling_device *pos2;
Zhang Rui203d3d42008-01-17 15:51:08 +08001210 int result;
1211
Len Brown543a9562008-02-07 16:55:08 -05001212 if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
Zhang Rui203d3d42008-01-17 15:51:08 +08001213 return -EINVAL;
1214
Thomas Sujithc7516702008-02-15 00:58:50 -05001215 list_for_each_entry(pos1, &thermal_tz_list, node) {
1216 if (pos1 == tz)
1217 break;
1218 }
1219 list_for_each_entry(pos2, &thermal_cdev_list, node) {
1220 if (pos2 == cdev)
1221 break;
1222 }
1223
1224 if (tz != pos1 || cdev != pos2)
Zhang Rui203d3d42008-01-17 15:51:08 +08001225 return -EINVAL;
1226
1227 dev =
1228 kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL);
1229 if (!dev)
1230 return -ENOMEM;
1231 dev->tz = tz;
1232 dev->cdev = cdev;
1233 dev->trip = trip;
1234 result = get_idr(&tz->idr, &tz->lock, &dev->id);
1235 if (result)
1236 goto free_mem;
1237
1238 sprintf(dev->name, "cdev%d", dev->id);
1239 result =
1240 sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
1241 if (result)
1242 goto release_idr;
1243
1244 sprintf(dev->attr_name, "cdev%d_trip_point", dev->id);
Sergey Senozhatsky975f8c52010-04-06 14:34:51 -07001245 sysfs_attr_init(&dev->attr.attr);
Zhang Rui203d3d42008-01-17 15:51:08 +08001246 dev->attr.attr.name = dev->attr_name;
1247 dev->attr.attr.mode = 0444;
1248 dev->attr.show = thermal_cooling_device_trip_point_show;
1249 result = device_create_file(&tz->device, &dev->attr);
1250 if (result)
1251 goto remove_symbol_link;
1252
1253 mutex_lock(&tz->lock);
1254 list_for_each_entry(pos, &tz->cooling_devices, node)
1255 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
1256 result = -EEXIST;
1257 break;
1258 }
1259 if (!result)
1260 list_add_tail(&dev->node, &tz->cooling_devices);
1261 mutex_unlock(&tz->lock);
1262
1263 if (!result)
1264 return 0;
1265
1266 device_remove_file(&tz->device, &dev->attr);
Joe Perchescaca8b82012-03-21 12:55:02 -07001267remove_symbol_link:
Zhang Rui203d3d42008-01-17 15:51:08 +08001268 sysfs_remove_link(&tz->device.kobj, dev->name);
Joe Perchescaca8b82012-03-21 12:55:02 -07001269release_idr:
Zhang Rui203d3d42008-01-17 15:51:08 +08001270 release_idr(&tz->idr, &tz->lock, dev->id);
Joe Perchescaca8b82012-03-21 12:55:02 -07001271free_mem:
Zhang Rui203d3d42008-01-17 15:51:08 +08001272 kfree(dev);
1273 return result;
1274}
1275EXPORT_SYMBOL(thermal_zone_bind_cooling_device);
1276
1277/**
1278 * thermal_zone_unbind_cooling_device - unbind a cooling device from a thermal zone
Zhang Rui203d3d42008-01-17 15:51:08 +08001279 * @tz: thermal zone device
1280 * @trip: indicates which trip point the cooling devices is
1281 * associated with in this thermal zone.
1282 * @cdev: thermal cooling device
Len Brown543a9562008-02-07 16:55:08 -05001283 *
1284 * This function is usually called in the thermal zone device .unbind callback.
Zhang Rui203d3d42008-01-17 15:51:08 +08001285 */
1286int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
1287 int trip,
1288 struct thermal_cooling_device *cdev)
1289{
1290 struct thermal_cooling_device_instance *pos, *next;
1291
1292 mutex_lock(&tz->lock);
1293 list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) {
Len Brown543a9562008-02-07 16:55:08 -05001294 if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001295 list_del(&pos->node);
1296 mutex_unlock(&tz->lock);
1297 goto unbind;
1298 }
1299 }
1300 mutex_unlock(&tz->lock);
1301
1302 return -ENODEV;
1303
Joe Perchescaca8b82012-03-21 12:55:02 -07001304unbind:
Zhang Rui203d3d42008-01-17 15:51:08 +08001305 device_remove_file(&tz->device, &pos->attr);
1306 sysfs_remove_link(&tz->device.kobj, pos->name);
1307 release_idr(&tz->idr, &tz->lock, pos->id);
1308 kfree(pos);
1309 return 0;
1310}
1311EXPORT_SYMBOL(thermal_zone_unbind_cooling_device);
1312
1313static void thermal_release(struct device *dev)
1314{
1315 struct thermal_zone_device *tz;
1316 struct thermal_cooling_device *cdev;
1317
Joe Perchescaca8b82012-03-21 12:55:02 -07001318 if (!strncmp(dev_name(dev), "thermal_zone",
1319 sizeof("thermal_zone") - 1)) {
Zhang Rui203d3d42008-01-17 15:51:08 +08001320 tz = to_thermal_zone(dev);
1321 kfree(tz);
1322 } else {
1323 cdev = to_cooling_device(dev);
1324 kfree(cdev);
1325 }
1326}
1327
1328static struct class thermal_class = {
1329 .name = "thermal",
1330 .dev_release = thermal_release,
1331};
1332
1333/**
1334 * thermal_cooling_device_register - register a new thermal cooling device
1335 * @type: the thermal cooling device type.
1336 * @devdata: device private data.
1337 * @ops: standard thermal cooling devices callbacks.
1338 */
Joe Perchescaca8b82012-03-21 12:55:02 -07001339struct thermal_cooling_device *
1340thermal_cooling_device_register(char *type, void *devdata,
1341 const struct thermal_cooling_device_ops *ops)
Zhang Rui203d3d42008-01-17 15:51:08 +08001342{
1343 struct thermal_cooling_device *cdev;
1344 struct thermal_zone_device *pos;
1345 int result;
1346
1347 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001348 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001349
1350 if (!ops || !ops->get_max_state || !ops->get_cur_state ||
Len Brown543a9562008-02-07 16:55:08 -05001351 !ops->set_cur_state)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001352 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001353
1354 cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);
1355 if (!cdev)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001356 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001357
1358 result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);
1359 if (result) {
1360 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001361 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001362 }
1363
1364 strcpy(cdev->type, type);
1365 cdev->ops = ops;
1366 cdev->device.class = &thermal_class;
1367 cdev->devdata = devdata;
Kay Sievers354655e2009-01-06 10:44:37 -08001368 dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001369 result = device_register(&cdev->device);
1370 if (result) {
1371 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1372 kfree(cdev);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001373 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001374 }
1375
1376 /* sys I/F */
1377 if (type) {
Len Brown543a9562008-02-07 16:55:08 -05001378 result = device_create_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001379 if (result)
1380 goto unregister;
1381 }
1382
1383 result = device_create_file(&cdev->device, &dev_attr_max_state);
1384 if (result)
1385 goto unregister;
1386
1387 result = device_create_file(&cdev->device, &dev_attr_cur_state);
1388 if (result)
1389 goto unregister;
1390
1391 mutex_lock(&thermal_list_lock);
1392 list_add(&cdev->node, &thermal_cdev_list);
1393 list_for_each_entry(pos, &thermal_tz_list, node) {
1394 if (!pos->ops->bind)
1395 continue;
1396 result = pos->ops->bind(pos, cdev);
1397 if (result)
1398 break;
1399
1400 }
1401 mutex_unlock(&thermal_list_lock);
1402
1403 if (!result)
1404 return cdev;
1405
Joe Perchescaca8b82012-03-21 12:55:02 -07001406unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001407 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1408 device_unregister(&cdev->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001409 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001410}
1411EXPORT_SYMBOL(thermal_cooling_device_register);
1412
1413/**
1414 * thermal_cooling_device_unregister - removes the registered thermal cooling device
Zhang Rui203d3d42008-01-17 15:51:08 +08001415 * @cdev: the thermal cooling device to remove.
1416 *
1417 * thermal_cooling_device_unregister() must be called when the device is no
1418 * longer needed.
1419 */
1420void thermal_cooling_device_unregister(struct
1421 thermal_cooling_device
1422 *cdev)
1423{
1424 struct thermal_zone_device *tz;
1425 struct thermal_cooling_device *pos = NULL;
1426
1427 if (!cdev)
1428 return;
1429
1430 mutex_lock(&thermal_list_lock);
1431 list_for_each_entry(pos, &thermal_cdev_list, node)
1432 if (pos == cdev)
1433 break;
1434 if (pos != cdev) {
1435 /* thermal cooling device not found */
1436 mutex_unlock(&thermal_list_lock);
1437 return;
1438 }
1439 list_del(&cdev->node);
1440 list_for_each_entry(tz, &thermal_tz_list, node) {
1441 if (!tz->ops->unbind)
1442 continue;
1443 tz->ops->unbind(tz, cdev);
1444 }
1445 mutex_unlock(&thermal_list_lock);
1446 if (cdev->type[0])
Len Brown543a9562008-02-07 16:55:08 -05001447 device_remove_file(&cdev->device, &dev_attr_cdev_type);
Zhang Rui203d3d42008-01-17 15:51:08 +08001448 device_remove_file(&cdev->device, &dev_attr_max_state);
1449 device_remove_file(&cdev->device, &dev_attr_cur_state);
1450
1451 release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
1452 device_unregister(&cdev->device);
1453 return;
1454}
1455EXPORT_SYMBOL(thermal_cooling_device_unregister);
1456
1457/**
Matthew Garrettb1569e92008-12-03 17:55:32 +00001458 * thermal_zone_device_update - force an update of a thermal zone's state
1459 * @ttz: the thermal zone to update
1460 */
1461
1462void thermal_zone_device_update(struct thermal_zone_device *tz)
1463{
1464 int count, ret = 0;
1465 long temp, trip_temp;
1466 enum thermal_trip_type trip_type;
1467 struct thermal_cooling_device_instance *instance;
1468 struct thermal_cooling_device *cdev;
1469
1470 mutex_lock(&tz->lock);
1471
Michael Brunner0d288162009-08-26 14:29:25 -07001472 if (tz->ops->get_temp(tz, &temp)) {
1473 /* get_temp failed - retry it later */
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001474 pr_warn("failed to read out thermal zone %d\n", tz->id);
Michael Brunner0d288162009-08-26 14:29:25 -07001475 goto leave;
1476 }
Matthew Garrettb1569e92008-12-03 17:55:32 +00001477
1478 for (count = 0; count < tz->trips; count++) {
1479 tz->ops->get_trip_type(tz, count, &trip_type);
1480 tz->ops->get_trip_temp(tz, count, &trip_temp);
1481
1482 switch (trip_type) {
1483 case THERMAL_TRIP_CRITICAL:
Vladimir Zajac29321352009-05-06 19:34:21 +02001484 if (temp >= trip_temp) {
Matthew Garrettb1569e92008-12-03 17:55:32 +00001485 if (tz->ops->notify)
1486 ret = tz->ops->notify(tz, count,
1487 trip_type);
1488 if (!ret) {
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001489 pr_emerg("Critical temperature reached (%ld C), shutting down\n",
1490 temp/1000);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001491 orderly_poweroff(true);
1492 }
1493 }
1494 break;
1495 case THERMAL_TRIP_HOT:
Vladimir Zajac29321352009-05-06 19:34:21 +02001496 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001497 if (tz->ops->notify)
1498 tz->ops->notify(tz, count, trip_type);
1499 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001500 case THERMAL_TRIP_CONFIGURABLE_HI:
1501 if (temp >= trip_temp)
1502 if (tz->ops->notify)
1503 tz->ops->notify(tz, count, trip_type);
1504 break;
1505 case THERMAL_TRIP_CONFIGURABLE_LOW:
1506 if (temp <= trip_temp)
1507 if (tz->ops->notify)
1508 tz->ops->notify(tz, count, trip_type);
1509 break;
1510 case THERMAL_TRIP_CRITICAL_LOW:
1511 if (temp <= trip_temp) {
1512 if (tz->ops->notify)
1513 ret = tz->ops->notify(tz, count,
1514 trip_type);
1515 if (!ret) {
1516 printk(KERN_EMERG
1517 "Critical temperature reached (%ld C), \
1518 shutting down.\n", temp/1000);
1519 orderly_poweroff(true);
1520 }
1521 }
1522 break;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001523 case THERMAL_TRIP_ACTIVE:
1524 list_for_each_entry(instance, &tz->cooling_devices,
1525 node) {
1526 if (instance->trip != count)
1527 continue;
1528
1529 cdev = instance->cdev;
1530
Vladimir Zajac29321352009-05-06 19:34:21 +02001531 if (temp >= trip_temp)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001532 cdev->ops->set_cur_state(cdev, 1);
1533 else
1534 cdev->ops->set_cur_state(cdev, 0);
1535 }
1536 break;
1537 case THERMAL_TRIP_PASSIVE:
Vladimir Zajac29321352009-05-06 19:34:21 +02001538 if (temp >= trip_temp || tz->passive)
Matthew Garrettb1569e92008-12-03 17:55:32 +00001539 thermal_zone_device_passive(tz, temp,
1540 trip_temp, count);
1541 break;
1542 }
1543 }
Matthew Garrett03a971a2008-12-03 18:00:38 +00001544
1545 if (tz->forced_passive)
1546 thermal_zone_device_passive(tz, temp, tz->forced_passive,
1547 THERMAL_TRIPS_NONE);
1548
Matthew Garrettb1569e92008-12-03 17:55:32 +00001549 tz->last_temperature = temp;
Michael Brunner0d288162009-08-26 14:29:25 -07001550
Joe Perchescaca8b82012-03-21 12:55:02 -07001551leave:
Matthew Garrettb1569e92008-12-03 17:55:32 +00001552 if (tz->passive)
1553 thermal_zone_device_set_polling(tz, tz->passive_delay);
1554 else if (tz->polling_delay)
1555 thermal_zone_device_set_polling(tz, tz->polling_delay);
Frans Pop3767cb52009-10-26 08:39:04 +01001556 else
1557 thermal_zone_device_set_polling(tz, 0);
Matthew Garrettb1569e92008-12-03 17:55:32 +00001558 mutex_unlock(&tz->lock);
1559}
1560EXPORT_SYMBOL(thermal_zone_device_update);
1561
1562/**
Zhang Rui203d3d42008-01-17 15:51:08 +08001563 * thermal_zone_device_register - register a new thermal zone device
1564 * @type: the thermal zone device type
1565 * @trips: the number of trip points the thermal zone support
1566 * @devdata: private device data
1567 * @ops: standard thermal zone device callbacks
Matthew Garrettb1569e92008-12-03 17:55:32 +00001568 * @tc1: thermal coefficient 1 for passive calculations
1569 * @tc2: thermal coefficient 2 for passive calculations
1570 * @passive_delay: number of milliseconds to wait between polls when
1571 * performing passive cooling
1572 * @polling_delay: number of milliseconds to wait between polls when checking
1573 * whether trip points have been crossed (0 for interrupt
1574 * driven systems)
Zhang Rui203d3d42008-01-17 15:51:08 +08001575 *
1576 * thermal_zone_device_unregister() must be called when the device is no
Matthew Garrettb1569e92008-12-03 17:55:32 +00001577 * longer needed. The passive cooling formula uses tc1 and tc2 as described in
1578 * section 11.1.5.1 of the ACPI specification 3.0.
Zhang Rui203d3d42008-01-17 15:51:08 +08001579 */
1580struct thermal_zone_device *thermal_zone_device_register(char *type,
Alan Cox5b275ce2010-11-11 15:27:29 +00001581 int trips, void *devdata,
1582 const struct thermal_zone_device_ops *ops,
1583 int tc1, int tc2, int passive_delay, int polling_delay)
Zhang Rui203d3d42008-01-17 15:51:08 +08001584{
1585 struct thermal_zone_device *tz;
1586 struct thermal_cooling_device *pos;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001587 enum thermal_trip_type trip_type;
Zhang Rui203d3d42008-01-17 15:51:08 +08001588 int result;
1589 int count;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001590 int passive = 0;
Zhang Rui203d3d42008-01-17 15:51:08 +08001591
1592 if (strlen(type) >= THERMAL_NAME_LENGTH)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001593 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001594
1595 if (trips > THERMAL_MAX_TRIPS || trips < 0)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001596 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001597
1598 if (!ops || !ops->get_temp)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001599 return ERR_PTR(-EINVAL);
Zhang Rui203d3d42008-01-17 15:51:08 +08001600
1601 tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
1602 if (!tz)
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001603 return ERR_PTR(-ENOMEM);
Zhang Rui203d3d42008-01-17 15:51:08 +08001604
1605 INIT_LIST_HEAD(&tz->cooling_devices);
1606 idr_init(&tz->idr);
1607 mutex_init(&tz->lock);
1608 result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
1609 if (result) {
1610 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001611 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001612 }
1613
1614 strcpy(tz->type, type);
1615 tz->ops = ops;
1616 tz->device.class = &thermal_class;
1617 tz->devdata = devdata;
1618 tz->trips = trips;
Matthew Garrettb1569e92008-12-03 17:55:32 +00001619 tz->tc1 = tc1;
1620 tz->tc2 = tc2;
1621 tz->passive_delay = passive_delay;
1622 tz->polling_delay = polling_delay;
1623
Kay Sievers354655e2009-01-06 10:44:37 -08001624 dev_set_name(&tz->device, "thermal_zone%d", tz->id);
Zhang Rui203d3d42008-01-17 15:51:08 +08001625 result = device_register(&tz->device);
1626 if (result) {
1627 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1628 kfree(tz);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001629 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001630 }
1631
1632 /* sys I/F */
1633 if (type) {
1634 result = device_create_file(&tz->device, &dev_attr_type);
1635 if (result)
1636 goto unregister;
1637 }
1638
1639 result = device_create_file(&tz->device, &dev_attr_temp);
1640 if (result)
1641 goto unregister;
1642
1643 if (ops->get_mode) {
1644 result = device_create_file(&tz->device, &dev_attr_mode);
1645 if (result)
1646 goto unregister;
1647 }
1648
1649 for (count = 0; count < trips; count++) {
Joe Perchesec797682012-03-21 12:55:02 -07001650 result = device_create_file(&tz->device,
1651 &trip_point_attrs[count * 2]);
1652 if (result)
1653 break;
1654 result = device_create_file(&tz->device,
1655 &trip_point_attrs[count * 2 + 1]);
Zhang Rui203d3d42008-01-17 15:51:08 +08001656 if (result)
1657 goto unregister;
Matthew Garrett03a971a2008-12-03 18:00:38 +00001658 tz->ops->get_trip_type(tz, count, &trip_type);
1659 if (trip_type == THERMAL_TRIP_PASSIVE)
1660 passive = 1;
Zhang Rui203d3d42008-01-17 15:51:08 +08001661 }
1662
Matthew Garrett03a971a2008-12-03 18:00:38 +00001663 if (!passive)
1664 result = device_create_file(&tz->device,
1665 &dev_attr_passive);
1666
1667 if (result)
1668 goto unregister;
1669
Zhang Ruie68b16a2008-04-21 16:07:52 +08001670 result = thermal_add_hwmon_sysfs(tz);
1671 if (result)
1672 goto unregister;
1673
Zhang Rui203d3d42008-01-17 15:51:08 +08001674 mutex_lock(&thermal_list_lock);
1675 list_add_tail(&tz->node, &thermal_tz_list);
1676 if (ops->bind)
1677 list_for_each_entry(pos, &thermal_cdev_list, node) {
Len Brown543a9562008-02-07 16:55:08 -05001678 result = ops->bind(tz, pos);
1679 if (result)
1680 break;
Zhang Rui203d3d42008-01-17 15:51:08 +08001681 }
Praveen Chidambarambb646212013-07-02 13:04:58 -06001682 sensor_init(tz);
Zhang Rui203d3d42008-01-17 15:51:08 +08001683 mutex_unlock(&thermal_list_lock);
1684
Matthew Garrettb1569e92008-12-03 17:55:32 +00001685 INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
1686
1687 thermal_zone_device_update(tz);
1688
Zhang Rui203d3d42008-01-17 15:51:08 +08001689 if (!result)
1690 return tz;
1691
Joe Perchescaca8b82012-03-21 12:55:02 -07001692unregister:
Zhang Rui203d3d42008-01-17 15:51:08 +08001693 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1694 device_unregister(&tz->device);
Thomas Sujith3e6fda52008-02-15 00:59:50 -05001695 return ERR_PTR(result);
Zhang Rui203d3d42008-01-17 15:51:08 +08001696}
1697EXPORT_SYMBOL(thermal_zone_device_register);
1698
1699/**
1700 * thermal_device_unregister - removes the registered thermal zone device
Zhang Rui203d3d42008-01-17 15:51:08 +08001701 * @tz: the thermal zone device to remove
1702 */
1703void thermal_zone_device_unregister(struct thermal_zone_device *tz)
1704{
1705 struct thermal_cooling_device *cdev;
1706 struct thermal_zone_device *pos = NULL;
1707 int count;
1708
1709 if (!tz)
1710 return;
1711
1712 mutex_lock(&thermal_list_lock);
1713 list_for_each_entry(pos, &thermal_tz_list, node)
1714 if (pos == tz)
1715 break;
1716 if (pos != tz) {
1717 /* thermal zone device not found */
1718 mutex_unlock(&thermal_list_lock);
1719 return;
1720 }
1721 list_del(&tz->node);
1722 if (tz->ops->unbind)
1723 list_for_each_entry(cdev, &thermal_cdev_list, node)
1724 tz->ops->unbind(tz, cdev);
1725 mutex_unlock(&thermal_list_lock);
1726
Matthew Garrettb1569e92008-12-03 17:55:32 +00001727 thermal_zone_device_set_polling(tz, 0);
1728
Zhang Rui203d3d42008-01-17 15:51:08 +08001729 if (tz->type[0])
1730 device_remove_file(&tz->device, &dev_attr_type);
1731 device_remove_file(&tz->device, &dev_attr_temp);
1732 if (tz->ops->get_mode)
1733 device_remove_file(&tz->device, &dev_attr_mode);
1734
Joe Perchesec797682012-03-21 12:55:02 -07001735 for (count = 0; count < tz->trips; count++) {
1736 device_remove_file(&tz->device,
1737 &trip_point_attrs[count * 2]);
1738 device_remove_file(&tz->device,
1739 &trip_point_attrs[count * 2 + 1]);
1740 }
Zhang Ruie68b16a2008-04-21 16:07:52 +08001741 thermal_remove_hwmon_sysfs(tz);
Praveen Chidambarambb646212013-07-02 13:04:58 -06001742 flush_work(&tz->sensor.work);
1743 mutex_lock(&thermal_list_lock);
1744 list_del(&tz->sensor.sensor_list);
1745 mutex_unlock(&thermal_list_lock);
Zhang Rui203d3d42008-01-17 15:51:08 +08001746 release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
1747 idr_destroy(&tz->idr);
1748 mutex_destroy(&tz->lock);
1749 device_unregister(&tz->device);
1750 return;
1751}
Zhang Rui203d3d42008-01-17 15:51:08 +08001752EXPORT_SYMBOL(thermal_zone_device_unregister);
1753
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001754#ifdef CONFIG_NET
1755static struct genl_family thermal_event_genl_family = {
1756 .id = GENL_ID_GENERATE,
1757 .name = THERMAL_GENL_FAMILY_NAME,
1758 .version = THERMAL_GENL_VERSION,
1759 .maxattr = THERMAL_GENL_ATTR_MAX,
1760};
1761
1762static struct genl_multicast_group thermal_event_mcgrp = {
1763 .name = THERMAL_GENL_MCAST_GROUP_NAME,
1764};
1765
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001766int thermal_generate_netlink_event(u32 orig, enum events event)
R.Durgadoss4cb18722010-10-27 03:33:29 +05301767{
1768 struct sk_buff *skb;
1769 struct nlattr *attr;
1770 struct thermal_genl_event *thermal_event;
1771 void *msg_header;
1772 int size;
1773 int result;
Fabio Estevamb11de072012-03-21 12:55:00 -07001774 static unsigned int thermal_event_seqnum;
R.Durgadoss4cb18722010-10-27 03:33:29 +05301775
1776 /* allocate memory */
Joe Perches886ee542012-03-21 12:55:01 -07001777 size = nla_total_size(sizeof(struct thermal_genl_event)) +
1778 nla_total_size(0);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301779
1780 skb = genlmsg_new(size, GFP_ATOMIC);
1781 if (!skb)
1782 return -ENOMEM;
1783
1784 /* add the genetlink message header */
1785 msg_header = genlmsg_put(skb, 0, thermal_event_seqnum++,
1786 &thermal_event_genl_family, 0,
1787 THERMAL_GENL_CMD_EVENT);
1788 if (!msg_header) {
1789 nlmsg_free(skb);
1790 return -ENOMEM;
1791 }
1792
1793 /* fill the data */
Joe Perches886ee542012-03-21 12:55:01 -07001794 attr = nla_reserve(skb, THERMAL_GENL_ATTR_EVENT,
1795 sizeof(struct thermal_genl_event));
R.Durgadoss4cb18722010-10-27 03:33:29 +05301796
1797 if (!attr) {
1798 nlmsg_free(skb);
1799 return -EINVAL;
1800 }
1801
1802 thermal_event = nla_data(attr);
1803 if (!thermal_event) {
1804 nlmsg_free(skb);
1805 return -EINVAL;
1806 }
1807
1808 memset(thermal_event, 0, sizeof(struct thermal_genl_event));
1809
1810 thermal_event->orig = orig;
1811 thermal_event->event = event;
1812
1813 /* send multicast genetlink message */
1814 result = genlmsg_end(skb, msg_header);
1815 if (result < 0) {
1816 nlmsg_free(skb);
1817 return result;
1818 }
1819
1820 result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC);
1821 if (result)
Joe Perchesc5a01dd2012-03-21 12:55:02 -07001822 pr_info("failed to send netlink event:%d\n", result);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301823
1824 return result;
1825}
Jean Delvare2d58d7e2011-11-04 10:31:04 +01001826EXPORT_SYMBOL(thermal_generate_netlink_event);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301827
1828static int genetlink_init(void)
1829{
1830 int result;
1831
1832 result = genl_register_family(&thermal_event_genl_family);
1833 if (result)
1834 return result;
1835
1836 result = genl_register_mc_group(&thermal_event_genl_family,
1837 &thermal_event_mcgrp);
1838 if (result)
1839 genl_unregister_family(&thermal_event_genl_family);
1840 return result;
1841}
1842
Rafael J. Wysockiaf062162011-03-01 01:12:19 +01001843static void genetlink_exit(void)
1844{
1845 genl_unregister_family(&thermal_event_genl_family);
1846}
1847#else /* !CONFIG_NET */
1848static inline int genetlink_init(void) { return 0; }
1849static inline void genetlink_exit(void) {}
1850#endif /* !CONFIG_NET */
1851
Zhang Rui203d3d42008-01-17 15:51:08 +08001852static int __init thermal_init(void)
1853{
1854 int result = 0;
1855
1856 result = class_register(&thermal_class);
1857 if (result) {
1858 idr_destroy(&thermal_tz_idr);
1859 idr_destroy(&thermal_cdev_idr);
1860 mutex_destroy(&thermal_idr_lock);
1861 mutex_destroy(&thermal_list_lock);
1862 }
R.Durgadoss4cb18722010-10-27 03:33:29 +05301863 result = genetlink_init();
Zhang Rui203d3d42008-01-17 15:51:08 +08001864 return result;
1865}
1866
1867static void __exit thermal_exit(void)
1868{
1869 class_unregister(&thermal_class);
1870 idr_destroy(&thermal_tz_idr);
1871 idr_destroy(&thermal_cdev_idr);
1872 mutex_destroy(&thermal_idr_lock);
1873 mutex_destroy(&thermal_list_lock);
R.Durgadoss4cb18722010-10-27 03:33:29 +05301874 genetlink_exit();
Zhang Rui203d3d42008-01-17 15:51:08 +08001875}
1876
R.Durgadoss4cb18722010-10-27 03:33:29 +05301877fs_initcall(thermal_init);
Zhang Rui203d3d42008-01-17 15:51:08 +08001878module_exit(thermal_exit);