import PULS_20160108
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / thermal / mt8127 / mtk_cooler_bcct.c
1 /*
2 * Copyright (C) 2011-2014 MediaTek Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify it under the terms of the
5 * GNU General Public License version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9 * See the GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License along with this program.
12 * If not, see <http://www.gnu.org/licenses/>.
13 */
14
15 #ifdef pr_fmt
16 #undef pr_fmt
17 #endif
18 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
20 #include <linux/version.h>
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/printk.h>
24 #include <linux/types.h>
25 #include <linux/kobject.h>
26 #include <linux/proc_fs.h>
27 #include <linux/seq_file.h>
28 #include <asm/uaccess.h>
29 #include <linux/err.h>
30 #include <linux/syscalls.h>
31 #include <asm/system.h>
32
33 #include "mach/mtk_thermal_monitor.h"
34 #include <mach/system.h>
35
36
37 /* Extern two API functions from battery driver to limit max charging current. */
38 #if 1
39 /**
40 * return value means charging current in mA
41 * -1 means error
42 * Implementation in mt_battery.c and mt_battery_fan5405.c
43 */
44 extern int get_bat_charging_current_level(void);
45
46 /**
47 * current_limit means limit of charging current in mA
48 * -1 means no limit
49 * Implementation in mt_battery.c and mt_battery_fan5405.c
50 */
51 extern int set_bat_charging_current_limit(int current_limit);
52 #endif
53
54 extern struct proc_dir_entry * mtk_thermal_get_proc_drv_therm_dir_entry(void);
55
56 #define mtk_cooler_bcct_dprintk_always(fmt, args...) \
57 do { pr_debug("thermal/cooler/bcct " fmt, ##args); } while (0)
58
59 #define mtk_cooler_bcct_dprintk(fmt, args...) \
60 do { \
61 if (1 == cl_bcct_klog_on) { \
62 pr_debug("thermal/cooler/bcct " fmt, ##args); \
63 } \
64 } while (0)
65
66 #define MAX_NUM_INSTANCE_MTK_COOLER_BCCT 3
67
68 #define MTK_CL_BCCT_GET_LIMIT(limit, state) \
69 do { (limit) = (short) (((unsigned long) (state))>>16); } while (0)
70
71 #define MTK_CL_BCCT_SET_LIMIT(limit, state) \
72 do { state = ((((unsigned long) (state))&0xFFFF) | ((short) limit<<16)); } while (0)
73
74 #define MTK_CL_BCCT_GET_CURR_STATE(curr_state, state) \
75 do { curr_state = (((unsigned long) (state))&0xFFFF); } while (0)
76
77 #define MTK_CL_BCCT_SET_CURR_STATE(curr_state, state) \
78 do { if (0 == curr_state) \
79 state &= ~0x1; \
80 else \
81 state |= 0x1; \
82 } while (0)
83
84 static int cl_bcct_klog_on;
85 static struct thermal_cooling_device *cl_bcct_dev[MAX_NUM_INSTANCE_MTK_COOLER_BCCT] = { 0 };
86 static unsigned long cl_bcct_state[MAX_NUM_INSTANCE_MTK_COOLER_BCCT] = { 0 };
87
88 static int cl_bcct_cur_limit = 65535;
89
90 static void mtk_cl_bcct_set_bcct_limit(void)
91 {
92 /* TODO: optimize */
93 int i = 0;
94 int min_limit = 65535;
95 for (; i < MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i++) {
96 unsigned long curr_state;
97
98 MTK_CL_BCCT_GET_CURR_STATE(curr_state, cl_bcct_state[i]);
99 if (1 == curr_state) {
100 int limit;
101 MTK_CL_BCCT_GET_LIMIT(limit, cl_bcct_state[i]);
102 if ((min_limit > limit) && (limit > 0))
103 min_limit = limit;
104 }
105 }
106
107 if (min_limit != cl_bcct_cur_limit) {
108 cl_bcct_cur_limit = min_limit;
109 #if 1
110 if (65535 <= cl_bcct_cur_limit) {
111 set_bat_charging_current_limit(-1);
112 mtk_cooler_bcct_dprintk_always("%s limit=-1\n", __func__);
113 } else {
114 set_bat_charging_current_limit(cl_bcct_cur_limit);
115 mtk_cooler_bcct_dprintk_always("%s limit=%d\n", __func__,
116 cl_bcct_cur_limit);
117 }
118
119 mtk_cooler_bcct_dprintk_always("%s real limit=%d\n", __func__,
120 get_bat_charging_current_level() / 100);
121 #endif
122 }
123 }
124
125 static int mtk_cl_bcct_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
126 {
127 *state = 1;
128 mtk_cooler_bcct_dprintk("%s %s %lu\n", __func__, cdev->type, *state);
129 return 0;
130 }
131
132 static int mtk_cl_bcct_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
133 {
134 MTK_CL_BCCT_GET_CURR_STATE(*state, *((unsigned long *)cdev->devdata));
135 mtk_cooler_bcct_dprintk("%s %s %lu\n", __func__, cdev->type, *state);
136 mtk_cooler_bcct_dprintk("%s %s limit=%d\n", __func__, cdev->type,
137 get_bat_charging_current_level() / 100);
138 return 0;
139 }
140
141 static int mtk_cl_bcct_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
142 {
143 mtk_cooler_bcct_dprintk("%s %s %lu\n", __func__, cdev->type, state);
144 MTK_CL_BCCT_SET_CURR_STATE(state, *((unsigned long *)cdev->devdata));
145 mtk_cl_bcct_set_bcct_limit();
146 mtk_cooler_bcct_dprintk("%s %s limit=%d\n", __func__, cdev->type,
147 get_bat_charging_current_level() / 100);
148
149 return 0;
150 }
151
152 /* bind fan callbacks to fan device */
153 static struct thermal_cooling_device_ops mtk_cl_bcct_ops = {
154 .get_max_state = mtk_cl_bcct_get_max_state,
155 .get_cur_state = mtk_cl_bcct_get_cur_state,
156 .set_cur_state = mtk_cl_bcct_set_cur_state,
157 };
158
159 static int mtk_cooler_bcct_register_ltf(void)
160 {
161 int i;
162 mtk_cooler_bcct_dprintk("register ltf\n");
163
164 for (i = MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i-- > 0;) {
165 char temp[20] = { 0 };
166 sprintf(temp, "mtk-cl-bcct%02d", i);
167 cl_bcct_dev[i] = mtk_thermal_cooling_device_register(temp, (void *)&cl_bcct_state[i], /* put bcct state to cooler devdata */
168 &mtk_cl_bcct_ops);
169 }
170
171 return 0;
172 }
173
174 static void mtk_cooler_bcct_unregister_ltf(void)
175 {
176 int i;
177 mtk_cooler_bcct_dprintk("unregister ltf\n");
178
179 for (i = MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i-- > 0;) {
180 if (cl_bcct_dev[i]) {
181 mtk_thermal_cooling_device_unregister(cl_bcct_dev[i]);
182 cl_bcct_dev[i] = NULL;
183 cl_bcct_state[i] = 0;
184 }
185 }
186 }
187
188 #if 0
189 static int _mtk_cl_bcct_proc_read(char *buf, char **start, off_t off, int count, int *eof,
190 void *data)
191 {
192 int len = 0;
193 char *p = buf;
194
195 mtk_cooler_bcct_dprintk("[_mtk_cl_bcct_proc_read] invoked.\n");
196
197 /**
198 * The format to print out:
199 * kernel_log <0 or 1>
200 * <mtk-cl-bcct<ID>> <bcc limit>
201 * ..
202 */
203 if (NULL == data) {
204 mtk_cooler_bcct_dprintk("[_mtk_cl_bcct_proc_read] null data\n");
205 } else {
206 int i = 0;
207
208 p += sprintf(p, "klog %d\n", cl_bcct_klog_on);
209 p += sprintf(p, "curr_limit %d\n", cl_bcct_cur_limit);
210
211 for (; i < MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i++) {
212 int limit;
213 unsigned int curr_state;
214
215 MTK_CL_BCCT_GET_LIMIT(limit, cl_bcct_state[i]);
216 MTK_CL_BCCT_GET_CURR_STATE(curr_state, cl_bcct_state[i]);
217
218 p += sprintf(p, "mtk-cl-bcct%02d %d mA, state %d\n", i, limit, curr_state);
219 }
220 }
221
222 *start = buf + off;
223
224 len = p - buf;
225 if (len > off)
226 len -= off;
227 else
228 len = 0;
229
230 return len < count ? len : count;
231 }
232
233 static ssize_t _mtk_cl_bcct_proc_write(struct file *file, const char *buffer, unsigned long count,
234 void *data)
235 {
236 int len = 0;
237 char desc[128];
238 int klog_on, limit0, limit1, limit2;
239
240 len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
241 if (copy_from_user(desc, buffer, len)) {
242 return 0;
243 }
244 desc[len] = '\0';
245
246 /**
247 * sscanf format <klog_on> <mtk-cl-bcct00 limit> <mtk-cl-bcct01 limit> ...
248 * <klog_on> can only be 0 or 1
249 * <mtk-cl-bcct00 limit> can only be positive integer or -1 to denote no limit
250 */
251
252 if (NULL == data) {
253 mtk_cooler_bcct_dprintk("[_mtk_cl_bcct_proc_write] null data\n");
254 return -EINVAL;
255 }
256 /* WARNING: Modify here if MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS is changed to other than 3 */
257 #if (3 == MAX_NUM_INSTANCE_MTK_COOLER_BCCT)
258 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[0]);
259 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[1]);
260 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[2]);
261
262 if (1 <= sscanf(desc, "%d %d %d %d", &klog_on, &limit0, &limit1, &limit2)) {
263 if (klog_on == 0 || klog_on == 1) {
264 cl_bcct_klog_on = klog_on;
265 }
266
267 if (limit0 >= -1)
268 MTK_CL_BCCT_SET_LIMIT(limit0, cl_bcct_state[0]);
269 if (limit1 >= -1)
270 MTK_CL_BCCT_SET_LIMIT(limit1, cl_bcct_state[1]);
271 if (limit2 >= -1)
272 MTK_CL_BCCT_SET_LIMIT(limit2, cl_bcct_state[2]);
273
274 return count;
275 } else
276 #else
277 #error "Change correspondent part when changing MAX_NUM_INSTANCE_MTK_COOLER_BCCT!"
278 #endif
279 {
280 mtk_cooler_bcct_dprintk("[_mtk_cl_bcct_proc_write] bad argument\n");
281 }
282
283 return -EINVAL;
284 }
285 #endif
286
287 static ssize_t _cl_bcct_write(struct file *filp, const char __user *buf, size_t len, loff_t *data)
288 {
289 /* int ret = 0; */
290 char tmp[128] = { 0 };
291 int klog_on, limit0, limit1, limit2;
292
293 /* write data to the buffer */
294 if (copy_from_user(tmp, buf, len)) {
295 return -EFAULT;
296 }
297
298 /**
299 * sscanf format <klog_on> <mtk-cl-bcct00 limit> <mtk-cl-bcct01 limit> ...
300 * <klog_on> can only be 0 or 1
301 * <mtk-cl-bcct00 limit> can only be positive integer or -1 to denote no limit
302 */
303
304 if (NULL == data) {
305 mtk_cooler_bcct_dprintk("%s null data\n", __func__);
306 return -EINVAL;
307 }
308 /* WARNING: Modify here if MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS is changed to other than 3 */
309 #if (3 == MAX_NUM_INSTANCE_MTK_COOLER_BCCT)
310 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[0]);
311 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[1]);
312 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[2]);
313
314 if (1 <= sscanf(tmp, "%d %d %d %d", &klog_on, &limit0, &limit1, &limit2)) {
315 if (klog_on == 0 || klog_on == 1) {
316 cl_bcct_klog_on = klog_on;
317 }
318
319 if (limit0 >= -1)
320 MTK_CL_BCCT_SET_LIMIT(limit0, cl_bcct_state[0]);
321 if (limit1 >= -1)
322 MTK_CL_BCCT_SET_LIMIT(limit1, cl_bcct_state[1]);
323 if (limit2 >= -1)
324 MTK_CL_BCCT_SET_LIMIT(limit2, cl_bcct_state[2]);
325
326 return len;
327 } else
328 #else
329 #error "Change correspondent part when changing MAX_NUM_INSTANCE_MTK_COOLER_BCCT!"
330 #endif
331 {
332 mtk_cooler_bcct_dprintk("%s bad arg\n", __func__);
333 }
334
335 return -EINVAL;
336 }
337
338 static int _cl_bcct_read(struct seq_file *m, void *v)
339 {
340 /**
341 * The format to print out:
342 * kernel_log <0 or 1>
343 * <mtk-cl-bcct<ID>> <bcc limit>
344 * ..
345 */
346
347 {
348 int i = 0;
349
350 seq_printf(m, "klog %d\n", cl_bcct_klog_on);
351 seq_printf(m, "curr_limit %d\n", cl_bcct_cur_limit);
352
353 for (; i < MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i++) {
354 int limit;
355 unsigned int curr_state;
356
357 MTK_CL_BCCT_GET_LIMIT(limit, cl_bcct_state[i]);
358 MTK_CL_BCCT_GET_CURR_STATE(curr_state, cl_bcct_state[i]);
359
360 seq_printf(m, "mtk-cl-bcct%02d %d mA, state %d\n", i, limit, curr_state);
361 }
362 }
363
364 return 0;
365 }
366
367 static int _cl_bcct_open(struct inode *inode, struct file *file)
368 {
369 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
370 return single_open(file, _cl_bcct_read, PDE_DATA(inode));
371 #else
372 return single_open(file, _cl_bcct_read, PDE(inode)->data);
373 #endif
374 }
375
376 static const struct file_operations _cl_bcct_fops = {
377 .owner = THIS_MODULE,
378 .open = _cl_bcct_open,
379 .read = seq_read,
380 .llseek = seq_lseek,
381 .write = _cl_bcct_write,
382 .release = single_release,
383 };
384
385
386 static int __init mtk_cooler_bcct_init(void)
387 {
388 int err = 0;
389 int i;
390
391 for (i = MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i-- > 0;) {
392 cl_bcct_dev[i] = NULL;
393 cl_bcct_state[i] = 0;
394 }
395
396 mtk_cooler_bcct_dprintk("%s\n", __func__);
397
398 err = mtk_cooler_bcct_register_ltf();
399 if (err)
400 goto err_unreg;
401
402 /* create a proc file */
403 {
404 struct proc_dir_entry *entry = NULL;
405 struct proc_dir_entry *dir_entry = NULL;
406
407 dir_entry = mtk_thermal_get_proc_drv_therm_dir_entry();
408 if (!dir_entry) {
409 mtk_cooler_bcct_dprintk("[%s]: mkdir /proc/driver/thermal failed\n", __func__);
410 }
411
412 entry =
413 proc_create("clbcct", S_IRUGO | S_IWUSR | S_IWGRP, dir_entry,
414 &_cl_bcct_fops);
415 if (!entry) {
416 mtk_cooler_bcct_dprintk_always("%s clbcct creation failed\n",
417 __func__);
418 } else {
419 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
420 proc_set_user(entry, 0, 1000);
421 #else
422 entry->gid = 1000;
423 #endif
424 }
425 }
426 return 0;
427
428 err_unreg:
429 mtk_cooler_bcct_unregister_ltf();
430 return err;
431 }
432
433 static void __exit mtk_cooler_bcct_exit(void)
434 {
435 mtk_cooler_bcct_dprintk("exit\n");
436
437 /* remove the proc file */
438 remove_proc_entry("driver/mtk-cl-bcct", NULL);
439
440 mtk_cooler_bcct_unregister_ltf();
441 }
442 module_init(mtk_cooler_bcct_init);
443 module_exit(mtk_cooler_bcct_exit);