blob: a6251ffd3a4b0800fc102b8c88f9e0a89ff3dd3b [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;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -070092 unsigned int charging_began;
93 unsigned int start_percent;
94 unsigned int end_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095};
96
97static struct pm8921_bms_chip *the_chip;
98
99#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101#define DEFAULT_CHARGE_CYCLES 0
102
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700103static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
104static int last_charge_increase;
105module_param(last_chargecycles, int, 0644);
106module_param(last_charge_increase, int, 0644);
107
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110static int last_soc = -EINVAL;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700111static int last_real_fcc = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700112static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700114static int bms_ops_set(const char *val, const struct kernel_param *kp)
115{
116 if (*(int *)kp->arg == -EINVAL)
117 return param_set_int(val, kp);
118 else
119 return 0;
120}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700122static struct kernel_param_ops bms_param_ops = {
123 .set = bms_ops_set,
124 .get = param_get_int,
125};
126
127module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
128module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
129module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
130
131static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
132static void readjust_fcc_table(void)
133{
134 struct single_row_lut *temp, *old;
135 int i, fcc, ratio;
136
137 if (!the_chip->fcc_temp_lut) {
138 pr_err("The static fcc lut table is NULL\n");
139 return;
140 }
141
142 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
143 if (!temp) {
144 pr_err("Cannot allocate memory for adjusted fcc table\n");
145 return;
146 }
147
148 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
149
150 temp->cols = the_chip->fcc_temp_lut->cols;
151 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
152 temp->x[i] = the_chip->fcc_temp_lut->x[i];
153 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
154 temp->y[i] = (ratio * last_real_fcc);
155 temp->y[i] /= 1000;
156 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
157 temp->x[i], the_chip->fcc_temp_lut->y[i],
158 temp->y[i], ratio);
159 }
160
161 old = the_chip->adjusted_fcc_temp_lut;
162 the_chip->adjusted_fcc_temp_lut = temp;
163 kfree(old);
164}
165
166static int bms_last_real_fcc_set(const char *val,
167 const struct kernel_param *kp)
168{
169 int rc = 0;
170
171 if (last_real_fcc == -EINVAL)
172 rc = param_set_int(val, kp);
173 if (rc) {
174 pr_err("Failed to set last_real_fcc rc=%d\n", rc);
175 return rc;
176 }
177 if (last_real_fcc_batt_temp != -EINVAL)
178 readjust_fcc_table();
179 return rc;
180}
181static struct kernel_param_ops bms_last_real_fcc_param_ops = {
182 .set = bms_last_real_fcc_set,
183 .get = param_get_int,
184};
185module_param_cb(last_real_fcc, &bms_last_real_fcc_param_ops,
186 &last_real_fcc, 0644);
187
188static int bms_last_real_fcc_batt_temp_set(const char *val,
189 const struct kernel_param *kp)
190{
191 int rc = 0;
192
193 if (last_real_fcc_batt_temp == -EINVAL)
194 rc = param_set_int(val, kp);
195 if (rc) {
196 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
197 return rc;
198 }
199 if (last_real_fcc != -EINVAL)
200 readjust_fcc_table();
201 return rc;
202}
203
204static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
205 .set = bms_last_real_fcc_batt_temp_set,
206 .get = param_get_int,
207};
208module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
209 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210
211static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
212{
213 return pm8xxx_read_irq_stat(chip->dev->parent,
214 chip->pmic_bms_irq[irq_id]);
215}
216
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700217static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
218{
219 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
220 dev_dbg(chip->dev, "%s %d\n", __func__,
221 chip->pmic_bms_irq[interrupt]);
222 enable_irq(chip->pmic_bms_irq[interrupt]);
223 }
224}
225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
227{
228 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700229 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
231 }
232}
233
234static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
235 u8 mask, u8 val)
236{
237 int rc;
238 u8 reg;
239
240 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
241 if (rc) {
242 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
243 return rc;
244 }
245 reg &= ~mask;
246 reg |= val & mask;
247 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
248 if (rc) {
249 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
250 return rc;
251 }
252 return 0;
253}
254
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700255#define HOLD_OREG_DATA BIT(1)
256static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
257{
258 int rc;
259
260 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
261 HOLD_OREG_DATA);
262 if (rc) {
263 pr_err("couldnt lock bms output rc = %d\n", rc);
264 return rc;
265 }
266 return 0;
267}
268
269static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
270{
271 int rc;
272
273 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
274 if (rc) {
275 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
276 return rc;
277 }
278 return 0;
279}
280
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281#define SELECT_OUTPUT_DATA 0x1C
282#define SELECT_OUTPUT_TYPE_SHIFT 2
283#define OCV_FOR_RBATT 0x0
284#define VSENSE_FOR_RBATT 0x1
285#define VBATT_FOR_RBATT 0x2
286#define CC_MSB 0x3
287#define CC_LSB 0x4
288#define LAST_GOOD_OCV_VALUE 0x5
289#define VSENSE_AVG 0x6
290#define VBATT_AVG 0x7
291
292static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
293 int16_t *result)
294{
295 int rc;
296 u8 reg;
297
298 if (!result) {
299 pr_err("result pointer null\n");
300 return -EINVAL;
301 }
302 *result = 0;
303 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
304 pr_err("invalid type %d asked to read\n", type);
305 return -EINVAL;
306 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700307
308 /* make sure the bms registers are locked */
309 rc = pm8xxx_readb(chip->dev->parent, BMS_CONTROL, &reg);
310 if (rc) {
311 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
312 type, rc);
313 return rc;
314 }
315
316 /* Output register data must be held (locked) while reading output */
317 WARN_ON(!(reg && HOLD_OREG_DATA));
318
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
320 type << SELECT_OUTPUT_TYPE_SHIFT);
321 if (rc) {
322 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
323 type, rc);
324 return rc;
325 }
326
327 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
328 if (rc) {
329 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
330 type, rc);
331 return rc;
332 }
333 *result = reg;
334 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
335 if (rc) {
336 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
337 type, rc);
338 return rc;
339 }
340 *result |= reg << 8;
341 pr_debug("type %d result %x", type, *result);
342 return 0;
343}
344
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700345#define V_PER_BIT_MUL_FACTOR 97656
346#define V_PER_BIT_DIV_FACTOR 1000
347#define XOADC_INTRINSIC_OFFSET 0x6000
348static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700349{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700350 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700351 return 0;
352
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700353 return (a - XOADC_INTRINSIC_OFFSET)
354 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700355}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700357#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700358#define VBATT_MUL_FACTOR 3
359static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
360 unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700361{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700362 u64 numerator, denominator;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700363
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700364 if (uv == 0)
365 return 0;
366
367 numerator = ((u64)uv - chip->xoadc_v0625) * XOADC_CALIB_UV;
368 denominator = chip->xoadc_v125 - chip->xoadc_v0625;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700369 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700370 return uv * VBATT_MUL_FACTOR;
371 return (XOADC_CALIB_UV + div_u64(numerator, denominator))
372 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700373}
374
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700375#define CC_RESOLUTION_N_V1 1085069
376#define CC_RESOLUTION_D_V1 100000
377#define CC_RESOLUTION_N_V2 868056
378#define CC_RESOLUTION_D_V2 10000
379static s64 cc_to_microvolt_v1(s64 cc)
380{
381 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
382}
383
384static s64 cc_to_microvolt_v2(s64 cc)
385{
386 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
387}
388
389static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
390{
391 /*
392 * resolution (the value of a single bit) was changed after revision 2.0
393 * for more accurate readings
394 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700395 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700396 cc_to_microvolt_v1((s64)cc) :
397 cc_to_microvolt_v2((s64)cc);
398}
399
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700400#define CCADC_READING_RESOLUTION_N_V1 1085069
401#define CCADC_READING_RESOLUTION_D_V1 100000
402#define CCADC_READING_RESOLUTION_N_V2 542535
403#define CCADC_READING_RESOLUTION_D_V2 100000
404static s64 ccadc_reading_to_microvolt_v1(s64 cc)
405{
406 return div_s64(cc * CCADC_READING_RESOLUTION_N_V1,
407 CCADC_READING_RESOLUTION_D_V1);
408}
409
410static s64 ccadc_reading_to_microvolt_v2(s64 cc)
411{
412 return div_s64(cc * CCADC_READING_RESOLUTION_N_V2,
413 CCADC_READING_RESOLUTION_D_V2);
414}
415
416static s64 ccadc_reading_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
417{
418 /*
419 * resolution (the value of a single bit) was changed after revision 2.0
420 * for more accurate readings
421 */
422 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
423 ccadc_reading_to_microvolt_v1((s64)cc) :
424 ccadc_reading_to_microvolt_v2((s64)cc);
425}
426
427static s64 microvolt_to_ccadc_reading_v1(s64 uv)
428{
429 return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
430 CCADC_READING_RESOLUTION_N_V1);
431}
432
433static s64 microvolt_to_ccadc_reading_v2(s64 uv)
434{
435 return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
436 CCADC_READING_RESOLUTION_N_V2);
437}
438
439static s64 microvolt_to_ccadc_reading(struct pm8921_bms_chip *chip, s64 cc)
440{
441 /*
442 * resolution (the value of a single bit) was changed after revision 2.0
443 * for more accurate readings
444 */
445 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
446 microvolt_to_ccadc_reading_v1((s64)cc) :
447 microvolt_to_ccadc_reading_v2((s64)cc);
448}
449
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700450#define CC_READING_TICKS 55
451#define SLEEP_CLK_HZ 32768
452#define SECONDS_PER_HOUR 3600
453static s64 ccmicrovolt_to_uvh(s64 cc_uv)
454{
455 return div_s64(cc_uv * CC_READING_TICKS,
456 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
457}
458
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700459#define GAIN_REFERENCE_UV 25000
460/*
461 * gain compensation for ccadc readings - common for vsense based and
462 * couloumb counter based readings
463 */
464static s64 cc_adjust_for_gain(struct pm8921_bms_chip *chip, s64 cc)
465{
466 if (chip->ccadc_gain_uv == 0)
467 return cc;
468
469 return div_s64(cc * GAIN_REFERENCE_UV, chip->ccadc_gain_uv);
470}
471
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472/* returns the signed value read from the hardware */
473static int read_cc(struct pm8921_bms_chip *chip, int *result)
474{
475 int rc;
476 uint16_t msw, lsw;
477
478 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
479 if (rc) {
480 pr_err("fail to read CC_LSB rc = %d\n", rc);
481 return rc;
482 }
483 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
484 if (rc) {
485 pr_err("fail to read CC_MSB rc = %d\n", rc);
486 return rc;
487 }
488 *result = msw << 16 | lsw;
489 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
490 return 0;
491}
492
493static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
494{
495 int rc;
496 uint16_t reading;
497
498 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
499 if (rc) {
500 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
501 return rc;
502 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700503 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700504 pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
505 *result = adjust_xo_vbatt_reading(chip, *result);
506 pr_debug("after adj ocv_uV = %u\n", *result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700507 if (*result != 0)
508 last_ocv_uv = *result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509 return 0;
510}
511
512static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
513{
514 int rc;
515 uint16_t reading;
516
517 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
518 if (rc) {
519 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
520 return rc;
521 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700522 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700524 *result = adjust_xo_vbatt_reading(chip, *result);
525 pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700526 return 0;
527}
528
529static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
530{
531 int rc;
532 uint16_t reading;
533
534 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
535 if (rc) {
536 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
537 return rc;
538 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700539 *result = ccadc_reading_to_microvolt(chip, reading);
540 pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
541 *result = cc_adjust_for_gain(chip, *result);
542 pr_debug("after adj vsense_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543 return 0;
544}
545
546static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
547{
548 int rc;
549 uint16_t reading;
550
551 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
552 if (rc) {
553 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
554 return rc;
555 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700556 *result = xoadc_reading_to_microvolt(reading);
557 pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
558 *result = adjust_xo_vbatt_reading(chip, *result);
559 pr_debug("after adj ocv_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 return 0;
561}
562
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700563static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
564{
565 int rc;
566 int16_t reading;
567
568 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
569 if (rc) {
570 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
571 return rc;
572 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700573 *result = ccadc_reading_to_microvolt(chip, reading);
574 pr_debug("raw = %04x vsense = %d\n", reading, *result);
575 *result = cc_adjust_for_gain(the_chip, (s64)*result);
576 pr_debug("after adj vsense = %d\n", *result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700577 return 0;
578}
579
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
581{
582 if (y0 == y1 || x == x0)
583 return y0;
584 if (x1 == x0 || x == x1)
585 return y1;
586
587 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
588}
589
590static int interpolate_single_lut(struct single_row_lut *lut, int x)
591{
592 int i, result;
593
594 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700595 pr_debug("x %d less than known range return y = %d lut = %pS\n",
596 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597 return lut->y[0];
598 }
599 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700600 pr_debug("x %d more than known range return y = %d lut = %pS\n",
601 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700602 return lut->y[lut->cols - 1];
603 }
604
605 for (i = 0; i < lut->cols; i++)
606 if (x <= lut->x[i])
607 break;
608 if (x == lut->x[i]) {
609 result = lut->y[i];
610 } else {
611 result = linear_interpolate(
612 lut->y[i - 1],
613 lut->x[i - 1],
614 lut->y[i],
615 lut->x[i],
616 x);
617 }
618 return result;
619}
620
621static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
622{
623 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
624}
625
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700626static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
627{
628 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
629}
630
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700631static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
632 int cycles)
633{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700634 /*
635 * sf table could be null when no battery aging data is available, in
636 * that case return 100%
637 */
638 if (chip->fcc_sf_lut)
639 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
640 else
641 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642}
643
644static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
645 int cycles, int pc)
646{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700647 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700648 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700649 int row1 = 0;
650 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700652 /*
653 * sf table could be null when no battery aging data is available, in
654 * that case return 100%
655 */
656 if (!chip->pc_sf_lut)
657 return 100;
658
659 rows = chip->pc_sf_lut->rows;
660 cols = chip->pc_sf_lut->cols;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700661 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700662 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663 row1 = 0;
664 row2 = 0;
665 }
666 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700667 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668 row1 = rows - 1;
669 row2 = rows - 1;
670 }
671 for (i = 0; i < rows; i++) {
672 if (pc == chip->pc_sf_lut->percent[i]) {
673 row1 = i;
674 row2 = i;
675 break;
676 }
677 if (pc > chip->pc_sf_lut->percent[i]) {
678 row1 = i - 1;
679 row2 = i;
680 break;
681 }
682 }
683
684 if (cycles < chip->pc_sf_lut->cycles[0])
685 cycles = chip->pc_sf_lut->cycles[0];
686 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
687 cycles = chip->pc_sf_lut->cycles[cols - 1];
688
689 for (i = 0; i < cols; i++)
690 if (cycles <= chip->pc_sf_lut->cycles[i])
691 break;
692 if (cycles == chip->pc_sf_lut->cycles[i]) {
693 scalefactor = linear_interpolate(
694 chip->pc_sf_lut->sf[row1][i],
695 chip->pc_sf_lut->percent[row1],
696 chip->pc_sf_lut->sf[row2][i],
697 chip->pc_sf_lut->percent[row2],
698 pc);
699 return scalefactor;
700 }
701
702 scalefactorrow1 = linear_interpolate(
703 chip->pc_sf_lut->sf[row1][i - 1],
704 chip->pc_sf_lut->cycles[i - 1],
705 chip->pc_sf_lut->sf[row1][i],
706 chip->pc_sf_lut->cycles[i],
707 cycles);
708
709 scalefactorrow2 = linear_interpolate(
710 chip->pc_sf_lut->sf[row2][i - 1],
711 chip->pc_sf_lut->cycles[i - 1],
712 chip->pc_sf_lut->sf[row2][i],
713 chip->pc_sf_lut->cycles[i],
714 cycles);
715
716 scalefactor = linear_interpolate(
717 scalefactorrow1,
718 chip->pc_sf_lut->percent[row1],
719 scalefactorrow2,
720 chip->pc_sf_lut->percent[row2],
721 pc);
722
723 return scalefactor;
724}
725
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700726static int is_between(int left, int right, int value)
727{
728 if (left >= right && left >= value && value >= right)
729 return 1;
730 if (left <= right && left <= value && value <= right)
731 return 1;
732
733 return 0;
734}
735
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736static int interpolate_pc(struct pm8921_bms_chip *chip,
737 int batt_temp, int ocv)
738{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700739 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700740 int rows = chip->pc_temp_ocv_lut->rows;
741 int cols = chip->pc_temp_ocv_lut->cols;
742
743 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700744 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700745 batt_temp = chip->pc_temp_ocv_lut->temp[0];
746 }
747 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700748 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700749 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
750 }
751
752 for (j = 0; j < cols; j++)
753 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
754 break;
755 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
756 /* found an exact match for temp in the table */
757 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
758 return chip->pc_temp_ocv_lut->percent[0];
759 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
760 return chip->pc_temp_ocv_lut->percent[rows - 1];
761 for (i = 0; i < rows; i++) {
762 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
763 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
764 return
765 chip->pc_temp_ocv_lut->percent[i];
766 pc = linear_interpolate(
767 chip->pc_temp_ocv_lut->percent[i],
768 chip->pc_temp_ocv_lut->ocv[i][j],
769 chip->pc_temp_ocv_lut->percent[i - 1],
770 chip->pc_temp_ocv_lut->ocv[i - 1][j],
771 ocv);
772 return pc;
773 }
774 }
775 }
776
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700777 /*
778 * batt_temp is within temperature for
779 * column j-1 and j
780 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700781 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
782 return chip->pc_temp_ocv_lut->percent[0];
783 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
784 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700786 pcj_minus_one = 0;
787 pcj = 0;
788 for (i = 0; i < rows-1; i++) {
789 if (pcj == 0
790 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
791 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
792 pcj = linear_interpolate(
793 chip->pc_temp_ocv_lut->percent[i],
794 chip->pc_temp_ocv_lut->ocv[i][j],
795 chip->pc_temp_ocv_lut->percent[i + 1],
796 chip->pc_temp_ocv_lut->ocv[i+1][j],
797 ocv);
798 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700799
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700800 if (pcj_minus_one == 0
801 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
802 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700804 pcj_minus_one = linear_interpolate(
805 chip->pc_temp_ocv_lut->percent[i],
806 chip->pc_temp_ocv_lut->ocv[i][j-1],
807 chip->pc_temp_ocv_lut->percent[i + 1],
808 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
809 ocv);
810 }
811
812 if (pcj && pcj_minus_one) {
813 pc = linear_interpolate(
814 pcj_minus_one,
815 chip->pc_temp_ocv_lut->temp[j-1],
816 pcj,
817 chip->pc_temp_ocv_lut->temp[j],
818 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819 return pc;
820 }
821 }
822
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700823 if (pcj)
824 return pcj;
825
826 if (pcj_minus_one)
827 return pcj_minus_one;
828
829 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
830 ocv, batt_temp);
831 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700832}
833
834static int calculate_rbatt(struct pm8921_bms_chip *chip)
835{
836 int rc;
837 unsigned int ocv, vsense, vbatt, r_batt;
838
839 rc = read_ocv_for_rbatt(chip, &ocv);
840 if (rc) {
841 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
842 ocv = 0;
843 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700844
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845 rc = read_vbatt_for_rbatt(chip, &vbatt);
846 if (rc) {
847 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
848 ocv = 0;
849 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700850
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851 rc = read_vsense_for_rbatt(chip, &vsense);
852 if (rc) {
853 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
854 ocv = 0;
855 }
856 if (ocv == 0
857 || ocv == vbatt
858 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700859 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
860 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861 ocv, vbatt, vsense);
862 return -EINVAL;
863 }
864 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700865 last_rbatt = r_batt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700866 pr_debug("r_batt = %umilliOhms", r_batt);
867 return r_batt;
868}
869
870static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
871 int chargecycles)
872{
873 int initfcc, result, scalefactor = 0;
874
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700875 if (chip->adjusted_fcc_temp_lut == NULL) {
876 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700877
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700878 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700879
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700880 /* Multiply the initial FCC value by the scale factor. */
881 result = (initfcc * scalefactor) / 100;
882 pr_debug("fcc mAh = %d\n", result);
883 return result;
884 } else {
885 return interpolate_fcc_adjusted(chip, batt_temp);
886 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887}
888
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700889static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
890{
891 int rc;
892 struct pm8921_adc_chan_result result;
893
894 rc = pm8921_adc_read(chip->vbat_channel, &result);
895 if (rc) {
896 pr_err("error reading adc channel = %d, rc = %d\n",
897 chip->vbat_channel, rc);
898 return rc;
899 }
900 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
901 result.measurement);
902 *uvolts = (int)result.physical;
903 *uvolts = *uvolts * 1000;
904 return 0;
905}
906
907static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
908{
909 int vbatt, rbatt, ibatt, rc;
910
911 rc = get_battery_uvolts(chip, &vbatt);
912 if (rc) {
913 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700914 return rc;
915 }
916
917 rc = pm8921_bms_get_battery_current(&ibatt);
918 if (rc) {
919 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700920 return rc;
921 }
922
923 rbatt = calculate_rbatt(the_chip);
924 if (rbatt < 0)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700925 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700926 *ocv = vbatt + ibatt * rbatt;
927 return 0;
928}
929
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
931 int chargecycles)
932{
933 int pc, scalefactor;
934
935 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
936 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
937 pc, ocv_uv, batt_temp);
938
939 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
940 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
941
942 /* Multiply the initial FCC value by the scale factor. */
943 pc = (pc * scalefactor) / 100;
944 return pc;
945}
946
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700947static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700948 int *coulumb_counter)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700949{
950 int rc;
951 int64_t cc_voltage_uv, cc_uvh, cc_mah;
952
953 rc = read_cc(the_chip, coulumb_counter);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954 cc_voltage_uv = (int64_t)*coulumb_counter;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700955 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700956 cc_voltage_uv = cc_adjust_for_gain(chip, cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700958 cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
960 cc_mah = div_s64(cc_uvh, chip->r_sense);
961 *val = cc_mah;
962}
963
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700964static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
965 int fcc, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700966{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700967 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968
969 rbatt = calculate_rbatt(chip);
970 if (rbatt < 0) {
971 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700972 pr_debug("rbatt unavailable assuming %d\n", rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700974
975 /* calculate unusable charge */
976 voltage_unusable_uv = (rbatt * chip->i_test)
977 + (chip->v_failure * 1000);
978 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
979 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700980 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
981 rbatt, voltage_unusable_uv, pc_unusable);
982 return (fcc * pc_unusable) / 100;
983}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700984
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700985/* calculate remainging charge at the time of ocv */
986static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
987 int batt_temp, int chargecycles)
988{
989 int rc, ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700990
991 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700992 ocv = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993 rc = read_last_good_ocv(chip, &ocv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700994 if (rc)
995 pr_debug("failed to read ocv rc = %d\n", rc);
996
997 if (ocv == 0) {
998 ocv = last_ocv_uv;
999 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001000 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001001
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001002 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001003 pr_debug("ocv = %d pc = %d\n", ocv, pc);
1004 return (fcc * pc) / 100;
1005}
1006
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001007static void calculate_charging_params(struct pm8921_bms_chip *chip,
1008 int batt_temp, int chargecycles,
1009 int *fcc,
1010 int *unusable_charge,
1011 int *remaining_charge,
1012 int64_t *cc_mah)
1013{
1014 int coulumb_counter;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001015 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001016
1017 *fcc = calculate_fcc(chip, batt_temp, chargecycles);
1018 pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
1019 *fcc, batt_temp, chargecycles);
1020
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001021 /* fcc doesnt need to be read from hardware, lock the bms now */
1022 spin_lock_irqsave(&chip->bms_output_lock, flags);
1023 pm_bms_lock_output_data(chip);
1024
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001025 *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
1026 batt_temp, chargecycles);
1027
1028 pr_debug("UUC = %umAh", *unusable_charge);
1029
1030 /* calculate remainging charge */
1031 *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
1032 batt_temp, chargecycles);
1033 pr_debug("RC = %umAh\n", *remaining_charge);
1034
1035 /* calculate cc milli_volt_hour */
1036 calculate_cc_mah(chip, cc_mah, &coulumb_counter);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001037
1038 pm_bms_unlock_output_data(chip);
1039 spin_unlock_irqrestore(&chip->bms_output_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001040 pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
1041}
1042
1043static int calculate_real_fcc(struct pm8921_bms_chip *chip,
1044 int batt_temp, int chargecycles)
1045{
1046 int fcc, unusable_charge;
1047 int remaining_charge;
1048 int64_t cc_mah;
1049 int real_fcc;
1050
1051 calculate_charging_params(chip, batt_temp, chargecycles,
1052 &fcc,
1053 &unusable_charge,
1054 &remaining_charge,
1055 &cc_mah);
1056
1057 real_fcc = remaining_charge - cc_mah;
1058
1059 return real_fcc;
1060}
1061/*
1062 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1063 * - coloumb counter charge
1064 * - unusable charge (due to battery resistance)
1065 * SOC% = (remaining usable charge/ fcc - usable_charge);
1066 */
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001067#define BMS_BATT_NOMINAL 3700000
1068#define MIN_OPERABLE_SOC 10
1069#define BATTERY_POWER_SUPPLY_SOC 53
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001070static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
1071 int batt_temp, int chargecycles)
1072{
1073 int remaining_usable_charge, fcc, unusable_charge;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001074 int remaining_charge, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001075 int update_userspace = 1;
1076 int64_t cc_mah;
1077
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001078 calculate_charging_params(chip, batt_temp, chargecycles,
1079 &fcc,
1080 &unusable_charge,
1081 &remaining_charge,
1082 &cc_mah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001083
1084 /* calculate remaining usable charge */
1085 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001086 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001087 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
1088 if (soc > 100)
1089 soc = 100;
1090 pr_debug("SOC = %u%%\n", soc);
1091
1092 if (soc < MIN_OPERABLE_SOC) {
1093 int ocv = 0, rc;
1094
1095 rc = adc_based_ocv(chip, &ocv);
1096 if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
1097 /*
1098 * The ocv doesnt seem to have dropped for
1099 * soc to go negative.
1100 * The setup must be using a power supply
1101 * instead of real batteries.
1102 * Fake high enough soc to prevent userspace
1103 * shutdown for low battery
1104 */
1105 soc = BATTERY_POWER_SUPPLY_SOC;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001106 pr_debug("Adjusting SOC to %d\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001107 }
1108 }
1109
1110 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001111 pr_err("bad rem_usb_chg = %d rem_chg %d,"
1112 "cc_mah %lld, unusb_chg %d\n",
1113 remaining_usable_charge, remaining_charge,
1114 cc_mah, unusable_charge);
1115 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001116 "chargecycles = %d, batt_temp = %d"
1117 "fcc = %d soc =%d\n",
1118 last_ocv_uv, chargecycles, batt_temp,
1119 fcc, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001120 update_userspace = 0;
1121 }
1122
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001123 if (last_soc == -EINVAL || soc <= last_soc) {
1124 last_soc = update_userspace ? soc : last_soc;
1125 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001126 }
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001127
1128 /*
1129 * soc > last_soc
1130 * the device must be charging for reporting a higher soc, if not ignore
1131 * this soc and continue reporting the last_soc
1132 */
1133 if (the_chip->start_percent != 0) {
1134 last_soc = soc;
1135 } else {
1136 pr_debug("soc = %d reporting last_soc = %d\n", soc, last_soc);
1137 soc = last_soc;
1138 }
1139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001140 return soc;
1141}
1142
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001143#define XOADC_MAX_1P25V 1312500
1144#define XOADC_MIN_1P25V 1187500
1145#define XOADC_MAX_0P625V 656250
1146#define XOADC_MIN_0P625V 593750
1147
1148#define HKADC_V_PER_BIT_MUL_FACTOR 977
1149#define HKADC_V_PER_BIT_DIV_FACTOR 10
1150static int calib_hkadc_convert_microvolt(unsigned int phy)
1151{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001152 return (phy - 0x6000) *
1153 HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001154}
1155
1156static void calib_hkadc(struct pm8921_bms_chip *chip)
1157{
1158 int voltage, rc;
1159 struct pm8921_adc_chan_result result;
1160
1161 rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
1162 if (rc) {
1163 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1164 return;
1165 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001166 voltage = calib_hkadc_convert_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001167
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001168 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1169 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001170
1171 /* check for valid range */
1172 if (voltage > XOADC_MAX_1P25V)
1173 voltage = XOADC_MAX_1P25V;
1174 else if (voltage < XOADC_MIN_1P25V)
1175 voltage = XOADC_MIN_1P25V;
1176 chip->xoadc_v125 = voltage;
1177
1178 rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
1179 if (rc) {
1180 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1181 return;
1182 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001183 voltage = calib_hkadc_convert_microvolt(result.adc_code);
1184 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
1185 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001186 /* check for valid range */
1187 if (voltage > XOADC_MAX_0P625V)
1188 voltage = XOADC_MAX_0P625V;
1189 else if (voltage < XOADC_MIN_0P625V)
1190 voltage = XOADC_MIN_0P625V;
1191
1192 chip->xoadc_v0625 = voltage;
1193}
1194
1195static void calibrate_hkadc_work(struct work_struct *work)
1196{
1197 struct pm8921_bms_chip *chip = container_of(work,
1198 struct pm8921_bms_chip, calib_hkadc_work);
1199
1200 calib_hkadc(chip);
1201}
1202
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001203#define START_CONV_BIT BIT(7)
1204#define EOC_CONV_BIT BIT(6)
1205#define SEL_CCADC_BIT BIT(1)
1206#define EN_ARB_BIT BIT(0)
1207
1208#define CCADC_CALIB_DIG_PARAM 0xE3
1209#define CCADC_CALIB_RSV_GND 0x40
1210#define CCADC_CALIB_RSV_25MV 0x80
1211#define CCADC_CALIB_ANA_PARAM 0x1B
1212#define SAMPLE_COUNT 16
1213#define ADC_WAIT_COUNT 10
1214
1215#define CCADC_MAX_25MV 30000
1216#define CCADC_MIN_25MV 20000
1217#define CCADC_MAX_0UV -4000
1218#define CCADC_MIN_0UV -7000
1219
1220#define CCADC_INTRINSIC_OFFSET 0xC000
1221
1222#define REG_SBI_CONFIG 0x04F
1223#define PAGE3_ENABLE_MASK 0x6
1224
1225static int calib_ccadc_enable_trim_access(struct pm8921_bms_chip *chip,
1226 u8 *sbi_config)
1227{
1228 u8 reg;
1229 int rc;
1230
1231 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1232 if (rc) {
1233 pr_err("error = %d reading sbi config reg\n", rc);
1234 return rc;
1235 }
1236
1237 reg = *sbi_config | PAGE3_ENABLE_MASK;
1238 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
1239}
1240
1241static int calib_ccadc_restore_trim_access(struct pm8921_bms_chip *chip,
1242 u8 sbi_config)
1243{
1244 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1245}
1246
1247static int calib_ccadc_enable_arbiter(struct pm8921_bms_chip *chip)
1248{
1249 int rc;
1250
1251 /* enable Arbiter, must be sent twice */
1252 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1253 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1254 if (rc < 0) {
1255 pr_err("error = %d enabling arbiter for offset\n", rc);
1256 return rc;
1257 }
1258 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1259 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1260 if (rc < 0) {
1261 pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
1262 return rc;
1263 }
1264 return 0;
1265}
1266
1267static int calib_start_conv(struct pm8921_bms_chip *chip,
1268 u16 *result)
1269{
1270 int rc, i;
1271 u8 data_msb, data_lsb, reg;
1272
1273 /* Start conversion */
1274 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1275 START_CONV_BIT, START_CONV_BIT);
1276 if (rc < 0) {
1277 pr_err("error = %d starting offset meas\n", rc);
1278 return rc;
1279 }
1280
1281 /* Wait for End of conversion */
1282 for (i = 0; i < ADC_WAIT_COUNT; i++) {
1283 rc = pm8xxx_readb(chip->dev->parent,
1284 ADC_ARB_SECP_CNTRL, &reg);
1285 if (rc < 0) {
1286 pr_err("error = %d read eoc for offset\n", rc);
1287 return rc;
1288 }
1289 if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
1290 msleep(60);
1291 else
1292 break;
1293 }
1294 if (i == ADC_WAIT_COUNT) {
1295 pr_err("waited too long for offset eoc\n");
1296 return rc;
1297 }
1298
1299 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
1300 if (rc < 0) {
1301 pr_err("error = %d reading offset lsb\n", rc);
1302 return rc;
1303 }
1304
1305 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
1306 if (rc < 0) {
1307 pr_err("error = %d reading offset msb\n", rc);
1308 return rc;
1309 }
1310
1311 *result = (data_msb << 8) | data_lsb;
1312 return 0;
1313}
1314
1315static int calib_ccadc_read_trim(struct pm8921_bms_chip *chip,
1316 int addr, u8 *data_msb, u8 *data_lsb)
1317{
1318 int rc;
1319 u8 sbi_config;
1320
1321 calib_ccadc_enable_trim_access(chip, &sbi_config);
1322 rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
1323 if (rc < 0) {
1324 pr_err("error = %d read msb\n", rc);
1325 return rc;
1326 }
1327 rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
1328 if (rc < 0) {
1329 pr_err("error = %d read lsb\n", rc);
1330 return rc;
1331 }
1332 calib_ccadc_restore_trim_access(chip, sbi_config);
1333 return 0;
1334}
1335
1336static int calib_ccadc_read_gain_uv(struct pm8921_bms_chip *chip)
1337{
1338 s8 data_msb;
1339 u8 data_lsb;
1340 int rc, gain, offset;
1341
1342 rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
1343 &data_msb, &data_lsb);
1344 gain = (data_msb << 8) | data_lsb;
1345
1346 rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
1347 &data_msb, &data_lsb);
1348 offset = (data_msb << 8) | data_lsb;
1349
1350 pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", gain, offset);
1351 gain = ccadc_reading_to_microvolt(chip, (s64)gain - offset);
1352 return gain;
1353}
1354
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001355#define CCADC_PROGRAM_TRIM_COUNT 2
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001356#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT 4
1357#define ADC_ARB_BMS_CNTRL_CONV_MASK 0x03
1358#define BMS_CONV_IN_PROGRESS 0x2
1359
1360static int calib_ccadc_program_trim(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001361 int addr, u8 data_msb, u8 data_lsb,
1362 int wait)
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001363{
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001364 int i, rc, loop;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001365 u8 cntrl, sbi_config;
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001366 bool in_progress = 0;
1367
1368 loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001369
1370 calib_ccadc_enable_trim_access(chip, &sbi_config);
1371
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001372 for (i = 0; i < loop; i++) {
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001373 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
1374 if (rc < 0) {
1375 pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
1376 return rc;
1377 }
1378
1379 /* break if a ccadc conversion is not happening */
1380 in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
1381 & ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
1382
1383 if (!in_progress)
1384 break;
1385 }
1386
1387 if (in_progress) {
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001388 pr_debug("conv in progress cannot write trim,returing EBUSY\n");
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001389 return -EBUSY;
1390 }
1391
1392 rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
1393 if (rc < 0) {
1394 pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
1395 return rc;
1396 }
1397 rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
1398 if (rc < 0) {
1399 pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
1400 return rc;
1401 }
1402 calib_ccadc_restore_trim_access(chip, sbi_config);
1403 return 0;
1404}
1405
1406static void calib_ccadc(struct pm8921_bms_chip *chip)
1407{
1408 u8 data_msb, data_lsb, sec_cntrl;
1409 int result_offset, voltage_offset, result_gain;
1410 u16 result;
1411 int i, rc;
1412
1413 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_CNTRL, &sec_cntrl);
1414 if (rc < 0) {
1415 pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
1416 return;
1417 }
1418
1419 rc = calib_ccadc_enable_arbiter(chip);
1420 if (rc < 0) {
1421 pr_err("error = %d enabling arbiter for offset\n", rc);
1422 goto bail;
1423 }
1424
1425 /*
1426 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1427 * up, pending verification through bench
1428 */
1429 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1430 CCADC_CALIB_DIG_PARAM);
1431 if (rc < 0) {
1432 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
1433 goto bail;
1434 }
1435
1436 result_offset = 0;
1437 for (i = 0; i < SAMPLE_COUNT; i++) {
1438 /* Short analog inputs to CCADC internally to ground */
1439 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_RSV,
1440 CCADC_CALIB_RSV_GND);
1441 if (rc < 0) {
1442 pr_err("error = %d selecting gnd voltage\n", rc);
1443 goto bail;
1444 }
1445
1446 /* Enable CCADC */
1447 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1448 CCADC_CALIB_ANA_PARAM);
1449 if (rc < 0) {
1450 pr_err("error = %d enabling ccadc\n", rc);
1451 goto bail;
1452 }
1453
1454 rc = calib_start_conv(chip, &result);
1455 if (rc < 0) {
1456 pr_err("error = %d for zero volt measurement\n", rc);
1457 goto bail;
1458 }
1459
1460 result_offset += result;
1461 }
1462
1463 result_offset = result_offset / SAMPLE_COUNT;
1464
1465 voltage_offset = ccadc_reading_to_microvolt(chip,
1466 ((s64)result_offset - CCADC_INTRINSIC_OFFSET));
1467
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001468 pr_debug("offset result_offset = 0x%x, voltage = %d microVolts\n",
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001469 result_offset, voltage_offset);
1470
1471 /* Sanity Check */
1472 if (voltage_offset > CCADC_MAX_0UV) {
1473 pr_err("offset voltage = %d is huge limiting to %d\n",
1474 voltage_offset, CCADC_MAX_0UV);
1475 result_offset = CCADC_INTRINSIC_OFFSET
1476 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MAX_0UV);
1477 } else if (voltage_offset < CCADC_MIN_0UV) {
1478 pr_err("offset voltage = %d is too low limiting to %d\n",
1479 voltage_offset, CCADC_MIN_0UV);
1480 result_offset = CCADC_INTRINSIC_OFFSET
1481 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MIN_0UV);
1482 }
1483
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001484 chip->ccadc_result_offset = result_offset;
1485 data_msb = chip->ccadc_result_offset >> 8;
1486 data_lsb = chip->ccadc_result_offset;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001487
1488 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001489 data_msb, data_lsb, 1);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001490 if (rc) {
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001491 pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
1492 rc, data_msb, data_lsb);
1493 /* enable the interrupt and write it when it fires */
1494 pm8921_bms_enable_irq(chip, PM8921_BMS_CCADC_EOC);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001495 }
1496
1497 rc = calib_ccadc_enable_arbiter(chip);
1498 if (rc < 0) {
1499 pr_err("error = %d enabling arbiter for gain\n", rc);
1500 goto bail;
1501 }
1502
1503 /*
1504 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1505 * up, pending verification through bench
1506 */
1507 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1508 CCADC_CALIB_DIG_PARAM);
1509 if (rc < 0) {
1510 pr_err("error = %d enabling decimation ration for gain\n", rc);
1511 goto bail;
1512 }
1513
1514 result_gain = 0;
1515 for (i = 0; i < SAMPLE_COUNT; i++) {
1516 rc = pm8xxx_writeb(chip->dev->parent,
1517 ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
1518 if (rc < 0) {
1519 pr_err("error = %d selecting 25mV for gain\n", rc);
1520 goto bail;
1521 }
1522
1523 /* Enable CCADC */
1524 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1525 CCADC_CALIB_ANA_PARAM);
1526 if (rc < 0) {
1527 pr_err("error = %d enabling ccadc\n", rc);
1528 goto bail;
1529 }
1530
1531 rc = calib_start_conv(chip, &result);
1532 if (rc < 0) {
1533 pr_err("error = %d for adc reading 25mV\n", rc);
1534 goto bail;
1535 }
1536
1537 result_gain += result;
1538 }
1539 result_gain = result_gain / SAMPLE_COUNT;
1540
1541 /*
1542 * result_offset includes INTRINSIC OFFSET
1543 * chip->ccadc_gain_uv will be the actual voltage
1544 * measured for 25000UV
1545 */
1546 chip->ccadc_gain_uv = ccadc_reading_to_microvolt(chip,
1547 ((s64)result_gain - result_offset));
1548
1549 pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
1550 result_gain,
1551 chip->ccadc_gain_uv);
1552 /* Sanity Check */
1553 if (chip->ccadc_gain_uv > CCADC_MAX_25MV) {
1554 pr_err("gain voltage = %d is huge limiting to %d\n",
1555 chip->ccadc_gain_uv, CCADC_MAX_25MV);
1556 chip->ccadc_gain_uv = CCADC_MAX_25MV;
1557 result_gain = result_offset +
1558 microvolt_to_ccadc_reading(chip, CCADC_MAX_25MV);
1559 } else if (chip->ccadc_gain_uv < CCADC_MIN_25MV) {
1560 pr_err("gain voltage = %d is too low limiting to %d\n",
1561 chip->ccadc_gain_uv, CCADC_MIN_25MV);
1562 chip->ccadc_gain_uv = CCADC_MIN_25MV;
1563 result_gain = result_offset +
1564 microvolt_to_ccadc_reading(chip, CCADC_MIN_25MV);
1565 }
1566
1567 data_msb = result_gain >> 8;
1568 data_lsb = result_gain;
1569 rc = calib_ccadc_program_trim(chip, CCADC_FULLSCALE_TRIM1,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001570 data_msb, data_lsb, 0);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001571 if (rc)
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001572 pr_debug("error = %d programming gain trim\n", rc);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001573bail:
1574 pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
1575}
1576
1577static void calibrate_ccadc_work(struct work_struct *work)
1578{
1579 struct pm8921_bms_chip *chip = container_of(work,
1580 struct pm8921_bms_chip, calib_ccadc_work.work);
1581
1582 calib_ccadc(chip);
1583 schedule_delayed_work(&chip->calib_ccadc_work,
1584 round_jiffies_relative(msecs_to_jiffies
1585 (chip->calib_delay_ms)));
1586}
1587
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001588int pm8921_bms_get_vsense_avg(int *result)
1589{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001590 int rc = -EINVAL;
1591 unsigned long flags;
1592
1593 if (the_chip) {
1594 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1595 pm_bms_lock_output_data(the_chip);
1596 rc = read_vsense_avg(the_chip, result);
1597 pm_bms_unlock_output_data(the_chip);
1598 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1599 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001600
1601 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001602 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001603}
1604EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1605
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001606int pm8921_bms_get_battery_current(int *result)
1607{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001608 unsigned long flags;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001609 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001610
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001611 if (!the_chip) {
1612 pr_err("called before initialization\n");
1613 return -EINVAL;
1614 }
1615 if (the_chip->r_sense == 0) {
1616 pr_err("r_sense is zero\n");
1617 return -EINVAL;
1618 }
1619
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001620 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1621 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001622 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001623 pm_bms_unlock_output_data(the_chip);
1624 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001625 pr_debug("vsense=%d\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001626 /* cast for signed division */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001627 *result = vsense / (int)the_chip->r_sense;
1628
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001629 return 0;
1630}
1631EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1632
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001633int pm8921_bms_get_percent_charge(void)
1634{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001635 int batt_temp, rc;
1636 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001637
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001638 if (!the_chip) {
1639 pr_err("called before initialization\n");
1640 return -EINVAL;
1641 }
1642
1643 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1644 if (rc) {
1645 pr_err("error reading adc channel = %d, rc = %d\n",
1646 the_chip->batt_temp_channel, rc);
1647 return rc;
1648 }
1649 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1650 result.measurement);
1651 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001652 return calculate_state_of_charge(the_chip,
1653 batt_temp, last_chargecycles);
1654}
1655EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1656
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001657int pm8921_bms_get_fcc(void)
1658{
1659 int batt_temp, rc;
1660 struct pm8921_adc_chan_result result;
1661
1662 if (!the_chip) {
1663 pr_err("called before initialization\n");
1664 return -EINVAL;
1665 }
1666
1667 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1668 if (rc) {
1669 pr_err("error reading adc channel = %d, rc = %d\n",
1670 the_chip->batt_temp_channel, rc);
1671 return rc;
1672 }
1673 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1674 result.measurement);
1675 batt_temp = (int)result.physical;
1676 return calculate_fcc(the_chip, batt_temp, last_chargecycles);
1677}
1678EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1679
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001680void pm8921_bms_charging_began(void)
1681{
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001682 the_chip->start_percent = pm8921_bms_get_percent_charge();
1683 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001684}
1685EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1686
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001687void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001688{
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001689 if (is_battery_full && the_chip != NULL) {
1690 int batt_temp, rc;
1691 struct pm8921_adc_chan_result result;
1692
1693 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1694 if (rc) {
1695 pr_err("error reading adc channel = %d, rc = %d\n",
1696 the_chip->batt_temp_channel, rc);
1697 goto charge_cycle_calculation;
1698 }
1699 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1700 result.measurement);
1701 batt_temp = (int)result.physical;
1702 last_real_fcc = calculate_real_fcc(the_chip,
1703 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001704 last_real_fcc_batt_temp = batt_temp;
1705 readjust_fcc_table();
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001706 }
1707
1708charge_cycle_calculation:
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001709 the_chip->end_percent = pm8921_bms_get_percent_charge();
1710 if (the_chip->end_percent > the_chip->start_percent) {
1711 last_charge_increase =
1712 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001713 if (last_charge_increase > 100) {
1714 last_chargecycles++;
1715 last_charge_increase = last_charge_increase % 100;
1716 }
1717 }
1718 pr_debug("end_percent = %u%% last_charge_increase = %d"
1719 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001720 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001721 last_charge_increase,
1722 last_chargecycles);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001723 the_chip->start_percent = 0;
1724 the_chip->end_percent = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001725}
1726EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1727
1728static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1729{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001730 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001731 return IRQ_HANDLED;
1732}
1733
1734static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1735{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001736 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001737 return IRQ_HANDLED;
1738}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001739
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001740static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1741{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001742 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001743 return IRQ_HANDLED;
1744}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001745
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001746static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1747{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001748 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001749 return IRQ_HANDLED;
1750}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001751
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001752static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1753{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001754 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001755
1756 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001757 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001758 return IRQ_HANDLED;
1759}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001760
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001761static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1762{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001763 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001764
1765 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001766 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001767 return IRQ_HANDLED;
1768}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001769
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001770static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1771{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001772 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001773 return IRQ_HANDLED;
1774}
1775
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001776static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
1777{
1778 u8 data_msb, data_lsb;
1779 struct pm8921_bms_chip *chip = data;
1780 int rc;
1781
1782 pr_debug("irq = %d triggered\n", irq);
1783 data_msb = chip->ccadc_result_offset >> 8;
1784 data_lsb = chip->ccadc_result_offset;
1785
1786 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
1787 data_msb, data_lsb, 0);
1788 pm8921_bms_disable_irq(chip, PM8921_BMS_CCADC_EOC);
1789
1790 return IRQ_HANDLED;
1791}
1792
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001793struct pm_bms_irq_init_data {
1794 unsigned int irq_id;
1795 char *name;
1796 unsigned long flags;
1797 irqreturn_t (*handler)(int, void *);
1798};
1799
1800#define BMS_IRQ(_id, _flags, _handler) \
1801{ \
1802 .irq_id = _id, \
1803 .name = #_id, \
1804 .flags = _flags, \
1805 .handler = _handler, \
1806}
1807
1808struct pm_bms_irq_init_data bms_irq_data[] = {
1809 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1810 pm8921_bms_sbi_write_ok_handler),
1811 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1812 pm8921_bms_cc_thr_handler),
1813 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1814 pm8921_bms_vsense_thr_handler),
1815 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1816 pm8921_bms_vsense_for_r_handler),
1817 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1818 pm8921_bms_ocv_for_r_handler),
1819 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1820 pm8921_bms_good_ocv_handler),
1821 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1822 pm8921_bms_vsense_avg_handler),
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001823 BMS_IRQ(PM8921_BMS_CCADC_EOC, IRQF_TRIGGER_RISING,
1824 pm8921_bms_ccadc_eoc_handler),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001825};
1826
1827static void free_irqs(struct pm8921_bms_chip *chip)
1828{
1829 int i;
1830
1831 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1832 if (chip->pmic_bms_irq[i]) {
1833 free_irq(chip->pmic_bms_irq[i], NULL);
1834 chip->pmic_bms_irq[i] = 0;
1835 }
1836}
1837
1838static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1839 struct platform_device *pdev)
1840{
1841 struct resource *res;
1842 int ret, i;
1843
1844 ret = 0;
1845 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1846
1847 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1848 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1849 bms_irq_data[i].name);
1850 if (res == NULL) {
1851 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1852 goto err_out;
1853 }
1854 ret = request_irq(res->start, bms_irq_data[i].handler,
1855 bms_irq_data[i].flags,
1856 bms_irq_data[i].name, chip);
1857 if (ret < 0) {
1858 pr_err("couldn't request %d (%s) %d\n", res->start,
1859 bms_irq_data[i].name, ret);
1860 goto err_out;
1861 }
1862 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1863 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1864 }
1865 return 0;
1866
1867err_out:
1868 free_irqs(chip);
1869 return -EINVAL;
1870}
1871
1872#define EN_BMS_BIT BIT(7)
1873#define EN_PON_HS_BIT BIT(0)
1874static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1875{
1876 int rc;
1877
1878 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1879 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1880 if (rc) {
1881 pr_err("failed to enable pon and bms addr = %d %d",
1882 BMS_CONTROL, rc);
1883 }
1884
1885 return 0;
1886}
1887
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001888static void check_initial_ocv(struct pm8921_bms_chip *chip)
1889{
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001890 int ocv, rc;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001891
1892 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001893 * Check if a ocv is available in bms hw,
1894 * if not compute it here at boot time and save it
1895 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001896 */
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001897 ocv = 0;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001898 rc = read_last_good_ocv(chip, &ocv);
1899 if (rc || ocv == 0) {
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001900 rc = adc_based_ocv(chip, &ocv);
1901 if (rc) {
1902 pr_err("failed to read adc based ocv rc = %d\n", rc);
1903 ocv = DEFAULT_OCV_MICROVOLTS;
1904 }
1905 last_ocv_uv = ocv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001906 }
1907 pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
1908}
1909
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001910static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1911{
1912 int rc;
1913 struct pm8921_adc_chan_result result;
1914
1915 rc = pm8921_adc_read(chip->batt_id_channel, &result);
1916 if (rc) {
1917 pr_err("error reading batt id channel = %d, rc = %d\n",
1918 chip->vbat_channel, rc);
1919 return rc;
1920 }
1921 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1922 result.measurement);
1923 return result.physical;
1924}
1925
1926#define PALLADIUM_ID_MIN 2500
1927#define PALLADIUM_ID_MAX 4000
1928static int set_battery_data(struct pm8921_bms_chip *chip)
1929{
1930 int64_t battery_id;
1931
1932 battery_id = read_battery_id(chip);
1933
1934 if (battery_id < 0) {
1935 pr_err("cannot read battery id err = %lld\n", battery_id);
1936 return battery_id;
1937 }
1938
1939 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
1940 chip->fcc = palladium_1500_data.fcc;
1941 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1942 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1943 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1944 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1945 return 0;
1946 } else {
1947 pr_warn("invalid battery id, palladium 1500 assumed\n");
1948 chip->fcc = palladium_1500_data.fcc;
1949 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1950 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1951 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1952 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1953 return 0;
1954 }
1955}
1956
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001957enum {
1958 CALC_RBATT,
1959 CALC_FCC,
1960 CALC_PC,
1961 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001962 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001963 CALIB_CCADC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001964};
1965
1966static int test_batt_temp = 5;
1967static int test_chargecycle = 150;
1968static int test_ocv = 3900000;
1969enum {
1970 TEST_BATT_TEMP,
1971 TEST_CHARGE_CYCLE,
1972 TEST_OCV,
1973};
1974static int get_test_param(void *data, u64 * val)
1975{
1976 switch ((int)data) {
1977 case TEST_BATT_TEMP:
1978 *val = test_batt_temp;
1979 break;
1980 case TEST_CHARGE_CYCLE:
1981 *val = test_chargecycle;
1982 break;
1983 case TEST_OCV:
1984 *val = test_ocv;
1985 break;
1986 default:
1987 return -EINVAL;
1988 }
1989 return 0;
1990}
1991static int set_test_param(void *data, u64 val)
1992{
1993 switch ((int)data) {
1994 case TEST_BATT_TEMP:
1995 test_batt_temp = (int)val;
1996 break;
1997 case TEST_CHARGE_CYCLE:
1998 test_chargecycle = (int)val;
1999 break;
2000 case TEST_OCV:
2001 test_ocv = (int)val;
2002 break;
2003 default:
2004 return -EINVAL;
2005 }
2006 return 0;
2007}
2008DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
2009
2010static int get_calc(void *data, u64 * val)
2011{
2012 int param = (int)data;
2013 int ret = 0;
2014
2015 *val = 0;
2016
2017 /* global irq number passed in via data */
2018 switch (param) {
2019 case CALC_RBATT:
2020 *val = calculate_rbatt(the_chip);
2021 break;
2022 case CALC_FCC:
2023 *val = calculate_fcc(the_chip, test_batt_temp,
2024 test_chargecycle);
2025 break;
2026 case CALC_PC:
2027 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
2028 test_chargecycle);
2029 break;
2030 case CALC_SOC:
2031 *val = calculate_state_of_charge(the_chip,
2032 test_batt_temp, test_chargecycle);
2033 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002034 case CALIB_HKADC:
2035 /* reading this will trigger calibration */
2036 *val = 0;
2037 calib_hkadc(the_chip);
2038 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002039 case CALIB_CCADC:
2040 /* reading this will trigger calibration */
2041 *val = 0;
2042 calib_ccadc(the_chip);
2043 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002044 default:
2045 ret = -EINVAL;
2046 }
2047 return ret;
2048}
2049DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
2050
2051static int get_reading(void *data, u64 * val)
2052{
2053 int param = (int)data;
2054 int ret = 0;
2055
2056 *val = 0;
2057
2058 /* global irq number passed in via data */
2059 switch (param) {
2060 case CC_MSB:
2061 case CC_LSB:
2062 read_cc(the_chip, (int *)val);
2063 break;
2064 case LAST_GOOD_OCV_VALUE:
2065 read_last_good_ocv(the_chip, (uint *)val);
2066 break;
2067 case VBATT_FOR_RBATT:
2068 read_vbatt_for_rbatt(the_chip, (uint *)val);
2069 break;
2070 case VSENSE_FOR_RBATT:
2071 read_vsense_for_rbatt(the_chip, (uint *)val);
2072 break;
2073 case OCV_FOR_RBATT:
2074 read_ocv_for_rbatt(the_chip, (uint *)val);
2075 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002076 case VSENSE_AVG:
2077 read_vsense_avg(the_chip, (uint *)val);
2078 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002079 default:
2080 ret = -EINVAL;
2081 }
2082 return ret;
2083}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002084DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002085
2086static int get_rt_status(void *data, u64 * val)
2087{
2088 int i = (int)data;
2089 int ret;
2090
2091 /* global irq number passed in via data */
2092 ret = pm_bms_get_rt_status(the_chip, i);
2093 *val = ret;
2094 return 0;
2095}
2096DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2097
2098static int get_reg(void *data, u64 * val)
2099{
2100 int addr = (int)data;
2101 int ret;
2102 u8 temp;
2103
2104 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2105 if (ret) {
2106 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2107 addr, temp, ret);
2108 return -EAGAIN;
2109 }
2110 *val = temp;
2111 return 0;
2112}
2113
2114static int set_reg(void *data, u64 val)
2115{
2116 int addr = (int)data;
2117 int ret;
2118 u8 temp;
2119
2120 temp = (u8) val;
2121 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2122 if (ret) {
2123 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2124 addr, temp, ret);
2125 return -EAGAIN;
2126 }
2127 return 0;
2128}
2129DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2130
2131static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2132{
2133 int i;
2134
2135 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
2136
2137 if (IS_ERR(chip->dent)) {
2138 pr_err("pmic bms couldnt create debugfs dir\n");
2139 return;
2140 }
2141
2142 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2143 (void *)BMS_CONTROL, &reg_fops);
2144 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2145 (void *)BMS_OUTPUT0, &reg_fops);
2146 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2147 (void *)BMS_OUTPUT1, &reg_fops);
2148 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2149 (void *)BMS_TEST1, &reg_fops);
2150 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
2151 (void *)CCADC_ANA_PARAM, &reg_fops);
2152 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
2153 (void *)CCADC_DIG_PARAM, &reg_fops);
2154 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
2155 (void *)CCADC_RSV, &reg_fops);
2156 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
2157 (void *)CCADC_DATA0, &reg_fops);
2158 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
2159 (void *)CCADC_DATA1, &reg_fops);
2160 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
2161 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
2162 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
2163 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
2164
2165 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2166 (void *)TEST_BATT_TEMP, &temp_fops);
2167 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2168 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2169 debugfs_create_file("test_ocv", 0644, chip->dent,
2170 (void *)TEST_OCV, &temp_fops);
2171
2172 debugfs_create_file("read_cc", 0644, chip->dent,
2173 (void *)CC_MSB, &reading_fops);
2174 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2175 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2176 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2177 (void *)VBATT_FOR_RBATT, &reading_fops);
2178 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2179 (void *)VSENSE_FOR_RBATT, &reading_fops);
2180 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2181 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002182 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2183 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002184
2185 debugfs_create_file("show_rbatt", 0644, chip->dent,
2186 (void *)CALC_RBATT, &calc_fops);
2187 debugfs_create_file("show_fcc", 0644, chip->dent,
2188 (void *)CALC_FCC, &calc_fops);
2189 debugfs_create_file("show_pc", 0644, chip->dent,
2190 (void *)CALC_PC, &calc_fops);
2191 debugfs_create_file("show_soc", 0644, chip->dent,
2192 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002193 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2194 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002195 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2196 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002197
2198 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2199 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2200 debugfs_create_file(bms_irq_data[i].name, 0444,
2201 chip->dent,
2202 (void *)bms_irq_data[i].irq_id,
2203 &rt_fops);
2204 }
2205}
2206
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002207static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2208{
2209 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002210 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002211 struct pm8921_bms_chip *chip;
2212 const struct pm8921_bms_platform_data *pdata
2213 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002214
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002215 if (!pdata) {
2216 pr_err("missing platform data\n");
2217 return -EINVAL;
2218 }
2219
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002220 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002221 if (!chip) {
2222 pr_err("Cannot allocate pm_bms_chip\n");
2223 return -ENOMEM;
2224 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002225 spin_lock_init(&chip->bms_output_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002226 chip->dev = &pdev->dev;
2227 chip->r_sense = pdata->r_sense;
2228 chip->i_test = pdata->i_test;
2229 chip->v_failure = pdata->v_failure;
2230 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002231 rc = set_battery_data(chip);
2232 if (rc) {
2233 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002234 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002235 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002236
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002237 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2238 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002239 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2240 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002241 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002242 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002243 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002244
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002245 rc = request_irqs(chip, pdev);
2246 if (rc) {
2247 pr_err("couldn't register interrupts rc = %d\n", rc);
2248 goto free_chip;
2249 }
2250
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002251 rc = pm8921_bms_hw_init(chip);
2252 if (rc) {
2253 pr_err("couldn't init hardware rc = %d\n", rc);
2254 goto free_irqs;
2255 }
2256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002257 platform_set_drvdata(pdev, chip);
2258 the_chip = chip;
2259 create_debugfs_entries(chip);
2260
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002261 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002262 chip->ccadc_gain_uv = calib_ccadc_read_gain_uv(chip);
2263
2264 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
2265 /* begin calibration only on chips > 2.0 */
2266 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
2267 calibrate_ccadc_work(&(chip->calib_ccadc_work.work));
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002268
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002269 /* initial hkadc calibration */
2270 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002271 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2272 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2273 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002274
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002275 get_battery_uvolts(chip, &vbatt);
2276 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2277 pm8921_bms_get_percent_charge(),
2278 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002279 return 0;
2280
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002281free_irqs:
2282 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002283free_chip:
2284 kfree(chip);
2285 return rc;
2286}
2287
2288static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2289{
2290 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2291
2292 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002293 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002294 platform_set_drvdata(pdev, NULL);
2295 the_chip = NULL;
2296 kfree(chip);
2297 return 0;
2298}
2299
2300static struct platform_driver pm8921_bms_driver = {
2301 .probe = pm8921_bms_probe,
2302 .remove = __devexit_p(pm8921_bms_remove),
2303 .driver = {
2304 .name = PM8921_BMS_DEV_NAME,
2305 .owner = THIS_MODULE,
2306 },
2307};
2308
2309static int __init pm8921_bms_init(void)
2310{
2311 return platform_driver_register(&pm8921_bms_driver);
2312}
2313
2314static void __exit pm8921_bms_exit(void)
2315{
2316 platform_driver_unregister(&pm8921_bms_driver);
2317}
2318
2319late_initcall(pm8921_bms_init);
2320module_exit(pm8921_bms_exit);
2321
2322MODULE_LICENSE("GPL v2");
2323MODULE_DESCRIPTION("PMIC8921 bms driver");
2324MODULE_VERSION("1.0");
2325MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);