blob: d7cf3a87dab9985062807b66555709b6367672cb [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;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -080095
96 uint16_t ocv_reading_at_100;
97 int cc_reading_at_100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098};
99
100static struct pm8921_bms_chip *the_chip;
101
102#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104#define DEFAULT_CHARGE_CYCLES 0
105
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700106static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
107static int last_charge_increase;
108module_param(last_chargecycles, int, 0644);
109module_param(last_charge_increase, int, 0644);
110
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113static int last_soc = -EINVAL;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700114static int last_real_fcc = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700115static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700117static int bms_ops_set(const char *val, const struct kernel_param *kp)
118{
119 if (*(int *)kp->arg == -EINVAL)
120 return param_set_int(val, kp);
121 else
122 return 0;
123}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700125static struct kernel_param_ops bms_param_ops = {
126 .set = bms_ops_set,
127 .get = param_get_int,
128};
129
130module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
131module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
132module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
133
134static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
135static void readjust_fcc_table(void)
136{
137 struct single_row_lut *temp, *old;
138 int i, fcc, ratio;
139
140 if (!the_chip->fcc_temp_lut) {
141 pr_err("The static fcc lut table is NULL\n");
142 return;
143 }
144
145 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
146 if (!temp) {
147 pr_err("Cannot allocate memory for adjusted fcc table\n");
148 return;
149 }
150
151 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
152
153 temp->cols = the_chip->fcc_temp_lut->cols;
154 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
155 temp->x[i] = the_chip->fcc_temp_lut->x[i];
156 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
157 temp->y[i] = (ratio * last_real_fcc);
158 temp->y[i] /= 1000;
159 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
160 temp->x[i], the_chip->fcc_temp_lut->y[i],
161 temp->y[i], ratio);
162 }
163
164 old = the_chip->adjusted_fcc_temp_lut;
165 the_chip->adjusted_fcc_temp_lut = temp;
166 kfree(old);
167}
168
169static int bms_last_real_fcc_set(const char *val,
170 const struct kernel_param *kp)
171{
172 int rc = 0;
173
174 if (last_real_fcc == -EINVAL)
175 rc = param_set_int(val, kp);
176 if (rc) {
177 pr_err("Failed to set last_real_fcc rc=%d\n", rc);
178 return rc;
179 }
180 if (last_real_fcc_batt_temp != -EINVAL)
181 readjust_fcc_table();
182 return rc;
183}
184static struct kernel_param_ops bms_last_real_fcc_param_ops = {
185 .set = bms_last_real_fcc_set,
186 .get = param_get_int,
187};
188module_param_cb(last_real_fcc, &bms_last_real_fcc_param_ops,
189 &last_real_fcc, 0644);
190
191static int bms_last_real_fcc_batt_temp_set(const char *val,
192 const struct kernel_param *kp)
193{
194 int rc = 0;
195
196 if (last_real_fcc_batt_temp == -EINVAL)
197 rc = param_set_int(val, kp);
198 if (rc) {
199 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
200 return rc;
201 }
202 if (last_real_fcc != -EINVAL)
203 readjust_fcc_table();
204 return rc;
205}
206
207static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
208 .set = bms_last_real_fcc_batt_temp_set,
209 .get = param_get_int,
210};
211module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
212 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213
214static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
215{
216 return pm8xxx_read_irq_stat(chip->dev->parent,
217 chip->pmic_bms_irq[irq_id]);
218}
219
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700220static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
221{
222 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
223 dev_dbg(chip->dev, "%s %d\n", __func__,
224 chip->pmic_bms_irq[interrupt]);
225 enable_irq(chip->pmic_bms_irq[interrupt]);
226 }
227}
228
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
230{
231 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700232 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
234 }
235}
236
237static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
238 u8 mask, u8 val)
239{
240 int rc;
241 u8 reg;
242
243 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
244 if (rc) {
245 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
246 return rc;
247 }
248 reg &= ~mask;
249 reg |= val & mask;
250 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
251 if (rc) {
252 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
253 return rc;
254 }
255 return 0;
256}
257
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700258#define HOLD_OREG_DATA BIT(1)
259static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
260{
261 int rc;
262
263 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
264 HOLD_OREG_DATA);
265 if (rc) {
266 pr_err("couldnt lock bms output rc = %d\n", rc);
267 return rc;
268 }
269 return 0;
270}
271
272static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
273{
274 int rc;
275
276 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
277 if (rc) {
278 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
279 return rc;
280 }
281 return 0;
282}
283
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284#define SELECT_OUTPUT_DATA 0x1C
285#define SELECT_OUTPUT_TYPE_SHIFT 2
286#define OCV_FOR_RBATT 0x0
287#define VSENSE_FOR_RBATT 0x1
288#define VBATT_FOR_RBATT 0x2
289#define CC_MSB 0x3
290#define CC_LSB 0x4
291#define LAST_GOOD_OCV_VALUE 0x5
292#define VSENSE_AVG 0x6
293#define VBATT_AVG 0x7
294
295static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
296 int16_t *result)
297{
298 int rc;
299 u8 reg;
300
301 if (!result) {
302 pr_err("result pointer null\n");
303 return -EINVAL;
304 }
305 *result = 0;
306 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
307 pr_err("invalid type %d asked to read\n", type);
308 return -EINVAL;
309 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700310
311 /* make sure the bms registers are locked */
312 rc = pm8xxx_readb(chip->dev->parent, BMS_CONTROL, &reg);
313 if (rc) {
314 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
315 type, rc);
316 return rc;
317 }
318
319 /* Output register data must be held (locked) while reading output */
320 WARN_ON(!(reg && HOLD_OREG_DATA));
321
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
323 type << SELECT_OUTPUT_TYPE_SHIFT);
324 if (rc) {
325 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
326 type, rc);
327 return rc;
328 }
329
330 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
331 if (rc) {
332 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
333 type, rc);
334 return rc;
335 }
336 *result = reg;
337 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
338 if (rc) {
339 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
340 type, rc);
341 return rc;
342 }
343 *result |= reg << 8;
344 pr_debug("type %d result %x", type, *result);
345 return 0;
346}
347
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700348#define V_PER_BIT_MUL_FACTOR 97656
349#define V_PER_BIT_DIV_FACTOR 1000
350#define XOADC_INTRINSIC_OFFSET 0x6000
351static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700352{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700353 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700354 return 0;
355
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700356 return (a - XOADC_INTRINSIC_OFFSET)
357 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700358}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700360#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700361#define VBATT_MUL_FACTOR 3
362static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
363 unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700364{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700365 u64 numerator, denominator;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700366
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700367 if (uv == 0)
368 return 0;
369
370 numerator = ((u64)uv - chip->xoadc_v0625) * XOADC_CALIB_UV;
371 denominator = chip->xoadc_v125 - chip->xoadc_v0625;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700372 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700373 return uv * VBATT_MUL_FACTOR;
374 return (XOADC_CALIB_UV + div_u64(numerator, denominator))
375 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700376}
377
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700378#define CC_RESOLUTION_N_V1 1085069
379#define CC_RESOLUTION_D_V1 100000
380#define CC_RESOLUTION_N_V2 868056
381#define CC_RESOLUTION_D_V2 10000
382static s64 cc_to_microvolt_v1(s64 cc)
383{
384 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
385}
386
387static s64 cc_to_microvolt_v2(s64 cc)
388{
389 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
390}
391
392static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
393{
394 /*
395 * resolution (the value of a single bit) was changed after revision 2.0
396 * for more accurate readings
397 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700398 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700399 cc_to_microvolt_v1((s64)cc) :
400 cc_to_microvolt_v2((s64)cc);
401}
402
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700403#define CCADC_READING_RESOLUTION_N_V1 1085069
404#define CCADC_READING_RESOLUTION_D_V1 100000
405#define CCADC_READING_RESOLUTION_N_V2 542535
406#define CCADC_READING_RESOLUTION_D_V2 100000
407static s64 ccadc_reading_to_microvolt_v1(s64 cc)
408{
409 return div_s64(cc * CCADC_READING_RESOLUTION_N_V1,
410 CCADC_READING_RESOLUTION_D_V1);
411}
412
413static s64 ccadc_reading_to_microvolt_v2(s64 cc)
414{
415 return div_s64(cc * CCADC_READING_RESOLUTION_N_V2,
416 CCADC_READING_RESOLUTION_D_V2);
417}
418
419static s64 ccadc_reading_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
420{
421 /*
422 * resolution (the value of a single bit) was changed after revision 2.0
423 * for more accurate readings
424 */
425 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
426 ccadc_reading_to_microvolt_v1((s64)cc) :
427 ccadc_reading_to_microvolt_v2((s64)cc);
428}
429
430static s64 microvolt_to_ccadc_reading_v1(s64 uv)
431{
432 return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
433 CCADC_READING_RESOLUTION_N_V1);
434}
435
436static s64 microvolt_to_ccadc_reading_v2(s64 uv)
437{
438 return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
439 CCADC_READING_RESOLUTION_N_V2);
440}
441
442static s64 microvolt_to_ccadc_reading(struct pm8921_bms_chip *chip, s64 cc)
443{
444 /*
445 * resolution (the value of a single bit) was changed after revision 2.0
446 * for more accurate readings
447 */
448 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
449 microvolt_to_ccadc_reading_v1((s64)cc) :
450 microvolt_to_ccadc_reading_v2((s64)cc);
451}
452
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700453#define CC_READING_TICKS 55
454#define SLEEP_CLK_HZ 32768
455#define SECONDS_PER_HOUR 3600
456static s64 ccmicrovolt_to_uvh(s64 cc_uv)
457{
458 return div_s64(cc_uv * CC_READING_TICKS,
459 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
460}
461
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700462#define GAIN_REFERENCE_UV 25000
463/*
464 * gain compensation for ccadc readings - common for vsense based and
465 * couloumb counter based readings
466 */
467static s64 cc_adjust_for_gain(struct pm8921_bms_chip *chip, s64 cc)
468{
469 if (chip->ccadc_gain_uv == 0)
470 return cc;
471
472 return div_s64(cc * GAIN_REFERENCE_UV, chip->ccadc_gain_uv);
473}
474
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475/* returns the signed value read from the hardware */
476static int read_cc(struct pm8921_bms_chip *chip, int *result)
477{
478 int rc;
479 uint16_t msw, lsw;
480
481 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
482 if (rc) {
483 pr_err("fail to read CC_LSB rc = %d\n", rc);
484 return rc;
485 }
486 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
487 if (rc) {
488 pr_err("fail to read CC_MSB rc = %d\n", rc);
489 return rc;
490 }
491 *result = msw << 16 | lsw;
492 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800493 *result = *result - chip->cc_reading_at_100;
494 pr_debug("cc = %d after subtracting %d\n",
495 *result, chip->cc_reading_at_100);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700496 return 0;
497}
498
499static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
500{
501 int rc;
502 uint16_t reading;
503
504 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
505 if (rc) {
506 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
507 return rc;
508 }
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800509
510 if (chip->ocv_reading_at_100 != reading) {
511 chip->ocv_reading_at_100 = 0;
512 chip->cc_reading_at_100 = 0;
513 *result = xoadc_reading_to_microvolt(reading);
514 pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
515 *result = adjust_xo_vbatt_reading(chip, *result);
516 pr_debug("after adj ocv_uV = %u\n", *result);
517 if (*result != 0)
518 last_ocv_uv = *result;
519 } else {
520 /*
521 * force 100% ocv by selecting the highest profiled ocv
522 * This is the first row last column entry in the ocv
523 * lookup table
524 */
525 int cols = chip->pc_temp_ocv_lut->cols;
526
527 pr_debug("Forcing max voltage %d\n",
528 1000 * chip->pc_temp_ocv_lut->ocv[0][cols-1]);
529 *result = 1000 * chip->pc_temp_ocv_lut->ocv[0][cols-1];
530 }
531
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532 return 0;
533}
534
535static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
536{
537 int rc;
538 uint16_t reading;
539
540 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
541 if (rc) {
542 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
543 return rc;
544 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700545 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700546 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700547 *result = adjust_xo_vbatt_reading(chip, *result);
548 pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549 return 0;
550}
551
552static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
553{
554 int rc;
555 uint16_t reading;
556
557 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
558 if (rc) {
559 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
560 return rc;
561 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700562 *result = ccadc_reading_to_microvolt(chip, reading);
563 pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
564 *result = cc_adjust_for_gain(chip, *result);
565 pr_debug("after adj vsense_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566 return 0;
567}
568
569static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
570{
571 int rc;
572 uint16_t reading;
573
574 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
575 if (rc) {
576 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
577 return rc;
578 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700579 *result = xoadc_reading_to_microvolt(reading);
580 pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
581 *result = adjust_xo_vbatt_reading(chip, *result);
582 pr_debug("after adj ocv_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583 return 0;
584}
585
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700586static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
587{
588 int rc;
589 int16_t reading;
590
591 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
592 if (rc) {
593 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
594 return rc;
595 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700596 *result = ccadc_reading_to_microvolt(chip, reading);
597 pr_debug("raw = %04x vsense = %d\n", reading, *result);
598 *result = cc_adjust_for_gain(the_chip, (s64)*result);
599 pr_debug("after adj vsense = %d\n", *result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700600 return 0;
601}
602
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700603static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
604{
605 if (y0 == y1 || x == x0)
606 return y0;
607 if (x1 == x0 || x == x1)
608 return y1;
609
610 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
611}
612
613static int interpolate_single_lut(struct single_row_lut *lut, int x)
614{
615 int i, result;
616
617 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700618 pr_debug("x %d less than known range return y = %d lut = %pS\n",
619 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700620 return lut->y[0];
621 }
622 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700623 pr_debug("x %d more than known range return y = %d lut = %pS\n",
624 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 return lut->y[lut->cols - 1];
626 }
627
628 for (i = 0; i < lut->cols; i++)
629 if (x <= lut->x[i])
630 break;
631 if (x == lut->x[i]) {
632 result = lut->y[i];
633 } else {
634 result = linear_interpolate(
635 lut->y[i - 1],
636 lut->x[i - 1],
637 lut->y[i],
638 lut->x[i],
639 x);
640 }
641 return result;
642}
643
644static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
645{
646 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
647}
648
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700649static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
650{
651 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
652}
653
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700654static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
655 int cycles)
656{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700657 /*
658 * sf table could be null when no battery aging data is available, in
659 * that case return 100%
660 */
661 if (chip->fcc_sf_lut)
662 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
663 else
664 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700665}
666
667static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
668 int cycles, int pc)
669{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700670 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700671 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700672 int row1 = 0;
673 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700675 /*
676 * sf table could be null when no battery aging data is available, in
677 * that case return 100%
678 */
679 if (!chip->pc_sf_lut)
680 return 100;
681
682 rows = chip->pc_sf_lut->rows;
683 cols = chip->pc_sf_lut->cols;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700685 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 row1 = 0;
687 row2 = 0;
688 }
689 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700690 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691 row1 = rows - 1;
692 row2 = rows - 1;
693 }
694 for (i = 0; i < rows; i++) {
695 if (pc == chip->pc_sf_lut->percent[i]) {
696 row1 = i;
697 row2 = i;
698 break;
699 }
700 if (pc > chip->pc_sf_lut->percent[i]) {
701 row1 = i - 1;
702 row2 = i;
703 break;
704 }
705 }
706
707 if (cycles < chip->pc_sf_lut->cycles[0])
708 cycles = chip->pc_sf_lut->cycles[0];
709 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
710 cycles = chip->pc_sf_lut->cycles[cols - 1];
711
712 for (i = 0; i < cols; i++)
713 if (cycles <= chip->pc_sf_lut->cycles[i])
714 break;
715 if (cycles == chip->pc_sf_lut->cycles[i]) {
716 scalefactor = linear_interpolate(
717 chip->pc_sf_lut->sf[row1][i],
718 chip->pc_sf_lut->percent[row1],
719 chip->pc_sf_lut->sf[row2][i],
720 chip->pc_sf_lut->percent[row2],
721 pc);
722 return scalefactor;
723 }
724
725 scalefactorrow1 = linear_interpolate(
726 chip->pc_sf_lut->sf[row1][i - 1],
727 chip->pc_sf_lut->cycles[i - 1],
728 chip->pc_sf_lut->sf[row1][i],
729 chip->pc_sf_lut->cycles[i],
730 cycles);
731
732 scalefactorrow2 = linear_interpolate(
733 chip->pc_sf_lut->sf[row2][i - 1],
734 chip->pc_sf_lut->cycles[i - 1],
735 chip->pc_sf_lut->sf[row2][i],
736 chip->pc_sf_lut->cycles[i],
737 cycles);
738
739 scalefactor = linear_interpolate(
740 scalefactorrow1,
741 chip->pc_sf_lut->percent[row1],
742 scalefactorrow2,
743 chip->pc_sf_lut->percent[row2],
744 pc);
745
746 return scalefactor;
747}
748
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700749static int is_between(int left, int right, int value)
750{
751 if (left >= right && left >= value && value >= right)
752 return 1;
753 if (left <= right && left <= value && value <= right)
754 return 1;
755
756 return 0;
757}
758
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759static int interpolate_pc(struct pm8921_bms_chip *chip,
760 int batt_temp, int ocv)
761{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700762 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 int rows = chip->pc_temp_ocv_lut->rows;
764 int cols = chip->pc_temp_ocv_lut->cols;
765
766 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700767 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768 batt_temp = chip->pc_temp_ocv_lut->temp[0];
769 }
770 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700771 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700772 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
773 }
774
775 for (j = 0; j < cols; j++)
776 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
777 break;
778 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
779 /* found an exact match for temp in the table */
780 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
781 return chip->pc_temp_ocv_lut->percent[0];
782 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
783 return chip->pc_temp_ocv_lut->percent[rows - 1];
784 for (i = 0; i < rows; i++) {
785 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
786 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
787 return
788 chip->pc_temp_ocv_lut->percent[i];
789 pc = linear_interpolate(
790 chip->pc_temp_ocv_lut->percent[i],
791 chip->pc_temp_ocv_lut->ocv[i][j],
792 chip->pc_temp_ocv_lut->percent[i - 1],
793 chip->pc_temp_ocv_lut->ocv[i - 1][j],
794 ocv);
795 return pc;
796 }
797 }
798 }
799
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700800 /*
801 * batt_temp is within temperature for
802 * column j-1 and j
803 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700804 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
805 return chip->pc_temp_ocv_lut->percent[0];
806 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
807 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700808
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700809 pcj_minus_one = 0;
810 pcj = 0;
811 for (i = 0; i < rows-1; i++) {
812 if (pcj == 0
813 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
814 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
815 pcj = linear_interpolate(
816 chip->pc_temp_ocv_lut->percent[i],
817 chip->pc_temp_ocv_lut->ocv[i][j],
818 chip->pc_temp_ocv_lut->percent[i + 1],
819 chip->pc_temp_ocv_lut->ocv[i+1][j],
820 ocv);
821 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700823 if (pcj_minus_one == 0
824 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
825 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700827 pcj_minus_one = linear_interpolate(
828 chip->pc_temp_ocv_lut->percent[i],
829 chip->pc_temp_ocv_lut->ocv[i][j-1],
830 chip->pc_temp_ocv_lut->percent[i + 1],
831 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
832 ocv);
833 }
834
835 if (pcj && pcj_minus_one) {
836 pc = linear_interpolate(
837 pcj_minus_one,
838 chip->pc_temp_ocv_lut->temp[j-1],
839 pcj,
840 chip->pc_temp_ocv_lut->temp[j],
841 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700842 return pc;
843 }
844 }
845
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700846 if (pcj)
847 return pcj;
848
849 if (pcj_minus_one)
850 return pcj_minus_one;
851
852 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
853 ocv, batt_temp);
854 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855}
856
857static int calculate_rbatt(struct pm8921_bms_chip *chip)
858{
859 int rc;
860 unsigned int ocv, vsense, vbatt, r_batt;
861
862 rc = read_ocv_for_rbatt(chip, &ocv);
863 if (rc) {
864 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
865 ocv = 0;
866 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700867
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700868 rc = read_vbatt_for_rbatt(chip, &vbatt);
869 if (rc) {
870 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
871 ocv = 0;
872 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700873
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700874 rc = read_vsense_for_rbatt(chip, &vsense);
875 if (rc) {
876 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
877 ocv = 0;
878 }
879 if (ocv == 0
880 || ocv == vbatt
881 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700882 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
883 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700884 ocv, vbatt, vsense);
885 return -EINVAL;
886 }
887 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700888 last_rbatt = r_batt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700889 pr_debug("r_batt = %umilliOhms", r_batt);
890 return r_batt;
891}
892
893static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
894 int chargecycles)
895{
896 int initfcc, result, scalefactor = 0;
897
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700898 if (chip->adjusted_fcc_temp_lut == NULL) {
899 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700900
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700901 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700902
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700903 /* Multiply the initial FCC value by the scale factor. */
904 result = (initfcc * scalefactor) / 100;
905 pr_debug("fcc mAh = %d\n", result);
906 return result;
907 } else {
908 return interpolate_fcc_adjusted(chip, batt_temp);
909 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700910}
911
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700912static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
913{
914 int rc;
915 struct pm8921_adc_chan_result result;
916
917 rc = pm8921_adc_read(chip->vbat_channel, &result);
918 if (rc) {
919 pr_err("error reading adc channel = %d, rc = %d\n",
920 chip->vbat_channel, rc);
921 return rc;
922 }
923 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
924 result.measurement);
925 *uvolts = (int)result.physical;
926 *uvolts = *uvolts * 1000;
927 return 0;
928}
929
930static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
931{
932 int vbatt, rbatt, ibatt, rc;
933
934 rc = get_battery_uvolts(chip, &vbatt);
935 if (rc) {
936 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700937 return rc;
938 }
939
940 rc = pm8921_bms_get_battery_current(&ibatt);
941 if (rc) {
942 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700943 return rc;
944 }
945
946 rbatt = calculate_rbatt(the_chip);
947 if (rbatt < 0)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700948 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700949 *ocv = vbatt + ibatt * rbatt;
950 return 0;
951}
952
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
954 int chargecycles)
955{
956 int pc, scalefactor;
957
958 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
959 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
960 pc, ocv_uv, batt_temp);
961
962 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
963 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
964
965 /* Multiply the initial FCC value by the scale factor. */
966 pc = (pc * scalefactor) / 100;
967 return pc;
968}
969
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700970static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700971 int *coulumb_counter)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972{
973 int rc;
974 int64_t cc_voltage_uv, cc_uvh, cc_mah;
975
976 rc = read_cc(the_chip, coulumb_counter);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700977 cc_voltage_uv = (int64_t)*coulumb_counter;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700978 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700979 cc_voltage_uv = cc_adjust_for_gain(chip, cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700981 cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
983 cc_mah = div_s64(cc_uvh, chip->r_sense);
984 *val = cc_mah;
985}
986
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700987static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
988 int fcc, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700990 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991
992 rbatt = calculate_rbatt(chip);
993 if (rbatt < 0) {
994 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700995 pr_debug("rbatt unavailable assuming %d\n", rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700996 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700997
998 /* calculate unusable charge */
999 voltage_unusable_uv = (rbatt * chip->i_test)
1000 + (chip->v_failure * 1000);
1001 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
1002 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001003 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
1004 rbatt, voltage_unusable_uv, pc_unusable);
1005 return (fcc * pc_unusable) / 100;
1006}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -07001007
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001008/* calculate remainging charge at the time of ocv */
1009static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
1010 int batt_temp, int chargecycles)
1011{
1012 int rc, ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001013
1014 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001015 ocv = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001016 rc = read_last_good_ocv(chip, &ocv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001017 if (rc)
1018 pr_debug("failed to read ocv rc = %d\n", rc);
1019
1020 if (ocv == 0) {
1021 ocv = last_ocv_uv;
1022 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001023 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001024
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001025 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001026 pr_debug("ocv = %d pc = %d\n", ocv, pc);
1027 return (fcc * pc) / 100;
1028}
1029
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001030static void calculate_charging_params(struct pm8921_bms_chip *chip,
1031 int batt_temp, int chargecycles,
1032 int *fcc,
1033 int *unusable_charge,
1034 int *remaining_charge,
1035 int64_t *cc_mah)
1036{
1037 int coulumb_counter;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001038 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001039
1040 *fcc = calculate_fcc(chip, batt_temp, chargecycles);
1041 pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
1042 *fcc, batt_temp, chargecycles);
1043
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001044 /* fcc doesnt need to be read from hardware, lock the bms now */
1045 spin_lock_irqsave(&chip->bms_output_lock, flags);
1046 pm_bms_lock_output_data(chip);
1047
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001048 *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
1049 batt_temp, chargecycles);
1050
1051 pr_debug("UUC = %umAh", *unusable_charge);
1052
1053 /* calculate remainging charge */
1054 *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
1055 batt_temp, chargecycles);
1056 pr_debug("RC = %umAh\n", *remaining_charge);
1057
1058 /* calculate cc milli_volt_hour */
1059 calculate_cc_mah(chip, cc_mah, &coulumb_counter);
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001060 pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001061
1062 pm_bms_unlock_output_data(chip);
1063 spin_unlock_irqrestore(&chip->bms_output_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001064}
1065
1066static int calculate_real_fcc(struct pm8921_bms_chip *chip,
1067 int batt_temp, int chargecycles)
1068{
1069 int fcc, unusable_charge;
1070 int remaining_charge;
1071 int64_t cc_mah;
1072 int real_fcc;
1073
1074 calculate_charging_params(chip, batt_temp, chargecycles,
1075 &fcc,
1076 &unusable_charge,
1077 &remaining_charge,
1078 &cc_mah);
1079
1080 real_fcc = remaining_charge - cc_mah;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001081 pr_debug("real_fcc = %d, RC = %d CC = %lld\n",
1082 real_fcc, remaining_charge, cc_mah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001083 return real_fcc;
1084}
1085/*
1086 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1087 * - coloumb counter charge
1088 * - unusable charge (due to battery resistance)
1089 * SOC% = (remaining usable charge/ fcc - usable_charge);
1090 */
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001091#define BMS_BATT_NOMINAL 3700000
1092#define MIN_OPERABLE_SOC 10
1093#define BATTERY_POWER_SUPPLY_SOC 53
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001094static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
1095 int batt_temp, int chargecycles)
1096{
1097 int remaining_usable_charge, fcc, unusable_charge;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001098 int remaining_charge, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001099 int update_userspace = 1;
1100 int64_t cc_mah;
1101
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001102 calculate_charging_params(chip, batt_temp, chargecycles,
1103 &fcc,
1104 &unusable_charge,
1105 &remaining_charge,
1106 &cc_mah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107
1108 /* calculate remaining usable charge */
1109 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001110 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001111 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
1112 if (soc > 100)
1113 soc = 100;
1114 pr_debug("SOC = %u%%\n", soc);
1115
1116 if (soc < MIN_OPERABLE_SOC) {
1117 int ocv = 0, rc;
1118
1119 rc = adc_based_ocv(chip, &ocv);
1120 if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
1121 /*
1122 * The ocv doesnt seem to have dropped for
1123 * soc to go negative.
1124 * The setup must be using a power supply
1125 * instead of real batteries.
1126 * Fake high enough soc to prevent userspace
1127 * shutdown for low battery
1128 */
1129 soc = BATTERY_POWER_SUPPLY_SOC;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001130 pr_debug("Adjusting SOC to %d\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001131 }
1132 }
1133
1134 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001135 pr_err("bad rem_usb_chg = %d rem_chg %d,"
1136 "cc_mah %lld, unusb_chg %d\n",
1137 remaining_usable_charge, remaining_charge,
1138 cc_mah, unusable_charge);
1139 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001140 "chargecycles = %d, batt_temp = %d"
1141 "fcc = %d soc =%d\n",
1142 last_ocv_uv, chargecycles, batt_temp,
1143 fcc, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001144 update_userspace = 0;
1145 }
1146
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001147 if (last_soc == -EINVAL || soc <= last_soc) {
1148 last_soc = update_userspace ? soc : last_soc;
1149 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001150 }
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001151
1152 /*
1153 * soc > last_soc
1154 * the device must be charging for reporting a higher soc, if not ignore
1155 * this soc and continue reporting the last_soc
1156 */
1157 if (the_chip->start_percent != 0) {
1158 last_soc = soc;
1159 } else {
1160 pr_debug("soc = %d reporting last_soc = %d\n", soc, last_soc);
1161 soc = last_soc;
1162 }
1163
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001164 return soc;
1165}
1166
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001167#define XOADC_MAX_1P25V 1312500
1168#define XOADC_MIN_1P25V 1187500
1169#define XOADC_MAX_0P625V 656250
1170#define XOADC_MIN_0P625V 593750
1171
1172#define HKADC_V_PER_BIT_MUL_FACTOR 977
1173#define HKADC_V_PER_BIT_DIV_FACTOR 10
1174static int calib_hkadc_convert_microvolt(unsigned int phy)
1175{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001176 return (phy - 0x6000) *
1177 HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001178}
1179
1180static void calib_hkadc(struct pm8921_bms_chip *chip)
1181{
1182 int voltage, rc;
1183 struct pm8921_adc_chan_result result;
1184
1185 rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
1186 if (rc) {
1187 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1188 return;
1189 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001190 voltage = calib_hkadc_convert_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001191
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001192 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1193 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001194
1195 /* check for valid range */
1196 if (voltage > XOADC_MAX_1P25V)
1197 voltage = XOADC_MAX_1P25V;
1198 else if (voltage < XOADC_MIN_1P25V)
1199 voltage = XOADC_MIN_1P25V;
1200 chip->xoadc_v125 = voltage;
1201
1202 rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
1203 if (rc) {
1204 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1205 return;
1206 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001207 voltage = calib_hkadc_convert_microvolt(result.adc_code);
1208 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
1209 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001210 /* check for valid range */
1211 if (voltage > XOADC_MAX_0P625V)
1212 voltage = XOADC_MAX_0P625V;
1213 else if (voltage < XOADC_MIN_0P625V)
1214 voltage = XOADC_MIN_0P625V;
1215
1216 chip->xoadc_v0625 = voltage;
1217}
1218
1219static void calibrate_hkadc_work(struct work_struct *work)
1220{
1221 struct pm8921_bms_chip *chip = container_of(work,
1222 struct pm8921_bms_chip, calib_hkadc_work);
1223
1224 calib_hkadc(chip);
1225}
1226
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001227#define START_CONV_BIT BIT(7)
1228#define EOC_CONV_BIT BIT(6)
1229#define SEL_CCADC_BIT BIT(1)
1230#define EN_ARB_BIT BIT(0)
1231
1232#define CCADC_CALIB_DIG_PARAM 0xE3
1233#define CCADC_CALIB_RSV_GND 0x40
1234#define CCADC_CALIB_RSV_25MV 0x80
1235#define CCADC_CALIB_ANA_PARAM 0x1B
1236#define SAMPLE_COUNT 16
1237#define ADC_WAIT_COUNT 10
1238
1239#define CCADC_MAX_25MV 30000
1240#define CCADC_MIN_25MV 20000
1241#define CCADC_MAX_0UV -4000
1242#define CCADC_MIN_0UV -7000
1243
1244#define CCADC_INTRINSIC_OFFSET 0xC000
1245
1246#define REG_SBI_CONFIG 0x04F
1247#define PAGE3_ENABLE_MASK 0x6
1248
1249static int calib_ccadc_enable_trim_access(struct pm8921_bms_chip *chip,
1250 u8 *sbi_config)
1251{
1252 u8 reg;
1253 int rc;
1254
1255 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1256 if (rc) {
1257 pr_err("error = %d reading sbi config reg\n", rc);
1258 return rc;
1259 }
1260
1261 reg = *sbi_config | PAGE3_ENABLE_MASK;
1262 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
1263}
1264
1265static int calib_ccadc_restore_trim_access(struct pm8921_bms_chip *chip,
1266 u8 sbi_config)
1267{
1268 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1269}
1270
1271static int calib_ccadc_enable_arbiter(struct pm8921_bms_chip *chip)
1272{
1273 int rc;
1274
1275 /* enable Arbiter, must be sent twice */
1276 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1277 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1278 if (rc < 0) {
1279 pr_err("error = %d enabling arbiter for offset\n", rc);
1280 return rc;
1281 }
1282 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1283 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1284 if (rc < 0) {
1285 pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
1286 return rc;
1287 }
1288 return 0;
1289}
1290
1291static int calib_start_conv(struct pm8921_bms_chip *chip,
1292 u16 *result)
1293{
1294 int rc, i;
1295 u8 data_msb, data_lsb, reg;
1296
1297 /* Start conversion */
1298 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1299 START_CONV_BIT, START_CONV_BIT);
1300 if (rc < 0) {
1301 pr_err("error = %d starting offset meas\n", rc);
1302 return rc;
1303 }
1304
1305 /* Wait for End of conversion */
1306 for (i = 0; i < ADC_WAIT_COUNT; i++) {
1307 rc = pm8xxx_readb(chip->dev->parent,
1308 ADC_ARB_SECP_CNTRL, &reg);
1309 if (rc < 0) {
1310 pr_err("error = %d read eoc for offset\n", rc);
1311 return rc;
1312 }
1313 if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
1314 msleep(60);
1315 else
1316 break;
1317 }
1318 if (i == ADC_WAIT_COUNT) {
1319 pr_err("waited too long for offset eoc\n");
1320 return rc;
1321 }
1322
1323 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
1324 if (rc < 0) {
1325 pr_err("error = %d reading offset lsb\n", rc);
1326 return rc;
1327 }
1328
1329 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
1330 if (rc < 0) {
1331 pr_err("error = %d reading offset msb\n", rc);
1332 return rc;
1333 }
1334
1335 *result = (data_msb << 8) | data_lsb;
1336 return 0;
1337}
1338
1339static int calib_ccadc_read_trim(struct pm8921_bms_chip *chip,
1340 int addr, u8 *data_msb, u8 *data_lsb)
1341{
1342 int rc;
1343 u8 sbi_config;
1344
1345 calib_ccadc_enable_trim_access(chip, &sbi_config);
1346 rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
1347 if (rc < 0) {
1348 pr_err("error = %d read msb\n", rc);
1349 return rc;
1350 }
1351 rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
1352 if (rc < 0) {
1353 pr_err("error = %d read lsb\n", rc);
1354 return rc;
1355 }
1356 calib_ccadc_restore_trim_access(chip, sbi_config);
1357 return 0;
1358}
1359
1360static int calib_ccadc_read_gain_uv(struct pm8921_bms_chip *chip)
1361{
1362 s8 data_msb;
1363 u8 data_lsb;
1364 int rc, gain, offset;
1365
1366 rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
1367 &data_msb, &data_lsb);
1368 gain = (data_msb << 8) | data_lsb;
1369
1370 rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
1371 &data_msb, &data_lsb);
1372 offset = (data_msb << 8) | data_lsb;
1373
1374 pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", gain, offset);
1375 gain = ccadc_reading_to_microvolt(chip, (s64)gain - offset);
1376 return gain;
1377}
1378
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001379#define CCADC_PROGRAM_TRIM_COUNT 2
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001380#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT 4
1381#define ADC_ARB_BMS_CNTRL_CONV_MASK 0x03
1382#define BMS_CONV_IN_PROGRESS 0x2
1383
1384static int calib_ccadc_program_trim(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001385 int addr, u8 data_msb, u8 data_lsb,
1386 int wait)
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001387{
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001388 int i, rc, loop;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001389 u8 cntrl, sbi_config;
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001390 bool in_progress = 0;
1391
1392 loop = wait ? CCADC_PROGRAM_TRIM_COUNT : 0;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001393
1394 calib_ccadc_enable_trim_access(chip, &sbi_config);
1395
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001396 for (i = 0; i < loop; i++) {
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001397 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
1398 if (rc < 0) {
1399 pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
1400 return rc;
1401 }
1402
1403 /* break if a ccadc conversion is not happening */
1404 in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
1405 & ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
1406
1407 if (!in_progress)
1408 break;
1409 }
1410
1411 if (in_progress) {
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001412 pr_debug("conv in progress cannot write trim,returing EBUSY\n");
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001413 return -EBUSY;
1414 }
1415
1416 rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
1417 if (rc < 0) {
1418 pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
1419 return rc;
1420 }
1421 rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
1422 if (rc < 0) {
1423 pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
1424 return rc;
1425 }
1426 calib_ccadc_restore_trim_access(chip, sbi_config);
1427 return 0;
1428}
1429
1430static void calib_ccadc(struct pm8921_bms_chip *chip)
1431{
1432 u8 data_msb, data_lsb, sec_cntrl;
1433 int result_offset, voltage_offset, result_gain;
1434 u16 result;
1435 int i, rc;
1436
1437 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_CNTRL, &sec_cntrl);
1438 if (rc < 0) {
1439 pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
1440 return;
1441 }
1442
1443 rc = calib_ccadc_enable_arbiter(chip);
1444 if (rc < 0) {
1445 pr_err("error = %d enabling arbiter for offset\n", rc);
1446 goto bail;
1447 }
1448
1449 /*
1450 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1451 * up, pending verification through bench
1452 */
1453 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1454 CCADC_CALIB_DIG_PARAM);
1455 if (rc < 0) {
1456 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
1457 goto bail;
1458 }
1459
1460 result_offset = 0;
1461 for (i = 0; i < SAMPLE_COUNT; i++) {
1462 /* Short analog inputs to CCADC internally to ground */
1463 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_RSV,
1464 CCADC_CALIB_RSV_GND);
1465 if (rc < 0) {
1466 pr_err("error = %d selecting gnd voltage\n", rc);
1467 goto bail;
1468 }
1469
1470 /* Enable CCADC */
1471 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1472 CCADC_CALIB_ANA_PARAM);
1473 if (rc < 0) {
1474 pr_err("error = %d enabling ccadc\n", rc);
1475 goto bail;
1476 }
1477
1478 rc = calib_start_conv(chip, &result);
1479 if (rc < 0) {
1480 pr_err("error = %d for zero volt measurement\n", rc);
1481 goto bail;
1482 }
1483
1484 result_offset += result;
1485 }
1486
1487 result_offset = result_offset / SAMPLE_COUNT;
1488
1489 voltage_offset = ccadc_reading_to_microvolt(chip,
1490 ((s64)result_offset - CCADC_INTRINSIC_OFFSET));
1491
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001492 pr_debug("offset result_offset = 0x%x, voltage = %d microVolts\n",
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001493 result_offset, voltage_offset);
1494
1495 /* Sanity Check */
1496 if (voltage_offset > CCADC_MAX_0UV) {
1497 pr_err("offset voltage = %d is huge limiting to %d\n",
1498 voltage_offset, CCADC_MAX_0UV);
1499 result_offset = CCADC_INTRINSIC_OFFSET
1500 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MAX_0UV);
1501 } else if (voltage_offset < CCADC_MIN_0UV) {
1502 pr_err("offset voltage = %d is too low limiting to %d\n",
1503 voltage_offset, CCADC_MIN_0UV);
1504 result_offset = CCADC_INTRINSIC_OFFSET
1505 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MIN_0UV);
1506 }
1507
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001508 chip->ccadc_result_offset = result_offset;
1509 data_msb = chip->ccadc_result_offset >> 8;
1510 data_lsb = chip->ccadc_result_offset;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001511
1512 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001513 data_msb, data_lsb, 1);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001514 if (rc) {
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001515 pr_debug("error = %d programming offset trim 0x%02x 0x%02x\n",
1516 rc, data_msb, data_lsb);
1517 /* enable the interrupt and write it when it fires */
1518 pm8921_bms_enable_irq(chip, PM8921_BMS_CCADC_EOC);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001519 }
1520
1521 rc = calib_ccadc_enable_arbiter(chip);
1522 if (rc < 0) {
1523 pr_err("error = %d enabling arbiter for gain\n", rc);
1524 goto bail;
1525 }
1526
1527 /*
1528 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1529 * up, pending verification through bench
1530 */
1531 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1532 CCADC_CALIB_DIG_PARAM);
1533 if (rc < 0) {
1534 pr_err("error = %d enabling decimation ration for gain\n", rc);
1535 goto bail;
1536 }
1537
1538 result_gain = 0;
1539 for (i = 0; i < SAMPLE_COUNT; i++) {
1540 rc = pm8xxx_writeb(chip->dev->parent,
1541 ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
1542 if (rc < 0) {
1543 pr_err("error = %d selecting 25mV for gain\n", rc);
1544 goto bail;
1545 }
1546
1547 /* Enable CCADC */
1548 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1549 CCADC_CALIB_ANA_PARAM);
1550 if (rc < 0) {
1551 pr_err("error = %d enabling ccadc\n", rc);
1552 goto bail;
1553 }
1554
1555 rc = calib_start_conv(chip, &result);
1556 if (rc < 0) {
1557 pr_err("error = %d for adc reading 25mV\n", rc);
1558 goto bail;
1559 }
1560
1561 result_gain += result;
1562 }
1563 result_gain = result_gain / SAMPLE_COUNT;
1564
1565 /*
1566 * result_offset includes INTRINSIC OFFSET
1567 * chip->ccadc_gain_uv will be the actual voltage
1568 * measured for 25000UV
1569 */
1570 chip->ccadc_gain_uv = ccadc_reading_to_microvolt(chip,
1571 ((s64)result_gain - result_offset));
1572
1573 pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
1574 result_gain,
1575 chip->ccadc_gain_uv);
1576 /* Sanity Check */
1577 if (chip->ccadc_gain_uv > CCADC_MAX_25MV) {
1578 pr_err("gain voltage = %d is huge limiting to %d\n",
1579 chip->ccadc_gain_uv, CCADC_MAX_25MV);
1580 chip->ccadc_gain_uv = CCADC_MAX_25MV;
1581 result_gain = result_offset +
1582 microvolt_to_ccadc_reading(chip, CCADC_MAX_25MV);
1583 } else if (chip->ccadc_gain_uv < CCADC_MIN_25MV) {
1584 pr_err("gain voltage = %d is too low limiting to %d\n",
1585 chip->ccadc_gain_uv, CCADC_MIN_25MV);
1586 chip->ccadc_gain_uv = CCADC_MIN_25MV;
1587 result_gain = result_offset +
1588 microvolt_to_ccadc_reading(chip, CCADC_MIN_25MV);
1589 }
1590
1591 data_msb = result_gain >> 8;
1592 data_lsb = result_gain;
1593 rc = calib_ccadc_program_trim(chip, CCADC_FULLSCALE_TRIM1,
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001594 data_msb, data_lsb, 0);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001595 if (rc)
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001596 pr_debug("error = %d programming gain trim\n", rc);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001597bail:
1598 pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
1599}
1600
1601static void calibrate_ccadc_work(struct work_struct *work)
1602{
1603 struct pm8921_bms_chip *chip = container_of(work,
1604 struct pm8921_bms_chip, calib_ccadc_work.work);
1605
1606 calib_ccadc(chip);
1607 schedule_delayed_work(&chip->calib_ccadc_work,
1608 round_jiffies_relative(msecs_to_jiffies
1609 (chip->calib_delay_ms)));
1610}
1611
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001612int pm8921_bms_get_vsense_avg(int *result)
1613{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001614 int rc = -EINVAL;
1615 unsigned long flags;
1616
1617 if (the_chip) {
1618 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1619 pm_bms_lock_output_data(the_chip);
1620 rc = read_vsense_avg(the_chip, result);
1621 pm_bms_unlock_output_data(the_chip);
1622 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1623 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001624
1625 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001626 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001627}
1628EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1629
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001630int pm8921_bms_get_battery_current(int *result)
1631{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001632 unsigned long flags;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001633 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001634
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001635 if (!the_chip) {
1636 pr_err("called before initialization\n");
1637 return -EINVAL;
1638 }
1639 if (the_chip->r_sense == 0) {
1640 pr_err("r_sense is zero\n");
1641 return -EINVAL;
1642 }
1643
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001644 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1645 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001646 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001647 pm_bms_unlock_output_data(the_chip);
1648 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001649 pr_debug("vsense=%d\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001650 /* cast for signed division */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001651 *result = vsense / (int)the_chip->r_sense;
1652
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001653 return 0;
1654}
1655EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1656
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001657int pm8921_bms_get_percent_charge(void)
1658{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001659 int batt_temp, rc;
1660 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001661
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001662 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;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001676 return calculate_state_of_charge(the_chip,
1677 batt_temp, last_chargecycles);
1678}
1679EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1680
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001681int pm8921_bms_get_fcc(void)
1682{
1683 int batt_temp, rc;
1684 struct pm8921_adc_chan_result result;
1685
1686 if (!the_chip) {
1687 pr_err("called before initialization\n");
1688 return -EINVAL;
1689 }
1690
1691 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1692 if (rc) {
1693 pr_err("error reading adc channel = %d, rc = %d\n",
1694 the_chip->batt_temp_channel, rc);
1695 return rc;
1696 }
1697 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1698 result.measurement);
1699 batt_temp = (int)result.physical;
1700 return calculate_fcc(the_chip, batt_temp, last_chargecycles);
1701}
1702EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1703
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001704void pm8921_bms_charging_began(void)
1705{
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001706 the_chip->start_percent = pm8921_bms_get_percent_charge();
1707 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001708}
1709EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1710
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001711void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001712{
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001713 if (is_battery_full && the_chip != NULL) {
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001714 unsigned long flags;
1715 int batt_temp, rc, cc_reading;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001716 struct pm8921_adc_chan_result result;
1717
1718 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1719 if (rc) {
1720 pr_err("error reading adc channel = %d, rc = %d\n",
1721 the_chip->batt_temp_channel, rc);
1722 goto charge_cycle_calculation;
1723 }
1724 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1725 result.measurement);
1726 batt_temp = (int)result.physical;
1727 last_real_fcc = calculate_real_fcc(the_chip,
1728 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001729 last_real_fcc_batt_temp = batt_temp;
1730 readjust_fcc_table();
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001731
1732 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1733 pm_bms_lock_output_data(the_chip);
1734 pm_bms_read_output_data(the_chip, LAST_GOOD_OCV_VALUE,
1735 &the_chip->ocv_reading_at_100);
1736 read_cc(the_chip, &cc_reading);
1737 pm_bms_unlock_output_data(the_chip);
1738 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1739 the_chip->cc_reading_at_100 = cc_reading;
1740 pr_debug("EOC ocv_reading = 0x%x cc_reading = %d\n",
1741 the_chip->ocv_reading_at_100,
1742 the_chip->cc_reading_at_100);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001743 }
1744
1745charge_cycle_calculation:
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001746 the_chip->end_percent = pm8921_bms_get_percent_charge();
1747 if (the_chip->end_percent > the_chip->start_percent) {
1748 last_charge_increase =
1749 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001750 if (last_charge_increase > 100) {
1751 last_chargecycles++;
1752 last_charge_increase = last_charge_increase % 100;
1753 }
1754 }
1755 pr_debug("end_percent = %u%% last_charge_increase = %d"
1756 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001757 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001758 last_charge_increase,
1759 last_chargecycles);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001760 the_chip->start_percent = 0;
1761 the_chip->end_percent = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001762}
1763EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1764
1765static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1766{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001767 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001768 return IRQ_HANDLED;
1769}
1770
1771static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1772{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001773 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001774 return IRQ_HANDLED;
1775}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001776
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001777static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1778{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001779 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001780 return IRQ_HANDLED;
1781}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001782
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001783static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1784{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001785 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001786 return IRQ_HANDLED;
1787}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001788
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001789static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1790{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001791 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001792
1793 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001794 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001795 return IRQ_HANDLED;
1796}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001797
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001798static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1799{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001800 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001801
1802 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001803 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001804 return IRQ_HANDLED;
1805}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001806
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001807static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1808{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001809 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001810 return IRQ_HANDLED;
1811}
1812
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001813static irqreturn_t pm8921_bms_ccadc_eoc_handler(int irq, void *data)
1814{
1815 u8 data_msb, data_lsb;
1816 struct pm8921_bms_chip *chip = data;
1817 int rc;
1818
1819 pr_debug("irq = %d triggered\n", irq);
1820 data_msb = chip->ccadc_result_offset >> 8;
1821 data_lsb = chip->ccadc_result_offset;
1822
1823 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
1824 data_msb, data_lsb, 0);
1825 pm8921_bms_disable_irq(chip, PM8921_BMS_CCADC_EOC);
1826
1827 return IRQ_HANDLED;
1828}
1829
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001830struct pm_bms_irq_init_data {
1831 unsigned int irq_id;
1832 char *name;
1833 unsigned long flags;
1834 irqreturn_t (*handler)(int, void *);
1835};
1836
1837#define BMS_IRQ(_id, _flags, _handler) \
1838{ \
1839 .irq_id = _id, \
1840 .name = #_id, \
1841 .flags = _flags, \
1842 .handler = _handler, \
1843}
1844
1845struct pm_bms_irq_init_data bms_irq_data[] = {
1846 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1847 pm8921_bms_sbi_write_ok_handler),
1848 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1849 pm8921_bms_cc_thr_handler),
1850 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1851 pm8921_bms_vsense_thr_handler),
1852 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1853 pm8921_bms_vsense_for_r_handler),
1854 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1855 pm8921_bms_ocv_for_r_handler),
1856 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1857 pm8921_bms_good_ocv_handler),
1858 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1859 pm8921_bms_vsense_avg_handler),
Abhijeet Dharmapurikare5f46522011-10-28 19:04:50 -07001860 BMS_IRQ(PM8921_BMS_CCADC_EOC, IRQF_TRIGGER_RISING,
1861 pm8921_bms_ccadc_eoc_handler),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001862};
1863
1864static void free_irqs(struct pm8921_bms_chip *chip)
1865{
1866 int i;
1867
1868 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1869 if (chip->pmic_bms_irq[i]) {
1870 free_irq(chip->pmic_bms_irq[i], NULL);
1871 chip->pmic_bms_irq[i] = 0;
1872 }
1873}
1874
1875static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1876 struct platform_device *pdev)
1877{
1878 struct resource *res;
1879 int ret, i;
1880
1881 ret = 0;
1882 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1883
1884 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1885 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1886 bms_irq_data[i].name);
1887 if (res == NULL) {
1888 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1889 goto err_out;
1890 }
1891 ret = request_irq(res->start, bms_irq_data[i].handler,
1892 bms_irq_data[i].flags,
1893 bms_irq_data[i].name, chip);
1894 if (ret < 0) {
1895 pr_err("couldn't request %d (%s) %d\n", res->start,
1896 bms_irq_data[i].name, ret);
1897 goto err_out;
1898 }
1899 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1900 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1901 }
1902 return 0;
1903
1904err_out:
1905 free_irqs(chip);
1906 return -EINVAL;
1907}
1908
1909#define EN_BMS_BIT BIT(7)
1910#define EN_PON_HS_BIT BIT(0)
1911static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1912{
1913 int rc;
1914
1915 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1916 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1917 if (rc) {
1918 pr_err("failed to enable pon and bms addr = %d %d",
1919 BMS_CONTROL, rc);
1920 }
1921
1922 return 0;
1923}
1924
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001925static void check_initial_ocv(struct pm8921_bms_chip *chip)
1926{
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001927 int ocv, rc;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001928
1929 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001930 * Check if a ocv is available in bms hw,
1931 * if not compute it here at boot time and save it
1932 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001933 */
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001934 ocv = 0;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001935 rc = read_last_good_ocv(chip, &ocv);
1936 if (rc || ocv == 0) {
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001937 rc = adc_based_ocv(chip, &ocv);
1938 if (rc) {
1939 pr_err("failed to read adc based ocv rc = %d\n", rc);
1940 ocv = DEFAULT_OCV_MICROVOLTS;
1941 }
1942 last_ocv_uv = ocv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001943 }
1944 pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
1945}
1946
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001947static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1948{
1949 int rc;
1950 struct pm8921_adc_chan_result result;
1951
1952 rc = pm8921_adc_read(chip->batt_id_channel, &result);
1953 if (rc) {
1954 pr_err("error reading batt id channel = %d, rc = %d\n",
1955 chip->vbat_channel, rc);
1956 return rc;
1957 }
1958 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1959 result.measurement);
1960 return result.physical;
1961}
1962
1963#define PALLADIUM_ID_MIN 2500
1964#define PALLADIUM_ID_MAX 4000
1965static int set_battery_data(struct pm8921_bms_chip *chip)
1966{
1967 int64_t battery_id;
1968
1969 battery_id = read_battery_id(chip);
1970
1971 if (battery_id < 0) {
1972 pr_err("cannot read battery id err = %lld\n", battery_id);
1973 return battery_id;
1974 }
1975
1976 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
1977 chip->fcc = palladium_1500_data.fcc;
1978 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1979 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1980 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1981 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1982 return 0;
1983 } else {
1984 pr_warn("invalid battery id, palladium 1500 assumed\n");
1985 chip->fcc = palladium_1500_data.fcc;
1986 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1987 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1988 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1989 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1990 return 0;
1991 }
1992}
1993
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001994enum {
1995 CALC_RBATT,
1996 CALC_FCC,
1997 CALC_PC,
1998 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001999 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002000 CALIB_CCADC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002001};
2002
2003static int test_batt_temp = 5;
2004static int test_chargecycle = 150;
2005static int test_ocv = 3900000;
2006enum {
2007 TEST_BATT_TEMP,
2008 TEST_CHARGE_CYCLE,
2009 TEST_OCV,
2010};
2011static int get_test_param(void *data, u64 * val)
2012{
2013 switch ((int)data) {
2014 case TEST_BATT_TEMP:
2015 *val = test_batt_temp;
2016 break;
2017 case TEST_CHARGE_CYCLE:
2018 *val = test_chargecycle;
2019 break;
2020 case TEST_OCV:
2021 *val = test_ocv;
2022 break;
2023 default:
2024 return -EINVAL;
2025 }
2026 return 0;
2027}
2028static int set_test_param(void *data, u64 val)
2029{
2030 switch ((int)data) {
2031 case TEST_BATT_TEMP:
2032 test_batt_temp = (int)val;
2033 break;
2034 case TEST_CHARGE_CYCLE:
2035 test_chargecycle = (int)val;
2036 break;
2037 case TEST_OCV:
2038 test_ocv = (int)val;
2039 break;
2040 default:
2041 return -EINVAL;
2042 }
2043 return 0;
2044}
2045DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
2046
2047static int get_calc(void *data, u64 * val)
2048{
2049 int param = (int)data;
2050 int ret = 0;
2051
2052 *val = 0;
2053
2054 /* global irq number passed in via data */
2055 switch (param) {
2056 case CALC_RBATT:
2057 *val = calculate_rbatt(the_chip);
2058 break;
2059 case CALC_FCC:
2060 *val = calculate_fcc(the_chip, test_batt_temp,
2061 test_chargecycle);
2062 break;
2063 case CALC_PC:
2064 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
2065 test_chargecycle);
2066 break;
2067 case CALC_SOC:
2068 *val = calculate_state_of_charge(the_chip,
2069 test_batt_temp, test_chargecycle);
2070 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002071 case CALIB_HKADC:
2072 /* reading this will trigger calibration */
2073 *val = 0;
2074 calib_hkadc(the_chip);
2075 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002076 case CALIB_CCADC:
2077 /* reading this will trigger calibration */
2078 *val = 0;
2079 calib_ccadc(the_chip);
2080 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002081 default:
2082 ret = -EINVAL;
2083 }
2084 return ret;
2085}
2086DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
2087
2088static int get_reading(void *data, u64 * val)
2089{
2090 int param = (int)data;
2091 int ret = 0;
2092
2093 *val = 0;
2094
2095 /* global irq number passed in via data */
2096 switch (param) {
2097 case CC_MSB:
2098 case CC_LSB:
2099 read_cc(the_chip, (int *)val);
2100 break;
2101 case LAST_GOOD_OCV_VALUE:
2102 read_last_good_ocv(the_chip, (uint *)val);
2103 break;
2104 case VBATT_FOR_RBATT:
2105 read_vbatt_for_rbatt(the_chip, (uint *)val);
2106 break;
2107 case VSENSE_FOR_RBATT:
2108 read_vsense_for_rbatt(the_chip, (uint *)val);
2109 break;
2110 case OCV_FOR_RBATT:
2111 read_ocv_for_rbatt(the_chip, (uint *)val);
2112 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002113 case VSENSE_AVG:
2114 read_vsense_avg(the_chip, (uint *)val);
2115 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002116 default:
2117 ret = -EINVAL;
2118 }
2119 return ret;
2120}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002121DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002122
2123static int get_rt_status(void *data, u64 * val)
2124{
2125 int i = (int)data;
2126 int ret;
2127
2128 /* global irq number passed in via data */
2129 ret = pm_bms_get_rt_status(the_chip, i);
2130 *val = ret;
2131 return 0;
2132}
2133DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2134
2135static int get_reg(void *data, u64 * val)
2136{
2137 int addr = (int)data;
2138 int ret;
2139 u8 temp;
2140
2141 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2142 if (ret) {
2143 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2144 addr, temp, ret);
2145 return -EAGAIN;
2146 }
2147 *val = temp;
2148 return 0;
2149}
2150
2151static int set_reg(void *data, u64 val)
2152{
2153 int addr = (int)data;
2154 int ret;
2155 u8 temp;
2156
2157 temp = (u8) val;
2158 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2159 if (ret) {
2160 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2161 addr, temp, ret);
2162 return -EAGAIN;
2163 }
2164 return 0;
2165}
2166DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2167
2168static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2169{
2170 int i;
2171
2172 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
2173
2174 if (IS_ERR(chip->dent)) {
2175 pr_err("pmic bms couldnt create debugfs dir\n");
2176 return;
2177 }
2178
2179 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2180 (void *)BMS_CONTROL, &reg_fops);
2181 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2182 (void *)BMS_OUTPUT0, &reg_fops);
2183 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2184 (void *)BMS_OUTPUT1, &reg_fops);
2185 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2186 (void *)BMS_TEST1, &reg_fops);
2187 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
2188 (void *)CCADC_ANA_PARAM, &reg_fops);
2189 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
2190 (void *)CCADC_DIG_PARAM, &reg_fops);
2191 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
2192 (void *)CCADC_RSV, &reg_fops);
2193 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
2194 (void *)CCADC_DATA0, &reg_fops);
2195 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
2196 (void *)CCADC_DATA1, &reg_fops);
2197 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
2198 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
2199 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
2200 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
2201
2202 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2203 (void *)TEST_BATT_TEMP, &temp_fops);
2204 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2205 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2206 debugfs_create_file("test_ocv", 0644, chip->dent,
2207 (void *)TEST_OCV, &temp_fops);
2208
2209 debugfs_create_file("read_cc", 0644, chip->dent,
2210 (void *)CC_MSB, &reading_fops);
2211 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2212 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2213 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2214 (void *)VBATT_FOR_RBATT, &reading_fops);
2215 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2216 (void *)VSENSE_FOR_RBATT, &reading_fops);
2217 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2218 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002219 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2220 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002221
2222 debugfs_create_file("show_rbatt", 0644, chip->dent,
2223 (void *)CALC_RBATT, &calc_fops);
2224 debugfs_create_file("show_fcc", 0644, chip->dent,
2225 (void *)CALC_FCC, &calc_fops);
2226 debugfs_create_file("show_pc", 0644, chip->dent,
2227 (void *)CALC_PC, &calc_fops);
2228 debugfs_create_file("show_soc", 0644, chip->dent,
2229 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002230 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2231 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002232 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2233 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002234
2235 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2236 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2237 debugfs_create_file(bms_irq_data[i].name, 0444,
2238 chip->dent,
2239 (void *)bms_irq_data[i].irq_id,
2240 &rt_fops);
2241 }
2242}
2243
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002244static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2245{
2246 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002247 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002248 struct pm8921_bms_chip *chip;
2249 const struct pm8921_bms_platform_data *pdata
2250 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002251
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002252 if (!pdata) {
2253 pr_err("missing platform data\n");
2254 return -EINVAL;
2255 }
2256
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002257 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002258 if (!chip) {
2259 pr_err("Cannot allocate pm_bms_chip\n");
2260 return -ENOMEM;
2261 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002262 spin_lock_init(&chip->bms_output_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002263 chip->dev = &pdev->dev;
2264 chip->r_sense = pdata->r_sense;
2265 chip->i_test = pdata->i_test;
2266 chip->v_failure = pdata->v_failure;
2267 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002268 rc = set_battery_data(chip);
2269 if (rc) {
2270 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002271 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002272 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002273
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002274 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2275 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002276 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2277 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002278 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002279 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002280 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002281
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002282 rc = request_irqs(chip, pdev);
2283 if (rc) {
2284 pr_err("couldn't register interrupts rc = %d\n", rc);
2285 goto free_chip;
2286 }
2287
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002288 rc = pm8921_bms_hw_init(chip);
2289 if (rc) {
2290 pr_err("couldn't init hardware rc = %d\n", rc);
2291 goto free_irqs;
2292 }
2293
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002294 platform_set_drvdata(pdev, chip);
2295 the_chip = chip;
2296 create_debugfs_entries(chip);
2297
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002298 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002299 chip->ccadc_gain_uv = calib_ccadc_read_gain_uv(chip);
2300
2301 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
2302 /* begin calibration only on chips > 2.0 */
2303 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
2304 calibrate_ccadc_work(&(chip->calib_ccadc_work.work));
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002305
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002306 /* initial hkadc calibration */
2307 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002308 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2309 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2310 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002311
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002312 get_battery_uvolts(chip, &vbatt);
2313 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2314 pm8921_bms_get_percent_charge(),
2315 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002316 return 0;
2317
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002318free_irqs:
2319 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002320free_chip:
2321 kfree(chip);
2322 return rc;
2323}
2324
2325static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2326{
2327 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2328
2329 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002330 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002331 platform_set_drvdata(pdev, NULL);
2332 the_chip = NULL;
2333 kfree(chip);
2334 return 0;
2335}
2336
2337static struct platform_driver pm8921_bms_driver = {
2338 .probe = pm8921_bms_probe,
2339 .remove = __devexit_p(pm8921_bms_remove),
2340 .driver = {
2341 .name = PM8921_BMS_DEV_NAME,
2342 .owner = THIS_MODULE,
2343 },
2344};
2345
2346static int __init pm8921_bms_init(void)
2347{
2348 return platform_driver_register(&pm8921_bms_driver);
2349}
2350
2351static void __exit pm8921_bms_exit(void)
2352{
2353 platform_driver_unregister(&pm8921_bms_driver);
2354}
2355
2356late_initcall(pm8921_bms_init);
2357module_exit(pm8921_bms_exit);
2358
2359MODULE_LICENSE("GPL v2");
2360MODULE_DESCRIPTION("PMIC8921 bms driver");
2361MODULE_VERSION("1.0");
2362MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);