Commit | Line | Data |
---|---|---|
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 | */ | |
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 | ||
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 | ||
339 | static 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 | ||
368 | static 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 | ||
377 | static 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 | ||
387 | static 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 | ||
434 | static 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 | } | |
443 | module_init(mtk_cooler_bcct_init); | |
444 | module_exit(mtk_cooler_bcct_exit); |