blob: 751c6fcc9d48abd06348747f99f3dfc484d4bad1 [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,
60 PM_BMS_MAX_INTS,
61};
62
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063struct pm8921_bms_chip {
64 struct device *dev;
65 struct dentry *dent;
66 unsigned int r_sense;
67 unsigned int i_test;
68 unsigned int v_failure;
69 unsigned int fcc;
70 struct single_row_lut *fcc_temp_lut;
71 struct single_row_lut *fcc_sf_lut;
72 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
73 struct pc_sf_lut *pc_sf_lut;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070074 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070075 struct delayed_work calib_ccadc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076 unsigned int calib_delay_ms;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070077 int ccadc_gain_uv;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -070078 unsigned int revision;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070079 unsigned int xoadc_v0625;
80 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -070081 unsigned int batt_temp_channel;
82 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070083 unsigned int ref625mv_channel;
84 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -070085 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
87 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -070088 spinlock_t bms_output_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -070089 struct single_row_lut *adjusted_fcc_temp_lut;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090};
91
92static struct pm8921_bms_chip *the_chip;
93
94#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096#define DEFAULT_CHARGE_CYCLES 0
97
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -070098static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
99static int last_charge_increase;
100module_param(last_chargecycles, int, 0644);
101module_param(last_charge_increase, int, 0644);
102
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105static int last_soc = -EINVAL;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700106static int last_real_fcc = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700107static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700109static int bms_ops_set(const char *val, const struct kernel_param *kp)
110{
111 if (*(int *)kp->arg == -EINVAL)
112 return param_set_int(val, kp);
113 else
114 return 0;
115}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700117static struct kernel_param_ops bms_param_ops = {
118 .set = bms_ops_set,
119 .get = param_get_int,
120};
121
122module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
123module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
124module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
125
126static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
127static void readjust_fcc_table(void)
128{
129 struct single_row_lut *temp, *old;
130 int i, fcc, ratio;
131
132 if (!the_chip->fcc_temp_lut) {
133 pr_err("The static fcc lut table is NULL\n");
134 return;
135 }
136
137 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
138 if (!temp) {
139 pr_err("Cannot allocate memory for adjusted fcc table\n");
140 return;
141 }
142
143 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
144
145 temp->cols = the_chip->fcc_temp_lut->cols;
146 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
147 temp->x[i] = the_chip->fcc_temp_lut->x[i];
148 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
149 temp->y[i] = (ratio * last_real_fcc);
150 temp->y[i] /= 1000;
151 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
152 temp->x[i], the_chip->fcc_temp_lut->y[i],
153 temp->y[i], ratio);
154 }
155
156 old = the_chip->adjusted_fcc_temp_lut;
157 the_chip->adjusted_fcc_temp_lut = temp;
158 kfree(old);
159}
160
161static int bms_last_real_fcc_set(const char *val,
162 const struct kernel_param *kp)
163{
164 int rc = 0;
165
166 if (last_real_fcc == -EINVAL)
167 rc = param_set_int(val, kp);
168 if (rc) {
169 pr_err("Failed to set last_real_fcc rc=%d\n", rc);
170 return rc;
171 }
172 if (last_real_fcc_batt_temp != -EINVAL)
173 readjust_fcc_table();
174 return rc;
175}
176static struct kernel_param_ops bms_last_real_fcc_param_ops = {
177 .set = bms_last_real_fcc_set,
178 .get = param_get_int,
179};
180module_param_cb(last_real_fcc, &bms_last_real_fcc_param_ops,
181 &last_real_fcc, 0644);
182
183static int bms_last_real_fcc_batt_temp_set(const char *val,
184 const struct kernel_param *kp)
185{
186 int rc = 0;
187
188 if (last_real_fcc_batt_temp == -EINVAL)
189 rc = param_set_int(val, kp);
190 if (rc) {
191 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
192 return rc;
193 }
194 if (last_real_fcc != -EINVAL)
195 readjust_fcc_table();
196 return rc;
197}
198
199static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
200 .set = bms_last_real_fcc_batt_temp_set,
201 .get = param_get_int,
202};
203module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
204 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205
206static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
207{
208 return pm8xxx_read_irq_stat(chip->dev->parent,
209 chip->pmic_bms_irq[irq_id]);
210}
211
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700212static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
213{
214 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
215 dev_dbg(chip->dev, "%s %d\n", __func__,
216 chip->pmic_bms_irq[interrupt]);
217 enable_irq(chip->pmic_bms_irq[interrupt]);
218 }
219}
220
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
222{
223 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700224 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
226 }
227}
228
229static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
230 u8 mask, u8 val)
231{
232 int rc;
233 u8 reg;
234
235 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
236 if (rc) {
237 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
238 return rc;
239 }
240 reg &= ~mask;
241 reg |= val & mask;
242 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
243 if (rc) {
244 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
245 return rc;
246 }
247 return 0;
248}
249
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700250#define HOLD_OREG_DATA BIT(1)
251static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
252{
253 int rc;
254
255 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
256 HOLD_OREG_DATA);
257 if (rc) {
258 pr_err("couldnt lock bms output rc = %d\n", rc);
259 return rc;
260 }
261 return 0;
262}
263
264static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
265{
266 int rc;
267
268 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
269 if (rc) {
270 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
271 return rc;
272 }
273 return 0;
274}
275
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276#define SELECT_OUTPUT_DATA 0x1C
277#define SELECT_OUTPUT_TYPE_SHIFT 2
278#define OCV_FOR_RBATT 0x0
279#define VSENSE_FOR_RBATT 0x1
280#define VBATT_FOR_RBATT 0x2
281#define CC_MSB 0x3
282#define CC_LSB 0x4
283#define LAST_GOOD_OCV_VALUE 0x5
284#define VSENSE_AVG 0x6
285#define VBATT_AVG 0x7
286
287static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
288 int16_t *result)
289{
290 int rc;
291 u8 reg;
292
293 if (!result) {
294 pr_err("result pointer null\n");
295 return -EINVAL;
296 }
297 *result = 0;
298 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
299 pr_err("invalid type %d asked to read\n", type);
300 return -EINVAL;
301 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700302
303 /* make sure the bms registers are locked */
304 rc = pm8xxx_readb(chip->dev->parent, BMS_CONTROL, &reg);
305 if (rc) {
306 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
307 type, rc);
308 return rc;
309 }
310
311 /* Output register data must be held (locked) while reading output */
312 WARN_ON(!(reg && HOLD_OREG_DATA));
313
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
315 type << SELECT_OUTPUT_TYPE_SHIFT);
316 if (rc) {
317 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
318 type, rc);
319 return rc;
320 }
321
322 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
323 if (rc) {
324 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
325 type, rc);
326 return rc;
327 }
328 *result = reg;
329 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
330 if (rc) {
331 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
332 type, rc);
333 return rc;
334 }
335 *result |= reg << 8;
336 pr_debug("type %d result %x", type, *result);
337 return 0;
338}
339
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700340#define V_PER_BIT_MUL_FACTOR 97656
341#define V_PER_BIT_DIV_FACTOR 1000
342#define XOADC_INTRINSIC_OFFSET 0x6000
343static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700344{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700345 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700346 return 0;
347
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700348 return (a - XOADC_INTRINSIC_OFFSET)
349 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700350}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700352#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700353#define VBATT_MUL_FACTOR 3
354static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
355 unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700356{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700357 u64 numerator, denominator;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700358
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700359 if (uv == 0)
360 return 0;
361
362 numerator = ((u64)uv - chip->xoadc_v0625) * XOADC_CALIB_UV;
363 denominator = chip->xoadc_v125 - chip->xoadc_v0625;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700364 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700365 return uv * VBATT_MUL_FACTOR;
366 return (XOADC_CALIB_UV + div_u64(numerator, denominator))
367 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700368}
369
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700370#define CC_RESOLUTION_N_V1 1085069
371#define CC_RESOLUTION_D_V1 100000
372#define CC_RESOLUTION_N_V2 868056
373#define CC_RESOLUTION_D_V2 10000
374static s64 cc_to_microvolt_v1(s64 cc)
375{
376 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
377}
378
379static s64 cc_to_microvolt_v2(s64 cc)
380{
381 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
382}
383
384static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
385{
386 /*
387 * resolution (the value of a single bit) was changed after revision 2.0
388 * for more accurate readings
389 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700390 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700391 cc_to_microvolt_v1((s64)cc) :
392 cc_to_microvolt_v2((s64)cc);
393}
394
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700395#define CCADC_READING_RESOLUTION_N_V1 1085069
396#define CCADC_READING_RESOLUTION_D_V1 100000
397#define CCADC_READING_RESOLUTION_N_V2 542535
398#define CCADC_READING_RESOLUTION_D_V2 100000
399static s64 ccadc_reading_to_microvolt_v1(s64 cc)
400{
401 return div_s64(cc * CCADC_READING_RESOLUTION_N_V1,
402 CCADC_READING_RESOLUTION_D_V1);
403}
404
405static s64 ccadc_reading_to_microvolt_v2(s64 cc)
406{
407 return div_s64(cc * CCADC_READING_RESOLUTION_N_V2,
408 CCADC_READING_RESOLUTION_D_V2);
409}
410
411static s64 ccadc_reading_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
412{
413 /*
414 * resolution (the value of a single bit) was changed after revision 2.0
415 * for more accurate readings
416 */
417 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
418 ccadc_reading_to_microvolt_v1((s64)cc) :
419 ccadc_reading_to_microvolt_v2((s64)cc);
420}
421
422static s64 microvolt_to_ccadc_reading_v1(s64 uv)
423{
424 return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
425 CCADC_READING_RESOLUTION_N_V1);
426}
427
428static s64 microvolt_to_ccadc_reading_v2(s64 uv)
429{
430 return div_s64(uv * CCADC_READING_RESOLUTION_D_V2,
431 CCADC_READING_RESOLUTION_N_V2);
432}
433
434static s64 microvolt_to_ccadc_reading(struct pm8921_bms_chip *chip, s64 cc)
435{
436 /*
437 * resolution (the value of a single bit) was changed after revision 2.0
438 * for more accurate readings
439 */
440 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
441 microvolt_to_ccadc_reading_v1((s64)cc) :
442 microvolt_to_ccadc_reading_v2((s64)cc);
443}
444
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700445#define CC_READING_TICKS 55
446#define SLEEP_CLK_HZ 32768
447#define SECONDS_PER_HOUR 3600
448static s64 ccmicrovolt_to_uvh(s64 cc_uv)
449{
450 return div_s64(cc_uv * CC_READING_TICKS,
451 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
452}
453
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700454#define GAIN_REFERENCE_UV 25000
455/*
456 * gain compensation for ccadc readings - common for vsense based and
457 * couloumb counter based readings
458 */
459static s64 cc_adjust_for_gain(struct pm8921_bms_chip *chip, s64 cc)
460{
461 if (chip->ccadc_gain_uv == 0)
462 return cc;
463
464 return div_s64(cc * GAIN_REFERENCE_UV, chip->ccadc_gain_uv);
465}
466
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467/* returns the signed value read from the hardware */
468static int read_cc(struct pm8921_bms_chip *chip, int *result)
469{
470 int rc;
471 uint16_t msw, lsw;
472
473 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
474 if (rc) {
475 pr_err("fail to read CC_LSB rc = %d\n", rc);
476 return rc;
477 }
478 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
479 if (rc) {
480 pr_err("fail to read CC_MSB rc = %d\n", rc);
481 return rc;
482 }
483 *result = msw << 16 | lsw;
484 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
485 return 0;
486}
487
488static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
489{
490 int rc;
491 uint16_t reading;
492
493 rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
494 if (rc) {
495 pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
496 return rc;
497 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700498 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700499 pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
500 *result = adjust_xo_vbatt_reading(chip, *result);
501 pr_debug("after adj ocv_uV = %u\n", *result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700502 if (*result != 0)
503 last_ocv_uv = *result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700504 return 0;
505}
506
507static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
508{
509 int rc;
510 uint16_t reading;
511
512 rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
513 if (rc) {
514 pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
515 return rc;
516 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700517 *result = xoadc_reading_to_microvolt(reading);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700519 *result = adjust_xo_vbatt_reading(chip, *result);
520 pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 return 0;
522}
523
524static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
525{
526 int rc;
527 uint16_t reading;
528
529 rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
530 if (rc) {
531 pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
532 return rc;
533 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700534 *result = ccadc_reading_to_microvolt(chip, reading);
535 pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
536 *result = cc_adjust_for_gain(chip, *result);
537 pr_debug("after adj vsense_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 return 0;
539}
540
541static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
542{
543 int rc;
544 uint16_t reading;
545
546 rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
547 if (rc) {
548 pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
549 return rc;
550 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700551 *result = xoadc_reading_to_microvolt(reading);
552 pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
553 *result = adjust_xo_vbatt_reading(chip, *result);
554 pr_debug("after adj ocv_for_r_uV = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 return 0;
556}
557
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700558static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
559{
560 int rc;
561 int16_t reading;
562
563 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
564 if (rc) {
565 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
566 return rc;
567 }
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700568 *result = ccadc_reading_to_microvolt(chip, reading);
569 pr_debug("raw = %04x vsense = %d\n", reading, *result);
570 *result = cc_adjust_for_gain(the_chip, (s64)*result);
571 pr_debug("after adj vsense = %d\n", *result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700572 return 0;
573}
574
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
576{
577 if (y0 == y1 || x == x0)
578 return y0;
579 if (x1 == x0 || x == x1)
580 return y1;
581
582 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
583}
584
585static int interpolate_single_lut(struct single_row_lut *lut, int x)
586{
587 int i, result;
588
589 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700590 pr_debug("x %d less than known range return y = %d lut = %pS\n",
591 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592 return lut->y[0];
593 }
594 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700595 pr_debug("x %d more than known range return y = %d lut = %pS\n",
596 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597 return lut->y[lut->cols - 1];
598 }
599
600 for (i = 0; i < lut->cols; i++)
601 if (x <= lut->x[i])
602 break;
603 if (x == lut->x[i]) {
604 result = lut->y[i];
605 } else {
606 result = linear_interpolate(
607 lut->y[i - 1],
608 lut->x[i - 1],
609 lut->y[i],
610 lut->x[i],
611 x);
612 }
613 return result;
614}
615
616static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
617{
618 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
619}
620
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700621static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
622{
623 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
624}
625
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
627 int cycles)
628{
629 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
630}
631
632static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
633 int cycles, int pc)
634{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700635 int i, scalefactorrow1, scalefactorrow2, scalefactor;
636 int row1 = 0;
637 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638 int rows = chip->pc_sf_lut->rows;
639 int cols = chip->pc_sf_lut->cols;
640
641 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700642 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643 row1 = 0;
644 row2 = 0;
645 }
646 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700647 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648 row1 = rows - 1;
649 row2 = rows - 1;
650 }
651 for (i = 0; i < rows; i++) {
652 if (pc == chip->pc_sf_lut->percent[i]) {
653 row1 = i;
654 row2 = i;
655 break;
656 }
657 if (pc > chip->pc_sf_lut->percent[i]) {
658 row1 = i - 1;
659 row2 = i;
660 break;
661 }
662 }
663
664 if (cycles < chip->pc_sf_lut->cycles[0])
665 cycles = chip->pc_sf_lut->cycles[0];
666 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
667 cycles = chip->pc_sf_lut->cycles[cols - 1];
668
669 for (i = 0; i < cols; i++)
670 if (cycles <= chip->pc_sf_lut->cycles[i])
671 break;
672 if (cycles == chip->pc_sf_lut->cycles[i]) {
673 scalefactor = linear_interpolate(
674 chip->pc_sf_lut->sf[row1][i],
675 chip->pc_sf_lut->percent[row1],
676 chip->pc_sf_lut->sf[row2][i],
677 chip->pc_sf_lut->percent[row2],
678 pc);
679 return scalefactor;
680 }
681
682 scalefactorrow1 = linear_interpolate(
683 chip->pc_sf_lut->sf[row1][i - 1],
684 chip->pc_sf_lut->cycles[i - 1],
685 chip->pc_sf_lut->sf[row1][i],
686 chip->pc_sf_lut->cycles[i],
687 cycles);
688
689 scalefactorrow2 = linear_interpolate(
690 chip->pc_sf_lut->sf[row2][i - 1],
691 chip->pc_sf_lut->cycles[i - 1],
692 chip->pc_sf_lut->sf[row2][i],
693 chip->pc_sf_lut->cycles[i],
694 cycles);
695
696 scalefactor = linear_interpolate(
697 scalefactorrow1,
698 chip->pc_sf_lut->percent[row1],
699 scalefactorrow2,
700 chip->pc_sf_lut->percent[row2],
701 pc);
702
703 return scalefactor;
704}
705
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700706static int is_between(int left, int right, int value)
707{
708 if (left >= right && left >= value && value >= right)
709 return 1;
710 if (left <= right && left <= value && value <= right)
711 return 1;
712
713 return 0;
714}
715
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716static int interpolate_pc(struct pm8921_bms_chip *chip,
717 int batt_temp, int ocv)
718{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700719 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700720 int rows = chip->pc_temp_ocv_lut->rows;
721 int cols = chip->pc_temp_ocv_lut->cols;
722
723 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700724 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725 batt_temp = chip->pc_temp_ocv_lut->temp[0];
726 }
727 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700728 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
730 }
731
732 for (j = 0; j < cols; j++)
733 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
734 break;
735 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
736 /* found an exact match for temp in the table */
737 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
738 return chip->pc_temp_ocv_lut->percent[0];
739 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
740 return chip->pc_temp_ocv_lut->percent[rows - 1];
741 for (i = 0; i < rows; i++) {
742 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
743 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
744 return
745 chip->pc_temp_ocv_lut->percent[i];
746 pc = linear_interpolate(
747 chip->pc_temp_ocv_lut->percent[i],
748 chip->pc_temp_ocv_lut->ocv[i][j],
749 chip->pc_temp_ocv_lut->percent[i - 1],
750 chip->pc_temp_ocv_lut->ocv[i - 1][j],
751 ocv);
752 return pc;
753 }
754 }
755 }
756
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700757 /*
758 * batt_temp is within temperature for
759 * column j-1 and j
760 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
762 return chip->pc_temp_ocv_lut->percent[0];
763 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
764 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700766 pcj_minus_one = 0;
767 pcj = 0;
768 for (i = 0; i < rows-1; i++) {
769 if (pcj == 0
770 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
771 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
772 pcj = linear_interpolate(
773 chip->pc_temp_ocv_lut->percent[i],
774 chip->pc_temp_ocv_lut->ocv[i][j],
775 chip->pc_temp_ocv_lut->percent[i + 1],
776 chip->pc_temp_ocv_lut->ocv[i+1][j],
777 ocv);
778 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700780 if (pcj_minus_one == 0
781 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
782 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700783
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700784 pcj_minus_one = linear_interpolate(
785 chip->pc_temp_ocv_lut->percent[i],
786 chip->pc_temp_ocv_lut->ocv[i][j-1],
787 chip->pc_temp_ocv_lut->percent[i + 1],
788 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
789 ocv);
790 }
791
792 if (pcj && pcj_minus_one) {
793 pc = linear_interpolate(
794 pcj_minus_one,
795 chip->pc_temp_ocv_lut->temp[j-1],
796 pcj,
797 chip->pc_temp_ocv_lut->temp[j],
798 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700799 return pc;
800 }
801 }
802
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700803 if (pcj)
804 return pcj;
805
806 if (pcj_minus_one)
807 return pcj_minus_one;
808
809 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
810 ocv, batt_temp);
811 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812}
813
814static int calculate_rbatt(struct pm8921_bms_chip *chip)
815{
816 int rc;
817 unsigned int ocv, vsense, vbatt, r_batt;
818
819 rc = read_ocv_for_rbatt(chip, &ocv);
820 if (rc) {
821 pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
822 ocv = 0;
823 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700824
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700825 rc = read_vbatt_for_rbatt(chip, &vbatt);
826 if (rc) {
827 pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
828 ocv = 0;
829 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700830
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700831 rc = read_vsense_for_rbatt(chip, &vsense);
832 if (rc) {
833 pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
834 ocv = 0;
835 }
836 if (ocv == 0
837 || ocv == vbatt
838 || vsense == 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700839 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
840 "vsen = %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700841 ocv, vbatt, vsense);
842 return -EINVAL;
843 }
844 r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700845 last_rbatt = r_batt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700846 pr_debug("r_batt = %umilliOhms", r_batt);
847 return r_batt;
848}
849
850static int calculate_fcc(struct pm8921_bms_chip *chip, int batt_temp,
851 int chargecycles)
852{
853 int initfcc, result, scalefactor = 0;
854
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700855 if (chip->adjusted_fcc_temp_lut == NULL) {
856 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700858 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700860 /* Multiply the initial FCC value by the scale factor. */
861 result = (initfcc * scalefactor) / 100;
862 pr_debug("fcc mAh = %d\n", result);
863 return result;
864 } else {
865 return interpolate_fcc_adjusted(chip, batt_temp);
866 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867}
868
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700869static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
870{
871 int rc;
872 struct pm8921_adc_chan_result result;
873
874 rc = pm8921_adc_read(chip->vbat_channel, &result);
875 if (rc) {
876 pr_err("error reading adc channel = %d, rc = %d\n",
877 chip->vbat_channel, rc);
878 return rc;
879 }
880 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
881 result.measurement);
882 *uvolts = (int)result.physical;
883 *uvolts = *uvolts * 1000;
884 return 0;
885}
886
887static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
888{
889 int vbatt, rbatt, ibatt, rc;
890
891 rc = get_battery_uvolts(chip, &vbatt);
892 if (rc) {
893 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700894 return rc;
895 }
896
897 rc = pm8921_bms_get_battery_current(&ibatt);
898 if (rc) {
899 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700900 return rc;
901 }
902
903 rbatt = calculate_rbatt(the_chip);
904 if (rbatt < 0)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700905 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700906 *ocv = vbatt + ibatt * rbatt;
907 return 0;
908}
909
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700910static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
911 int chargecycles)
912{
913 int pc, scalefactor;
914
915 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
916 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
917 pc, ocv_uv, batt_temp);
918
919 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
920 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
921
922 /* Multiply the initial FCC value by the scale factor. */
923 pc = (pc * scalefactor) / 100;
924 return pc;
925}
926
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700927static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700928 int *coulumb_counter)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929{
930 int rc;
931 int64_t cc_voltage_uv, cc_uvh, cc_mah;
932
933 rc = read_cc(the_chip, coulumb_counter);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934 cc_voltage_uv = (int64_t)*coulumb_counter;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700935 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700936 cc_voltage_uv = cc_adjust_for_gain(chip, cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700937 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700938 cc_uvh = ccmicrovolt_to_uvh(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 pr_debug("cc_uvh = %lld micro_volt_hour\n", cc_uvh);
940 cc_mah = div_s64(cc_uvh, chip->r_sense);
941 *val = cc_mah;
942}
943
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700944static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
945 int fcc, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700946{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700947 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700948
949 rbatt = calculate_rbatt(chip);
950 if (rbatt < 0) {
951 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700952 pr_debug("rbatt unavailable assuming %d\n", rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954
955 /* calculate unusable charge */
956 voltage_unusable_uv = (rbatt * chip->i_test)
957 + (chip->v_failure * 1000);
958 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
959 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700960 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
961 rbatt, voltage_unusable_uv, pc_unusable);
962 return (fcc * pc_unusable) / 100;
963}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -0700964
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700965/* calculate remainging charge at the time of ocv */
966static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
967 int batt_temp, int chargecycles)
968{
969 int rc, ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700970
971 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700972 ocv = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973 rc = read_last_good_ocv(chip, &ocv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -0700974 if (rc)
975 pr_debug("failed to read ocv rc = %d\n", rc);
976
977 if (ocv == 0) {
978 ocv = last_ocv_uv;
979 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700981
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -0700983 pr_debug("ocv = %d pc = %d\n", ocv, pc);
984 return (fcc * pc) / 100;
985}
986
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700987static void calculate_charging_params(struct pm8921_bms_chip *chip,
988 int batt_temp, int chargecycles,
989 int *fcc,
990 int *unusable_charge,
991 int *remaining_charge,
992 int64_t *cc_mah)
993{
994 int coulumb_counter;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700995 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -0700996
997 *fcc = calculate_fcc(chip, batt_temp, chargecycles);
998 pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
999 *fcc, batt_temp, chargecycles);
1000
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001001 /* fcc doesnt need to be read from hardware, lock the bms now */
1002 spin_lock_irqsave(&chip->bms_output_lock, flags);
1003 pm_bms_lock_output_data(chip);
1004
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001005 *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
1006 batt_temp, chargecycles);
1007
1008 pr_debug("UUC = %umAh", *unusable_charge);
1009
1010 /* calculate remainging charge */
1011 *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
1012 batt_temp, chargecycles);
1013 pr_debug("RC = %umAh\n", *remaining_charge);
1014
1015 /* calculate cc milli_volt_hour */
1016 calculate_cc_mah(chip, cc_mah, &coulumb_counter);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001017
1018 pm_bms_unlock_output_data(chip);
1019 spin_unlock_irqrestore(&chip->bms_output_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001020 pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
1021}
1022
1023static int calculate_real_fcc(struct pm8921_bms_chip *chip,
1024 int batt_temp, int chargecycles)
1025{
1026 int fcc, unusable_charge;
1027 int remaining_charge;
1028 int64_t cc_mah;
1029 int real_fcc;
1030
1031 calculate_charging_params(chip, batt_temp, chargecycles,
1032 &fcc,
1033 &unusable_charge,
1034 &remaining_charge,
1035 &cc_mah);
1036
1037 real_fcc = remaining_charge - cc_mah;
1038
1039 return real_fcc;
1040}
1041/*
1042 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1043 * - coloumb counter charge
1044 * - unusable charge (due to battery resistance)
1045 * SOC% = (remaining usable charge/ fcc - usable_charge);
1046 */
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001047#define BMS_BATT_NOMINAL 3700000
1048#define MIN_OPERABLE_SOC 10
1049#define BATTERY_POWER_SUPPLY_SOC 53
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001050static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
1051 int batt_temp, int chargecycles)
1052{
1053 int remaining_usable_charge, fcc, unusable_charge;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001054 int remaining_charge, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001055 int update_userspace = 1;
1056 int64_t cc_mah;
1057
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001058 calculate_charging_params(chip, batt_temp, chargecycles,
1059 &fcc,
1060 &unusable_charge,
1061 &remaining_charge,
1062 &cc_mah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001063
1064 /* calculate remaining usable charge */
1065 remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001066 pr_debug("RUC = %dmAh\n", remaining_usable_charge);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001067 soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
1068 if (soc > 100)
1069 soc = 100;
1070 pr_debug("SOC = %u%%\n", soc);
1071
1072 if (soc < MIN_OPERABLE_SOC) {
1073 int ocv = 0, rc;
1074
1075 rc = adc_based_ocv(chip, &ocv);
1076 if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
1077 /*
1078 * The ocv doesnt seem to have dropped for
1079 * soc to go negative.
1080 * The setup must be using a power supply
1081 * instead of real batteries.
1082 * Fake high enough soc to prevent userspace
1083 * shutdown for low battery
1084 */
1085 soc = BATTERY_POWER_SUPPLY_SOC;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001086 pr_debug("Adjusting SOC to %d\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001087 }
1088 }
1089
1090 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001091 pr_err("bad rem_usb_chg = %d rem_chg %d,"
1092 "cc_mah %lld, unusb_chg %d\n",
1093 remaining_usable_charge, remaining_charge,
1094 cc_mah, unusable_charge);
1095 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001096 "chargecycles = %d, batt_temp = %d"
1097 "fcc = %d soc =%d\n",
1098 last_ocv_uv, chargecycles, batt_temp,
1099 fcc, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001100 update_userspace = 0;
1101 }
1102
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001103 if (update_userspace) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001104 last_soc = soc;
1105 }
1106 return soc;
1107}
1108
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001109#define XOADC_MAX_1P25V 1312500
1110#define XOADC_MIN_1P25V 1187500
1111#define XOADC_MAX_0P625V 656250
1112#define XOADC_MIN_0P625V 593750
1113
1114#define HKADC_V_PER_BIT_MUL_FACTOR 977
1115#define HKADC_V_PER_BIT_DIV_FACTOR 10
1116static int calib_hkadc_convert_microvolt(unsigned int phy)
1117{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001118 return (phy - 0x6000) *
1119 HKADC_V_PER_BIT_MUL_FACTOR / HKADC_V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001120}
1121
1122static void calib_hkadc(struct pm8921_bms_chip *chip)
1123{
1124 int voltage, rc;
1125 struct pm8921_adc_chan_result result;
1126
1127 rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
1128 if (rc) {
1129 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1130 return;
1131 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001132 voltage = calib_hkadc_convert_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001133
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001134 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1135 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001136
1137 /* check for valid range */
1138 if (voltage > XOADC_MAX_1P25V)
1139 voltage = XOADC_MAX_1P25V;
1140 else if (voltage < XOADC_MIN_1P25V)
1141 voltage = XOADC_MIN_1P25V;
1142 chip->xoadc_v125 = voltage;
1143
1144 rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
1145 if (rc) {
1146 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1147 return;
1148 }
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001149 voltage = calib_hkadc_convert_microvolt(result.adc_code);
1150 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
1151 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001152 /* check for valid range */
1153 if (voltage > XOADC_MAX_0P625V)
1154 voltage = XOADC_MAX_0P625V;
1155 else if (voltage < XOADC_MIN_0P625V)
1156 voltage = XOADC_MIN_0P625V;
1157
1158 chip->xoadc_v0625 = voltage;
1159}
1160
1161static void calibrate_hkadc_work(struct work_struct *work)
1162{
1163 struct pm8921_bms_chip *chip = container_of(work,
1164 struct pm8921_bms_chip, calib_hkadc_work);
1165
1166 calib_hkadc(chip);
1167}
1168
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001169#define START_CONV_BIT BIT(7)
1170#define EOC_CONV_BIT BIT(6)
1171#define SEL_CCADC_BIT BIT(1)
1172#define EN_ARB_BIT BIT(0)
1173
1174#define CCADC_CALIB_DIG_PARAM 0xE3
1175#define CCADC_CALIB_RSV_GND 0x40
1176#define CCADC_CALIB_RSV_25MV 0x80
1177#define CCADC_CALIB_ANA_PARAM 0x1B
1178#define SAMPLE_COUNT 16
1179#define ADC_WAIT_COUNT 10
1180
1181#define CCADC_MAX_25MV 30000
1182#define CCADC_MIN_25MV 20000
1183#define CCADC_MAX_0UV -4000
1184#define CCADC_MIN_0UV -7000
1185
1186#define CCADC_INTRINSIC_OFFSET 0xC000
1187
1188#define REG_SBI_CONFIG 0x04F
1189#define PAGE3_ENABLE_MASK 0x6
1190
1191static int calib_ccadc_enable_trim_access(struct pm8921_bms_chip *chip,
1192 u8 *sbi_config)
1193{
1194 u8 reg;
1195 int rc;
1196
1197 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1198 if (rc) {
1199 pr_err("error = %d reading sbi config reg\n", rc);
1200 return rc;
1201 }
1202
1203 reg = *sbi_config | PAGE3_ENABLE_MASK;
1204 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
1205}
1206
1207static int calib_ccadc_restore_trim_access(struct pm8921_bms_chip *chip,
1208 u8 sbi_config)
1209{
1210 return pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
1211}
1212
1213static int calib_ccadc_enable_arbiter(struct pm8921_bms_chip *chip)
1214{
1215 int rc;
1216
1217 /* enable Arbiter, must be sent twice */
1218 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1219 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1220 if (rc < 0) {
1221 pr_err("error = %d enabling arbiter for offset\n", rc);
1222 return rc;
1223 }
1224 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1225 SEL_CCADC_BIT | EN_ARB_BIT, SEL_CCADC_BIT | EN_ARB_BIT);
1226 if (rc < 0) {
1227 pr_err("error = %d writing ADC_ARB_SECP_CNTRL\n", rc);
1228 return rc;
1229 }
1230 return 0;
1231}
1232
1233static int calib_start_conv(struct pm8921_bms_chip *chip,
1234 u16 *result)
1235{
1236 int rc, i;
1237 u8 data_msb, data_lsb, reg;
1238
1239 /* Start conversion */
1240 rc = pm_bms_masked_write(chip, ADC_ARB_SECP_CNTRL,
1241 START_CONV_BIT, START_CONV_BIT);
1242 if (rc < 0) {
1243 pr_err("error = %d starting offset meas\n", rc);
1244 return rc;
1245 }
1246
1247 /* Wait for End of conversion */
1248 for (i = 0; i < ADC_WAIT_COUNT; i++) {
1249 rc = pm8xxx_readb(chip->dev->parent,
1250 ADC_ARB_SECP_CNTRL, &reg);
1251 if (rc < 0) {
1252 pr_err("error = %d read eoc for offset\n", rc);
1253 return rc;
1254 }
1255 if ((reg & (START_CONV_BIT | EOC_CONV_BIT)) != EOC_CONV_BIT)
1256 msleep(60);
1257 else
1258 break;
1259 }
1260 if (i == ADC_WAIT_COUNT) {
1261 pr_err("waited too long for offset eoc\n");
1262 return rc;
1263 }
1264
1265 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA0, &data_lsb);
1266 if (rc < 0) {
1267 pr_err("error = %d reading offset lsb\n", rc);
1268 return rc;
1269 }
1270
1271 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_DATA1, &data_msb);
1272 if (rc < 0) {
1273 pr_err("error = %d reading offset msb\n", rc);
1274 return rc;
1275 }
1276
1277 *result = (data_msb << 8) | data_lsb;
1278 return 0;
1279}
1280
1281static int calib_ccadc_read_trim(struct pm8921_bms_chip *chip,
1282 int addr, u8 *data_msb, u8 *data_lsb)
1283{
1284 int rc;
1285 u8 sbi_config;
1286
1287 calib_ccadc_enable_trim_access(chip, &sbi_config);
1288 rc = pm8xxx_readb(chip->dev->parent, addr, data_msb);
1289 if (rc < 0) {
1290 pr_err("error = %d read msb\n", rc);
1291 return rc;
1292 }
1293 rc = pm8xxx_readb(chip->dev->parent, addr + 1, data_lsb);
1294 if (rc < 0) {
1295 pr_err("error = %d read lsb\n", rc);
1296 return rc;
1297 }
1298 calib_ccadc_restore_trim_access(chip, sbi_config);
1299 return 0;
1300}
1301
1302static int calib_ccadc_read_gain_uv(struct pm8921_bms_chip *chip)
1303{
1304 s8 data_msb;
1305 u8 data_lsb;
1306 int rc, gain, offset;
1307
1308 rc = calib_ccadc_read_trim(chip, CCADC_FULLSCALE_TRIM1,
1309 &data_msb, &data_lsb);
1310 gain = (data_msb << 8) | data_lsb;
1311
1312 rc = calib_ccadc_read_trim(chip, CCADC_OFFSET_TRIM1,
1313 &data_msb, &data_lsb);
1314 offset = (data_msb << 8) | data_lsb;
1315
1316 pr_debug("raw gain trim = 0x%x offset trim =0x%x\n", gain, offset);
1317 gain = ccadc_reading_to_microvolt(chip, (s64)gain - offset);
1318 return gain;
1319}
1320
1321#define CCADC_PROGRAM_TRIM_COUNT 10
1322#define ADC_ARB_BMS_CNTRL_CCADC_SHIFT 4
1323#define ADC_ARB_BMS_CNTRL_CONV_MASK 0x03
1324#define BMS_CONV_IN_PROGRESS 0x2
1325
1326static int calib_ccadc_program_trim(struct pm8921_bms_chip *chip,
1327 int addr, u8 data_msb, u8 data_lsb)
1328{
1329 int rc, i;
1330 u8 cntrl, sbi_config;
1331 bool in_progress;
1332
1333 calib_ccadc_enable_trim_access(chip, &sbi_config);
1334
1335 for (i = 0; i < CCADC_PROGRAM_TRIM_COUNT; i++) {
1336 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_BMS_CNTRL, &cntrl);
1337 if (rc < 0) {
1338 pr_err("error = %d reading ADC_ARB_BMS_CNTRL\n", rc);
1339 return rc;
1340 }
1341
1342 /* break if a ccadc conversion is not happening */
1343 in_progress = (((cntrl >> ADC_ARB_BMS_CNTRL_CCADC_SHIFT)
1344 & ADC_ARB_BMS_CNTRL_CONV_MASK) == BMS_CONV_IN_PROGRESS);
1345
1346 if (!in_progress)
1347 break;
1348 }
1349
1350 if (in_progress) {
1351 pr_err("conv in progress cannot write trim,returing EBUSY\n");
1352 return -EBUSY;
1353 }
1354
1355 rc = pm8xxx_writeb(chip->dev->parent, addr, data_msb);
1356 if (rc < 0) {
1357 pr_err("error = %d write msb = 0x%x\n", rc, data_msb);
1358 return rc;
1359 }
1360 rc = pm8xxx_writeb(chip->dev->parent, addr + 1, data_lsb);
1361 if (rc < 0) {
1362 pr_err("error = %d write lsb = 0x%x\n", rc, data_lsb);
1363 return rc;
1364 }
1365 calib_ccadc_restore_trim_access(chip, sbi_config);
1366 return 0;
1367}
1368
1369static void calib_ccadc(struct pm8921_bms_chip *chip)
1370{
1371 u8 data_msb, data_lsb, sec_cntrl;
1372 int result_offset, voltage_offset, result_gain;
1373 u16 result;
1374 int i, rc;
1375
1376 rc = pm8xxx_readb(chip->dev->parent, ADC_ARB_SECP_CNTRL, &sec_cntrl);
1377 if (rc < 0) {
1378 pr_err("error = %d reading ADC_ARB_SECP_CNTRL\n", rc);
1379 return;
1380 }
1381
1382 rc = calib_ccadc_enable_arbiter(chip);
1383 if (rc < 0) {
1384 pr_err("error = %d enabling arbiter for offset\n", rc);
1385 goto bail;
1386 }
1387
1388 /*
1389 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1390 * up, pending verification through bench
1391 */
1392 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1393 CCADC_CALIB_DIG_PARAM);
1394 if (rc < 0) {
1395 pr_err("error = %d writing ADC_ARB_SECP_DIG_PARAM\n", rc);
1396 goto bail;
1397 }
1398
1399 result_offset = 0;
1400 for (i = 0; i < SAMPLE_COUNT; i++) {
1401 /* Short analog inputs to CCADC internally to ground */
1402 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_RSV,
1403 CCADC_CALIB_RSV_GND);
1404 if (rc < 0) {
1405 pr_err("error = %d selecting gnd voltage\n", rc);
1406 goto bail;
1407 }
1408
1409 /* Enable CCADC */
1410 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1411 CCADC_CALIB_ANA_PARAM);
1412 if (rc < 0) {
1413 pr_err("error = %d enabling ccadc\n", rc);
1414 goto bail;
1415 }
1416
1417 rc = calib_start_conv(chip, &result);
1418 if (rc < 0) {
1419 pr_err("error = %d for zero volt measurement\n", rc);
1420 goto bail;
1421 }
1422
1423 result_offset += result;
1424 }
1425
1426 result_offset = result_offset / SAMPLE_COUNT;
1427
1428 voltage_offset = ccadc_reading_to_microvolt(chip,
1429 ((s64)result_offset - CCADC_INTRINSIC_OFFSET));
1430
1431 pr_err("offset result_offset = 0x%x, voltage = %d microVolts\n",
1432 result_offset, voltage_offset);
1433
1434 /* Sanity Check */
1435 if (voltage_offset > CCADC_MAX_0UV) {
1436 pr_err("offset voltage = %d is huge limiting to %d\n",
1437 voltage_offset, CCADC_MAX_0UV);
1438 result_offset = CCADC_INTRINSIC_OFFSET
1439 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MAX_0UV);
1440 } else if (voltage_offset < CCADC_MIN_0UV) {
1441 pr_err("offset voltage = %d is too low limiting to %d\n",
1442 voltage_offset, CCADC_MIN_0UV);
1443 result_offset = CCADC_INTRINSIC_OFFSET
1444 + microvolt_to_ccadc_reading(chip, (s64)CCADC_MIN_0UV);
1445 }
1446
1447 data_msb = result_offset >> 8;
1448 data_lsb = result_offset;
1449
1450 rc = calib_ccadc_program_trim(chip, CCADC_OFFSET_TRIM1,
1451 data_msb, data_lsb);
1452 if (rc) {
1453 pr_err("error = %d programming offset trim\n", rc);
1454 goto bail;
1455 }
1456
1457 rc = calib_ccadc_enable_arbiter(chip);
1458 if (rc < 0) {
1459 pr_err("error = %d enabling arbiter for gain\n", rc);
1460 goto bail;
1461 }
1462
1463 /*
1464 * Set decimation ratio to 4k, lower ratio may be used in order to speed
1465 * up, pending verification through bench
1466 */
1467 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_DIG_PARAM,
1468 CCADC_CALIB_DIG_PARAM);
1469 if (rc < 0) {
1470 pr_err("error = %d enabling decimation ration for gain\n", rc);
1471 goto bail;
1472 }
1473
1474 result_gain = 0;
1475 for (i = 0; i < SAMPLE_COUNT; i++) {
1476 rc = pm8xxx_writeb(chip->dev->parent,
1477 ADC_ARB_SECP_RSV, CCADC_CALIB_RSV_25MV);
1478 if (rc < 0) {
1479 pr_err("error = %d selecting 25mV for gain\n", rc);
1480 goto bail;
1481 }
1482
1483 /* Enable CCADC */
1484 rc = pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_ANA_PARAM,
1485 CCADC_CALIB_ANA_PARAM);
1486 if (rc < 0) {
1487 pr_err("error = %d enabling ccadc\n", rc);
1488 goto bail;
1489 }
1490
1491 rc = calib_start_conv(chip, &result);
1492 if (rc < 0) {
1493 pr_err("error = %d for adc reading 25mV\n", rc);
1494 goto bail;
1495 }
1496
1497 result_gain += result;
1498 }
1499 result_gain = result_gain / SAMPLE_COUNT;
1500
1501 /*
1502 * result_offset includes INTRINSIC OFFSET
1503 * chip->ccadc_gain_uv will be the actual voltage
1504 * measured for 25000UV
1505 */
1506 chip->ccadc_gain_uv = ccadc_reading_to_microvolt(chip,
1507 ((s64)result_gain - result_offset));
1508
1509 pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
1510 result_gain,
1511 chip->ccadc_gain_uv);
1512 /* Sanity Check */
1513 if (chip->ccadc_gain_uv > CCADC_MAX_25MV) {
1514 pr_err("gain voltage = %d is huge limiting to %d\n",
1515 chip->ccadc_gain_uv, CCADC_MAX_25MV);
1516 chip->ccadc_gain_uv = CCADC_MAX_25MV;
1517 result_gain = result_offset +
1518 microvolt_to_ccadc_reading(chip, CCADC_MAX_25MV);
1519 } else if (chip->ccadc_gain_uv < CCADC_MIN_25MV) {
1520 pr_err("gain voltage = %d is too low limiting to %d\n",
1521 chip->ccadc_gain_uv, CCADC_MIN_25MV);
1522 chip->ccadc_gain_uv = CCADC_MIN_25MV;
1523 result_gain = result_offset +
1524 microvolt_to_ccadc_reading(chip, CCADC_MIN_25MV);
1525 }
1526
1527 data_msb = result_gain >> 8;
1528 data_lsb = result_gain;
1529 rc = calib_ccadc_program_trim(chip, CCADC_FULLSCALE_TRIM1,
1530 data_msb, data_lsb);
1531 if (rc)
1532 pr_err("error = %d programming gain trim\n", rc);
1533bail:
1534 pm8xxx_writeb(chip->dev->parent, ADC_ARB_SECP_CNTRL, sec_cntrl);
1535}
1536
1537static void calibrate_ccadc_work(struct work_struct *work)
1538{
1539 struct pm8921_bms_chip *chip = container_of(work,
1540 struct pm8921_bms_chip, calib_ccadc_work.work);
1541
1542 calib_ccadc(chip);
1543 schedule_delayed_work(&chip->calib_ccadc_work,
1544 round_jiffies_relative(msecs_to_jiffies
1545 (chip->calib_delay_ms)));
1546}
1547
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001548int pm8921_bms_get_vsense_avg(int *result)
1549{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001550 int rc = -EINVAL;
1551 unsigned long flags;
1552
1553 if (the_chip) {
1554 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1555 pm_bms_lock_output_data(the_chip);
1556 rc = read_vsense_avg(the_chip, result);
1557 pm_bms_unlock_output_data(the_chip);
1558 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
1559 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001560
1561 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001562 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001563}
1564EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1565
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001566int pm8921_bms_get_battery_current(int *result)
1567{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001568 unsigned long flags;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001569 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001570
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001571 if (!the_chip) {
1572 pr_err("called before initialization\n");
1573 return -EINVAL;
1574 }
1575 if (the_chip->r_sense == 0) {
1576 pr_err("r_sense is zero\n");
1577 return -EINVAL;
1578 }
1579
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001580 spin_lock_irqsave(&the_chip->bms_output_lock, flags);
1581 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001582 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001583 pm_bms_unlock_output_data(the_chip);
1584 spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001585 pr_debug("vsense=%d\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001586 /* cast for signed division */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001587 *result = vsense / (int)the_chip->r_sense;
1588
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001589 return 0;
1590}
1591EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1592
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001593int pm8921_bms_get_percent_charge(void)
1594{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001595 int batt_temp, rc;
1596 struct pm8921_adc_chan_result result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001597
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001598 if (!the_chip) {
1599 pr_err("called before initialization\n");
1600 return -EINVAL;
1601 }
1602
1603 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1604 if (rc) {
1605 pr_err("error reading adc channel = %d, rc = %d\n",
1606 the_chip->batt_temp_channel, rc);
1607 return rc;
1608 }
1609 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1610 result.measurement);
1611 batt_temp = (int)result.physical;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001612 return calculate_state_of_charge(the_chip,
1613 batt_temp, last_chargecycles);
1614}
1615EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1616
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001617int pm8921_bms_get_fcc(void)
1618{
1619 int batt_temp, rc;
1620 struct pm8921_adc_chan_result result;
1621
1622 if (!the_chip) {
1623 pr_err("called before initialization\n");
1624 return -EINVAL;
1625 }
1626
1627 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1628 if (rc) {
1629 pr_err("error reading adc channel = %d, rc = %d\n",
1630 the_chip->batt_temp_channel, rc);
1631 return rc;
1632 }
1633 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1634 result.measurement);
1635 batt_temp = (int)result.physical;
1636 return calculate_fcc(the_chip, batt_temp, last_chargecycles);
1637}
1638EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1639
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001640static int start_percent;
1641static int end_percent;
1642void pm8921_bms_charging_began(void)
1643{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001644 start_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001645 pr_debug("start_percent = %u%%\n", start_percent);
1646}
1647EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1648
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001649void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001650{
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001651 if (is_battery_full && the_chip != NULL) {
1652 int batt_temp, rc;
1653 struct pm8921_adc_chan_result result;
1654
1655 rc = pm8921_adc_read(the_chip->batt_temp_channel, &result);
1656 if (rc) {
1657 pr_err("error reading adc channel = %d, rc = %d\n",
1658 the_chip->batt_temp_channel, rc);
1659 goto charge_cycle_calculation;
1660 }
1661 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1662 result.measurement);
1663 batt_temp = (int)result.physical;
1664 last_real_fcc = calculate_real_fcc(the_chip,
1665 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001666 last_real_fcc_batt_temp = batt_temp;
1667 readjust_fcc_table();
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001668 }
1669
1670charge_cycle_calculation:
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001671 end_percent = pm8921_bms_get_percent_charge();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001672 if (end_percent > start_percent) {
1673 last_charge_increase = end_percent - start_percent;
1674 if (last_charge_increase > 100) {
1675 last_chargecycles++;
1676 last_charge_increase = last_charge_increase % 100;
1677 }
1678 }
1679 pr_debug("end_percent = %u%% last_charge_increase = %d"
1680 "last_chargecycles = %d\n",
1681 end_percent,
1682 last_charge_increase,
1683 last_chargecycles);
1684}
1685EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1686
1687static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1688{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001689 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001690 return IRQ_HANDLED;
1691}
1692
1693static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1694{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001695 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001696 return IRQ_HANDLED;
1697}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001698
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001699static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1700{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001701 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001702 return IRQ_HANDLED;
1703}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001704
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001705static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1706{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001707 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001708 return IRQ_HANDLED;
1709}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001710
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001711static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1712{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001713 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001714
1715 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001716 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001717 return IRQ_HANDLED;
1718}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001719
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001720static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1721{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001722 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001723
1724 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001725 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001726 return IRQ_HANDLED;
1727}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001728
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001729static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1730{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001731 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001732 return IRQ_HANDLED;
1733}
1734
1735struct pm_bms_irq_init_data {
1736 unsigned int irq_id;
1737 char *name;
1738 unsigned long flags;
1739 irqreturn_t (*handler)(int, void *);
1740};
1741
1742#define BMS_IRQ(_id, _flags, _handler) \
1743{ \
1744 .irq_id = _id, \
1745 .name = #_id, \
1746 .flags = _flags, \
1747 .handler = _handler, \
1748}
1749
1750struct pm_bms_irq_init_data bms_irq_data[] = {
1751 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1752 pm8921_bms_sbi_write_ok_handler),
1753 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1754 pm8921_bms_cc_thr_handler),
1755 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1756 pm8921_bms_vsense_thr_handler),
1757 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1758 pm8921_bms_vsense_for_r_handler),
1759 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1760 pm8921_bms_ocv_for_r_handler),
1761 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1762 pm8921_bms_good_ocv_handler),
1763 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1764 pm8921_bms_vsense_avg_handler),
1765};
1766
1767static void free_irqs(struct pm8921_bms_chip *chip)
1768{
1769 int i;
1770
1771 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1772 if (chip->pmic_bms_irq[i]) {
1773 free_irq(chip->pmic_bms_irq[i], NULL);
1774 chip->pmic_bms_irq[i] = 0;
1775 }
1776}
1777
1778static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1779 struct platform_device *pdev)
1780{
1781 struct resource *res;
1782 int ret, i;
1783
1784 ret = 0;
1785 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1786
1787 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1788 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1789 bms_irq_data[i].name);
1790 if (res == NULL) {
1791 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1792 goto err_out;
1793 }
1794 ret = request_irq(res->start, bms_irq_data[i].handler,
1795 bms_irq_data[i].flags,
1796 bms_irq_data[i].name, chip);
1797 if (ret < 0) {
1798 pr_err("couldn't request %d (%s) %d\n", res->start,
1799 bms_irq_data[i].name, ret);
1800 goto err_out;
1801 }
1802 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1803 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1804 }
1805 return 0;
1806
1807err_out:
1808 free_irqs(chip);
1809 return -EINVAL;
1810}
1811
1812#define EN_BMS_BIT BIT(7)
1813#define EN_PON_HS_BIT BIT(0)
1814static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1815{
1816 int rc;
1817
1818 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1819 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1820 if (rc) {
1821 pr_err("failed to enable pon and bms addr = %d %d",
1822 BMS_CONTROL, rc);
1823 }
1824
1825 return 0;
1826}
1827
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001828static void check_initial_ocv(struct pm8921_bms_chip *chip)
1829{
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001830 int ocv, rc;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001831
1832 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001833 * Check if a ocv is available in bms hw,
1834 * if not compute it here at boot time and save it
1835 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001836 */
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001837 ocv = 0;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001838 rc = read_last_good_ocv(chip, &ocv);
1839 if (rc || ocv == 0) {
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001840 rc = adc_based_ocv(chip, &ocv);
1841 if (rc) {
1842 pr_err("failed to read adc based ocv rc = %d\n", rc);
1843 ocv = DEFAULT_OCV_MICROVOLTS;
1844 }
1845 last_ocv_uv = ocv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001846 }
1847 pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
1848}
1849
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001850static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1851{
1852 int rc;
1853 struct pm8921_adc_chan_result result;
1854
1855 rc = pm8921_adc_read(chip->batt_id_channel, &result);
1856 if (rc) {
1857 pr_err("error reading batt id channel = %d, rc = %d\n",
1858 chip->vbat_channel, rc);
1859 return rc;
1860 }
1861 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1862 result.measurement);
1863 return result.physical;
1864}
1865
1866#define PALLADIUM_ID_MIN 2500
1867#define PALLADIUM_ID_MAX 4000
1868static int set_battery_data(struct pm8921_bms_chip *chip)
1869{
1870 int64_t battery_id;
1871
1872 battery_id = read_battery_id(chip);
1873
1874 if (battery_id < 0) {
1875 pr_err("cannot read battery id err = %lld\n", battery_id);
1876 return battery_id;
1877 }
1878
1879 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
1880 chip->fcc = palladium_1500_data.fcc;
1881 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1882 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1883 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1884 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1885 return 0;
1886 } else {
1887 pr_warn("invalid battery id, palladium 1500 assumed\n");
1888 chip->fcc = palladium_1500_data.fcc;
1889 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1890 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1891 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1892 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1893 return 0;
1894 }
1895}
1896
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001897enum {
1898 CALC_RBATT,
1899 CALC_FCC,
1900 CALC_PC,
1901 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001902 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001903 CALIB_CCADC,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001904};
1905
1906static int test_batt_temp = 5;
1907static int test_chargecycle = 150;
1908static int test_ocv = 3900000;
1909enum {
1910 TEST_BATT_TEMP,
1911 TEST_CHARGE_CYCLE,
1912 TEST_OCV,
1913};
1914static int get_test_param(void *data, u64 * val)
1915{
1916 switch ((int)data) {
1917 case TEST_BATT_TEMP:
1918 *val = test_batt_temp;
1919 break;
1920 case TEST_CHARGE_CYCLE:
1921 *val = test_chargecycle;
1922 break;
1923 case TEST_OCV:
1924 *val = test_ocv;
1925 break;
1926 default:
1927 return -EINVAL;
1928 }
1929 return 0;
1930}
1931static int set_test_param(void *data, u64 val)
1932{
1933 switch ((int)data) {
1934 case TEST_BATT_TEMP:
1935 test_batt_temp = (int)val;
1936 break;
1937 case TEST_CHARGE_CYCLE:
1938 test_chargecycle = (int)val;
1939 break;
1940 case TEST_OCV:
1941 test_ocv = (int)val;
1942 break;
1943 default:
1944 return -EINVAL;
1945 }
1946 return 0;
1947}
1948DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
1949
1950static int get_calc(void *data, u64 * val)
1951{
1952 int param = (int)data;
1953 int ret = 0;
1954
1955 *val = 0;
1956
1957 /* global irq number passed in via data */
1958 switch (param) {
1959 case CALC_RBATT:
1960 *val = calculate_rbatt(the_chip);
1961 break;
1962 case CALC_FCC:
1963 *val = calculate_fcc(the_chip, test_batt_temp,
1964 test_chargecycle);
1965 break;
1966 case CALC_PC:
1967 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
1968 test_chargecycle);
1969 break;
1970 case CALC_SOC:
1971 *val = calculate_state_of_charge(the_chip,
1972 test_batt_temp, test_chargecycle);
1973 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001974 case CALIB_HKADC:
1975 /* reading this will trigger calibration */
1976 *val = 0;
1977 calib_hkadc(the_chip);
1978 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001979 case CALIB_CCADC:
1980 /* reading this will trigger calibration */
1981 *val = 0;
1982 calib_ccadc(the_chip);
1983 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001984 default:
1985 ret = -EINVAL;
1986 }
1987 return ret;
1988}
1989DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
1990
1991static int get_reading(void *data, u64 * val)
1992{
1993 int param = (int)data;
1994 int ret = 0;
1995
1996 *val = 0;
1997
1998 /* global irq number passed in via data */
1999 switch (param) {
2000 case CC_MSB:
2001 case CC_LSB:
2002 read_cc(the_chip, (int *)val);
2003 break;
2004 case LAST_GOOD_OCV_VALUE:
2005 read_last_good_ocv(the_chip, (uint *)val);
2006 break;
2007 case VBATT_FOR_RBATT:
2008 read_vbatt_for_rbatt(the_chip, (uint *)val);
2009 break;
2010 case VSENSE_FOR_RBATT:
2011 read_vsense_for_rbatt(the_chip, (uint *)val);
2012 break;
2013 case OCV_FOR_RBATT:
2014 read_ocv_for_rbatt(the_chip, (uint *)val);
2015 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002016 case VSENSE_AVG:
2017 read_vsense_avg(the_chip, (uint *)val);
2018 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002019 default:
2020 ret = -EINVAL;
2021 }
2022 return ret;
2023}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002024DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002025
2026static int get_rt_status(void *data, u64 * val)
2027{
2028 int i = (int)data;
2029 int ret;
2030
2031 /* global irq number passed in via data */
2032 ret = pm_bms_get_rt_status(the_chip, i);
2033 *val = ret;
2034 return 0;
2035}
2036DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2037
2038static int get_reg(void *data, u64 * val)
2039{
2040 int addr = (int)data;
2041 int ret;
2042 u8 temp;
2043
2044 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2045 if (ret) {
2046 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2047 addr, temp, ret);
2048 return -EAGAIN;
2049 }
2050 *val = temp;
2051 return 0;
2052}
2053
2054static int set_reg(void *data, u64 val)
2055{
2056 int addr = (int)data;
2057 int ret;
2058 u8 temp;
2059
2060 temp = (u8) val;
2061 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2062 if (ret) {
2063 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2064 addr, temp, ret);
2065 return -EAGAIN;
2066 }
2067 return 0;
2068}
2069DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2070
2071static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2072{
2073 int i;
2074
2075 chip->dent = debugfs_create_dir("pm8921_bms", NULL);
2076
2077 if (IS_ERR(chip->dent)) {
2078 pr_err("pmic bms couldnt create debugfs dir\n");
2079 return;
2080 }
2081
2082 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2083 (void *)BMS_CONTROL, &reg_fops);
2084 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2085 (void *)BMS_OUTPUT0, &reg_fops);
2086 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2087 (void *)BMS_OUTPUT1, &reg_fops);
2088 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2089 (void *)BMS_TEST1, &reg_fops);
2090 debugfs_create_file("CCADC_ANA_PARAM", 0644, chip->dent,
2091 (void *)CCADC_ANA_PARAM, &reg_fops);
2092 debugfs_create_file("CCADC_DIG_PARAM", 0644, chip->dent,
2093 (void *)CCADC_DIG_PARAM, &reg_fops);
2094 debugfs_create_file("CCADC_RSV", 0644, chip->dent,
2095 (void *)CCADC_RSV, &reg_fops);
2096 debugfs_create_file("CCADC_DATA0", 0644, chip->dent,
2097 (void *)CCADC_DATA0, &reg_fops);
2098 debugfs_create_file("CCADC_DATA1", 0644, chip->dent,
2099 (void *)CCADC_DATA1, &reg_fops);
2100 debugfs_create_file("CCADC_OFFSET_TRIM1", 0644, chip->dent,
2101 (void *)CCADC_OFFSET_TRIM1, &reg_fops);
2102 debugfs_create_file("CCADC_OFFSET_TRIM0", 0644, chip->dent,
2103 (void *)CCADC_OFFSET_TRIM0, &reg_fops);
2104
2105 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2106 (void *)TEST_BATT_TEMP, &temp_fops);
2107 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2108 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2109 debugfs_create_file("test_ocv", 0644, chip->dent,
2110 (void *)TEST_OCV, &temp_fops);
2111
2112 debugfs_create_file("read_cc", 0644, chip->dent,
2113 (void *)CC_MSB, &reading_fops);
2114 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2115 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2116 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2117 (void *)VBATT_FOR_RBATT, &reading_fops);
2118 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2119 (void *)VSENSE_FOR_RBATT, &reading_fops);
2120 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2121 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002122 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2123 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002124
2125 debugfs_create_file("show_rbatt", 0644, chip->dent,
2126 (void *)CALC_RBATT, &calc_fops);
2127 debugfs_create_file("show_fcc", 0644, chip->dent,
2128 (void *)CALC_FCC, &calc_fops);
2129 debugfs_create_file("show_pc", 0644, chip->dent,
2130 (void *)CALC_PC, &calc_fops);
2131 debugfs_create_file("show_soc", 0644, chip->dent,
2132 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002133 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2134 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002135 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2136 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002137
2138 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2139 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2140 debugfs_create_file(bms_irq_data[i].name, 0444,
2141 chip->dent,
2142 (void *)bms_irq_data[i].irq_id,
2143 &rt_fops);
2144 }
2145}
2146
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002147static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2148{
2149 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002150 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002151 struct pm8921_bms_chip *chip;
2152 const struct pm8921_bms_platform_data *pdata
2153 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002154
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002155 if (!pdata) {
2156 pr_err("missing platform data\n");
2157 return -EINVAL;
2158 }
2159
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002160 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002161 if (!chip) {
2162 pr_err("Cannot allocate pm_bms_chip\n");
2163 return -ENOMEM;
2164 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002165 spin_lock_init(&chip->bms_output_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002166 chip->dev = &pdev->dev;
2167 chip->r_sense = pdata->r_sense;
2168 chip->i_test = pdata->i_test;
2169 chip->v_failure = pdata->v_failure;
2170 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002171 rc = set_battery_data(chip);
2172 if (rc) {
2173 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002174 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002175 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002176
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002177 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2178 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002179 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2180 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002181 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002182 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002183 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002184
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002185 rc = request_irqs(chip, pdev);
2186 if (rc) {
2187 pr_err("couldn't register interrupts rc = %d\n", rc);
2188 goto free_chip;
2189 }
2190
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002191 rc = pm8921_bms_hw_init(chip);
2192 if (rc) {
2193 pr_err("couldn't init hardware rc = %d\n", rc);
2194 goto free_irqs;
2195 }
2196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002197 platform_set_drvdata(pdev, chip);
2198 the_chip = chip;
2199 create_debugfs_entries(chip);
2200
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002201 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002202 chip->ccadc_gain_uv = calib_ccadc_read_gain_uv(chip);
2203
2204 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
2205 /* begin calibration only on chips > 2.0 */
2206 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
2207 calibrate_ccadc_work(&(chip->calib_ccadc_work.work));
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002208
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002209 /* initial hkadc calibration */
2210 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002211 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2212 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2213 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002214
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002215 get_battery_uvolts(chip, &vbatt);
2216 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2217 pm8921_bms_get_percent_charge(),
2218 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002219 return 0;
2220
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002221free_irqs:
2222 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002223free_chip:
2224 kfree(chip);
2225 return rc;
2226}
2227
2228static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2229{
2230 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2231
2232 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002233 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002234 platform_set_drvdata(pdev, NULL);
2235 the_chip = NULL;
2236 kfree(chip);
2237 return 0;
2238}
2239
2240static struct platform_driver pm8921_bms_driver = {
2241 .probe = pm8921_bms_probe,
2242 .remove = __devexit_p(pm8921_bms_remove),
2243 .driver = {
2244 .name = PM8921_BMS_DEV_NAME,
2245 .owner = THIS_MODULE,
2246 },
2247};
2248
2249static int __init pm8921_bms_init(void)
2250{
2251 return platform_driver_register(&pm8921_bms_driver);
2252}
2253
2254static void __exit pm8921_bms_exit(void)
2255{
2256 platform_driver_unregister(&pm8921_bms_driver);
2257}
2258
2259late_initcall(pm8921_bms_init);
2260module_exit(pm8921_bms_exit);
2261
2262MODULE_LICENSE("GPL v2");
2263MODULE_DESCRIPTION("PMIC8921 bms driver");
2264MODULE_VERSION("1.0");
2265MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);