thermal: Add palmas thermal support
[linux-2.6.git] / drivers / thermal / pid_thermal_gov.c
1 /*
2  * drivers/thermal/pid_thermal_gov.c
3  *
4  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
5
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
21 #include <linux/module.h>
22 #include <linux/kobject.h>
23 #include <linux/slab.h>
24 #include <linux/thermal.h>
25
26 #include "thermal_core.h"
27
28 #define DRV_NAME        "pid_thermal_gov"
29
30 #define MAX_ERR_TEMP_DEFAULT            9000    /* in mC */
31 #define MAX_ERR_GAIN_DEFAULT            1000
32 #define GAIN_P_DEFAULT                  1000
33 #define GAIN_D_DEFAULT                  0
34 #define UP_COMPENSATION_DEFAULT         20
35 #define DOWN_COMPENSATION_DEFAULT       20
36
37 struct pid_thermal_gov_attribute {
38         struct attribute attr;
39         ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
40                         char *buf);
41         ssize_t (*store)(struct kobject *kobj, struct attribute *attr,
42                          const char *buf, size_t count);
43 };
44
45 struct pid_thermal_governor {
46         struct kobject kobj;
47
48         int max_err_temp; /* max error temperature in mC */
49         int max_err_gain; /* max error gain */
50
51         int gain_p; /* proportional gain */
52         int gain_i; /* integral gain */
53         int gain_d; /* derivative gain */
54
55         /* max derivative output, percentage of max error */
56         unsigned long max_dout;
57
58         unsigned long up_compensation;
59         unsigned long down_compensation;
60 };
61
62 #define tz_to_gov(t)            \
63         (t->governor_data)
64
65 #define kobj_to_gov(k)          \
66         container_of(k, struct pid_thermal_governor, kobj)
67
68 #define attr_to_gov_attr(a)     \
69         container_of(a, struct pid_thermal_gov_attribute, attr)
70
71 static ssize_t max_err_temp_show(struct kobject *kobj, struct attribute *attr,
72                                  char *buf)
73 {
74         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
75
76         if (!gov)
77                 return -ENODEV;
78
79         return sprintf(buf, "%d\n", gov->max_err_temp);
80 }
81
82 static ssize_t max_err_temp_store(struct kobject *kobj, struct attribute *attr,
83                                   const char *buf, size_t count)
84 {
85         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
86         int val;
87
88         if (!gov)
89                 return -ENODEV;
90
91         if (!sscanf(buf, "%d\n", &val))
92                 return -EINVAL;
93
94         gov->max_err_temp = val;
95         return count;
96 }
97
98 static struct pid_thermal_gov_attribute max_err_temp_attr =
99         __ATTR(max_err_temp, 0644, max_err_temp_show, max_err_temp_store);
100
101 static ssize_t max_err_gain_show(struct kobject *kobj, struct attribute *attr,
102                                  char *buf)
103 {
104         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
105
106         if (!gov)
107                 return -ENODEV;
108
109         return sprintf(buf, "%d\n", gov->max_err_gain);
110 }
111
112 static ssize_t max_err_gain_store(struct kobject *kobj, struct attribute *attr,
113                                   const char *buf, size_t count)
114 {
115         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
116         int val;
117
118         if (!gov)
119                 return -ENODEV;
120
121         if (!sscanf(buf, "%d\n", &val))
122                 return -EINVAL;
123
124         gov->max_err_gain = val;
125         return count;
126 }
127
128 static struct pid_thermal_gov_attribute max_err_gain_attr =
129         __ATTR(max_err_gain, 0644, max_err_gain_show, max_err_gain_store);
130
131 static ssize_t max_dout_show(struct kobject *kobj,
132                              struct attribute *attr, char *buf)
133 {
134         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
135
136         if (!gov)
137                 return -ENODEV;
138
139         return sprintf(buf, "%lu\n", gov->max_dout);
140 }
141
142 static ssize_t max_dout_store(struct kobject *kobj, struct attribute *attr,
143                               const char *buf, size_t count)
144 {
145         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
146         unsigned long val;
147
148         if (!gov)
149                 return -ENODEV;
150
151         if (!sscanf(buf, "%lu\n", &val))
152                 return -EINVAL;
153
154         gov->max_dout = val;
155         return count;
156 }
157
158 static struct pid_thermal_gov_attribute max_dout_attr =
159         __ATTR(max_dout, 0644, max_dout_show, max_dout_store);
160
161 static ssize_t gain_p_show(struct kobject *kobj, struct attribute *attr,
162                            char *buf)
163 {
164         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
165
166         if (!gov)
167                 return -ENODEV;
168
169         return sprintf(buf, "%d\n", gov->gain_p);
170 }
171
172 static ssize_t gain_p_store(struct kobject *kobj, struct attribute *attr,
173                             const char *buf, size_t count)
174 {
175         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
176         int val;
177
178         if (!gov)
179                 return -ENODEV;
180
181         if (!sscanf(buf, "%d\n", &val))
182                 return -EINVAL;
183
184         gov->gain_p = val;
185         return count;
186 }
187
188 static struct pid_thermal_gov_attribute gain_p_attr =
189         __ATTR(gain_p, 0644, gain_p_show, gain_p_store);
190
191 static ssize_t gain_d_show(struct kobject *kobj, struct attribute *attr,
192                            char *buf)
193 {
194         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
195
196         if (!gov)
197                 return -ENODEV;
198
199         return sprintf(buf, "%d\n", gov->gain_d);
200 }
201
202 static ssize_t gain_d_store(struct kobject *kobj, struct attribute *attr,
203                             const char *buf, size_t count)
204 {
205         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
206         int val;
207
208         if (!gov)
209                 return -ENODEV;
210
211         if (!sscanf(buf, "%d\n", &val))
212                 return -EINVAL;
213
214         gov->gain_d = val;
215         return count;
216 }
217
218 static struct pid_thermal_gov_attribute gain_d_attr =
219         __ATTR(gain_d, 0644, gain_d_show, gain_d_store);
220
221 static ssize_t up_compensation_show(struct kobject *kobj,
222                                     struct attribute *attr, char *buf)
223 {
224         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
225
226         if (!gov)
227                 return -ENODEV;
228
229         return sprintf(buf, "%lu\n", gov->up_compensation);
230 }
231
232 static ssize_t up_compensation_store(struct kobject *kobj,
233                                      struct attribute *attr, const char *buf,
234                                      size_t count)
235 {
236         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
237         unsigned long val;
238
239         if (!gov)
240                 return -ENODEV;
241
242         if (!sscanf(buf, "%lu\n", &val))
243                 return -EINVAL;
244
245         gov->up_compensation = val;
246         return count;
247 }
248
249 static struct pid_thermal_gov_attribute up_compensation_attr =
250         __ATTR(up_compensation, 0644,
251                up_compensation_show, up_compensation_store);
252
253 static ssize_t down_compensation_show(struct kobject *kobj,
254                                       struct attribute *attr, char *buf)
255 {
256         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
257
258         if (!gov)
259                 return -ENODEV;
260
261         return sprintf(buf, "%lu\n", gov->down_compensation);
262 }
263
264 static ssize_t down_compensation_store(struct kobject *kobj,
265                                        struct attribute *attr, const char *buf,
266                                        size_t count)
267 {
268         struct pid_thermal_governor *gov = kobj_to_gov(kobj);
269         unsigned long val;
270
271         if (!gov)
272                 return -ENODEV;
273
274         if (!sscanf(buf, "%lu\n", &val))
275                 return -EINVAL;
276
277         gov->down_compensation = val;
278         return count;
279 }
280
281 static struct pid_thermal_gov_attribute down_compensation_attr =
282         __ATTR(down_compensation, 0644,
283                down_compensation_show, down_compensation_store);
284
285 static struct attribute *pid_thermal_gov_default_attrs[] = {
286         &max_err_temp_attr.attr,
287         &max_err_gain_attr.attr,
288         &gain_p_attr.attr,
289         &gain_d_attr.attr,
290         &max_dout_attr.attr,
291         &up_compensation_attr.attr,
292         &down_compensation_attr.attr,
293         NULL,
294 };
295
296 static ssize_t pid_thermal_gov_show(struct kobject *kobj,
297                                     struct attribute *attr, char *buf)
298 {
299         struct pid_thermal_gov_attribute *gov_attr = attr_to_gov_attr(attr);
300
301         if (!gov_attr->show)
302                 return -EIO;
303
304         return gov_attr->show(kobj, attr, buf);
305 }
306
307 static ssize_t pid_thermal_gov_store(struct kobject *kobj,
308                                      struct attribute *attr, const char *buf,
309                                      size_t len)
310 {
311         struct pid_thermal_gov_attribute *gov_attr = attr_to_gov_attr(attr);
312
313         if (!gov_attr->store)
314                 return -EIO;
315
316         return gov_attr->store(kobj, attr, buf, len);
317 }
318
319 static const struct sysfs_ops pid_thermal_gov_sysfs_ops = {
320         .show   = pid_thermal_gov_show,
321         .store  = pid_thermal_gov_store,
322 };
323
324 static struct kobj_type pid_thermal_gov_ktype = {
325         .default_attrs  = pid_thermal_gov_default_attrs,
326         .sysfs_ops      = &pid_thermal_gov_sysfs_ops,
327 };
328
329 static int pid_thermal_gov_start(struct thermal_zone_device *tz)
330 {
331         struct pid_thermal_governor *gov;
332         int ret;
333
334         gov = kzalloc(sizeof(struct pid_thermal_governor), GFP_KERNEL);
335         if (!gov) {
336                 dev_err(&tz->device, "%s: Can't alloc governor data\n",
337                         DRV_NAME);
338                 return -ENOMEM;
339         }
340
341         ret = kobject_init_and_add(&gov->kobj, &pid_thermal_gov_ktype,
342                                    &tz->device.kobj, DRV_NAME);
343         if (ret) {
344                 dev_err(&tz->device, "%s: Can't init kobject\n", DRV_NAME);
345                 kobject_put(&gov->kobj);
346                 kfree(gov);
347                 return ret;
348         }
349
350         gov->max_err_temp = MAX_ERR_TEMP_DEFAULT;
351         gov->max_err_gain = MAX_ERR_GAIN_DEFAULT;
352         gov->gain_p = GAIN_P_DEFAULT;
353         gov->gain_d = GAIN_D_DEFAULT;
354         gov->up_compensation = UP_COMPENSATION_DEFAULT;
355         gov->down_compensation = DOWN_COMPENSATION_DEFAULT;
356         tz->governor_data = gov;
357
358         return 0;
359 }
360
361 static void pid_thermal_gov_stop(struct thermal_zone_device *tz)
362 {
363         struct pid_thermal_governor *gov = tz_to_gov(tz);
364
365         if (!gov)
366                 return;
367
368         kobject_put(&gov->kobj);
369         kfree(gov);
370 }
371
372 static void pid_thermal_gov_update_passive(struct thermal_zone_device *tz,
373                                            enum thermal_trip_type trip_type,
374                                            unsigned long old,
375                                            unsigned long new)
376 {
377         if ((trip_type != THERMAL_TRIP_PASSIVE) &&
378                         (trip_type != THERMAL_TRIPS_NONE))
379                 return;
380
381         if ((old == THERMAL_NO_TARGET) && (new != THERMAL_NO_TARGET))
382                 tz->passive++;
383         else if ((old != THERMAL_NO_TARGET) && (new == THERMAL_NO_TARGET))
384                 tz->passive--;
385 }
386
387 static unsigned long
388 pid_thermal_gov_get_target(struct thermal_zone_device *tz,
389                            struct thermal_cooling_device *cdev,
390                            enum thermal_trip_type trip_type,
391                            unsigned long trip_temp)
392 {
393         struct pid_thermal_governor *gov = tz_to_gov(tz);
394         int last_temperature = tz->passive ? tz->last_temperature : trip_temp;
395         int passive_delay = tz->passive ? tz->passive_delay : MSEC_PER_SEC;
396         s64 proportional, derivative, sum_err, max_err;
397         unsigned long max_state, cur_state, target, compensation;
398
399         if (cdev->ops->get_max_state(cdev, &max_state) < 0)
400                 return 0;
401
402         if (cdev->ops->get_cur_state(cdev, &cur_state) < 0)
403                 return 0;
404
405         max_err = (s64)gov->max_err_temp * (s64)gov->max_err_gain;
406
407         /* Calculate proportional term */
408         proportional = (s64)tz->temperature - (s64)trip_temp;
409         proportional *= gov->gain_p;
410
411         /* Calculate derivative term */
412         derivative = (s64)tz->temperature - (s64)last_temperature;
413         derivative *= gov->gain_d;
414         derivative *= MSEC_PER_SEC;
415         derivative = div64_s64(derivative, passive_delay);
416         if (gov->max_dout) {
417                 s64 max_dout = div64_s64(max_err * gov->max_dout, 100);
418                 if (derivative < 0)
419                         derivative = -min_t(s64, abs64(derivative), max_dout);
420                 else
421                         derivative = min_t(s64, derivative, max_dout);
422         }
423
424         sum_err = max_t(s64, proportional + derivative, 0);
425         sum_err = min_t(s64, sum_err, max_err);
426         sum_err = sum_err * max_state + max_err - 1;
427         target = (unsigned long)div64_s64(sum_err, max_err);
428
429         /* Apply compensation */
430         if (target == cur_state)
431                 return target;
432
433         if (target > cur_state) {
434                 compensation = DIV_ROUND_UP(gov->up_compensation *
435                                             (target - cur_state), 100);
436                 target = min(cur_state + compensation, max_state);
437         } else if (target < cur_state) {
438                 compensation = DIV_ROUND_UP(gov->down_compensation *
439                                             (cur_state - target), 100);
440                 target = (cur_state > compensation) ?
441                          (cur_state - compensation) : 0;
442         }
443
444         return target;
445 }
446
447 static int pid_thermal_gov_throttle(struct thermal_zone_device *tz, int trip)
448 {
449         struct thermal_instance *instance;
450         enum thermal_trip_type trip_type;
451         long trip_temp;
452         unsigned long target;
453
454         tz->ops->get_trip_type(tz, trip, &trip_type);
455         tz->ops->get_trip_temp(tz, trip, &trip_temp);
456
457         mutex_lock(&tz->lock);
458
459         list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
460                 if ((instance->trip != trip) ||
461                                 ((tz->temperature < trip_temp) &&
462                                  (instance->target == THERMAL_NO_TARGET)))
463                         continue;
464
465                 if (instance->upper == instance->lower) {
466                         target = instance->upper;
467                 } else {
468                         target = pid_thermal_gov_get_target(tz, instance->cdev,
469                                                         trip_type, trip_temp);
470                         target = min(max(target, instance->lower),
471                                      instance->upper);
472                 }
473
474                 if ((tz->temperature < trip_temp) &&
475                                 (instance->target == instance->lower) &&
476                                 (target == instance->lower))
477                         target = THERMAL_NO_TARGET;
478
479                 if (instance->target == target)
480                         continue;
481
482                 pid_thermal_gov_update_passive(tz, trip_type, instance->target,
483                                                target);
484                 instance->target = target;
485                 instance->cdev->updated = false;
486         }
487
488         list_for_each_entry(instance, &tz->thermal_instances, tz_node)
489                 thermal_cdev_update(instance->cdev);
490
491         mutex_unlock(&tz->lock);
492
493         return 0;
494 }
495
496 static struct thermal_governor pid_thermal_gov = {
497         .name           = DRV_NAME,
498         .start          = pid_thermal_gov_start,
499         .stop           = pid_thermal_gov_stop,
500         .throttle       = pid_thermal_gov_throttle,
501         .owner          = THIS_MODULE,
502 };
503
504 static int __init pid_thermal_gov_init(void)
505 {
506         return thermal_register_governor(&pid_thermal_gov);
507 }
508 fs_initcall(pid_thermal_gov_init);
509
510 static void __exit pid_thermal_gov_exit(void)
511 {
512         thermal_unregister_governor(&pid_thermal_gov);
513 }
514 module_exit(pid_thermal_gov_exit);
515
516 MODULE_AUTHOR("Jinyoung Park <jinyoungp@nvidia.com>");
517 MODULE_DESCRIPTION("PID thermal governor");
518 MODULE_LICENSE("GPL");