blob: 2d0f7cd0b2ed83add66f70f56b1e6a9a0123e823 [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>
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070026#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
28#define BMS_CONTROL 0x224
29#define BMS_OUTPUT0 0x230
30#define BMS_OUTPUT1 0x231
31#define BMS_TEST1 0x237
32#define CCADC_ANA_PARAM 0x240
33#define CCADC_DIG_PARAM 0x241
34#define CCADC_RSV 0x242
35#define CCADC_DATA0 0x244
36#define CCADC_DATA1 0x245
37#define CCADC_OFFSET_TRIM1 0x34A
38#define CCADC_OFFSET_TRIM0 0x34B
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070039#define CCADC_FULLSCALE_TRIM1 0x34C
40#define CCADC_FULLSCALE_TRIM0 0x34D
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070042#define ADC_ARB_SECP_CNTRL 0x190
43#define ADC_ARB_SECP_AMUX_CNTRL 0x191
44#define ADC_ARB_SECP_ANA_PARAM 0x192
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070045#define ADC_ARB_SECP_DIG_PARAM 0x193
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070046#define ADC_ARB_SECP_RSV 0x194
47#define ADC_ARB_SECP_DATA1 0x195
48#define ADC_ARB_SECP_DATA0 0x196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070050#define ADC_ARB_BMS_CNTRL 0x18D
51
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052enum pmic_bms_interrupts {
53 PM8921_BMS_SBI_WRITE_OK,
54 PM8921_BMS_CC_THR,
55 PM8921_BMS_VSENSE_THR,
56 PM8921_BMS_VSENSE_FOR_R,
57 PM8921_BMS_OCV_FOR_R,
58 PM8921_BMS_GOOD_OCV,
59 PM8921_BMS_VSENSE_AVG,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -070060 PM8921_BMS_CCADC_EOC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061 PM_BMS_MAX_INTS,
62};
63
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064struct pm8921_bms_chip {
65 struct device *dev;
66 struct dentry *dent;
67 unsigned int r_sense;
68 unsigned int i_test;
69 unsigned int v_failure;
70 unsigned int fcc;
71 struct single_row_lut *fcc_temp_lut;
72 struct single_row_lut *fcc_sf_lut;
73 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
74 struct pc_sf_lut *pc_sf_lut;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070075 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070076 struct delayed_work calib_ccadc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077 unsigned int calib_delay_ms;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070078 int ccadc_gain_uv;
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -070079 u16 ccadc_result_offset;
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 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700392 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700393 cc_to_microvolt_v1((s64)cc) :
394 cc_to_microvolt_v2((s64)cc);
395}
396
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700397#define CCADC_READING_RESOLUTION_N_V1 1085069
398#define CCADC_READING_RESOLUTION_D_V1 100000
399#define CCADC_READING_RESOLUTION_N_V2 542535
400#define CCADC_READING_RESOLUTION_D_V2 100000
401static s64 ccadc_reading_to_microvolt_v1(s64 cc)
402{
403 return div_s64(cc * CCADC_READING_RESOLUTION_N_V1,
404 CCADC_READING_RESOLUTION_D_V1);
405}
406
407static s64 ccadc_reading_to_microvolt_v2(s64 cc)
408{
409 return div_s64(cc * CCADC_READING_RESOLUTION_N_V2,
410 CCADC_READING_RESOLUTION_D_V2);
411}
412
413static s64 ccadc_reading_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
414{
415 /*
416 * resolution (the value of a single bit) was changed after revision 2.0
417 * for more accurate readings
418 */
419 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
420 ccadc_reading_to_microvolt_v1((s64)cc) :
421 ccadc_reading_to_microvolt_v2((s64)cc);
422}
423
424static s64 microvolt_to_ccadc_reading_v1(s64 uv)
425{
426 return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
427 CCADC_READING_RESOLUTION_N_V1);
428}
429
430static s64 microvolt_to_ccadc_reading_v2(s64 uv)
431{
432 return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
433 CCADC_READING_RESOLUTION_N_V2);
434}
435
436static s64 microvolt_to_ccadc_reading(struct pm8921_bms_chip *chip, s64 cc)
437{
438 /*
439 * resolution (the value of a single bit) was changed after revision 2.0
440 * for more accurate readings
441 */
442 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
443 microvolt_to_ccadc_reading_v1((s64)cc) :
444 microvolt_to_ccadc_reading_v2((s64)cc);
445}
446
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700447#define CC_READING_TICKS 55
448#define SLEEP_CLK_HZ 32768
449#define SECONDS_PER_HOUR 3600
450static s64 ccmicrovolt_to_uvh(s64 cc_uv)
451{
452 return div_s64(cc_uv * CC_READING_TICKS,
453 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
454}
455
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700456#define GAIN_REFERENCE_UV 25000
457/*
458 * gain compensation for ccadc readings - common for vsense based and
459 * couloumb counter based readings
460 */
461static s64 cc_adjust_for_gain(struct pm8921_bms_chip *chip, s64 cc)
462{
463 if (chip->ccadc_gain_uv == 0)
464 return cc;
465
466 return div_s64(cc * GAIN_REFERENCE_UV, chip->ccadc_gain_uv);
467}
468
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469/* returns the signed value read from the hardware */
470static int read_cc(struct pm8921_bms_chip *chip, int *result)
471{
472 int rc;
473 uint16_t msw, lsw;
474
475 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
476 if (rc) {
477 pr_err("fail to read CC_LSB rc = %d\n", rc);
478 return rc;
479 }
480 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
481 if (rc) {
482 pr_err("fail to read CC_MSB rc = %d\n", rc);
483 return rc;
484 }
485 *result = msw << 16 | lsw;
486 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
487 return 0;
488}
489
490static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
491{
492 int rc;
493 uint16_t reading;
494
495 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
496 if (rc) {
497 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
498 return rc;
499 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700500 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700501 pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
502 *result = adjust_xo_vbatt_reading(chip, *result);
503 pr_debug("after adj ocv_uV = %u\n", *result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700504 if (*result != 0)
505 last_ocv_uv = *result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506 return 0;
507}
508
509static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
510{
511 int rc;
512 uint16_t reading;
513
514 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
515 if (rc) {
516 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
517 return rc;
518 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700519 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700521 *result = adjust_xo_vbatt_reading(chip, *result);
522 pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523 return 0;
524}
525
526static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
527{
528 int rc;
529 uint16_t reading;
530
531 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
532 if (rc) {
533 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
534 return rc;
535 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700536 *result = ccadc_reading_to_microvolt(chip, reading);
537 pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
538 *result = cc_adjust_for_gain(chip, *result);
539 pr_debug("after adj vsense_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 return 0;
541}
542
543static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
544{
545 int rc;
546 uint16_t reading;
547
548 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
549 if (rc) {
550 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
551 return rc;
552 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700553 *result = xoadc_reading_to_microvolt(reading);
554 pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
555 *result = adjust_xo_vbatt_reading(chip, *result);
556 pr_debug("after adj ocv_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 return 0;
558}
559
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700560static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
561{
562 int rc;
563 int16_t reading;
564
565 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
566 if (rc) {
567 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
568 return rc;
569 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700570 *result = ccadc_reading_to_microvolt(chip, reading);
571 pr_debug("raw = %04x vsense = %d\n", reading, *result);
572 *result = cc_adjust_for_gain(the_chip, (s64)*result);
573 pr_debug("after adj vsense = %d\n", *result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700574 return 0;
575}
576
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
578{
579 if (y0 == y1 || x == x0)
580 return y0;
581 if (x1 == x0 || x == x1)
582 return y1;
583
584 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
585}
586
587static int interpolate_single_lut(struct single_row_lut *lut, int x)
588{
589 int i, result;
590
591 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700592 pr_debug("x %d less than known range return y = %d lut = %pS\n",
593 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594 return lut->y[0];
595 }
596 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700597 pr_debug("x %d more than known range return y = %d lut = %pS\n",
598 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700599 return lut->y[lut->cols - 1];
600 }
601
602 for (i = 0; i < lut->cols; i++)
603 if (x <= lut->x[i])
604 break;
605 if (x == lut->x[i]) {
606 result = lut->y[i];
607 } else {
608 result = linear_interpolate(
609 lut->y[i - 1],
610 lut->x[i - 1],
611 lut->y[i],
612 lut->x[i],
613 x);
614 }
615 return result;
616}
617
618static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
619{
620 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
621}
622
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700623static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
624{
625 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
626}
627
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700628static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
629 int cycles)
630{
631 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
632}
633
634static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
635 int cycles, int pc)
636{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700637 int i, scalefactorrow1, scalefactorrow2, scalefactor;
638 int row1 = 0;
639 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640 int rows = chip->pc_sf_lut->rows;
641 int cols = chip->pc_sf_lut->cols;
642
643 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700644 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700645 row1 = 0;
646 row2 = 0;
647 }
648 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700649 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700650 row1 = rows - 1;
651 row2 = rows - 1;
652 }
653 for (i = 0; i < rows; i++) {
654 if (pc == chip->pc_sf_lut->percent[i]) {
655 row1 = i;
656 row2 = i;
657 break;
658 }
659 if (pc > chip->pc_sf_lut->percent[i]) {
660 row1 = i - 1;
661 row2 = i;
662 break;
663 }
664 }
665
666 if (cycles < chip->pc_sf_lut->cycles[0])
667 cycles = chip->pc_sf_lut->cycles[0];
668 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
669 cycles = chip->pc_sf_lut->cycles[cols - 1];
670
671 for (i = 0; i < cols; i++)
672 if (cycles <= chip->pc_sf_lut->cycles[i])
673 break;
674 if (cycles == chip->pc_sf_lut->cycles[i]) {
675 scalefactor = linear_interpolate(
676 chip->pc_sf_lut->sf[row1][i],
677 chip->pc_sf_lut->percent[row1],
678 chip->pc_sf_lut->sf[row2][i],
679 chip->pc_sf_lut->percent[row2],
680 pc);
681 return scalefactor;
682 }
683
684 scalefactorrow1 = linear_interpolate(
685 chip->pc_sf_lut->sf[row1][i - 1],
686 chip->pc_sf_lut->cycles[i - 1],
687 chip->pc_sf_lut->sf[row1][i],
688 chip->pc_sf_lut->cycles[i],
689 cycles);
690
691 scalefactorrow2 = linear_interpolate(
692 chip->pc_sf_lut->sf[row2][i - 1],
693 chip->pc_sf_lut->cycles[i - 1],
694 chip->pc_sf_lut->sf[row2][i],
695 chip->pc_sf_lut->cycles[i],
696 cycles);
697
698 scalefactor = linear_interpolate(
699 scalefactorrow1,
700 chip->pc_sf_lut->percent[row1],
701 scalefactorrow2,
702 chip->pc_sf_lut->percent[row2],
703 pc);
704
705 return scalefactor;
706}
707
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700708static int is_between(int left, int right, int value)
709{
710 if (left >= right && left >= value && value >= right)
711 return 1;
712 if (left <= right && left <= value && value <= right)
713 return 1;
714
715 return 0;
716}
717
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718static int interpolate_pc(struct pm8921_bms_chip *chip,
719 int batt_temp, int ocv)
720{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700721 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700722 int rows = chip->pc_temp_ocv_lut->rows;
723 int cols = chip->pc_temp_ocv_lut->cols;
724
725 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700726 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727 batt_temp = chip->pc_temp_ocv_lut->temp[0];
728 }
729 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700730 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
732 }
733
734 for (j = 0; j < cols; j++)
735 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
736 break;
737 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
738 /* found an exact match for temp in the table */
739 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
740 return chip->pc_temp_ocv_lut->percent[0];
741 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
742 return chip->pc_temp_ocv_lut->percent[rows - 1];
743 for (i = 0; i < rows; i++) {
744 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
745 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
746 return
747 chip->pc_temp_ocv_lut->percent[i];
748 pc = linear_interpolate(
749 chip->pc_temp_ocv_lut->percent[i],
750 chip->pc_temp_ocv_lut->ocv[i][j],
751 chip->pc_temp_ocv_lut->percent[i - 1],
752 chip->pc_temp_ocv_lut->ocv[i - 1][j],
753 ocv);
754 return pc;
755 }
756 }
757 }
758
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700759 /*
760 * batt_temp is within temperature for
761 * column j-1 and j
762 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
764 return chip->pc_temp_ocv_lut->percent[0];
765 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
766 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700767
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700768 pcj_minus_one = 0;
769 pcj = 0;
770 for (i = 0; i < rows-1; i++) {
771 if (pcj == 0
772 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
773 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
774 pcj = linear_interpolate(
775 chip->pc_temp_ocv_lut->percent[i],
776 chip->pc_temp_ocv_lut->ocv[i][j],
777 chip->pc_temp_ocv_lut->percent[i + 1],
778 chip->pc_temp_ocv_lut->ocv[i+1][j],
779 ocv);
780 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700781
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700782 if (pcj_minus_one == 0
783 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
784 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700786 pcj_minus_one = linear_interpolate(
787 chip->pc_temp_ocv_lut->percent[i],
788 chip->pc_temp_ocv_lut->ocv[i][j-1],
789 chip->pc_temp_ocv_lut->percent[i + 1],
790 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
791 ocv);
792 }
793
794 if (pcj && pcj_minus_one) {
795 pc = linear_interpolate(
796 pcj_minus_one,
797 chip->pc_temp_ocv_lut->temp[j-1],
798 pcj,
799 chip->pc_temp_ocv_lut->temp[j],
800 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801 return pc;
802 }
803 }
804
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700805 if (pcj)
806 return pcj;
807
808 if (pcj_minus_one)
809 return pcj_minus_one;
810
811 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
812 ocv, batt_temp);
813 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700814}
815
816static int calculate_rbatt(struct pm8921_bms_chip *chip)
817{
818 int rc;
819 unsigned int ocv, vsense, vbatt, r_batt;
820
821 rc = read_ocv_for_rbatt(chip, &ocv);
822 if (rc) {
823 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
824 ocv = 0;
825 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700826
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700827 rc = read_vbatt_for_rbatt(chip, &vbatt);
828 if (rc) {
829 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
830 ocv = 0;
831 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700832
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700833 rc = read_vsense_for_rbatt(chip, &vsense);
834 if (rc) {
835 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
836 ocv = 0;
837 }
838 if (ocv == 0
839 || ocv == vbatt
840 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700841 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
842 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700843 ocv, vbatt, vsense);
844 return -EINVAL;
845 }
846 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700847 last_rbatt = r_batt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700848 pr_debug("r_batt = %umilliOhms", r_batt);
849 return r_batt;
850}
851
852static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
853 int chargecycles)
854{
855 int initfcc, result, scalefactor = 0;
856
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700857 if (chip->adjusted_fcc_temp_lut == NULL) {
858 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700860 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700862 /* Multiply the initial FCC value by the scale factor. */
863 result = (initfcc * scalefactor) / 100;
864 pr_debug("fcc mAh = %d\n", result);
865 return result;
866 } else {
867 return interpolate_fcc_adjusted(chip, batt_temp);
868 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700869}
870
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700871static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
872{
873 int rc;
874 struct pm8921_adc_chan_result result;
875
876 rc = pm8921_adc_read(chip->vbat_channel, &result);
877 if (rc) {
878 pr_err("error reading adc channel = %d, rc = %d\n",
879 chip->vbat_channel, rc);
880 return rc;
881 }
882 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
883 result.measurement);
884 *uvolts = (int)result.physical;
885 *uvolts = *uvolts * 1000;
886 return 0;
887}
888
889static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
890{
891 int vbatt, rbatt, ibatt, rc;
892
893 rc = get_battery_uvolts(chip, &vbatt);
894 if (rc) {
895 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700896 return rc;
897 }
898
899 rc = pm8921_bms_get_battery_current(&ibatt);
900 if (rc) {
901 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700902 return rc;
903 }
904
905 rbatt = calculate_rbatt(the_chip);
906 if (rbatt < 0)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700907 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700908 *ocv = vbatt + ibatt * rbatt;
909 return 0;
910}
911
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
913 int chargecycles)
914{
915 int pc, scalefactor;
916
917 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
918 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
919 pc, ocv_uv, batt_temp);
920
921 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
922 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
923
924 /* Multiply the initial FCC value by the scale factor. */
925 pc = (pc * scalefactor) / 100;
926 return pc;
927}
928
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700929static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700930 int *coulumb_counter)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700931{
932 int rc;
933 int64_t cc_voltage_uv, cc_uvh, cc_mah;
934
935 rc = read_cc(the_chip, coulumb_counter);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700936 cc_voltage_uv = (int64_t)*coulumb_counter;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700937 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700938 cc_voltage_uv = cc_adjust_for_gain(chip, cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700940 cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700941 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
942 cc_mah = div_s64(cc_uvh, chip->r_sense);
943 *val = cc_mah;
944}
945
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700946static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
947 int fcc, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700948{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700949 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950
951 rbatt = calculate_rbatt(chip);
952 if (rbatt < 0) {
953 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700954 pr_debug("rbatt unavailable assuming %d\n", rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956
957 /* calculate unusable charge */
958 voltage_unusable_uv = (rbatt * chip->i_test)
959 + (chip->v_failure * 1000);
960 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
961 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700962 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
963 rbatt, voltage_unusable_uv, pc_unusable);
964 return (fcc * pc_unusable) / 100;
965}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700966
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700967/* calculate remainging charge at the time of ocv */
968static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
969 int batt_temp, int chargecycles)
970{
971 int rc, ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972
973 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700974 ocv = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700975 rc = read_last_good_ocv(chip, &ocv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700976 if (rc)
977 pr_debug("failed to read ocv rc = %d\n", rc);
978
979 if (ocv == 0) {
980 ocv = last_ocv_uv;
981 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700983
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700984 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700985 pr_debug("ocv = %d pc = %d\n", ocv, pc);
986 return (fcc * pc) / 100;
987}
988
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700989static void calculate_charging_params(struct pm8921_bms_chip *chip,
990 int batt_temp, int chargecycles,
991 int *fcc,
992 int *unusable_charge,
993 int *remaining_charge,
994 int64_t *cc_mah)
995{
996 int coulumb_counter;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700997 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700998
999 *fcc = calculate_fcc(chip, batt_temp, chargecycles);
1000 pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
1001 *fcc, batt_temp, chargecycles);
1002
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001003 /* fcc doesnt need to be read from hardware, lock the bms now */
1004 spin_lock_irqsave(&chip->bms_output_lock, flags);
1005 pm_bms_lock_output_data(chip);
1006
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001007 *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
1008 batt_temp, chargecycles);
1009
1010 pr_debug("UUC = %umAh", *unusable_charge);
1011
1012 /* calculate remainging charge */
1013 *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
1014 batt_temp, chargecycles);
1015 pr_debug("RC = %umAh\n", *remaining_charge);
1016
1017 /* calculate cc milli_volt_hour */
1018 calculate_cc_mah(chip, cc_mah, &coulumb_counter);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001019
1020 pm_bms_unlock_output_data(chip);
1021 spin_unlock_irqrestore(&chip->bms_output_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001022 pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
1023}
1024
1025static int calculate_real_fcc(struct pm8921_bms_chip *chip,
1026 int batt_temp, int chargecycles)
1027{
1028 int fcc, unusable_charge;
1029 int remaining_charge;
1030 int64_t cc_mah;
1031 int real_fcc;
1032
1033 calculate_charging_params(chip, batt_temp, chargecycles,
1034 &fcc,
1035 &unusable_charge,
1036 &remaining_charge,
1037 &cc_mah);
1038
1039 real_fcc = remaining_charge - cc_mah;
1040
1041 return real_fcc;
1042}
1043/*
1044 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1045 * - coloumb counter charge
1046 * - unusable charge (due to battery resistance)
1047 * SOC% = (remaining usable charge/ fcc - usable_charge);
1048 */
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001049#define BMS_BATT_NOMINAL 3700000
1050#define MIN_OPERABLE_SOC 10
1051#define BATTERY_POWER_SUPPLY_SOC 53
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001052static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
1053 int batt_temp, int chargecycles)
1054{
1055 int remaining_usable_charge, fcc, unusable_charge;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001056 int remaining_charge, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001057 int update_userspace = 1;
1058 int64_t cc_mah;
1059
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001060 calculate_charging_params(chip, batt_temp, chargecycles,
1061 &fcc,
1062 &unusable_charge,
1063 &remaining_charge,
1064 &cc_mah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001065
1066 /* calculate remaining usable charge */
1067 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001068 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001069 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
1070 if (soc > 100)
1071 soc = 100;
1072 pr_debug("SOC = %u%%\n", soc);
1073
1074 if (soc < MIN_OPERABLE_SOC) {
1075 int ocv = 0, rc;
1076
1077 rc = adc_based_ocv(chip, &ocv);
1078 if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
1079 /*
1080 * The ocv doesnt seem to have dropped for
1081 * soc to go negative.
1082 * The setup must be using a power supply
1083 * instead of real batteries.
1084 * Fake high enough soc to prevent userspace
1085 * shutdown for low battery
1086 */
1087 soc = BATTERY_POWER_SUPPLY_SOC;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001088 pr_debug("Adjusting SOC to %d\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001089 }
1090 }
1091
1092 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001093 pr_err("bad rem_usb_chg = %d rem_chg %d,"
1094 "cc_mah %lld, unusb_chg %d\n",
1095 remaining_usable_charge, remaining_charge,
1096 cc_mah, unusable_charge);
1097 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001098 "chargecycles = %d, batt_temp = %d"
1099 "fcc = %d soc =%d\n",
1100 last_ocv_uv, chargecycles, batt_temp,
1101 fcc, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001102 update_userspace = 0;
1103 }
1104
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001105 if (update_userspace) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001106 last_soc = soc;
1107 }
1108 return soc;
1109}
1110
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001111#define XOADC_MAX_1P25V 1312500
1112#define XOADC_MIN_1P25V 1187500
1113#define XOADC_MAX_0P625V 656250
1114#define XOADC_MIN_0P625V 593750
1115
1116#define HKADC_V_PER_BIT_MUL_FACTOR 977
1117#define HKADC_V_PER_BIT_DIV_FACTOR 10
1118static int calib_hkadc_convert_microvolt(unsigned int phy)
1119{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001120 return (phy - 0x6000) *
1121 HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001122}
1123
1124static void calib_hkadc(struct pm8921_bms_chip *chip)
1125{
1126 int voltage, rc;
1127 struct pm8921_adc_chan_result result;
1128
1129 rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
1130 if (rc) {
1131 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1132 return;
1133 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001134 voltage = calib_hkadc_convert_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001135
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001136 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1137 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001138
1139 /* check for valid range */
1140 if (voltage > XOADC_MAX_1P25V)
1141 voltage = XOADC_MAX_1P25V;
1142 else if (voltage < XOADC_MIN_1P25V)
1143 voltage = XOADC_MIN_1P25V;
1144 chip->xoadc_v125 = voltage;
1145
1146 rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
1147 if (rc) {
1148 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1149 return;
1150 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001151 voltage = calib_hkadc_convert_microvolt(result.adc_code);
1152 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
1153 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001154 /* check for valid range */
1155 if (voltage > XOADC_MAX_0P625V)
1156 voltage = XOADC_MAX_0P625V;
1157 else if (voltage < XOADC_MIN_0P625V)
1158 voltage = XOADC_MIN_0P625V;
1159
1160 chip->xoadc_v0625 = voltage;
1161}
1162
1163static void calibrate_hkadc_work(struct work_struct *work)
1164{
1165 struct pm8921_bms_chip *chip = container_of(work,
1166 struct pm8921_bms_chip, calib_hkadc_work);
1167
1168 calib_hkadc(chip);
1169}
1170
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001171#define START_CONV_BIT BIT(7)
1172#define EOC_CONV_BIT BIT(6)
1173#define SEL_CCADC_BIT BIT(1)
1174#define EN_ARB_BIT BIT(0)
1175
1176#define CCADC_CALIB_DIG_PARAM 0xE3
1177#define CCADC_CALIB_RSV_GND 0x40
1178#define CCADC_CALIB_RSV_25MV 0x80
1179#define CCADC_CALIB_ANA_PARAM 0x1B
1180#define SAMPLE_COUNT 16
1181#define ADC_WAIT_COUNT 10
1182
1183#define CCADC_MAX_25MV 30000
1184#define CCADC_MIN_25MV 20000
1185#define CCADC_MAX_0UV -4000
1186#define CCADC_MIN_0UV -7000
1187
1188#define CCADC_INTRINSIC_OFFSET 0xC000
1189
1190#define REG_SBI_CONFIG 0x04F
1191#define PAGE3_ENABLE_MASK 0x6
1192
1193static int calib_ccadc_enable_trim_access(struct pm8921_bms_chip *chip,
1194 u8 *sbi_config)
1195{
1196 u8 reg;
1197 int rc;
1198
1199 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1200 if (rc) {
1201 pr_err("error = %d reading sbi config reg\n", rc);
1202 return rc;
1203 }
1204
1205 reg = *sbi_config | PAGE3_ENABLE_MASK;
1206 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
1207}
1208
1209static int calib_ccadc_restore_trim_access(struct pm8921_bms_chip *chip,
1210 u8 sbi_config)
1211{
1212 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1213}
1214
1215static int calib_ccadc_enable_arbiter(struct pm8921_bms_chip *chip)
1216{
1217 int rc;
1218
1219 /* enable Arbiter, must be sent twice */
1220 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1221 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1222 if (rc < 0) {
1223 pr_err("error = %d enabling arbiter for offset\n", rc);
1224 return rc;
1225 }
1226 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1227 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1228 if (rc < 0) {
1229 pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
1230 return rc;
1231 }
1232 return 0;
1233}
1234
1235static int calib_start_conv(struct pm8921_bms_chip *chip,
1236 u16 *result)
1237{
1238 int rc, i;
1239 u8 data_msb, data_lsb, reg;
1240
1241 /* Start conversion */
1242 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1243 START_CONV_BIT, START_CONV_BIT);
1244 if (rc < 0) {
1245 pr_err("error = %d starting offset meas\n", rc);
1246 return rc;
1247 }
1248
1249 /* Wait for End of conversion */
1250 for (i = 0; i < ADC_WAIT_COUNT; i++) {
1251 rc = pm8xxx_readb(chip->dev->parent,
1252 ADC_ARB_SECP_CNTRL, &reg);
1253 if (rc < 0) {
1254 pr_err("error = %d read eoc for offset\n", rc);
1255 return rc;
1256 }
1257 if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
1258 msleep(60);
1259 else
1260 break;
1261 }
1262 if (i == ADC_WAIT_COUNT) {
1263 pr_err("waited too long for offset eoc\n");
1264 return rc;
1265 }
1266
1267 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
1268 if (rc < 0) {
1269 pr_err("error = %d reading offset lsb\n", rc);
1270 return rc;
1271 }
1272
1273 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
1274 if (rc < 0) {
1275 pr_err("error = %d reading offset msb\n", rc);
1276 return rc;
1277 }
1278
1279 *result = (data_msb << 8) | data_lsb;
1280 return 0;
1281}
1282
1283static int calib_ccadc_read_trim(struct pm8921_bms_chip *chip,
1284 int addr, u8 *data_msb, u8 *data_lsb)
1285{
1286 int rc;
1287 u8 sbi_config;
1288
1289 calib_ccadc_enable_trim_access(chip, &sbi_config);
1290 rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
1291 if (rc < 0) {
1292 pr_err("error = %d read msb\n", rc);
1293 return rc;
1294 }
1295 rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
1296 if (rc < 0) {
1297 pr_err("error = %d read lsb\n", rc);
1298 return rc;
1299 }
1300 calib_ccadc_restore_trim_access(chip, sbi_config);
1301 return 0;
1302}
1303
1304static int calib_ccadc_read_gain_uv(struct pm8921_bms_chip *chip)
1305{
1306 s8 data_msb;
1307 u8 data_lsb;
1308 int rc, gain, offset;
1309
1310 rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
1311 &data_msb, &data_lsb);
1312 gain = (data_msb << 8) | data_lsb;
1313
1314 rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
1315 &data_msb, &data_lsb);
1316 offset = (data_msb << 8) | data_lsb;
1317
1318 pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", gain, offset);
1319 gain = ccadc_reading_to_microvolt(chip, (s64)gain - offset);
1320 return gain;
1321}
1322
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001323#define CCADC_PROGRAM_TRIM_COUNT 2
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001324#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT 4
1325#define ADC_ARB_BMS_CNTRL_CONV_MASK 0x03
1326#define BMS_CONV_IN_PROGRESS 0x2
1327
1328static int calib_ccadc_program_trim(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001329 int addr, u8 data_msb, u8 data_lsb,
1330 int wait)
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001331{
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001332 int i, rc, loop;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001333 u8 cntrl, sbi_config;
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001334 bool in_progress = 0;
1335
1336 loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001337
1338 calib_ccadc_enable_trim_access(chip, &sbi_config);
1339
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001340 for (i = 0; i < loop; i++) {
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001341 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
1342 if (rc < 0) {
1343 pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
1344 return rc;
1345 }
1346
1347 /* break if a ccadc conversion is not happening */
1348 in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
1349 & ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
1350
1351 if (!in_progress)
1352 break;
1353 }
1354
1355 if (in_progress) {
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001356 pr_debug("conv in progress cannot write trim,returing EBUSY\n");
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001357 return -EBUSY;
1358 }
1359
1360 rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
1361 if (rc < 0) {
1362 pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
1363 return rc;
1364 }
1365 rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
1366 if (rc < 0) {
1367 pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
1368 return rc;
1369 }
1370 calib_ccadc_restore_trim_access(chip, sbi_config);
1371 return 0;
1372}
1373
1374static void calib_ccadc(struct pm8921_bms_chip *chip)
1375{
1376 u8 data_msb, data_lsb, sec_cntrl;
1377 int result_offset, voltage_offset, result_gain;
1378 u16 result;
1379 int i, rc;
1380
1381 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_CNTRL, &sec_cntrl);
1382 if (rc < 0) {
1383 pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
1384 return;
1385 }
1386
1387 rc = calib_ccadc_enable_arbiter(chip);
1388 if (rc < 0) {
1389 pr_err("error = %d enabling arbiter for offset\n", rc);
1390 goto bail;
1391 }
1392
1393 /*
1394 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1395 * up, pending verification through bench
1396 */
1397 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1398 CCADC_CALIB_DIG_PARAM);
1399 if (rc < 0) {
1400 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
1401 goto bail;
1402 }
1403
1404 result_offset = 0;
1405 for (i = 0; i < SAMPLE_COUNT; i++) {
1406 /* Short analog inputs to CCADC internally to ground */
1407 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_RSV,
1408 CCADC_CALIB_RSV_GND);
1409 if (rc < 0) {
1410 pr_err("error = %d selecting gnd voltage\n", rc);
1411 goto bail;
1412 }
1413
1414 /* Enable CCADC */
1415 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1416 CCADC_CALIB_ANA_PARAM);
1417 if (rc < 0) {
1418 pr_err("error = %d enabling ccadc\n", rc);
1419 goto bail;
1420 }
1421
1422 rc = calib_start_conv(chip, &result);
1423 if (rc < 0) {
1424 pr_err("error = %d for zero volt measurement\n", rc);
1425 goto bail;
1426 }
1427
1428 result_offset += result;
1429 }
1430
1431 result_offset = result_offset / SAMPLE_COUNT;
1432
1433 voltage_offset = ccadc_reading_to_microvolt(chip,
1434 ((s64)result_offset - CCADC_INTRINSIC_OFFSET));
1435
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001436 pr_debug("offset result_offset = 0x%x, voltage = %d microVolts\n",
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001437 result_offset, voltage_offset);
1438
1439 /* Sanity Check */
1440 if (voltage_offset > CCADC_MAX_0UV) {
1441 pr_err("offset voltage = %d is huge limiting to %d\n",
1442 voltage_offset, CCADC_MAX_0UV);
1443 result_offset = CCADC_INTRINSIC_OFFSET
1444 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MAX_0UV);
1445 } else if (voltage_offset < CCADC_MIN_0UV) {
1446 pr_err("offset voltage = %d is too low limiting to %d\n",
1447 voltage_offset, CCADC_MIN_0UV);
1448 result_offset = CCADC_INTRINSIC_OFFSET
1449 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MIN_0UV);
1450 }
1451
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001452 chip->ccadc_result_offset = result_offset;
1453 data_msb = chip->ccadc_result_offset >> 8;
1454 data_lsb = chip->ccadc_result_offset;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001455
1456 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001457 data_msb, data_lsb, 1);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001458 if (rc) {
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001459 pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
1460 rc, data_msb, data_lsb);
1461 /* enable the interrupt and write it when it fires */
1462 pm8921_bms_enable_irq(chip, PM8921_BMS_CCADC_EOC);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001463 }
1464
1465 rc = calib_ccadc_enable_arbiter(chip);
1466 if (rc < 0) {
1467 pr_err("error = %d enabling arbiter for gain\n", rc);
1468 goto bail;
1469 }
1470
1471 /*
1472 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1473 * up, pending verification through bench
1474 */
1475 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1476 CCADC_CALIB_DIG_PARAM);
1477 if (rc < 0) {
1478 pr_err("error = %d enabling decimation ration for gain\n", rc);
1479 goto bail;
1480 }
1481
1482 result_gain = 0;
1483 for (i = 0; i < SAMPLE_COUNT; i++) {
1484 rc = pm8xxx_writeb(chip->dev->parent,
1485 ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
1486 if (rc < 0) {
1487 pr_err("error = %d selecting 25mV for gain\n", rc);
1488 goto bail;
1489 }
1490
1491 /* Enable CCADC */
1492 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1493 CCADC_CALIB_ANA_PARAM);
1494 if (rc < 0) {
1495 pr_err("error = %d enabling ccadc\n", rc);
1496 goto bail;
1497 }
1498
1499 rc = calib_start_conv(chip, &result);
1500 if (rc < 0) {
1501 pr_err("error = %d for adc reading 25mV\n", rc);
1502 goto bail;
1503 }
1504
1505 result_gain += result;
1506 }
1507 result_gain = result_gain / SAMPLE_COUNT;
1508
1509 /*
1510 * result_offset includes INTRINSIC OFFSET
1511 * chip->ccadc_gain_uv will be the actual voltage
1512 * measured for 25000UV
1513 */
1514 chip->ccadc_gain_uv = ccadc_reading_to_microvolt(chip,
1515 ((s64)result_gain - result_offset));
1516
1517 pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
1518 result_gain,
1519 chip->ccadc_gain_uv);
1520 /* Sanity Check */
1521 if (chip->ccadc_gain_uv > CCADC_MAX_25MV) {
1522 pr_err("gain voltage = %d is huge limiting to %d\n",
1523 chip->ccadc_gain_uv, CCADC_MAX_25MV);
1524 chip->ccadc_gain_uv = CCADC_MAX_25MV;
1525 result_gain = result_offset +
1526 microvolt_to_ccadc_reading(chip, CCADC_MAX_25MV);
1527 } else if (chip->ccadc_gain_uv < CCADC_MIN_25MV) {
1528 pr_err("gain voltage = %d is too low limiting to %d\n",
1529 chip->ccadc_gain_uv, CCADC_MIN_25MV);
1530 chip->ccadc_gain_uv = CCADC_MIN_25MV;
1531 result_gain = result_offset +
1532 microvolt_to_ccadc_reading(chip, CCADC_MIN_25MV);
1533 }
1534
1535 data_msb = result_gain >> 8;
1536 data_lsb = result_gain;
1537 rc = calib_ccadc_program_trim(chip, CCADC_FULLSCALE_TRIM1,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001538 data_msb, data_lsb, 0);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001539 if (rc)
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001540 pr_debug("error = %d programming gain trim\n", rc);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001541bail:
1542 pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
1543}
1544
1545static void calibrate_ccadc_work(struct work_struct *work)
1546{
1547 struct pm8921_bms_chip *chip = container_of(work,
1548 struct pm8921_bms_chip, calib_ccadc_work.work);
1549
1550 calib_ccadc(chip);
1551 schedule_delayed_work(&chip->calib_ccadc_work,
1552 round_jiffies_relative(msecs_to_jiffies
1553 (chip->calib_delay_ms)));
1554}
1555
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001556int pm8921_bms_get_vsense_avg(int *result)
1557{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001558 int rc = -EINVAL;
1559 unsigned long flags;
1560
1561 if (the_chip) {
1562 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1563 pm_bms_lock_output_data(the_chip);
1564 rc = read_vsense_avg(the_chip, result);
1565 pm_bms_unlock_output_data(the_chip);
1566 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1567 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001568
1569 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001570 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001571}
1572EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1573
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001574int pm8921_bms_get_battery_current(int *result)
1575{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001576 unsigned long flags;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001577 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001578
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001579 if (!the_chip) {
1580 pr_err("called before initialization\n");
1581 return -EINVAL;
1582 }
1583 if (the_chip->r_sense == 0) {
1584 pr_err("r_sense is zero\n");
1585 return -EINVAL;
1586 }
1587
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001588 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1589 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001590 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001591 pm_bms_unlock_output_data(the_chip);
1592 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001593 pr_debug("vsense=%d\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001594 /* cast for signed division */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001595 *result = vsense / (int)the_chip->r_sense;
1596
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001597 return 0;
1598}
1599EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1600
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001601int pm8921_bms_get_percent_charge(void)
1602{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001603 int batt_temp, rc;
1604 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001605
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001606 if (!the_chip) {
1607 pr_err("called before initialization\n");
1608 return -EINVAL;
1609 }
1610
1611 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1612 if (rc) {
1613 pr_err("error reading adc channel = %d, rc = %d\n",
1614 the_chip->batt_temp_channel, rc);
1615 return rc;
1616 }
1617 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1618 result.measurement);
1619 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001620 return calculate_state_of_charge(the_chip,
1621 batt_temp, last_chargecycles);
1622}
1623EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1624
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001625int pm8921_bms_get_fcc(void)
1626{
1627 int batt_temp, rc;
1628 struct pm8921_adc_chan_result result;
1629
1630 if (!the_chip) {
1631 pr_err("called before initialization\n");
1632 return -EINVAL;
1633 }
1634
1635 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1636 if (rc) {
1637 pr_err("error reading adc channel = %d, rc = %d\n",
1638 the_chip->batt_temp_channel, rc);
1639 return rc;
1640 }
1641 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1642 result.measurement);
1643 batt_temp = (int)result.physical;
1644 return calculate_fcc(the_chip, batt_temp, last_chargecycles);
1645}
1646EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1647
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001648static int start_percent;
1649static int end_percent;
1650void pm8921_bms_charging_began(void)
1651{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001652 start_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001653 pr_debug("start_percent = %u%%\n", start_percent);
1654}
1655EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1656
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001657void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001658{
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001659 if (is_battery_full && the_chip != NULL) {
1660 int batt_temp, rc;
1661 struct pm8921_adc_chan_result result;
1662
1663 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1664 if (rc) {
1665 pr_err("error reading adc channel = %d, rc = %d\n",
1666 the_chip->batt_temp_channel, rc);
1667 goto charge_cycle_calculation;
1668 }
1669 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1670 result.measurement);
1671 batt_temp = (int)result.physical;
1672 last_real_fcc = calculate_real_fcc(the_chip,
1673 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001674 last_real_fcc_batt_temp = batt_temp;
1675 readjust_fcc_table();
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001676 }
1677
1678charge_cycle_calculation:
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001679 end_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001680 if (end_percent > start_percent) {
1681 last_charge_increase = end_percent - start_percent;
1682 if (last_charge_increase > 100) {
1683 last_chargecycles++;
1684 last_charge_increase = last_charge_increase % 100;
1685 }
1686 }
1687 pr_debug("end_percent = %u%% last_charge_increase = %d"
1688 "last_chargecycles = %d\n",
1689 end_percent,
1690 last_charge_increase,
1691 last_chargecycles);
1692}
1693EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1694
1695static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1696{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001697 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001698 return IRQ_HANDLED;
1699}
1700
1701static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1702{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001703 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001704 return IRQ_HANDLED;
1705}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001706
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001707static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1708{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001709 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001710 return IRQ_HANDLED;
1711}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001712
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001713static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1714{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001715 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001716 return IRQ_HANDLED;
1717}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001718
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001719static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1720{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001721 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001722
1723 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001724 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001725 return IRQ_HANDLED;
1726}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001727
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001728static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1729{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001730 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001731
1732 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001733 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001734 return IRQ_HANDLED;
1735}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001736
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001737static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1738{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001739 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001740 return IRQ_HANDLED;
1741}
1742
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001743static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
1744{
1745 u8 data_msb, data_lsb;
1746 struct pm8921_bms_chip *chip = data;
1747 int rc;
1748
1749 pr_debug("irq = %d triggered\n", irq);
1750 data_msb = chip->ccadc_result_offset >> 8;
1751 data_lsb = chip->ccadc_result_offset;
1752
1753 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
1754 data_msb, data_lsb, 0);
1755 pm8921_bms_disable_irq(chip, PM8921_BMS_CCADC_EOC);
1756
1757 return IRQ_HANDLED;
1758}
1759
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001760struct pm_bms_irq_init_data {
1761 unsigned int irq_id;
1762 char *name;
1763 unsigned long flags;
1764 irqreturn_t (*handler)(int, void *);
1765};
1766
1767#define BMS_IRQ(_id, _flags, _handler) \
1768{ \
1769 .irq_id = _id, \
1770 .name = #_id, \
1771 .flags = _flags, \
1772 .handler = _handler, \
1773}
1774
1775struct pm_bms_irq_init_data bms_irq_data[] = {
1776 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1777 pm8921_bms_sbi_write_ok_handler),
1778 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1779 pm8921_bms_cc_thr_handler),
1780 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1781 pm8921_bms_vsense_thr_handler),
1782 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1783 pm8921_bms_vsense_for_r_handler),
1784 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1785 pm8921_bms_ocv_for_r_handler),
1786 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1787 pm8921_bms_good_ocv_handler),
1788 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1789 pm8921_bms_vsense_avg_handler),
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001790 BMS_IRQ(PM8921_BMS_CCADC_EOC, IRQF_TRIGGER_RISING,
1791 pm8921_bms_ccadc_eoc_handler),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001792};
1793
1794static void free_irqs(struct pm8921_bms_chip *chip)
1795{
1796 int i;
1797
1798 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1799 if (chip->pmic_bms_irq[i]) {
1800 free_irq(chip->pmic_bms_irq[i], NULL);
1801 chip->pmic_bms_irq[i] = 0;
1802 }
1803}
1804
1805static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1806 struct platform_device *pdev)
1807{
1808 struct resource *res;
1809 int ret, i;
1810
1811 ret = 0;
1812 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1813
1814 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1815 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1816 bms_irq_data[i].name);
1817 if (res == NULL) {
1818 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1819 goto err_out;
1820 }
1821 ret = request_irq(res->start, bms_irq_data[i].handler,
1822 bms_irq_data[i].flags,
1823 bms_irq_data[i].name, chip);
1824 if (ret < 0) {
1825 pr_err("couldn't request %d (%s) %d\n", res->start,
1826 bms_irq_data[i].name, ret);
1827 goto err_out;
1828 }
1829 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1830 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1831 }
1832 return 0;
1833
1834err_out:
1835 free_irqs(chip);
1836 return -EINVAL;
1837}
1838
1839#define EN_BMS_BIT BIT(7)
1840#define EN_PON_HS_BIT BIT(0)
1841static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1842{
1843 int rc;
1844
1845 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1846 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1847 if (rc) {
1848 pr_err("failed to enable pon and bms addr = %d %d",
1849 BMS_CONTROL, rc);
1850 }
1851
1852 return 0;
1853}
1854
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001855static void check_initial_ocv(struct pm8921_bms_chip *chip)
1856{
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001857 int ocv, rc;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001858
1859 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001860 * Check if a ocv is available in bms hw,
1861 * if not compute it here at boot time and save it
1862 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001863 */
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001864 ocv = 0;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001865 rc = read_last_good_ocv(chip, &ocv);
1866 if (rc || ocv == 0) {
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001867 rc = adc_based_ocv(chip, &ocv);
1868 if (rc) {
1869 pr_err("failed to read adc based ocv rc = %d\n", rc);
1870 ocv = DEFAULT_OCV_MICROVOLTS;
1871 }
1872 last_ocv_uv = ocv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001873 }
1874 pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
1875}
1876
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001877static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1878{
1879 int rc;
1880 struct pm8921_adc_chan_result result;
1881
1882 rc = pm8921_adc_read(chip->batt_id_channel, &result);
1883 if (rc) {
1884 pr_err("error reading batt id channel = %d, rc = %d\n",
1885 chip->vbat_channel, rc);
1886 return rc;
1887 }
1888 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1889 result.measurement);
1890 return result.physical;
1891}
1892
1893#define PALLADIUM_ID_MIN 2500
1894#define PALLADIUM_ID_MAX 4000
1895static int set_battery_data(struct pm8921_bms_chip *chip)
1896{
1897 int64_t battery_id;
1898
1899 battery_id = read_battery_id(chip);
1900
1901 if (battery_id < 0) {
1902 pr_err("cannot read battery id err = %lld\n", battery_id);
1903 return battery_id;
1904 }
1905
1906 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
1907 chip->fcc = palladium_1500_data.fcc;
1908 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1909 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1910 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1911 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1912 return 0;
1913 } else {
1914 pr_warn("invalid battery id, palladium 1500 assumed\n");
1915 chip->fcc = palladium_1500_data.fcc;
1916 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1917 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1918 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1919 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1920 return 0;
1921 }
1922}
1923
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001924enum {
1925 CALC_RBATT,
1926 CALC_FCC,
1927 CALC_PC,
1928 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001929 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001930 CALIB_CCADC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001931};
1932
1933static int test_batt_temp = 5;
1934static int test_chargecycle = 150;
1935static int test_ocv = 3900000;
1936enum {
1937 TEST_BATT_TEMP,
1938 TEST_CHARGE_CYCLE,
1939 TEST_OCV,
1940};
1941static int get_test_param(void *data, u64 * val)
1942{
1943 switch ((int)data) {
1944 case TEST_BATT_TEMP:
1945 *val = test_batt_temp;
1946 break;
1947 case TEST_CHARGE_CYCLE:
1948 *val = test_chargecycle;
1949 break;
1950 case TEST_OCV:
1951 *val = test_ocv;
1952 break;
1953 default:
1954 return -EINVAL;
1955 }
1956 return 0;
1957}
1958static int set_test_param(void *data, u64 val)
1959{
1960 switch ((int)data) {
1961 case TEST_BATT_TEMP:
1962 test_batt_temp = (int)val;
1963 break;
1964 case TEST_CHARGE_CYCLE:
1965 test_chargecycle = (int)val;
1966 break;
1967 case TEST_OCV:
1968 test_ocv = (int)val;
1969 break;
1970 default:
1971 return -EINVAL;
1972 }
1973 return 0;
1974}
1975DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
1976
1977static int get_calc(void *data, u64 * val)
1978{
1979 int param = (int)data;
1980 int ret = 0;
1981
1982 *val = 0;
1983
1984 /* global irq number passed in via data */
1985 switch (param) {
1986 case CALC_RBATT:
1987 *val = calculate_rbatt(the_chip);
1988 break;
1989 case CALC_FCC:
1990 *val = calculate_fcc(the_chip, test_batt_temp,
1991 test_chargecycle);
1992 break;
1993 case CALC_PC:
1994 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
1995 test_chargecycle);
1996 break;
1997 case CALC_SOC:
1998 *val = calculate_state_of_charge(the_chip,
1999 test_batt_temp, test_chargecycle);
2000 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002001 case CALIB_HKADC:
2002 /* reading this will trigger calibration */
2003 *val = 0;
2004 calib_hkadc(the_chip);
2005 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002006 case CALIB_CCADC:
2007 /* reading this will trigger calibration */
2008 *val = 0;
2009 calib_ccadc(the_chip);
2010 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002011 default:
2012 ret = -EINVAL;
2013 }
2014 return ret;
2015}
2016DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
2017
2018static int get_reading(void *data, u64 * val)
2019{
2020 int param = (int)data;
2021 int ret = 0;
2022
2023 *val = 0;
2024
2025 /* global irq number passed in via data */
2026 switch (param) {
2027 case CC_MSB:
2028 case CC_LSB:
2029 read_cc(the_chip, (int *)val);
2030 break;
2031 case LAST_GOOD_OCV_VALUE:
2032 read_last_good_ocv(the_chip, (uint *)val);
2033 break;
2034 case VBATT_FOR_RBATT:
2035 read_vbatt_for_rbatt(the_chip, (uint *)val);
2036 break;
2037 case VSENSE_FOR_RBATT:
2038 read_vsense_for_rbatt(the_chip, (uint *)val);
2039 break;
2040 case OCV_FOR_RBATT:
2041 read_ocv_for_rbatt(the_chip, (uint *)val);
2042 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002043 case VSENSE_AVG:
2044 read_vsense_avg(the_chip, (uint *)val);
2045 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002046 default:
2047 ret = -EINVAL;
2048 }
2049 return ret;
2050}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002051DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002052
2053static int get_rt_status(void *data, u64 * val)
2054{
2055 int i = (int)data;
2056 int ret;
2057
2058 /* global irq number passed in via data */
2059 ret = pm_bms_get_rt_status(the_chip, i);
2060 *val = ret;
2061 return 0;
2062}
2063DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2064
2065static int get_reg(void *data, u64 * val)
2066{
2067 int addr = (int)data;
2068 int ret;
2069 u8 temp;
2070
2071 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2072 if (ret) {
2073 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2074 addr, temp, ret);
2075 return -EAGAIN;
2076 }
2077 *val = temp;
2078 return 0;
2079}
2080
2081static int set_reg(void *data, u64 val)
2082{
2083 int addr = (int)data;
2084 int ret;
2085 u8 temp;
2086
2087 temp = (u8) val;
2088 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2089 if (ret) {
2090 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2091 addr, temp, ret);
2092 return -EAGAIN;
2093 }
2094 return 0;
2095}
2096DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2097
2098static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2099{
2100 int i;
2101
2102 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
2103
2104 if (IS_ERR(chip->dent)) {
2105 pr_err("pmic bms couldnt create debugfs dir\n");
2106 return;
2107 }
2108
2109 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2110 (void *)BMS_CONTROL, &reg_fops);
2111 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2112 (void *)BMS_OUTPUT0, &reg_fops);
2113 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2114 (void *)BMS_OUTPUT1, &reg_fops);
2115 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2116 (void *)BMS_TEST1, &reg_fops);
2117 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
2118 (void *)CCADC_ANA_PARAM, &reg_fops);
2119 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
2120 (void *)CCADC_DIG_PARAM, &reg_fops);
2121 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
2122 (void *)CCADC_RSV, &reg_fops);
2123 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
2124 (void *)CCADC_DATA0, &reg_fops);
2125 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
2126 (void *)CCADC_DATA1, &reg_fops);
2127 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
2128 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
2129 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
2130 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
2131
2132 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2133 (void *)TEST_BATT_TEMP, &temp_fops);
2134 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2135 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2136 debugfs_create_file("test_ocv", 0644, chip->dent,
2137 (void *)TEST_OCV, &temp_fops);
2138
2139 debugfs_create_file("read_cc", 0644, chip->dent,
2140 (void *)CC_MSB, &reading_fops);
2141 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2142 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2143 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2144 (void *)VBATT_FOR_RBATT, &reading_fops);
2145 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2146 (void *)VSENSE_FOR_RBATT, &reading_fops);
2147 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2148 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002149 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2150 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002151
2152 debugfs_create_file("show_rbatt", 0644, chip->dent,
2153 (void *)CALC_RBATT, &calc_fops);
2154 debugfs_create_file("show_fcc", 0644, chip->dent,
2155 (void *)CALC_FCC, &calc_fops);
2156 debugfs_create_file("show_pc", 0644, chip->dent,
2157 (void *)CALC_PC, &calc_fops);
2158 debugfs_create_file("show_soc", 0644, chip->dent,
2159 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002160 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2161 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002162 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2163 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002164
2165 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2166 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2167 debugfs_create_file(bms_irq_data[i].name, 0444,
2168 chip->dent,
2169 (void *)bms_irq_data[i].irq_id,
2170 &rt_fops);
2171 }
2172}
2173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002174static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2175{
2176 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002177 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002178 struct pm8921_bms_chip *chip;
2179 const struct pm8921_bms_platform_data *pdata
2180 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002181
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002182 if (!pdata) {
2183 pr_err("missing platform data\n");
2184 return -EINVAL;
2185 }
2186
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002187 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002188 if (!chip) {
2189 pr_err("Cannot allocate pm_bms_chip\n");
2190 return -ENOMEM;
2191 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002192 spin_lock_init(&chip->bms_output_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002193 chip->dev = &pdev->dev;
2194 chip->r_sense = pdata->r_sense;
2195 chip->i_test = pdata->i_test;
2196 chip->v_failure = pdata->v_failure;
2197 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002198 rc = set_battery_data(chip);
2199 if (rc) {
2200 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002201 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002202 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002203
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002204 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2205 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002206 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2207 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002208 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002209 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002210 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002211
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002212 rc = request_irqs(chip, pdev);
2213 if (rc) {
2214 pr_err("couldn't register interrupts rc = %d\n", rc);
2215 goto free_chip;
2216 }
2217
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002218 rc = pm8921_bms_hw_init(chip);
2219 if (rc) {
2220 pr_err("couldn't init hardware rc = %d\n", rc);
2221 goto free_irqs;
2222 }
2223
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002224 platform_set_drvdata(pdev, chip);
2225 the_chip = chip;
2226 create_debugfs_entries(chip);
2227
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002228 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002229 chip->ccadc_gain_uv = calib_ccadc_read_gain_uv(chip);
2230
2231 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
2232 /* begin calibration only on chips > 2.0 */
2233 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
2234 calibrate_ccadc_work(&(chip->calib_ccadc_work.work));
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002235
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002236 /* initial hkadc calibration */
2237 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002238 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2239 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2240 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002241
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002242 get_battery_uvolts(chip, &vbatt);
2243 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2244 pm8921_bms_get_percent_charge(),
2245 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002246 return 0;
2247
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002248free_irqs:
2249 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002250free_chip:
2251 kfree(chip);
2252 return rc;
2253}
2254
2255static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2256{
2257 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2258
2259 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002260 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002261 platform_set_drvdata(pdev, NULL);
2262 the_chip = NULL;
2263 kfree(chip);
2264 return 0;
2265}
2266
2267static struct platform_driver pm8921_bms_driver = {
2268 .probe = pm8921_bms_probe,
2269 .remove = __devexit_p(pm8921_bms_remove),
2270 .driver = {
2271 .name = PM8921_BMS_DEV_NAME,
2272 .owner = THIS_MODULE,
2273 },
2274};
2275
2276static int __init pm8921_bms_init(void)
2277{
2278 return platform_driver_register(&pm8921_bms_driver);
2279}
2280
2281static void __exit pm8921_bms_exit(void)
2282{
2283 platform_driver_unregister(&pm8921_bms_driver);
2284}
2285
2286late_initcall(pm8921_bms_init);
2287module_exit(pm8921_bms_exit);
2288
2289MODULE_LICENSE("GPL v2");
2290MODULE_DESCRIPTION("PMIC8921 bms driver");
2291MODULE_VERSION("1.0");
2292MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);