blob: 6b6b1102fda9c4c26df80a617a547e11943c79d8 [file] [log] [blame]
Jennifer Liuda8a7be2013-04-19 13:39:56 -07001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Binqiang Qiud8c96a92012-08-15 17:44:42 -07002 *
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
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060034#define BATTERY_VOLTAGE_MIN 3400
Binqiang Qiud8c96a92012-08-15 17:44:42 -070035
36static const char bcl_type[] = "bcl";
37
38/*
39 * Battery Current Limit Enable or Not
40 */
41enum bcl_device_mode {
42 BCL_DEVICE_DISABLED = 0,
43 BCL_DEVICE_ENABLED,
44};
45
46/*
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060047 * Battery Current Limit Iavail Threshold Mode set
Binqiang Qiud8c96a92012-08-15 17:44:42 -070048 */
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060049enum bcl_iavail_threshold_mode {
50 BCL_IAVAIL_THRESHOLD_DISABLED = 0,
51 BCL_IAVAIL_THRESHOLD_ENABLED,
Binqiang Qiud8c96a92012-08-15 17:44:42 -070052};
53
54/*
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060055 * Battery Current Limit Iavail Threshold Mode
Binqiang Qiud8c96a92012-08-15 17:44:42 -070056 */
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060057enum bcl_iavail_threshold_type {
58 BCL_IAVAIL_LOW_THRESHOLD_TYPE = 0,
59 BCL_IAVAIL_HIGH_THRESHOLD_TYPE,
60 BCL_IAVAIL_THRESHOLD_TYPE_MAX,
Binqiang Qiud8c96a92012-08-15 17:44:42 -070061};
62
63/**
64 * BCL control block
65 *
66 */
67struct bcl_context {
68 /* BCL device */
69 struct device *dev;
70
71 /* BCL related config parameter */
72 /* BCL mode enable or not */
73 enum bcl_device_mode bcl_mode;
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060074 /* BCL Iavail Threshold Activate or Not */
75 enum bcl_iavail_threshold_mode
76 bcl_threshold_mode[BCL_IAVAIL_THRESHOLD_TYPE_MAX];
77 /* BCL Iavail Threshold value in milli Amp */
78 int bcl_threshold_value_ma[BCL_IAVAIL_THRESHOLD_TYPE_MAX];
Binqiang Qiud8c96a92012-08-15 17:44:42 -070079 /* BCL Type */
80 char bcl_type[BCL_NAME_LENGTH];
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060081 /* BCL poll in msec */
Binqiang Qiud8c96a92012-08-15 17:44:42 -070082 int bcl_poll_interval_msec;
83
84 /* BCL realtime value based on poll */
Binqiang Qiud8c96a92012-08-15 17:44:42 -070085 /* BCL realtime vbat in mV*/
86 int bcl_vbat_mv;
87 /* BCL realtime rbat in mOhms*/
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060088 int bcl_rbat_mohm;
89 /*BCL realtime iavail in milli Amp*/
90 int bcl_iavail;
91 /*BCL vbatt min in mV*/
92 int bcl_vbat_min;
Binqiang Qiud8c96a92012-08-15 17:44:42 -070093 /* BCL period poll delay work structure */
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -060094 struct delayed_work bcl_iavail_work;
Binqiang Qiud8c96a92012-08-15 17:44:42 -070095
96};
97
98static struct bcl_context *gbcl;
99
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600100static int bcl_get_battery_voltage(int *vbatt_mv)
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700101{
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700102 static struct power_supply *psy;
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600103 union power_supply_propval ret = {0,};
104
105 if (psy == NULL) {
106 psy = power_supply_get_by_name("battery");
107 if (psy == NULL) {
108 pr_err("failed to get ps battery\n");
109 return -EINVAL;
110 }
111 }
112
113 if (psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret))
114 return -EINVAL;
115
116 if (ret.intval <= 0)
117 return -EINVAL;
118
119 *vbatt_mv = ret.intval / 1000;
120 return 0;
121}
122
123
124static int bcl_get_resistance(int *rbatt_mohm)
125{
126 static struct power_supply *psy;
127 union power_supply_propval ret = {0,};
128
129 if (psy == NULL) {
130 psy = power_supply_get_by_name("bms");
131 if (psy == NULL) {
132 pr_err("failed to get ps bms\n");
133 return -EINVAL;
134 }
135 }
136 if (psy->get_property(psy, POWER_SUPPLY_PROP_RESISTANCE, &ret))
137 return -EINVAL;
138
139 if (ret.intval <= 0)
140 return -EINVAL;
141
142 *rbatt_mohm = ret.intval / 1000;
143
144 return 0;
145}
146
147/*
148 * BCL iavail calculation and trigger notification to user space
149 * if iavail cross threshold
150 */
151static void bcl_calculate_iavail_trigger(void)
152{
153 int iavail_ma = 0;
154 int vbatt_mv;
155 int rbatt_mohm;
156 bool threshold_cross = false;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700157
158 if (!gbcl) {
159 pr_err("called before initialization\n");
160 return;
161 }
162
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600163 if (bcl_get_battery_voltage(&vbatt_mv))
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700164 return;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700165
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600166 if (bcl_get_resistance(&rbatt_mohm))
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700167 return;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700168
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600169 iavail_ma = (vbatt_mv - gbcl->bcl_vbat_min) * 1000 / rbatt_mohm;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700170
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600171 gbcl->bcl_rbat_mohm = rbatt_mohm;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700172 gbcl->bcl_vbat_mv = vbatt_mv;
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600173 gbcl->bcl_iavail = iavail_ma;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700174
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600175 pr_debug("iavail %d, vbatt %d rbatt %d\n", iavail_ma, vbatt_mv,
176 rbatt_mohm);
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700177
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600178 if ((gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] ==
179 BCL_IAVAIL_THRESHOLD_ENABLED)
180 && (iavail_ma >=
181 gbcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]))
182 threshold_cross = true;
183 else if ((gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
184 == BCL_IAVAIL_THRESHOLD_ENABLED)
185 && (iavail_ma <=
186 gbcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE]))
187 threshold_cross = true;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700188
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600189 if (threshold_cross)
190 sysfs_notify(&gbcl->dev->kobj, NULL, "type");
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700191}
192
193/*
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600194 * BCL iavail work
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700195 */
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600196static void bcl_iavail_work(struct work_struct *work)
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700197{
198 struct bcl_context *bcl = container_of(work,
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600199 struct bcl_context, bcl_iavail_work.work);
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700200
201 if (gbcl->bcl_mode == BCL_DEVICE_ENABLED) {
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600202 bcl_calculate_iavail_trigger();
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700203 /* restart the delay work for caculating imax */
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600204 schedule_delayed_work(&bcl->bcl_iavail_work,
Jennifer Liuda8a7be2013-04-19 13:39:56 -0700205 msecs_to_jiffies(bcl->bcl_poll_interval_msec));
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700206 }
207}
208
209/*
210 * Set BCL mode
211 */
212static void bcl_mode_set(enum bcl_device_mode mode)
213{
214 if (!gbcl)
215 return;
216
217 if (gbcl->bcl_mode == mode)
218 return;
219
220 if (gbcl->bcl_mode == BCL_DEVICE_DISABLED
221 && mode == BCL_DEVICE_ENABLED) {
222 gbcl->bcl_mode = mode;
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600223 bcl_iavail_work(&(gbcl->bcl_iavail_work.work));
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700224 return;
225 } else if (gbcl->bcl_mode == BCL_DEVICE_ENABLED
226 && mode == BCL_DEVICE_DISABLED) {
227 gbcl->bcl_mode = mode;
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600228 cancel_delayed_work_sync(&(gbcl->bcl_iavail_work));
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700229 return;
230 }
231
232 return;
233}
234
235#define show_bcl(name, variable, format) \
236static ssize_t \
237name##_show(struct device *dev, struct device_attribute *attr, char *buf) \
238{ \
239 if (gbcl) \
240 return snprintf(buf, PAGE_SIZE, format, gbcl->variable); \
241 else \
242 return -EPERM; \
243}
244
245show_bcl(type, bcl_type, "%s\n")
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700246show_bcl(vbat, bcl_vbat_mv, "%d\n")
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600247show_bcl(rbat, bcl_rbat_mohm, "%d\n")
248show_bcl(iavail, bcl_iavail, "%d\n")
249show_bcl(vbat_min, bcl_vbat_min, "%d\n");
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700250show_bcl(poll_interval, bcl_poll_interval_msec, "%d\n")
251
252static ssize_t
253mode_show(struct device *dev, struct device_attribute *attr, char *buf)
254{
255 if (!gbcl)
256 return -EPERM;
257
258 return snprintf(buf, PAGE_SIZE, "%s\n",
259 gbcl->bcl_mode == BCL_DEVICE_ENABLED ? "enabled"
260 : "disabled");
261}
262
263static ssize_t
264mode_store(struct device *dev, struct device_attribute *attr,
265 const char *buf, size_t count)
266{
267 if (!gbcl)
268 return -EPERM;
269
270 if (!strncmp(buf, "enabled", 7))
271 bcl_mode_set(BCL_DEVICE_ENABLED);
272 else if (!strncmp(buf, "disabled", 8))
273 bcl_mode_set(BCL_DEVICE_DISABLED);
274 else
275 return -EINVAL;
276
277 return count;
278}
279
280static ssize_t
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700281poll_interval_store(struct device *dev,
282 struct device_attribute *attr,
283 const char *buf, size_t count)
284{
285 int value;
286
287 if (!gbcl)
288 return -EPERM;
289
290 if (!sscanf(buf, "%d", &value))
291 return -EINVAL;
292
293 if (value < MIN_BCL_POLL_INTERVAL)
294 return -EINVAL;
295
296 gbcl->bcl_poll_interval_msec = value;
297
298 return count;
299}
300
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600301static ssize_t vbat_min_store(struct device *dev,
302 struct device_attribute *attr,
303 const char *buf, size_t count)
304{
305 int value;
306 int ret;
307
308 if (!gbcl)
309 return -EPERM;
310
311 ret = kstrtoint(buf, 10, &value);
312
313 if (ret || (value < 0)) {
314 pr_err("Incorrect vbatt min value\n");
315 return -EINVAL;
316 }
317
318 gbcl->bcl_vbat_min = value;
319 return count;
320}
321
322static ssize_t iavail_low_threshold_mode_show(struct device *dev,
323 struct device_attribute *attr, char *buf)
324{
325 if (!gbcl)
326 return -EPERM;
327
328 return snprintf(buf, PAGE_SIZE, "%s\n",
329 gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
330 == BCL_IAVAIL_THRESHOLD_ENABLED ? "enabled" : "disabled");
331}
332
333static ssize_t iavail_low_threshold_mode_store(struct device *dev,
334 struct device_attribute *attr,
335 const char *buf, size_t count)
336{
337 if (!gbcl)
338 return -EPERM;
339
340 if (!strncmp(buf, "enabled", 7))
341 gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
342 = BCL_IAVAIL_THRESHOLD_ENABLED;
343 else if (!strncmp(buf, "disabled", 7))
344 gbcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE]
345 = BCL_IAVAIL_THRESHOLD_DISABLED;
346 else
347 return -EINVAL;
348
349 return count;
350}
351static ssize_t iavail_high_threshold_mode_show(struct device *dev,
352 struct device_attribute *attr, char *buf)
353{
354 if (!gbcl)
355 return -EPERM;
356
357 return snprintf(buf, PAGE_SIZE, "%s\n",
358 gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]
359 == BCL_IAVAIL_THRESHOLD_ENABLED ? "enabled" : "disabled");
360}
361
362static ssize_t iavail_high_threshold_mode_store(struct device *dev,
363 struct device_attribute *attr,
364 const char *buf, size_t count)
365{
366 if (!gbcl)
367 return -EPERM;
368
369 if (!strncmp(buf, "enabled", 7))
370 gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]
371 = BCL_IAVAIL_THRESHOLD_ENABLED;
372 else if (!strncmp(buf, "disabled", 7))
373 gbcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]
374 = BCL_IAVAIL_THRESHOLD_DISABLED;
375 else
376 return -EINVAL;
377
378 return count;
379}
380
381static ssize_t iavail_low_threshold_value_show(struct device *dev,
382 struct device_attribute *attr, char *buf)
383{
384 if (!gbcl)
385 return -EPERM;
386
387 return snprintf(buf, PAGE_SIZE, "%d\n",
388 gbcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE]);
389}
390
391
392static ssize_t iavail_low_threshold_value_store(struct device *dev,
393 struct device_attribute *attr,
394 const char *buf, size_t count)
395{
396 int val;
397 int ret;
398
399 ret = kstrtoint(buf, 10, &val);
400
401 if (ret || (val < 0)) {
402 pr_err("Incorrect available current threshold value\n");
403 return -EINVAL;
404 }
405
406 gbcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE] = val;
407
408 return count;
409}
410static ssize_t iavail_high_threshold_value_show(struct device *dev,
411 struct device_attribute *attr, char *buf)
412{
413 if (!gbcl)
414 return -EPERM;
415
416 return snprintf(buf, PAGE_SIZE, "%d\n",
417 gbcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE]);
418}
419
420static ssize_t iavail_high_threshold_value_store(struct device *dev,
421 struct device_attribute *attr,
422 const char *buf, size_t count)
423{
424 int val;
425 int ret;
426
427 ret = kstrtoint(buf, 10, &val);
428
429 if (ret || (val < 0)) {
430 pr_err("Incorrect available current threshold value\n");
431 return -EINVAL;
432 }
433
434 gbcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] = val;
435
436 return count;
437}
438
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700439/*
440 * BCL device attributes
441 */
442static struct device_attribute bcl_dev_attr[] = {
443 __ATTR(type, 0444, type_show, NULL),
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600444 __ATTR(iavail, 0444, iavail_show, NULL),
445 __ATTR(vbat_min, 0644, vbat_min_show, vbat_min_store),
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700446 __ATTR(vbat, 0444, vbat_show, NULL),
447 __ATTR(rbat, 0444, rbat_show, NULL),
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700448 __ATTR(mode, 0644, mode_show, mode_store),
449 __ATTR(poll_interval, 0644,
450 poll_interval_show, poll_interval_store),
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600451 __ATTR(iavail_low_threshold_mode, 0644,
452 iavail_low_threshold_mode_show,
453 iavail_low_threshold_mode_store),
454 __ATTR(iavail_high_threshold_mode, 0644,
455 iavail_high_threshold_mode_show,
456 iavail_high_threshold_mode_store),
457 __ATTR(iavail_low_threshold_value, 0644,
458 iavail_low_threshold_value_show,
459 iavail_low_threshold_value_store),
460 __ATTR(iavail_high_threshold_value, 0644,
461 iavail_high_threshold_value_show,
462 iavail_high_threshold_value_store),
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700463};
464
465static int create_bcl_sysfs(struct bcl_context *bcl)
466{
467 int result = 0;
468 int num_attr = sizeof(bcl_dev_attr)/sizeof(struct device_attribute);
469 int i;
470
471 for (i = 0; i < num_attr; i++) {
472 result = device_create_file(bcl->dev, &bcl_dev_attr[i]);
473 if (result < 0)
474 return result;
475 }
476
477 return 0;
478}
479
480static void remove_bcl_sysfs(struct bcl_context *bcl)
481{
482 int num_attr = sizeof(bcl_dev_attr)/sizeof(struct device_attribute);
483 int i;
484
485 for (i = 0; i < num_attr; i++)
486 device_remove_file(bcl->dev, &bcl_dev_attr[i]);
487
488 return;
489}
490
491static int __devinit bcl_probe(struct platform_device *pdev)
492{
493 struct bcl_context *bcl;
494 int ret = 0;
495
496 bcl = kzalloc(sizeof(struct bcl_context), GFP_KERNEL);
497
498 if (!bcl) {
499 pr_err("Cannot allocate bcl_context\n");
500 return -ENOMEM;
501 }
502
503 gbcl = bcl;
504
505 /* For BCL */
506 /* Init default BCL params */
507 bcl->dev = &pdev->dev;
508 bcl->bcl_mode = BCL_DEVICE_DISABLED;
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600509 bcl->bcl_threshold_mode[BCL_IAVAIL_LOW_THRESHOLD_TYPE] =
510 BCL_IAVAIL_THRESHOLD_DISABLED;
511 bcl->bcl_threshold_mode[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] =
512 BCL_IAVAIL_THRESHOLD_DISABLED;
513 bcl->bcl_threshold_value_ma[BCL_IAVAIL_LOW_THRESHOLD_TYPE] = 0;
514 bcl->bcl_threshold_value_ma[BCL_IAVAIL_HIGH_THRESHOLD_TYPE] = 0;
515 bcl->bcl_vbat_min = BATTERY_VOLTAGE_MIN;
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700516 snprintf(bcl->bcl_type, BCL_NAME_LENGTH, "%s", bcl_type);
517 bcl->bcl_poll_interval_msec = BCL_POLL_INTERVAL;
518 ret = create_bcl_sysfs(bcl);
519 if (ret < 0) {
520 pr_err("Cannot create bcl sysfs\n");
521 kfree(bcl);
522 return ret;
523 }
524 platform_set_drvdata(pdev, bcl);
Archana Sathyakumara3b4eb42013-05-07 15:48:05 -0600525 INIT_DELAYED_WORK_DEFERRABLE(&bcl->bcl_iavail_work, bcl_iavail_work);
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700526
527 return 0;
528}
529
530static int __devexit bcl_remove(struct platform_device *pdev)
531{
532 remove_bcl_sysfs(gbcl);
533 kfree(gbcl);
534 gbcl = NULL;
535 platform_set_drvdata(pdev, NULL);
536 return 0;
537}
538
Praveen Chidambaram2ee1b282013-02-06 17:04:09 -0700539static struct of_device_id bcl_match_table[] = {
540 {.compatible = "qcom,bcl"},
541 {},
542};
543
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700544static struct platform_driver bcl_driver = {
545 .probe = bcl_probe,
546 .remove = __devexit_p(bcl_remove),
547 .driver = {
548 .name = BCL_DEV_NAME,
549 .owner = THIS_MODULE,
Praveen Chidambaram2ee1b282013-02-06 17:04:09 -0700550 .of_match_table = bcl_match_table,
Binqiang Qiud8c96a92012-08-15 17:44:42 -0700551 },
552};
553
554static int __init bcl_init(void)
555{
556 return platform_driver_register(&bcl_driver);
557}
558
559static void __exit bcl_exit(void)
560{
561 platform_driver_unregister(&bcl_driver);
562}
563
564late_initcall(bcl_init);
565module_exit(bcl_exit);
566
567MODULE_LICENSE("GPL v2");
568MODULE_DESCRIPTION("battery current limit driver");
569MODULE_ALIAS("platform:" BCL_DEV_NAME);