drivers: misc: therm_est: enable sysFs nodes for tc1&tc2
[linux-2.6.git] / drivers / misc / therm_est.c
1 /*
2  * drivers/misc/therm_est.c
3  *
4  * Copyright (C) 2010-2013 NVIDIA Corporation.
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         long cur_temp;
38         long polling_period;
39         struct workqueue_struct *workqueue;
40         struct delayed_work therm_est_work;
41         long toffset;
42         int ntemp;
43         int ndevs;
44         struct therm_est_subdevice *devs;
45         struct thermal_zone_device *thz;
46         char *cdev_type;
47         long trip_temp;
48         int tc1;
49         int tc2;
50 #ifdef CONFIG_PM
51         struct notifier_block pm_nb;
52 #endif
53 };
54
55 static void therm_est_work_func(struct work_struct *work)
56 {
57         int i, j, index, sum = 0;
58         long temp;
59         struct delayed_work *dwork = container_of (work,
60                                         struct delayed_work, work);
61         struct therm_estimator *est = container_of(
62                                         dwork,
63                                         struct therm_estimator,
64                                         therm_est_work);
65
66         for (i = 0; i < est->ndevs; i++) {
67                 if (est->devs[i].get_temp(est->devs[i].dev_data, &temp))
68                         continue;
69                 est->devs[i].hist[(est->ntemp % HIST_LEN)] = temp;
70         }
71
72         for (i = 0; i < est->ndevs; i++) {
73                 for (j = 0; j < HIST_LEN; j++) {
74                         index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
75                         sum += est->devs[i].hist[index] *
76                                 est->devs[i].coeffs[j];
77                 }
78         }
79
80         est->cur_temp = sum / 100 + est->toffset;
81
82         est->ntemp++;
83
84         if (est->cur_temp >= est->trip_temp)
85                 if (est->thz && !est->thz->passive)
86                         thermal_zone_device_update(est->thz);
87
88         queue_delayed_work(est->workqueue, &est->therm_est_work,
89                                 msecs_to_jiffies(est->polling_period));
90 }
91
92 static int therm_est_bind(struct thermal_zone_device *thz,
93                                 struct thermal_cooling_device *cdev)
94 {
95         struct therm_estimator *est = thz->devdata;
96
97         if (!strcmp(cdev->type, est->cdev_type))
98                 thermal_zone_bind_cooling_device(thz, 0, cdev,
99                             THERMAL_NO_LIMIT, THERMAL_NO_LIMIT);
100
101         return 0;
102 }
103
104 static int therm_est_unbind(struct thermal_zone_device *thz,
105                                 struct thermal_cooling_device *cdev)
106 {
107         struct therm_estimator *est = thz->devdata;
108
109         if (!strcmp(cdev->type, est->cdev_type))
110                 thermal_zone_unbind_cooling_device(thz, 0, cdev);
111
112         return 0;
113 }
114
115 static int therm_est_get_trip_type(struct thermal_zone_device *thz,
116                                         int trip,
117                                         enum thermal_trip_type *type)
118 {
119         *type = THERMAL_TRIP_PASSIVE;
120         return 0;
121 }
122
123 static int therm_est_get_trip_temp(struct thermal_zone_device *thz,
124                                         int trip,
125                                         unsigned long *temp)
126 {
127         struct therm_estimator *est = thz->devdata;
128
129         *temp = est->trip_temp;
130
131         return 0;
132 }
133
134 static int therm_est_set_trip_temp(struct thermal_zone_device *thz,
135                                         int trip,
136                                         unsigned long temp)
137 {
138         struct therm_estimator *est = thz->devdata;
139
140         est->trip_temp = temp;
141
142         return 0;
143 }
144
145 static int therm_est_get_temp(struct thermal_zone_device *thz,
146                                 unsigned long *temp)
147 {
148         struct therm_estimator *est = thz->devdata;
149         *temp = est->cur_temp;
150         return 0;
151 }
152
153 static int therm_est_get_trend(struct thermal_zone_device *thz,
154                                 int trip,
155                                 enum thermal_trend *trend)
156 {
157         struct therm_estimator *est = thz->devdata;
158         int new_trend;
159         int cur_temp;
160
161         cur_temp = thz->temperature;
162         new_trend = (est->tc1 * (cur_temp - thz->last_temperature)) +
163                     (est->tc2 * (cur_temp - est->trip_temp));
164
165         if (new_trend > 0)
166                 *trend = THERMAL_TREND_RAISING;
167         else if (new_trend < 0)
168                 *trend = THERMAL_TREND_DROPPING;
169         else
170                 *trend = THERMAL_TREND_STABLE;
171
172         return 0;
173 }
174
175 static struct thermal_zone_device_ops therm_est_ops = {
176         .bind = therm_est_bind,
177         .unbind = therm_est_unbind,
178         .get_trip_type = therm_est_get_trip_type,
179         .get_trip_temp = therm_est_get_trip_temp,
180         .set_trip_temp = therm_est_set_trip_temp,
181         .get_temp = therm_est_get_temp,
182         .get_trend = therm_est_get_trend,
183 };
184
185 static ssize_t show_coeff(struct device *dev,
186                                 struct device_attribute *da,
187                                 char *buf)
188 {
189         struct therm_estimator *est = dev_get_drvdata(dev);
190         ssize_t len, total_len = 0;
191         int i, j;
192         for (i = 0; i < est->ndevs; i++) {
193                 len = snprintf(buf + total_len, PAGE_SIZE, "[%d]", i);
194                 total_len += len;
195                 for (j = 0; j < HIST_LEN; j++) {
196                         len = snprintf(buf + total_len, PAGE_SIZE, " %ld",
197                                         est->devs[i].coeffs[j]);
198                         total_len += len;
199                 }
200                 len = snprintf(buf + total_len, PAGE_SIZE, "\n");
201                 total_len += len;
202         }
203         return strlen(buf);
204 }
205
206 static ssize_t set_coeff(struct device *dev,
207                                 struct device_attribute *da,
208                                 const char *buf, size_t count)
209 {
210         struct therm_estimator *est = dev_get_drvdata(dev);
211         int devid, scount;
212         long coeff[20];
213
214         if (HIST_LEN > 20)
215                 return -EINVAL;
216
217         scount = sscanf(buf, "[%d] %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld " \
218                         "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
219                         &devid,
220                         &coeff[0],
221                         &coeff[1],
222                         &coeff[2],
223                         &coeff[3],
224                         &coeff[4],
225                         &coeff[5],
226                         &coeff[6],
227                         &coeff[7],
228                         &coeff[8],
229                         &coeff[9],
230                         &coeff[10],
231                         &coeff[11],
232                         &coeff[12],
233                         &coeff[13],
234                         &coeff[14],
235                         &coeff[15],
236                         &coeff[16],
237                         &coeff[17],
238                         &coeff[18],
239                         &coeff[19]);
240
241         if (scount != HIST_LEN + 1)
242                 return -1;
243
244         if (devid < 0 || devid >= est->ndevs)
245                 return -EINVAL;
246
247         /* This has obvious locking issues but don't worry about it */
248         memcpy(est->devs[devid].coeffs, coeff, sizeof(long) * HIST_LEN);
249
250         return count;
251 }
252
253 static ssize_t show_offset(struct device *dev,
254                                 struct device_attribute *da,
255                                 char *buf)
256 {
257         struct therm_estimator *est = dev_get_drvdata(dev);
258         snprintf(buf, PAGE_SIZE, "%ld\n", est->toffset);
259         return strlen(buf);
260 }
261
262 static ssize_t set_offset(struct device *dev,
263                                 struct device_attribute *da,
264                                 const char *buf, size_t count)
265 {
266         struct therm_estimator *est = dev_get_drvdata(dev);
267         int offset;
268
269         if (kstrtoint(buf, 0, &offset))
270                 return -EINVAL;
271
272         est->toffset = offset;
273
274         return count;
275 }
276
277 static ssize_t show_temps(struct device *dev,
278                                 struct device_attribute *da,
279                                 char *buf)
280 {
281         struct therm_estimator *est = dev_get_drvdata(dev);
282         ssize_t total_len = 0;
283         int i, j;
284         int index;
285
286         /* This has obvious locking issues but don't worry about it */
287         for (i = 0; i < est->ndevs; i++) {
288                 total_len += snprintf(buf + total_len, PAGE_SIZE, "[%d]", i);
289                 for (j = 0; j < HIST_LEN; j++) {
290                         index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
291                         total_len += snprintf(buf + total_len,
292                                                 PAGE_SIZE,
293                                                 " %ld",
294                                                 est->devs[i].hist[index]);
295                 }
296                 total_len += snprintf(buf + total_len, PAGE_SIZE, "\n");
297         }
298         return strlen(buf);
299 }
300
301 static ssize_t show_tc1(struct device *dev,
302                         struct device_attribute *da,
303                         char *buf)
304 {
305         struct therm_estimator *est = dev_get_drvdata(dev);
306         snprintf(buf, PAGE_SIZE, "%d\n", est->tc1);
307         return strlen(buf);
308 }
309
310 static ssize_t set_tc1(struct device *dev,
311                         struct device_attribute *da,
312                         const char *buf, size_t count)
313 {
314         struct therm_estimator *est = dev_get_drvdata(dev);
315         int tc1;
316
317         if (kstrtoint(buf, 0, &tc1))
318                 return -EINVAL;
319
320         est->tc1 = tc1;
321
322         return count;
323 }
324
325 static ssize_t show_tc2(struct device *dev,
326                         struct device_attribute *da,
327                         char *buf)
328 {
329         struct therm_estimator *est = dev_get_drvdata(dev);
330         snprintf(buf, PAGE_SIZE, "%d\n", est->tc2);
331         return strlen(buf);
332 }
333
334 static ssize_t set_tc2(struct device *dev,
335                         struct device_attribute *da,
336                         const char *buf, size_t count)
337 {
338         struct therm_estimator *est = dev_get_drvdata(dev);
339         int tc2;
340
341         if (kstrtoint(buf, 0, &tc2))
342                 return -EINVAL;
343
344         est->tc2 = tc2;
345
346         return count;
347 }
348
349 static struct sensor_device_attribute therm_est_nodes[] = {
350         SENSOR_ATTR(coeff, S_IRUGO | S_IWUSR, show_coeff, set_coeff, 0),
351         SENSOR_ATTR(offset, S_IRUGO | S_IWUSR, show_offset, set_offset, 0),
352         SENSOR_ATTR(tc1, S_IRUGO | S_IWUSR, show_tc1, set_tc1, 0),
353         SENSOR_ATTR(tc2, S_IRUGO | S_IWUSR, show_tc2, set_tc2, 0),
354         SENSOR_ATTR(temps, S_IRUGO, show_temps, 0, 0),
355 };
356
357 static int therm_est_init_history(struct therm_estimator *est)
358 {
359         int i, j;
360         struct therm_est_subdevice *dev;
361         long temp;
362
363         for (i = 0; i < est->ndevs; i++) {
364                 dev = &est->devs[i];
365
366                 if (dev->get_temp(dev->dev_data, &temp))
367                         return -EINVAL;
368
369                 for (j = 0; j < HIST_LEN; j++)
370                         dev->hist[j] = temp;
371         }
372
373         return 0;
374 }
375
376 #ifdef CONFIG_PM
377 static int therm_est_pm_notify(struct notifier_block *nb,
378                                 unsigned long event, void *data)
379 {
380         struct therm_estimator *est = container_of(
381                                         nb,
382                                         struct therm_estimator,
383                                         pm_nb);
384
385         switch (event) {
386         case PM_SUSPEND_PREPARE:
387                 cancel_delayed_work_sync(&est->therm_est_work);
388                 break;
389         case PM_POST_SUSPEND:
390                 therm_est_init_history(est);
391                 queue_delayed_work(est->workqueue,
392                                 &est->therm_est_work,
393                                 msecs_to_jiffies(est->polling_period));
394                 break;
395         }
396
397         return NOTIFY_OK;
398 }
399 #endif
400
401 static int __devinit therm_est_probe(struct platform_device *pdev)
402 {
403         int i;
404         struct therm_estimator *est;
405         struct therm_est_data *data;
406
407         est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL);
408         if (IS_ERR_OR_NULL(est))
409                 return -ENOMEM;
410
411         platform_set_drvdata(pdev, est);
412
413         data = pdev->dev.platform_data;
414
415         est->devs = data->devs;
416         est->ndevs = data->ndevs;
417         est->toffset = data->toffset;
418         est->polling_period = data->polling_period;
419         est->tc1 = data->tc1;
420         est->tc2 = data->tc2;
421
422         /* initialize history */
423         therm_est_init_history(est);
424
425         est->workqueue = alloc_workqueue(dev_name(&pdev->dev),
426                                     WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
427         if (!est->workqueue)
428                 goto err;
429
430         INIT_DELAYED_WORK(&est->therm_est_work, therm_est_work_func);
431
432         queue_delayed_work(est->workqueue,
433                                 &est->therm_est_work,
434                                 msecs_to_jiffies(est->polling_period));
435
436         est->trip_temp = data->trip_temp;
437         est->cdev_type = data->cdev_type;
438
439         est->thz = thermal_zone_device_register(dev_name(&pdev->dev),
440                                         1,
441                                         0x1,
442                                         est,
443                                         &therm_est_ops,
444                                         NULL,
445                                         data->passive_delay,
446                                         0);
447         if (IS_ERR_OR_NULL(est->thz))
448                 goto err;
449
450         for (i = 0; i < ARRAY_SIZE(therm_est_nodes); i++)
451                 device_create_file(&pdev->dev, &therm_est_nodes[i].dev_attr);
452
453 #ifdef CONFIG_PM
454         est->pm_nb.notifier_call = therm_est_pm_notify,
455         register_pm_notifier(&est->pm_nb);
456 #endif
457
458         return 0;
459 err:
460         kfree(est);
461         return -EINVAL;
462 }
463
464 static int __devexit therm_est_remove(struct platform_device *pdev)
465 {
466         return 0;
467 }
468
469 static struct platform_driver therm_est_driver = {
470         .driver = {
471                 .owner = THIS_MODULE,
472                 .name  = "therm_est",
473         },
474         .probe  = therm_est_probe,
475         .remove = __devexit_p(therm_est_remove),
476 };
477
478 static int __init therm_est_driver_init(void)
479 {
480         return platform_driver_register(&therm_est_driver);
481 }
482 module_init(therm_est_driver_init);