drivers: misc: therm_est: Fixed buffer issues
[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         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,
194                                 PAGE_SIZE - total_len, "[%d]", i);
195                 total_len += len;
196                 for (j = 0; j < HIST_LEN; j++) {
197                         len = snprintf(buf + total_len,
198                                         PAGE_SIZE - total_len, " %ld",
199                                         est->devs[i].coeffs[j]);
200                         total_len += len;
201                 }
202                 len = snprintf(buf + total_len, PAGE_SIZE - total_len, "\n");
203                 total_len += len;
204         }
205         return strlen(buf);
206 }
207
208 static ssize_t set_coeff(struct device *dev,
209                                 struct device_attribute *da,
210                                 const char *buf, size_t count)
211 {
212         struct therm_estimator *est = dev_get_drvdata(dev);
213         int devid, scount;
214         long coeff[20];
215
216         if (HIST_LEN > 20)
217                 return -EINVAL;
218
219         scount = sscanf(buf, "[%d] %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld " \
220                         "%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
221                         &devid,
222                         &coeff[0],
223                         &coeff[1],
224                         &coeff[2],
225                         &coeff[3],
226                         &coeff[4],
227                         &coeff[5],
228                         &coeff[6],
229                         &coeff[7],
230                         &coeff[8],
231                         &coeff[9],
232                         &coeff[10],
233                         &coeff[11],
234                         &coeff[12],
235                         &coeff[13],
236                         &coeff[14],
237                         &coeff[15],
238                         &coeff[16],
239                         &coeff[17],
240                         &coeff[18],
241                         &coeff[19]);
242
243         if (scount != HIST_LEN + 1)
244                 return -1;
245
246         if (devid < 0 || devid >= est->ndevs)
247                 return -EINVAL;
248
249         /* This has obvious locking issues but don't worry about it */
250         memcpy(est->devs[devid].coeffs, coeff, sizeof(coeff[0]) * HIST_LEN);
251
252         return count;
253 }
254
255 static ssize_t show_offset(struct device *dev,
256                                 struct device_attribute *da,
257                                 char *buf)
258 {
259         struct therm_estimator *est = dev_get_drvdata(dev);
260         snprintf(buf, PAGE_SIZE, "%ld\n", est->toffset);
261         return strlen(buf);
262 }
263
264 static ssize_t set_offset(struct device *dev,
265                                 struct device_attribute *da,
266                                 const char *buf, size_t count)
267 {
268         struct therm_estimator *est = dev_get_drvdata(dev);
269         int offset;
270
271         if (kstrtoint(buf, 0, &offset))
272                 return -EINVAL;
273
274         est->toffset = offset;
275
276         return count;
277 }
278
279 static ssize_t show_temps(struct device *dev,
280                                 struct device_attribute *da,
281                                 char *buf)
282 {
283         struct therm_estimator *est = dev_get_drvdata(dev);
284         ssize_t total_len = 0;
285         int i, j;
286         int index;
287
288         /* This has obvious locking issues but don't worry about it */
289         for (i = 0; i < est->ndevs; i++) {
290                 total_len += snprintf(buf + total_len,
291                                         PAGE_SIZE - total_len, "[%d]", i);
292                 for (j = 0; j < HIST_LEN; j++) {
293                         index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
294                         total_len += snprintf(buf + total_len,
295                                                 PAGE_SIZE - total_len, " %ld",
296                                                 est->devs[i].hist[index]);
297                 }
298                 total_len += snprintf(buf + total_len,
299                                         PAGE_SIZE - total_len, "\n");
300         }
301         return strlen(buf);
302 }
303
304 static ssize_t show_tc1(struct device *dev,
305                         struct device_attribute *da,
306                         char *buf)
307 {
308         struct therm_estimator *est = dev_get_drvdata(dev);
309         snprintf(buf, PAGE_SIZE, "%d\n", est->tc1);
310         return strlen(buf);
311 }
312
313 static ssize_t set_tc1(struct device *dev,
314                         struct device_attribute *da,
315                         const char *buf, size_t count)
316 {
317         struct therm_estimator *est = dev_get_drvdata(dev);
318         int tc1;
319
320         if (kstrtoint(buf, 0, &tc1))
321                 return -EINVAL;
322
323         est->tc1 = tc1;
324
325         return count;
326 }
327
328 static ssize_t show_tc2(struct device *dev,
329                         struct device_attribute *da,
330                         char *buf)
331 {
332         struct therm_estimator *est = dev_get_drvdata(dev);
333         snprintf(buf, PAGE_SIZE, "%d\n", est->tc2);
334         return strlen(buf);
335 }
336
337 static ssize_t set_tc2(struct device *dev,
338                         struct device_attribute *da,
339                         const char *buf, size_t count)
340 {
341         struct therm_estimator *est = dev_get_drvdata(dev);
342         int tc2;
343
344         if (kstrtoint(buf, 0, &tc2))
345                 return -EINVAL;
346
347         est->tc2 = tc2;
348
349         return count;
350 }
351
352 static struct sensor_device_attribute therm_est_nodes[] = {
353         SENSOR_ATTR(coeff, S_IRUGO | S_IWUSR, show_coeff, set_coeff, 0),
354         SENSOR_ATTR(offset, S_IRUGO | S_IWUSR, show_offset, set_offset, 0),
355         SENSOR_ATTR(tc1, S_IRUGO | S_IWUSR, show_tc1, set_tc1, 0),
356         SENSOR_ATTR(tc2, S_IRUGO | S_IWUSR, show_tc2, set_tc2, 0),
357         SENSOR_ATTR(temps, S_IRUGO, show_temps, 0, 0),
358 };
359
360 static int therm_est_init_history(struct therm_estimator *est)
361 {
362         int i, j;
363         struct therm_est_subdevice *dev;
364         long temp;
365
366         for (i = 0; i < est->ndevs; i++) {
367                 dev = &est->devs[i];
368
369                 if (dev->get_temp(dev->dev_data, &temp))
370                         return -EINVAL;
371
372                 for (j = 0; j < HIST_LEN; j++)
373                         dev->hist[j] = temp;
374         }
375
376         return 0;
377 }
378
379 #ifdef CONFIG_PM
380 static int therm_est_pm_notify(struct notifier_block *nb,
381                                 unsigned long event, void *data)
382 {
383         struct therm_estimator *est = container_of(
384                                         nb,
385                                         struct therm_estimator,
386                                         pm_nb);
387
388         switch (event) {
389         case PM_SUSPEND_PREPARE:
390                 cancel_delayed_work_sync(&est->therm_est_work);
391                 break;
392         case PM_POST_SUSPEND:
393                 therm_est_init_history(est);
394                 queue_delayed_work(est->workqueue,
395                                 &est->therm_est_work,
396                                 msecs_to_jiffies(est->polling_period));
397                 break;
398         }
399
400         return NOTIFY_OK;
401 }
402 #endif
403
404 static int __devinit therm_est_probe(struct platform_device *pdev)
405 {
406         int i;
407         struct therm_estimator *est;
408         struct therm_est_data *data;
409
410         est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL);
411         if (IS_ERR_OR_NULL(est))
412                 return -ENOMEM;
413
414         platform_set_drvdata(pdev, est);
415
416         data = pdev->dev.platform_data;
417
418         est->devs = data->devs;
419         est->ndevs = data->ndevs;
420         est->toffset = data->toffset;
421         est->polling_period = data->polling_period;
422         est->tc1 = data->tc1;
423         est->tc2 = data->tc2;
424
425         /* initialize history */
426         therm_est_init_history(est);
427
428         est->workqueue = alloc_workqueue(dev_name(&pdev->dev),
429                                     WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
430         if (!est->workqueue)
431                 goto err;
432
433         INIT_DELAYED_WORK(&est->therm_est_work, therm_est_work_func);
434
435         queue_delayed_work(est->workqueue,
436                                 &est->therm_est_work,
437                                 msecs_to_jiffies(est->polling_period));
438
439         est->trip_temp = data->trip_temp;
440         est->cdev_type = data->cdev_type;
441
442         est->thz = thermal_zone_device_register(dev_name(&pdev->dev),
443                                         1,
444                                         0x1,
445                                         est,
446                                         &therm_est_ops,
447                                         NULL,
448                                         data->passive_delay,
449                                         0);
450         if (IS_ERR_OR_NULL(est->thz))
451                 goto err;
452
453         for (i = 0; i < ARRAY_SIZE(therm_est_nodes); i++)
454                 device_create_file(&pdev->dev, &therm_est_nodes[i].dev_attr);
455
456 #ifdef CONFIG_PM
457         est->pm_nb.notifier_call = therm_est_pm_notify,
458         register_pm_notifier(&est->pm_nb);
459 #endif
460
461         return 0;
462 err:
463         kfree(est);
464         return -EINVAL;
465 }
466
467 static int __devexit therm_est_remove(struct platform_device *pdev)
468 {
469         return 0;
470 }
471
472 static struct platform_driver therm_est_driver = {
473         .driver = {
474                 .owner = THIS_MODULE,
475                 .name  = "therm_est",
476         },
477         .probe  = therm_est_probe,
478         .remove = __devexit_p(therm_est_remove),
479 };
480
481 static int __init therm_est_driver_init(void)
482 {
483         return platform_driver_register(&therm_est_driver);
484 }
485 module_init(therm_est_driver_init);