Tegra CEC Support
[linux-2.6.git] / drivers / misc / therm_est.c
1 /*
2  * drivers/misc/therm_est.c
3  *
4  * Copyright (c) 2010-2013, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/platform_device.h>
18 #include <linux/kernel.h>
19 #include <linux/cpufreq.h>
20 #include <linux/delay.h>
21 #include <linux/mutex.h>
22 #include <linux/init.h>
23 #include <linux/err.h>
24 #include <linux/clk.h>
25 #include <linux/debugfs.h>
26 #include <linux/seq_file.h>
27 #include <linux/uaccess.h>
28 #include <linux/slab.h>
29 #include <linux/syscalls.h>
30 #include <linux/therm_est.h>
31 #include <linux/thermal.h>
32 #include <linux/module.h>
33 #include <linux/hwmon-sysfs.h>
34 #include <linux/suspend.h>
35
36 struct therm_estimator {
37         struct thermal_zone_device *thz;
38         int num_trips;
39         struct thermal_trip_info *trips;
40         struct thermal_zone_params *tzp;
41
42         struct workqueue_struct *workqueue;
43         struct delayed_work therm_est_work;
44         long cur_temp;
45         long low_limit;
46         long high_limit;
47         int ntemp;
48         long toffset;
49         long polling_period;
50         int tc1;
51         int tc2;
52         int ndevs;
53         struct therm_est_subdevice *devs;
54
55 #ifdef CONFIG_PM
56         struct notifier_block pm_nb;
57 #endif
58 };
59
60 static int therm_est_subdev_match(struct thermal_zone_device *thz, void *data)
61 {
62         return strcmp((char *)data, thz->type) == 0;
63 }
64
65 static int therm_est_subdev_get_temp(void *data, long *temp)
66 {
67         struct thermal_zone_device *thz;
68
69         thz = thermal_zone_device_find(data, therm_est_subdev_match);
70
71         if (!thz || thz->ops->get_temp(thz, temp))
72                 *temp = 25000;
73
74         return 0;
75 }
76
77 static void therm_est_update_limits(struct therm_estimator *est)
78 {
79         const int MAX_HIGH_TEMP = 128000;
80         long low_temp = 0, high_temp = MAX_HIGH_TEMP;
81         long trip_temp, passive_low_temp = MAX_HIGH_TEMP;
82         enum thermal_trip_type trip_type;
83         struct thermal_trip_info *trip_state;
84         int i;
85
86         for (i = 0; i < est->num_trips; i++) {
87                 trip_state = &est->trips[i];
88                 est->thz->ops->get_trip_temp(est->thz, i, &trip_temp);
89                 est->thz->ops->get_trip_type(est->thz, i, &trip_type);
90
91                 if (!trip_state->tripped) { /* not tripped? update high */
92                         if (trip_temp < high_temp)
93                                 high_temp = trip_temp;
94                 } else { /* tripped? update low */
95                         if (trip_type != THERMAL_TRIP_PASSIVE) {
96                                 /* get highest ACTIVE */
97                                 if (trip_temp > low_temp)
98                                         low_temp = trip_temp;
99                         } else {
100                                 /* get lowest PASSIVE */
101                                 if (trip_temp < passive_low_temp)
102                                         passive_low_temp = trip_temp;
103                         }
104                 }
105         }
106
107         if (passive_low_temp != MAX_HIGH_TEMP)
108                 low_temp = max(low_temp, passive_low_temp);
109
110         est->low_limit = low_temp;
111         est->high_limit = high_temp;
112 }
113
114 static void therm_est_work_func(struct work_struct *work)
115 {
116         int i, j, index, sum = 0;
117         long temp;
118         struct delayed_work *dwork = container_of(work,
119                                         struct delayed_work, work);
120         struct therm_estimator *est = container_of(dwork,
121                                         struct therm_estimator,
122                                         therm_est_work);
123
124         for (i = 0; i < est->ndevs; i++) {
125                 if (therm_est_subdev_get_temp(est->devs[i].dev_data, &temp))
126                         continue;
127                 est->devs[i].hist[(est->ntemp % HIST_LEN)] = temp;
128         }
129
130         for (i = 0; i < est->ndevs; i++) {
131                 for (j = 0; j < HIST_LEN; j++) {
132                         index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
133                         sum += est->devs[i].hist[index] *
134                                 est->devs[i].coeffs[j];
135                 }
136         }
137
138         est->cur_temp = sum / 100 + est->toffset;
139         est->ntemp++;
140
141         if (est->thz && ((est->cur_temp < est->low_limit) ||
142                         (est->cur_temp >= est->high_limit))) {
143                 thermal_zone_device_update(est->thz);
144                 therm_est_update_limits(est);
145         }
146
147         queue_delayed_work(est->workqueue, &est->therm_est_work,
148                                 msecs_to_jiffies(est->polling_period));
149 }
150
151 static int therm_est_bind(struct thermal_zone_device *thz,
152                                 struct thermal_cooling_device *cdev)
153 {
154         struct therm_estimator *est = thz->devdata;
155         struct thermal_trip_info *trip_state;
156         int i;
157
158         for (i = 0; i < est->num_trips; i++) {
159                 trip_state = &est->trips[i];
160                 if (trip_state->cdev_type &&
161                     !strncmp(trip_state->cdev_type, cdev->type,
162                              THERMAL_NAME_LENGTH))
163                         thermal_zone_bind_cooling_device(thz, i, cdev,
164                                                          trip_state->upper,
165                                                          trip_state->lower);
166         }
167
168         return 0;
169 }
170
171 static int therm_est_unbind(struct thermal_zone_device *thz,
172                                 struct thermal_cooling_device *cdev)
173 {
174         struct therm_estimator *est = thz->devdata;
175         struct thermal_trip_info *trip_state;
176         int i;
177
178         for (i = 0; i < est->num_trips; i++) {
179                 trip_state = &est->trips[i];
180                 if (trip_state->cdev_type &&
181                     !strncmp(trip_state->cdev_type, cdev->type,
182                              THERMAL_NAME_LENGTH))
183                         thermal_zone_unbind_cooling_device(thz, i, cdev);
184         }
185
186         return 0;
187 }
188
189 static int therm_est_get_trip_type(struct thermal_zone_device *thz,
190                                    int trip, enum thermal_trip_type *type)
191 {
192         struct therm_estimator *est = thz->devdata;
193
194         *type = est->trips[trip].trip_type;
195         return 0;
196 }
197
198 static int therm_est_get_trip_temp(struct thermal_zone_device *thz,
199                                    int trip, unsigned long *temp)
200 {
201         struct therm_estimator *est = thz->devdata;
202         struct thermal_trip_info *trip_state = &est->trips[trip];
203         unsigned long trip_temp, zone_temp;
204
205         trip_temp = trip_state->trip_temp;
206         zone_temp = thz->temperature;
207
208         if (zone_temp >= trip_temp) {
209                 trip_temp -= trip_state->hysteresis;
210                 trip_state->tripped = true;
211         } else if (trip_state->tripped) {
212                 trip_temp -= trip_state->hysteresis;
213                 if (zone_temp < trip_temp)
214                         trip_state->tripped = false;
215         }
216
217         *temp = trip_temp;
218
219         return 0;
220 }
221
222 static int therm_est_set_trip_temp(struct thermal_zone_device *thz,
223                                    int trip, unsigned long temp)
224 {
225         struct therm_estimator *est = thz->devdata;
226
227         est->trips[trip].trip_temp = temp;
228         return 0;
229 }
230
231 static int therm_est_get_temp(struct thermal_zone_device *thz,
232                                 unsigned long *temp)
233 {
234         struct therm_estimator *est = thz->devdata;
235
236         *temp = est->cur_temp;
237         return 0;
238 }
239
240 static int therm_est_get_trend(struct thermal_zone_device *thz,
241                                int trip, enum thermal_trend *trend)
242 {
243         struct therm_estimator *est = thz->devdata;
244         struct thermal_trip_info *trip_state = &est->trips[trip];
245         long trip_temp;
246         int new_trend;
247         int cur_temp;
248
249         thz->ops->get_trip_temp(thz, trip, &trip_temp);
250
251         cur_temp = thz->temperature;
252         new_trend = (est->tc1 * (cur_temp - thz->last_temperature)) +
253                     (est->tc2 * (cur_temp - trip_temp));
254
255         switch (trip_state->trip_type) {
256         case THERMAL_TRIP_ACTIVE:
257                 /* aggressive active cooling */
258                 *trend = THERMAL_TREND_RAISING;
259                 break;
260         case THERMAL_TRIP_PASSIVE:
261                 if (new_trend > 0)
262                         *trend = THERMAL_TREND_RAISING;
263                 else if (new_trend < 0)
264                         *trend = THERMAL_TREND_DROPPING;
265                 else
266                         *trend = THERMAL_TREND_STABLE;
267                 break;
268         default:
269                 return -EINVAL;
270         }
271         return 0;
272 }
273
274 static struct thermal_zone_device_ops therm_est_ops = {
275         .bind = therm_est_bind,
276         .unbind = therm_est_unbind,
277         .get_trip_type = therm_est_get_trip_type,
278         .get_trip_temp = therm_est_get_trip_temp,
279         .set_trip_temp = therm_est_set_trip_temp,
280         .get_temp = therm_est_get_temp,
281         .get_trend = therm_est_get_trend,
282 };
283
284 static ssize_t show_coeff(struct device *dev,
285                                 struct device_attribute *da,
286                                 char *buf)
287 {
288         struct therm_estimator *est = dev_get_drvdata(dev);
289         ssize_t len, total_len = 0;
290         int i, j;
291         for (i = 0; i < est->ndevs; i++) {
292                 len = snprintf(buf + total_len,
293                                 PAGE_SIZE - total_len, "[%d]", i);
294                 total_len += len;
295                 for (j = 0; j < HIST_LEN; j++) {
296                         len = snprintf(buf + total_len,
297                                         PAGE_SIZE - total_len, " %ld",
298                                         est->devs[i].coeffs[j]);
299                         total_len += len;
300                 }
301                 len = snprintf(buf + total_len, PAGE_SIZE - total_len, "\n");
302                 total_len += len;
303         }
304         return strlen(buf);
305 }
306
307 static ssize_t set_coeff(struct device *dev,
308                                 struct device_attribute *da,
309                                 const char *buf, size_t count)
310 {
311         struct therm_estimator *est = dev_get_drvdata(dev);
312         int devid, scount;
313         long coeff[20];
314
315         if (HIST_LEN > 20)
316                 return -EINVAL;
317
318         scount = sscanf(buf, "[%d] %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld " \
319                         "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
320                         &devid,
321                         &coeff[0],
322                         &coeff[1],
323                         &coeff[2],
324                         &coeff[3],
325                         &coeff[4],
326                         &coeff[5],
327                         &coeff[6],
328                         &coeff[7],
329                         &coeff[8],
330                         &coeff[9],
331                         &coeff[10],
332                         &coeff[11],
333                         &coeff[12],
334                         &coeff[13],
335                         &coeff[14],
336                         &coeff[15],
337                         &coeff[16],
338                         &coeff[17],
339                         &coeff[18],
340                         &coeff[19]);
341
342         if (scount != HIST_LEN + 1)
343                 return -1;
344
345         if (devid < 0 || devid >= est->ndevs)
346                 return -EINVAL;
347
348         /* This has obvious locking issues but don't worry about it */
349         memcpy(est->devs[devid].coeffs, coeff, sizeof(coeff[0]) * HIST_LEN);
350
351         return count;
352 }
353
354 static ssize_t show_offset(struct device *dev,
355                                 struct device_attribute *da,
356                                 char *buf)
357 {
358         struct therm_estimator *est = dev_get_drvdata(dev);
359         snprintf(buf, PAGE_SIZE, "%ld\n", est->toffset);
360         return strlen(buf);
361 }
362
363 static ssize_t set_offset(struct device *dev,
364                                 struct device_attribute *da,
365                                 const char *buf, size_t count)
366 {
367         struct therm_estimator *est = dev_get_drvdata(dev);
368         int offset;
369
370         if (kstrtoint(buf, 0, &offset))
371                 return -EINVAL;
372
373         est->toffset = offset;
374
375         return count;
376 }
377
378 static ssize_t show_temps(struct device *dev,
379                                 struct device_attribute *da,
380                                 char *buf)
381 {
382         struct therm_estimator *est = dev_get_drvdata(dev);
383         ssize_t total_len = 0;
384         int i, j;
385         int index;
386
387         /* This has obvious locking issues but don't worry about it */
388         for (i = 0; i < est->ndevs; i++) {
389                 total_len += snprintf(buf + total_len,
390                                         PAGE_SIZE - total_len, "[%d]", i);
391                 for (j = 0; j < HIST_LEN; j++) {
392                         index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
393                         total_len += snprintf(buf + total_len,
394                                                 PAGE_SIZE - total_len, " %ld",
395                                                 est->devs[i].hist[index]);
396                 }
397                 total_len += snprintf(buf + total_len,
398                                         PAGE_SIZE - total_len, "\n");
399         }
400         return strlen(buf);
401 }
402
403 static ssize_t show_tc1(struct device *dev,
404                         struct device_attribute *da,
405                         char *buf)
406 {
407         struct therm_estimator *est = dev_get_drvdata(dev);
408         snprintf(buf, PAGE_SIZE, "%d\n", est->tc1);
409         return strlen(buf);
410 }
411
412 static ssize_t set_tc1(struct device *dev,
413                         struct device_attribute *da,
414                         const char *buf, size_t count)
415 {
416         struct therm_estimator *est = dev_get_drvdata(dev);
417         int tc1;
418
419         if (kstrtoint(buf, 0, &tc1))
420                 return -EINVAL;
421
422         est->tc1 = tc1;
423
424         return count;
425 }
426
427 static ssize_t show_tc2(struct device *dev,
428                         struct device_attribute *da,
429                         char *buf)
430 {
431         struct therm_estimator *est = dev_get_drvdata(dev);
432         snprintf(buf, PAGE_SIZE, "%d\n", est->tc2);
433         return strlen(buf);
434 }
435
436 static ssize_t set_tc2(struct device *dev,
437                         struct device_attribute *da,
438                         const char *buf, size_t count)
439 {
440         struct therm_estimator *est = dev_get_drvdata(dev);
441         int tc2;
442
443         if (kstrtoint(buf, 0, &tc2))
444                 return -EINVAL;
445
446         est->tc2 = tc2;
447
448         return count;
449 }
450
451 static struct sensor_device_attribute therm_est_nodes[] = {
452         SENSOR_ATTR(coeff, S_IRUGO | S_IWUSR, show_coeff, set_coeff, 0),
453         SENSOR_ATTR(offset, S_IRUGO | S_IWUSR, show_offset, set_offset, 0),
454         SENSOR_ATTR(tc1, S_IRUGO | S_IWUSR, show_tc1, set_tc1, 0),
455         SENSOR_ATTR(tc2, S_IRUGO | S_IWUSR, show_tc2, set_tc2, 0),
456         SENSOR_ATTR(temps, S_IRUGO, show_temps, 0, 0),
457 };
458
459 static int therm_est_init_history(struct therm_estimator *est)
460 {
461         int i, j;
462         struct therm_est_subdevice *dev;
463         long temp;
464
465         for (i = 0; i < est->ndevs; i++) {
466                 dev = &est->devs[i];
467
468                 if (therm_est_subdev_get_temp(dev->dev_data, &temp))
469                         return -EINVAL;
470
471                 for (j = 0; j < HIST_LEN; j++)
472                         dev->hist[j] = temp;
473         }
474
475         return 0;
476 }
477
478 #ifdef CONFIG_PM
479 static int therm_est_pm_notify(struct notifier_block *nb,
480                                 unsigned long event, void *data)
481 {
482         struct therm_estimator *est = container_of(
483                                         nb,
484                                         struct therm_estimator,
485                                         pm_nb);
486
487         switch (event) {
488         case PM_SUSPEND_PREPARE:
489                 cancel_delayed_work_sync(&est->therm_est_work);
490                 break;
491         case PM_POST_SUSPEND:
492                 est->low_limit = 0;
493                 est->high_limit = 0;
494                 therm_est_init_history(est);
495                 queue_delayed_work(est->workqueue,
496                                 &est->therm_est_work,
497                                 msecs_to_jiffies(est->polling_period));
498                 break;
499         }
500
501         return NOTIFY_OK;
502 }
503 #endif
504
505 static int __devinit therm_est_probe(struct platform_device *pdev)
506 {
507         int i;
508         struct therm_estimator *est;
509         struct therm_est_data *data;
510
511         est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL);
512         if (IS_ERR_OR_NULL(est))
513                 return -ENOMEM;
514
515         platform_set_drvdata(pdev, est);
516
517         data = pdev->dev.platform_data;
518
519         est->devs = data->devs;
520         est->ndevs = data->ndevs;
521         est->toffset = data->toffset;
522         est->polling_period = data->polling_period;
523         est->tc1 = data->tc1;
524         est->tc2 = data->tc2;
525
526         /* initialize history */
527         therm_est_init_history(est);
528
529         est->workqueue = alloc_workqueue(dev_name(&pdev->dev),
530                                     WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
531         if (!est->workqueue)
532                 goto err;
533
534         INIT_DELAYED_WORK(&est->therm_est_work, therm_est_work_func);
535
536         queue_delayed_work(est->workqueue,
537                                 &est->therm_est_work,
538                                 msecs_to_jiffies(est->polling_period));
539
540         est->num_trips = data->num_trips;
541         est->trips = data->trips;
542         est->tzp = data->tzp;
543
544         est->thz = thermal_zone_device_register(dev_name(&pdev->dev),
545                                                 est->num_trips,
546                                                 (1 << est->num_trips) - 1,
547                                                 est,
548                                                 &therm_est_ops,
549                                                 est->tzp,
550                                                 data->passive_delay,
551                                                 0);
552         if (IS_ERR_OR_NULL(est->thz))
553                 goto err;
554
555         for (i = 0; i < ARRAY_SIZE(therm_est_nodes); i++)
556                 device_create_file(&pdev->dev, &therm_est_nodes[i].dev_attr);
557
558 #ifdef CONFIG_PM
559         est->pm_nb.notifier_call = therm_est_pm_notify,
560         register_pm_notifier(&est->pm_nb);
561 #endif
562
563         return 0;
564 err:
565         kfree(est);
566         return -EINVAL;
567 }
568
569 static int __devexit therm_est_remove(struct platform_device *pdev)
570 {
571         return 0;
572 }
573
574 static struct platform_driver therm_est_driver = {
575         .driver = {
576                 .owner = THIS_MODULE,
577                 .name  = "therm_est",
578         },
579         .probe  = therm_est_probe,
580         .remove = __devexit_p(therm_est_remove),
581 };
582
583 static int __init therm_est_driver_init(void)
584 {
585         return platform_driver_register(&therm_est_driver);
586 }
587 module_init(therm_est_driver_init);