ARM: tegra: thermal: Thermal Refactor
[linux-3.10.git] / arch / arm / mach-tegra / tegra3_thermal.c
1 /*
2  * arch/arm/mach-tegra/tegra3_thermal.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/thermal.h>
28 #include <linux/module.h>
29 #include <mach/thermal.h>
30 #include <linux/slab.h>
31 #include <linux/suspend.h>
32
33 #include "clock.h"
34 #include "cpu-tegra.h"
35 #include "dvfs.h"
36
37 static struct tegra_thermal_bind *thermal_binds;
38 static struct tegra_skin_data *skin_therm;
39 static LIST_HEAD(tegra_therm_list);
40 static DEFINE_MUTEX(tegra_therm_mutex);
41
42 static struct balanced_throttle *throttle_list;
43 static int throttle_list_size;
44
45 #define MAX_TEMP (120000)
46
47 #ifdef CONFIG_TEGRA_SKIN_THROTTLE
48 static int skin_devs_bitmap;
49 static struct therm_est_subdevice *skin_devs[THERMAL_DEVICE_MAX];
50 static int skin_devs_count;
51 #endif
52 static bool tegra_thermal_suspend;
53
54 static int tegra_thermal_zone_bind(struct thermal_zone_device *thz,
55                                 struct thermal_cooling_device *cdevice)
56 {
57         int i, j, trip=0, trip_size, index;
58         struct tegra_cooling_device *tegra_cdev = cdevice->devdata;
59         struct tegra_thermal_device *device = thz->devdata;
60
61         for (i=0; thermal_binds[i].tdev_id; i++) {
62                 if(device->id == thermal_binds[i].tdev_id) {
63                         trip_size = thermal_binds[i].get_trip_size();
64                         index = tegra_cdev->id & 0xffff;
65
66                         if (thermal_binds[i].cdev_id  == (tegra_cdev->id & 0xffff0000)) {
67                                 for (j=0; j < trip_size; j++) {
68                                         thermal_zone_bind_cooling_device(
69                                                         thz,
70                                                         trip + index,
71                                                         cdevice);
72                                 }
73                         }
74
75                         trip += trip_size;
76                 }
77         }
78
79         return 0;
80 }
81
82 static int tegra_thermal_zone_unbind(struct thermal_zone_device *thz,
83                                 struct thermal_cooling_device *cdevice) {
84         int i, trip = 0;
85         struct tegra_cooling_device *tegra_cdev = cdevice->devdata;
86         struct tegra_thermal_device *device = thz->devdata;
87
88         for (i=0; thermal_binds[i].tdev_id; i++) {
89                 if(device->id == thermal_binds[i].tdev_id) {
90                         if (tegra_cdev->id == thermal_binds[i].cdev_id)
91                                 thermal_zone_unbind_cooling_device(thz, trip, cdevice);
92                         trip += thermal_binds[i].get_trip_size();
93                 }
94         }
95
96         return 0;
97 }
98
99 static int tegra_thermal_zone_get_temp(struct thermal_zone_device *thz,
100                                         unsigned long *temp)
101 {
102         struct tegra_thermal_device *device = thz->devdata;
103
104         if (!tegra_thermal_suspend)
105                 device->get_temp(device->data, temp);
106
107         return 0;
108 }
109
110 static int tegra_thermal_zone_get_trip_type(
111                         struct thermal_zone_device *thz,
112                         int trip,
113                         enum thermal_trip_type *type) {
114         int i, trip_count = 0;
115         struct tegra_thermal_device *device = thz->devdata;
116
117         for (i=0; thermal_binds[i].tdev_id; i++) {
118                 if(device->id == thermal_binds[i].tdev_id) {
119                         trip_count += thermal_binds[i].get_trip_size();
120                         if (trip < trip_count) {
121                                 *type = thermal_binds[i].type;
122                                 return 0;
123                         }
124                 }
125         }
126
127         return -EINVAL;
128 }
129
130 static int tegra_thermal_zone_get_trip_temp(struct thermal_zone_device *thz,
131                                         int trip,
132                                         unsigned long *temp) {
133         int i, j, trip_size, trip_count = 0;
134         struct tegra_thermal_device *device = thz->devdata;
135
136         for (i=0; thermal_binds[i].tdev_id; i++) {
137                 if(device->id == thermal_binds[i].tdev_id) {
138                         trip_size = thermal_binds[i].get_trip_size();
139                         for (j=0; j < trip_size; j++) {
140                                 if (trip == trip_count) {
141                                         *temp = thermal_binds[i].get_trip_temp(
142                                                         &thermal_binds[i], j);
143                                         return 0;
144                                 }
145                                 trip_count++;
146                         }
147                 }
148         }
149
150         return -EINVAL;
151 }
152
153 static struct thermal_zone_device_ops tegra_thermal_zone_ops = {
154         .bind = tegra_thermal_zone_bind,
155         .unbind = tegra_thermal_zone_unbind,
156         .get_temp = tegra_thermal_zone_get_temp,
157         .get_trip_type = tegra_thermal_zone_get_trip_type,
158         .get_trip_temp = tegra_thermal_zone_get_trip_temp,
159 };
160
161 static int tegra_thermal_pm_notify(struct notifier_block *nb,
162                                 unsigned long event, void *data)
163 {
164         switch (event) {
165         case PM_SUSPEND_PREPARE:
166                 tegra_thermal_suspend = true;
167                 break;
168         case PM_POST_SUSPEND:
169                 tegra_thermal_suspend = false;
170                 break;
171         }
172
173         return NOTIFY_OK;
174 };
175
176 static struct notifier_block tegra_thermal_nb = {
177         .notifier_call = tegra_thermal_pm_notify,
178 };
179
180 static void tegra_thermal_alert(void *data)
181 {
182         struct tegra_thermal_device *device = data;
183         long temp, trip_temp, low_temp = 0, high_temp = MAX_TEMP;
184         int count;
185
186         mutex_lock(&tegra_therm_mutex);
187
188         if (!device->thz)
189                 goto done;
190
191         if ((!device->thz->passive) && (!tegra_thermal_suspend))
192                 thermal_zone_device_update(device->thz);
193
194         device->thz->ops->get_temp(device->thz, &temp);
195
196         for (count = 0; count < device->thz->trips; count++) {
197                 device->thz->ops->get_trip_temp(device->thz, count, &trip_temp);
198
199                 if ((trip_temp >= temp) && (trip_temp < high_temp))
200                         high_temp = trip_temp;
201
202                 if ((trip_temp < temp) && (trip_temp > low_temp))
203                         low_temp = trip_temp;
204         }
205
206         if (device->set_limits && device->thz->trips)
207                 device->set_limits(device->data, low_temp, high_temp);
208 done:
209         mutex_unlock(&tegra_therm_mutex);
210 }
211
212 #ifdef CONFIG_TEGRA_SKIN_THROTTLE
213 static int tegra_skin_device_register(struct tegra_thermal_device *device)
214 {
215         int i;
216         struct therm_est_subdevice *skin_dev =
217                 kzalloc(sizeof(struct therm_est_subdevice), GFP_KERNEL);
218
219         for (i = 0; i < skin_therm->skin_devs_size; i++) {
220                 if (skin_therm->skin_devs[i].id == device->id) {
221                         memcpy(skin_dev->coeffs,
222                                 skin_therm->skin_devs[i].coeffs,
223                                 sizeof(skin_devs[i]->coeffs));
224                         break;
225                 }
226         }
227
228         skin_dev->dev_data = device->data;
229         skin_dev->get_temp = device->get_temp;
230
231         skin_devs[skin_devs_count++] = skin_dev;
232
233         /* Create skin thermal device */
234         if (skin_devs_count == skin_therm->skin_devs_size) {
235                 struct tegra_thermal_device *thermal_skin_device;
236                 struct therm_estimator *skin_estimator;
237
238                 skin_estimator = therm_est_register(
239                                         skin_devs,
240                                         skin_devs_count,
241                                         skin_therm->skin_temp_offset,
242                                         skin_therm->skin_period);
243                 thermal_skin_device = kzalloc(sizeof(struct tegra_thermal_device),
244                                                         GFP_KERNEL);
245                 thermal_skin_device->name = "skin_pred";
246                 thermal_skin_device->id = THERMAL_DEVICE_ID_SKIN;
247                 thermal_skin_device->data = skin_estimator;
248                 thermal_skin_device->get_temp =
249                         (int (*)(void *, long *)) therm_est_get_temp;
250                 thermal_skin_device->set_limits =
251                         (int (*)(void *, long, long)) therm_est_set_limits;
252                 thermal_skin_device->set_alert =
253                         (int (*)(void *, void (*)(void *), void *))
254                                 therm_est_set_alert;
255
256                 tegra_thermal_device_register(thermal_skin_device);
257         }
258
259         return 0;
260 }
261 #endif
262
263 static int passive_get_trip_temp(void *data, long trip)
264 {
265         struct tegra_thermal_bind *bind = data;
266         return bind->passive.trip_temp;
267 }
268
269 static int passive_get_trip_size(void)
270 {
271         return 1;
272 }
273
274 int tegra_thermal_device_register(struct tegra_thermal_device *device)
275 {
276         struct tegra_thermal_device *dev;
277         struct thermal_zone_device *thz = NULL;
278         int i, t1 = 0, t2 = 0, pdelay = 0, trips=0;
279
280         mutex_lock(&tegra_therm_mutex);
281         list_for_each_entry(dev, &tegra_therm_list, node) {
282                 if (dev->id == device->id) {
283                         mutex_unlock(&tegra_therm_mutex);
284                         return -EINVAL;
285                 }
286         }
287
288         for (i=0; thermal_binds[i].tdev_id; i++) {
289                 if(device->id == thermal_binds[i].tdev_id) {
290                         switch(thermal_binds[i].type) {
291                         case THERMAL_TRIP_PASSIVE:
292                                 /* only one passive type allowed for now */
293                                 if (pdelay)
294                                         return -EINVAL;
295
296                                 /* These should be set only for ACTIVE types */
297                                 if (thermal_binds[i].get_trip_temp ||
298                                         thermal_binds[i].get_trip_size)
299                                         return -EINVAL;
300
301                                 t1 = thermal_binds[i].passive.tc1;
302                                 t2 = thermal_binds[i].passive.tc2;
303                                 pdelay = thermal_binds[i].passive.passive_delay;
304
305                                 thermal_binds[i].get_trip_temp = passive_get_trip_temp;
306                                 thermal_binds[i].get_trip_size = passive_get_trip_size;
307                                 break;
308                         }
309
310                         trips += thermal_binds[i].get_trip_size();
311                 }
312         }
313
314         if (trips) {
315                 thz = thermal_zone_device_register(
316                                         device->name,
317                                         trips, /* trips */
318                                         device,
319                                         &tegra_thermal_zone_ops,
320                                         t1, /* dT/dt */
321                                         t2, /* throttle */
322                                         pdelay,
323                                         0); /* polling delay */
324                 if (IS_ERR_OR_NULL(thz))
325                         return -ENODEV;
326
327                 device->thz = thz;
328         }
329
330         list_add(&device->node, &tegra_therm_list);
331         mutex_unlock(&tegra_therm_mutex);
332
333         if (thz) {
334                 device->set_alert(device->data, tegra_thermal_alert, device);
335                 tegra_thermal_alert(device);
336         }
337
338 #ifdef CONFIG_TEGRA_SKIN_THROTTLE
339         if ((skin_therm->skin_device_id == THERMAL_DEVICE_ID_SKIN) &&
340                 device->id && skin_devs_bitmap)
341                 tegra_skin_device_register(device);
342 #endif
343
344         return 0;
345 }
346
347 /* This needs to be inialized later hand */
348 static int __init throttle_list_init(void)
349 {
350         int i;
351         for (i = 0; i < throttle_list_size; i++)
352                 if (balanced_throttle_register(&throttle_list[i]))
353                         return -ENODEV;
354
355         return 0;
356 }
357 late_initcall(throttle_list_init);
358
359 int __init tegra_thermal_init(struct tegra_thermal_bind *binds,
360                                 struct tegra_skin_data *skin_data,
361                                 struct balanced_throttle *tlist,
362                                 int tlist_size)
363 {
364         thermal_binds = binds;
365         skin_therm = skin_data;
366
367 #ifdef CONFIG_TEGRA_SKIN_THROTTLE
368         {
369                 int i;
370                 for (i = 0; i < skin_therm->skin_devs_size; i++)
371                         skin_devs_bitmap |= skin_therm->skin_devs[i].id;
372         }
373 #endif
374
375         throttle_list = tlist;
376         throttle_list_size = tlist_size;
377
378         register_pm_notifier(&tegra_thermal_nb);
379
380         return 0;
381 }
382
383 int tegra_thermal_exit(void)
384 {
385         struct tegra_thermal_device *dev;
386         mutex_lock(&tegra_therm_mutex);
387         list_for_each_entry(dev, &tegra_therm_list, node) {
388                 thermal_zone_device_unregister(dev->thz);
389         }
390         mutex_unlock(&tegra_therm_mutex);
391
392         return 0;
393 }
394