drivers: misc: Thermal estimator driver
[linux-2.6.git] / drivers / misc / therm_est.c
1 /*
2  * drivers/misc/therm_est.c
3  *
4  * Copyright (C) 2010-2012 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/kernel.h>
18 #include <linux/cpufreq.h>
19 #include <linux/delay.h>
20 #include <linux/mutex.h>
21 #include <linux/init.h>
22 #include <linux/err.h>
23 #include <linux/clk.h>
24 #include <linux/debugfs.h>
25 #include <linux/seq_file.h>
26 #include <linux/uaccess.h>
27 #include <linux/slab.h>
28 #include <linux/syscalls.h>
29 #include <linux/therm_est.h>
30
31 int therm_est_get_temp(struct therm_estimator *est, long *temp)
32 {
33         *temp = est->cur_temp;
34         return 0;
35 }
36
37 int therm_est_set_limits(struct therm_estimator *est,
38                                 long lo_limit,
39                                 long hi_limit)
40 {
41         est->therm_est_lo_limit = lo_limit;
42         est->therm_est_hi_limit = hi_limit;
43         return 0;
44 }
45
46 int therm_est_set_alert(struct therm_estimator *est,
47                         void (*cb)(void *),
48                         void *cb_data)
49 {
50         if ((!cb) || est->callback)
51                 BUG();
52
53         est->callback = cb;
54         est->callback_data = cb_data;
55
56         return 0;
57 }
58
59 static void therm_est_work_func(struct work_struct *work)
60 {
61         int i, j, index, sum = 0;
62         long temp;
63         struct delayed_work *dwork = container_of (work,
64                                         struct delayed_work, work);
65         struct therm_estimator *est = container_of(
66                                         dwork,
67                                         struct therm_estimator,
68                                         therm_est_work);
69
70         for (i = 0; i < est->ndevs; i++) {
71                 if (est->devs[i]->get_temp(est->devs[i]->dev_data, &temp))
72                         continue;
73                 est->devs[i]->hist[(est->ntemp % HIST_LEN)] = temp;
74         }
75
76         for (i = 0; i < est->ndevs; i++) {
77                 for (j = 0; j < HIST_LEN; j++) {
78                         index = (est->ntemp - j + HIST_LEN) % HIST_LEN;
79                         sum += est->devs[i]->hist[index] *
80                                 est->devs[i]->coeffs[j];
81                 }
82         }
83
84         est->cur_temp = sum / 100 + est->toffset;
85
86         est->ntemp++;
87
88         if (est->callback && ((est->cur_temp >= est->therm_est_hi_limit) ||
89                          (est->cur_temp <= est->therm_est_hi_limit)))
90                 est->callback(est->callback_data);
91
92         queue_delayed_work(est->workqueue, &est->therm_est_work,
93                                 msecs_to_jiffies(est->polling_period));
94 }
95
96 struct therm_estimator *therm_est_register(
97                         struct therm_est_subdevice **devs,
98                         int ndevs,
99                         long toffset,
100                         long polling_period)
101 {
102         int i, j;
103         long temp;
104         struct therm_estimator *est;
105         struct therm_est_subdevice *dev;
106
107         est = kzalloc(sizeof(struct therm_estimator), GFP_KERNEL);
108         if (IS_ERR_OR_NULL(est))
109                 return ERR_PTR(-ENOMEM);
110
111         est->devs = devs;
112         est->ndevs = ndevs;
113         est->toffset = toffset;
114         est->polling_period = polling_period;
115
116         /* initialize history */
117         for (i = 0; i < ndevs; i++) {
118                 dev = est->devs[i];
119
120                 if (dev->get_temp(dev->dev_data, &temp)) {
121                         kfree(est);
122                         return ERR_PTR(-EINVAL);
123                 }
124
125                 for (j = 0; j < HIST_LEN; j++) {
126                         dev->hist[j] = temp;
127                 }
128         }
129
130         est->workqueue = alloc_workqueue("therm_est",
131                                     WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
132         INIT_DELAYED_WORK(&est->therm_est_work, therm_est_work_func);
133
134         queue_delayed_work(est->workqueue,
135                                 &est->therm_est_work,
136                                 msecs_to_jiffies(est->polling_period));
137
138         return est;
139 }