blob: e6e59de565fc60f70920de023b49759a38f38f94 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/platform_device.h>
18#include <linux/errno.h>
19#include <linux/mfd/pm8xxx/pm8921-bms.h>
20#include <linux/mfd/pm8xxx/core.h>
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -070021#include <linux/mfd/pm8921-adc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <linux/interrupt.h>
23#include <linux/bitops.h>
24#include <linux/debugfs.h>
25#include <linux/slab.h>
26
27#define BMS_CONTROL 0x224
28#define BMS_OUTPUT0 0x230
29#define BMS_OUTPUT1 0x231
30#define BMS_TEST1 0x237
31#define CCADC_ANA_PARAM 0x240
32#define CCADC_DIG_PARAM 0x241
33#define CCADC_RSV 0x242
34#define CCADC_DATA0 0x244
35#define CCADC_DATA1 0x245
36#define CCADC_OFFSET_TRIM1 0x34A
37#define CCADC_OFFSET_TRIM0 0x34B
38
39#define ADC_ARB_SECP_CNTRL 0x190
40#define ADC_ARB_SECP_AMUX_CNTRL 0x191
41#define ADC_ARB_SECP_ANA_PARAM 0x192
42#define ADC_ARB_SECP_RSV 0x194
43#define ADC_ARB_SECP_DATA1 0x195
44#define ADC_ARB_SECP_DATA0 0x196
45
46enum pmic_bms_interrupts {
47 PM8921_BMS_SBI_WRITE_OK,
48 PM8921_BMS_CC_THR,
49 PM8921_BMS_VSENSE_THR,
50 PM8921_BMS_VSENSE_FOR_R,
51 PM8921_BMS_OCV_FOR_R,
52 PM8921_BMS_GOOD_OCV,
53 PM8921_BMS_VSENSE_AVG,
54 PM_BMS_MAX_INTS,
55};
56
57/**
58 * struct pm8921_bms_chip -device information
59 * @dev: device pointer to access the parent
60 * @dent: debugfs directory
61 * @r_sense: batt sense resistance value
62 * @i_test: peak current
63 * @v_failure: battery dead voltage
64 * @fcc: battery capacity
65 *
66 */
67struct pm8921_bms_chip {
68 struct device *dev;
69 struct dentry *dent;
70 unsigned int r_sense;
71 unsigned int i_test;
72 unsigned int v_failure;
73 unsigned int fcc;
74 struct single_row_lut *fcc_temp_lut;
75 struct single_row_lut *fcc_sf_lut;
76 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
77 struct pc_sf_lut *pc_sf_lut;
78 struct delayed_work calib_work;
79 unsigned int calib_delay_ms;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -070080 unsigned int batt_temp_channel;
81 unsigned int vbat_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
83 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
84};
85
86static struct pm8921_bms_chip *the_chip;
87
88#define DEFAULT_RBATT_MOHMS 128
89#define DEFAULT_UNUSABLE_CHARGE_MAH 10
90#define DEFAULT_OCV_MICROVOLTS 3900000
91#define DEFAULT_REMAINING_CHARGE_MAH 990
92#define DEFAULT_COULUMB_COUNTER 0
93#define DEFAULT_CHARGE_CYCLES 0
94
95static int last_rbatt = -EINVAL;
96static int last_fcc = -EINVAL;
97static int last_unusable_charge = -EINVAL;
98static int last_ocv_uv = -EINVAL;
99static int last_remaining_charge = -EINVAL;
100static int last_coulumb_counter = -EINVAL;
101static int last_soc = -EINVAL;
102
103static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
104static int last_charge_increase;
105
106module_param(last_rbatt, int, 0644);
107module_param(last_fcc, int, 0644);
108module_param(last_unusable_charge, int, 0644);
109module_param(last_ocv_uv, int, 0644);
110module_param(last_remaining_charge, int, 0644);
111module_param(last_coulumb_counter, int, 0644);
112module_param(last_chargecycles, int, 0644);
113module_param(last_charge_increase, int, 0644);
114
115static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
116{
117 return pm8xxx_read_irq_stat(chip->dev->parent,
118 chip->pmic_bms_irq[irq_id]);
119}
120
121static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
122{
123 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700124 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
126 }
127}
128
129static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
130 u8 mask, u8 val)
131{
132 int rc;
133 u8 reg;
134
135 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
136 if (rc) {
137 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
138 return rc;
139 }
140 reg &= ~mask;
141 reg |= val & mask;
142 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
143 if (rc) {
144 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
145 return rc;
146 }
147 return 0;
148}
149
150#define SELECT_OUTPUT_DATA 0x1C
151#define SELECT_OUTPUT_TYPE_SHIFT 2
152#define OCV_FOR_RBATT 0x0
153#define VSENSE_FOR_RBATT 0x1
154#define VBATT_FOR_RBATT 0x2
155#define CC_MSB 0x3
156#define CC_LSB 0x4
157#define LAST_GOOD_OCV_VALUE 0x5
158#define VSENSE_AVG 0x6
159#define VBATT_AVG 0x7
160
161static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
162 int16_t *result)
163{
164 int rc;
165 u8 reg;
166
167 if (!result) {
168 pr_err("result pointer null\n");
169 return -EINVAL;
170 }
171 *result = 0;
172 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
173 pr_err("invalid type %d asked to read\n", type);
174 return -EINVAL;
175 }
176 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
177 type << SELECT_OUTPUT_TYPE_SHIFT);
178 if (rc) {
179 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
180 type, rc);
181 return rc;
182 }
183
184 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
185 if (rc) {
186 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
187 type, rc);
188 return rc;
189 }
190 *result = reg;
191 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
192 if (rc) {
193 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
194 type, rc);
195 return rc;
196 }
197 *result |= reg << 8;
198 pr_debug("type %d result %x", type, *result);
199 return 0;
200}
201
202#define V_PER_BIT_MUL_FACTOR 977
203#define V_PER_BIT_DIV_FACTOR 10
204#define CONV_READING(a) (((a) * (int)V_PER_BIT_MUL_FACTOR)\
205 /V_PER_BIT_DIV_FACTOR)
206
207/* returns the signed value read from the hardware */
208static int read_cc(struct pm8921_bms_chip *chip, int *result)
209{
210 int rc;
211 uint16_t msw, lsw;
212
213 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
214 if (rc) {
215 pr_err("fail to read CC_LSB rc = %d\n", rc);
216 return rc;
217 }
218 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
219 if (rc) {
220 pr_err("fail to read CC_MSB rc = %d\n", rc);
221 return rc;
222 }
223 *result = msw << 16 | lsw;
224 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
225 return 0;
226}
227
228static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
229{
230 int rc;
231 uint16_t reading;
232
233 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
234 if (rc) {
235 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
236 return rc;
237 }
238 *result = CONV_READING(reading);
239 pr_debug("raw = %04x ocv_microV = %u\n", reading, *result);
240 return 0;
241}
242
243static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
244{
245 int rc;
246 uint16_t reading;
247
248 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
249 if (rc) {
250 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
251 return rc;
252 }
253 *result = CONV_READING(reading);
254 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
255 return 0;
256}
257
258static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
259{
260 int rc;
261 uint16_t reading;
262
263 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
264 if (rc) {
265 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
266 return rc;
267 }
268 *result = CONV_READING(reading);
269 pr_debug("raw = %04x vsense_for_r_microV = %u\n", reading, *result);
270 return 0;
271}
272
273static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
274{
275 int rc;
276 uint16_t reading;
277
278 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
279 if (rc) {
280 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
281 return rc;
282 }
283 *result = CONV_READING(reading);
284 pr_debug("read = %04x ocv_for_r_microV = %u\n", reading, *result);
285 return 0;
286}
287
288static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
289{
290 if (y0 == y1 || x == x0)
291 return y0;
292 if (x1 == x0 || x == x1)
293 return y1;
294
295 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
296}
297
298static int interpolate_single_lut(struct single_row_lut *lut, int x)
299{
300 int i, result;
301
302 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700303 pr_debug("x %d less than known range return y = %d lut = %pS\n",
304 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 return lut->y[0];
306 }
307 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700308 pr_debug("x %d more than known range return y = %d lut = %pS\n",
309 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 return lut->y[lut->cols - 1];
311 }
312
313 for (i = 0; i < lut->cols; i++)
314 if (x <= lut->x[i])
315 break;
316 if (x == lut->x[i]) {
317 result = lut->y[i];
318 } else {
319 result = linear_interpolate(
320 lut->y[i - 1],
321 lut->x[i - 1],
322 lut->y[i],
323 lut->x[i],
324 x);
325 }
326 return result;
327}
328
329static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
330{
331 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
332}
333
334static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
335 int cycles)
336{
337 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
338}
339
340static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
341 int cycles, int pc)
342{
343 int i, scalefactorrow1, scalefactorrow2, scalefactor, row1, row2;
344 int rows = chip->pc_sf_lut->rows;
345 int cols = chip->pc_sf_lut->cols;
346
347 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700348 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 row1 = 0;
350 row2 = 0;
351 }
352 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700353 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354 row1 = rows - 1;
355 row2 = rows - 1;
356 }
357 for (i = 0; i < rows; i++) {
358 if (pc == chip->pc_sf_lut->percent[i]) {
359 row1 = i;
360 row2 = i;
361 break;
362 }
363 if (pc > chip->pc_sf_lut->percent[i]) {
364 row1 = i - 1;
365 row2 = i;
366 break;
367 }
368 }
369
370 if (cycles < chip->pc_sf_lut->cycles[0])
371 cycles = chip->pc_sf_lut->cycles[0];
372 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
373 cycles = chip->pc_sf_lut->cycles[cols - 1];
374
375 for (i = 0; i < cols; i++)
376 if (cycles <= chip->pc_sf_lut->cycles[i])
377 break;
378 if (cycles == chip->pc_sf_lut->cycles[i]) {
379 scalefactor = linear_interpolate(
380 chip->pc_sf_lut->sf[row1][i],
381 chip->pc_sf_lut->percent[row1],
382 chip->pc_sf_lut->sf[row2][i],
383 chip->pc_sf_lut->percent[row2],
384 pc);
385 return scalefactor;
386 }
387
388 scalefactorrow1 = linear_interpolate(
389 chip->pc_sf_lut->sf[row1][i - 1],
390 chip->pc_sf_lut->cycles[i - 1],
391 chip->pc_sf_lut->sf[row1][i],
392 chip->pc_sf_lut->cycles[i],
393 cycles);
394
395 scalefactorrow2 = linear_interpolate(
396 chip->pc_sf_lut->sf[row2][i - 1],
397 chip->pc_sf_lut->cycles[i - 1],
398 chip->pc_sf_lut->sf[row2][i],
399 chip->pc_sf_lut->cycles[i],
400 cycles);
401
402 scalefactor = linear_interpolate(
403 scalefactorrow1,
404 chip->pc_sf_lut->percent[row1],
405 scalefactorrow2,
406 chip->pc_sf_lut->percent[row2],
407 pc);
408
409 return scalefactor;
410}
411
412static int interpolate_pc(struct pm8921_bms_chip *chip,
413 int batt_temp, int ocv)
414{
415 int i, j, ocvi, ocviplusone, pc = 0;
416 int rows = chip->pc_temp_ocv_lut->rows;
417 int cols = chip->pc_temp_ocv_lut->cols;
418
419 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700420 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421 batt_temp = chip->pc_temp_ocv_lut->temp[0];
422 }
423 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700424 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
426 }
427
428 for (j = 0; j < cols; j++)
429 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
430 break;
431 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
432 /* found an exact match for temp in the table */
433 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
434 return chip->pc_temp_ocv_lut->percent[0];
435 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
436 return chip->pc_temp_ocv_lut->percent[rows - 1];
437 for (i = 0; i < rows; i++) {
438 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
439 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
440 return
441 chip->pc_temp_ocv_lut->percent[i];
442 pc = linear_interpolate(
443 chip->pc_temp_ocv_lut->percent[i],
444 chip->pc_temp_ocv_lut->ocv[i][j],
445 chip->pc_temp_ocv_lut->percent[i - 1],
446 chip->pc_temp_ocv_lut->ocv[i - 1][j],
447 ocv);
448 return pc;
449 }
450 }
451 }
452
453 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
454 return chip->pc_temp_ocv_lut->percent[0];
455 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
456 return chip->pc_temp_ocv_lut->percent[rows - 1];
457 for (i = 0; i < rows; i++) {
458 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j - 1]
459 && ocv <= chip->pc_temp_ocv_lut->ocv[i][j]) {
460 pc = chip->pc_temp_ocv_lut->percent[i];
461
462 if (i < rows - 1
463 && ocv >=
464 chip->pc_temp_ocv_lut->ocv[i + 1][j - 1]
465 && ocv <=
466 chip->pc_temp_ocv_lut->ocv[i + 1][j]) {
467 ocvi = linear_interpolate(
468 chip->pc_temp_ocv_lut->ocv[i][j - 1],
469 chip->pc_temp_ocv_lut->temp[j - 1],
470 chip->pc_temp_ocv_lut->ocv[i][j],
471 chip->pc_temp_ocv_lut->temp[j],
472 batt_temp);
473
474 ocviplusone = linear_interpolate(
475 chip->pc_temp_ocv_lut
476 ->ocv[i + 1][j - 1],
477 chip->pc_temp_ocv_lut->temp[j - 1],
478 chip->pc_temp_ocv_lut->ocv[i + 1][j],
479 chip->pc_temp_ocv_lut->temp[j],
480 batt_temp);
481
482 pc = linear_interpolate(
483 chip->pc_temp_ocv_lut->percent[i],
484 ocvi,
485 chip->pc_temp_ocv_lut->percent[i + 1],
486 ocviplusone,
487 ocv);
488 }
489 return pc;
490 }
491 }
492
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700493 pr_debug("%d ocv wasn't found for temp %d in the LUT returning pc = %d",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494 ocv, batt_temp, pc);
495 return pc;
496}
497
498static int calculate_rbatt(struct pm8921_bms_chip *chip)
499{
500 int rc;
501 unsigned int ocv, vsense, vbatt, r_batt;
502
503 rc = read_ocv_for_rbatt(chip, &ocv);
504 if (rc) {
505 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
506 ocv = 0;
507 }
508 rc = read_vbatt_for_rbatt(chip, &vbatt);
509 if (rc) {
510 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
511 ocv = 0;
512 }
513 rc = read_vsense_for_rbatt(chip, &vsense);
514 if (rc) {
515 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
516 ocv = 0;
517 }
518 if (ocv == 0
519 || ocv == vbatt
520 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700521 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
522 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523 ocv, vbatt, vsense);
524 return -EINVAL;
525 }
526 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
527 pr_debug("r_batt = %umilliOhms", r_batt);
528 return r_batt;
529}
530
531static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
532 int chargecycles)
533{
534 int initfcc, result, scalefactor = 0;
535
536 initfcc = interpolate_fcc(chip, batt_temp);
537 pr_debug("intfcc = %umAh batt_temp = %d\n", initfcc, batt_temp);
538
539 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
540 pr_debug("scalefactor = %d batt_temp = %d\n", scalefactor, batt_temp);
541
542 /* Multiply the initial FCC value by the scale factor. */
543 result = (initfcc * scalefactor) / 100;
544 pr_debug("fcc mAh = %d\n", result);
545 return result;
546}
547
548static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
549 int chargecycles)
550{
551 int pc, scalefactor;
552
553 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
554 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
555 pc, ocv_uv, batt_temp);
556
557 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
558 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
559
560 /* Multiply the initial FCC value by the scale factor. */
561 pc = (pc * scalefactor) / 100;
562 return pc;
563}
564
565#define CC_TO_MICROVOLT(cc) div_s64(cc * 1085069, 100000);
566#define CCMICROVOLT_TO_UVH(cc_uv) div_s64(cc_uv * 55, 32768 * 3600)
567
568static void calculate_cc_mvh(struct pm8921_bms_chip *chip, int64_t *val,
569 int *coulumb_counter, int *update_userspace)
570{
571 int rc;
572 int64_t cc_voltage_uv, cc_uvh, cc_mah;
573
574 rc = read_cc(the_chip, coulumb_counter);
575 if (rc) {
576 *coulumb_counter = (last_coulumb_counter < 0) ?
577 DEFAULT_COULUMB_COUNTER : last_coulumb_counter;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700578 pr_debug("couldn't read coulumb counter err = %d assuming %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579 rc, *coulumb_counter);
580 *update_userspace = 0;
581 }
582 cc_voltage_uv = (int64_t)*coulumb_counter;
583 cc_voltage_uv = CC_TO_MICROVOLT(cc_voltage_uv);
584 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
585 cc_uvh = CCMICROVOLT_TO_UVH(cc_voltage_uv);
586 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
587 cc_mah = div_s64(cc_uvh, chip->r_sense);
588 *val = cc_mah;
589}
590
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700591static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
592{
593 int rc;
594 struct pm8921_adc_chan_result result;
595
596 rc = pm8921_adc_read(chip->vbat_channel, &result);
597 if (rc) {
598 pr_err("error reading adc channel = %d, rc = %d\n",
599 chip->vbat_channel, rc);
600 return rc;
601 }
602 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
603 result.measurement);
604 *uvolts = (int)result.physical;
605 *uvolts = *uvolts * 1000;
606 return 0;
607}
608
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700609static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
610 int batt_temp, int chargecycles)
611{
612 int remaining_usable_charge, fcc, unusable_charge;
613 int remaining_charge, soc, rc, ocv, pc, coulumb_counter;
614 int rbatt, voltage_unusable_uv, pc_unusable;
615 int update_userspace = 1;
616 int64_t cc_mah;
617
618 rbatt = calculate_rbatt(chip);
619 if (rbatt < 0) {
620 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700621 pr_debug("rbatt unavailable assuming %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622 rbatt);
623 update_userspace = 0;
624 }
625 pr_debug("rbatt = %umilliOhms", rbatt);
626
627 fcc = calculate_fcc(chip, batt_temp, chargecycles);
628 if (fcc < -EINVAL) {
629 fcc = (last_fcc < 0) ? chip->fcc : last_fcc;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700630 pr_debug("failed to read fcc assuming %d\n", fcc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700631 update_userspace = 0;
632 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700633 pr_debug("FCC = %umAh", fcc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634
635 /* calculate unusable charge */
636 voltage_unusable_uv = (rbatt * chip->i_test)
637 + (chip->v_failure * 1000);
638 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
639 batt_temp, chargecycles);
640 if (pc_unusable < 0) {
641 unusable_charge = (last_unusable_charge < 0) ?
642 DEFAULT_UNUSABLE_CHARGE_MAH : last_unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700643 pr_debug("unusable_charge failed assuming %d\n",
644 unusable_charge);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700645 } else {
646 unusable_charge = (fcc * pc_unusable) / 100;
647 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700648 pr_debug("UUC = %umAh at temp = %d, fcc = %umAh"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 "unusable_voltage = %umicroVolts pc_unusable = %d\n",
650 unusable_charge, batt_temp, fcc,
651 voltage_unusable_uv, pc_unusable);
652
653 /* calculate remainging charge */
654 rc = read_last_good_ocv(chip, &ocv);
655 if (rc || ocv == 0) {
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700656 rc = get_battery_uvolts(chip, &ocv);
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700657 pr_debug("ocv not available adc vbat = %d rc = %d\n", ocv, rc);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700658 if (rc) {
659 ocv = (last_ocv_uv < 0) ?
660 DEFAULT_OCV_MICROVOLTS : last_ocv_uv;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700661 pr_debug("adc ocv failed assuming %d rc = %d\n",
662 ocv, rc);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700663 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664 update_userspace = 0;
665 }
666 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
667 if (pc < 0) {
668 remaining_charge = (last_remaining_charge < 0) ?
669 DEFAULT_REMAINING_CHARGE_MAH : last_remaining_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700670 pr_debug("calculate remaining charge failed assuming %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700671 remaining_charge);
672 update_userspace = 0;
673 } else {
674 remaining_charge = (fcc * pc) / 100;
675 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700676 pr_debug("RC = %umAh ocv = %d pc = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677 remaining_charge, ocv, pc);
678
679 /* calculate cc milli_volt_hour */
680 calculate_cc_mvh(chip, &cc_mah, &coulumb_counter, &update_userspace);
681 pr_debug("cc_mah = %lldmAh cc = %d\n", cc_mah, coulumb_counter);
682
683 /* calculate remaining usable charge */
684 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700685 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 if (remaining_usable_charge < 0) {
687 pr_err("bad rem_usb_chg cc_mah %lld, rem_chg %d unusb_chg %d\n",
688 cc_mah, remaining_charge, unusable_charge);
689 update_userspace = 0;
690 }
691
692 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
693 if (soc > 100 || soc < 0) {
694 pr_err("bad soc rem_usb_chg %d fcc %d unusb_chg %d\n",
695 remaining_usable_charge, fcc, unusable_charge);
696 update_userspace = 0;
697 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700698 pr_debug("SOC = %u%%\n", soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699
700 if (update_userspace) {
701 last_rbatt = rbatt;
702 last_fcc = fcc;
703 last_unusable_charge = unusable_charge;
704 last_ocv_uv = ocv;
705 last_remaining_charge = remaining_charge;
706 last_coulumb_counter = coulumb_counter;
707 last_soc = soc;
708 }
709 return soc;
710}
711
712int pm8921_bms_get_percent_charge(void)
713{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700714 int batt_temp, rc;
715 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700717 if (!the_chip) {
718 pr_err("called before initialization\n");
719 return -EINVAL;
720 }
721
722 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
723 if (rc) {
724 pr_err("error reading adc channel = %d, rc = %d\n",
725 the_chip->batt_temp_channel, rc);
726 return rc;
727 }
728 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
729 result.measurement);
730 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731 return calculate_state_of_charge(the_chip,
732 batt_temp, last_chargecycles);
733}
734EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
735
736static int start_percent;
737static int end_percent;
738void pm8921_bms_charging_began(void)
739{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700740 start_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741 pr_debug("start_percent = %u%%\n", start_percent);
742}
743EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
744
745void pm8921_bms_charging_end(void)
746{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700747 end_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748 if (end_percent > start_percent) {
749 last_charge_increase = end_percent - start_percent;
750 if (last_charge_increase > 100) {
751 last_chargecycles++;
752 last_charge_increase = last_charge_increase % 100;
753 }
754 }
755 pr_debug("end_percent = %u%% last_charge_increase = %d"
756 "last_chargecycles = %d\n",
757 end_percent,
758 last_charge_increase,
759 last_chargecycles);
760}
761EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
762
763static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
764{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700765 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766 return IRQ_HANDLED;
767}
768
769static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
770{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700771 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700772 return IRQ_HANDLED;
773}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700774
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700775static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
776{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700777 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778 return IRQ_HANDLED;
779}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700780
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700781static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
782{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700783 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784 return IRQ_HANDLED;
785}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700786
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700787static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
788{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700789
790 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700791 return IRQ_HANDLED;
792}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700793
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
795{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700796
797 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700798 return IRQ_HANDLED;
799}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700800
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
802{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700803 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700804 return IRQ_HANDLED;
805}
806
807struct pm_bms_irq_init_data {
808 unsigned int irq_id;
809 char *name;
810 unsigned long flags;
811 irqreturn_t (*handler)(int, void *);
812};
813
814#define BMS_IRQ(_id, _flags, _handler) \
815{ \
816 .irq_id = _id, \
817 .name = #_id, \
818 .flags = _flags, \
819 .handler = _handler, \
820}
821
822struct pm_bms_irq_init_data bms_irq_data[] = {
823 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
824 pm8921_bms_sbi_write_ok_handler),
825 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
826 pm8921_bms_cc_thr_handler),
827 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
828 pm8921_bms_vsense_thr_handler),
829 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
830 pm8921_bms_vsense_for_r_handler),
831 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
832 pm8921_bms_ocv_for_r_handler),
833 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
834 pm8921_bms_good_ocv_handler),
835 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
836 pm8921_bms_vsense_avg_handler),
837};
838
839static void free_irqs(struct pm8921_bms_chip *chip)
840{
841 int i;
842
843 for (i = 0; i < PM_BMS_MAX_INTS; i++)
844 if (chip->pmic_bms_irq[i]) {
845 free_irq(chip->pmic_bms_irq[i], NULL);
846 chip->pmic_bms_irq[i] = 0;
847 }
848}
849
850static int __devinit request_irqs(struct pm8921_bms_chip *chip,
851 struct platform_device *pdev)
852{
853 struct resource *res;
854 int ret, i;
855
856 ret = 0;
857 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
858
859 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
860 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
861 bms_irq_data[i].name);
862 if (res == NULL) {
863 pr_err("couldn't find %s\n", bms_irq_data[i].name);
864 goto err_out;
865 }
866 ret = request_irq(res->start, bms_irq_data[i].handler,
867 bms_irq_data[i].flags,
868 bms_irq_data[i].name, chip);
869 if (ret < 0) {
870 pr_err("couldn't request %d (%s) %d\n", res->start,
871 bms_irq_data[i].name, ret);
872 goto err_out;
873 }
874 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
875 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
876 }
877 return 0;
878
879err_out:
880 free_irqs(chip);
881 return -EINVAL;
882}
883
884#define EN_BMS_BIT BIT(7)
885#define EN_PON_HS_BIT BIT(0)
886static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
887{
888 int rc;
889
890 rc = pm_bms_masked_write(chip, BMS_CONTROL,
891 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
892 if (rc) {
893 pr_err("failed to enable pon and bms addr = %d %d",
894 BMS_CONTROL, rc);
895 }
896
897 return 0;
898}
899
900enum {
901 CALC_RBATT,
902 CALC_FCC,
903 CALC_PC,
904 CALC_SOC,
905};
906
907static int test_batt_temp = 5;
908static int test_chargecycle = 150;
909static int test_ocv = 3900000;
910enum {
911 TEST_BATT_TEMP,
912 TEST_CHARGE_CYCLE,
913 TEST_OCV,
914};
915static int get_test_param(void *data, u64 * val)
916{
917 switch ((int)data) {
918 case TEST_BATT_TEMP:
919 *val = test_batt_temp;
920 break;
921 case TEST_CHARGE_CYCLE:
922 *val = test_chargecycle;
923 break;
924 case TEST_OCV:
925 *val = test_ocv;
926 break;
927 default:
928 return -EINVAL;
929 }
930 return 0;
931}
932static int set_test_param(void *data, u64 val)
933{
934 switch ((int)data) {
935 case TEST_BATT_TEMP:
936 test_batt_temp = (int)val;
937 break;
938 case TEST_CHARGE_CYCLE:
939 test_chargecycle = (int)val;
940 break;
941 case TEST_OCV:
942 test_ocv = (int)val;
943 break;
944 default:
945 return -EINVAL;
946 }
947 return 0;
948}
949DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
950
951static int get_calc(void *data, u64 * val)
952{
953 int param = (int)data;
954 int ret = 0;
955
956 *val = 0;
957
958 /* global irq number passed in via data */
959 switch (param) {
960 case CALC_RBATT:
961 *val = calculate_rbatt(the_chip);
962 break;
963 case CALC_FCC:
964 *val = calculate_fcc(the_chip, test_batt_temp,
965 test_chargecycle);
966 break;
967 case CALC_PC:
968 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
969 test_chargecycle);
970 break;
971 case CALC_SOC:
972 *val = calculate_state_of_charge(the_chip,
973 test_batt_temp, test_chargecycle);
974 break;
975 default:
976 ret = -EINVAL;
977 }
978 return ret;
979}
980DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
981
982static int get_reading(void *data, u64 * val)
983{
984 int param = (int)data;
985 int ret = 0;
986
987 *val = 0;
988
989 /* global irq number passed in via data */
990 switch (param) {
991 case CC_MSB:
992 case CC_LSB:
993 read_cc(the_chip, (int *)val);
994 break;
995 case LAST_GOOD_OCV_VALUE:
996 read_last_good_ocv(the_chip, (uint *)val);
997 break;
998 case VBATT_FOR_RBATT:
999 read_vbatt_for_rbatt(the_chip, (uint *)val);
1000 break;
1001 case VSENSE_FOR_RBATT:
1002 read_vsense_for_rbatt(the_chip, (uint *)val);
1003 break;
1004 case OCV_FOR_RBATT:
1005 read_ocv_for_rbatt(the_chip, (uint *)val);
1006 break;
1007 default:
1008 ret = -EINVAL;
1009 }
1010 return ret;
1011}
1012DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%llu\n");
1013
1014static int get_rt_status(void *data, u64 * val)
1015{
1016 int i = (int)data;
1017 int ret;
1018
1019 /* global irq number passed in via data */
1020 ret = pm_bms_get_rt_status(the_chip, i);
1021 *val = ret;
1022 return 0;
1023}
1024DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
1025
1026static int get_reg(void *data, u64 * val)
1027{
1028 int addr = (int)data;
1029 int ret;
1030 u8 temp;
1031
1032 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
1033 if (ret) {
1034 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
1035 addr, temp, ret);
1036 return -EAGAIN;
1037 }
1038 *val = temp;
1039 return 0;
1040}
1041
1042static int set_reg(void *data, u64 val)
1043{
1044 int addr = (int)data;
1045 int ret;
1046 u8 temp;
1047
1048 temp = (u8) val;
1049 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
1050 if (ret) {
1051 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
1052 addr, temp, ret);
1053 return -EAGAIN;
1054 }
1055 return 0;
1056}
1057DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
1058
1059static void create_debugfs_entries(struct pm8921_bms_chip *chip)
1060{
1061 int i;
1062
1063 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
1064
1065 if (IS_ERR(chip->dent)) {
1066 pr_err("pmic bms couldnt create debugfs dir\n");
1067 return;
1068 }
1069
1070 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
1071 (void *)BMS_CONTROL, &reg_fops);
1072 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
1073 (void *)BMS_OUTPUT0, &reg_fops);
1074 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
1075 (void *)BMS_OUTPUT1, &reg_fops);
1076 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
1077 (void *)BMS_TEST1, &reg_fops);
1078 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
1079 (void *)CCADC_ANA_PARAM, &reg_fops);
1080 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
1081 (void *)CCADC_DIG_PARAM, &reg_fops);
1082 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
1083 (void *)CCADC_RSV, &reg_fops);
1084 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
1085 (void *)CCADC_DATA0, &reg_fops);
1086 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
1087 (void *)CCADC_DATA1, &reg_fops);
1088 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
1089 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
1090 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
1091 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
1092
1093 debugfs_create_file("test_batt_temp", 0644, chip->dent,
1094 (void *)TEST_BATT_TEMP, &temp_fops);
1095 debugfs_create_file("test_chargecycle", 0644, chip->dent,
1096 (void *)TEST_CHARGE_CYCLE, &temp_fops);
1097 debugfs_create_file("test_ocv", 0644, chip->dent,
1098 (void *)TEST_OCV, &temp_fops);
1099
1100 debugfs_create_file("read_cc", 0644, chip->dent,
1101 (void *)CC_MSB, &reading_fops);
1102 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
1103 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
1104 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
1105 (void *)VBATT_FOR_RBATT, &reading_fops);
1106 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
1107 (void *)VSENSE_FOR_RBATT, &reading_fops);
1108 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
1109 (void *)OCV_FOR_RBATT, &reading_fops);
1110
1111 debugfs_create_file("show_rbatt", 0644, chip->dent,
1112 (void *)CALC_RBATT, &calc_fops);
1113 debugfs_create_file("show_fcc", 0644, chip->dent,
1114 (void *)CALC_FCC, &calc_fops);
1115 debugfs_create_file("show_pc", 0644, chip->dent,
1116 (void *)CALC_PC, &calc_fops);
1117 debugfs_create_file("show_soc", 0644, chip->dent,
1118 (void *)CALC_SOC, &calc_fops);
1119
1120 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1121 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
1122 debugfs_create_file(bms_irq_data[i].name, 0444,
1123 chip->dent,
1124 (void *)bms_irq_data[i].irq_id,
1125 &rt_fops);
1126 }
1127}
1128
1129static void calibrate_work(struct work_struct *work)
1130{
1131 /* TODO */
1132}
1133
1134static int __devinit pm8921_bms_probe(struct platform_device *pdev)
1135{
1136 int rc = 0;
1137 struct pm8921_bms_chip *chip;
1138 const struct pm8921_bms_platform_data *pdata
1139 = pdev->dev.platform_data;
1140 if (!pdata) {
1141 pr_err("missing platform data\n");
1142 return -EINVAL;
1143 }
1144
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001145 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001146 if (!chip) {
1147 pr_err("Cannot allocate pm_bms_chip\n");
1148 return -ENOMEM;
1149 }
1150
1151 chip->dev = &pdev->dev;
1152 chip->r_sense = pdata->r_sense;
1153 chip->i_test = pdata->i_test;
1154 chip->v_failure = pdata->v_failure;
1155 chip->calib_delay_ms = pdata->calib_delay_ms;
1156 chip->fcc = pdata->batt_data->fcc;
1157
1158 chip->fcc_temp_lut = pdata->batt_data->fcc_temp_lut;
1159 chip->fcc_sf_lut = pdata->batt_data->fcc_sf_lut;
1160 chip->pc_temp_ocv_lut = pdata->batt_data->pc_temp_ocv_lut;
1161 chip->pc_sf_lut = pdata->batt_data->pc_sf_lut;
1162
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001163 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
1164 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
1165
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166 rc = pm8921_bms_hw_init(chip);
1167 if (rc) {
1168 pr_err("couldn't init hardware rc = %d\n", rc);
1169 goto free_chip;
1170 }
1171
1172 rc = request_irqs(chip, pdev);
1173 if (rc) {
1174 pr_err("couldn't register interrupts rc = %d\n", rc);
1175 goto free_chip;
1176 }
1177
1178 platform_set_drvdata(pdev, chip);
1179 the_chip = chip;
1180 create_debugfs_entries(chip);
1181
1182 INIT_DELAYED_WORK(&chip->calib_work, calibrate_work);
1183 schedule_delayed_work(&chip->calib_work,
1184 round_jiffies_relative(msecs_to_jiffies
1185 (chip->calib_delay_ms)));
1186 return 0;
1187
1188free_chip:
1189 kfree(chip);
1190 return rc;
1191}
1192
1193static int __devexit pm8921_bms_remove(struct platform_device *pdev)
1194{
1195 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
1196
1197 free_irqs(chip);
1198 platform_set_drvdata(pdev, NULL);
1199 the_chip = NULL;
1200 kfree(chip);
1201 return 0;
1202}
1203
1204static struct platform_driver pm8921_bms_driver = {
1205 .probe = pm8921_bms_probe,
1206 .remove = __devexit_p(pm8921_bms_remove),
1207 .driver = {
1208 .name = PM8921_BMS_DEV_NAME,
1209 .owner = THIS_MODULE,
1210 },
1211};
1212
1213static int __init pm8921_bms_init(void)
1214{
1215 return platform_driver_register(&pm8921_bms_driver);
1216}
1217
1218static void __exit pm8921_bms_exit(void)
1219{
1220 platform_driver_unregister(&pm8921_bms_driver);
1221}
1222
1223late_initcall(pm8921_bms_init);
1224module_exit(pm8921_bms_exit);
1225
1226MODULE_LICENSE("GPL v2");
1227MODULE_DESCRIPTION("PMIC8921 bms driver");
1228MODULE_VERSION("1.0");
1229MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);