aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power/88pm860x_charger.c
diff options
context:
space:
mode:
authorJett.Zhou <jtzhou@marvell.com>2012-07-27 04:27:16 -0400
committerAnton Vorontsov <anton.vorontsov@linaro.org>2012-09-20 18:32:55 -0400
commita830d28b48bf92944e57058e87d17cee5a7cd2a1 (patch)
tree304249dfc3d1100114a88e3c01509158a52eb86e /drivers/power/88pm860x_charger.c
parent98a2766493589c18c327ae3dad5243b53fcb5f70 (diff)
power_supply: Enable battery-charger for 88pm860x
There are charger and battery measurement feature for 88pm860x PMIC. For charger, it can support pre-charge with small current when battery is nearly exausted and then changed into fast-charge with CC&CV mode. For battery monitor, it can support battery measurement such as vbat,vsys,vchg and ibat etc,it can aslo accumulating the Coulomb value charged or discharged from battery based on Conlomb Counter, we use it to estimate battery capacity. Signed-off-by: Jett.Zhou <jtzhou@marvell.com> Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Diffstat (limited to 'drivers/power/88pm860x_charger.c')
-rw-r--r--drivers/power/88pm860x_charger.c746
1 files changed, 746 insertions, 0 deletions
diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c
new file mode 100644
index 000000000000..4fd7614ee839
--- /dev/null
+++ b/drivers/power/88pm860x_charger.c
@@ -0,0 +1,746 @@
1/*
2 * Battery driver for Marvell 88PM860x PMIC
3 *
4 * Copyright (c) 2012 Marvell International Ltd.
5 * Author: Jett Zhou <jtzhou@marvell.com>
6 * Haojian Zhuang <haojian.zhuang@marvell.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/slab.h>
17#include <linux/power_supply.h>
18#include <linux/mfd/88pm860x.h>
19#include <linux/delay.h>
20#include <linux/uaccess.h>
21#include <asm/div64.h>
22
23/* bit definitions of Status Query Interface 2 */
24#define STATUS2_CHG (1 << 2)
25
26/* bit definitions of Reset Out Register */
27#define RESET_SW_PD (1 << 7)
28
29/* bit definitions of PreReg 1 */
30#define PREREG1_90MA (0x0)
31#define PREREG1_180MA (0x1)
32#define PREREG1_450MA (0x4)
33#define PREREG1_540MA (0x5)
34#define PREREG1_1350MA (0xE)
35#define PREREG1_VSYS_4_5V (3 << 4)
36
37/* bit definitions of Charger Control 1 Register */
38#define CC1_MODE_OFF (0)
39#define CC1_MODE_PRECHARGE (1)
40#define CC1_MODE_FASTCHARGE (2)
41#define CC1_MODE_PULSECHARGE (3)
42#define CC1_ITERM_20MA (0 << 2)
43#define CC1_ITERM_60MA (2 << 2)
44#define CC1_VFCHG_4_2V (9 << 4)
45
46/* bit definitions of Charger Control 2 Register */
47#define CC2_ICHG_100MA (0x1)
48#define CC2_ICHG_500MA (0x9)
49#define CC2_ICHG_1000MA (0x13)
50
51/* bit definitions of Charger Control 3 Register */
52#define CC3_180MIN_TIMEOUT (0x6 << 4)
53#define CC3_270MIN_TIMEOUT (0x7 << 4)
54#define CC3_360MIN_TIMEOUT (0xA << 4)
55#define CC3_DISABLE_TIMEOUT (0xF << 4)
56
57/* bit definitions of Charger Control 4 Register */
58#define CC4_IPRE_40MA (7)
59#define CC4_VPCHG_3_2V (3 << 4)
60#define CC4_IFCHG_MON_EN (1 << 6)
61#define CC4_BTEMP_MON_EN (1 << 7)
62
63/* bit definitions of Charger Control 6 Register */
64#define CC6_BAT_OV_EN (1 << 2)
65#define CC6_BAT_UV_EN (1 << 3)
66#define CC6_UV_VBAT_SET (0x3 << 6) /* 2.8v */
67
68/* bit definitions of Charger Control 7 Register */
69#define CC7_BAT_REM_EN (1 << 3)
70#define CC7_IFSM_EN (1 << 7)
71
72/* bit definitions of Measurement Enable 1 Register */
73#define MEAS1_VBAT (1 << 0)
74
75/* bit definitions of Measurement Enable 3 Register */
76#define MEAS3_IBAT_EN (1 << 0)
77#define MEAS3_CC_EN (1 << 2)
78
79#define FSM_INIT 0
80#define FSM_DISCHARGE 1
81#define FSM_PRECHARGE 2
82#define FSM_FASTCHARGE 3
83
84#define PRECHARGE_THRESHOLD 3100
85#define POWEROFF_THRESHOLD 3400
86#define CHARGE_THRESHOLD 4000
87#define DISCHARGE_THRESHOLD 4180
88
89/* over-temperature on PM8606 setting */
90#define OVER_TEMP_FLAG (1 << 6)
91#define OVTEMP_AUTORECOVER (1 << 3)
92
93/* over-voltage protect on vchg setting mv */
94#define VCHG_NORMAL_LOW 4200
95#define VCHG_NORMAL_CHECK 5800
96#define VCHG_NORMAL_HIGH 6000
97#define VCHG_OVP_LOW 5500
98
99struct pm860x_charger_info {
100 struct pm860x_chip *chip;
101 struct i2c_client *i2c;
102 struct i2c_client *i2c_8606;
103 struct device *dev;
104
105 struct power_supply usb;
106 struct mutex lock;
107 int irq_nums;
108 int irq[7];
109 unsigned state:3; /* fsm state */
110 unsigned online:1; /* usb charger */
111 unsigned present:1; /* battery present */
112 unsigned allowed:1;
113};
114
115static char *pm860x_supplied_to[] = {
116 "battery-monitor",
117};
118
119static int measure_vchg(struct pm860x_charger_info *info, int *data)
120{
121 unsigned char buf[2];
122 int ret = 0;
123
124 ret = pm860x_bulk_read(info->i2c, PM8607_VCHG_MEAS1, 2, buf);
125 if (ret < 0)
126 return ret;
127
128 *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
129 /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */
130 *data = ((*data & 0xfff) * 9 * 125) >> 9;
131
132 dev_dbg(info->dev, "%s, vchg: %d mv\n", __func__, *data);
133
134 return ret;
135}
136
137static void set_vchg_threshold(struct pm860x_charger_info *info,
138 int min, int max)
139{
140 int data;
141
142 /* (tmp << 8) * / 5 / 1800 */
143 if (min <= 0)
144 data = 0;
145 else
146 data = (min << 5) / 1125;
147 pm860x_reg_write(info->i2c, PM8607_VCHG_LOWTH, data);
148 dev_dbg(info->dev, "VCHG_LOWTH:%dmv, 0x%x\n", min, data);
149
150 if (max <= 0)
151 data = 0xff;
152 else
153 data = (max << 5) / 1125;
154 pm860x_reg_write(info->i2c, PM8607_VCHG_HIGHTH, data);
155 dev_dbg(info->dev, "VCHG_HIGHTH:%dmv, 0x%x\n", max, data);
156
157}
158
159static void set_vbatt_threshold(struct pm860x_charger_info *info,
160 int min, int max)
161{
162 int data;
163
164 /* (tmp << 8) * 3 / 1800 */
165 if (min <= 0)
166 data = 0;
167 else
168 data = (min << 5) / 675;
169 pm860x_reg_write(info->i2c, PM8607_VBAT_LOWTH, data);
170 dev_dbg(info->dev, "VBAT Min:%dmv, LOWTH:0x%x\n", min, data);
171
172 if (max <= 0)
173 data = 0xff;
174 else
175 data = (max << 5) / 675;
176 pm860x_reg_write(info->i2c, PM8607_VBAT_HIGHTH, data);
177 dev_dbg(info->dev, "VBAT Max:%dmv, HIGHTH:0x%x\n", max, data);
178
179 return;
180}
181
182static int start_precharge(struct pm860x_charger_info *info)
183{
184 int ret;
185
186 dev_dbg(info->dev, "Start Pre-charging!\n");
187 set_vbatt_threshold(info, 0, 0);
188
189 ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
190 PREREG1_1350MA | PREREG1_VSYS_4_5V);
191 if (ret < 0)
192 goto out;
193 /* stop charging */
194 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
195 CC1_MODE_OFF);
196 if (ret < 0)
197 goto out;
198 /* set 270 minutes timeout */
199 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
200 CC3_270MIN_TIMEOUT);
201 if (ret < 0)
202 goto out;
203 /* set precharge current, termination voltage, IBAT & TBAT monitor */
204 ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL4,
205 CC4_IPRE_40MA | CC4_VPCHG_3_2V |
206 CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
207 if (ret < 0)
208 goto out;
209 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
210 CC7_BAT_REM_EN | CC7_IFSM_EN,
211 CC7_BAT_REM_EN | CC7_IFSM_EN);
212 if (ret < 0)
213 goto out;
214 /* trigger precharge */
215 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
216 CC1_MODE_PRECHARGE);
217out:
218 return ret;
219}
220
221static int start_fastcharge(struct pm860x_charger_info *info)
222{
223 int ret;
224
225 dev_dbg(info->dev, "Start Fast-charging!\n");
226
227 /* set fastcharge termination current & voltage, disable charging */
228 ret = pm860x_reg_write(info->i2c, PM8607_CHG_CTRL1,
229 CC1_MODE_OFF | CC1_ITERM_60MA |
230 CC1_VFCHG_4_2V);
231 if (ret < 0)
232 goto out;
233 ret = pm860x_reg_write(info->i2c_8606, PM8606_PREREGULATORA,
234 PREREG1_540MA | PREREG1_VSYS_4_5V);
235 if (ret < 0)
236 goto out;
237 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f,
238 CC2_ICHG_500MA);
239 if (ret < 0)
240 goto out;
241 /* set 270 minutes timeout */
242 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL3, (0xf << 4),
243 CC3_270MIN_TIMEOUT);
244 if (ret < 0)
245 goto out;
246 /* set IBAT & TBAT monitor */
247 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL4,
248 CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN,
249 CC4_IFCHG_MON_EN | CC4_BTEMP_MON_EN);
250 if (ret < 0)
251 goto out;
252 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
253 CC6_BAT_OV_EN | CC6_BAT_UV_EN |
254 CC6_UV_VBAT_SET,
255 CC6_BAT_OV_EN | CC6_BAT_UV_EN |
256 CC6_UV_VBAT_SET);
257 if (ret < 0)
258 goto out;
259 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL7,
260 CC7_BAT_REM_EN | CC7_IFSM_EN,
261 CC7_BAT_REM_EN | CC7_IFSM_EN);
262 if (ret < 0)
263 goto out;
264 /* launch fast-charge */
265 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3,
266 CC1_MODE_FASTCHARGE);
267 /* vchg threshold setting */
268 set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_NORMAL_HIGH);
269out:
270 return ret;
271}
272
273static void stop_charge(struct pm860x_charger_info *info, int vbatt)
274{
275 dev_dbg(info->dev, "Stop charging!\n");
276 pm860x_set_bits(info->i2c, PM8607_CHG_CTRL1, 3, CC1_MODE_OFF);
277 if (vbatt > CHARGE_THRESHOLD && info->online)
278 set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
279}
280
281static void power_off_notification(struct pm860x_charger_info *info)
282{
283 dev_dbg(info->dev, "Power-off notification!\n");
284}
285
286static int set_charging_fsm(struct pm860x_charger_info *info)
287{
288 struct power_supply *psy;
289 union power_supply_propval data;
290 unsigned char fsm_state[][16] = { "init", "discharge", "precharge",
291 "fastcharge",
292 };
293 int ret;
294 int vbatt;
295
296 psy = power_supply_get_by_name(pm860x_supplied_to[0]);
297 if (!psy)
298 return -EINVAL;
299 ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &data);
300 if (ret)
301 return ret;
302 vbatt = data.intval / 1000;
303
304 ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data);
305 if (ret)
306 return ret;
307
308 mutex_lock(&info->lock);
309 info->present = data.intval;
310
311 dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, "
312 "Allowed:%d\n",
313 &fsm_state[info->state][0],
314 (info->online) ? "online" : "N/A",
315 (info->present) ? "present" : "N/A", info->allowed);
316 dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt);
317
318 switch (info->state) {
319 case FSM_INIT:
320 if (info->online && info->present && info->allowed) {
321 if (vbatt < PRECHARGE_THRESHOLD) {
322 info->state = FSM_PRECHARGE;
323 start_precharge(info);
324 } else if (vbatt > DISCHARGE_THRESHOLD) {
325 info->state = FSM_DISCHARGE;
326 stop_charge(info, vbatt);
327 } else if (vbatt < DISCHARGE_THRESHOLD) {
328 info->state = FSM_FASTCHARGE;
329 start_fastcharge(info);
330 }
331 } else {
332 if (vbatt < POWEROFF_THRESHOLD) {
333 power_off_notification(info);
334 } else {
335 info->state = FSM_DISCHARGE;
336 stop_charge(info, vbatt);
337 }
338 }
339 break;
340 case FSM_PRECHARGE:
341 if (info->online && info->present && info->allowed) {
342 if (vbatt > PRECHARGE_THRESHOLD) {
343 info->state = FSM_FASTCHARGE;
344 start_fastcharge(info);
345 }
346 } else {
347 info->state = FSM_DISCHARGE;
348 stop_charge(info, vbatt);
349 }
350 break;
351 case FSM_FASTCHARGE:
352 if (info->online && info->present && info->allowed) {
353 if (vbatt < PRECHARGE_THRESHOLD) {
354 info->state = FSM_PRECHARGE;
355 start_precharge(info);
356 }
357 } else {
358 info->state = FSM_DISCHARGE;
359 stop_charge(info, vbatt);
360 }
361 break;
362 case FSM_DISCHARGE:
363 if (info->online && info->present && info->allowed) {
364 if (vbatt < PRECHARGE_THRESHOLD) {
365 info->state = FSM_PRECHARGE;
366 start_precharge(info);
367 } else if (vbatt < DISCHARGE_THRESHOLD) {
368 info->state = FSM_FASTCHARGE;
369 start_fastcharge(info);
370 }
371 } else {
372 if (vbatt < POWEROFF_THRESHOLD)
373 power_off_notification(info);
374 else if (vbatt > CHARGE_THRESHOLD && info->online)
375 set_vbatt_threshold(info, CHARGE_THRESHOLD, 0);
376 }
377 break;
378 default:
379 dev_warn(info->dev, "FSM meets wrong state:%d\n",
380 info->state);
381 break;
382 }
383 dev_dbg(info->dev,
384 "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n",
385 &fsm_state[info->state][0],
386 (info->online) ? "online" : "N/A",
387 (info->present) ? "present" : "N/A", info->allowed);
388 mutex_unlock(&info->lock);
389
390 return 0;
391}
392
393static irqreturn_t pm860x_charger_handler(int irq, void *data)
394{
395 struct pm860x_charger_info *info = data;
396 int ret;
397
398 mutex_lock(&info->lock);
399 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
400 if (ret < 0) {
401 mutex_unlock(&info->lock);
402 goto out;
403 }
404 if (ret & STATUS2_CHG) {
405 info->online = 1;
406 info->allowed = 1;
407 } else {
408 info->online = 0;
409 info->allowed = 0;
410 }
411 mutex_unlock(&info->lock);
412 dev_dbg(info->dev, "%s, Charger:%s, Allowed:%d\n", __func__,
413 (info->online) ? "online" : "N/A", info->allowed);
414
415 set_charging_fsm(info);
416
417 power_supply_changed(&info->usb);
418out:
419 return IRQ_HANDLED;
420}
421
422static irqreturn_t pm860x_temp_handler(int irq, void *data)
423{
424 struct power_supply *psy;
425 struct pm860x_charger_info *info = data;
426 union power_supply_propval temp;
427 int value;
428 int ret;
429
430 psy = power_supply_get_by_name(pm860x_supplied_to[0]);
431 if (!psy)
432 goto out;
433 ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp);
434 if (ret)
435 goto out;
436 value = temp.intval / 10;
437
438 mutex_lock(&info->lock);
439 /* Temperature < -10 C or >40 C, Will not allow charge */
440 if (value < -10 || value > 40)
441 info->allowed = 0;
442 else
443 info->allowed = 1;
444 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
445 mutex_unlock(&info->lock);
446
447 set_charging_fsm(info);
448out:
449 return IRQ_HANDLED;
450}
451
452static irqreturn_t pm860x_exception_handler(int irq, void *data)
453{
454 struct pm860x_charger_info *info = data;
455
456 mutex_lock(&info->lock);
457 info->allowed = 0;
458 mutex_unlock(&info->lock);
459 dev_dbg(info->dev, "%s, irq: %d\n", __func__, irq);
460
461 set_charging_fsm(info);
462 return IRQ_HANDLED;
463}
464
465static irqreturn_t pm860x_done_handler(int irq, void *data)
466{
467 struct pm860x_charger_info *info = data;
468 struct power_supply *psy;
469 union power_supply_propval val;
470 int ret;
471 int vbatt;
472
473 mutex_lock(&info->lock);
474 /* pre-charge done, will transimit to fast-charge stage */
475 if (info->state == FSM_PRECHARGE) {
476 info->allowed = 1;
477 goto out;
478 }
479 /*
480 * Fast charge done, delay to read
481 * the correct status of CHG_DET.
482 */
483 mdelay(5);
484 info->allowed = 0;
485 psy = power_supply_get_by_name(pm860x_supplied_to[0]);
486 if (!psy)
487 goto out;
488 ret = psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
489 if (ret)
490 goto out;
491 vbatt = val.intval / 1000;
492 /*
493 * CHG_DONE interrupt is faster than CHG_DET interrupt when
494 * plug in/out usb, So we can not rely on info->online, we
495 * need check pm8607 status register to check usb is online
496 * or not, then we can decide it is real charge done
497 * automatically or it is triggered by usb plug out;
498 */
499 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
500 if (ret < 0)
501 goto out;
502 if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG)
503 psy->set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, &val);
504
505out:
506 mutex_unlock(&info->lock);
507 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
508 set_charging_fsm(info);
509
510 return IRQ_HANDLED;
511}
512
513static irqreturn_t pm860x_vbattery_handler(int irq, void *data)
514{
515 struct pm860x_charger_info *info = data;
516
517 mutex_lock(&info->lock);
518
519 set_vbatt_threshold(info, 0, 0);
520
521 if (info->present && info->online)
522 info->allowed = 1;
523 else
524 info->allowed = 0;
525 mutex_unlock(&info->lock);
526 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
527
528 set_charging_fsm(info);
529
530 return IRQ_HANDLED;
531}
532
533static irqreturn_t pm860x_vchg_handler(int irq, void *data)
534{
535 struct pm860x_charger_info *info = data;
536 int vchg = 0;
537
538 if (info->present)
539 goto out;
540
541 measure_vchg(info, &vchg);
542
543 mutex_lock(&info->lock);
544 if (!info->online) {
545 int status;
546 /* check if over-temp on pm8606 or not */
547 status = pm860x_reg_read(info->i2c_8606, PM8606_FLAGS);
548 if (status & OVER_TEMP_FLAG) {
549 /* clear over temp flag and set auto recover */
550 pm860x_set_bits(info->i2c_8606, PM8606_FLAGS,
551 OVER_TEMP_FLAG, OVER_TEMP_FLAG);
552 pm860x_set_bits(info->i2c_8606,
553 PM8606_VSYS,
554 OVTEMP_AUTORECOVER,
555 OVTEMP_AUTORECOVER);
556 dev_dbg(info->dev,
557 "%s, pm8606 over-temp occure\n", __func__);
558 }
559 }
560
561 if (vchg > VCHG_NORMAL_CHECK) {
562 set_vchg_threshold(info, VCHG_OVP_LOW, 0);
563 info->allowed = 0;
564 dev_dbg(info->dev,
565 "%s,pm8607 over-vchg occure,vchg = %dmv\n",
566 __func__, vchg);
567 } else if (vchg < VCHG_OVP_LOW) {
568 set_vchg_threshold(info, VCHG_NORMAL_LOW,
569 VCHG_NORMAL_HIGH);
570 info->allowed = 1;
571 dev_dbg(info->dev,
572 "%s,pm8607 over-vchg recover,vchg = %dmv\n",
573 __func__, vchg);
574 }
575 mutex_unlock(&info->lock);
576
577 dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed);
578 set_charging_fsm(info);
579out:
580 return IRQ_HANDLED;
581}
582
583static int pm860x_usb_get_prop(struct power_supply *psy,
584 enum power_supply_property psp,
585 union power_supply_propval *val)
586{
587 struct pm860x_charger_info *info =
588 dev_get_drvdata(psy->dev->parent);
589
590 switch (psp) {
591 case POWER_SUPPLY_PROP_STATUS:
592 if (info->state == FSM_FASTCHARGE ||
593 info->state == FSM_PRECHARGE)
594 val->intval = POWER_SUPPLY_STATUS_CHARGING;
595 else
596 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
597 break;
598 case POWER_SUPPLY_PROP_ONLINE:
599 val->intval = info->online;
600 break;
601 default:
602 return -ENODEV;
603 }
604 return 0;
605}
606
607static enum power_supply_property pm860x_usb_props[] = {
608 POWER_SUPPLY_PROP_STATUS,
609 POWER_SUPPLY_PROP_ONLINE,
610};
611
612static int pm860x_init_charger(struct pm860x_charger_info *info)
613{
614 int ret;
615
616 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
617 if (ret < 0)
618 return ret;
619
620 mutex_lock(&info->lock);
621 info->state = FSM_INIT;
622 if (ret & STATUS2_CHG) {
623 info->online = 1;
624 info->allowed = 1;
625 } else {
626 info->online = 0;
627 info->allowed = 0;
628 }
629 mutex_unlock(&info->lock);
630
631 set_charging_fsm(info);
632 return 0;
633}
634
635struct pm860x_irq_desc {
636 const char *name;
637 irqreturn_t (*handler)(int irq, void *data);
638} pm860x_irq_descs[] = {
639 { "usb supply detect", pm860x_charger_handler },
640 { "charge done", pm860x_done_handler },
641 { "charge timeout", pm860x_exception_handler },
642 { "charge fault", pm860x_exception_handler },
643 { "temperature", pm860x_temp_handler },
644 { "vbatt", pm860x_vbattery_handler },
645 { "vchg", pm860x_vchg_handler },
646};
647
648static __devinit int pm860x_charger_probe(struct platform_device *pdev)
649{
650 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
651 struct pm860x_charger_info *info;
652 int ret;
653 int count;
654 int i;
655 int j;
656
657 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
658 if (!info)
659 return -ENOMEM;
660
661 count = pdev->num_resources;
662 for (i = 0, j = 0; i < count; i++) {
663 info->irq[j] = platform_get_irq(pdev, i);
664 if (info->irq[j] < 0)
665 continue;
666 j++;
667 }
668 info->irq_nums = j;
669
670 info->chip = chip;
671 info->i2c =
672 (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
673 info->i2c_8606 =
674 (chip->id == CHIP_PM8607) ? chip->companion : chip->client;
675 if (!info->i2c_8606) {
676 dev_err(&pdev->dev, "Missed I2C address of 88PM8606!\n");
677 ret = -EINVAL;
678 goto out;
679 }
680 info->dev = &pdev->dev;
681
682 /* set init value for the case we are not using battery */
683 set_vchg_threshold(info, VCHG_NORMAL_LOW, VCHG_OVP_LOW);
684
685 mutex_init(&info->lock);
686 platform_set_drvdata(pdev, info);
687
688 info->usb.name = "usb";
689 info->usb.type = POWER_SUPPLY_TYPE_USB;
690 info->usb.supplied_to = pm860x_supplied_to;
691 info->usb.num_supplicants = ARRAY_SIZE(pm860x_supplied_to);
692 info->usb.properties = pm860x_usb_props;
693 info->usb.num_properties = ARRAY_SIZE(pm860x_usb_props);
694 info->usb.get_property = pm860x_usb_get_prop;
695 ret = power_supply_register(&pdev->dev, &info->usb);
696 if (ret)
697 goto out;
698
699 pm860x_init_charger(info);
700
701 for (i = 0; i < ARRAY_SIZE(info->irq); i++) {
702 ret = request_threaded_irq(info->irq[i], NULL,
703 pm860x_irq_descs[i].handler,
704 IRQF_ONESHOT, pm860x_irq_descs[i].name, info);
705 if (ret < 0) {
706 dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
707 info->irq[i], ret);
708 goto out_irq;
709 }
710 }
711 return 0;
712
713out_irq:
714 while (--i >= 0)
715 free_irq(info->irq[i], info);
716out:
717 kfree(info);
718 return ret;
719}
720
721static int __devexit pm860x_charger_remove(struct platform_device *pdev)
722{
723 struct pm860x_charger_info *info = platform_get_drvdata(pdev);
724 int i;
725
726 platform_set_drvdata(pdev, NULL);
727 power_supply_unregister(&info->usb);
728 free_irq(info->irq[0], info);
729 for (i = 0; i < info->irq_nums; i++)
730 free_irq(info->irq[i], info);
731 kfree(info);
732 return 0;
733}
734
735static struct platform_driver pm860x_charger_driver = {
736 .driver = {
737 .name = "88pm860x-charger",
738 .owner = THIS_MODULE,
739 },
740 .probe = pm860x_charger_probe,
741 .remove = __devexit_p(pm860x_charger_remove),
742};
743module_platform_driver(pm860x_charger_driver);
744
745MODULE_DESCRIPTION("Marvell 88PM860x Charger driver");
746MODULE_LICENSE("GPL");