blob: fdbc819a11f6375d37fd20cbbb388589201b556c [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>
Siddartha Mohanadoss77d106e2011-09-20 16:25:59 -070021#include <linux/mfd/pm8xxx/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
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070039#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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
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;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070078 struct work_struct calib_hkadc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079 unsigned int calib_delay_ms;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -070080 unsigned int revision;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070081 unsigned int xoadc_v0625;
82 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -070083 unsigned int batt_temp_channel;
84 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070085 unsigned int ref625mv_channel;
86 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -070087 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
89 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -070090 spinlock_t bms_output_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -070091 struct single_row_lut *adjusted_fcc_temp_lut;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092};
93
94static struct pm8921_bms_chip *the_chip;
95
96#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098#define DEFAULT_CHARGE_CYCLES 0
99
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700100static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
101static int last_charge_increase;
102module_param(last_chargecycles, int, 0644);
103module_param(last_charge_increase, int, 0644);
104
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107static int last_soc = -EINVAL;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700108static int last_real_fcc = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700109static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700111static int bms_ops_set(const char *val, const struct kernel_param *kp)
112{
113 if (*(int *)kp->arg == -EINVAL)
114 return param_set_int(val, kp);
115 else
116 return 0;
117}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700119static struct kernel_param_ops bms_param_ops = {
120 .set = bms_ops_set,
121 .get = param_get_int,
122};
123
124module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
125module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
126module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
127
128static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
129static void readjust_fcc_table(void)
130{
131 struct single_row_lut *temp, *old;
132 int i, fcc, ratio;
133
134 if (!the_chip->fcc_temp_lut) {
135 pr_err("The static fcc lut table is NULL\n");
136 return;
137 }
138
139 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
140 if (!temp) {
141 pr_err("Cannot allocate memory for adjusted fcc table\n");
142 return;
143 }
144
145 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
146
147 temp->cols = the_chip->fcc_temp_lut->cols;
148 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
149 temp->x[i] = the_chip->fcc_temp_lut->x[i];
150 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
151 temp->y[i] = (ratio * last_real_fcc);
152 temp->y[i] /= 1000;
153 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
154 temp->x[i], the_chip->fcc_temp_lut->y[i],
155 temp->y[i], ratio);
156 }
157
158 old = the_chip->adjusted_fcc_temp_lut;
159 the_chip->adjusted_fcc_temp_lut = temp;
160 kfree(old);
161}
162
163static int bms_last_real_fcc_set(const char *val,
164 const struct kernel_param *kp)
165{
166 int rc = 0;
167
168 if (last_real_fcc == -EINVAL)
169 rc = param_set_int(val, kp);
170 if (rc) {
171 pr_err("Failed to set last_real_fcc rc=%d\n", rc);
172 return rc;
173 }
174 if (last_real_fcc_batt_temp != -EINVAL)
175 readjust_fcc_table();
176 return rc;
177}
178static struct kernel_param_ops bms_last_real_fcc_param_ops = {
179 .set = bms_last_real_fcc_set,
180 .get = param_get_int,
181};
182module_param_cb(last_real_fcc, &bms_last_real_fcc_param_ops,
183 &last_real_fcc, 0644);
184
185static int bms_last_real_fcc_batt_temp_set(const char *val,
186 const struct kernel_param *kp)
187{
188 int rc = 0;
189
190 if (last_real_fcc_batt_temp == -EINVAL)
191 rc = param_set_int(val, kp);
192 if (rc) {
193 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
194 return rc;
195 }
196 if (last_real_fcc != -EINVAL)
197 readjust_fcc_table();
198 return rc;
199}
200
201static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
202 .set = bms_last_real_fcc_batt_temp_set,
203 .get = param_get_int,
204};
205module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
206 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207
208static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
209{
210 return pm8xxx_read_irq_stat(chip->dev->parent,
211 chip->pmic_bms_irq[irq_id]);
212}
213
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700214static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
215{
216 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
217 dev_dbg(chip->dev, "%s %d\n", __func__,
218 chip->pmic_bms_irq[interrupt]);
219 enable_irq(chip->pmic_bms_irq[interrupt]);
220 }
221}
222
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
224{
225 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700226 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
228 }
229}
230
231static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
232 u8 mask, u8 val)
233{
234 int rc;
235 u8 reg;
236
237 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
238 if (rc) {
239 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
240 return rc;
241 }
242 reg &= ~mask;
243 reg |= val & mask;
244 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
245 if (rc) {
246 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
247 return rc;
248 }
249 return 0;
250}
251
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700252#define HOLD_OREG_DATA BIT(1)
253static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
254{
255 int rc;
256
257 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
258 HOLD_OREG_DATA);
259 if (rc) {
260 pr_err("couldnt lock bms output rc = %d\n", rc);
261 return rc;
262 }
263 return 0;
264}
265
266static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
267{
268 int rc;
269
270 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
271 if (rc) {
272 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
273 return rc;
274 }
275 return 0;
276}
277
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278#define SELECT_OUTPUT_DATA 0x1C
279#define SELECT_OUTPUT_TYPE_SHIFT 2
280#define OCV_FOR_RBATT 0x0
281#define VSENSE_FOR_RBATT 0x1
282#define VBATT_FOR_RBATT 0x2
283#define CC_MSB 0x3
284#define CC_LSB 0x4
285#define LAST_GOOD_OCV_VALUE 0x5
286#define VSENSE_AVG 0x6
287#define VBATT_AVG 0x7
288
289static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
290 int16_t *result)
291{
292 int rc;
293 u8 reg;
294
295 if (!result) {
296 pr_err("result pointer null\n");
297 return -EINVAL;
298 }
299 *result = 0;
300 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
301 pr_err("invalid type %d asked to read\n", type);
302 return -EINVAL;
303 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700304
305 /* make sure the bms registers are locked */
306 rc = pm8xxx_readb(chip->dev->parent, BMS_CONTROL, &reg);
307 if (rc) {
308 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
309 type, rc);
310 return rc;
311 }
312
313 /* Output register data must be held (locked) while reading output */
314 WARN_ON(!(reg && HOLD_OREG_DATA));
315
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
317 type << SELECT_OUTPUT_TYPE_SHIFT);
318 if (rc) {
319 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
320 type, rc);
321 return rc;
322 }
323
324 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
325 if (rc) {
326 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
327 type, rc);
328 return rc;
329 }
330 *result = reg;
331 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
332 if (rc) {
333 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
334 type, rc);
335 return rc;
336 }
337 *result |= reg << 8;
338 pr_debug("type %d result %x", type, *result);
339 return 0;
340}
341
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700342#define V_PER_BIT_MUL_FACTOR 97656
343#define V_PER_BIT_DIV_FACTOR 1000
344#define XOADC_INTRINSIC_OFFSET 0x6000
345static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700346{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700347 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700348 return 0;
349
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700350 return (a - XOADC_INTRINSIC_OFFSET)
351 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700352}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700353
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700354#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700355#define VBATT_MUL_FACTOR 3
356static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
357 unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700358{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700359 u64 numerator, denominator;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700360
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700361 if (uv == 0)
362 return 0;
363
364 numerator = ((u64)uv - chip->xoadc_v0625) * XOADC_CALIB_UV;
365 denominator = chip->xoadc_v125 - chip->xoadc_v0625;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700366 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700367 return uv * VBATT_MUL_FACTOR;
368 return (XOADC_CALIB_UV + div_u64(numerator, denominator))
369 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700370}
371
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700372#define CC_RESOLUTION_N_V1 1085069
373#define CC_RESOLUTION_D_V1 100000
374#define CC_RESOLUTION_N_V2 868056
375#define CC_RESOLUTION_D_V2 10000
376static s64 cc_to_microvolt_v1(s64 cc)
377{
378 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
379}
380
381static s64 cc_to_microvolt_v2(s64 cc)
382{
383 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
384}
385
386static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
387{
388 /*
389 * resolution (the value of a single bit) was changed after revision 2.0
390 * for more accurate readings
391 */
392 return (chip->revision < PM8XXX_REVISION_8901_2p0) ?
393 cc_to_microvolt_v1((s64)cc) :
394 cc_to_microvolt_v2((s64)cc);
395}
396
397#define CC_READING_TICKS 55
398#define SLEEP_CLK_HZ 32768
399#define SECONDS_PER_HOUR 3600
400static s64 ccmicrovolt_to_uvh(s64 cc_uv)
401{
402 return div_s64(cc_uv * CC_READING_TICKS,
403 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
404}
405
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406/* returns the signed value read from the hardware */
407static int read_cc(struct pm8921_bms_chip *chip, int *result)
408{
409 int rc;
410 uint16_t msw, lsw;
411
412 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
413 if (rc) {
414 pr_err("fail to read CC_LSB rc = %d\n", rc);
415 return rc;
416 }
417 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
418 if (rc) {
419 pr_err("fail to read CC_MSB rc = %d\n", rc);
420 return rc;
421 }
422 *result = msw << 16 | lsw;
423 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
424 return 0;
425}
426
427static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
428{
429 int rc;
430 uint16_t reading;
431
432 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
433 if (rc) {
434 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
435 return rc;
436 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700437 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438 pr_debug("raw = %04x ocv_microV = %u\n", reading, *result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700439 if (*result != 0)
440 last_ocv_uv = *result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441 return 0;
442}
443
444static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
445{
446 int rc;
447 uint16_t reading;
448
449 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
450 if (rc) {
451 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
452 return rc;
453 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700454 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700456 *result = adjust_xo_vbatt_reading(chip, *result);
457 pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 return 0;
459}
460
461static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
462{
463 int rc;
464 uint16_t reading;
465
466 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
467 if (rc) {
468 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
469 return rc;
470 }
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700471 *result = cc_to_microvolt(chip, reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472 pr_debug("raw = %04x vsense_for_r_microV = %u\n", reading, *result);
473 return 0;
474}
475
476static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
477{
478 int rc;
479 uint16_t reading;
480
481 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
482 if (rc) {
483 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
484 return rc;
485 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700486 *result = xoadc_reading_to_microvolt(reading);
487 pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
488 *result = adjust_xo_vbatt_reading(chip, *result);
489 pr_debug("after adj ocv_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490 return 0;
491}
492
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700493static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
494{
495 int rc;
496 int16_t reading;
497
498 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
499 if (rc) {
500 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
501 return rc;
502 }
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700503 *result = cc_to_microvolt(chip, reading);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700504 pr_debug("read = %04x vsense = %d\n", reading, *result);
505 return 0;
506}
507
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
509{
510 if (y0 == y1 || x == x0)
511 return y0;
512 if (x1 == x0 || x == x1)
513 return y1;
514
515 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
516}
517
518static int interpolate_single_lut(struct single_row_lut *lut, int x)
519{
520 int i, result;
521
522 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700523 pr_debug("x %d less than known range return y = %d lut = %pS\n",
524 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 return lut->y[0];
526 }
527 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700528 pr_debug("x %d more than known range return y = %d lut = %pS\n",
529 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 return lut->y[lut->cols - 1];
531 }
532
533 for (i = 0; i < lut->cols; i++)
534 if (x <= lut->x[i])
535 break;
536 if (x == lut->x[i]) {
537 result = lut->y[i];
538 } else {
539 result = linear_interpolate(
540 lut->y[i - 1],
541 lut->x[i - 1],
542 lut->y[i],
543 lut->x[i],
544 x);
545 }
546 return result;
547}
548
549static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
550{
551 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
552}
553
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700554static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
555{
556 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
557}
558
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
560 int cycles)
561{
562 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
563}
564
565static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
566 int cycles, int pc)
567{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700568 int i, scalefactorrow1, scalefactorrow2, scalefactor;
569 int row1 = 0;
570 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 int rows = chip->pc_sf_lut->rows;
572 int cols = chip->pc_sf_lut->cols;
573
574 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700575 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576 row1 = 0;
577 row2 = 0;
578 }
579 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700580 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700581 row1 = rows - 1;
582 row2 = rows - 1;
583 }
584 for (i = 0; i < rows; i++) {
585 if (pc == chip->pc_sf_lut->percent[i]) {
586 row1 = i;
587 row2 = i;
588 break;
589 }
590 if (pc > chip->pc_sf_lut->percent[i]) {
591 row1 = i - 1;
592 row2 = i;
593 break;
594 }
595 }
596
597 if (cycles < chip->pc_sf_lut->cycles[0])
598 cycles = chip->pc_sf_lut->cycles[0];
599 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
600 cycles = chip->pc_sf_lut->cycles[cols - 1];
601
602 for (i = 0; i < cols; i++)
603 if (cycles <= chip->pc_sf_lut->cycles[i])
604 break;
605 if (cycles == chip->pc_sf_lut->cycles[i]) {
606 scalefactor = linear_interpolate(
607 chip->pc_sf_lut->sf[row1][i],
608 chip->pc_sf_lut->percent[row1],
609 chip->pc_sf_lut->sf[row2][i],
610 chip->pc_sf_lut->percent[row2],
611 pc);
612 return scalefactor;
613 }
614
615 scalefactorrow1 = linear_interpolate(
616 chip->pc_sf_lut->sf[row1][i - 1],
617 chip->pc_sf_lut->cycles[i - 1],
618 chip->pc_sf_lut->sf[row1][i],
619 chip->pc_sf_lut->cycles[i],
620 cycles);
621
622 scalefactorrow2 = linear_interpolate(
623 chip->pc_sf_lut->sf[row2][i - 1],
624 chip->pc_sf_lut->cycles[i - 1],
625 chip->pc_sf_lut->sf[row2][i],
626 chip->pc_sf_lut->cycles[i],
627 cycles);
628
629 scalefactor = linear_interpolate(
630 scalefactorrow1,
631 chip->pc_sf_lut->percent[row1],
632 scalefactorrow2,
633 chip->pc_sf_lut->percent[row2],
634 pc);
635
636 return scalefactor;
637}
638
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700639static int is_between(int left, int right, int value)
640{
641 if (left >= right && left >= value && value >= right)
642 return 1;
643 if (left <= right && left <= value && value <= right)
644 return 1;
645
646 return 0;
647}
648
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649static int interpolate_pc(struct pm8921_bms_chip *chip,
650 int batt_temp, int ocv)
651{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700652 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653 int rows = chip->pc_temp_ocv_lut->rows;
654 int cols = chip->pc_temp_ocv_lut->cols;
655
656 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700657 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 batt_temp = chip->pc_temp_ocv_lut->temp[0];
659 }
660 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700661 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700662 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
663 }
664
665 for (j = 0; j < cols; j++)
666 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
667 break;
668 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
669 /* found an exact match for temp in the table */
670 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
671 return chip->pc_temp_ocv_lut->percent[0];
672 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
673 return chip->pc_temp_ocv_lut->percent[rows - 1];
674 for (i = 0; i < rows; i++) {
675 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
676 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
677 return
678 chip->pc_temp_ocv_lut->percent[i];
679 pc = linear_interpolate(
680 chip->pc_temp_ocv_lut->percent[i],
681 chip->pc_temp_ocv_lut->ocv[i][j],
682 chip->pc_temp_ocv_lut->percent[i - 1],
683 chip->pc_temp_ocv_lut->ocv[i - 1][j],
684 ocv);
685 return pc;
686 }
687 }
688 }
689
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700690 /*
691 * batt_temp is within temperature for
692 * column j-1 and j
693 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700694 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
695 return chip->pc_temp_ocv_lut->percent[0];
696 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
697 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700698
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700699 pcj_minus_one = 0;
700 pcj = 0;
701 for (i = 0; i < rows-1; i++) {
702 if (pcj == 0
703 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
704 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
705 pcj = linear_interpolate(
706 chip->pc_temp_ocv_lut->percent[i],
707 chip->pc_temp_ocv_lut->ocv[i][j],
708 chip->pc_temp_ocv_lut->percent[i + 1],
709 chip->pc_temp_ocv_lut->ocv[i+1][j],
710 ocv);
711 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700712
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700713 if (pcj_minus_one == 0
714 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
715 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700717 pcj_minus_one = linear_interpolate(
718 chip->pc_temp_ocv_lut->percent[i],
719 chip->pc_temp_ocv_lut->ocv[i][j-1],
720 chip->pc_temp_ocv_lut->percent[i + 1],
721 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
722 ocv);
723 }
724
725 if (pcj && pcj_minus_one) {
726 pc = linear_interpolate(
727 pcj_minus_one,
728 chip->pc_temp_ocv_lut->temp[j-1],
729 pcj,
730 chip->pc_temp_ocv_lut->temp[j],
731 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732 return pc;
733 }
734 }
735
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700736 if (pcj)
737 return pcj;
738
739 if (pcj_minus_one)
740 return pcj_minus_one;
741
742 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
743 ocv, batt_temp);
744 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700745}
746
747static int calculate_rbatt(struct pm8921_bms_chip *chip)
748{
749 int rc;
750 unsigned int ocv, vsense, vbatt, r_batt;
751
752 rc = read_ocv_for_rbatt(chip, &ocv);
753 if (rc) {
754 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
755 ocv = 0;
756 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700757
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758 rc = read_vbatt_for_rbatt(chip, &vbatt);
759 if (rc) {
760 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
761 ocv = 0;
762 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700763
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700764 rc = read_vsense_for_rbatt(chip, &vsense);
765 if (rc) {
766 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
767 ocv = 0;
768 }
769 if (ocv == 0
770 || ocv == vbatt
771 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700772 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
773 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700774 ocv, vbatt, vsense);
775 return -EINVAL;
776 }
777 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700778 last_rbatt = r_batt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779 pr_debug("r_batt = %umilliOhms", r_batt);
780 return r_batt;
781}
782
783static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
784 int chargecycles)
785{
786 int initfcc, result, scalefactor = 0;
787
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700788 if (chip->adjusted_fcc_temp_lut == NULL) {
789 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700790
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700791 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700792
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700793 /* Multiply the initial FCC value by the scale factor. */
794 result = (initfcc * scalefactor) / 100;
795 pr_debug("fcc mAh = %d\n", result);
796 return result;
797 } else {
798 return interpolate_fcc_adjusted(chip, batt_temp);
799 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700800}
801
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700802static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
803{
804 int rc;
805 struct pm8921_adc_chan_result result;
806
807 rc = pm8921_adc_read(chip->vbat_channel, &result);
808 if (rc) {
809 pr_err("error reading adc channel = %d, rc = %d\n",
810 chip->vbat_channel, rc);
811 return rc;
812 }
813 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
814 result.measurement);
815 *uvolts = (int)result.physical;
816 *uvolts = *uvolts * 1000;
817 return 0;
818}
819
820static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
821{
822 int vbatt, rbatt, ibatt, rc;
823
824 rc = get_battery_uvolts(chip, &vbatt);
825 if (rc) {
826 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700827 return rc;
828 }
829
830 rc = pm8921_bms_get_battery_current(&ibatt);
831 if (rc) {
832 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700833 return rc;
834 }
835
836 rbatt = calculate_rbatt(the_chip);
837 if (rbatt < 0)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700838 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700839 *ocv = vbatt + ibatt * rbatt;
840 return 0;
841}
842
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700843static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
844 int chargecycles)
845{
846 int pc, scalefactor;
847
848 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
849 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
850 pc, ocv_uv, batt_temp);
851
852 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
853 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
854
855 /* Multiply the initial FCC value by the scale factor. */
856 pc = (pc * scalefactor) / 100;
857 return pc;
858}
859
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700860static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700861 int *coulumb_counter)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700862{
863 int rc;
864 int64_t cc_voltage_uv, cc_uvh, cc_mah;
865
866 rc = read_cc(the_chip, coulumb_counter);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867 cc_voltage_uv = (int64_t)*coulumb_counter;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700868 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700870 cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700871 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
872 cc_mah = div_s64(cc_uvh, chip->r_sense);
873 *val = cc_mah;
874}
875
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700876static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
877 int fcc, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700878{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700879 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880
881 rbatt = calculate_rbatt(chip);
882 if (rbatt < 0) {
883 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700884 pr_debug("rbatt unavailable assuming %d\n", rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700885 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700886
887 /* calculate unusable charge */
888 voltage_unusable_uv = (rbatt * chip->i_test)
889 + (chip->v_failure * 1000);
890 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
891 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700892 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
893 rbatt, voltage_unusable_uv, pc_unusable);
894 return (fcc * pc_unusable) / 100;
895}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700896
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700897/* calculate remainging charge at the time of ocv */
898static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
899 int batt_temp, int chargecycles)
900{
901 int rc, ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902
903 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700904 ocv = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700905 rc = read_last_good_ocv(chip, &ocv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700906 if (rc)
907 pr_debug("failed to read ocv rc = %d\n", rc);
908
909 if (ocv == 0) {
910 ocv = last_ocv_uv;
911 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700913
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700914 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700915 pr_debug("ocv = %d pc = %d\n", ocv, pc);
916 return (fcc * pc) / 100;
917}
918
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700919static void calculate_charging_params(struct pm8921_bms_chip *chip,
920 int batt_temp, int chargecycles,
921 int *fcc,
922 int *unusable_charge,
923 int *remaining_charge,
924 int64_t *cc_mah)
925{
926 int coulumb_counter;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700927 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700928
929 *fcc = calculate_fcc(chip, batt_temp, chargecycles);
930 pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
931 *fcc, batt_temp, chargecycles);
932
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700933 /* fcc doesnt need to be read from hardware, lock the bms now */
934 spin_lock_irqsave(&chip->bms_output_lock, flags);
935 pm_bms_lock_output_data(chip);
936
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700937 *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
938 batt_temp, chargecycles);
939
940 pr_debug("UUC = %umAh", *unusable_charge);
941
942 /* calculate remainging charge */
943 *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
944 batt_temp, chargecycles);
945 pr_debug("RC = %umAh\n", *remaining_charge);
946
947 /* calculate cc milli_volt_hour */
948 calculate_cc_mah(chip, cc_mah, &coulumb_counter);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700949
950 pm_bms_unlock_output_data(chip);
951 spin_unlock_irqrestore(&chip->bms_output_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700952 pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
953}
954
955static int calculate_real_fcc(struct pm8921_bms_chip *chip,
956 int batt_temp, int chargecycles)
957{
958 int fcc, unusable_charge;
959 int remaining_charge;
960 int64_t cc_mah;
961 int real_fcc;
962
963 calculate_charging_params(chip, batt_temp, chargecycles,
964 &fcc,
965 &unusable_charge,
966 &remaining_charge,
967 &cc_mah);
968
969 real_fcc = remaining_charge - cc_mah;
970
971 return real_fcc;
972}
973/*
974 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
975 * - coloumb counter charge
976 * - unusable charge (due to battery resistance)
977 * SOC% = (remaining usable charge/ fcc - usable_charge);
978 */
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700979#define BMS_BATT_NOMINAL 3700000
980#define MIN_OPERABLE_SOC 10
981#define BATTERY_POWER_SUPPLY_SOC 53
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700982static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
983 int batt_temp, int chargecycles)
984{
985 int remaining_usable_charge, fcc, unusable_charge;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700986 int remaining_charge, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700987 int update_userspace = 1;
988 int64_t cc_mah;
989
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700990 calculate_charging_params(chip, batt_temp, chargecycles,
991 &fcc,
992 &unusable_charge,
993 &remaining_charge,
994 &cc_mah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995
996 /* calculate remaining usable charge */
997 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700998 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700999 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
1000 if (soc > 100)
1001 soc = 100;
1002 pr_debug("SOC = %u%%\n", soc);
1003
1004 if (soc < MIN_OPERABLE_SOC) {
1005 int ocv = 0, rc;
1006
1007 rc = adc_based_ocv(chip, &ocv);
1008 if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
1009 /*
1010 * The ocv doesnt seem to have dropped for
1011 * soc to go negative.
1012 * The setup must be using a power supply
1013 * instead of real batteries.
1014 * Fake high enough soc to prevent userspace
1015 * shutdown for low battery
1016 */
1017 soc = BATTERY_POWER_SUPPLY_SOC;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001018 pr_debug("Adjusting SOC to %d\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001019 }
1020 }
1021
1022 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001023 pr_err("bad rem_usb_chg = %d rem_chg %d,"
1024 "cc_mah %lld, unusb_chg %d\n",
1025 remaining_usable_charge, remaining_charge,
1026 cc_mah, unusable_charge);
1027 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001028 "chargecycles = %d, batt_temp = %d"
1029 "fcc = %d soc =%d\n",
1030 last_ocv_uv, chargecycles, batt_temp,
1031 fcc, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001032 update_userspace = 0;
1033 }
1034
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001035 if (update_userspace) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036 last_soc = soc;
1037 }
1038 return soc;
1039}
1040
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001041#define XOADC_MAX_1P25V 1312500
1042#define XOADC_MIN_1P25V 1187500
1043#define XOADC_MAX_0P625V 656250
1044#define XOADC_MIN_0P625V 593750
1045
1046#define HKADC_V_PER_BIT_MUL_FACTOR 977
1047#define HKADC_V_PER_BIT_DIV_FACTOR 10
1048static int calib_hkadc_convert_microvolt(unsigned int phy)
1049{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001050 return (phy - 0x6000) *
1051 HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001052}
1053
1054static void calib_hkadc(struct pm8921_bms_chip *chip)
1055{
1056 int voltage, rc;
1057 struct pm8921_adc_chan_result result;
1058
1059 rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
1060 if (rc) {
1061 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1062 return;
1063 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001064 voltage = calib_hkadc_convert_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001065
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001066 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1067 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001068
1069 /* check for valid range */
1070 if (voltage > XOADC_MAX_1P25V)
1071 voltage = XOADC_MAX_1P25V;
1072 else if (voltage < XOADC_MIN_1P25V)
1073 voltage = XOADC_MIN_1P25V;
1074 chip->xoadc_v125 = voltage;
1075
1076 rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
1077 if (rc) {
1078 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1079 return;
1080 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001081 voltage = calib_hkadc_convert_microvolt(result.adc_code);
1082 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
1083 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001084 /* check for valid range */
1085 if (voltage > XOADC_MAX_0P625V)
1086 voltage = XOADC_MAX_0P625V;
1087 else if (voltage < XOADC_MIN_0P625V)
1088 voltage = XOADC_MIN_0P625V;
1089
1090 chip->xoadc_v0625 = voltage;
1091}
1092
1093static void calibrate_hkadc_work(struct work_struct *work)
1094{
1095 struct pm8921_bms_chip *chip = container_of(work,
1096 struct pm8921_bms_chip, calib_hkadc_work);
1097
1098 calib_hkadc(chip);
1099}
1100
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001101int pm8921_bms_get_vsense_avg(int *result)
1102{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001103 int rc = -EINVAL;
1104 unsigned long flags;
1105
1106 if (the_chip) {
1107 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1108 pm_bms_lock_output_data(the_chip);
1109 rc = read_vsense_avg(the_chip, result);
1110 pm_bms_unlock_output_data(the_chip);
1111 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1112 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001113
1114 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001115 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001116}
1117EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1118
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001119int pm8921_bms_get_battery_current(int *result)
1120{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001121 unsigned long flags;
1122
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001123 if (!the_chip) {
1124 pr_err("called before initialization\n");
1125 return -EINVAL;
1126 }
1127 if (the_chip->r_sense == 0) {
1128 pr_err("r_sense is zero\n");
1129 return -EINVAL;
1130 }
1131
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001132 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1133 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001134 read_vsense_avg(the_chip, result);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001135 pm_bms_unlock_output_data(the_chip);
1136 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001137 pr_debug("vsense=%d\n", *result);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001138 /* cast for signed division */
1139 *result = *result / (int)the_chip->r_sense;
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001140 return 0;
1141}
1142EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1143
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001144int pm8921_bms_get_percent_charge(void)
1145{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001146 int batt_temp, rc;
1147 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001148
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001149 if (!the_chip) {
1150 pr_err("called before initialization\n");
1151 return -EINVAL;
1152 }
1153
1154 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1155 if (rc) {
1156 pr_err("error reading adc channel = %d, rc = %d\n",
1157 the_chip->batt_temp_channel, rc);
1158 return rc;
1159 }
1160 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1161 result.measurement);
1162 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001163 return calculate_state_of_charge(the_chip,
1164 batt_temp, last_chargecycles);
1165}
1166EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1167
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001168int pm8921_bms_get_fcc(void)
1169{
1170 int batt_temp, rc;
1171 struct pm8921_adc_chan_result result;
1172
1173 if (!the_chip) {
1174 pr_err("called before initialization\n");
1175 return -EINVAL;
1176 }
1177
1178 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1179 if (rc) {
1180 pr_err("error reading adc channel = %d, rc = %d\n",
1181 the_chip->batt_temp_channel, rc);
1182 return rc;
1183 }
1184 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1185 result.measurement);
1186 batt_temp = (int)result.physical;
1187 return calculate_fcc(the_chip, batt_temp, last_chargecycles);
1188}
1189EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1190
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001191static int start_percent;
1192static int end_percent;
1193void pm8921_bms_charging_began(void)
1194{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001195 start_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001196 pr_debug("start_percent = %u%%\n", start_percent);
1197}
1198EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1199
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001200void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001201{
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001202 if (is_battery_full && the_chip != NULL) {
1203 int batt_temp, rc;
1204 struct pm8921_adc_chan_result result;
1205
1206 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1207 if (rc) {
1208 pr_err("error reading adc channel = %d, rc = %d\n",
1209 the_chip->batt_temp_channel, rc);
1210 goto charge_cycle_calculation;
1211 }
1212 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1213 result.measurement);
1214 batt_temp = (int)result.physical;
1215 last_real_fcc = calculate_real_fcc(the_chip,
1216 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001217 last_real_fcc_batt_temp = batt_temp;
1218 readjust_fcc_table();
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001219 }
1220
1221charge_cycle_calculation:
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001222 end_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001223 if (end_percent > start_percent) {
1224 last_charge_increase = end_percent - start_percent;
1225 if (last_charge_increase > 100) {
1226 last_chargecycles++;
1227 last_charge_increase = last_charge_increase % 100;
1228 }
1229 }
1230 pr_debug("end_percent = %u%% last_charge_increase = %d"
1231 "last_chargecycles = %d\n",
1232 end_percent,
1233 last_charge_increase,
1234 last_chargecycles);
1235}
1236EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1237
1238static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1239{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001240 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001241 return IRQ_HANDLED;
1242}
1243
1244static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1245{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001246 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001247 return IRQ_HANDLED;
1248}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001249
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001250static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1251{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001252 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001253 return IRQ_HANDLED;
1254}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001255
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001256static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1257{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001258 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001259 return IRQ_HANDLED;
1260}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001261
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001262static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1263{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001264 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001265
1266 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001267 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001268 return IRQ_HANDLED;
1269}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001270
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001271static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1272{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001273 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001274
1275 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001276 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001277 return IRQ_HANDLED;
1278}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001279
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001280static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1281{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001282 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001283 return IRQ_HANDLED;
1284}
1285
1286struct pm_bms_irq_init_data {
1287 unsigned int irq_id;
1288 char *name;
1289 unsigned long flags;
1290 irqreturn_t (*handler)(int, void *);
1291};
1292
1293#define BMS_IRQ(_id, _flags, _handler) \
1294{ \
1295 .irq_id = _id, \
1296 .name = #_id, \
1297 .flags = _flags, \
1298 .handler = _handler, \
1299}
1300
1301struct pm_bms_irq_init_data bms_irq_data[] = {
1302 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1303 pm8921_bms_sbi_write_ok_handler),
1304 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1305 pm8921_bms_cc_thr_handler),
1306 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1307 pm8921_bms_vsense_thr_handler),
1308 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1309 pm8921_bms_vsense_for_r_handler),
1310 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1311 pm8921_bms_ocv_for_r_handler),
1312 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1313 pm8921_bms_good_ocv_handler),
1314 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1315 pm8921_bms_vsense_avg_handler),
1316};
1317
1318static void free_irqs(struct pm8921_bms_chip *chip)
1319{
1320 int i;
1321
1322 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1323 if (chip->pmic_bms_irq[i]) {
1324 free_irq(chip->pmic_bms_irq[i], NULL);
1325 chip->pmic_bms_irq[i] = 0;
1326 }
1327}
1328
1329static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1330 struct platform_device *pdev)
1331{
1332 struct resource *res;
1333 int ret, i;
1334
1335 ret = 0;
1336 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1337
1338 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1339 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1340 bms_irq_data[i].name);
1341 if (res == NULL) {
1342 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1343 goto err_out;
1344 }
1345 ret = request_irq(res->start, bms_irq_data[i].handler,
1346 bms_irq_data[i].flags,
1347 bms_irq_data[i].name, chip);
1348 if (ret < 0) {
1349 pr_err("couldn't request %d (%s) %d\n", res->start,
1350 bms_irq_data[i].name, ret);
1351 goto err_out;
1352 }
1353 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1354 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1355 }
1356 return 0;
1357
1358err_out:
1359 free_irqs(chip);
1360 return -EINVAL;
1361}
1362
1363#define EN_BMS_BIT BIT(7)
1364#define EN_PON_HS_BIT BIT(0)
1365static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1366{
1367 int rc;
1368
1369 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1370 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1371 if (rc) {
1372 pr_err("failed to enable pon and bms addr = %d %d",
1373 BMS_CONTROL, rc);
1374 }
1375
1376 return 0;
1377}
1378
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001379static void check_initial_ocv(struct pm8921_bms_chip *chip)
1380{
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001381 int ocv, rc;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001382
1383 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001384 * Check if a ocv is available in bms hw,
1385 * if not compute it here at boot time and save it
1386 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001387 */
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001388 ocv = 0;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001389 rc = read_last_good_ocv(chip, &ocv);
1390 if (rc || ocv == 0) {
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001391 rc = adc_based_ocv(chip, &ocv);
1392 if (rc) {
1393 pr_err("failed to read adc based ocv rc = %d\n", rc);
1394 ocv = DEFAULT_OCV_MICROVOLTS;
1395 }
1396 last_ocv_uv = ocv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001397 }
1398 pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
1399}
1400
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001401static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1402{
1403 int rc;
1404 struct pm8921_adc_chan_result result;
1405
1406 rc = pm8921_adc_read(chip->batt_id_channel, &result);
1407 if (rc) {
1408 pr_err("error reading batt id channel = %d, rc = %d\n",
1409 chip->vbat_channel, rc);
1410 return rc;
1411 }
1412 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1413 result.measurement);
1414 return result.physical;
1415}
1416
1417#define PALLADIUM_ID_MIN 2500
1418#define PALLADIUM_ID_MAX 4000
1419static int set_battery_data(struct pm8921_bms_chip *chip)
1420{
1421 int64_t battery_id;
1422
1423 battery_id = read_battery_id(chip);
1424
1425 if (battery_id < 0) {
1426 pr_err("cannot read battery id err = %lld\n", battery_id);
1427 return battery_id;
1428 }
1429
1430 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
1431 chip->fcc = palladium_1500_data.fcc;
1432 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1433 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1434 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1435 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1436 return 0;
1437 } else {
1438 pr_warn("invalid battery id, palladium 1500 assumed\n");
1439 chip->fcc = palladium_1500_data.fcc;
1440 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1441 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1442 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1443 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1444 return 0;
1445 }
1446}
1447
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001448enum {
1449 CALC_RBATT,
1450 CALC_FCC,
1451 CALC_PC,
1452 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001453 CALIB_HKADC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001454};
1455
1456static int test_batt_temp = 5;
1457static int test_chargecycle = 150;
1458static int test_ocv = 3900000;
1459enum {
1460 TEST_BATT_TEMP,
1461 TEST_CHARGE_CYCLE,
1462 TEST_OCV,
1463};
1464static int get_test_param(void *data, u64 * val)
1465{
1466 switch ((int)data) {
1467 case TEST_BATT_TEMP:
1468 *val = test_batt_temp;
1469 break;
1470 case TEST_CHARGE_CYCLE:
1471 *val = test_chargecycle;
1472 break;
1473 case TEST_OCV:
1474 *val = test_ocv;
1475 break;
1476 default:
1477 return -EINVAL;
1478 }
1479 return 0;
1480}
1481static int set_test_param(void *data, u64 val)
1482{
1483 switch ((int)data) {
1484 case TEST_BATT_TEMP:
1485 test_batt_temp = (int)val;
1486 break;
1487 case TEST_CHARGE_CYCLE:
1488 test_chargecycle = (int)val;
1489 break;
1490 case TEST_OCV:
1491 test_ocv = (int)val;
1492 break;
1493 default:
1494 return -EINVAL;
1495 }
1496 return 0;
1497}
1498DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
1499
1500static int get_calc(void *data, u64 * val)
1501{
1502 int param = (int)data;
1503 int ret = 0;
1504
1505 *val = 0;
1506
1507 /* global irq number passed in via data */
1508 switch (param) {
1509 case CALC_RBATT:
1510 *val = calculate_rbatt(the_chip);
1511 break;
1512 case CALC_FCC:
1513 *val = calculate_fcc(the_chip, test_batt_temp,
1514 test_chargecycle);
1515 break;
1516 case CALC_PC:
1517 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
1518 test_chargecycle);
1519 break;
1520 case CALC_SOC:
1521 *val = calculate_state_of_charge(the_chip,
1522 test_batt_temp, test_chargecycle);
1523 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001524 case CALIB_HKADC:
1525 /* reading this will trigger calibration */
1526 *val = 0;
1527 calib_hkadc(the_chip);
1528 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529 default:
1530 ret = -EINVAL;
1531 }
1532 return ret;
1533}
1534DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
1535
1536static int get_reading(void *data, u64 * val)
1537{
1538 int param = (int)data;
1539 int ret = 0;
1540
1541 *val = 0;
1542
1543 /* global irq number passed in via data */
1544 switch (param) {
1545 case CC_MSB:
1546 case CC_LSB:
1547 read_cc(the_chip, (int *)val);
1548 break;
1549 case LAST_GOOD_OCV_VALUE:
1550 read_last_good_ocv(the_chip, (uint *)val);
1551 break;
1552 case VBATT_FOR_RBATT:
1553 read_vbatt_for_rbatt(the_chip, (uint *)val);
1554 break;
1555 case VSENSE_FOR_RBATT:
1556 read_vsense_for_rbatt(the_chip, (uint *)val);
1557 break;
1558 case OCV_FOR_RBATT:
1559 read_ocv_for_rbatt(the_chip, (uint *)val);
1560 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001561 case VSENSE_AVG:
1562 read_vsense_avg(the_chip, (uint *)val);
1563 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001564 default:
1565 ret = -EINVAL;
1566 }
1567 return ret;
1568}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001569DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001570
1571static int get_rt_status(void *data, u64 * val)
1572{
1573 int i = (int)data;
1574 int ret;
1575
1576 /* global irq number passed in via data */
1577 ret = pm_bms_get_rt_status(the_chip, i);
1578 *val = ret;
1579 return 0;
1580}
1581DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
1582
1583static int get_reg(void *data, u64 * val)
1584{
1585 int addr = (int)data;
1586 int ret;
1587 u8 temp;
1588
1589 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
1590 if (ret) {
1591 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
1592 addr, temp, ret);
1593 return -EAGAIN;
1594 }
1595 *val = temp;
1596 return 0;
1597}
1598
1599static int set_reg(void *data, u64 val)
1600{
1601 int addr = (int)data;
1602 int ret;
1603 u8 temp;
1604
1605 temp = (u8) val;
1606 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
1607 if (ret) {
1608 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
1609 addr, temp, ret);
1610 return -EAGAIN;
1611 }
1612 return 0;
1613}
1614DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
1615
1616static void create_debugfs_entries(struct pm8921_bms_chip *chip)
1617{
1618 int i;
1619
1620 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
1621
1622 if (IS_ERR(chip->dent)) {
1623 pr_err("pmic bms couldnt create debugfs dir\n");
1624 return;
1625 }
1626
1627 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
1628 (void *)BMS_CONTROL, &reg_fops);
1629 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
1630 (void *)BMS_OUTPUT0, &reg_fops);
1631 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
1632 (void *)BMS_OUTPUT1, &reg_fops);
1633 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
1634 (void *)BMS_TEST1, &reg_fops);
1635 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
1636 (void *)CCADC_ANA_PARAM, &reg_fops);
1637 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
1638 (void *)CCADC_DIG_PARAM, &reg_fops);
1639 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
1640 (void *)CCADC_RSV, &reg_fops);
1641 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
1642 (void *)CCADC_DATA0, &reg_fops);
1643 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
1644 (void *)CCADC_DATA1, &reg_fops);
1645 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
1646 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
1647 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
1648 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
1649
1650 debugfs_create_file("test_batt_temp", 0644, chip->dent,
1651 (void *)TEST_BATT_TEMP, &temp_fops);
1652 debugfs_create_file("test_chargecycle", 0644, chip->dent,
1653 (void *)TEST_CHARGE_CYCLE, &temp_fops);
1654 debugfs_create_file("test_ocv", 0644, chip->dent,
1655 (void *)TEST_OCV, &temp_fops);
1656
1657 debugfs_create_file("read_cc", 0644, chip->dent,
1658 (void *)CC_MSB, &reading_fops);
1659 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
1660 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
1661 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
1662 (void *)VBATT_FOR_RBATT, &reading_fops);
1663 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
1664 (void *)VSENSE_FOR_RBATT, &reading_fops);
1665 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
1666 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001667 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
1668 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001669
1670 debugfs_create_file("show_rbatt", 0644, chip->dent,
1671 (void *)CALC_RBATT, &calc_fops);
1672 debugfs_create_file("show_fcc", 0644, chip->dent,
1673 (void *)CALC_FCC, &calc_fops);
1674 debugfs_create_file("show_pc", 0644, chip->dent,
1675 (void *)CALC_PC, &calc_fops);
1676 debugfs_create_file("show_soc", 0644, chip->dent,
1677 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001678 debugfs_create_file("calib_hkadc", 0644, chip->dent,
1679 (void *)CALIB_HKADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001680
1681 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1682 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
1683 debugfs_create_file(bms_irq_data[i].name, 0444,
1684 chip->dent,
1685 (void *)bms_irq_data[i].irq_id,
1686 &rt_fops);
1687 }
1688}
1689
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001690static int __devinit pm8921_bms_probe(struct platform_device *pdev)
1691{
1692 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001693 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001694 struct pm8921_bms_chip *chip;
1695 const struct pm8921_bms_platform_data *pdata
1696 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001697
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001698 if (!pdata) {
1699 pr_err("missing platform data\n");
1700 return -EINVAL;
1701 }
1702
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001703 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001704 if (!chip) {
1705 pr_err("Cannot allocate pm_bms_chip\n");
1706 return -ENOMEM;
1707 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001708 spin_lock_init(&chip->bms_output_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001709 chip->dev = &pdev->dev;
1710 chip->r_sense = pdata->r_sense;
1711 chip->i_test = pdata->i_test;
1712 chip->v_failure = pdata->v_failure;
1713 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001714 rc = set_battery_data(chip);
1715 if (rc) {
1716 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07001717 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001718 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001719
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001720 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
1721 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001722 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
1723 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001724 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07001725 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001726 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001727
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001728 rc = pm8921_bms_hw_init(chip);
1729 if (rc) {
1730 pr_err("couldn't init hardware rc = %d\n", rc);
1731 goto free_chip;
1732 }
1733
1734 rc = request_irqs(chip, pdev);
1735 if (rc) {
1736 pr_err("couldn't register interrupts rc = %d\n", rc);
1737 goto free_chip;
1738 }
1739
1740 platform_set_drvdata(pdev, chip);
1741 the_chip = chip;
1742 create_debugfs_entries(chip);
1743
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001744 check_initial_ocv(chip);
1745
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001746 /* initial hkadc calibration */
1747 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001748 /* enable the vbatt reading interrupts for scheduling hkadc calib */
1749 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
1750 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001751
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001752 get_battery_uvolts(chip, &vbatt);
1753 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
1754 pm8921_bms_get_percent_charge(),
1755 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001756 return 0;
1757
1758free_chip:
1759 kfree(chip);
1760 return rc;
1761}
1762
1763static int __devexit pm8921_bms_remove(struct platform_device *pdev)
1764{
1765 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
1766
1767 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001768 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001769 platform_set_drvdata(pdev, NULL);
1770 the_chip = NULL;
1771 kfree(chip);
1772 return 0;
1773}
1774
1775static struct platform_driver pm8921_bms_driver = {
1776 .probe = pm8921_bms_probe,
1777 .remove = __devexit_p(pm8921_bms_remove),
1778 .driver = {
1779 .name = PM8921_BMS_DEV_NAME,
1780 .owner = THIS_MODULE,
1781 },
1782};
1783
1784static int __init pm8921_bms_init(void)
1785{
1786 return platform_driver_register(&pm8921_bms_driver);
1787}
1788
1789static void __exit pm8921_bms_exit(void)
1790{
1791 platform_driver_unregister(&pm8921_bms_driver);
1792}
1793
1794late_initcall(pm8921_bms_init);
1795module_exit(pm8921_bms_exit);
1796
1797MODULE_LICENSE("GPL v2");
1798MODULE_DESCRIPTION("PMIC8921 bms driver");
1799MODULE_VERSION("1.0");
1800MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);