Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / drivers / thermal / samsung / exynos_thermal_common.c
1 /*
2  * exynos_thermal_common.c - Samsung EXYNOS common thermal file
3  *
4  *  Copyright (C) 2013 Samsung Electronics
5  *  Amit Daniel Kachhap <amit.daniel@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include <linux/cpu_cooling.h>
24 #include <linux/err.h>
25 #include <linux/slab.h>
26 #include <linux/thermal.h>
27
28 #include "exynos_thermal_common.h"
29
30 struct exynos_thermal_zone {
31         enum thermal_device_mode mode;
32         struct thermal_zone_device *therm_dev;
33         struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
34         unsigned int cool_dev_size;
35         struct platform_device *exynos4_dev;
36         struct thermal_sensor_conf *sensor_conf;
37         bool bind;
38 };
39
40 /* Get mode callback functions for thermal zone */
41 static int exynos_get_mode(struct thermal_zone_device *thermal,
42                         enum thermal_device_mode *mode)
43 {
44         struct exynos_thermal_zone *th_zone = thermal->devdata;
45         if (th_zone)
46                 *mode = th_zone->mode;
47         return 0;
48 }
49
50 /* Set mode callback functions for thermal zone */
51 static int exynos_set_mode(struct thermal_zone_device *thermal,
52                         enum thermal_device_mode mode)
53 {
54         struct exynos_thermal_zone *th_zone = thermal->devdata;
55         if (!th_zone) {
56                 dev_err(&thermal->device,
57                         "thermal zone not registered\n");
58                 return 0;
59         }
60
61         mutex_lock(&thermal->lock);
62
63         if (mode == THERMAL_DEVICE_ENABLED &&
64                 !th_zone->sensor_conf->trip_data.trigger_falling)
65                 thermal->polling_delay = IDLE_INTERVAL;
66         else
67                 thermal->polling_delay = 0;
68
69         mutex_unlock(&thermal->lock);
70
71         th_zone->mode = mode;
72         thermal_zone_device_update(thermal);
73         dev_dbg(th_zone->sensor_conf->dev,
74                 "thermal polling set for duration=%d msec\n",
75                 thermal->polling_delay);
76         return 0;
77 }
78
79
80 /* Get trip type callback functions for thermal zone */
81 static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
82                                  enum thermal_trip_type *type)
83 {
84         struct exynos_thermal_zone *th_zone = thermal->devdata;
85         int max_trip = th_zone->sensor_conf->trip_data.trip_count;
86         int trip_type;
87
88         if (trip < 0 || trip >= max_trip)
89                 return -EINVAL;
90
91         trip_type = th_zone->sensor_conf->trip_data.trip_type[trip];
92
93         if (trip_type == SW_TRIP)
94                 *type = THERMAL_TRIP_CRITICAL;
95         else if (trip_type == THROTTLE_ACTIVE)
96                 *type = THERMAL_TRIP_ACTIVE;
97         else if (trip_type == THROTTLE_PASSIVE)
98                 *type = THERMAL_TRIP_PASSIVE;
99         else
100                 return -EINVAL;
101
102         return 0;
103 }
104
105 /* Get trip temperature callback functions for thermal zone */
106 static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
107                                 unsigned long *temp)
108 {
109         struct exynos_thermal_zone *th_zone = thermal->devdata;
110         int max_trip = th_zone->sensor_conf->trip_data.trip_count;
111
112         if (trip < 0 || trip >= max_trip)
113                 return -EINVAL;
114
115         *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
116         /* convert the temperature into millicelsius */
117         *temp = *temp * MCELSIUS;
118
119         return 0;
120 }
121
122 /* Get critical temperature callback functions for thermal zone */
123 static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
124                                 unsigned long *temp)
125 {
126         struct exynos_thermal_zone *th_zone = thermal->devdata;
127         int max_trip = th_zone->sensor_conf->trip_data.trip_count;
128         /* Get the temp of highest trip*/
129         return exynos_get_trip_temp(thermal, max_trip - 1, temp);
130 }
131
132 /* Bind callback functions for thermal zone */
133 static int exynos_bind(struct thermal_zone_device *thermal,
134                         struct thermal_cooling_device *cdev)
135 {
136         int ret = 0, i, tab_size, level;
137         struct freq_clip_table *tab_ptr, *clip_data;
138         struct exynos_thermal_zone *th_zone = thermal->devdata;
139         struct thermal_sensor_conf *data = th_zone->sensor_conf;
140
141         tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
142         tab_size = data->cooling_data.freq_clip_count;
143
144         if (tab_ptr == NULL || tab_size == 0)
145                 return 0;
146
147         /* find the cooling device registered*/
148         for (i = 0; i < th_zone->cool_dev_size; i++)
149                 if (cdev == th_zone->cool_dev[i])
150                         break;
151
152         /* No matching cooling device */
153         if (i == th_zone->cool_dev_size)
154                 return 0;
155
156         /* Bind the thermal zone to the cpufreq cooling device */
157         for (i = 0; i < tab_size; i++) {
158                 clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
159                 level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
160                 if (level == THERMAL_CSTATE_INVALID)
161                         return 0;
162                 switch (GET_ZONE(i)) {
163                 case MONITOR_ZONE:
164                 case WARN_ZONE:
165                         if (thermal_zone_bind_cooling_device(thermal, i, cdev,
166                                                                 level, 0)) {
167                                 dev_err(data->dev,
168                                         "error unbinding cdev inst=%d\n", i);
169                                 ret = -EINVAL;
170                         }
171                         th_zone->bind = true;
172                         break;
173                 default:
174                         ret = -EINVAL;
175                 }
176         }
177
178         return ret;
179 }
180
181 /* Unbind callback functions for thermal zone */
182 static int exynos_unbind(struct thermal_zone_device *thermal,
183                         struct thermal_cooling_device *cdev)
184 {
185         int ret = 0, i, tab_size;
186         struct exynos_thermal_zone *th_zone = thermal->devdata;
187         struct thermal_sensor_conf *data = th_zone->sensor_conf;
188
189         if (th_zone->bind == false)
190                 return 0;
191
192         tab_size = data->cooling_data.freq_clip_count;
193
194         if (tab_size == 0)
195                 return 0;
196
197         /* find the cooling device registered*/
198         for (i = 0; i < th_zone->cool_dev_size; i++)
199                 if (cdev == th_zone->cool_dev[i])
200                         break;
201
202         /* No matching cooling device */
203         if (i == th_zone->cool_dev_size)
204                 return 0;
205
206         /* Bind the thermal zone to the cpufreq cooling device */
207         for (i = 0; i < tab_size; i++) {
208                 switch (GET_ZONE(i)) {
209                 case MONITOR_ZONE:
210                 case WARN_ZONE:
211                         if (thermal_zone_unbind_cooling_device(thermal, i,
212                                                                 cdev)) {
213                                 dev_err(data->dev,
214                                         "error unbinding cdev inst=%d\n", i);
215                                 ret = -EINVAL;
216                         }
217                         th_zone->bind = false;
218                         break;
219                 default:
220                         ret = -EINVAL;
221                 }
222         }
223         return ret;
224 }
225
226 /* Get temperature callback functions for thermal zone */
227 static int exynos_get_temp(struct thermal_zone_device *thermal,
228                         unsigned long *temp)
229 {
230         struct exynos_thermal_zone *th_zone = thermal->devdata;
231         void *data;
232
233         if (!th_zone->sensor_conf) {
234                 dev_err(&thermal->device,
235                         "Temperature sensor not initialised\n");
236                 return -EINVAL;
237         }
238         data = th_zone->sensor_conf->driver_data;
239         *temp = th_zone->sensor_conf->read_temperature(data);
240         /* convert the temperature into millicelsius */
241         *temp = *temp * MCELSIUS;
242         return 0;
243 }
244
245 /* Get temperature callback functions for thermal zone */
246 static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
247                                                 unsigned long temp)
248 {
249         void *data;
250         int ret = -EINVAL;
251         struct exynos_thermal_zone *th_zone = thermal->devdata;
252
253         if (!th_zone->sensor_conf) {
254                 dev_err(&thermal->device,
255                         "Temperature sensor not initialised\n");
256                 return -EINVAL;
257         }
258         data = th_zone->sensor_conf->driver_data;
259         if (th_zone->sensor_conf->write_emul_temp)
260                 ret = th_zone->sensor_conf->write_emul_temp(data, temp);
261         return ret;
262 }
263
264 /* Get the temperature trend */
265 static int exynos_get_trend(struct thermal_zone_device *thermal,
266                         int trip, enum thermal_trend *trend)
267 {
268         int ret;
269         unsigned long trip_temp;
270
271         ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
272         if (ret < 0)
273                 return ret;
274
275         if (thermal->temperature >= trip_temp)
276                 *trend = THERMAL_TREND_RAISE_FULL;
277         else
278                 *trend = THERMAL_TREND_DROP_FULL;
279
280         return 0;
281 }
282 /* Operation callback functions for thermal zone */
283 static struct thermal_zone_device_ops exynos_dev_ops = {
284         .bind = exynos_bind,
285         .unbind = exynos_unbind,
286         .get_temp = exynos_get_temp,
287         .set_emul_temp = exynos_set_emul_temp,
288         .get_trend = exynos_get_trend,
289         .get_mode = exynos_get_mode,
290         .set_mode = exynos_set_mode,
291         .get_trip_type = exynos_get_trip_type,
292         .get_trip_temp = exynos_get_trip_temp,
293         .get_crit_temp = exynos_get_crit_temp,
294 };
295
296 /*
297  * This function may be called from interrupt based temperature sensor
298  * when threshold is changed.
299  */
300 void exynos_report_trigger(struct thermal_sensor_conf *conf)
301 {
302         unsigned int i;
303         char data[10];
304         char *envp[] = { data, NULL };
305         struct exynos_thermal_zone *th_zone;
306
307         if (!conf || !conf->pzone_data) {
308                 pr_err("Invalid temperature sensor configuration data\n");
309                 return;
310         }
311
312         th_zone = conf->pzone_data;
313
314         if (th_zone->bind == false) {
315                 for (i = 0; i < th_zone->cool_dev_size; i++) {
316                         if (!th_zone->cool_dev[i])
317                                 continue;
318                         exynos_bind(th_zone->therm_dev,
319                                         th_zone->cool_dev[i]);
320                 }
321         }
322
323         thermal_zone_device_update(th_zone->therm_dev);
324
325         mutex_lock(&th_zone->therm_dev->lock);
326         /* Find the level for which trip happened */
327         for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
328                 if (th_zone->therm_dev->last_temperature <
329                         th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
330                         break;
331         }
332
333         if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
334                 !th_zone->sensor_conf->trip_data.trigger_falling) {
335                 if (i > 0)
336                         th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
337                 else
338                         th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
339         }
340
341         snprintf(data, sizeof(data), "%u", i);
342         kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
343         mutex_unlock(&th_zone->therm_dev->lock);
344 }
345
346 /* Register with the in-kernel thermal management */
347 int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
348 {
349         int ret;
350         struct exynos_thermal_zone *th_zone;
351
352         if (!sensor_conf || !sensor_conf->read_temperature) {
353                 pr_err("Temperature sensor not initialised\n");
354                 return -EINVAL;
355         }
356
357         th_zone = devm_kzalloc(sensor_conf->dev,
358                                 sizeof(struct exynos_thermal_zone), GFP_KERNEL);
359         if (!th_zone)
360                 return -ENOMEM;
361
362         th_zone->sensor_conf = sensor_conf;
363         /*
364          * TODO: 1) Handle multiple cooling devices in a thermal zone
365          *       2) Add a flag/name in cooling info to map to specific
366          *       sensor
367          */
368         if (sensor_conf->cooling_data.freq_clip_count > 0) {
369                 th_zone->cool_dev[th_zone->cool_dev_size] =
370                                 cpufreq_cooling_register(cpu_present_mask);
371                 if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
372                         ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]);
373                         if (ret != -EPROBE_DEFER)
374                                 dev_err(sensor_conf->dev,
375                                         "Failed to register cpufreq cooling device: %d\n",
376                                         ret);
377                         goto err_unregister;
378                 }
379                 th_zone->cool_dev_size++;
380         }
381
382         th_zone->therm_dev = thermal_zone_device_register(
383                         sensor_conf->name, sensor_conf->trip_data.trip_count,
384                         0, th_zone, &exynos_dev_ops, NULL, 0,
385                         sensor_conf->trip_data.trigger_falling ? 0 :
386                         IDLE_INTERVAL);
387
388         if (IS_ERR(th_zone->therm_dev)) {
389                 dev_err(sensor_conf->dev,
390                         "Failed to register thermal zone device\n");
391                 ret = PTR_ERR(th_zone->therm_dev);
392                 goto err_unregister;
393         }
394         th_zone->mode = THERMAL_DEVICE_ENABLED;
395         sensor_conf->pzone_data = th_zone;
396
397         dev_info(sensor_conf->dev,
398                 "Exynos: Thermal zone(%s) registered\n", sensor_conf->name);
399
400         return 0;
401
402 err_unregister:
403         exynos_unregister_thermal(sensor_conf);
404         return ret;
405 }
406
407 /* Un-Register with the in-kernel thermal management */
408 void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
409 {
410         int i;
411         struct exynos_thermal_zone *th_zone;
412
413         if (!sensor_conf || !sensor_conf->pzone_data) {
414                 pr_err("Invalid temperature sensor configuration data\n");
415                 return;
416         }
417
418         th_zone = sensor_conf->pzone_data;
419
420         thermal_zone_device_unregister(th_zone->therm_dev);
421
422         for (i = 0; i < th_zone->cool_dev_size; ++i)
423                 cpufreq_cooling_unregister(th_zone->cool_dev[i]);
424
425         dev_info(sensor_conf->dev,
426                 "Exynos: Kernel Thermal management unregistered\n");
427 }