aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power
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
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')
-rw-r--r--drivers/power/88pm860x_battery.c1041
-rw-r--r--drivers/power/88pm860x_charger.c746
-rw-r--r--drivers/power/Kconfig12
-rw-r--r--drivers/power/Makefile2
4 files changed, 1801 insertions, 0 deletions
diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c
new file mode 100644
index 000000000000..5e905f3db4bf
--- /dev/null
+++ b/drivers/power/88pm860x_battery.c
@@ -0,0 +1,1041 @@
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/mutex.h>
18#include <linux/string.h>
19#include <linux/power_supply.h>
20#include <linux/mfd/88pm860x.h>
21#include <linux/delay.h>
22
23/* bit definitions of Status Query Interface 2 */
24#define STATUS2_CHG (1 << 2)
25#define STATUS2_BAT (1 << 3)
26#define STATUS2_VBUS (1 << 4)
27
28/* bit definitions of Measurement Enable 1 Register */
29#define MEAS1_TINT (1 << 3)
30#define MEAS1_GP1 (1 << 5)
31
32/* bit definitions of Measurement Enable 3 Register */
33#define MEAS3_IBAT (1 << 0)
34#define MEAS3_BAT_DET (1 << 1)
35#define MEAS3_CC (1 << 2)
36
37/* bit definitions of Measurement Off Time Register */
38#define MEAS_OFF_SLEEP_EN (1 << 1)
39
40/* bit definitions of GPADC Bias Current 2 Register */
41#define GPBIAS2_GPADC1_SET (2 << 4)
42/* GPADC1 Bias Current value in uA unit */
43#define GPBIAS2_GPADC1_UA ((GPBIAS2_GPADC1_SET >> 4) * 5 + 1)
44
45/* bit definitions of GPADC Misc 1 Register */
46#define GPMISC1_GPADC_EN (1 << 0)
47
48/* bit definitions of Charger Control 6 Register */
49#define CC6_BAT_DET_GPADC1 1
50
51/* bit definitions of Coulomb Counter Reading Register */
52#define CCNT_AVG_SEL (4 << 3)
53
54/* bit definitions of RTC miscellaneous Register1 */
55#define RTC_SOC_5LSB (0x1F << 3)
56
57/* bit definitions of RTC Register1 */
58#define RTC_SOC_3MSB (0x7)
59
60/* bit definitions of Power up Log register */
61#define BAT_WU_LOG (1<<6)
62
63/* coulomb counter index */
64#define CCNT_POS1 0
65#define CCNT_POS2 1
66#define CCNT_NEG1 2
67#define CCNT_NEG2 3
68#define CCNT_SPOS 4
69#define CCNT_SNEG 5
70
71/* OCV -- Open Circuit Voltage */
72#define OCV_MODE_ACTIVE 0
73#define OCV_MODE_SLEEP 1
74
75/* Vbat range of CC for measuring Rbat */
76#define LOW_BAT_THRESHOLD 3600
77#define VBATT_RESISTOR_MIN 3800
78#define VBATT_RESISTOR_MAX 4100
79
80/* TBAT for batt, TINT for chip itself */
81#define PM860X_TEMP_TINT (0)
82#define PM860X_TEMP_TBAT (1)
83
84/*
85 * Battery temperature based on NTC resistor, defined
86 * corresponding resistor value -- Ohm / C degeree.
87 */
88#define TBAT_NEG_25D 127773 /* -25 */
89#define TBAT_NEG_10D 54564 /* -10 */
90#define TBAT_0D 32330 /* 0 */
91#define TBAT_10D 19785 /* 10 */
92#define TBAT_20D 12468 /* 20 */
93#define TBAT_30D 8072 /* 30 */
94#define TBAT_40D 5356 /* 40 */
95
96struct pm860x_battery_info {
97 struct pm860x_chip *chip;
98 struct i2c_client *i2c;
99 struct device *dev;
100
101 struct power_supply battery;
102 struct mutex lock;
103 int status;
104 int irq_cc;
105 int irq_batt;
106 int max_capacity;
107 int resistor; /* Battery Internal Resistor */
108 int last_capacity;
109 int start_soc;
110 unsigned present:1;
111 unsigned temp_type:1; /* TINT or TBAT */
112};
113
114struct ccnt {
115 unsigned long long int pos;
116 unsigned long long int neg;
117 unsigned int spos;
118 unsigned int sneg;
119
120 int total_chg; /* mAh(3.6C) */
121 int total_dischg; /* mAh(3.6C) */
122};
123
124/*
125 * State of Charge.
126 * The first number is mAh(=3.6C), and the second number is percent point.
127 */
128int array_soc[][2] = {
129 {4170, 100}, {4154, 99}, {4136, 98}, {4122, 97}, {4107, 96},
130 {4102, 95}, {4088, 94}, {4081, 93}, {4070, 92}, {4060, 91},
131 {4053, 90}, {4044, 89}, {4035, 88}, {4028, 87}, {4019, 86},
132 {4013, 85}, {4006, 84}, {3995, 83}, {3987, 82}, {3982, 81},
133 {3976, 80}, {3968, 79}, {3962, 78}, {3954, 77}, {3946, 76},
134 {3941, 75}, {3934, 74}, {3929, 73}, {3922, 72}, {3916, 71},
135 {3910, 70}, {3904, 69}, {3898, 68}, {3892, 67}, {3887, 66},
136 {3880, 65}, {3874, 64}, {3868, 63}, {3862, 62}, {3854, 61},
137 {3849, 60}, {3843, 59}, {3840, 58}, {3833, 57}, {3829, 56},
138 {3824, 55}, {3818, 54}, {3815, 53}, {3810, 52}, {3808, 51},
139 {3804, 50}, {3801, 49}, {3798, 48}, {3796, 47}, {3792, 46},
140 {3789, 45}, {3785, 44}, {3784, 43}, {3782, 42}, {3780, 41},
141 {3777, 40}, {3776, 39}, {3774, 38}, {3772, 37}, {3771, 36},
142 {3769, 35}, {3768, 34}, {3764, 33}, {3763, 32}, {3760, 31},
143 {3760, 30}, {3754, 29}, {3750, 28}, {3749, 27}, {3744, 26},
144 {3740, 25}, {3734, 24}, {3732, 23}, {3728, 22}, {3726, 21},
145 {3720, 20}, {3716, 19}, {3709, 18}, {3703, 17}, {3698, 16},
146 {3692, 15}, {3683, 14}, {3675, 13}, {3670, 12}, {3665, 11},
147 {3661, 10}, {3649, 9}, {3637, 8}, {3622, 7}, {3609, 6},
148 {3580, 5}, {3558, 4}, {3540, 3}, {3510, 2}, {3429, 1},
149};
150
151static struct ccnt ccnt_data;
152
153/*
154 * register 1 bit[7:0] -- bit[11:4] of measured value of voltage
155 * register 0 bit[3:0] -- bit[3:0] of measured value of voltage
156 */
157static int measure_12bit_voltage(struct pm860x_battery_info *info,
158 int offset, int *data)
159{
160 unsigned char buf[2];
161 int ret;
162
163 ret = pm860x_bulk_read(info->i2c, offset, 2, buf);
164 if (ret < 0)
165 return ret;
166
167 *data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
168 /* V_MEAS(mV) = data * 1.8 * 1000 / (2^12) */
169 *data = ((*data & 0xfff) * 9 * 25) >> 9;
170 return 0;
171}
172
173static int measure_vbatt(struct pm860x_battery_info *info, int state,
174 int *data)
175{
176 unsigned char buf[5];
177 int ret;
178
179 switch (state) {
180 case OCV_MODE_ACTIVE:
181 ret = measure_12bit_voltage(info, PM8607_VBAT_MEAS1, data);
182 if (ret)
183 return ret;
184 /* V_BATT_MEAS(mV) = value * 3 * 1.8 * 1000 / (2^12) */
185 *data *= 3;
186 break;
187 case OCV_MODE_SLEEP:
188 /*
189 * voltage value of VBATT in sleep mode is saved in different
190 * registers.
191 * bit[11:10] -- bit[7:6] of LDO9(0x18)
192 * bit[9:8] -- bit[7:6] of LDO8(0x17)
193 * bit[7:6] -- bit[7:6] of LDO7(0x16)
194 * bit[5:4] -- bit[7:6] of LDO6(0x15)
195 * bit[3:0] -- bit[7:4] of LDO5(0x14)
196 */
197 ret = pm860x_bulk_read(info->i2c, PM8607_LDO5, 5, buf);
198 if (ret < 0)
199 return ret;
200 ret = ((buf[4] >> 6) << 10) | ((buf[3] >> 6) << 8)
201 | ((buf[2] >> 6) << 6) | ((buf[1] >> 6) << 4)
202 | (buf[0] >> 4);
203 /* V_BATT_MEAS(mV) = data * 3 * 1.8 * 1000 / (2^12) */
204 *data = ((*data & 0xff) * 27 * 25) >> 9;
205 break;
206 default:
207 return -EINVAL;
208 }
209 return 0;
210}
211
212/*
213 * Return value is signed data.
214 * Negative value means discharging, and positive value means charging.
215 */
216static int measure_current(struct pm860x_battery_info *info, int *data)
217{
218 unsigned char buf[2];
219 short s;
220 int ret;
221
222 ret = pm860x_bulk_read(info->i2c, PM8607_IBAT_MEAS1, 2, buf);
223 if (ret < 0)
224 return ret;
225
226 s = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
227 /* current(mA) = value * 0.125 */
228 *data = s >> 3;
229 return 0;
230}
231
232static int set_charger_current(struct pm860x_battery_info *info, int data,
233 int *old)
234{
235 int ret;
236
237 if (data < 50 || data > 1600 || !old)
238 return -EINVAL;
239
240 data = ((data - 50) / 50) & 0x1f;
241 *old = pm860x_reg_read(info->i2c, PM8607_CHG_CTRL2);
242 *old = (*old & 0x1f) * 50 + 50;
243 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, data);
244 if (ret < 0)
245 return ret;
246 return 0;
247}
248
249static int read_ccnt(struct pm860x_battery_info *info, int offset,
250 int *ccnt)
251{
252 unsigned char buf[2];
253 int ret;
254
255 ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7, offset & 7);
256 if (ret < 0)
257 goto out;
258 ret = pm860x_bulk_read(info->i2c, PM8607_CCNT_MEAS1, 2, buf);
259 if (ret < 0)
260 goto out;
261 *ccnt = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
262 return 0;
263out:
264 return ret;
265}
266
267static int calc_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
268{
269 unsigned int sum;
270 int ret;
271 int data;
272
273 ret = read_ccnt(info, CCNT_POS1, &data);
274 if (ret)
275 goto out;
276 sum = data & 0xffff;
277 ret = read_ccnt(info, CCNT_POS2, &data);
278 if (ret)
279 goto out;
280 sum |= (data & 0xffff) << 16;
281 ccnt->pos += sum;
282
283 ret = read_ccnt(info, CCNT_NEG1, &data);
284 if (ret)
285 goto out;
286 sum = data & 0xffff;
287 ret = read_ccnt(info, CCNT_NEG2, &data);
288 if (ret)
289 goto out;
290 sum |= (data & 0xffff) << 16;
291 sum = ~sum + 1; /* since it's negative */
292 ccnt->neg += sum;
293
294 ret = read_ccnt(info, CCNT_SPOS, &data);
295 if (ret)
296 goto out;
297 ccnt->spos += data;
298 ret = read_ccnt(info, CCNT_SNEG, &data);
299 if (ret)
300 goto out;
301
302 /*
303 * charge(mAh) = count * 1.6984 * 1e(-8)
304 * = count * 16984 * 1.024 * 1.024 * 1.024 / (2 ^ 40)
305 * = count * 18236 / (2 ^ 40)
306 */
307 ccnt->total_chg = (int) ((ccnt->pos * 18236) >> 40);
308 ccnt->total_dischg = (int) ((ccnt->neg * 18236) >> 40);
309 return 0;
310out:
311 return ret;
312}
313
314static int clear_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
315{
316 int data;
317
318 memset(ccnt, 0, sizeof(*ccnt));
319 /* read to clear ccnt */
320 read_ccnt(info, CCNT_POS1, &data);
321 read_ccnt(info, CCNT_POS2, &data);
322 read_ccnt(info, CCNT_NEG1, &data);
323 read_ccnt(info, CCNT_NEG2, &data);
324 read_ccnt(info, CCNT_SPOS, &data);
325 read_ccnt(info, CCNT_SNEG, &data);
326 return 0;
327}
328
329/* Calculate Open Circuit Voltage */
330static int calc_ocv(struct pm860x_battery_info *info, int *ocv)
331{
332 int ret;
333 int i;
334 int data;
335 int vbatt_avg;
336 int vbatt_sum;
337 int ibatt_avg;
338 int ibatt_sum;
339
340 if (!ocv)
341 return -EINVAL;
342
343 for (i = 0, ibatt_sum = 0, vbatt_sum = 0; i < 10; i++) {
344 ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
345 if (ret)
346 goto out;
347 vbatt_sum += data;
348 ret = measure_current(info, &data);
349 if (ret)
350 goto out;
351 ibatt_sum += data;
352 }
353 vbatt_avg = vbatt_sum / 10;
354 ibatt_avg = ibatt_sum / 10;
355
356 mutex_lock(&info->lock);
357 if (info->present)
358 *ocv = vbatt_avg - ibatt_avg * info->resistor / 1000;
359 else
360 *ocv = vbatt_avg;
361 mutex_unlock(&info->lock);
362 dev_dbg(info->dev, "VBAT average:%d, OCV:%d\n", vbatt_avg, *ocv);
363 return 0;
364out:
365 return ret;
366}
367
368/* Calculate State of Charge (percent points) */
369static int calc_soc(struct pm860x_battery_info *info, int state, int *soc)
370{
371 int i;
372 int ocv;
373 int count;
374 int ret = -EINVAL;
375
376 if (!soc)
377 return -EINVAL;
378
379 switch (state) {
380 case OCV_MODE_ACTIVE:
381 ret = calc_ocv(info, &ocv);
382 break;
383 case OCV_MODE_SLEEP:
384 ret = measure_vbatt(info, OCV_MODE_SLEEP, &ocv);
385 break;
386 }
387 if (ret)
388 return ret;
389
390 count = ARRAY_SIZE(array_soc);
391 if (ocv < array_soc[count - 1][0]) {
392 *soc = 0;
393 return 0;
394 }
395
396 for (i = 0; i < count; i++) {
397 if (ocv >= array_soc[i][0]) {
398 *soc = array_soc[i][1];
399 break;
400 }
401 }
402 return 0;
403}
404
405static irqreturn_t pm860x_coulomb_handler(int irq, void *data)
406{
407 struct pm860x_battery_info *info = data;
408
409 calc_ccnt(info, &ccnt_data);
410 return IRQ_HANDLED;
411}
412
413static irqreturn_t pm860x_batt_handler(int irq, void *data)
414{
415 struct pm860x_battery_info *info = data;
416 int ret;
417
418 mutex_lock(&info->lock);
419 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
420 if (ret & STATUS2_BAT) {
421 info->present = 1;
422 info->temp_type = PM860X_TEMP_TBAT;
423 } else {
424 info->present = 0;
425 info->temp_type = PM860X_TEMP_TINT;
426 }
427 mutex_unlock(&info->lock);
428 /* clear ccnt since battery is attached or dettached */
429 clear_ccnt(info, &ccnt_data);
430 return IRQ_HANDLED;
431}
432
433static void pm860x_init_battery(struct pm860x_battery_info *info)
434{
435 unsigned char buf[2];
436 int ret;
437 int data;
438 int bat_remove;
439 int soc;
440
441 /* measure enable on GPADC1 */
442 data = MEAS1_GP1;
443 if (info->temp_type == PM860X_TEMP_TINT)
444 data |= MEAS1_TINT;
445 ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN1, data, data);
446 if (ret)
447 goto out;
448
449 /* measure enable on IBAT, BAT_DET, CC. IBAT is depend on CC. */
450 data = MEAS3_IBAT | MEAS3_BAT_DET | MEAS3_CC;
451 ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN3, data, data);
452 if (ret)
453 goto out;
454
455 /* measure disable CC in sleep time */
456 ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME1, 0x82);
457 if (ret)
458 goto out;
459 ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME2, 0x6c);
460 if (ret)
461 goto out;
462
463 /* enable GPADC */
464 ret = pm860x_set_bits(info->i2c, PM8607_GPADC_MISC1,
465 GPMISC1_GPADC_EN, GPMISC1_GPADC_EN);
466 if (ret < 0)
467 goto out;
468
469 /* detect battery via GPADC1 */
470 ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
471 CC6_BAT_DET_GPADC1, CC6_BAT_DET_GPADC1);
472 if (ret < 0)
473 goto out;
474
475 ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7 << 3,
476 CCNT_AVG_SEL);
477 if (ret < 0)
478 goto out;
479
480 /* set GPADC1 bias */
481 ret = pm860x_set_bits(info->i2c, PM8607_GP_BIAS2, 0xF << 4,
482 GPBIAS2_GPADC1_SET);
483 if (ret < 0)
484 goto out;
485
486 /* check whether battery present) */
487 mutex_lock(&info->lock);
488 ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
489 if (ret < 0) {
490 mutex_unlock(&info->lock);
491 goto out;
492 }
493 if (ret & STATUS2_BAT) {
494 info->present = 1;
495 info->temp_type = PM860X_TEMP_TBAT;
496 } else {
497 info->present = 0;
498 info->temp_type = PM860X_TEMP_TINT;
499 }
500 mutex_unlock(&info->lock);
501
502 calc_soc(info, OCV_MODE_ACTIVE, &soc);
503
504 data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG);
505 bat_remove = data & BAT_WU_LOG;
506
507 dev_dbg(info->dev, "battery wake up? %s\n",
508 bat_remove != 0 ? "yes" : "no");
509
510 /* restore SOC from RTC domain register */
511 if (bat_remove == 0) {
512 buf[0] = pm860x_reg_read(info->i2c, PM8607_RTC_MISC2);
513 buf[1] = pm860x_reg_read(info->i2c, PM8607_RTC1);
514 data = ((buf[1] & 0x3) << 5) | ((buf[0] >> 3) & 0x1F);
515 if (data > soc + 15)
516 info->start_soc = soc;
517 else if (data < soc - 15)
518 info->start_soc = soc;
519 else
520 info->start_soc = data;
521 dev_dbg(info->dev, "soc_rtc %d, soc_ocv :%d\n", data, soc);
522 } else {
523 pm860x_set_bits(info->i2c, PM8607_POWER_UP_LOG,
524 BAT_WU_LOG, BAT_WU_LOG);
525 info->start_soc = soc;
526 }
527 info->last_capacity = info->start_soc;
528 dev_dbg(info->dev, "init soc : %d\n", info->last_capacity);
529out:
530 return;
531}
532
533static void set_temp_threshold(struct pm860x_battery_info *info,
534 int min, int max)
535{
536 int data;
537
538 /* (tmp << 8) / 1800 */
539 if (min <= 0)
540 data = 0;
541 else
542 data = (min << 8) / 1800;
543 pm860x_reg_write(info->i2c, PM8607_GPADC1_HIGHTH, data);
544 dev_dbg(info->dev, "TEMP_HIGHTH : min: %d, 0x%x\n", min, data);
545
546 if (max <= 0)
547 data = 0xff;
548 else
549 data = (max << 8) / 1800;
550 pm860x_reg_write(info->i2c, PM8607_GPADC1_LOWTH, data);
551 dev_dbg(info->dev, "TEMP_LOWTH:max : %d, 0x%x\n", max, data);
552}
553
554static int measure_temp(struct pm860x_battery_info *info, int *data)
555{
556 int ret;
557 int temp;
558 int min;
559 int max;
560
561 if (info->temp_type == PM860X_TEMP_TINT) {
562 ret = measure_12bit_voltage(info, PM8607_TINT_MEAS1, data);
563 if (ret)
564 return ret;
565 *data = (*data - 884) * 1000 / 3611;
566 } else {
567 ret = measure_12bit_voltage(info, PM8607_GPADC1_MEAS1, data);
568 if (ret)
569 return ret;
570 /* meausered Vtbat(mV) / Ibias_current(11uA)*/
571 *data = (*data * 1000) / GPBIAS2_GPADC1_UA;
572
573 if (*data > TBAT_NEG_25D) {
574 temp = -30; /* over cold , suppose -30 roughly */
575 max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
576 set_temp_threshold(info, 0, max);
577 } else if (*data > TBAT_NEG_10D) {
578 temp = -15; /* -15 degree, code */
579 max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
580 set_temp_threshold(info, 0, max);
581 } else if (*data > TBAT_0D) {
582 temp = -5; /* -5 degree */
583 min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
584 max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
585 set_temp_threshold(info, min, max);
586 } else if (*data > TBAT_10D) {
587 temp = 5; /* in range of (0, 10) */
588 min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
589 max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
590 set_temp_threshold(info, min, max);
591 } else if (*data > TBAT_20D) {
592 temp = 15; /* in range of (10, 20) */
593 min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
594 max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
595 set_temp_threshold(info, min, max);
596 } else if (*data > TBAT_30D) {
597 temp = 25; /* in range of (20, 30) */
598 min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
599 max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
600 set_temp_threshold(info, min, max);
601 } else if (*data > TBAT_40D) {
602 temp = 35; /* in range of (30, 40) */
603 min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
604 max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
605 set_temp_threshold(info, min, max);
606 } else {
607 min = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
608 set_temp_threshold(info, min, 0);
609 temp = 45; /* over heat ,suppose 45 roughly */
610 }
611
612 dev_dbg(info->dev, "temp_C:%d C,temp_mv:%d mv\n", temp, *data);
613 *data = temp;
614 }
615 return 0;
616}
617
618static int calc_resistor(struct pm860x_battery_info *info)
619{
620 int vbatt_sum1;
621 int vbatt_sum2;
622 int chg_current;
623 int ibatt_sum1;
624 int ibatt_sum2;
625 int data;
626 int ret;
627 int i;
628
629 ret = measure_current(info, &data);
630 /* make sure that charging is launched by data > 0 */
631 if (ret || data < 0)
632 goto out;
633
634 ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
635 if (ret)
636 goto out;
637 /* calculate resistor only in CC charge mode */
638 if (data < VBATT_RESISTOR_MIN || data > VBATT_RESISTOR_MAX)
639 goto out;
640
641 /* current is saved */
642 if (set_charger_current(info, 500, &chg_current))
643 goto out;
644
645 /*
646 * set charge current as 500mA, wait about 500ms till charging
647 * process is launched and stable with the newer charging current.
648 */
649 msleep(500);
650
651 for (i = 0, vbatt_sum1 = 0, ibatt_sum1 = 0; i < 10; i++) {
652 ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
653 if (ret)
654 goto out_meas;
655 vbatt_sum1 += data;
656 ret = measure_current(info, &data);
657 if (ret)
658 goto out_meas;
659
660 if (data < 0)
661 ibatt_sum1 = ibatt_sum1 - data; /* discharging */
662 else
663 ibatt_sum1 = ibatt_sum1 + data; /* charging */
664 }
665
666 if (set_charger_current(info, 100, &ret))
667 goto out_meas;
668 /*
669 * set charge current as 100mA, wait about 500ms till charging
670 * process is launched and stable with the newer charging current.
671 */
672 msleep(500);
673
674 for (i = 0, vbatt_sum2 = 0, ibatt_sum2 = 0; i < 10; i++) {
675 ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
676 if (ret)
677 goto out_meas;
678 vbatt_sum2 += data;
679 ret = measure_current(info, &data);
680 if (ret)
681 goto out_meas;
682
683 if (data < 0)
684 ibatt_sum2 = ibatt_sum2 - data; /* discharging */
685 else
686 ibatt_sum2 = ibatt_sum2 + data; /* charging */
687 }
688
689 /* restore current setting */
690 if (set_charger_current(info, chg_current, &ret))
691 goto out_meas;
692
693 if ((vbatt_sum1 > vbatt_sum2) && (ibatt_sum1 > ibatt_sum2) &&
694 (ibatt_sum2 > 0)) {
695 /* calculate resistor in discharging case */
696 data = 1000 * (vbatt_sum1 - vbatt_sum2)
697 / (ibatt_sum1 - ibatt_sum2);
698 if ((data - info->resistor > 0) &&
699 (data - info->resistor < info->resistor))
700 info->resistor = data;
701 if ((info->resistor - data > 0) &&
702 (info->resistor - data < data))
703 info->resistor = data;
704 }
705 return 0;
706
707out_meas:
708 set_charger_current(info, chg_current, &ret);
709out:
710 return -EINVAL;
711}
712
713static int calc_capacity(struct pm860x_battery_info *info, int *cap)
714{
715 int ret;
716 int data;
717 int ibat;
718 int cap_ocv = 0;
719 int cap_cc = 0;
720
721 ret = calc_ccnt(info, &ccnt_data);
722 if (ret)
723 goto out;
724soc:
725 data = info->max_capacity * info->start_soc / 100;
726 if (ccnt_data.total_dischg - ccnt_data.total_chg <= data) {
727 cap_cc =
728 data + ccnt_data.total_chg - ccnt_data.total_dischg;
729 } else {
730 clear_ccnt(info, &ccnt_data);
731 calc_soc(info, OCV_MODE_ACTIVE, &info->start_soc);
732 dev_dbg(info->dev, "restart soc = %d !\n",
733 info->start_soc);
734 goto soc;
735 }
736
737 cap_cc = cap_cc * 100 / info->max_capacity;
738 if (cap_cc < 0)
739 cap_cc = 0;
740 else if (cap_cc > 100)
741 cap_cc = 100;
742
743 dev_dbg(info->dev, "%s, last cap : %d", __func__,
744 info->last_capacity);
745
746 ret = measure_current(info, &ibat);
747 if (ret)
748 goto out;
749 /* Calculate the capacity when discharging(ibat < 0) */
750 if (ibat < 0) {
751 ret = calc_soc(info, OCV_MODE_ACTIVE, &cap_ocv);
752 if (ret)
753 cap_ocv = info->last_capacity;
754 ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
755 if (ret)
756 goto out;
757 if (data <= LOW_BAT_THRESHOLD) {
758 /* choose the lower capacity value to report
759 * between vbat and CC when vbat < 3.6v;
760 * than 3.6v;
761 */
762 *cap = min(cap_ocv, cap_cc);
763 } else {
764 /* when detect vbat > 3.6v, but cap_cc < 15,and
765 * cap_ocv is 10% larger than cap_cc, we can think
766 * CC have some accumulation error, switch to OCV
767 * to estimate capacity;
768 * */
769 if (cap_cc < 15 && cap_ocv - cap_cc > 10)
770 *cap = cap_ocv;
771 else
772 *cap = cap_cc;
773 }
774 /* when discharging, make sure current capacity
775 * is lower than last*/
776 if (*cap > info->last_capacity)
777 *cap = info->last_capacity;
778 } else {
779 *cap = cap_cc;
780 }
781 info->last_capacity = *cap;
782
783 dev_dbg(info->dev, "%s, cap_ocv:%d cap_cc:%d, cap:%d\n",
784 (ibat < 0) ? "discharging" : "charging",
785 cap_ocv, cap_cc, *cap);
786 /*
787 * store the current capacity to RTC domain register,
788 * after next power up , it will be restored.
789 */
790 pm860x_set_bits(info->i2c, PM8607_RTC_MISC2, RTC_SOC_5LSB,
791 (*cap & 0x1F) << 3);
792 pm860x_set_bits(info->i2c, PM8607_RTC1, RTC_SOC_3MSB,
793 ((*cap >> 5) & 0x3));
794 return 0;
795out:
796 return ret;
797}
798
799static void pm860x_external_power_changed(struct power_supply *psy)
800{
801 struct pm860x_battery_info *info;
802
803 info = container_of(psy, struct pm860x_battery_info, battery);
804 calc_resistor(info);
805}
806
807static int pm860x_batt_get_prop(struct power_supply *psy,
808 enum power_supply_property psp,
809 union power_supply_propval *val)
810{
811 struct pm860x_battery_info *info = dev_get_drvdata(psy->dev->parent);
812 int data;
813 int ret;
814
815 switch (psp) {
816 case POWER_SUPPLY_PROP_PRESENT:
817 val->intval = info->present;
818 break;
819 case POWER_SUPPLY_PROP_CAPACITY:
820 ret = calc_capacity(info, &data);
821 if (ret)
822 return ret;
823 if (data < 0)
824 data = 0;
825 else if (data > 100)
826 data = 100;
827 /* return 100 if battery is not attached */
828 if (!info->present)
829 data = 100;
830 val->intval = data;
831 break;
832 case POWER_SUPPLY_PROP_TECHNOLOGY:
833 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
834 break;
835 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
836 /* return real vbatt Voltage */
837 ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
838 if (ret)
839 return ret;
840 val->intval = data * 1000;
841 break;
842 case POWER_SUPPLY_PROP_VOLTAGE_AVG:
843 /* return Open Circuit Voltage (not measured voltage) */
844 ret = calc_ocv(info, &data);
845 if (ret)
846 return ret;
847 val->intval = data * 1000;
848 break;
849 case POWER_SUPPLY_PROP_CURRENT_NOW:
850 ret = measure_current(info, &data);
851 if (ret)
852 return ret;
853 val->intval = data;
854 break;
855 case POWER_SUPPLY_PROP_TEMP:
856 if (info->present) {
857 ret = measure_temp(info, &data);
858 if (ret)
859 return ret;
860 data *= 10;
861 } else {
862 /* Fake Temp 25C Without Battery */
863 data = 250;
864 }
865 val->intval = data;
866 break;
867 default:
868 return -ENODEV;
869 }
870 return 0;
871}
872
873static int pm860x_batt_set_prop(struct power_supply *psy,
874 enum power_supply_property psp,
875 const union power_supply_propval *val)
876{
877 struct pm860x_battery_info *info = dev_get_drvdata(psy->dev->parent);
878
879 switch (psp) {
880 case POWER_SUPPLY_PROP_CHARGE_FULL:
881 clear_ccnt(info, &ccnt_data);
882 info->start_soc = 100;
883 dev_dbg(info->dev, "chg done, update soc = %d\n",
884 info->start_soc);
885 break;
886 default:
887 return -EPERM;
888 }
889
890 return 0;
891}
892
893
894static enum power_supply_property pm860x_batt_props[] = {
895 POWER_SUPPLY_PROP_PRESENT,
896 POWER_SUPPLY_PROP_CAPACITY,
897 POWER_SUPPLY_PROP_TECHNOLOGY,
898 POWER_SUPPLY_PROP_VOLTAGE_NOW,
899 POWER_SUPPLY_PROP_VOLTAGE_AVG,
900 POWER_SUPPLY_PROP_CURRENT_NOW,
901 POWER_SUPPLY_PROP_TEMP,
902};
903
904static __devinit int pm860x_battery_probe(struct platform_device *pdev)
905{
906 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
907 struct pm860x_battery_info *info;
908 struct pm860x_power_pdata *pdata;
909 int ret;
910
911 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
912 if (!info)
913 return -ENOMEM;
914
915 info->irq_cc = platform_get_irq(pdev, 0);
916 if (info->irq_cc <= 0) {
917 dev_err(&pdev->dev, "No IRQ resource!\n");
918 ret = -EINVAL;
919 goto out;
920 }
921
922 info->irq_batt = platform_get_irq(pdev, 1);
923 if (info->irq_batt <= 0) {
924 dev_err(&pdev->dev, "No IRQ resource!\n");
925 ret = -EINVAL;
926 goto out;
927 }
928
929 info->chip = chip;
930 info->i2c =
931 (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
932 info->dev = &pdev->dev;
933 info->status = POWER_SUPPLY_STATUS_UNKNOWN;
934 pdata = pdev->dev.platform_data;
935
936 mutex_init(&info->lock);
937 platform_set_drvdata(pdev, info);
938
939 pm860x_init_battery(info);
940
941 info->battery.name = "battery-monitor";
942 info->battery.type = POWER_SUPPLY_TYPE_BATTERY;
943 info->battery.properties = pm860x_batt_props;
944 info->battery.num_properties = ARRAY_SIZE(pm860x_batt_props);
945 info->battery.get_property = pm860x_batt_get_prop;
946 info->battery.set_property = pm860x_batt_set_prop;
947 info->battery.external_power_changed = pm860x_external_power_changed;
948
949 if (pdata && pdata->max_capacity)
950 info->max_capacity = pdata->max_capacity;
951 else
952 info->max_capacity = 1500; /* set default capacity */
953 if (pdata && pdata->resistor)
954 info->resistor = pdata->resistor;
955 else
956 info->resistor = 300; /* set default internal resistor */
957
958 ret = power_supply_register(&pdev->dev, &info->battery);
959 if (ret)
960 goto out;
961 info->battery.dev->parent = &pdev->dev;
962
963 ret = request_threaded_irq(info->irq_cc, NULL,
964 pm860x_coulomb_handler, IRQF_ONESHOT,
965 "coulomb", info);
966 if (ret < 0) {
967 dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
968 info->irq_cc, ret);
969 goto out_reg;
970 }
971
972 ret = request_threaded_irq(info->irq_batt, NULL, pm860x_batt_handler,
973 IRQF_ONESHOT, "battery", info);
974 if (ret < 0) {
975 dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
976 info->irq_batt, ret);
977 goto out_coulomb;
978 }
979
980
981 return 0;
982
983out_coulomb:
984 free_irq(info->irq_cc, info);
985out_reg:
986 power_supply_unregister(&info->battery);
987out:
988 kfree(info);
989 return ret;
990}
991
992static int __devexit pm860x_battery_remove(struct platform_device *pdev)
993{
994 struct pm860x_battery_info *info = platform_get_drvdata(pdev);
995
996 power_supply_unregister(&info->battery);
997 free_irq(info->irq_batt, info);
998 free_irq(info->irq_cc, info);
999 kfree(info);
1000 platform_set_drvdata(pdev, NULL);
1001 return 0;
1002}
1003
1004#ifdef CONFIG_PM_SLEEP
1005static int pm860x_battery_suspend(struct device *dev)
1006{
1007 struct platform_device *pdev = to_platform_device(dev);
1008 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
1009
1010 if (device_may_wakeup(dev))
1011 chip->wakeup_flag |= 1 << PM8607_IRQ_CC;
1012 return 0;
1013}
1014
1015static int pm860x_battery_resume(struct device *dev)
1016{
1017 struct platform_device *pdev = to_platform_device(dev);
1018 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
1019
1020 if (device_may_wakeup(dev))
1021 chip->wakeup_flag &= ~(1 << PM8607_IRQ_CC);
1022 return 0;
1023}
1024#endif
1025
1026static SIMPLE_DEV_PM_OPS(pm860x_battery_pm_ops,
1027 pm860x_battery_suspend, pm860x_battery_resume);
1028
1029static struct platform_driver pm860x_battery_driver = {
1030 .driver = {
1031 .name = "88pm860x-battery",
1032 .owner = THIS_MODULE,
1033 .pm = &pm860x_battery_pm_ops,
1034 },
1035 .probe = pm860x_battery_probe,
1036 .remove = __devexit_p(pm860x_battery_remove),
1037};
1038module_platform_driver(pm860x_battery_driver);
1039
1040MODULE_DESCRIPTION("Marvell 88PM860x Battery driver");
1041MODULE_LICENSE("GPL");
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");
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 4ec2ff0ee4a2..9d8047735841 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -62,6 +62,12 @@ config TEST_POWER
62 help 62 help
63 This driver is used for testing. It's safe to say M here. 63 This driver is used for testing. It's safe to say M here.
64 64
65config BATTERY_88PM860X
66 tristate "Marvell 88PM860x battery driver"
67 depends on MFD_88PM860X
68 help
69 Say Y here to enable battery monitor for Marvell 88PM860x chip.
70
65config BATTERY_DS2760 71config BATTERY_DS2760
66 tristate "DS2760 battery driver (HP iPAQ & others)" 72 tristate "DS2760 battery driver (HP iPAQ & others)"
67 depends on W1 && W1_SLAVE_DS2760 73 depends on W1 && W1_SLAVE_DS2760
@@ -202,6 +208,12 @@ config BATTERY_S3C_ADC
202 help 208 help
203 Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery 209 Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery
204 210
211config CHARGER_88PM860X
212 tristate "Marvell 88PM860x Charger driver"
213 depends on MFD_88PM860X && BATTERY_88PM860X
214 help
215 Say Y here to enable charger for Marvell 88PM860x chip.
216
205config CHARGER_PCF50633 217config CHARGER_PCF50633
206 tristate "NXP PCF50633 MBC" 218 tristate "NXP PCF50633 MBC"
207 depends on MFD_PCF50633 219 depends on MFD_PCF50633
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 587c5f174626..059c0a931526 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
14obj-$(CONFIG_WM8350_POWER) += wm8350_power.o 14obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
15obj-$(CONFIG_TEST_POWER) += test_power.o 15obj-$(CONFIG_TEST_POWER) += test_power.o
16 16
17obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
17obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o 18obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
18obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o 19obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
19obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o 20obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
@@ -31,6 +32,7 @@ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
31obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o 32obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
32obj-$(CONFIG_BATTERY_Z2) += z2_battery.o 33obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
33obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o 34obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o
35obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
34obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o 36obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
35obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o 37obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
36obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o 38obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o