blob: eb98eed010527d94a10651ff982506b2960c515c [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
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700288static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
289{
290 int rc;
291 int16_t reading;
292
293 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
294 if (rc) {
295 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
296 return rc;
297 }
298 *result = CONV_READING(reading);
299 pr_debug("read = %04x vsense = %d\n", reading, *result);
300 return 0;
301}
302
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
304{
305 if (y0 == y1 || x == x0)
306 return y0;
307 if (x1 == x0 || x == x1)
308 return y1;
309
310 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
311}
312
313static int interpolate_single_lut(struct single_row_lut *lut, int x)
314{
315 int i, result;
316
317 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700318 pr_debug("x %d less than known range return y = %d lut = %pS\n",
319 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 return lut->y[0];
321 }
322 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700323 pr_debug("x %d more than known range return y = %d lut = %pS\n",
324 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 return lut->y[lut->cols - 1];
326 }
327
328 for (i = 0; i < lut->cols; i++)
329 if (x <= lut->x[i])
330 break;
331 if (x == lut->x[i]) {
332 result = lut->y[i];
333 } else {
334 result = linear_interpolate(
335 lut->y[i - 1],
336 lut->x[i - 1],
337 lut->y[i],
338 lut->x[i],
339 x);
340 }
341 return result;
342}
343
344static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
345{
346 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
347}
348
349static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
350 int cycles)
351{
352 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
353}
354
355static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
356 int cycles, int pc)
357{
358 int i, scalefactorrow1, scalefactorrow2, scalefactor, row1, row2;
359 int rows = chip->pc_sf_lut->rows;
360 int cols = chip->pc_sf_lut->cols;
361
362 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700363 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 row1 = 0;
365 row2 = 0;
366 }
367 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700368 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 row1 = rows - 1;
370 row2 = rows - 1;
371 }
372 for (i = 0; i < rows; i++) {
373 if (pc == chip->pc_sf_lut->percent[i]) {
374 row1 = i;
375 row2 = i;
376 break;
377 }
378 if (pc > chip->pc_sf_lut->percent[i]) {
379 row1 = i - 1;
380 row2 = i;
381 break;
382 }
383 }
384
385 if (cycles < chip->pc_sf_lut->cycles[0])
386 cycles = chip->pc_sf_lut->cycles[0];
387 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
388 cycles = chip->pc_sf_lut->cycles[cols - 1];
389
390 for (i = 0; i < cols; i++)
391 if (cycles <= chip->pc_sf_lut->cycles[i])
392 break;
393 if (cycles == chip->pc_sf_lut->cycles[i]) {
394 scalefactor = linear_interpolate(
395 chip->pc_sf_lut->sf[row1][i],
396 chip->pc_sf_lut->percent[row1],
397 chip->pc_sf_lut->sf[row2][i],
398 chip->pc_sf_lut->percent[row2],
399 pc);
400 return scalefactor;
401 }
402
403 scalefactorrow1 = linear_interpolate(
404 chip->pc_sf_lut->sf[row1][i - 1],
405 chip->pc_sf_lut->cycles[i - 1],
406 chip->pc_sf_lut->sf[row1][i],
407 chip->pc_sf_lut->cycles[i],
408 cycles);
409
410 scalefactorrow2 = linear_interpolate(
411 chip->pc_sf_lut->sf[row2][i - 1],
412 chip->pc_sf_lut->cycles[i - 1],
413 chip->pc_sf_lut->sf[row2][i],
414 chip->pc_sf_lut->cycles[i],
415 cycles);
416
417 scalefactor = linear_interpolate(
418 scalefactorrow1,
419 chip->pc_sf_lut->percent[row1],
420 scalefactorrow2,
421 chip->pc_sf_lut->percent[row2],
422 pc);
423
424 return scalefactor;
425}
426
427static int interpolate_pc(struct pm8921_bms_chip *chip,
428 int batt_temp, int ocv)
429{
430 int i, j, ocvi, ocviplusone, pc = 0;
431 int rows = chip->pc_temp_ocv_lut->rows;
432 int cols = chip->pc_temp_ocv_lut->cols;
433
434 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700435 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436 batt_temp = chip->pc_temp_ocv_lut->temp[0];
437 }
438 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700439 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
441 }
442
443 for (j = 0; j < cols; j++)
444 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
445 break;
446 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
447 /* found an exact match for temp in the table */
448 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
449 return chip->pc_temp_ocv_lut->percent[0];
450 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
451 return chip->pc_temp_ocv_lut->percent[rows - 1];
452 for (i = 0; i < rows; i++) {
453 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
454 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
455 return
456 chip->pc_temp_ocv_lut->percent[i];
457 pc = linear_interpolate(
458 chip->pc_temp_ocv_lut->percent[i],
459 chip->pc_temp_ocv_lut->ocv[i][j],
460 chip->pc_temp_ocv_lut->percent[i - 1],
461 chip->pc_temp_ocv_lut->ocv[i - 1][j],
462 ocv);
463 return pc;
464 }
465 }
466 }
467
468 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
469 return chip->pc_temp_ocv_lut->percent[0];
470 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
471 return chip->pc_temp_ocv_lut->percent[rows - 1];
472 for (i = 0; i < rows; i++) {
473 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j - 1]
474 && ocv <= chip->pc_temp_ocv_lut->ocv[i][j]) {
475 pc = chip->pc_temp_ocv_lut->percent[i];
476
477 if (i < rows - 1
478 && ocv >=
479 chip->pc_temp_ocv_lut->ocv[i + 1][j - 1]
480 && ocv <=
481 chip->pc_temp_ocv_lut->ocv[i + 1][j]) {
482 ocvi = linear_interpolate(
483 chip->pc_temp_ocv_lut->ocv[i][j - 1],
484 chip->pc_temp_ocv_lut->temp[j - 1],
485 chip->pc_temp_ocv_lut->ocv[i][j],
486 chip->pc_temp_ocv_lut->temp[j],
487 batt_temp);
488
489 ocviplusone = linear_interpolate(
490 chip->pc_temp_ocv_lut
491 ->ocv[i + 1][j - 1],
492 chip->pc_temp_ocv_lut->temp[j - 1],
493 chip->pc_temp_ocv_lut->ocv[i + 1][j],
494 chip->pc_temp_ocv_lut->temp[j],
495 batt_temp);
496
497 pc = linear_interpolate(
498 chip->pc_temp_ocv_lut->percent[i],
499 ocvi,
500 chip->pc_temp_ocv_lut->percent[i + 1],
501 ocviplusone,
502 ocv);
503 }
504 return pc;
505 }
506 }
507
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700508 pr_debug("%d ocv wasn't found for temp %d in the LUT returning pc = %d",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509 ocv, batt_temp, pc);
510 return pc;
511}
512
513static int calculate_rbatt(struct pm8921_bms_chip *chip)
514{
515 int rc;
516 unsigned int ocv, vsense, vbatt, r_batt;
517
518 rc = read_ocv_for_rbatt(chip, &ocv);
519 if (rc) {
520 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
521 ocv = 0;
522 }
523 rc = read_vbatt_for_rbatt(chip, &vbatt);
524 if (rc) {
525 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
526 ocv = 0;
527 }
528 rc = read_vsense_for_rbatt(chip, &vsense);
529 if (rc) {
530 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
531 ocv = 0;
532 }
533 if (ocv == 0
534 || ocv == vbatt
535 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700536 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
537 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 ocv, vbatt, vsense);
539 return -EINVAL;
540 }
541 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
542 pr_debug("r_batt = %umilliOhms", r_batt);
543 return r_batt;
544}
545
546static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
547 int chargecycles)
548{
549 int initfcc, result, scalefactor = 0;
550
551 initfcc = interpolate_fcc(chip, batt_temp);
552 pr_debug("intfcc = %umAh batt_temp = %d\n", initfcc, batt_temp);
553
554 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
555 pr_debug("scalefactor = %d batt_temp = %d\n", scalefactor, batt_temp);
556
557 /* Multiply the initial FCC value by the scale factor. */
558 result = (initfcc * scalefactor) / 100;
559 pr_debug("fcc mAh = %d\n", result);
560 return result;
561}
562
563static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
564 int chargecycles)
565{
566 int pc, scalefactor;
567
568 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
569 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
570 pc, ocv_uv, batt_temp);
571
572 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
573 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
574
575 /* Multiply the initial FCC value by the scale factor. */
576 pc = (pc * scalefactor) / 100;
577 return pc;
578}
579
580#define CC_TO_MICROVOLT(cc) div_s64(cc * 1085069, 100000);
581#define CCMICROVOLT_TO_UVH(cc_uv) div_s64(cc_uv * 55, 32768 * 3600)
582
583static void calculate_cc_mvh(struct pm8921_bms_chip *chip, int64_t *val,
584 int *coulumb_counter, int *update_userspace)
585{
586 int rc;
587 int64_t cc_voltage_uv, cc_uvh, cc_mah;
588
589 rc = read_cc(the_chip, coulumb_counter);
590 if (rc) {
591 *coulumb_counter = (last_coulumb_counter < 0) ?
592 DEFAULT_COULUMB_COUNTER : last_coulumb_counter;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700593 pr_debug("couldn't read coulumb counter err = %d assuming %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594 rc, *coulumb_counter);
595 *update_userspace = 0;
596 }
597 cc_voltage_uv = (int64_t)*coulumb_counter;
598 cc_voltage_uv = CC_TO_MICROVOLT(cc_voltage_uv);
599 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
600 cc_uvh = CCMICROVOLT_TO_UVH(cc_voltage_uv);
601 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
602 cc_mah = div_s64(cc_uvh, chip->r_sense);
603 *val = cc_mah;
604}
605
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700606static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
607{
608 int rc;
609 struct pm8921_adc_chan_result result;
610
611 rc = pm8921_adc_read(chip->vbat_channel, &result);
612 if (rc) {
613 pr_err("error reading adc channel = %d, rc = %d\n",
614 chip->vbat_channel, rc);
615 return rc;
616 }
617 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
618 result.measurement);
619 *uvolts = (int)result.physical;
620 *uvolts = *uvolts * 1000;
621 return 0;
622}
623
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
625 int batt_temp, int chargecycles)
626{
627 int remaining_usable_charge, fcc, unusable_charge;
628 int remaining_charge, soc, rc, ocv, pc, coulumb_counter;
629 int rbatt, voltage_unusable_uv, pc_unusable;
630 int update_userspace = 1;
631 int64_t cc_mah;
632
633 rbatt = calculate_rbatt(chip);
634 if (rbatt < 0) {
635 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700636 pr_debug("rbatt unavailable assuming %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637 rbatt);
638 update_userspace = 0;
639 }
640 pr_debug("rbatt = %umilliOhms", rbatt);
641
642 fcc = calculate_fcc(chip, batt_temp, chargecycles);
643 if (fcc < -EINVAL) {
644 fcc = (last_fcc < 0) ? chip->fcc : last_fcc;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700645 pr_debug("failed to read fcc assuming %d\n", fcc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700646 update_userspace = 0;
647 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700648 pr_debug("FCC = %umAh", fcc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649
650 /* calculate unusable charge */
651 voltage_unusable_uv = (rbatt * chip->i_test)
652 + (chip->v_failure * 1000);
653 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
654 batt_temp, chargecycles);
655 if (pc_unusable < 0) {
656 unusable_charge = (last_unusable_charge < 0) ?
657 DEFAULT_UNUSABLE_CHARGE_MAH : last_unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700658 pr_debug("unusable_charge failed assuming %d\n",
659 unusable_charge);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660 } else {
661 unusable_charge = (fcc * pc_unusable) / 100;
662 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700663 pr_debug("UUC = %umAh at temp = %d, fcc = %umAh"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664 "unusable_voltage = %umicroVolts pc_unusable = %d\n",
665 unusable_charge, batt_temp, fcc,
666 voltage_unusable_uv, pc_unusable);
667
668 /* calculate remainging charge */
669 rc = read_last_good_ocv(chip, &ocv);
670 if (rc || ocv == 0) {
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700671 rc = get_battery_uvolts(chip, &ocv);
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700672 pr_debug("ocv not available adc vbat = %d rc = %d\n", ocv, rc);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700673 if (rc) {
674 ocv = (last_ocv_uv < 0) ?
675 DEFAULT_OCV_MICROVOLTS : last_ocv_uv;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700676 pr_debug("adc ocv failed assuming %d rc = %d\n",
677 ocv, rc);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700678 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679 update_userspace = 0;
680 }
681 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
682 if (pc < 0) {
683 remaining_charge = (last_remaining_charge < 0) ?
684 DEFAULT_REMAINING_CHARGE_MAH : last_remaining_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700685 pr_debug("calculate remaining charge failed assuming %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 remaining_charge);
687 update_userspace = 0;
688 } else {
689 remaining_charge = (fcc * pc) / 100;
690 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700691 pr_debug("RC = %umAh ocv = %d pc = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692 remaining_charge, ocv, pc);
693
694 /* calculate cc milli_volt_hour */
695 calculate_cc_mvh(chip, &cc_mah, &coulumb_counter, &update_userspace);
696 pr_debug("cc_mah = %lldmAh cc = %d\n", cc_mah, coulumb_counter);
697
698 /* calculate remaining usable charge */
699 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700700 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701 if (remaining_usable_charge < 0) {
702 pr_err("bad rem_usb_chg cc_mah %lld, rem_chg %d unusb_chg %d\n",
703 cc_mah, remaining_charge, unusable_charge);
704 update_userspace = 0;
705 }
706
707 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
708 if (soc > 100 || soc < 0) {
709 pr_err("bad soc rem_usb_chg %d fcc %d unusb_chg %d\n",
710 remaining_usable_charge, fcc, unusable_charge);
711 update_userspace = 0;
712 }
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700713 pr_debug("SOC = %u%%\n", soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714
715 if (update_userspace) {
716 last_rbatt = rbatt;
717 last_fcc = fcc;
718 last_unusable_charge = unusable_charge;
719 last_ocv_uv = ocv;
720 last_remaining_charge = remaining_charge;
721 last_coulumb_counter = coulumb_counter;
722 last_soc = soc;
723 }
724 return soc;
725}
726
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700727int pm8921_bms_get_vsense_avg(int *result)
728{
729 if (the_chip)
730 return read_vsense_avg(the_chip, result);
731
732 pr_err("called before initialization\n");
733 return -EINVAL;
734}
735EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
736
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700737int pm8921_bms_get_percent_charge(void)
738{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700739 int batt_temp, rc;
740 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700742 if (!the_chip) {
743 pr_err("called before initialization\n");
744 return -EINVAL;
745 }
746
747 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
748 if (rc) {
749 pr_err("error reading adc channel = %d, rc = %d\n",
750 the_chip->batt_temp_channel, rc);
751 return rc;
752 }
753 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
754 result.measurement);
755 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 return calculate_state_of_charge(the_chip,
757 batt_temp, last_chargecycles);
758}
759EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
760
761static int start_percent;
762static int end_percent;
763void pm8921_bms_charging_began(void)
764{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700765 start_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766 pr_debug("start_percent = %u%%\n", start_percent);
767}
768EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
769
770void pm8921_bms_charging_end(void)
771{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700772 end_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700773 if (end_percent > start_percent) {
774 last_charge_increase = end_percent - start_percent;
775 if (last_charge_increase > 100) {
776 last_chargecycles++;
777 last_charge_increase = last_charge_increase % 100;
778 }
779 }
780 pr_debug("end_percent = %u%% last_charge_increase = %d"
781 "last_chargecycles = %d\n",
782 end_percent,
783 last_charge_increase,
784 last_chargecycles);
785}
786EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
787
788static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
789{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700790 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700791 return IRQ_HANDLED;
792}
793
794static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
795{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700796 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700797 return IRQ_HANDLED;
798}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700799
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700800static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
801{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700802 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 return IRQ_HANDLED;
804}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700805
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700806static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
807{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700808 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700809 return IRQ_HANDLED;
810}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700811
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
813{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700814
815 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816 return IRQ_HANDLED;
817}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700818
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
820{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700821
822 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700823 return IRQ_HANDLED;
824}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700825
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
827{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700828 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829 return IRQ_HANDLED;
830}
831
832struct pm_bms_irq_init_data {
833 unsigned int irq_id;
834 char *name;
835 unsigned long flags;
836 irqreturn_t (*handler)(int, void *);
837};
838
839#define BMS_IRQ(_id, _flags, _handler) \
840{ \
841 .irq_id = _id, \
842 .name = #_id, \
843 .flags = _flags, \
844 .handler = _handler, \
845}
846
847struct pm_bms_irq_init_data bms_irq_data[] = {
848 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
849 pm8921_bms_sbi_write_ok_handler),
850 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
851 pm8921_bms_cc_thr_handler),
852 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
853 pm8921_bms_vsense_thr_handler),
854 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
855 pm8921_bms_vsense_for_r_handler),
856 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
857 pm8921_bms_ocv_for_r_handler),
858 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
859 pm8921_bms_good_ocv_handler),
860 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
861 pm8921_bms_vsense_avg_handler),
862};
863
864static void free_irqs(struct pm8921_bms_chip *chip)
865{
866 int i;
867
868 for (i = 0; i < PM_BMS_MAX_INTS; i++)
869 if (chip->pmic_bms_irq[i]) {
870 free_irq(chip->pmic_bms_irq[i], NULL);
871 chip->pmic_bms_irq[i] = 0;
872 }
873}
874
875static int __devinit request_irqs(struct pm8921_bms_chip *chip,
876 struct platform_device *pdev)
877{
878 struct resource *res;
879 int ret, i;
880
881 ret = 0;
882 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
883
884 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
885 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
886 bms_irq_data[i].name);
887 if (res == NULL) {
888 pr_err("couldn't find %s\n", bms_irq_data[i].name);
889 goto err_out;
890 }
891 ret = request_irq(res->start, bms_irq_data[i].handler,
892 bms_irq_data[i].flags,
893 bms_irq_data[i].name, chip);
894 if (ret < 0) {
895 pr_err("couldn't request %d (%s) %d\n", res->start,
896 bms_irq_data[i].name, ret);
897 goto err_out;
898 }
899 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
900 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
901 }
902 return 0;
903
904err_out:
905 free_irqs(chip);
906 return -EINVAL;
907}
908
909#define EN_BMS_BIT BIT(7)
910#define EN_PON_HS_BIT BIT(0)
911static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
912{
913 int rc;
914
915 rc = pm_bms_masked_write(chip, BMS_CONTROL,
916 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
917 if (rc) {
918 pr_err("failed to enable pon and bms addr = %d %d",
919 BMS_CONTROL, rc);
920 }
921
922 return 0;
923}
924
925enum {
926 CALC_RBATT,
927 CALC_FCC,
928 CALC_PC,
929 CALC_SOC,
930};
931
932static int test_batt_temp = 5;
933static int test_chargecycle = 150;
934static int test_ocv = 3900000;
935enum {
936 TEST_BATT_TEMP,
937 TEST_CHARGE_CYCLE,
938 TEST_OCV,
939};
940static int get_test_param(void *data, u64 * val)
941{
942 switch ((int)data) {
943 case TEST_BATT_TEMP:
944 *val = test_batt_temp;
945 break;
946 case TEST_CHARGE_CYCLE:
947 *val = test_chargecycle;
948 break;
949 case TEST_OCV:
950 *val = test_ocv;
951 break;
952 default:
953 return -EINVAL;
954 }
955 return 0;
956}
957static int set_test_param(void *data, u64 val)
958{
959 switch ((int)data) {
960 case TEST_BATT_TEMP:
961 test_batt_temp = (int)val;
962 break;
963 case TEST_CHARGE_CYCLE:
964 test_chargecycle = (int)val;
965 break;
966 case TEST_OCV:
967 test_ocv = (int)val;
968 break;
969 default:
970 return -EINVAL;
971 }
972 return 0;
973}
974DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
975
976static int get_calc(void *data, u64 * val)
977{
978 int param = (int)data;
979 int ret = 0;
980
981 *val = 0;
982
983 /* global irq number passed in via data */
984 switch (param) {
985 case CALC_RBATT:
986 *val = calculate_rbatt(the_chip);
987 break;
988 case CALC_FCC:
989 *val = calculate_fcc(the_chip, test_batt_temp,
990 test_chargecycle);
991 break;
992 case CALC_PC:
993 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
994 test_chargecycle);
995 break;
996 case CALC_SOC:
997 *val = calculate_state_of_charge(the_chip,
998 test_batt_temp, test_chargecycle);
999 break;
1000 default:
1001 ret = -EINVAL;
1002 }
1003 return ret;
1004}
1005DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
1006
1007static int get_reading(void *data, u64 * val)
1008{
1009 int param = (int)data;
1010 int ret = 0;
1011
1012 *val = 0;
1013
1014 /* global irq number passed in via data */
1015 switch (param) {
1016 case CC_MSB:
1017 case CC_LSB:
1018 read_cc(the_chip, (int *)val);
1019 break;
1020 case LAST_GOOD_OCV_VALUE:
1021 read_last_good_ocv(the_chip, (uint *)val);
1022 break;
1023 case VBATT_FOR_RBATT:
1024 read_vbatt_for_rbatt(the_chip, (uint *)val);
1025 break;
1026 case VSENSE_FOR_RBATT:
1027 read_vsense_for_rbatt(the_chip, (uint *)val);
1028 break;
1029 case OCV_FOR_RBATT:
1030 read_ocv_for_rbatt(the_chip, (uint *)val);
1031 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001032 case VSENSE_AVG:
1033 read_vsense_avg(the_chip, (uint *)val);
1034 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001035 default:
1036 ret = -EINVAL;
1037 }
1038 return ret;
1039}
1040DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%llu\n");
1041
1042static int get_rt_status(void *data, u64 * val)
1043{
1044 int i = (int)data;
1045 int ret;
1046
1047 /* global irq number passed in via data */
1048 ret = pm_bms_get_rt_status(the_chip, i);
1049 *val = ret;
1050 return 0;
1051}
1052DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
1053
1054static int get_reg(void *data, u64 * val)
1055{
1056 int addr = (int)data;
1057 int ret;
1058 u8 temp;
1059
1060 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
1061 if (ret) {
1062 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
1063 addr, temp, ret);
1064 return -EAGAIN;
1065 }
1066 *val = temp;
1067 return 0;
1068}
1069
1070static int set_reg(void *data, u64 val)
1071{
1072 int addr = (int)data;
1073 int ret;
1074 u8 temp;
1075
1076 temp = (u8) val;
1077 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
1078 if (ret) {
1079 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
1080 addr, temp, ret);
1081 return -EAGAIN;
1082 }
1083 return 0;
1084}
1085DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
1086
1087static void create_debugfs_entries(struct pm8921_bms_chip *chip)
1088{
1089 int i;
1090
1091 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
1092
1093 if (IS_ERR(chip->dent)) {
1094 pr_err("pmic bms couldnt create debugfs dir\n");
1095 return;
1096 }
1097
1098 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
1099 (void *)BMS_CONTROL, &reg_fops);
1100 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
1101 (void *)BMS_OUTPUT0, &reg_fops);
1102 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
1103 (void *)BMS_OUTPUT1, &reg_fops);
1104 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
1105 (void *)BMS_TEST1, &reg_fops);
1106 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
1107 (void *)CCADC_ANA_PARAM, &reg_fops);
1108 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
1109 (void *)CCADC_DIG_PARAM, &reg_fops);
1110 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
1111 (void *)CCADC_RSV, &reg_fops);
1112 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
1113 (void *)CCADC_DATA0, &reg_fops);
1114 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
1115 (void *)CCADC_DATA1, &reg_fops);
1116 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
1117 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
1118 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
1119 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
1120
1121 debugfs_create_file("test_batt_temp", 0644, chip->dent,
1122 (void *)TEST_BATT_TEMP, &temp_fops);
1123 debugfs_create_file("test_chargecycle", 0644, chip->dent,
1124 (void *)TEST_CHARGE_CYCLE, &temp_fops);
1125 debugfs_create_file("test_ocv", 0644, chip->dent,
1126 (void *)TEST_OCV, &temp_fops);
1127
1128 debugfs_create_file("read_cc", 0644, chip->dent,
1129 (void *)CC_MSB, &reading_fops);
1130 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
1131 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
1132 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
1133 (void *)VBATT_FOR_RBATT, &reading_fops);
1134 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
1135 (void *)VSENSE_FOR_RBATT, &reading_fops);
1136 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
1137 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001138 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
1139 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001140
1141 debugfs_create_file("show_rbatt", 0644, chip->dent,
1142 (void *)CALC_RBATT, &calc_fops);
1143 debugfs_create_file("show_fcc", 0644, chip->dent,
1144 (void *)CALC_FCC, &calc_fops);
1145 debugfs_create_file("show_pc", 0644, chip->dent,
1146 (void *)CALC_PC, &calc_fops);
1147 debugfs_create_file("show_soc", 0644, chip->dent,
1148 (void *)CALC_SOC, &calc_fops);
1149
1150 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1151 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
1152 debugfs_create_file(bms_irq_data[i].name, 0444,
1153 chip->dent,
1154 (void *)bms_irq_data[i].irq_id,
1155 &rt_fops);
1156 }
1157}
1158
1159static void calibrate_work(struct work_struct *work)
1160{
1161 /* TODO */
1162}
1163
1164static int __devinit pm8921_bms_probe(struct platform_device *pdev)
1165{
1166 int rc = 0;
1167 struct pm8921_bms_chip *chip;
1168 const struct pm8921_bms_platform_data *pdata
1169 = pdev->dev.platform_data;
1170 if (!pdata) {
1171 pr_err("missing platform data\n");
1172 return -EINVAL;
1173 }
1174
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001175 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001176 if (!chip) {
1177 pr_err("Cannot allocate pm_bms_chip\n");
1178 return -ENOMEM;
1179 }
1180
1181 chip->dev = &pdev->dev;
1182 chip->r_sense = pdata->r_sense;
1183 chip->i_test = pdata->i_test;
1184 chip->v_failure = pdata->v_failure;
1185 chip->calib_delay_ms = pdata->calib_delay_ms;
1186 chip->fcc = pdata->batt_data->fcc;
1187
1188 chip->fcc_temp_lut = pdata->batt_data->fcc_temp_lut;
1189 chip->fcc_sf_lut = pdata->batt_data->fcc_sf_lut;
1190 chip->pc_temp_ocv_lut = pdata->batt_data->pc_temp_ocv_lut;
1191 chip->pc_sf_lut = pdata->batt_data->pc_sf_lut;
1192
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001193 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
1194 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
1195
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001196 rc = pm8921_bms_hw_init(chip);
1197 if (rc) {
1198 pr_err("couldn't init hardware rc = %d\n", rc);
1199 goto free_chip;
1200 }
1201
1202 rc = request_irqs(chip, pdev);
1203 if (rc) {
1204 pr_err("couldn't register interrupts rc = %d\n", rc);
1205 goto free_chip;
1206 }
1207
1208 platform_set_drvdata(pdev, chip);
1209 the_chip = chip;
1210 create_debugfs_entries(chip);
1211
1212 INIT_DELAYED_WORK(&chip->calib_work, calibrate_work);
1213 schedule_delayed_work(&chip->calib_work,
1214 round_jiffies_relative(msecs_to_jiffies
1215 (chip->calib_delay_ms)));
1216 return 0;
1217
1218free_chip:
1219 kfree(chip);
1220 return rc;
1221}
1222
1223static int __devexit pm8921_bms_remove(struct platform_device *pdev)
1224{
1225 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
1226
1227 free_irqs(chip);
1228 platform_set_drvdata(pdev, NULL);
1229 the_chip = NULL;
1230 kfree(chip);
1231 return 0;
1232}
1233
1234static struct platform_driver pm8921_bms_driver = {
1235 .probe = pm8921_bms_probe,
1236 .remove = __devexit_p(pm8921_bms_remove),
1237 .driver = {
1238 .name = PM8921_BMS_DEV_NAME,
1239 .owner = THIS_MODULE,
1240 },
1241};
1242
1243static int __init pm8921_bms_init(void)
1244{
1245 return platform_driver_register(&pm8921_bms_driver);
1246}
1247
1248static void __exit pm8921_bms_exit(void)
1249{
1250 platform_driver_unregister(&pm8921_bms_driver);
1251}
1252
1253late_initcall(pm8921_bms_init);
1254module_exit(pm8921_bms_exit);
1255
1256MODULE_LICENSE("GPL v2");
1257MODULE_DESCRIPTION("PMIC8921 bms driver");
1258MODULE_VERSION("1.0");
1259MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);