import PULS_20180308
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / misc / mediatek / thermal / mt8127 / mtk_cooler_bcct.c
CommitLineData
6fa3eb70
S
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 */
44extern 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 */
51extern int set_bat_charging_current_limit(int current_limit);
52#endif
53
54extern 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
84static int cl_bcct_klog_on;
85static struct thermal_cooling_device *cl_bcct_dev[MAX_NUM_INSTANCE_MTK_COOLER_BCCT] = { 0 };
86static unsigned long cl_bcct_state[MAX_NUM_INSTANCE_MTK_COOLER_BCCT] = { 0 };
87
88static int cl_bcct_cur_limit = 65535;
89
90static 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
125static 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
132static 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
141static 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 */
153static 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
159static 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
174static 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
189static 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
233static 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
287static 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
4b9e9796 293 len = (len < 127) ? len : 127;
6fa3eb70
S
294 /* write data to the buffer */
295 if (copy_from_user(tmp, buf, len)) {
296 return -EFAULT;
297 }
298
299 /**
300 * sscanf format <klog_on> <mtk-cl-bcct00 limit> <mtk-cl-bcct01 limit> ...
301 * <klog_on> can only be 0 or 1
302 * <mtk-cl-bcct00 limit> can only be positive integer or -1 to denote no limit
303 */
304
305 if (NULL == data) {
306 mtk_cooler_bcct_dprintk("%s null data\n", __func__);
307 return -EINVAL;
308 }
309 /* WARNING: Modify here if MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS is changed to other than 3 */
310#if (3 == MAX_NUM_INSTANCE_MTK_COOLER_BCCT)
311 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[0]);
312 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[1]);
313 MTK_CL_BCCT_SET_LIMIT(-1, cl_bcct_state[2]);
314
315 if (1 <= sscanf(tmp, "%d %d %d %d", &klog_on, &limit0, &limit1, &limit2)) {
316 if (klog_on == 0 || klog_on == 1) {
317 cl_bcct_klog_on = klog_on;
318 }
319
320 if (limit0 >= -1)
321 MTK_CL_BCCT_SET_LIMIT(limit0, cl_bcct_state[0]);
322 if (limit1 >= -1)
323 MTK_CL_BCCT_SET_LIMIT(limit1, cl_bcct_state[1]);
324 if (limit2 >= -1)
325 MTK_CL_BCCT_SET_LIMIT(limit2, cl_bcct_state[2]);
326
327 return len;
328 } else
329#else
330#error "Change correspondent part when changing MAX_NUM_INSTANCE_MTK_COOLER_BCCT!"
331#endif
332 {
333 mtk_cooler_bcct_dprintk("%s bad arg\n", __func__);
334 }
335
336 return -EINVAL;
337}
338
339static int _cl_bcct_read(struct seq_file *m, void *v)
340{
341 /**
342 * The format to print out:
343 * kernel_log <0 or 1>
344 * <mtk-cl-bcct<ID>> <bcc limit>
345 * ..
346 */
347
348 {
349 int i = 0;
350
351 seq_printf(m, "klog %d\n", cl_bcct_klog_on);
352 seq_printf(m, "curr_limit %d\n", cl_bcct_cur_limit);
353
354 for (; i < MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i++) {
355 int limit;
356 unsigned int curr_state;
357
358 MTK_CL_BCCT_GET_LIMIT(limit, cl_bcct_state[i]);
359 MTK_CL_BCCT_GET_CURR_STATE(curr_state, cl_bcct_state[i]);
360
361 seq_printf(m, "mtk-cl-bcct%02d %d mA, state %d\n", i, limit, curr_state);
362 }
363 }
364
365 return 0;
366}
367
368static int _cl_bcct_open(struct inode *inode, struct file *file)
369{
370#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
371 return single_open(file, _cl_bcct_read, PDE_DATA(inode));
372#else
373 return single_open(file, _cl_bcct_read, PDE(inode)->data);
374#endif
375}
376
377static const struct file_operations _cl_bcct_fops = {
378 .owner = THIS_MODULE,
379 .open = _cl_bcct_open,
380 .read = seq_read,
381 .llseek = seq_lseek,
382 .write = _cl_bcct_write,
383 .release = single_release,
384};
385
386
387static int __init mtk_cooler_bcct_init(void)
388{
389 int err = 0;
390 int i;
391
392 for (i = MAX_NUM_INSTANCE_MTK_COOLER_BCCT; i-- > 0;) {
393 cl_bcct_dev[i] = NULL;
394 cl_bcct_state[i] = 0;
395 }
396
397 mtk_cooler_bcct_dprintk("%s\n", __func__);
398
399 err = mtk_cooler_bcct_register_ltf();
400 if (err)
401 goto err_unreg;
402
403 /* create a proc file */
404 {
405 struct proc_dir_entry *entry = NULL;
406 struct proc_dir_entry *dir_entry = NULL;
407
408 dir_entry = mtk_thermal_get_proc_drv_therm_dir_entry();
409 if (!dir_entry) {
410 mtk_cooler_bcct_dprintk("[%s]: mkdir /proc/driver/thermal failed\n", __func__);
411 }
412
413 entry =
414 proc_create("clbcct", S_IRUGO | S_IWUSR | S_IWGRP, dir_entry,
415 &_cl_bcct_fops);
416 if (!entry) {
417 mtk_cooler_bcct_dprintk_always("%s clbcct creation failed\n",
418 __func__);
419 } else {
420#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
421 proc_set_user(entry, 0, 1000);
422#else
423 entry->gid = 1000;
424#endif
425 }
426 }
427 return 0;
428
429 err_unreg:
430 mtk_cooler_bcct_unregister_ltf();
431 return err;
432}
433
434static void __exit mtk_cooler_bcct_exit(void)
435{
436 mtk_cooler_bcct_dprintk("exit\n");
437
438 /* remove the proc file */
439 remove_proc_entry("driver/mtk-cl-bcct", NULL);
440
441 mtk_cooler_bcct_unregister_ltf();
442}
443module_init(mtk_cooler_bcct_init);
444module_exit(mtk_cooler_bcct_exit);