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