blob: 6741ef4fd231d4ca08aa8e98183f214fa6e18933 [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 Mohanadossaf91d902011-10-20 10:23:34 -070021#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080022#include <linux/mfd/pm8xxx/ccadc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023#include <linux/interrupt.h>
24#include <linux/bitops.h>
25#include <linux/debugfs.h>
26#include <linux/slab.h>
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070027#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028
29#define BMS_CONTROL 0x224
30#define BMS_OUTPUT0 0x230
31#define BMS_OUTPUT1 0x231
32#define BMS_TEST1 0x237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070034#define ADC_ARB_SECP_CNTRL 0x190
35#define ADC_ARB_SECP_AMUX_CNTRL 0x191
36#define ADC_ARB_SECP_ANA_PARAM 0x192
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070037#define ADC_ARB_SECP_DIG_PARAM 0x193
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070038#define ADC_ARB_SECP_RSV 0x194
39#define ADC_ARB_SECP_DATA1 0x195
40#define ADC_ARB_SECP_DATA0 0x196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070042#define ADC_ARB_BMS_CNTRL 0x18D
43
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044enum pmic_bms_interrupts {
45 PM8921_BMS_SBI_WRITE_OK,
46 PM8921_BMS_CC_THR,
47 PM8921_BMS_VSENSE_THR,
48 PM8921_BMS_VSENSE_FOR_R,
49 PM8921_BMS_OCV_FOR_R,
50 PM8921_BMS_GOOD_OCV,
51 PM8921_BMS_VSENSE_AVG,
52 PM_BMS_MAX_INTS,
53};
54
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055struct pm8921_bms_chip {
56 struct device *dev;
57 struct dentry *dent;
58 unsigned int r_sense;
59 unsigned int i_test;
60 unsigned int v_failure;
61 unsigned int fcc;
62 struct single_row_lut *fcc_temp_lut;
63 struct single_row_lut *fcc_sf_lut;
64 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
65 struct pc_sf_lut *pc_sf_lut;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070066 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070067 struct delayed_work calib_ccadc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068 unsigned int calib_delay_ms;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -070069 unsigned int revision;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070070 unsigned int xoadc_v0625;
71 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -070072 unsigned int batt_temp_channel;
73 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070074 unsigned int ref625mv_channel;
75 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -070076 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
78 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -070079 spinlock_t bms_output_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -070080 struct single_row_lut *adjusted_fcc_temp_lut;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -070081 unsigned int charging_began;
82 unsigned int start_percent;
83 unsigned int end_percent;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -080084
85 uint16_t ocv_reading_at_100;
86 int cc_reading_at_100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087};
88
89static struct pm8921_bms_chip *the_chip;
90
91#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093#define DEFAULT_CHARGE_CYCLES 0
94
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -070095static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
96static int last_charge_increase;
97module_param(last_chargecycles, int, 0644);
98module_param(last_charge_increase, int, 0644);
99
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102static int last_soc = -EINVAL;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700103static int last_real_fcc = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700104static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700106static int bms_ops_set(const char *val, const struct kernel_param *kp)
107{
108 if (*(int *)kp->arg == -EINVAL)
109 return param_set_int(val, kp);
110 else
111 return 0;
112}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700114static struct kernel_param_ops bms_param_ops = {
115 .set = bms_ops_set,
116 .get = param_get_int,
117};
118
119module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
120module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
121module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
122
123static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
124static void readjust_fcc_table(void)
125{
126 struct single_row_lut *temp, *old;
127 int i, fcc, ratio;
128
129 if (!the_chip->fcc_temp_lut) {
130 pr_err("The static fcc lut table is NULL\n");
131 return;
132 }
133
134 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
135 if (!temp) {
136 pr_err("Cannot allocate memory for adjusted fcc table\n");
137 return;
138 }
139
140 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
141
142 temp->cols = the_chip->fcc_temp_lut->cols;
143 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
144 temp->x[i] = the_chip->fcc_temp_lut->x[i];
145 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
146 temp->y[i] = (ratio * last_real_fcc);
147 temp->y[i] /= 1000;
148 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
149 temp->x[i], the_chip->fcc_temp_lut->y[i],
150 temp->y[i], ratio);
151 }
152
153 old = the_chip->adjusted_fcc_temp_lut;
154 the_chip->adjusted_fcc_temp_lut = temp;
155 kfree(old);
156}
157
158static int bms_last_real_fcc_set(const char *val,
159 const struct kernel_param *kp)
160{
161 int rc = 0;
162
163 if (last_real_fcc == -EINVAL)
164 rc = param_set_int(val, kp);
165 if (rc) {
166 pr_err("Failed to set last_real_fcc rc=%d\n", rc);
167 return rc;
168 }
169 if (last_real_fcc_batt_temp != -EINVAL)
170 readjust_fcc_table();
171 return rc;
172}
173static struct kernel_param_ops bms_last_real_fcc_param_ops = {
174 .set = bms_last_real_fcc_set,
175 .get = param_get_int,
176};
177module_param_cb(last_real_fcc, &bms_last_real_fcc_param_ops,
178 &last_real_fcc, 0644);
179
180static int bms_last_real_fcc_batt_temp_set(const char *val,
181 const struct kernel_param *kp)
182{
183 int rc = 0;
184
185 if (last_real_fcc_batt_temp == -EINVAL)
186 rc = param_set_int(val, kp);
187 if (rc) {
188 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
189 return rc;
190 }
191 if (last_real_fcc != -EINVAL)
192 readjust_fcc_table();
193 return rc;
194}
195
196static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
197 .set = bms_last_real_fcc_batt_temp_set,
198 .get = param_get_int,
199};
200module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
201 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202
203static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
204{
205 return pm8xxx_read_irq_stat(chip->dev->parent,
206 chip->pmic_bms_irq[irq_id]);
207}
208
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700209static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
210{
211 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
212 dev_dbg(chip->dev, "%s %d\n", __func__,
213 chip->pmic_bms_irq[interrupt]);
214 enable_irq(chip->pmic_bms_irq[interrupt]);
215 }
216}
217
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
219{
220 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700221 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
223 }
224}
225
226static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
227 u8 mask, u8 val)
228{
229 int rc;
230 u8 reg;
231
232 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
233 if (rc) {
234 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
235 return rc;
236 }
237 reg &= ~mask;
238 reg |= val & mask;
239 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
240 if (rc) {
241 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
242 return rc;
243 }
244 return 0;
245}
246
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700247#define HOLD_OREG_DATA BIT(1)
248static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
249{
250 int rc;
251
252 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
253 HOLD_OREG_DATA);
254 if (rc) {
255 pr_err("couldnt lock bms output rc = %d\n", rc);
256 return rc;
257 }
258 return 0;
259}
260
261static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
262{
263 int rc;
264
265 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
266 if (rc) {
267 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
268 return rc;
269 }
270 return 0;
271}
272
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273#define SELECT_OUTPUT_DATA 0x1C
274#define SELECT_OUTPUT_TYPE_SHIFT 2
275#define OCV_FOR_RBATT 0x0
276#define VSENSE_FOR_RBATT 0x1
277#define VBATT_FOR_RBATT 0x2
278#define CC_MSB 0x3
279#define CC_LSB 0x4
280#define LAST_GOOD_OCV_VALUE 0x5
281#define VSENSE_AVG 0x6
282#define VBATT_AVG 0x7
283
284static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
285 int16_t *result)
286{
287 int rc;
288 u8 reg;
289
290 if (!result) {
291 pr_err("result pointer null\n");
292 return -EINVAL;
293 }
294 *result = 0;
295 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
296 pr_err("invalid type %d asked to read\n", type);
297 return -EINVAL;
298 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700299
300 /* make sure the bms registers are locked */
301 rc = pm8xxx_readb(chip->dev->parent, BMS_CONTROL, &reg);
302 if (rc) {
303 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
304 type, rc);
305 return rc;
306 }
307
308 /* Output register data must be held (locked) while reading output */
309 WARN_ON(!(reg && HOLD_OREG_DATA));
310
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
312 type << SELECT_OUTPUT_TYPE_SHIFT);
313 if (rc) {
314 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
315 type, rc);
316 return rc;
317 }
318
319 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
320 if (rc) {
321 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
322 type, rc);
323 return rc;
324 }
325 *result = reg;
326 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
327 if (rc) {
328 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
329 type, rc);
330 return rc;
331 }
332 *result |= reg << 8;
333 pr_debug("type %d result %x", type, *result);
334 return 0;
335}
336
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700337#define V_PER_BIT_MUL_FACTOR 97656
338#define V_PER_BIT_DIV_FACTOR 1000
339#define XOADC_INTRINSIC_OFFSET 0x6000
340static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700341{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700342 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700343 return 0;
344
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700345 return (a - XOADC_INTRINSIC_OFFSET)
346 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700347}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700349#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700350#define VBATT_MUL_FACTOR 3
351static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
352 unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700353{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700354 u64 numerator, denominator;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700355
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700356 if (uv == 0)
357 return 0;
358
359 numerator = ((u64)uv - chip->xoadc_v0625) * XOADC_CALIB_UV;
360 denominator = chip->xoadc_v125 - chip->xoadc_v0625;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700361 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700362 return uv * VBATT_MUL_FACTOR;
363 return (XOADC_CALIB_UV + div_u64(numerator, denominator))
364 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700365}
366
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700367#define CC_RESOLUTION_N_V1 1085069
368#define CC_RESOLUTION_D_V1 100000
369#define CC_RESOLUTION_N_V2 868056
370#define CC_RESOLUTION_D_V2 10000
371static s64 cc_to_microvolt_v1(s64 cc)
372{
373 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
374}
375
376static s64 cc_to_microvolt_v2(s64 cc)
377{
378 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
379}
380
381static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
382{
383 /*
384 * resolution (the value of a single bit) was changed after revision 2.0
385 * for more accurate readings
386 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700387 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700388 cc_to_microvolt_v1((s64)cc) :
389 cc_to_microvolt_v2((s64)cc);
390}
391
392#define CC_READING_TICKS 55
393#define SLEEP_CLK_HZ 32768
394#define SECONDS_PER_HOUR 3600
395static s64 ccmicrovolt_to_uvh(s64 cc_uv)
396{
397 return div_s64(cc_uv * CC_READING_TICKS,
398 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
399}
400
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401/* returns the signed value read from the hardware */
402static int read_cc(struct pm8921_bms_chip *chip, int *result)
403{
404 int rc;
405 uint16_t msw, lsw;
406
407 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
408 if (rc) {
409 pr_err("fail to read CC_LSB rc = %d\n", rc);
410 return rc;
411 }
412 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
413 if (rc) {
414 pr_err("fail to read CC_MSB rc = %d\n", rc);
415 return rc;
416 }
417 *result = msw << 16 | lsw;
418 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800419 *result = *result - chip->cc_reading_at_100;
420 pr_debug("cc = %d after subtracting %d\n",
421 *result, chip->cc_reading_at_100);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422 return 0;
423}
424
425static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
426{
427 int rc;
428 uint16_t reading;
429
430 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
431 if (rc) {
432 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
433 return rc;
434 }
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800435
436 if (chip->ocv_reading_at_100 != reading) {
437 chip->ocv_reading_at_100 = 0;
438 chip->cc_reading_at_100 = 0;
439 *result = xoadc_reading_to_microvolt(reading);
440 pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
441 *result = adjust_xo_vbatt_reading(chip, *result);
442 pr_debug("after adj ocv_uV = %u\n", *result);
443 if (*result != 0)
444 last_ocv_uv = *result;
445 } else {
446 /*
447 * force 100% ocv by selecting the highest profiled ocv
448 * This is the first row last column entry in the ocv
449 * lookup table
450 */
451 int cols = chip->pc_temp_ocv_lut->cols;
452
453 pr_debug("Forcing max voltage %d\n",
454 1000 * chip->pc_temp_ocv_lut->ocv[0][cols-1]);
455 *result = 1000 * chip->pc_temp_ocv_lut->ocv[0][cols-1];
456 }
457
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 return 0;
459}
460
461static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
462{
463 int rc;
464 uint16_t reading;
465
466 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
467 if (rc) {
468 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
469 return rc;
470 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700471 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700473 *result = adjust_xo_vbatt_reading(chip, *result);
474 pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475 return 0;
476}
477
478static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
479{
480 int rc;
481 uint16_t reading;
482
483 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
484 if (rc) {
485 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
486 return rc;
487 }
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800488 *result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700489 pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800490 *result = pm8xxx_cc_adjust_for_gain(*result);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700491 pr_debug("after adj vsense_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700492 return 0;
493}
494
495static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
496{
497 int rc;
498 uint16_t reading;
499
500 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
501 if (rc) {
502 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
503 return rc;
504 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700505 *result = xoadc_reading_to_microvolt(reading);
506 pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
507 *result = adjust_xo_vbatt_reading(chip, *result);
508 pr_debug("after adj ocv_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509 return 0;
510}
511
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700512static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
513{
514 int rc;
515 int16_t reading;
516
517 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
518 if (rc) {
519 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
520 return rc;
521 }
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800522 *result = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
523 reading);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700524 pr_debug("raw = %04x vsense = %d\n", reading, *result);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800525 *result = pm8xxx_cc_adjust_for_gain((s64)*result);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700526 pr_debug("after adj vsense = %d\n", *result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700527 return 0;
528}
529
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
531{
532 if (y0 == y1 || x == x0)
533 return y0;
534 if (x1 == x0 || x == x1)
535 return y1;
536
537 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
538}
539
540static int interpolate_single_lut(struct single_row_lut *lut, int x)
541{
542 int i, result;
543
544 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700545 pr_debug("x %d less than known range return y = %d lut = %pS\n",
546 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 return lut->y[0];
548 }
549 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700550 pr_debug("x %d more than known range return y = %d lut = %pS\n",
551 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552 return lut->y[lut->cols - 1];
553 }
554
555 for (i = 0; i < lut->cols; i++)
556 if (x <= lut->x[i])
557 break;
558 if (x == lut->x[i]) {
559 result = lut->y[i];
560 } else {
561 result = linear_interpolate(
562 lut->y[i - 1],
563 lut->x[i - 1],
564 lut->y[i],
565 lut->x[i],
566 x);
567 }
568 return result;
569}
570
571static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
572{
573 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
574}
575
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700576static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
577{
578 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
579}
580
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700581static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
582 int cycles)
583{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700584 /*
585 * sf table could be null when no battery aging data is available, in
586 * that case return 100%
587 */
588 if (chip->fcc_sf_lut)
589 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
590 else
591 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592}
593
594static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
595 int cycles, int pc)
596{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700597 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700598 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700599 int row1 = 0;
600 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700602 /*
603 * sf table could be null when no battery aging data is available, in
604 * that case return 100%
605 */
606 if (!chip->pc_sf_lut)
607 return 100;
608
609 rows = chip->pc_sf_lut->rows;
610 cols = chip->pc_sf_lut->cols;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700611 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700612 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700613 row1 = 0;
614 row2 = 0;
615 }
616 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700617 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618 row1 = rows - 1;
619 row2 = rows - 1;
620 }
621 for (i = 0; i < rows; i++) {
622 if (pc == chip->pc_sf_lut->percent[i]) {
623 row1 = i;
624 row2 = i;
625 break;
626 }
627 if (pc > chip->pc_sf_lut->percent[i]) {
628 row1 = i - 1;
629 row2 = i;
630 break;
631 }
632 }
633
634 if (cycles < chip->pc_sf_lut->cycles[0])
635 cycles = chip->pc_sf_lut->cycles[0];
636 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
637 cycles = chip->pc_sf_lut->cycles[cols - 1];
638
639 for (i = 0; i < cols; i++)
640 if (cycles <= chip->pc_sf_lut->cycles[i])
641 break;
642 if (cycles == chip->pc_sf_lut->cycles[i]) {
643 scalefactor = linear_interpolate(
644 chip->pc_sf_lut->sf[row1][i],
645 chip->pc_sf_lut->percent[row1],
646 chip->pc_sf_lut->sf[row2][i],
647 chip->pc_sf_lut->percent[row2],
648 pc);
649 return scalefactor;
650 }
651
652 scalefactorrow1 = linear_interpolate(
653 chip->pc_sf_lut->sf[row1][i - 1],
654 chip->pc_sf_lut->cycles[i - 1],
655 chip->pc_sf_lut->sf[row1][i],
656 chip->pc_sf_lut->cycles[i],
657 cycles);
658
659 scalefactorrow2 = linear_interpolate(
660 chip->pc_sf_lut->sf[row2][i - 1],
661 chip->pc_sf_lut->cycles[i - 1],
662 chip->pc_sf_lut->sf[row2][i],
663 chip->pc_sf_lut->cycles[i],
664 cycles);
665
666 scalefactor = linear_interpolate(
667 scalefactorrow1,
668 chip->pc_sf_lut->percent[row1],
669 scalefactorrow2,
670 chip->pc_sf_lut->percent[row2],
671 pc);
672
673 return scalefactor;
674}
675
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700676static int is_between(int left, int right, int value)
677{
678 if (left >= right && left >= value && value >= right)
679 return 1;
680 if (left <= right && left <= value && value <= right)
681 return 1;
682
683 return 0;
684}
685
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686static int interpolate_pc(struct pm8921_bms_chip *chip,
687 int batt_temp, int ocv)
688{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700689 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690 int rows = chip->pc_temp_ocv_lut->rows;
691 int cols = chip->pc_temp_ocv_lut->cols;
692
693 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700694 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 batt_temp = chip->pc_temp_ocv_lut->temp[0];
696 }
697 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700698 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
700 }
701
702 for (j = 0; j < cols; j++)
703 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
704 break;
705 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
706 /* found an exact match for temp in the table */
707 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
708 return chip->pc_temp_ocv_lut->percent[0];
709 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
710 return chip->pc_temp_ocv_lut->percent[rows - 1];
711 for (i = 0; i < rows; i++) {
712 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
713 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
714 return
715 chip->pc_temp_ocv_lut->percent[i];
716 pc = linear_interpolate(
717 chip->pc_temp_ocv_lut->percent[i],
718 chip->pc_temp_ocv_lut->ocv[i][j],
719 chip->pc_temp_ocv_lut->percent[i - 1],
720 chip->pc_temp_ocv_lut->ocv[i - 1][j],
721 ocv);
722 return pc;
723 }
724 }
725 }
726
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700727 /*
728 * batt_temp is within temperature for
729 * column j-1 and j
730 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
732 return chip->pc_temp_ocv_lut->percent[0];
733 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
734 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700735
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700736 pcj_minus_one = 0;
737 pcj = 0;
738 for (i = 0; i < rows-1; i++) {
739 if (pcj == 0
740 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
741 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
742 pcj = linear_interpolate(
743 chip->pc_temp_ocv_lut->percent[i],
744 chip->pc_temp_ocv_lut->ocv[i][j],
745 chip->pc_temp_ocv_lut->percent[i + 1],
746 chip->pc_temp_ocv_lut->ocv[i+1][j],
747 ocv);
748 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700749
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700750 if (pcj_minus_one == 0
751 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
752 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700753
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700754 pcj_minus_one = linear_interpolate(
755 chip->pc_temp_ocv_lut->percent[i],
756 chip->pc_temp_ocv_lut->ocv[i][j-1],
757 chip->pc_temp_ocv_lut->percent[i + 1],
758 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
759 ocv);
760 }
761
762 if (pcj && pcj_minus_one) {
763 pc = linear_interpolate(
764 pcj_minus_one,
765 chip->pc_temp_ocv_lut->temp[j-1],
766 pcj,
767 chip->pc_temp_ocv_lut->temp[j],
768 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 return pc;
770 }
771 }
772
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700773 if (pcj)
774 return pcj;
775
776 if (pcj_minus_one)
777 return pcj_minus_one;
778
779 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
780 ocv, batt_temp);
781 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700782}
783
784static int calculate_rbatt(struct pm8921_bms_chip *chip)
785{
786 int rc;
787 unsigned int ocv, vsense, vbatt, r_batt;
788
789 rc = read_ocv_for_rbatt(chip, &ocv);
790 if (rc) {
791 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
792 ocv = 0;
793 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700794
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700795 rc = read_vbatt_for_rbatt(chip, &vbatt);
796 if (rc) {
797 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
798 ocv = 0;
799 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700800
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801 rc = read_vsense_for_rbatt(chip, &vsense);
802 if (rc) {
803 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
804 ocv = 0;
805 }
806 if (ocv == 0
807 || ocv == vbatt
808 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700809 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
810 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700811 ocv, vbatt, vsense);
812 return -EINVAL;
813 }
814 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700815 last_rbatt = r_batt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816 pr_debug("r_batt = %umilliOhms", r_batt);
817 return r_batt;
818}
819
820static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
821 int chargecycles)
822{
823 int initfcc, result, scalefactor = 0;
824
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700825 if (chip->adjusted_fcc_temp_lut == NULL) {
826 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700827
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700828 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700830 /* Multiply the initial FCC value by the scale factor. */
831 result = (initfcc * scalefactor) / 100;
832 pr_debug("fcc mAh = %d\n", result);
833 return result;
834 } else {
835 return interpolate_fcc_adjusted(chip, batt_temp);
836 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700837}
838
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700839static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
840{
841 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700842 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700843
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700844 rc = pm8xxx_adc_read(chip->vbat_channel, &result);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700845 if (rc) {
846 pr_err("error reading adc channel = %d, rc = %d\n",
847 chip->vbat_channel, rc);
848 return rc;
849 }
850 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
851 result.measurement);
852 *uvolts = (int)result.physical;
853 *uvolts = *uvolts * 1000;
854 return 0;
855}
856
857static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
858{
859 int vbatt, rbatt, ibatt, rc;
860
861 rc = get_battery_uvolts(chip, &vbatt);
862 if (rc) {
863 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700864 return rc;
865 }
866
867 rc = pm8921_bms_get_battery_current(&ibatt);
868 if (rc) {
869 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700870 return rc;
871 }
872
873 rbatt = calculate_rbatt(the_chip);
874 if (rbatt < 0)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700875 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700876 *ocv = vbatt + ibatt * rbatt;
877 return 0;
878}
879
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
881 int chargecycles)
882{
883 int pc, scalefactor;
884
885 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
886 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
887 pc, ocv_uv, batt_temp);
888
889 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
890 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
891
892 /* Multiply the initial FCC value by the scale factor. */
893 pc = (pc * scalefactor) / 100;
894 return pc;
895}
896
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700897static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700898 int *coulumb_counter)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700899{
900 int rc;
901 int64_t cc_voltage_uv, cc_uvh, cc_mah;
902
903 rc = read_cc(the_chip, coulumb_counter);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700904 cc_voltage_uv = (int64_t)*coulumb_counter;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700905 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800906 cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700907 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700908 cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
910 cc_mah = div_s64(cc_uvh, chip->r_sense);
911 *val = cc_mah;
912}
913
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700914static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
915 int fcc, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700917 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918
919 rbatt = calculate_rbatt(chip);
920 if (rbatt < 0) {
921 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700922 pr_debug("rbatt unavailable assuming %d\n", rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700923 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700924
925 /* calculate unusable charge */
926 voltage_unusable_uv = (rbatt * chip->i_test)
927 + (chip->v_failure * 1000);
928 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
929 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700930 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
931 rbatt, voltage_unusable_uv, pc_unusable);
932 return (fcc * pc_unusable) / 100;
933}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700934
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700935/* calculate remainging charge at the time of ocv */
936static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
937 int batt_temp, int chargecycles)
938{
939 int rc, ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940
941 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700942 ocv = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943 rc = read_last_good_ocv(chip, &ocv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700944 if (rc)
945 pr_debug("failed to read ocv rc = %d\n", rc);
946
947 if (ocv == 0) {
948 ocv = last_ocv_uv;
949 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700951
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700953 pr_debug("ocv = %d pc = %d\n", ocv, pc);
954 return (fcc * pc) / 100;
955}
956
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700957static void calculate_charging_params(struct pm8921_bms_chip *chip,
958 int batt_temp, int chargecycles,
959 int *fcc,
960 int *unusable_charge,
961 int *remaining_charge,
962 int64_t *cc_mah)
963{
964 int coulumb_counter;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700965 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700966
967 *fcc = calculate_fcc(chip, batt_temp, chargecycles);
968 pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
969 *fcc, batt_temp, chargecycles);
970
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700971 /* fcc doesnt need to be read from hardware, lock the bms now */
972 spin_lock_irqsave(&chip->bms_output_lock, flags);
973 pm_bms_lock_output_data(chip);
974
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700975 *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
976 batt_temp, chargecycles);
977
978 pr_debug("UUC = %umAh", *unusable_charge);
979
980 /* calculate remainging charge */
981 *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
982 batt_temp, chargecycles);
983 pr_debug("RC = %umAh\n", *remaining_charge);
984
985 /* calculate cc milli_volt_hour */
986 calculate_cc_mah(chip, cc_mah, &coulumb_counter);
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800987 pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700988
989 pm_bms_unlock_output_data(chip);
990 spin_unlock_irqrestore(&chip->bms_output_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700991}
992
993static int calculate_real_fcc(struct pm8921_bms_chip *chip,
994 int batt_temp, int chargecycles)
995{
996 int fcc, unusable_charge;
997 int remaining_charge;
998 int64_t cc_mah;
999 int real_fcc;
1000
1001 calculate_charging_params(chip, batt_temp, chargecycles,
1002 &fcc,
1003 &unusable_charge,
1004 &remaining_charge,
1005 &cc_mah);
1006
1007 real_fcc = remaining_charge - cc_mah;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001008 pr_debug("real_fcc = %d, RC = %d CC = %lld\n",
1009 real_fcc, remaining_charge, cc_mah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001010 return real_fcc;
1011}
1012/*
1013 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1014 * - coloumb counter charge
1015 * - unusable charge (due to battery resistance)
1016 * SOC% = (remaining usable charge/ fcc - usable_charge);
1017 */
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001018#define BMS_BATT_NOMINAL 3700000
1019#define MIN_OPERABLE_SOC 10
1020#define BATTERY_POWER_SUPPLY_SOC 53
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001021static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
1022 int batt_temp, int chargecycles)
1023{
1024 int remaining_usable_charge, fcc, unusable_charge;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001025 int remaining_charge, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001026 int update_userspace = 1;
1027 int64_t cc_mah;
1028
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001029 calculate_charging_params(chip, batt_temp, chargecycles,
1030 &fcc,
1031 &unusable_charge,
1032 &remaining_charge,
1033 &cc_mah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001034
1035 /* calculate remaining usable charge */
1036 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001037 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001038 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
1039 if (soc > 100)
1040 soc = 100;
1041 pr_debug("SOC = %u%%\n", soc);
1042
1043 if (soc < MIN_OPERABLE_SOC) {
1044 int ocv = 0, rc;
1045
1046 rc = adc_based_ocv(chip, &ocv);
1047 if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
1048 /*
1049 * The ocv doesnt seem to have dropped for
1050 * soc to go negative.
1051 * The setup must be using a power supply
1052 * instead of real batteries.
1053 * Fake high enough soc to prevent userspace
1054 * shutdown for low battery
1055 */
1056 soc = BATTERY_POWER_SUPPLY_SOC;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001057 pr_debug("Adjusting SOC to %d\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001058 }
1059 }
1060
1061 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001062 pr_err("bad rem_usb_chg = %d rem_chg %d,"
1063 "cc_mah %lld, unusb_chg %d\n",
1064 remaining_usable_charge, remaining_charge,
1065 cc_mah, unusable_charge);
1066 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001067 "chargecycles = %d, batt_temp = %d"
1068 "fcc = %d soc =%d\n",
1069 last_ocv_uv, chargecycles, batt_temp,
1070 fcc, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001071 update_userspace = 0;
1072 }
1073
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001074 if (last_soc == -EINVAL || soc <= last_soc) {
1075 last_soc = update_userspace ? soc : last_soc;
1076 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001077 }
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001078
1079 /*
1080 * soc > last_soc
1081 * the device must be charging for reporting a higher soc, if not ignore
1082 * this soc and continue reporting the last_soc
1083 */
1084 if (the_chip->start_percent != 0) {
1085 last_soc = soc;
1086 } else {
1087 pr_debug("soc = %d reporting last_soc = %d\n", soc, last_soc);
1088 soc = last_soc;
1089 }
1090
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001091 return soc;
1092}
1093
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001094#define XOADC_MAX_1P25V 1312500
1095#define XOADC_MIN_1P25V 1187500
1096#define XOADC_MAX_0P625V 656250
1097#define XOADC_MIN_0P625V 593750
1098
1099#define HKADC_V_PER_BIT_MUL_FACTOR 977
1100#define HKADC_V_PER_BIT_DIV_FACTOR 10
1101static int calib_hkadc_convert_microvolt(unsigned int phy)
1102{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001103 return (phy - 0x6000) *
1104 HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001105}
1106
1107static void calib_hkadc(struct pm8921_bms_chip *chip)
1108{
1109 int voltage, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001110 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001111
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001112 rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001113 if (rc) {
1114 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1115 return;
1116 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001117 voltage = calib_hkadc_convert_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001118
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001119 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1120 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001121
1122 /* check for valid range */
1123 if (voltage > XOADC_MAX_1P25V)
1124 voltage = XOADC_MAX_1P25V;
1125 else if (voltage < XOADC_MIN_1P25V)
1126 voltage = XOADC_MIN_1P25V;
1127 chip->xoadc_v125 = voltage;
1128
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001129 rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001130 if (rc) {
1131 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1132 return;
1133 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001134 voltage = calib_hkadc_convert_microvolt(result.adc_code);
1135 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
1136 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001137 /* check for valid range */
1138 if (voltage > XOADC_MAX_0P625V)
1139 voltage = XOADC_MAX_0P625V;
1140 else if (voltage < XOADC_MIN_0P625V)
1141 voltage = XOADC_MIN_0P625V;
1142
1143 chip->xoadc_v0625 = voltage;
1144}
1145
1146static void calibrate_hkadc_work(struct work_struct *work)
1147{
1148 struct pm8921_bms_chip *chip = container_of(work,
1149 struct pm8921_bms_chip, calib_hkadc_work);
1150
1151 calib_hkadc(chip);
1152}
1153
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001154static void calibrate_ccadc_work(struct work_struct *work)
1155{
1156 struct pm8921_bms_chip *chip = container_of(work,
1157 struct pm8921_bms_chip, calib_ccadc_work.work);
1158
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001159 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001160 schedule_delayed_work(&chip->calib_ccadc_work,
1161 round_jiffies_relative(msecs_to_jiffies
1162 (chip->calib_delay_ms)));
1163}
1164
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001165int pm8921_bms_get_vsense_avg(int *result)
1166{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001167 int rc = -EINVAL;
1168 unsigned long flags;
1169
1170 if (the_chip) {
1171 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1172 pm_bms_lock_output_data(the_chip);
1173 rc = read_vsense_avg(the_chip, result);
1174 pm_bms_unlock_output_data(the_chip);
1175 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1176 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001177
1178 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001179 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001180}
1181EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1182
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001183int pm8921_bms_get_battery_current(int *result)
1184{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001185 unsigned long flags;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001186 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001187
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001188 if (!the_chip) {
1189 pr_err("called before initialization\n");
1190 return -EINVAL;
1191 }
1192 if (the_chip->r_sense == 0) {
1193 pr_err("r_sense is zero\n");
1194 return -EINVAL;
1195 }
1196
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001197 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1198 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001199 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001200 pm_bms_unlock_output_data(the_chip);
1201 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001202 pr_debug("vsense=%d\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001203 /* cast for signed division */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001204 *result = vsense / (int)the_chip->r_sense;
1205
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001206 return 0;
1207}
1208EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1209
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001210int pm8921_bms_get_percent_charge(void)
1211{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001212 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001213 struct pm8xxx_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001214
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001215 if (!the_chip) {
1216 pr_err("called before initialization\n");
1217 return -EINVAL;
1218 }
1219
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001220 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001221 if (rc) {
1222 pr_err("error reading adc channel = %d, rc = %d\n",
1223 the_chip->batt_temp_channel, rc);
1224 return rc;
1225 }
1226 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1227 result.measurement);
1228 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001229 return calculate_state_of_charge(the_chip,
1230 batt_temp, last_chargecycles);
1231}
1232EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1233
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001234int pm8921_bms_get_fcc(void)
1235{
1236 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001237 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001238
1239 if (!the_chip) {
1240 pr_err("called before initialization\n");
1241 return -EINVAL;
1242 }
1243
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001244 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001245 if (rc) {
1246 pr_err("error reading adc channel = %d, rc = %d\n",
1247 the_chip->batt_temp_channel, rc);
1248 return rc;
1249 }
1250 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1251 result.measurement);
1252 batt_temp = (int)result.physical;
1253 return calculate_fcc(the_chip, batt_temp, last_chargecycles);
1254}
1255EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257void pm8921_bms_charging_began(void)
1258{
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001259 the_chip->start_percent = pm8921_bms_get_percent_charge();
1260 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001261}
1262EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1263
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001264void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001265{
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001266 if (is_battery_full && the_chip != NULL) {
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001267 unsigned long flags;
1268 int batt_temp, rc, cc_reading;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001269 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001270
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001271 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001272 if (rc) {
1273 pr_err("error reading adc channel = %d, rc = %d\n",
1274 the_chip->batt_temp_channel, rc);
1275 goto charge_cycle_calculation;
1276 }
1277 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1278 result.measurement);
1279 batt_temp = (int)result.physical;
1280 last_real_fcc = calculate_real_fcc(the_chip,
1281 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001282 last_real_fcc_batt_temp = batt_temp;
1283 readjust_fcc_table();
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001284
1285 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1286 pm_bms_lock_output_data(the_chip);
1287 pm_bms_read_output_data(the_chip, LAST_GOOD_OCV_VALUE,
1288 &the_chip->ocv_reading_at_100);
1289 read_cc(the_chip, &cc_reading);
1290 pm_bms_unlock_output_data(the_chip);
1291 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1292 the_chip->cc_reading_at_100 = cc_reading;
1293 pr_debug("EOC ocv_reading = 0x%x cc_reading = %d\n",
1294 the_chip->ocv_reading_at_100,
1295 the_chip->cc_reading_at_100);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001296 }
1297
1298charge_cycle_calculation:
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001299 the_chip->end_percent = pm8921_bms_get_percent_charge();
1300 if (the_chip->end_percent > the_chip->start_percent) {
1301 last_charge_increase =
1302 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001303 if (last_charge_increase > 100) {
1304 last_chargecycles++;
1305 last_charge_increase = last_charge_increase % 100;
1306 }
1307 }
1308 pr_debug("end_percent = %u%% last_charge_increase = %d"
1309 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001310 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001311 last_charge_increase,
1312 last_chargecycles);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001313 the_chip->start_percent = 0;
1314 the_chip->end_percent = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001315}
1316EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1317
1318static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1319{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001320 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001321 return IRQ_HANDLED;
1322}
1323
1324static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1325{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001326 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001327 return IRQ_HANDLED;
1328}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001329
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001330static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1331{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001332 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001333 return IRQ_HANDLED;
1334}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001335
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001336static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1337{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001338 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001339 return IRQ_HANDLED;
1340}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001341
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001342static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1343{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001344 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001345
1346 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001347 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001348 return IRQ_HANDLED;
1349}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001350
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001351static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1352{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001353 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001354
1355 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001356 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001357 return IRQ_HANDLED;
1358}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001360static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1361{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001362 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001363 return IRQ_HANDLED;
1364}
1365
1366struct pm_bms_irq_init_data {
1367 unsigned int irq_id;
1368 char *name;
1369 unsigned long flags;
1370 irqreturn_t (*handler)(int, void *);
1371};
1372
1373#define BMS_IRQ(_id, _flags, _handler) \
1374{ \
1375 .irq_id = _id, \
1376 .name = #_id, \
1377 .flags = _flags, \
1378 .handler = _handler, \
1379}
1380
1381struct pm_bms_irq_init_data bms_irq_data[] = {
1382 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1383 pm8921_bms_sbi_write_ok_handler),
1384 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1385 pm8921_bms_cc_thr_handler),
1386 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1387 pm8921_bms_vsense_thr_handler),
1388 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1389 pm8921_bms_vsense_for_r_handler),
1390 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1391 pm8921_bms_ocv_for_r_handler),
1392 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1393 pm8921_bms_good_ocv_handler),
1394 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1395 pm8921_bms_vsense_avg_handler),
1396};
1397
1398static void free_irqs(struct pm8921_bms_chip *chip)
1399{
1400 int i;
1401
1402 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1403 if (chip->pmic_bms_irq[i]) {
1404 free_irq(chip->pmic_bms_irq[i], NULL);
1405 chip->pmic_bms_irq[i] = 0;
1406 }
1407}
1408
1409static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1410 struct platform_device *pdev)
1411{
1412 struct resource *res;
1413 int ret, i;
1414
1415 ret = 0;
1416 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1417
1418 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1419 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1420 bms_irq_data[i].name);
1421 if (res == NULL) {
1422 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1423 goto err_out;
1424 }
1425 ret = request_irq(res->start, bms_irq_data[i].handler,
1426 bms_irq_data[i].flags,
1427 bms_irq_data[i].name, chip);
1428 if (ret < 0) {
1429 pr_err("couldn't request %d (%s) %d\n", res->start,
1430 bms_irq_data[i].name, ret);
1431 goto err_out;
1432 }
1433 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1434 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1435 }
1436 return 0;
1437
1438err_out:
1439 free_irqs(chip);
1440 return -EINVAL;
1441}
1442
1443#define EN_BMS_BIT BIT(7)
1444#define EN_PON_HS_BIT BIT(0)
1445static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1446{
1447 int rc;
1448
1449 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1450 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1451 if (rc) {
1452 pr_err("failed to enable pon and bms addr = %d %d",
1453 BMS_CONTROL, rc);
1454 }
1455
1456 return 0;
1457}
1458
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001459static void check_initial_ocv(struct pm8921_bms_chip *chip)
1460{
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001461 int ocv, rc;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001462
1463 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001464 * Check if a ocv is available in bms hw,
1465 * if not compute it here at boot time and save it
1466 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001467 */
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001468 ocv = 0;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001469 rc = read_last_good_ocv(chip, &ocv);
1470 if (rc || ocv == 0) {
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001471 rc = adc_based_ocv(chip, &ocv);
1472 if (rc) {
1473 pr_err("failed to read adc based ocv rc = %d\n", rc);
1474 ocv = DEFAULT_OCV_MICROVOLTS;
1475 }
1476 last_ocv_uv = ocv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001477 }
1478 pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
1479}
1480
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001481static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1482{
1483 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001484 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001485
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001486 rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001487 if (rc) {
1488 pr_err("error reading batt id channel = %d, rc = %d\n",
1489 chip->vbat_channel, rc);
1490 return rc;
1491 }
1492 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1493 result.measurement);
1494 return result.physical;
1495}
1496
1497#define PALLADIUM_ID_MIN 2500
1498#define PALLADIUM_ID_MAX 4000
1499static int set_battery_data(struct pm8921_bms_chip *chip)
1500{
1501 int64_t battery_id;
1502
1503 battery_id = read_battery_id(chip);
1504
1505 if (battery_id < 0) {
1506 pr_err("cannot read battery id err = %lld\n", battery_id);
1507 return battery_id;
1508 }
1509
1510 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
1511 chip->fcc = palladium_1500_data.fcc;
1512 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1513 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1514 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1515 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1516 return 0;
1517 } else {
1518 pr_warn("invalid battery id, palladium 1500 assumed\n");
1519 chip->fcc = palladium_1500_data.fcc;
1520 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1521 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1522 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1523 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1524 return 0;
1525 }
1526}
1527
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001528enum {
1529 CALC_RBATT,
1530 CALC_FCC,
1531 CALC_PC,
1532 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001533 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001534 CALIB_CCADC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001535};
1536
1537static int test_batt_temp = 5;
1538static int test_chargecycle = 150;
1539static int test_ocv = 3900000;
1540enum {
1541 TEST_BATT_TEMP,
1542 TEST_CHARGE_CYCLE,
1543 TEST_OCV,
1544};
1545static int get_test_param(void *data, u64 * val)
1546{
1547 switch ((int)data) {
1548 case TEST_BATT_TEMP:
1549 *val = test_batt_temp;
1550 break;
1551 case TEST_CHARGE_CYCLE:
1552 *val = test_chargecycle;
1553 break;
1554 case TEST_OCV:
1555 *val = test_ocv;
1556 break;
1557 default:
1558 return -EINVAL;
1559 }
1560 return 0;
1561}
1562static int set_test_param(void *data, u64 val)
1563{
1564 switch ((int)data) {
1565 case TEST_BATT_TEMP:
1566 test_batt_temp = (int)val;
1567 break;
1568 case TEST_CHARGE_CYCLE:
1569 test_chargecycle = (int)val;
1570 break;
1571 case TEST_OCV:
1572 test_ocv = (int)val;
1573 break;
1574 default:
1575 return -EINVAL;
1576 }
1577 return 0;
1578}
1579DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
1580
1581static int get_calc(void *data, u64 * val)
1582{
1583 int param = (int)data;
1584 int ret = 0;
1585
1586 *val = 0;
1587
1588 /* global irq number passed in via data */
1589 switch (param) {
1590 case CALC_RBATT:
1591 *val = calculate_rbatt(the_chip);
1592 break;
1593 case CALC_FCC:
1594 *val = calculate_fcc(the_chip, test_batt_temp,
1595 test_chargecycle);
1596 break;
1597 case CALC_PC:
1598 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
1599 test_chargecycle);
1600 break;
1601 case CALC_SOC:
1602 *val = calculate_state_of_charge(the_chip,
1603 test_batt_temp, test_chargecycle);
1604 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001605 case CALIB_HKADC:
1606 /* reading this will trigger calibration */
1607 *val = 0;
1608 calib_hkadc(the_chip);
1609 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001610 case CALIB_CCADC:
1611 /* reading this will trigger calibration */
1612 *val = 0;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001613 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001614 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001615 default:
1616 ret = -EINVAL;
1617 }
1618 return ret;
1619}
1620DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
1621
1622static int get_reading(void *data, u64 * val)
1623{
1624 int param = (int)data;
1625 int ret = 0;
1626
1627 *val = 0;
1628
1629 /* global irq number passed in via data */
1630 switch (param) {
1631 case CC_MSB:
1632 case CC_LSB:
1633 read_cc(the_chip, (int *)val);
1634 break;
1635 case LAST_GOOD_OCV_VALUE:
1636 read_last_good_ocv(the_chip, (uint *)val);
1637 break;
1638 case VBATT_FOR_RBATT:
1639 read_vbatt_for_rbatt(the_chip, (uint *)val);
1640 break;
1641 case VSENSE_FOR_RBATT:
1642 read_vsense_for_rbatt(the_chip, (uint *)val);
1643 break;
1644 case OCV_FOR_RBATT:
1645 read_ocv_for_rbatt(the_chip, (uint *)val);
1646 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001647 case VSENSE_AVG:
1648 read_vsense_avg(the_chip, (uint *)val);
1649 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001650 default:
1651 ret = -EINVAL;
1652 }
1653 return ret;
1654}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001655DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001656
1657static int get_rt_status(void *data, u64 * val)
1658{
1659 int i = (int)data;
1660 int ret;
1661
1662 /* global irq number passed in via data */
1663 ret = pm_bms_get_rt_status(the_chip, i);
1664 *val = ret;
1665 return 0;
1666}
1667DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
1668
1669static int get_reg(void *data, u64 * val)
1670{
1671 int addr = (int)data;
1672 int ret;
1673 u8 temp;
1674
1675 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
1676 if (ret) {
1677 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
1678 addr, temp, ret);
1679 return -EAGAIN;
1680 }
1681 *val = temp;
1682 return 0;
1683}
1684
1685static int set_reg(void *data, u64 val)
1686{
1687 int addr = (int)data;
1688 int ret;
1689 u8 temp;
1690
1691 temp = (u8) val;
1692 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
1693 if (ret) {
1694 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
1695 addr, temp, ret);
1696 return -EAGAIN;
1697 }
1698 return 0;
1699}
1700DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
1701
1702static void create_debugfs_entries(struct pm8921_bms_chip *chip)
1703{
1704 int i;
1705
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001706 chip->dent = debugfs_create_dir("pm8921-bms", NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001707
1708 if (IS_ERR(chip->dent)) {
1709 pr_err("pmic bms couldnt create debugfs dir\n");
1710 return;
1711 }
1712
1713 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
1714 (void *)BMS_CONTROL, &reg_fops);
1715 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
1716 (void *)BMS_OUTPUT0, &reg_fops);
1717 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
1718 (void *)BMS_OUTPUT1, &reg_fops);
1719 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
1720 (void *)BMS_TEST1, &reg_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001721
1722 debugfs_create_file("test_batt_temp", 0644, chip->dent,
1723 (void *)TEST_BATT_TEMP, &temp_fops);
1724 debugfs_create_file("test_chargecycle", 0644, chip->dent,
1725 (void *)TEST_CHARGE_CYCLE, &temp_fops);
1726 debugfs_create_file("test_ocv", 0644, chip->dent,
1727 (void *)TEST_OCV, &temp_fops);
1728
1729 debugfs_create_file("read_cc", 0644, chip->dent,
1730 (void *)CC_MSB, &reading_fops);
1731 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
1732 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
1733 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
1734 (void *)VBATT_FOR_RBATT, &reading_fops);
1735 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
1736 (void *)VSENSE_FOR_RBATT, &reading_fops);
1737 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
1738 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001739 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
1740 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001741
1742 debugfs_create_file("show_rbatt", 0644, chip->dent,
1743 (void *)CALC_RBATT, &calc_fops);
1744 debugfs_create_file("show_fcc", 0644, chip->dent,
1745 (void *)CALC_FCC, &calc_fops);
1746 debugfs_create_file("show_pc", 0644, chip->dent,
1747 (void *)CALC_PC, &calc_fops);
1748 debugfs_create_file("show_soc", 0644, chip->dent,
1749 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001750 debugfs_create_file("calib_hkadc", 0644, chip->dent,
1751 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001752 debugfs_create_file("calib_ccadc", 0644, chip->dent,
1753 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001754
1755 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1756 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
1757 debugfs_create_file(bms_irq_data[i].name, 0444,
1758 chip->dent,
1759 (void *)bms_irq_data[i].irq_id,
1760 &rt_fops);
1761 }
1762}
1763
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001764static int __devinit pm8921_bms_probe(struct platform_device *pdev)
1765{
1766 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001767 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001768 struct pm8921_bms_chip *chip;
1769 const struct pm8921_bms_platform_data *pdata
1770 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001771
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001772 if (!pdata) {
1773 pr_err("missing platform data\n");
1774 return -EINVAL;
1775 }
1776
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001777 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001778 if (!chip) {
1779 pr_err("Cannot allocate pm_bms_chip\n");
1780 return -ENOMEM;
1781 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001782 spin_lock_init(&chip->bms_output_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001783 chip->dev = &pdev->dev;
1784 chip->r_sense = pdata->r_sense;
1785 chip->i_test = pdata->i_test;
1786 chip->v_failure = pdata->v_failure;
1787 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001788 rc = set_battery_data(chip);
1789 if (rc) {
1790 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07001791 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001792 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001793
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001794 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
1795 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001796 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
1797 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001798 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07001799 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001800 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001801
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001802 rc = request_irqs(chip, pdev);
1803 if (rc) {
1804 pr_err("couldn't register interrupts rc = %d\n", rc);
1805 goto free_chip;
1806 }
1807
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001808 rc = pm8921_bms_hw_init(chip);
1809 if (rc) {
1810 pr_err("couldn't init hardware rc = %d\n", rc);
1811 goto free_irqs;
1812 }
1813
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001814 platform_set_drvdata(pdev, chip);
1815 the_chip = chip;
1816 create_debugfs_entries(chip);
1817
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001818 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001819
1820 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
1821 /* begin calibration only on chips > 2.0 */
1822 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
1823 calibrate_ccadc_work(&(chip->calib_ccadc_work.work));
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001824
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001825 /* initial hkadc calibration */
1826 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001827 /* enable the vbatt reading interrupts for scheduling hkadc calib */
1828 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
1829 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001830
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001831 get_battery_uvolts(chip, &vbatt);
1832 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
1833 pm8921_bms_get_percent_charge(),
1834 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001835 return 0;
1836
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001837free_irqs:
1838 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001839free_chip:
1840 kfree(chip);
1841 return rc;
1842}
1843
1844static int __devexit pm8921_bms_remove(struct platform_device *pdev)
1845{
1846 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
1847
1848 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001849 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001850 platform_set_drvdata(pdev, NULL);
1851 the_chip = NULL;
1852 kfree(chip);
1853 return 0;
1854}
1855
1856static struct platform_driver pm8921_bms_driver = {
1857 .probe = pm8921_bms_probe,
1858 .remove = __devexit_p(pm8921_bms_remove),
1859 .driver = {
1860 .name = PM8921_BMS_DEV_NAME,
1861 .owner = THIS_MODULE,
1862 },
1863};
1864
1865static int __init pm8921_bms_init(void)
1866{
1867 return platform_driver_register(&pm8921_bms_driver);
1868}
1869
1870static void __exit pm8921_bms_exit(void)
1871{
1872 platform_driver_unregister(&pm8921_bms_driver);
1873}
1874
1875late_initcall(pm8921_bms_init);
1876module_exit(pm8921_bms_exit);
1877
1878MODULE_LICENSE("GPL v2");
1879MODULE_DESCRIPTION("PMIC8921 bms driver");
1880MODULE_VERSION("1.0");
1881MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);