blob: d1750ec3fd6bfc4424ad175a28e98058741cb6eb [file] [log] [blame]
Binqiang Qiub4b4e912012-08-15 17:44:42 -07001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/platform_device.h>
18#include <linux/errno.h>
19#include <linux/device.h>
20#include <linux/power_supply.h>
21#include <linux/delay.h>
22#include <linux/slab.h>
23
24#define BCL_DEV_NAME "battery_current_limit"
25#define BCL_NAME_LENGTH 20
26/*
27 * Default BCL poll interval 1000 msec
28 */
29#define BCL_POLL_INTERVAL 1000
30/*
31 * Mininum BCL poll interval 10 msec
32 */
33#define MIN_BCL_POLL_INTERVAL 10
34
35static const char bcl_type[] = "bcl";
36
37/*
38 * Battery Current Limit Enable or Not
39 */
40enum bcl_device_mode {
41 BCL_DEVICE_DISABLED = 0,
42 BCL_DEVICE_ENABLED,
43};
44
45/*
46 * Battery Current Limit IBat Imax Threshold Mode
47 */
48enum bcl_ibat_imax_threshold_mode {
49 BCL_IBAT_IMAX_THRESHOLD_DISABLED = 0,
50 BCL_IBAT_IMAX_THRESHOLD_ENABLED,
51};
52
53/*
54 * Battery Current Limit Ibat Imax Trip Type (High and Low Threshold)
55 */
56enum bcl_ibat_imax_threshold_type {
57 BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW = 0,
58 BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH,
59 BCL_IBAT_IMAX_THRESHOLD_TYPE_MAX,
60};
61
62/**
63 * BCL control block
64 *
65 */
66struct bcl_context {
67 /* BCL device */
68 struct device *dev;
69
70 /* BCL related config parameter */
71 /* BCL mode enable or not */
72 enum bcl_device_mode bcl_mode;
73 /* BCL Ibat/IMax Threshold Activate or Not */
74 enum bcl_ibat_imax_threshold_mode
75 bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_MAX];
76 /* BCL Ibat/IMax Threshold value in milli Amp */
77 int bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_MAX];
78 /* BCL Type */
79 char bcl_type[BCL_NAME_LENGTH];
80 /* BCL poll in usec */
81 int bcl_poll_interval_msec;
82
83 /* BCL realtime value based on poll */
84 /* BCL realtime ibat in milli Amp*/
85 int bcl_ibat_ma;
86 /* BCL realtime calculated imax in milli Amp*/
87 int bcl_imax_ma;
88 /* BCL realtime calculated ocv in uV*/
89 int bcl_ocv_uv;
90 /* BCL realtime vbat in mV*/
91 int bcl_vbat_mv;
92 /* BCL realtime rbat in mOhms*/
93 int bcl_rbat;
94 /* BCL period poll delay work structure */
95 struct delayed_work bcl_imax_work;
96
97};
98
99static struct bcl_context *gbcl;
100
101/*
102 * BCL imax calculation and trigger notification to user space
103 * if imax cross threshold
104 */
105static void bcl_calculate_imax_trigger(void)
106{
107 int ibatt_ua, vbatt_uv;
108 int imax_ma;
109 int ibatt_ma, vbatt_mv;
110 int imax_low_threshold;
111 int imax_high_threshold;
112 bool threshold_cross = false;
113 union power_supply_propval ret = {0,};
114 static struct power_supply *psy;
115
116 if (!gbcl) {
117 pr_err("called before initialization\n");
118 return;
119 }
120
121 if (psy == NULL) {
122 psy = power_supply_get_by_name("battery");
123 if (psy == NULL)
124 return;
125 }
126
127 if (psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &ret))
128 return;
129 ibatt_ua = ret.intval;
130
131 if (psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret))
132 return;
133 vbatt_uv = ret.intval;
134
135 if (psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &ret))
136 return;
137 imax_ma = ret.intval/1000;
138
139 ibatt_ma = ibatt_ua/1000;
140 vbatt_mv = vbatt_uv/1000;
141
142 gbcl->bcl_ibat_ma = ibatt_ma;
143 gbcl->bcl_imax_ma = imax_ma;
144 gbcl->bcl_vbat_mv = vbatt_mv;
145
146 if (gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
147 == BCL_IBAT_IMAX_THRESHOLD_ENABLED) {
148 imax_high_threshold =
149 imax_ma - gbcl->bcl_threshold_value_ma
150 [BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH];
151 if (ibatt_ma >= imax_high_threshold)
152 threshold_cross = true;
153 }
154
155 if (gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
156 == BCL_IBAT_IMAX_THRESHOLD_ENABLED) {
157 imax_low_threshold =
158 imax_ma - gbcl->bcl_threshold_value_ma
159 [BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW];
160 if (ibatt_ma <= imax_low_threshold)
161 threshold_cross = true;
162 }
163
164 if (threshold_cross) {
165 sysfs_notify(&gbcl->dev->kobj,
166 NULL, "type");
167 }
168}
169
170/*
171 * BCL imax work
172 */
173static void bcl_imax_work(struct work_struct *work)
174{
175 struct bcl_context *bcl = container_of(work,
176 struct bcl_context, bcl_imax_work.work);
177
178 if (gbcl->bcl_mode == BCL_DEVICE_ENABLED) {
179 bcl_calculate_imax_trigger();
180 /* restart the delay work for caculating imax */
181 schedule_delayed_work(&bcl->bcl_imax_work,
182 round_jiffies_relative(msecs_to_jiffies
183 (bcl->bcl_poll_interval_msec)));
184 }
185}
186
187/*
188 * Set BCL mode
189 */
190static void bcl_mode_set(enum bcl_device_mode mode)
191{
192 if (!gbcl)
193 return;
194
195 if (gbcl->bcl_mode == mode)
196 return;
197
198 if (gbcl->bcl_mode == BCL_DEVICE_DISABLED
199 && mode == BCL_DEVICE_ENABLED) {
200 gbcl->bcl_mode = mode;
201 bcl_imax_work(&(gbcl->bcl_imax_work.work));
202 return;
203 } else if (gbcl->bcl_mode == BCL_DEVICE_ENABLED
204 && mode == BCL_DEVICE_DISABLED) {
205 gbcl->bcl_mode = mode;
206 cancel_delayed_work_sync(&(gbcl->bcl_imax_work));
207 return;
208 }
209
210 return;
211}
212
213#define show_bcl(name, variable, format) \
214static ssize_t \
215name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
216{ \
217 if (gbcl) \
218 return snprintf(buf, PAGE_SIZE, format, gbcl->variable); \
219 else \
220 return -EPERM; \
221}
222
223show_bcl(type, bcl_type, "%s\n")
224show_bcl(ibat, bcl_ibat_ma, "%d\n")
225show_bcl(imax, bcl_imax_ma, "%d\n")
226show_bcl(vbat, bcl_vbat_mv, "%d\n")
227show_bcl(rbat, bcl_rbat, "%d\n")
228show_bcl(ocv, bcl_ocv_uv, "%d\n")
229show_bcl(poll_interval, bcl_poll_interval_msec, "%d\n")
230
231static ssize_t
232mode_show(struct device *dev, struct device_attribute *attr, char *buf)
233{
234 if (!gbcl)
235 return -EPERM;
236
237 return snprintf(buf, PAGE_SIZE, "%s\n",
238 gbcl->bcl_mode == BCL_DEVICE_ENABLED ? "enabled"
239 : "disabled");
240}
241
242static ssize_t
243mode_store(struct device *dev, struct device_attribute *attr,
244 const char *buf, size_t count)
245{
246 if (!gbcl)
247 return -EPERM;
248
249 if (!strncmp(buf, "enabled", 7))
250 bcl_mode_set(BCL_DEVICE_ENABLED);
251 else if (!strncmp(buf, "disabled", 8))
252 bcl_mode_set(BCL_DEVICE_DISABLED);
253 else
254 return -EINVAL;
255
256 return count;
257}
258
259static ssize_t
260ibat_imax_low_threshold_mode_show(struct device *dev,
261 struct device_attribute *attr, char *buf)
262{
263 if (!gbcl)
264 return -EPERM;
265
266 return snprintf(buf, PAGE_SIZE, "%s\n",
267 gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
268 == BCL_IBAT_IMAX_THRESHOLD_ENABLED ? "enabled" : "disabled");
269}
270
271static ssize_t
272ibat_imax_low_threshold_mode_store(struct device *dev,
273 struct device_attribute *attr,
274 const char *buf, size_t count)
275{
276 if (!gbcl)
277 return -EPERM;
278
279 if (!strncmp(buf, "enabled", 7))
280 gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
281 = BCL_IBAT_IMAX_THRESHOLD_ENABLED;
282 else if (!strncmp(buf, "disabled", 8))
283 gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
284 = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
285 else
286 return -EINVAL;
287
288 return count;
289}
290
291static ssize_t
292ibat_imax_low_threshold_value_show(struct device *dev,
293 struct device_attribute *attr, char *buf)
294{
295 if (!gbcl)
296 return -EPERM;
297
298 return snprintf(buf, PAGE_SIZE, "%d\n",
299 gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]);
300}
301
302static ssize_t
303ibat_imax_low_threshold_value_store(struct device *dev,
304 struct device_attribute *attr,
305 const char *buf, size_t count)
306{
307 int value;
308
309 if (!gbcl)
310 return -EPERM;
311
312 if (!sscanf(buf, "%d", &value))
313 return -EINVAL;
314
315 if (value < 0)
316 return -EINVAL;
317
318 gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
319 = value;
320
321 return count;
322}
323
324static ssize_t
325ibat_imax_high_threshold_mode_show(struct device *dev,
326 struct device_attribute *attr, char *buf)
327{
328 if (!gbcl)
329 return -EPERM;
330
331 return snprintf(buf, PAGE_SIZE, "%s\n",
332 gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
333 == BCL_IBAT_IMAX_THRESHOLD_ENABLED ? "enabled" : "disabled");
334}
335
336static ssize_t
337ibat_imax_high_threshold_mode_store(struct device *dev,
338 struct device_attribute *attr,
339 const char *buf, size_t count)
340{
341 if (!gbcl)
342 return -EPERM;
343
344 if (!strncmp(buf, "enabled", 7))
345 gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
346 = BCL_IBAT_IMAX_THRESHOLD_ENABLED;
347 else if (!strncmp(buf, "disabled", 8))
348 gbcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
349 = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
350 else
351 return -EINVAL;
352
353 return count;
354}
355
356static ssize_t
357ibat_imax_high_threshold_value_show(struct device *dev,
358 struct device_attribute *attr, char *buf)
359{
360 if (!gbcl)
361 return -EPERM;
362
363 return snprintf(buf, PAGE_SIZE, "%d\n",
364 gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]);
365}
366
367static ssize_t
368ibat_imax_high_threshold_value_store(struct device *dev,
369 struct device_attribute *attr,
370 const char *buf, size_t count)
371{
372 int value;
373
374 if (!gbcl)
375 return -EPERM;
376
377 if (!sscanf(buf, "%d", &value))
378 return -EINVAL;
379
380 if (value < 0)
381 return -EINVAL;
382
383 gbcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
384 = value;
385
386 return count;
387}
388
389static ssize_t
390poll_interval_store(struct device *dev,
391 struct device_attribute *attr,
392 const char *buf, size_t count)
393{
394 int value;
395
396 if (!gbcl)
397 return -EPERM;
398
399 if (!sscanf(buf, "%d", &value))
400 return -EINVAL;
401
402 if (value < MIN_BCL_POLL_INTERVAL)
403 return -EINVAL;
404
405 gbcl->bcl_poll_interval_msec = value;
406
407 return count;
408}
409
410/*
411 * BCL device attributes
412 */
413static struct device_attribute bcl_dev_attr[] = {
414 __ATTR(type, 0444, type_show, NULL),
415 __ATTR(ibat, 0444, ibat_show, NULL),
416 __ATTR(vbat, 0444, vbat_show, NULL),
417 __ATTR(rbat, 0444, rbat_show, NULL),
418 __ATTR(ocv, 0444, ocv_show, NULL),
419 __ATTR(imax, 0444, imax_show, NULL),
420 __ATTR(mode, 0644, mode_show, mode_store),
421 __ATTR(poll_interval, 0644,
422 poll_interval_show, poll_interval_store),
423 __ATTR(ibat_imax_low_threshold_mode, 0644,
424 ibat_imax_low_threshold_mode_show,
425 ibat_imax_low_threshold_mode_store),
426 __ATTR(ibat_imax_high_threshold_mode, 0644,
427 ibat_imax_high_threshold_mode_show,
428 ibat_imax_high_threshold_mode_store),
429 __ATTR(ibat_imax_low_threshold_value, 0644,
430 ibat_imax_low_threshold_value_show,
431 ibat_imax_low_threshold_value_store),
432 __ATTR(ibat_imax_high_threshold_value, 0644,
433 ibat_imax_high_threshold_value_show,
434 ibat_imax_high_threshold_value_store)
435};
436
437static int create_bcl_sysfs(struct bcl_context *bcl)
438{
439 int result = 0;
440 int num_attr = sizeof(bcl_dev_attr)/sizeof(struct device_attribute);
441 int i;
442
443 for (i = 0; i < num_attr; i++) {
444 result = device_create_file(bcl->dev, &bcl_dev_attr[i]);
445 if (result < 0)
446 return result;
447 }
448
449 return 0;
450}
451
452static void remove_bcl_sysfs(struct bcl_context *bcl)
453{
454 int num_attr = sizeof(bcl_dev_attr)/sizeof(struct device_attribute);
455 int i;
456
457 for (i = 0; i < num_attr; i++)
458 device_remove_file(bcl->dev, &bcl_dev_attr[i]);
459
460 return;
461}
462
463static int __devinit bcl_probe(struct platform_device *pdev)
464{
465 struct bcl_context *bcl;
466 int ret = 0;
467
468 bcl = kzalloc(sizeof(struct bcl_context), GFP_KERNEL);
469
470 if (!bcl) {
471 pr_err("Cannot allocate bcl_context\n");
472 return -ENOMEM;
473 }
474
475 gbcl = bcl;
476
477 /* For BCL */
478 /* Init default BCL params */
479 bcl->dev = &pdev->dev;
480 bcl->bcl_mode = BCL_DEVICE_DISABLED;
481 bcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW]
482 = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
483 bcl->bcl_threshold_mode[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH]
484 = BCL_IBAT_IMAX_THRESHOLD_DISABLED;
485 bcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_LOW] = 0;
486 bcl->bcl_threshold_value_ma[BCL_IBAT_IMAX_THRESHOLD_TYPE_HIGH] = 0;
487 snprintf(bcl->bcl_type, BCL_NAME_LENGTH, "%s", bcl_type);
488 bcl->bcl_poll_interval_msec = BCL_POLL_INTERVAL;
489 ret = create_bcl_sysfs(bcl);
490 if (ret < 0) {
491 pr_err("Cannot create bcl sysfs\n");
492 kfree(bcl);
493 return ret;
494 }
495 platform_set_drvdata(pdev, bcl);
496 INIT_DELAYED_WORK(&bcl->bcl_imax_work, bcl_imax_work);
497
498 return 0;
499}
500
501static int __devexit bcl_remove(struct platform_device *pdev)
502{
503 remove_bcl_sysfs(gbcl);
504 kfree(gbcl);
505 gbcl = NULL;
506 platform_set_drvdata(pdev, NULL);
507 return 0;
508}
509
510static struct platform_driver bcl_driver = {
511 .probe = bcl_probe,
512 .remove = __devexit_p(bcl_remove),
513 .driver = {
514 .name = BCL_DEV_NAME,
515 .owner = THIS_MODULE,
516 },
517};
518
519static int __init bcl_init(void)
520{
521 return platform_driver_register(&bcl_driver);
522}
523
524static void __exit bcl_exit(void)
525{
526 platform_driver_unregister(&bcl_driver);
527}
528
529late_initcall(bcl_init);
530module_exit(bcl_exit);
531
532MODULE_LICENSE("GPL v2");
533MODULE_DESCRIPTION("battery current limit driver");
534MODULE_ALIAS("platform:" BCL_DEV_NAME);