blob: b93ea95536297e6213cc47ce80f2d097ce50b2eb [file] [log] [blame]
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -080019#include <linux/power_supply.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/mfd/pm8xxx/pm8921-bms.h>
21#include <linux/mfd/pm8xxx/core.h>
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -070022#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080023#include <linux/mfd/pm8xxx/ccadc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024#include <linux/interrupt.h>
25#include <linux/bitops.h>
26#include <linux/debugfs.h>
27#include <linux/slab.h>
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070028#include <linux/delay.h>
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080029#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#define BMS_CONTROL 0x224
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080032#define BMS_S1_DELAY 0x225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033#define BMS_OUTPUT0 0x230
34#define BMS_OUTPUT1 0x231
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -080035#define BMS_TOLERANCES 0x232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036#define BMS_TEST1 0x237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070038#define ADC_ARB_SECP_CNTRL 0x190
39#define ADC_ARB_SECP_AMUX_CNTRL 0x191
40#define ADC_ARB_SECP_ANA_PARAM 0x192
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070041#define ADC_ARB_SECP_DIG_PARAM 0x193
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070042#define ADC_ARB_SECP_RSV 0x194
43#define ADC_ARB_SECP_DATA1 0x195
44#define ADC_ARB_SECP_DATA0 0x196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070046#define ADC_ARB_BMS_CNTRL 0x18D
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -070047#define AMUX_TRIM_2 0x322
48#define TEST_PROGRAM_REV 0x339
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070049
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050enum pmic_bms_interrupts {
51 PM8921_BMS_SBI_WRITE_OK,
52 PM8921_BMS_CC_THR,
53 PM8921_BMS_VSENSE_THR,
54 PM8921_BMS_VSENSE_FOR_R,
55 PM8921_BMS_OCV_FOR_R,
56 PM8921_BMS_GOOD_OCV,
57 PM8921_BMS_VSENSE_AVG,
58 PM_BMS_MAX_INTS,
59};
60
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080061struct pm8921_soc_params {
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -080062 uint16_t last_good_ocv_raw;
63 int cc;
64
65 int last_good_ocv_uv;
66};
67
68struct pm8921_rbatt_params {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080069 uint16_t ocv_for_rbatt_raw;
70 uint16_t vsense_for_rbatt_raw;
71 uint16_t vbatt_for_rbatt_raw;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080072
73 int ocv_for_rbatt_uv;
74 int vsense_for_rbatt_uv;
75 int vbatt_for_rbatt_uv;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080076};
77
78/**
79 * struct pm8921_bms_chip -
80 * @bms_output_lock: lock to prevent concurrent bms reads
81 * @bms_100_lock: lock to prevent concurrent updates to values that force
82 * 100% charge
83 *
84 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085struct pm8921_bms_chip {
86 struct device *dev;
87 struct dentry *dent;
88 unsigned int r_sense;
89 unsigned int i_test;
90 unsigned int v_failure;
91 unsigned int fcc;
92 struct single_row_lut *fcc_temp_lut;
93 struct single_row_lut *fcc_sf_lut;
94 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -080095 struct sf_lut *pc_sf_lut;
96 struct sf_lut *rbatt_sf_lut;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -070097 int delta_rbatt_mohm;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070098 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070099 struct delayed_work calib_ccadc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100 unsigned int calib_delay_ms;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700101 unsigned int revision;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800102 unsigned int xoadc_v0625_usb_present;
103 unsigned int xoadc_v0625_usb_absent;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700104 unsigned int xoadc_v0625;
105 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700106 unsigned int batt_temp_channel;
107 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700108 unsigned int ref625mv_channel;
109 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -0700110 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
112 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800113 struct mutex bms_output_lock;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800114 spinlock_t bms_100_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700115 struct single_row_lut *adjusted_fcc_temp_lut;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -0700116 unsigned int charging_began;
117 unsigned int start_percent;
118 unsigned int end_percent;
David Keitel35e11872012-02-17 17:40:42 -0800119 enum battery_type batt_type;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800120 uint16_t ocv_reading_at_100;
121 int cc_reading_at_100;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -0800122 int max_voltage_uv;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800123
124 int batt_temp_suspend;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800125 int soc_rbatt_suspend;
126 int default_rbatt_mohm;
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700127 int amux_2_trim_delta;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700128 uint16_t prev_last_good_ocv_raw;
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -0700129 unsigned int rconn_mohm;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130};
131
132static struct pm8921_bms_chip *the_chip;
133
134#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136#define DEFAULT_CHARGE_CYCLES 0
137
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800138static int last_usb_cal_delta_uv = 1800;
139module_param(last_usb_cal_delta_uv, int, 0644);
140
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700141static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
142static int last_charge_increase;
143module_param(last_chargecycles, int, 0644);
144module_param(last_charge_increase, int, 0644);
145
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148static int last_soc = -EINVAL;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800149static int last_real_fcc_mah = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700150static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700152static int bms_ops_set(const char *val, const struct kernel_param *kp)
153{
154 if (*(int *)kp->arg == -EINVAL)
155 return param_set_int(val, kp);
156 else
157 return 0;
158}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700160static struct kernel_param_ops bms_param_ops = {
161 .set = bms_ops_set,
162 .get = param_get_int,
163};
164
165module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
166module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
167module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
168
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800169/*
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800170 * bms_fake_battery is set in setups where a battery emulator is used instead
171 * of a real battery. This makes the bms driver report a different/fake value
172 * regardless of the calculated state of charge.
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800173 */
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800174static int bms_fake_battery = -EINVAL;
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800175module_param(bms_fake_battery, int, 0644);
176
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800177/* bms_start_XXX and bms_end_XXX are read only */
178static int bms_start_percent;
179static int bms_start_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800180static int bms_start_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800181static int bms_end_percent;
182static int bms_end_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800183static int bms_end_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800184
185static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
186{
187 return -EINVAL;
188}
189
190static struct kernel_param_ops bms_ro_param_ops = {
191 .set = bms_ro_ops_set,
192 .get = param_get_int,
193};
194module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
195module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800196module_param_cb(bms_start_cc_uah, &bms_ro_param_ops, &bms_start_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800197
198module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
199module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800200module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800201
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700202static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
203static void readjust_fcc_table(void)
204{
205 struct single_row_lut *temp, *old;
206 int i, fcc, ratio;
207
208 if (!the_chip->fcc_temp_lut) {
209 pr_err("The static fcc lut table is NULL\n");
210 return;
211 }
212
213 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
214 if (!temp) {
215 pr_err("Cannot allocate memory for adjusted fcc table\n");
216 return;
217 }
218
219 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
220
221 temp->cols = the_chip->fcc_temp_lut->cols;
222 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
223 temp->x[i] = the_chip->fcc_temp_lut->x[i];
224 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800225 temp->y[i] = (ratio * last_real_fcc_mah);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700226 temp->y[i] /= 1000;
227 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
228 temp->x[i], the_chip->fcc_temp_lut->y[i],
229 temp->y[i], ratio);
230 }
231
232 old = the_chip->adjusted_fcc_temp_lut;
233 the_chip->adjusted_fcc_temp_lut = temp;
234 kfree(old);
235}
236
237static int bms_last_real_fcc_set(const char *val,
238 const struct kernel_param *kp)
239{
240 int rc = 0;
241
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800242 if (last_real_fcc_mah == -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700243 rc = param_set_int(val, kp);
244 if (rc) {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800245 pr_err("Failed to set last_real_fcc_mah rc=%d\n", rc);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700246 return rc;
247 }
248 if (last_real_fcc_batt_temp != -EINVAL)
249 readjust_fcc_table();
250 return rc;
251}
252static struct kernel_param_ops bms_last_real_fcc_param_ops = {
253 .set = bms_last_real_fcc_set,
254 .get = param_get_int,
255};
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800256module_param_cb(last_real_fcc_mah, &bms_last_real_fcc_param_ops,
257 &last_real_fcc_mah, 0644);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700258
259static int bms_last_real_fcc_batt_temp_set(const char *val,
260 const struct kernel_param *kp)
261{
262 int rc = 0;
263
264 if (last_real_fcc_batt_temp == -EINVAL)
265 rc = param_set_int(val, kp);
266 if (rc) {
267 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
268 return rc;
269 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800270 if (last_real_fcc_mah != -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700271 readjust_fcc_table();
272 return rc;
273}
274
275static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
276 .set = bms_last_real_fcc_batt_temp_set,
277 .get = param_get_int,
278};
279module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
280 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281
282static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
283{
284 return pm8xxx_read_irq_stat(chip->dev->parent,
285 chip->pmic_bms_irq[irq_id]);
286}
287
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700288static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
289{
290 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
291 dev_dbg(chip->dev, "%s %d\n", __func__,
292 chip->pmic_bms_irq[interrupt]);
293 enable_irq(chip->pmic_bms_irq[interrupt]);
294 }
295}
296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
298{
299 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700300 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
302 }
303}
304
305static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
306 u8 mask, u8 val)
307{
308 int rc;
309 u8 reg;
310
311 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
312 if (rc) {
313 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
314 return rc;
315 }
316 reg &= ~mask;
317 reg |= val & mask;
318 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
319 if (rc) {
320 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
321 return rc;
322 }
323 return 0;
324}
325
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800326static int usb_chg_plugged_in(void)
327{
328 union power_supply_propval ret = {0,};
329 static struct power_supply *psy;
330
331 if (psy == NULL) {
332 psy = power_supply_get_by_name("usb");
333 if (psy == NULL)
334 return 0;
335 }
336
337 if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
338 return 0;
339
340 return ret.intval;
341}
342
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700343#define HOLD_OREG_DATA BIT(1)
344static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
345{
346 int rc;
347
348 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
349 HOLD_OREG_DATA);
350 if (rc) {
351 pr_err("couldnt lock bms output rc = %d\n", rc);
352 return rc;
353 }
354 return 0;
355}
356
357static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
358{
359 int rc;
360
361 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
362 if (rc) {
363 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
364 return rc;
365 }
366 return 0;
367}
368
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369#define SELECT_OUTPUT_DATA 0x1C
370#define SELECT_OUTPUT_TYPE_SHIFT 2
371#define OCV_FOR_RBATT 0x0
372#define VSENSE_FOR_RBATT 0x1
373#define VBATT_FOR_RBATT 0x2
374#define CC_MSB 0x3
375#define CC_LSB 0x4
376#define LAST_GOOD_OCV_VALUE 0x5
377#define VSENSE_AVG 0x6
378#define VBATT_AVG 0x7
379
380static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
381 int16_t *result)
382{
383 int rc;
384 u8 reg;
385
386 if (!result) {
387 pr_err("result pointer null\n");
388 return -EINVAL;
389 }
390 *result = 0;
391 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
392 pr_err("invalid type %d asked to read\n", type);
393 return -EINVAL;
394 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700395
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
397 type << SELECT_OUTPUT_TYPE_SHIFT);
398 if (rc) {
399 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
400 type, rc);
401 return rc;
402 }
403
404 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
405 if (rc) {
406 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
407 type, rc);
408 return rc;
409 }
410 *result = reg;
411 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
412 if (rc) {
413 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
414 type, rc);
415 return rc;
416 }
417 *result |= reg << 8;
418 pr_debug("type %d result %x", type, *result);
419 return 0;
420}
421
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700422#define V_PER_BIT_MUL_FACTOR 97656
423#define V_PER_BIT_DIV_FACTOR 1000
424#define XOADC_INTRINSIC_OFFSET 0x6000
425static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700426{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700427 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700428 return 0;
429
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700430 return (a - XOADC_INTRINSIC_OFFSET)
431 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700432}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700434#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700435#define VBATT_MUL_FACTOR 3
436static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800437 int usb_chg, unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700438{
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800439 s64 numerator, denominator;
440 int local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700441
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700442 if (uv == 0)
443 return 0;
444
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800445 /* dont adjust if not calibrated */
446 if (chip->xoadc_v0625 == 0 || chip->xoadc_v125 == 0) {
447 pr_debug("No cal yet return %d\n", VBATT_MUL_FACTOR * uv);
448 return VBATT_MUL_FACTOR * uv;
449 }
450
451 if (usb_chg)
452 local_delta = last_usb_cal_delta_uv;
453 else
454 local_delta = 0;
455
456 pr_debug("using delta = %d\n", local_delta);
457 numerator = ((s64)uv - chip->xoadc_v0625 - local_delta)
458 * XOADC_CALIB_UV;
459 denominator = (s64)chip->xoadc_v125 - chip->xoadc_v0625 - local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700460 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700461 return uv * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800462 return (XOADC_CALIB_UV + local_delta + div_s64(numerator, denominator))
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700463 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700464}
465
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700466#define CC_RESOLUTION_N_V1 1085069
467#define CC_RESOLUTION_D_V1 100000
468#define CC_RESOLUTION_N_V2 868056
469#define CC_RESOLUTION_D_V2 10000
470static s64 cc_to_microvolt_v1(s64 cc)
471{
472 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
473}
474
475static s64 cc_to_microvolt_v2(s64 cc)
476{
477 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
478}
479
480static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
481{
482 /*
483 * resolution (the value of a single bit) was changed after revision 2.0
484 * for more accurate readings
485 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700486 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700487 cc_to_microvolt_v1((s64)cc) :
488 cc_to_microvolt_v2((s64)cc);
489}
490
491#define CC_READING_TICKS 55
492#define SLEEP_CLK_HZ 32768
493#define SECONDS_PER_HOUR 3600
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800494/**
495 * ccmicrovolt_to_nvh -
496 * @cc_uv: coulumb counter converted to uV
497 *
498 * RETURNS: coulumb counter based charge in nVh
499 * (nano Volt Hour)
500 */
501static s64 ccmicrovolt_to_nvh(s64 cc_uv)
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700502{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800503 return div_s64(cc_uv * CC_READING_TICKS * 1000,
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700504 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
505}
506
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700507/* returns the signed value read from the hardware */
508static int read_cc(struct pm8921_bms_chip *chip, int *result)
509{
510 int rc;
511 uint16_t msw, lsw;
512
513 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
514 if (rc) {
515 pr_err("fail to read CC_LSB rc = %d\n", rc);
516 return rc;
517 }
518 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
519 if (rc) {
520 pr_err("fail to read CC_MSB rc = %d\n", rc);
521 return rc;
522 }
523 *result = msw << 16 | lsw;
524 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
525 return 0;
526}
527
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700528static int adjust_xo_vbatt_reading_for_mbg(struct pm8921_bms_chip *chip,
529 int result)
530{
531 int64_t numerator;
532 int64_t denominator;
533
534 if (chip->amux_2_trim_delta == 0)
535 return result;
536
537 numerator = (s64)result * 1000000;
538 denominator = (1000000 + (410 * (s64)chip->amux_2_trim_delta));
539 return div_s64(numerator, denominator);
540}
541
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800542static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800543 int usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800544 uint16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700546 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800547 pr_debug("raw = %04x vbatt = %u\n", reading, *result);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800548 *result = adjust_xo_vbatt_reading(chip, usb_chg, *result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800549 pr_debug("after adj vbatt = %u\n", *result);
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700550 *result = adjust_xo_vbatt_reading_for_mbg(chip, *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 return 0;
552}
553
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800554static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
555 int16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556{
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800557 *result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800558 pr_debug("raw = %04x vsense = %d\n", reading, *result);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800559 *result = pm8xxx_cc_adjust_for_gain(*result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800560 pr_debug("after adj vsense = %d\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561 return 0;
562}
563
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700564static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
565{
566 int rc;
567 int16_t reading;
568
569 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
570 if (rc) {
571 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
572 return rc;
573 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800574
575 convert_vsense_to_uv(chip, reading, result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700576 return 0;
577}
578
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
580{
581 if (y0 == y1 || x == x0)
582 return y0;
583 if (x1 == x0 || x == x1)
584 return y1;
585
586 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
587}
588
589static int interpolate_single_lut(struct single_row_lut *lut, int x)
590{
591 int i, result;
592
593 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700594 pr_debug("x %d less than known range return y = %d lut = %pS\n",
595 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596 return lut->y[0];
597 }
598 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700599 pr_debug("x %d more than known range return y = %d lut = %pS\n",
600 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601 return lut->y[lut->cols - 1];
602 }
603
604 for (i = 0; i < lut->cols; i++)
605 if (x <= lut->x[i])
606 break;
607 if (x == lut->x[i]) {
608 result = lut->y[i];
609 } else {
610 result = linear_interpolate(
611 lut->y[i - 1],
612 lut->x[i - 1],
613 lut->y[i],
614 lut->x[i],
615 x);
616 }
617 return result;
618}
619
620static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
621{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800622 /* batt_temp is in tenths of degC - convert it to degC for lookups */
623 batt_temp = batt_temp/10;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
625}
626
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700627static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
628{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800629 /* batt_temp is in tenths of degC - convert it to degC for lookups */
630 batt_temp = batt_temp/10;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700631 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
632}
633
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
635 int cycles)
636{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700637 /*
638 * sf table could be null when no battery aging data is available, in
639 * that case return 100%
640 */
641 if (chip->fcc_sf_lut)
642 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
643 else
644 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700645}
646
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800647static int interpolate_scalingfactor(struct pm8921_bms_chip *chip,
648 struct sf_lut *sf_lut,
649 int row_entry, int pc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700650{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700651 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700652 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700653 int row1 = 0;
654 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700656 /*
657 * sf table could be null when no battery aging data is available, in
658 * that case return 100%
659 */
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800660 if (!sf_lut)
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700661 return 100;
662
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800663 rows = sf_lut->rows;
664 cols = sf_lut->cols;
665 if (pc > sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700666 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667 row1 = 0;
668 row2 = 0;
669 }
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800670 if (pc < sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700671 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700672 row1 = rows - 1;
673 row2 = rows - 1;
674 }
675 for (i = 0; i < rows; i++) {
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800676 if (pc == sf_lut->percent[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677 row1 = i;
678 row2 = i;
679 break;
680 }
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800681 if (pc > sf_lut->percent[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 row1 = i - 1;
683 row2 = i;
684 break;
685 }
686 }
687
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800688 if (row_entry < sf_lut->row_entries[0])
689 row_entry = sf_lut->row_entries[0];
690 if (row_entry > sf_lut->row_entries[cols - 1])
691 row_entry = sf_lut->row_entries[cols - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692
693 for (i = 0; i < cols; i++)
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800694 if (row_entry <= sf_lut->row_entries[i])
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 break;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800696 if (row_entry == sf_lut->row_entries[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700697 scalefactor = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800698 sf_lut->sf[row1][i],
699 sf_lut->percent[row1],
700 sf_lut->sf[row2][i],
701 sf_lut->percent[row2],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702 pc);
703 return scalefactor;
704 }
705
706 scalefactorrow1 = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800707 sf_lut->sf[row1][i - 1],
708 sf_lut->row_entries[i - 1],
709 sf_lut->sf[row1][i],
710 sf_lut->row_entries[i],
711 row_entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700712
713 scalefactorrow2 = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800714 sf_lut->sf[row2][i - 1],
715 sf_lut->row_entries[i - 1],
716 sf_lut->sf[row2][i],
717 sf_lut->row_entries[i],
718 row_entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719
720 scalefactor = linear_interpolate(
721 scalefactorrow1,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800722 sf_lut->percent[row1],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723 scalefactorrow2,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800724 sf_lut->percent[row2],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725 pc);
726
727 return scalefactor;
728}
729
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700730static int is_between(int left, int right, int value)
731{
732 if (left >= right && left >= value && value >= right)
733 return 1;
734 if (left <= right && left <= value && value <= right)
735 return 1;
736
737 return 0;
738}
739
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700740static int interpolate_pc(struct pm8921_bms_chip *chip,
741 int batt_temp, int ocv)
742{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700743 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744 int rows = chip->pc_temp_ocv_lut->rows;
745 int cols = chip->pc_temp_ocv_lut->cols;
746
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800747 /* batt_temp is in tenths of degC - convert it to degC for lookups */
748 batt_temp = batt_temp/10;
749
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700751 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752 batt_temp = chip->pc_temp_ocv_lut->temp[0];
753 }
754 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700755 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
757 }
758
759 for (j = 0; j < cols; j++)
760 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
761 break;
762 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
763 /* found an exact match for temp in the table */
764 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
765 return chip->pc_temp_ocv_lut->percent[0];
766 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
767 return chip->pc_temp_ocv_lut->percent[rows - 1];
768 for (i = 0; i < rows; i++) {
769 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
770 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
771 return
772 chip->pc_temp_ocv_lut->percent[i];
773 pc = linear_interpolate(
774 chip->pc_temp_ocv_lut->percent[i],
775 chip->pc_temp_ocv_lut->ocv[i][j],
776 chip->pc_temp_ocv_lut->percent[i - 1],
777 chip->pc_temp_ocv_lut->ocv[i - 1][j],
778 ocv);
779 return pc;
780 }
781 }
782 }
783
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700784 /*
785 * batt_temp is within temperature for
786 * column j-1 and j
787 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700788 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
789 return chip->pc_temp_ocv_lut->percent[0];
790 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
791 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700792
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700793 pcj_minus_one = 0;
794 pcj = 0;
795 for (i = 0; i < rows-1; i++) {
796 if (pcj == 0
797 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
798 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
799 pcj = linear_interpolate(
800 chip->pc_temp_ocv_lut->percent[i],
801 chip->pc_temp_ocv_lut->ocv[i][j],
802 chip->pc_temp_ocv_lut->percent[i + 1],
803 chip->pc_temp_ocv_lut->ocv[i+1][j],
804 ocv);
805 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700806
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700807 if (pcj_minus_one == 0
808 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
809 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700810
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700811 pcj_minus_one = linear_interpolate(
812 chip->pc_temp_ocv_lut->percent[i],
813 chip->pc_temp_ocv_lut->ocv[i][j-1],
814 chip->pc_temp_ocv_lut->percent[i + 1],
815 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
816 ocv);
817 }
818
819 if (pcj && pcj_minus_one) {
820 pc = linear_interpolate(
821 pcj_minus_one,
822 chip->pc_temp_ocv_lut->temp[j-1],
823 pcj,
824 chip->pc_temp_ocv_lut->temp[j],
825 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826 return pc;
827 }
828 }
829
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700830 if (pcj)
831 return pcj;
832
833 if (pcj_minus_one)
834 return pcj_minus_one;
835
836 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
837 ocv, batt_temp);
838 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700839}
840
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800841#define BMS_MODE_BIT BIT(6)
842#define EN_VBAT_BIT BIT(5)
843#define OVERRIDE_MODE_DELAY_MS 20
844int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
845 int *vbat_uv)
846{
847 int16_t vsense_raw;
848 int16_t vbat_raw;
849 int vsense_uv;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800850 int usb_chg;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800851
852 if (the_chip == NULL) {
853 pr_err("Called to early\n");
854 return -EINVAL;
855 }
856
857 mutex_lock(&the_chip->bms_output_lock);
858
859 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x00);
860 pm_bms_masked_write(the_chip, BMS_CONTROL,
861 BMS_MODE_BIT | EN_VBAT_BIT, BMS_MODE_BIT | EN_VBAT_BIT);
862
863 msleep(OVERRIDE_MODE_DELAY_MS);
864
865 pm_bms_lock_output_data(the_chip);
866 pm_bms_read_output_data(the_chip, VSENSE_AVG, &vsense_raw);
867 pm_bms_read_output_data(the_chip, VBATT_AVG, &vbat_raw);
868 pm_bms_unlock_output_data(the_chip);
869 pm_bms_masked_write(the_chip, BMS_CONTROL,
870 BMS_MODE_BIT | EN_VBAT_BIT, 0);
871
872 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x0B);
873
874 mutex_unlock(&the_chip->bms_output_lock);
875
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800876 usb_chg = usb_chg_plugged_in();
877
878 convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800879 convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
880 *ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
881
882 pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
883 " ibat_ua = %d vbat_uv = %d\n",
884 (uint16_t)vsense_raw, (uint16_t)vbat_raw,
885 *ibat_ua, *vbat_uv);
886 return 0;
887}
888EXPORT_SYMBOL(pm8921_bms_get_simultaneous_battery_voltage_and_current);
889
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800890static int read_rbatt_params_raw(struct pm8921_bms_chip *chip,
891 struct pm8921_rbatt_params *raw)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700892{
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800893 int usb_chg;
894
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800895 mutex_lock(&chip->bms_output_lock);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800896 pm_bms_lock_output_data(chip);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700897
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800898 pm_bms_read_output_data(chip,
899 OCV_FOR_RBATT, &raw->ocv_for_rbatt_raw);
900 pm_bms_read_output_data(chip,
901 VBATT_FOR_RBATT, &raw->vbatt_for_rbatt_raw);
902 pm_bms_read_output_data(chip,
903 VSENSE_FOR_RBATT, &raw->vsense_for_rbatt_raw);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700904
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800905 pm_bms_unlock_output_data(chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800906 mutex_unlock(&chip->bms_output_lock);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800907
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800908 usb_chg = usb_chg_plugged_in();
909 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800910 raw->vbatt_for_rbatt_raw, &raw->vbatt_for_rbatt_uv);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800911 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800912 raw->ocv_for_rbatt_raw, &raw->ocv_for_rbatt_uv);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800913 convert_vsense_to_uv(chip, raw->vsense_for_rbatt_raw,
914 &raw->vsense_for_rbatt_uv);
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -0800915
916 pr_debug("vbatt_for_rbatt_raw = 0x%x, vbatt_for_rbatt= %duV\n",
917 raw->vbatt_for_rbatt_raw, raw->vbatt_for_rbatt_uv);
918 pr_debug("ocv_for_rbatt_raw = 0x%x, ocv_for_rbatt= %duV\n",
919 raw->ocv_for_rbatt_raw, raw->ocv_for_rbatt_uv);
920 pr_debug("vsense_for_rbatt_raw = 0x%x, vsense_for_rbatt= %duV\n",
921 raw->vsense_for_rbatt_raw, raw->vsense_for_rbatt_uv);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800922 return 0;
923}
924
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700925#define MBG_TRANSIENT_ERROR_RAW 51
926static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip,
927 struct pm8921_soc_params *raw)
928{
929 /* in 8921 parts the PON ocv is taken when the MBG is not settled.
930 * decrease the pon ocv by 15mV raw value to account for it
931 * Since a 1/3rd of vbatt is supplied to the adc the raw value
932 * needs to be adjusted by 5mV worth bits
933 */
934 if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW)
935 raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
936}
937
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800938static int read_soc_params_raw(struct pm8921_bms_chip *chip,
939 struct pm8921_soc_params *raw)
940{
941 int usb_chg;
942
943 mutex_lock(&chip->bms_output_lock);
944 pm_bms_lock_output_data(chip);
945
946 pm_bms_read_output_data(chip,
947 LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
948 read_cc(chip, &raw->cc);
949
950 pm_bms_unlock_output_data(chip);
951 mutex_unlock(&chip->bms_output_lock);
952
953 usb_chg = usb_chg_plugged_in();
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700954
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700955 if (chip->prev_last_good_ocv_raw == 0) {
956 chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
957 adjust_pon_ocv_raw(chip, raw);
958 convert_vbatt_raw_to_uv(chip, usb_chg,
959 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
960 last_ocv_uv = raw->last_good_ocv_uv;
961 } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700962 chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
963 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800964 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700965 last_ocv_uv = raw->last_good_ocv_uv;
966 } else {
967 raw->last_good_ocv_uv = last_ocv_uv;
968 }
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800969
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800970 pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
971 pr_debug("1p25 = %duV\n", chip->xoadc_v125);
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -0800972 pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
973 raw->last_good_ocv_raw, raw->last_good_ocv_uv);
974 pr_debug("cc_raw= 0x%x\n", raw->cc);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800975 return 0;
976}
977
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800978static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp)
979{
980 int rbatt, scalefactor;
981
982 rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
983 pr_debug("rbatt before scaling = %d\n", rbatt);
984 if (chip->rbatt_sf_lut == NULL) {
985 pr_debug("RBATT = %d\n", rbatt);
986 return rbatt;
987 }
Abhijeet Dharmapurikar12a891c2012-04-12 23:52:06 -0700988 /* Convert the batt_temp to DegC from deciDegC */
989 batt_temp = batt_temp / 10;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800990 scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut,
991 batt_temp, soc_rbatt);
992 pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
993 scalefactor, batt_temp, soc_rbatt);
994 rbatt = (rbatt * scalefactor) / 100;
995
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -0700996 rbatt += the_chip->rconn_mohm;
997 pr_debug("adding rconn_mohm = %d rbatt = %d\n",
998 the_chip->rconn_mohm, rbatt);
999
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07001000 if (is_between(20, 10, soc_rbatt))
1001 rbatt = rbatt
1002 + ((20 - soc_rbatt) * chip->delta_rbatt_mohm) / 10;
1003 else
1004 if (is_between(10, 0, soc_rbatt))
1005 rbatt = rbatt + chip->delta_rbatt_mohm;
1006
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001007 pr_debug("RBATT = %d\n", rbatt);
1008 return rbatt;
1009}
1010
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001011static int calculate_rbatt_resume(struct pm8921_bms_chip *chip,
1012 struct pm8921_rbatt_params *raw)
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001013{
1014 unsigned int r_batt;
1015
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001016 if (raw->ocv_for_rbatt_uv <= 0
1017 || raw->ocv_for_rbatt_uv <= raw->vbatt_for_rbatt_uv
1018 || raw->vsense_for_rbatt_raw <= 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001019 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
1020 "vsen = %d\n",
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001021 raw->ocv_for_rbatt_uv,
1022 raw->vbatt_for_rbatt_uv,
1023 raw->vsense_for_rbatt_raw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001024 return -EINVAL;
1025 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001026 r_batt = ((raw->ocv_for_rbatt_uv - raw->vbatt_for_rbatt_uv)
1027 * chip->r_sense) / raw->vsense_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001028 pr_debug("r_batt = %umilliOhms", r_batt);
1029 return r_batt;
1030}
1031
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001032static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001033 int chargecycles)
1034{
1035 int initfcc, result, scalefactor = 0;
1036
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001037 if (chip->adjusted_fcc_temp_lut == NULL) {
1038 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001039
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001040 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001041
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001042 /* Multiply the initial FCC value by the scale factor. */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001043 result = (initfcc * scalefactor * 1000) / 100;
1044 pr_debug("fcc = %d uAh\n", result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001045 return result;
1046 } else {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001047 return 1000 * interpolate_fcc_adjusted(chip, batt_temp);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001048 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001049}
1050
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001051static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
1052{
1053 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001054 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001055
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001056 rc = pm8xxx_adc_read(chip->vbat_channel, &result);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001057 if (rc) {
1058 pr_err("error reading adc channel = %d, rc = %d\n",
1059 chip->vbat_channel, rc);
1060 return rc;
1061 }
1062 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
1063 result.measurement);
1064 *uvolts = (int)result.physical;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001065 return 0;
1066}
1067
1068static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
1069{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001070 int vbatt, rbatt, ibatt_ua, rc;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001071
1072 rc = get_battery_uvolts(chip, &vbatt);
1073 if (rc) {
1074 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001075 return rc;
1076 }
1077
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001078 rc = pm8921_bms_get_battery_current(&ibatt_ua);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001079 if (rc) {
1080 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001081 return rc;
1082 }
1083
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001084 rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001085 *ocv = vbatt + (ibatt_ua * rbatt)/1000;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001086 return 0;
1087}
1088
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001089static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
1090 int chargecycles)
1091{
1092 int pc, scalefactor;
1093
1094 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
1095 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
1096 pc, ocv_uv, batt_temp);
1097
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001098 scalefactor = interpolate_scalingfactor(chip,
1099 chip->pc_sf_lut, chargecycles, pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001100 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
1101
1102 /* Multiply the initial FCC value by the scale factor. */
1103 pc = (pc * scalefactor) / 100;
1104 return pc;
1105}
1106
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001107/**
1108 * calculate_cc_uah -
1109 * @chip: the bms chip pointer
1110 * @cc: the cc reading from bms h/w
1111 * @val: return value
1112 * @coulumb_counter: adjusted coulumb counter for 100%
1113 *
1114 * RETURNS: in val pointer coulumb counter based charger in uAh
1115 * (micro Amp hour)
1116 */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001117static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001118{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001119 int64_t cc_voltage_uv, cc_nvh, cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001120
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001121 cc_voltage_uv = cc;
1122 cc_voltage_uv -= chip->cc_reading_at_100;
1123 pr_debug("cc = %d. after subtracting %d cc = %lld\n",
1124 cc, chip->cc_reading_at_100,
1125 cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07001126 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001127 cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001128 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001129 cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
1130 pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
1131 cc_uah = div_s64(cc_nvh, chip->r_sense);
1132 *val = cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001133}
1134
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001135static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001136 int rbatt, int fcc_uah,
1137 int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001138{
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001139 int voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001140
1141 /* calculate unusable charge */
1142 voltage_unusable_uv = (rbatt * chip->i_test)
1143 + (chip->v_failure * 1000);
1144 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
1145 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001146 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
1147 rbatt, voltage_unusable_uv, pc_unusable);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001148 return (fcc_uah * pc_unusable) / 100;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001149}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -07001150
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001151/* calculate remainging charge at the time of ocv */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001152static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001153 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001154 int fcc_uah, int batt_temp,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001155 int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001156{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001157 int ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001158
1159 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001160 ocv = 0;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001161 if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
1162 chip->ocv_reading_at_100 = 0;
1163 chip->cc_reading_at_100 = 0;
1164 ocv = raw->last_good_ocv_uv;
1165 } else {
1166 /*
1167 * force 100% ocv by selecting the highest voltage the
1168 * battery could every reach
1169 */
1170 ocv = chip->max_voltage_uv;
1171 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001172
1173 if (ocv == 0) {
1174 ocv = last_ocv_uv;
1175 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001176 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001177
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001178 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001179 pr_debug("ocv = %d pc = %d\n", ocv, pc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001180 return (fcc_uah * pc) / 100;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001181}
1182
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001183static void calculate_soc_params(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001184 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001185 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001186 int *fcc_uah,
1187 int *unusable_charge_uah,
1188 int *remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001189 int *cc_uah,
1190 int *rbatt)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001191{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001192 unsigned long flags;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001193 int soc_rbatt;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001194
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001195 *fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
1196 pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
1197 *fcc_uah, batt_temp, chargecycles);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001198
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001199
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001200 spin_lock_irqsave(&chip->bms_100_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001201 /* calculate remainging charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001202 *remaining_charge_uah = calculate_remaining_charge_uah(chip, raw,
1203 *fcc_uah, batt_temp, chargecycles);
1204 pr_debug("RC = %uuAh\n", *remaining_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001205
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001206 /* calculate cc micro_volt_hour */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001207 calculate_cc_uah(chip, raw->cc, cc_uah);
1208 pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %d\n",
1209 *cc_uah, raw->cc,
1210 (int64_t)raw->cc - chip->cc_reading_at_100,
1211 chip->cc_reading_at_100);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001212 spin_unlock_irqrestore(&chip->bms_100_lock, flags);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001213
1214 soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
1215 if (soc_rbatt < 0)
1216 soc_rbatt = 0;
1217 *rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
1218
1219 *unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt,
1220 *fcc_uah, batt_temp, chargecycles);
1221 pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001222}
1223
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001224static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001225 struct pm8921_soc_params *raw,
1226 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001227 int *ret_fcc_uah)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001228{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001229 int fcc_uah, unusable_charge_uah;
1230 int remaining_charge_uah;
1231 int cc_uah;
1232 int real_fcc_uah;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001233 int rbatt;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001234
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001235 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1236 &fcc_uah,
1237 &unusable_charge_uah,
1238 &remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001239 &cc_uah,
1240 &rbatt);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001241
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001242 real_fcc_uah = remaining_charge_uah - cc_uah;
1243 *ret_fcc_uah = fcc_uah;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001244 pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001245 real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
1246 return real_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001247}
1248/*
1249 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1250 * - coloumb counter charge
1251 * - unusable charge (due to battery resistance)
1252 * SOC% = (remaining usable charge/ fcc - usable_charge);
1253 */
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001254static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001255 struct pm8921_soc_params *raw,
1256 int batt_temp, int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001257{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001258 int remaining_usable_charge_uah, fcc_uah, unusable_charge_uah;
1259 int remaining_charge_uah, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001260 int update_userspace = 1;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001261 int cc_uah;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001262 int rbatt;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001263
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001264 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1265 &fcc_uah,
1266 &unusable_charge_uah,
1267 &remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001268 &cc_uah,
1269 &rbatt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001270
1271 /* calculate remaining usable charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001272 remaining_usable_charge_uah = remaining_charge_uah
1273 - cc_uah
1274 - unusable_charge_uah;
1275
1276 pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
Abhijeet Dharmapurikarbbae88312012-02-14 18:56:42 -08001277 if (fcc_uah - unusable_charge_uah <= 0) {
1278 pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
1279 fcc_uah, unusable_charge_uah);
1280 soc = 0;
1281 } else {
1282 soc = (remaining_usable_charge_uah * 100)
1283 / (fcc_uah - unusable_charge_uah);
1284 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001285
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001286 if (soc > 100)
1287 soc = 100;
1288 pr_debug("SOC = %u%%\n", soc);
1289
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001290 if (bms_fake_battery != -EINVAL) {
1291 pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
1292 return bms_fake_battery;
1293 }
1294
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001295 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001296 pr_err("bad rem_usb_chg = %d rem_chg %d,"
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001297 "cc_uah %d, unusb_chg %d\n",
1298 remaining_usable_charge_uah,
1299 remaining_charge_uah,
1300 cc_uah, unusable_charge_uah);
1301
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001302 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001303 "chargecycles = %d, batt_temp = %d"
1304 "fcc = %d soc =%d\n",
1305 last_ocv_uv, chargecycles, batt_temp,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001306 fcc_uah, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001307 update_userspace = 0;
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -08001308 soc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001309 }
1310
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001311 if (last_soc == -EINVAL || soc <= last_soc) {
1312 last_soc = update_userspace ? soc : last_soc;
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001313 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001314 }
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001315
1316 /*
1317 * soc > last_soc
1318 * the device must be charging for reporting a higher soc, if not ignore
1319 * this soc and continue reporting the last_soc
1320 */
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08001321 if (the_chip->start_percent != -EINVAL) {
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001322 last_soc = soc;
1323 } else {
1324 pr_debug("soc = %d reporting last_soc = %d\n", soc, last_soc);
1325 soc = last_soc;
1326 }
1327
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001328 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001329}
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001330#define MIN_DELTA_625_UV 1000
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001331static void calib_hkadc(struct pm8921_bms_chip *chip)
1332{
1333 int voltage, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001334 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001335 int usb_chg;
1336 int this_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001337
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001338 rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001339 if (rc) {
1340 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1341 return;
1342 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08001343 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001344
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001345 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1346 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001347
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001348 chip->xoadc_v125 = voltage;
1349
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001350 rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001351 if (rc) {
1352 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1353 return;
1354 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08001355 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001356
1357 usb_chg = usb_chg_plugged_in();
1358 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
1359 "usb_chg = %d\n",
1360 result.adc_code, voltage, result.measurement,
1361 usb_chg);
1362
1363 if (usb_chg)
1364 chip->xoadc_v0625_usb_present = voltage;
1365 else
1366 chip->xoadc_v0625_usb_absent = voltage;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001367
1368 chip->xoadc_v0625 = voltage;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001369 if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) {
1370 this_delta = chip->xoadc_v0625_usb_present
1371 - chip->xoadc_v0625_usb_absent;
1372 pr_debug("this_delta= %duV\n", this_delta);
1373 if (this_delta > MIN_DELTA_625_UV)
1374 last_usb_cal_delta_uv = this_delta;
1375 pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n",
1376 chip->xoadc_v0625_usb_present,
1377 chip->xoadc_v0625_usb_absent,
1378 last_usb_cal_delta_uv);
1379 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001380}
1381
1382static void calibrate_hkadc_work(struct work_struct *work)
1383{
1384 struct pm8921_bms_chip *chip = container_of(work,
1385 struct pm8921_bms_chip, calib_hkadc_work);
1386
1387 calib_hkadc(chip);
1388}
1389
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001390void pm8921_bms_calibrate_hkadc(void)
1391{
1392 schedule_work(&the_chip->calib_hkadc_work);
1393}
1394
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001395static void calibrate_ccadc_work(struct work_struct *work)
1396{
1397 struct pm8921_bms_chip *chip = container_of(work,
1398 struct pm8921_bms_chip, calib_ccadc_work.work);
1399
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001400 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001401 calib_hkadc(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001402 schedule_delayed_work(&chip->calib_ccadc_work,
1403 round_jiffies_relative(msecs_to_jiffies
1404 (chip->calib_delay_ms)));
1405}
1406
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001407int pm8921_bms_get_vsense_avg(int *result)
1408{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001409 int rc = -EINVAL;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001410
1411 if (the_chip) {
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001412 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001413 pm_bms_lock_output_data(the_chip);
1414 rc = read_vsense_avg(the_chip, result);
1415 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001416 mutex_unlock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001417 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001418
1419 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001420 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001421}
1422EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1423
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001424int pm8921_bms_get_battery_current(int *result_ua)
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001425{
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001426 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001427
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001428 if (!the_chip) {
1429 pr_err("called before initialization\n");
1430 return -EINVAL;
1431 }
1432 if (the_chip->r_sense == 0) {
1433 pr_err("r_sense is zero\n");
1434 return -EINVAL;
1435 }
1436
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001437 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001438 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001439 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001440 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001441 mutex_unlock(&the_chip->bms_output_lock);
1442 pr_debug("vsense=%duV\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001443 /* cast for signed division */
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001444 *result_ua = vsense * 1000 / (int)the_chip->r_sense;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001445 pr_debug("ibat=%duA\n", *result_ua);
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001446 return 0;
1447}
1448EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1449
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001450int pm8921_bms_get_percent_charge(void)
1451{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001452 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001453 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001454 struct pm8921_soc_params raw;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001455
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001456 if (!the_chip) {
1457 pr_err("called before initialization\n");
1458 return -EINVAL;
1459 }
1460
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001461 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001462 if (rc) {
1463 pr_err("error reading adc channel = %d, rc = %d\n",
1464 the_chip->batt_temp_channel, rc);
1465 return rc;
1466 }
1467 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1468 result.measurement);
1469 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001470
1471 read_soc_params_raw(the_chip, &raw);
1472
1473 return calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001474 batt_temp, last_chargecycles);
1475}
1476EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1477
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001478int pm8921_bms_get_rbatt(void)
1479{
1480 int batt_temp, rc;
1481 struct pm8xxx_adc_chan_result result;
1482 struct pm8921_soc_params raw;
1483 int fcc_uah;
1484 int unusable_charge_uah;
1485 int remaining_charge_uah;
1486 int cc_uah;
1487 int rbatt;
1488
1489 if (!the_chip) {
1490 pr_err("called before initialization\n");
1491 return -EINVAL;
1492 }
1493
1494 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1495 if (rc) {
1496 pr_err("error reading adc channel = %d, rc = %d\n",
1497 the_chip->batt_temp_channel, rc);
1498 return rc;
1499 }
1500 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1501 result.measurement);
1502 batt_temp = (int)result.physical;
1503
1504 read_soc_params_raw(the_chip, &raw);
1505
1506 calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles,
1507 &fcc_uah,
1508 &unusable_charge_uah,
1509 &remaining_charge_uah,
1510 &cc_uah,
1511 &rbatt);
1512 return rbatt;
1513}
1514EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt);
1515
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001516int pm8921_bms_get_fcc(void)
1517{
1518 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001519 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001520
1521 if (!the_chip) {
1522 pr_err("called before initialization\n");
1523 return -EINVAL;
1524 }
1525
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001526 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001527 if (rc) {
1528 pr_err("error reading adc channel = %d, rc = %d\n",
1529 the_chip->batt_temp_channel, rc);
1530 return rc;
1531 }
1532 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1533 result.measurement);
1534 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001535 return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001536}
1537EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1538
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001539#define IBAT_TOL_MASK 0x0F
1540#define OCV_TOL_MASK 0xF0
1541#define IBAT_TOL_DEFAULT 0x03
1542#define IBAT_TOL_NOCHG 0x0F
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001543void pm8921_bms_charging_began(void)
1544{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001545 int batt_temp, rc;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001546 struct pm8xxx_adc_chan_result result;
1547 struct pm8921_soc_params raw;
1548
1549 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1550 if (rc) {
1551 pr_err("error reading adc channel = %d, rc = %d\n",
1552 the_chip->batt_temp_channel, rc);
1553 return;
1554 }
1555 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1556 result.measurement);
1557 batt_temp = (int)result.physical;
1558
1559 read_soc_params_raw(the_chip, &raw);
1560
1561 the_chip->start_percent = calculate_state_of_charge(the_chip, &raw,
1562 batt_temp, last_chargecycles);
1563 bms_start_percent = the_chip->start_percent;
1564 bms_start_ocv_uv = raw.last_good_ocv_uv;
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001565 calculate_cc_uah(the_chip, raw.cc, &bms_start_cc_uah);
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001566 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
1567 IBAT_TOL_MASK, IBAT_TOL_DEFAULT);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001568 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001569}
1570EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1571
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001572#define DELTA_FCC_PERCENT 3
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08001573#define MIN_START_PERCENT_FOR_LEARNING 30
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001574void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001575{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001576 int batt_temp, rc;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001577 struct pm8xxx_adc_chan_result result;
1578 struct pm8921_soc_params raw;
1579
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001580 if (the_chip == NULL)
1581 return;
1582
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001583 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1584 if (rc) {
1585 pr_err("error reading adc channel = %d, rc = %d\n",
1586 the_chip->batt_temp_channel, rc);
1587 return;
1588 }
1589 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1590 result.measurement);
1591 batt_temp = (int)result.physical;
1592
1593 read_soc_params_raw(the_chip, &raw);
1594
Abhijeet Dharmapurikar57aeb922012-01-25 13:22:26 -08001595 calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah);
1596
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08001597 if (is_battery_full
1598 && the_chip->start_percent <= MIN_START_PERCENT_FOR_LEARNING) {
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001599 unsigned long flags;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001600 int fcc_uah, new_fcc_uah, delta_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001601
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001602 new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001603 batt_temp, last_chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001604 &fcc_uah);
1605 delta_fcc_uah = new_fcc_uah - fcc_uah;
1606 if (delta_fcc_uah < 0)
1607 delta_fcc_uah = -delta_fcc_uah;
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001608
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08001609 if (delta_fcc_uah * 100 > (DELTA_FCC_PERCENT * fcc_uah)) {
1610 /* new_fcc_uah is outside the scope limit it */
1611 if (new_fcc_uah > fcc_uah)
1612 new_fcc_uah
1613 = (fcc_uah + DELTA_FCC_PERCENT * fcc_uah);
1614 else
1615 new_fcc_uah
1616 = (fcc_uah - DELTA_FCC_PERCENT * fcc_uah);
1617
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001618 pr_debug("delta_fcc=%d > %d percent of fcc=%d"
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08001619 "restring it to %d\n",
1620 delta_fcc_uah, DELTA_FCC_PERCENT,
1621 fcc_uah, new_fcc_uah);
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001622 }
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001623
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08001624 last_real_fcc_mah = new_fcc_uah/1000;
1625 last_real_fcc_batt_temp = batt_temp;
1626 readjust_fcc_table();
1627
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001628 spin_lock_irqsave(&the_chip->bms_100_lock, flags);
1629 the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
1630 the_chip->cc_reading_at_100 = raw.cc;
1631 spin_unlock_irqrestore(&the_chip->bms_100_lock, flags);
1632 pr_debug("EOC ocv_reading = 0x%x cc = %d\n",
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001633 the_chip->ocv_reading_at_100,
1634 the_chip->cc_reading_at_100);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001635 }
1636
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001637 the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
1638 batt_temp, last_chargecycles);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001639
1640 bms_end_percent = the_chip->end_percent;
1641 bms_end_ocv_uv = raw.last_good_ocv_uv;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001642
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001643 if (the_chip->end_percent > the_chip->start_percent) {
Abhijeet Dharmapurikar8a113f82012-01-19 10:58:45 -08001644 last_charge_increase +=
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001645 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001646 if (last_charge_increase > 100) {
1647 last_chargecycles++;
1648 last_charge_increase = last_charge_increase % 100;
1649 }
1650 }
1651 pr_debug("end_percent = %u%% last_charge_increase = %d"
1652 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001653 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001654 last_charge_increase,
1655 last_chargecycles);
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08001656 the_chip->start_percent = -EINVAL;
1657 the_chip->end_percent = -EINVAL;
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001658 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
1659 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001660}
1661EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1662
1663static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1664{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001665 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001666 return IRQ_HANDLED;
1667}
1668
1669static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1670{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001671 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001672 return IRQ_HANDLED;
1673}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001674
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001675static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1676{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001677 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001678 return IRQ_HANDLED;
1679}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001680
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001681static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1682{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001683 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001684 return IRQ_HANDLED;
1685}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001686
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001687static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1688{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001689 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001690
1691 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001692 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001693 return IRQ_HANDLED;
1694}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001695
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001696static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1697{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001698 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001699
1700 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001701 schedule_work(&chip->calib_hkadc_work);
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_avg_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}
1710
1711struct pm_bms_irq_init_data {
1712 unsigned int irq_id;
1713 char *name;
1714 unsigned long flags;
1715 irqreturn_t (*handler)(int, void *);
1716};
1717
1718#define BMS_IRQ(_id, _flags, _handler) \
1719{ \
1720 .irq_id = _id, \
1721 .name = #_id, \
1722 .flags = _flags, \
1723 .handler = _handler, \
1724}
1725
1726struct pm_bms_irq_init_data bms_irq_data[] = {
1727 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1728 pm8921_bms_sbi_write_ok_handler),
1729 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1730 pm8921_bms_cc_thr_handler),
1731 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1732 pm8921_bms_vsense_thr_handler),
1733 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1734 pm8921_bms_vsense_for_r_handler),
1735 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1736 pm8921_bms_ocv_for_r_handler),
1737 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1738 pm8921_bms_good_ocv_handler),
1739 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1740 pm8921_bms_vsense_avg_handler),
1741};
1742
1743static void free_irqs(struct pm8921_bms_chip *chip)
1744{
1745 int i;
1746
1747 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1748 if (chip->pmic_bms_irq[i]) {
1749 free_irq(chip->pmic_bms_irq[i], NULL);
1750 chip->pmic_bms_irq[i] = 0;
1751 }
1752}
1753
1754static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1755 struct platform_device *pdev)
1756{
1757 struct resource *res;
1758 int ret, i;
1759
1760 ret = 0;
1761 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1762
1763 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1764 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1765 bms_irq_data[i].name);
1766 if (res == NULL) {
1767 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1768 goto err_out;
1769 }
1770 ret = request_irq(res->start, bms_irq_data[i].handler,
1771 bms_irq_data[i].flags,
1772 bms_irq_data[i].name, chip);
1773 if (ret < 0) {
1774 pr_err("couldn't request %d (%s) %d\n", res->start,
1775 bms_irq_data[i].name, ret);
1776 goto err_out;
1777 }
1778 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1779 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1780 }
1781 return 0;
1782
1783err_out:
1784 free_irqs(chip);
1785 return -EINVAL;
1786}
1787
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001788static int pm8921_bms_suspend(struct device *dev)
1789{
1790 int rc;
1791 struct pm8xxx_adc_chan_result result;
1792 struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001793 struct pm8921_soc_params raw;
1794 int fcc_uah;
1795 int remaining_charge_uah;
1796 int cc_uah;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001797
1798 chip->batt_temp_suspend = 0;
1799 rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
1800 if (rc) {
1801 pr_err("error reading adc channel = %d, rc = %d\n",
1802 chip->batt_temp_channel, rc);
1803 }
1804 chip->batt_temp_suspend = (int)result.physical;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001805 read_soc_params_raw(chip, &raw);
1806
1807 fcc_uah = calculate_fcc_uah(chip,
1808 chip->batt_temp_suspend, last_chargecycles);
1809 pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
1810 fcc_uah, chip->batt_temp_suspend, last_chargecycles);
1811 /* calculate remainging charge */
1812 remaining_charge_uah = calculate_remaining_charge_uah(chip, &raw,
1813 fcc_uah, chip->batt_temp_suspend,
1814 last_chargecycles);
1815 pr_debug("RC = %uuAh\n", remaining_charge_uah);
1816
1817 /* calculate cc micro_volt_hour */
1818 calculate_cc_uah(chip, raw.cc, &cc_uah);
1819 pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %d\n",
1820 cc_uah, raw.cc,
1821 (int64_t)raw.cc - chip->cc_reading_at_100,
1822 chip->cc_reading_at_100);
1823 chip->soc_rbatt_suspend = ((remaining_charge_uah - cc_uah) * 100)
1824 / fcc_uah;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001825 return 0;
1826}
1827
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001828#define DELTA_RBATT_PERCENT 10
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001829static int pm8921_bms_resume(struct device *dev)
1830{
1831 struct pm8921_rbatt_params raw;
1832 struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
1833 int rbatt;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001834 int expected_rbatt;
1835 int scalefactor;
1836 int delta_rbatt;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001837
1838 read_rbatt_params_raw(chip, &raw);
1839 rbatt = calculate_rbatt_resume(chip, &raw);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001840
1841 if (rbatt < 0)
1842 return 0;
1843
1844 expected_rbatt
1845 = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
1846
1847 if (chip->rbatt_sf_lut) {
1848 scalefactor = interpolate_scalingfactor(chip,
1849 chip->rbatt_sf_lut,
Abhijeet Dharmapurikar12a891c2012-04-12 23:52:06 -07001850 chip->batt_temp_suspend / 10,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001851 chip->soc_rbatt_suspend);
1852 rbatt = rbatt * 100 / scalefactor;
1853 }
1854
1855 delta_rbatt = expected_rbatt - rbatt;
1856 if (delta_rbatt)
1857 delta_rbatt = -delta_rbatt;
1858 /*
1859 * only update last_rbatt if rbatt is within some
1860 * percent of expected_rbatt
1861 */
1862 if (delta_rbatt * 100 <= DELTA_RBATT_PERCENT * expected_rbatt)
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001863 last_rbatt = rbatt;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001864
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001865 return 0;
1866}
1867
1868static const struct dev_pm_ops pm8921_pm_ops = {
1869 .suspend = pm8921_bms_suspend,
1870 .resume = pm8921_bms_resume,
1871};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001872#define EN_BMS_BIT BIT(7)
1873#define EN_PON_HS_BIT BIT(0)
1874static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1875{
1876 int rc;
1877
1878 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1879 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1880 if (rc) {
1881 pr_err("failed to enable pon and bms addr = %d %d",
1882 BMS_CONTROL, rc);
1883 }
1884
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001885 /* The charger will call start charge later if usb is present */
1886 pm_bms_masked_write(chip, BMS_TOLERANCES,
1887 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001888 return 0;
1889}
1890
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001891static void check_initial_ocv(struct pm8921_bms_chip *chip)
1892{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001893 int ocv_uv, rc;
1894 int16_t ocv_raw;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001895 int usb_chg;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001896
1897 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001898 * Check if a ocv is available in bms hw,
1899 * if not compute it here at boot time and save it
1900 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001901 */
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001902 ocv_uv = 0;
1903 pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001904 usb_chg = usb_chg_plugged_in();
1905 rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001906 if (rc || ocv_uv == 0) {
1907 rc = adc_based_ocv(chip, &ocv_uv);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001908 if (rc) {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001909 pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
1910 ocv_uv = DEFAULT_OCV_MICROVOLTS;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001911 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001912 last_ocv_uv = ocv_uv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001913 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001914 pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, last_ocv_uv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001915}
1916
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001917static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1918{
1919 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001920 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001921
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001922 rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001923 if (rc) {
1924 pr_err("error reading batt id channel = %d, rc = %d\n",
1925 chip->vbat_channel, rc);
1926 return rc;
1927 }
1928 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1929 result.measurement);
David Keitel8f2601b2012-02-14 22:31:07 -08001930 return result.adc_code;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001931}
1932
David Keitel8f2601b2012-02-14 22:31:07 -08001933#define PALLADIUM_ID_MIN 0x7F40
1934#define PALLADIUM_ID_MAX 0x7F5A
1935#define DESAY_5200_ID_MIN 0x7F7F
1936#define DESAY_5200_ID_MAX 0x802F
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001937static int set_battery_data(struct pm8921_bms_chip *chip)
1938{
1939 int64_t battery_id;
1940
David Keitel35e11872012-02-17 17:40:42 -08001941 if (chip->batt_type == BATT_DESAY)
1942 goto desay;
1943 else if (chip->batt_type == BATT_PALLADIUM)
1944 goto palladium;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001945
David Keitel35e11872012-02-17 17:40:42 -08001946 battery_id = read_battery_id(chip);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001947 if (battery_id < 0) {
1948 pr_err("cannot read battery id err = %lld\n", battery_id);
1949 return battery_id;
1950 }
1951
1952 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
David Keitel35e11872012-02-17 17:40:42 -08001953 goto palladium;
1954 } else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
1955 battery_id)) {
1956 goto desay;
1957 } else {
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001958 pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
1959 battery_id);
1960 goto palladium;
David Keitel35e11872012-02-17 17:40:42 -08001961 }
1962
1963palladium:
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001964 chip->fcc = palladium_1500_data.fcc;
1965 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1966 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1967 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1968 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001969 chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
1970 chip->default_rbatt_mohm
1971 = palladium_1500_data.default_rbatt_mohm;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07001972 chip->delta_rbatt_mohm = palladium_1500_data.delta_rbatt_mohm;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001973 return 0;
David Keitel35e11872012-02-17 17:40:42 -08001974desay:
David Keitel8f2601b2012-02-14 22:31:07 -08001975 chip->fcc = desay_5200_data.fcc;
1976 chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
David Keitel8f2601b2012-02-14 22:31:07 -08001977 chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
1978 chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001979 chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
1980 chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07001981 chip->delta_rbatt_mohm = desay_5200_data.delta_rbatt_mohm;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001982 return 0;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001983}
1984
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001985enum bms_request_operation {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001986 CALC_RBATT,
1987 CALC_FCC,
1988 CALC_PC,
1989 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001990 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001991 CALIB_CCADC,
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001992 GET_VBAT_VSENSE_SIMULTANEOUS,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001993};
1994
1995static int test_batt_temp = 5;
1996static int test_chargecycle = 150;
1997static int test_ocv = 3900000;
1998enum {
1999 TEST_BATT_TEMP,
2000 TEST_CHARGE_CYCLE,
2001 TEST_OCV,
2002};
2003static int get_test_param(void *data, u64 * val)
2004{
2005 switch ((int)data) {
2006 case TEST_BATT_TEMP:
2007 *val = test_batt_temp;
2008 break;
2009 case TEST_CHARGE_CYCLE:
2010 *val = test_chargecycle;
2011 break;
2012 case TEST_OCV:
2013 *val = test_ocv;
2014 break;
2015 default:
2016 return -EINVAL;
2017 }
2018 return 0;
2019}
2020static int set_test_param(void *data, u64 val)
2021{
2022 switch ((int)data) {
2023 case TEST_BATT_TEMP:
2024 test_batt_temp = (int)val;
2025 break;
2026 case TEST_CHARGE_CYCLE:
2027 test_chargecycle = (int)val;
2028 break;
2029 case TEST_OCV:
2030 test_ocv = (int)val;
2031 break;
2032 default:
2033 return -EINVAL;
2034 }
2035 return 0;
2036}
2037DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
2038
2039static int get_calc(void *data, u64 * val)
2040{
2041 int param = (int)data;
2042 int ret = 0;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002043 int ibat_ua, vbat_uv;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002044 struct pm8921_soc_params raw;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002045 struct pm8921_rbatt_params rraw;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002046
2047 read_soc_params_raw(the_chip, &raw);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002048 read_rbatt_params_raw(the_chip, &rraw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002049
2050 *val = 0;
2051
2052 /* global irq number passed in via data */
2053 switch (param) {
2054 case CALC_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002055 *val = calculate_rbatt_resume(the_chip, &rraw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002056 break;
2057 case CALC_FCC:
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002058 *val = calculate_fcc_uah(the_chip, test_batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002059 test_chargecycle);
2060 break;
2061 case CALC_PC:
2062 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
2063 test_chargecycle);
2064 break;
2065 case CALC_SOC:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002066 *val = calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002067 test_batt_temp, test_chargecycle);
2068 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002069 case CALIB_HKADC:
2070 /* reading this will trigger calibration */
2071 *val = 0;
2072 calib_hkadc(the_chip);
2073 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002074 case CALIB_CCADC:
2075 /* reading this will trigger calibration */
2076 *val = 0;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002077 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002078 break;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002079 case GET_VBAT_VSENSE_SIMULTANEOUS:
2080 /* reading this will call simultaneous vbat and vsense */
2081 *val =
2082 pm8921_bms_get_simultaneous_battery_voltage_and_current(
2083 &ibat_ua,
2084 &vbat_uv);
2085 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002086 default:
2087 ret = -EINVAL;
2088 }
2089 return ret;
2090}
2091DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
2092
2093static int get_reading(void *data, u64 * val)
2094{
2095 int param = (int)data;
2096 int ret = 0;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002097 struct pm8921_soc_params raw;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002098 struct pm8921_rbatt_params rraw;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002099
2100 read_soc_params_raw(the_chip, &raw);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002101 read_rbatt_params_raw(the_chip, &rraw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002102
2103 *val = 0;
2104
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002105 switch (param) {
2106 case CC_MSB:
2107 case CC_LSB:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002108 *val = raw.cc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002109 break;
2110 case LAST_GOOD_OCV_VALUE:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002111 *val = raw.last_good_ocv_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002112 break;
2113 case VBATT_FOR_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002114 *val = rraw.vbatt_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002115 break;
2116 case VSENSE_FOR_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002117 *val = rraw.vsense_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002118 break;
2119 case OCV_FOR_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002120 *val = rraw.ocv_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002121 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002122 case VSENSE_AVG:
2123 read_vsense_avg(the_chip, (uint *)val);
2124 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002125 default:
2126 ret = -EINVAL;
2127 }
2128 return ret;
2129}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002130DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002131
2132static int get_rt_status(void *data, u64 * val)
2133{
2134 int i = (int)data;
2135 int ret;
2136
2137 /* global irq number passed in via data */
2138 ret = pm_bms_get_rt_status(the_chip, i);
2139 *val = ret;
2140 return 0;
2141}
2142DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2143
2144static int get_reg(void *data, u64 * val)
2145{
2146 int addr = (int)data;
2147 int ret;
2148 u8 temp;
2149
2150 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2151 if (ret) {
2152 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2153 addr, temp, ret);
2154 return -EAGAIN;
2155 }
2156 *val = temp;
2157 return 0;
2158}
2159
2160static int set_reg(void *data, u64 val)
2161{
2162 int addr = (int)data;
2163 int ret;
2164 u8 temp;
2165
2166 temp = (u8) val;
2167 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2168 if (ret) {
2169 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2170 addr, temp, ret);
2171 return -EAGAIN;
2172 }
2173 return 0;
2174}
2175DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2176
2177static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2178{
2179 int i;
2180
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002181 chip->dent = debugfs_create_dir("pm8921-bms", NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002182
2183 if (IS_ERR(chip->dent)) {
2184 pr_err("pmic bms couldnt create debugfs dir\n");
2185 return;
2186 }
2187
2188 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2189 (void *)BMS_CONTROL, &reg_fops);
2190 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2191 (void *)BMS_OUTPUT0, &reg_fops);
2192 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2193 (void *)BMS_OUTPUT1, &reg_fops);
2194 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2195 (void *)BMS_TEST1, &reg_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002196
2197 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2198 (void *)TEST_BATT_TEMP, &temp_fops);
2199 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2200 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2201 debugfs_create_file("test_ocv", 0644, chip->dent,
2202 (void *)TEST_OCV, &temp_fops);
2203
2204 debugfs_create_file("read_cc", 0644, chip->dent,
2205 (void *)CC_MSB, &reading_fops);
2206 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2207 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2208 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2209 (void *)VBATT_FOR_RBATT, &reading_fops);
2210 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2211 (void *)VSENSE_FOR_RBATT, &reading_fops);
2212 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2213 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002214 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2215 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002216
2217 debugfs_create_file("show_rbatt", 0644, chip->dent,
2218 (void *)CALC_RBATT, &calc_fops);
2219 debugfs_create_file("show_fcc", 0644, chip->dent,
2220 (void *)CALC_FCC, &calc_fops);
2221 debugfs_create_file("show_pc", 0644, chip->dent,
2222 (void *)CALC_PC, &calc_fops);
2223 debugfs_create_file("show_soc", 0644, chip->dent,
2224 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002225 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2226 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002227 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2228 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002229
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002230 debugfs_create_file("simultaneous", 0644, chip->dent,
2231 (void *)GET_VBAT_VSENSE_SIMULTANEOUS, &calc_fops);
2232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002233 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2234 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2235 debugfs_create_file(bms_irq_data[i].name, 0444,
2236 chip->dent,
2237 (void *)bms_irq_data[i].irq_id,
2238 &rt_fops);
2239 }
2240}
2241
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -07002242#define REG_SBI_CONFIG 0x04F
2243#define PAGE3_ENABLE_MASK 0x6
2244#define PROGRAM_REV_MASK 0x0F
2245#define PROGRAM_REV 0x9
2246static int read_ocv_trim(struct pm8921_bms_chip *chip)
2247{
2248 int rc;
2249 u8 reg, sbi_config;
2250
2251 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
2252 if (rc) {
2253 pr_err("error = %d reading sbi config reg\n", rc);
2254 return rc;
2255 }
2256
2257 reg = sbi_config | PAGE3_ENABLE_MASK;
2258 rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
2259 if (rc) {
2260 pr_err("error = %d writing sbi config reg\n", rc);
2261 return rc;
2262 }
2263
2264 rc = pm8xxx_readb(chip->dev->parent, TEST_PROGRAM_REV, &reg);
2265 if (rc)
2266 pr_err("Error %d reading %d addr %d\n",
2267 rc, reg, TEST_PROGRAM_REV);
2268 pr_err("program rev reg is 0x%x\n", reg);
2269 reg &= PROGRAM_REV_MASK;
2270
2271 /* If the revision is equal or higher do not adjust trim delta */
2272 if (reg >= PROGRAM_REV) {
2273 chip->amux_2_trim_delta = 0;
2274 goto restore_sbi_config;
2275 }
2276
2277 rc = pm8xxx_readb(chip->dev->parent, AMUX_TRIM_2, &reg);
2278 if (rc) {
2279 pr_err("error = %d reading trim reg\n", rc);
2280 return rc;
2281 }
2282
2283 pr_err("trim reg is 0x%x\n", reg);
2284 chip->amux_2_trim_delta = abs(0x49 - reg);
2285 pr_err("trim delta is %d\n", chip->amux_2_trim_delta);
2286
2287restore_sbi_config:
2288 rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
2289 if (rc) {
2290 pr_err("error = %d writing sbi config reg\n", rc);
2291 return rc;
2292 }
2293
2294 return 0;
2295}
2296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002297static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2298{
2299 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002300 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002301 struct pm8921_bms_chip *chip;
2302 const struct pm8921_bms_platform_data *pdata
2303 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002304
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002305 if (!pdata) {
2306 pr_err("missing platform data\n");
2307 return -EINVAL;
2308 }
2309
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002310 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002311 if (!chip) {
2312 pr_err("Cannot allocate pm_bms_chip\n");
2313 return -ENOMEM;
2314 }
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002315 mutex_init(&chip->bms_output_lock);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002316 spin_lock_init(&chip->bms_100_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002317 chip->dev = &pdev->dev;
2318 chip->r_sense = pdata->r_sense;
2319 chip->i_test = pdata->i_test;
2320 chip->v_failure = pdata->v_failure;
2321 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -08002322 chip->max_voltage_uv = pdata->max_voltage_uv;
David Keitel35e11872012-02-17 17:40:42 -08002323 chip->batt_type = pdata->battery_type;
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -07002324 chip->rconn_mohm = pdata->rconn_mohm;
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08002325 chip->start_percent = -EINVAL;
2326 chip->end_percent = -EINVAL;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002327 rc = set_battery_data(chip);
2328 if (rc) {
2329 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002330 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002331 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002332
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002333 if (chip->pc_temp_ocv_lut == NULL) {
2334 pr_err("temp ocv lut table is NULL\n");
2335 rc = -EINVAL;
2336 goto free_chip;
2337 }
2338
2339 /* set defaults in the battery data */
2340 if (chip->default_rbatt_mohm <= 0)
2341 chip->default_rbatt_mohm = DEFAULT_RBATT_MOHMS;
2342
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002343 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2344 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002345 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2346 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002347 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002348 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002349 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002350
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002351 rc = request_irqs(chip, pdev);
2352 if (rc) {
2353 pr_err("couldn't register interrupts rc = %d\n", rc);
2354 goto free_chip;
2355 }
2356
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002357 rc = pm8921_bms_hw_init(chip);
2358 if (rc) {
2359 pr_err("couldn't init hardware rc = %d\n", rc);
2360 goto free_irqs;
2361 }
2362
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002363 platform_set_drvdata(pdev, chip);
2364 the_chip = chip;
2365 create_debugfs_entries(chip);
2366
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -07002367 rc = read_ocv_trim(chip);
2368 if (rc) {
2369 pr_err("couldn't adjust ocv_trim rc= %d\n", rc);
2370 goto free_irqs;
2371 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002372 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002373
2374 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
2375 /* begin calibration only on chips > 2.0 */
2376 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
Abhijeet Dharmapurikarccfc4f32012-01-16 17:35:18 -08002377 schedule_delayed_work(&chip->calib_ccadc_work, 0);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002378
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002379 /* initial hkadc calibration */
2380 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002381 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2382 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2383 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002384
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002385 get_battery_uvolts(chip, &vbatt);
2386 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2387 pm8921_bms_get_percent_charge(),
2388 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002389 return 0;
2390
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002391free_irqs:
2392 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002393free_chip:
2394 kfree(chip);
2395 return rc;
2396}
2397
2398static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2399{
2400 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2401
2402 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002403 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002404 platform_set_drvdata(pdev, NULL);
2405 the_chip = NULL;
2406 kfree(chip);
2407 return 0;
2408}
2409
2410static struct platform_driver pm8921_bms_driver = {
2411 .probe = pm8921_bms_probe,
2412 .remove = __devexit_p(pm8921_bms_remove),
2413 .driver = {
2414 .name = PM8921_BMS_DEV_NAME,
2415 .owner = THIS_MODULE,
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002416 .pm = &pm8921_pm_ops
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002417 },
2418};
2419
2420static int __init pm8921_bms_init(void)
2421{
2422 return platform_driver_register(&pm8921_bms_driver);
2423}
2424
2425static void __exit pm8921_bms_exit(void)
2426{
2427 platform_driver_unregister(&pm8921_bms_driver);
2428}
2429
2430late_initcall(pm8921_bms_init);
2431module_exit(pm8921_bms_exit);
2432
2433MODULE_LICENSE("GPL v2");
2434MODULE_DESCRIPTION("PMIC8921 bms driver");
2435MODULE_VERSION("1.0");
2436MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);