blob: 4b177c491659062f52fb1299c7a52f7c1736adb6 [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 Dharmapurikarb2680132012-05-15 16:32:51 -070023#include <linux/mfd/pm8xxx/pm8921-charger.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080024#include <linux/mfd/pm8xxx/ccadc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025#include <linux/interrupt.h>
26#include <linux/bitops.h>
27#include <linux/debugfs.h>
28#include <linux/slab.h>
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070029#include <linux/delay.h>
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080030#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#define BMS_CONTROL 0x224
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080033#define BMS_S1_DELAY 0x225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034#define BMS_OUTPUT0 0x230
35#define BMS_OUTPUT1 0x231
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -080036#define BMS_TOLERANCES 0x232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#define BMS_TEST1 0x237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070039#define ADC_ARB_SECP_CNTRL 0x190
40#define ADC_ARB_SECP_AMUX_CNTRL 0x191
41#define ADC_ARB_SECP_ANA_PARAM 0x192
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070042#define ADC_ARB_SECP_DIG_PARAM 0x193
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070043#define ADC_ARB_SECP_RSV 0x194
44#define ADC_ARB_SECP_DATA1 0x195
45#define ADC_ARB_SECP_DATA0 0x196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070047#define ADC_ARB_BMS_CNTRL 0x18D
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -070048#define AMUX_TRIM_2 0x322
49#define TEST_PROGRAM_REV 0x339
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070050
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -070051#define TEMP_SOC_STORAGE 0x107
52
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053enum pmic_bms_interrupts {
54 PM8921_BMS_SBI_WRITE_OK,
55 PM8921_BMS_CC_THR,
56 PM8921_BMS_VSENSE_THR,
57 PM8921_BMS_VSENSE_FOR_R,
58 PM8921_BMS_OCV_FOR_R,
59 PM8921_BMS_GOOD_OCV,
60 PM8921_BMS_VSENSE_AVG,
61 PM_BMS_MAX_INTS,
62};
63
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080064struct pm8921_soc_params {
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -080065 uint16_t last_good_ocv_raw;
66 int cc;
67
68 int last_good_ocv_uv;
69};
70
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080071/**
72 * struct pm8921_bms_chip -
73 * @bms_output_lock: lock to prevent concurrent bms reads
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080074 *
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -070075 * @last_ocv_uv_mutex: mutex to protect simultaneous invocations of calculate
76 * state of charge, note that last_ocv_uv could be
77 * changed as soc is adjusted. This mutex protects
78 * simultaneous updates of last_ocv_uv as well. This mutex
79 * also protects changes to *_at_100 variables used in
80 * faking 100% SOC.
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080081 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082struct pm8921_bms_chip {
83 struct device *dev;
84 struct dentry *dent;
85 unsigned int r_sense;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -070086 unsigned int v_cutoff;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 unsigned int fcc;
88 struct single_row_lut *fcc_temp_lut;
89 struct single_row_lut *fcc_sf_lut;
90 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -080091 struct sf_lut *pc_sf_lut;
92 struct sf_lut *rbatt_sf_lut;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -070093 int delta_rbatt_mohm;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070094 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -070095 struct delayed_work calib_hkadc_delayed_work;
96 struct mutex calib_mutex;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -070097 unsigned int revision;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -080098 unsigned int xoadc_v0625_usb_present;
99 unsigned int xoadc_v0625_usb_absent;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700100 unsigned int xoadc_v0625;
101 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700102 unsigned int batt_temp_channel;
103 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700104 unsigned int ref625mv_channel;
105 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -0700106 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
108 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800109 struct mutex bms_output_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700110 struct single_row_lut *adjusted_fcc_temp_lut;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -0700111 unsigned int charging_began;
112 unsigned int start_percent;
113 unsigned int end_percent;
David Keitel35e11872012-02-17 17:40:42 -0800114 enum battery_type batt_type;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800115 uint16_t ocv_reading_at_100;
116 int cc_reading_at_100;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -0800117 int max_voltage_uv;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800118
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800119 int default_rbatt_mohm;
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700120 int amux_2_trim_delta;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700121 uint16_t prev_last_good_ocv_raw;
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -0700122 unsigned int rconn_mohm;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700123 struct mutex last_ocv_uv_mutex;
124 int last_ocv_uv;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700125 int pon_ocv_uv;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -0700126 int last_cc_uah;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -0700127 struct timeval t;
Abhijeet Dharmapurikare88f2462012-04-25 18:22:38 -0700128 int enable_fcc_learning;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -0700129 int shutdown_soc;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700130 int shutdown_soc_timer_expired;
131 struct delayed_work shutdown_soc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132};
133
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700134/*
135 * protects against simultaneous adjustment of ocv based on shutdown soc and
136 * invalidating the shutdown soc
137 */
138static DEFINE_MUTEX(soc_invalidation_mutex);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -0700139static int shutdown_soc_invalid;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140static struct pm8921_bms_chip *the_chip;
141
142#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144#define DEFAULT_CHARGE_CYCLES 0
145
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800146static int last_usb_cal_delta_uv = 1800;
147module_param(last_usb_cal_delta_uv, int, 0644);
148
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700149static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
150static int last_charge_increase;
151module_param(last_chargecycles, int, 0644);
152module_param(last_charge_increase, int, 0644);
153
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154static int last_soc = -EINVAL;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800155static int last_real_fcc_mah = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700156static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700158static int bms_ops_set(const char *val, const struct kernel_param *kp)
159{
160 if (*(int *)kp->arg == -EINVAL)
161 return param_set_int(val, kp);
162 else
163 return 0;
164}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700166static struct kernel_param_ops bms_param_ops = {
167 .set = bms_ops_set,
168 .get = param_get_int,
169};
170
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700171module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
172
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800173/*
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800174 * bms_fake_battery is set in setups where a battery emulator is used instead
175 * of a real battery. This makes the bms driver report a different/fake value
176 * regardless of the calculated state of charge.
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800177 */
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800178static int bms_fake_battery = -EINVAL;
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800179module_param(bms_fake_battery, int, 0644);
180
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800181/* bms_start_XXX and bms_end_XXX are read only */
182static int bms_start_percent;
183static int bms_start_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800184static int bms_start_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800185static int bms_end_percent;
186static int bms_end_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800187static int bms_end_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800188
189static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
190{
191 return -EINVAL;
192}
193
194static struct kernel_param_ops bms_ro_param_ops = {
195 .set = bms_ro_ops_set,
196 .get = param_get_int,
197};
198module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
199module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800200module_param_cb(bms_start_cc_uah, &bms_ro_param_ops, &bms_start_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800201
202module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
203module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800204module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800205
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700206static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
207static void readjust_fcc_table(void)
208{
209 struct single_row_lut *temp, *old;
210 int i, fcc, ratio;
211
212 if (!the_chip->fcc_temp_lut) {
213 pr_err("The static fcc lut table is NULL\n");
214 return;
215 }
216
217 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
218 if (!temp) {
219 pr_err("Cannot allocate memory for adjusted fcc table\n");
220 return;
221 }
222
223 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
224
225 temp->cols = the_chip->fcc_temp_lut->cols;
226 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
227 temp->x[i] = the_chip->fcc_temp_lut->x[i];
228 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800229 temp->y[i] = (ratio * last_real_fcc_mah);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700230 temp->y[i] /= 1000;
231 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
232 temp->x[i], the_chip->fcc_temp_lut->y[i],
233 temp->y[i], ratio);
234 }
235
236 old = the_chip->adjusted_fcc_temp_lut;
237 the_chip->adjusted_fcc_temp_lut = temp;
238 kfree(old);
239}
240
241static int bms_last_real_fcc_set(const char *val,
242 const struct kernel_param *kp)
243{
244 int rc = 0;
245
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800246 if (last_real_fcc_mah == -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700247 rc = param_set_int(val, kp);
248 if (rc) {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800249 pr_err("Failed to set last_real_fcc_mah rc=%d\n", rc);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700250 return rc;
251 }
252 if (last_real_fcc_batt_temp != -EINVAL)
253 readjust_fcc_table();
254 return rc;
255}
256static struct kernel_param_ops bms_last_real_fcc_param_ops = {
257 .set = bms_last_real_fcc_set,
258 .get = param_get_int,
259};
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800260module_param_cb(last_real_fcc_mah, &bms_last_real_fcc_param_ops,
261 &last_real_fcc_mah, 0644);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700262
263static int bms_last_real_fcc_batt_temp_set(const char *val,
264 const struct kernel_param *kp)
265{
266 int rc = 0;
267
268 if (last_real_fcc_batt_temp == -EINVAL)
269 rc = param_set_int(val, kp);
270 if (rc) {
271 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
272 return rc;
273 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800274 if (last_real_fcc_mah != -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700275 readjust_fcc_table();
276 return rc;
277}
278
279static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
280 .set = bms_last_real_fcc_batt_temp_set,
281 .get = param_get_int,
282};
283module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
284 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285
286static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
287{
288 return pm8xxx_read_irq_stat(chip->dev->parent,
289 chip->pmic_bms_irq[irq_id]);
290}
291
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700292static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
293{
294 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
295 dev_dbg(chip->dev, "%s %d\n", __func__,
296 chip->pmic_bms_irq[interrupt]);
297 enable_irq(chip->pmic_bms_irq[interrupt]);
298 }
299}
300
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
302{
303 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700304 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
306 }
307}
308
309static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
310 u8 mask, u8 val)
311{
312 int rc;
313 u8 reg;
314
315 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
316 if (rc) {
317 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
318 return rc;
319 }
320 reg &= ~mask;
321 reg |= val & mask;
322 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
323 if (rc) {
324 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
325 return rc;
326 }
327 return 0;
328}
329
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800330static int usb_chg_plugged_in(void)
331{
Abhijeet Dharmapurikar3da61762012-07-12 15:56:58 -0700332 int val = pm8921_is_usb_chg_plugged_in();
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800333
Abhijeet Dharmapurikar3da61762012-07-12 15:56:58 -0700334 /* treat as if usb is not present in case of error */
335 if (val == -EINVAL)
336 val = 0;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800337
Abhijeet Dharmapurikar3da61762012-07-12 15:56:58 -0700338 return val;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800339}
340
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700341#define HOLD_OREG_DATA BIT(1)
342static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
343{
344 int rc;
345
346 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
347 HOLD_OREG_DATA);
348 if (rc) {
349 pr_err("couldnt lock bms output rc = %d\n", rc);
350 return rc;
351 }
352 return 0;
353}
354
355static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
356{
357 int rc;
358
359 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
360 if (rc) {
361 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
362 return rc;
363 }
364 return 0;
365}
366
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367#define SELECT_OUTPUT_DATA 0x1C
368#define SELECT_OUTPUT_TYPE_SHIFT 2
369#define OCV_FOR_RBATT 0x0
370#define VSENSE_FOR_RBATT 0x1
371#define VBATT_FOR_RBATT 0x2
372#define CC_MSB 0x3
373#define CC_LSB 0x4
374#define LAST_GOOD_OCV_VALUE 0x5
375#define VSENSE_AVG 0x6
376#define VBATT_AVG 0x7
377
378static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
379 int16_t *result)
380{
381 int rc;
382 u8 reg;
383
384 if (!result) {
385 pr_err("result pointer null\n");
386 return -EINVAL;
387 }
388 *result = 0;
389 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
390 pr_err("invalid type %d asked to read\n", type);
391 return -EINVAL;
392 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700393
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
395 type << SELECT_OUTPUT_TYPE_SHIFT);
396 if (rc) {
397 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
398 type, rc);
399 return rc;
400 }
401
402 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
403 if (rc) {
404 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
405 type, rc);
406 return rc;
407 }
408 *result = reg;
409 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
410 if (rc) {
411 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
412 type, rc);
413 return rc;
414 }
415 *result |= reg << 8;
416 pr_debug("type %d result %x", type, *result);
417 return 0;
418}
419
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700420#define V_PER_BIT_MUL_FACTOR 97656
421#define V_PER_BIT_DIV_FACTOR 1000
422#define XOADC_INTRINSIC_OFFSET 0x6000
423static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700424{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700425 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700426 return 0;
427
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700428 return (a - XOADC_INTRINSIC_OFFSET)
429 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700430}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700432#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700433#define VBATT_MUL_FACTOR 3
434static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800435 int usb_chg, unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700436{
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800437 s64 numerator, denominator;
438 int local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700439
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700440 if (uv == 0)
441 return 0;
442
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800443 /* dont adjust if not calibrated */
444 if (chip->xoadc_v0625 == 0 || chip->xoadc_v125 == 0) {
445 pr_debug("No cal yet return %d\n", VBATT_MUL_FACTOR * uv);
446 return VBATT_MUL_FACTOR * uv;
447 }
448
449 if (usb_chg)
450 local_delta = last_usb_cal_delta_uv;
451 else
452 local_delta = 0;
453
454 pr_debug("using delta = %d\n", local_delta);
455 numerator = ((s64)uv - chip->xoadc_v0625 - local_delta)
456 * XOADC_CALIB_UV;
457 denominator = (s64)chip->xoadc_v125 - chip->xoadc_v0625 - local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700458 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700459 return uv * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800460 return (XOADC_CALIB_UV + local_delta + div_s64(numerator, denominator))
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700461 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700462}
463
Abhijeet Dharmapurikar5adb2532012-06-21 16:15:43 -0700464#define CC_RESOLUTION_N 868056
465#define CC_RESOLUTION_D 10000
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700466
467static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
468{
Abhijeet Dharmapurikar5adb2532012-06-21 16:15:43 -0700469 return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D);
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700470}
471
472#define CC_READING_TICKS 55
473#define SLEEP_CLK_HZ 32768
474#define SECONDS_PER_HOUR 3600
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800475/**
476 * ccmicrovolt_to_nvh -
477 * @cc_uv: coulumb counter converted to uV
478 *
479 * RETURNS: coulumb counter based charge in nVh
480 * (nano Volt Hour)
481 */
482static s64 ccmicrovolt_to_nvh(s64 cc_uv)
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700483{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800484 return div_s64(cc_uv * CC_READING_TICKS * 1000,
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700485 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
486}
487
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700488/* returns the signed value read from the hardware */
489static int read_cc(struct pm8921_bms_chip *chip, int *result)
490{
491 int rc;
492 uint16_t msw, lsw;
493
494 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
495 if (rc) {
496 pr_err("fail to read CC_LSB rc = %d\n", rc);
497 return rc;
498 }
499 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
500 if (rc) {
501 pr_err("fail to read CC_MSB rc = %d\n", rc);
502 return rc;
503 }
504 *result = msw << 16 | lsw;
505 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
506 return 0;
507}
508
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700509static int adjust_xo_vbatt_reading_for_mbg(struct pm8921_bms_chip *chip,
510 int result)
511{
512 int64_t numerator;
513 int64_t denominator;
514
515 if (chip->amux_2_trim_delta == 0)
516 return result;
517
518 numerator = (s64)result * 1000000;
519 denominator = (1000000 + (410 * (s64)chip->amux_2_trim_delta));
520 return div_s64(numerator, denominator);
521}
522
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800523static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800524 int usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800525 uint16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700526{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700527 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800528 pr_debug("raw = %04x vbatt = %u\n", reading, *result);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800529 *result = adjust_xo_vbatt_reading(chip, usb_chg, *result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800530 pr_debug("after adj vbatt = %u\n", *result);
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700531 *result = adjust_xo_vbatt_reading_for_mbg(chip, *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532 return 0;
533}
534
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800535static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
536 int16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537{
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800538 *result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800539 pr_debug("raw = %04x vsense = %d\n", reading, *result);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800540 *result = pm8xxx_cc_adjust_for_gain(*result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800541 pr_debug("after adj vsense = %d\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 return 0;
543}
544
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700545static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
546{
547 int rc;
548 int16_t reading;
549
550 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
551 if (rc) {
552 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
553 return rc;
554 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800555
556 convert_vsense_to_uv(chip, reading, result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700557 return 0;
558}
559
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
561{
562 if (y0 == y1 || x == x0)
563 return y0;
564 if (x1 == x0 || x == x1)
565 return y1;
566
567 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
568}
569
570static int interpolate_single_lut(struct single_row_lut *lut, int x)
571{
572 int i, result;
573
574 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700575 pr_debug("x %d less than known range return y = %d lut = %pS\n",
576 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577 return lut->y[0];
578 }
579 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700580 pr_debug("x %d more than known range return y = %d lut = %pS\n",
581 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700582 return lut->y[lut->cols - 1];
583 }
584
585 for (i = 0; i < lut->cols; i++)
586 if (x <= lut->x[i])
587 break;
588 if (x == lut->x[i]) {
589 result = lut->y[i];
590 } else {
591 result = linear_interpolate(
592 lut->y[i - 1],
593 lut->x[i - 1],
594 lut->y[i],
595 lut->x[i],
596 x);
597 }
598 return result;
599}
600
601static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
602{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800603 /* batt_temp is in tenths of degC - convert it to degC for lookups */
604 batt_temp = batt_temp/10;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700605 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
606}
607
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700608static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
609{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800610 /* batt_temp is in tenths of degC - convert it to degC for lookups */
611 batt_temp = batt_temp/10;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700612 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
613}
614
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700615static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
616 int cycles)
617{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700618 /*
619 * sf table could be null when no battery aging data is available, in
620 * that case return 100%
621 */
622 if (chip->fcc_sf_lut)
623 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
624 else
625 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626}
627
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800628static int interpolate_scalingfactor(struct pm8921_bms_chip *chip,
629 struct sf_lut *sf_lut,
630 int row_entry, int pc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700631{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700632 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700633 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700634 int row1 = 0;
635 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636
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 */
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800641 if (!sf_lut)
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700642 return 100;
643
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800644 rows = sf_lut->rows;
645 cols = sf_lut->cols;
646 if (pc > sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700647 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648 row1 = 0;
649 row2 = 0;
650 }
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800651 if (pc < sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700652 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653 row1 = rows - 1;
654 row2 = rows - 1;
655 }
656 for (i = 0; i < rows; i++) {
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800657 if (pc == sf_lut->percent[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 row1 = i;
659 row2 = i;
660 break;
661 }
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800662 if (pc > sf_lut->percent[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663 row1 = i - 1;
664 row2 = i;
665 break;
666 }
667 }
668
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800669 if (row_entry < sf_lut->row_entries[0])
670 row_entry = sf_lut->row_entries[0];
671 if (row_entry > sf_lut->row_entries[cols - 1])
672 row_entry = sf_lut->row_entries[cols - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673
674 for (i = 0; i < cols; i++)
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800675 if (row_entry <= sf_lut->row_entries[i])
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700676 break;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800677 if (row_entry == sf_lut->row_entries[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678 scalefactor = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800679 sf_lut->sf[row1][i],
680 sf_lut->percent[row1],
681 sf_lut->sf[row2][i],
682 sf_lut->percent[row2],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700683 pc);
684 return scalefactor;
685 }
686
687 scalefactorrow1 = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800688 sf_lut->sf[row1][i - 1],
689 sf_lut->row_entries[i - 1],
690 sf_lut->sf[row1][i],
691 sf_lut->row_entries[i],
692 row_entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700693
694 scalefactorrow2 = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800695 sf_lut->sf[row2][i - 1],
696 sf_lut->row_entries[i - 1],
697 sf_lut->sf[row2][i],
698 sf_lut->row_entries[i],
699 row_entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700700
701 scalefactor = linear_interpolate(
702 scalefactorrow1,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800703 sf_lut->percent[row1],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700704 scalefactorrow2,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800705 sf_lut->percent[row2],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700706 pc);
707
708 return scalefactor;
709}
710
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700711static int is_between(int left, int right, int value)
712{
713 if (left >= right && left >= value && value >= right)
714 return 1;
715 if (left <= right && left <= value && value <= right)
716 return 1;
717
718 return 0;
719}
720
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700721/* get ocv given a soc -- reverse lookup */
722static int interpolate_ocv(struct pm8921_bms_chip *chip,
723 int batt_temp_degc, int pc)
724{
725 int i, ocvrow1, ocvrow2, ocv;
726 int rows, cols;
727 int row1 = 0;
728 int row2 = 0;
729
730 rows = chip->pc_temp_ocv_lut->rows;
731 cols = chip->pc_temp_ocv_lut->cols;
732 if (pc > chip->pc_temp_ocv_lut->percent[0]) {
733 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
734 row1 = 0;
735 row2 = 0;
736 }
737 if (pc < chip->pc_temp_ocv_lut->percent[rows - 1]) {
738 pr_debug("pc %d less than known pc ranges for sf\n", pc);
739 row1 = rows - 1;
740 row2 = rows - 1;
741 }
742 for (i = 0; i < rows; i++) {
743 if (pc == chip->pc_temp_ocv_lut->percent[i]) {
744 row1 = i;
745 row2 = i;
746 break;
747 }
748 if (pc > chip->pc_temp_ocv_lut->percent[i]) {
749 row1 = i - 1;
750 row2 = i;
751 break;
752 }
753 }
754
755 if (batt_temp_degc < chip->pc_temp_ocv_lut->temp[0])
756 batt_temp_degc = chip->pc_temp_ocv_lut->temp[0];
757 if (batt_temp_degc > chip->pc_temp_ocv_lut->temp[cols - 1])
758 batt_temp_degc = chip->pc_temp_ocv_lut->temp[cols - 1];
759
760 for (i = 0; i < cols; i++)
761 if (batt_temp_degc <= chip->pc_temp_ocv_lut->temp[i])
762 break;
763 if (batt_temp_degc == chip->pc_temp_ocv_lut->temp[i]) {
764 ocv = linear_interpolate(
765 chip->pc_temp_ocv_lut->ocv[row1][i],
766 chip->pc_temp_ocv_lut->percent[row1],
767 chip->pc_temp_ocv_lut->ocv[row2][i],
768 chip->pc_temp_ocv_lut->percent[row2],
769 pc);
770 return ocv;
771 }
772
773 ocvrow1 = linear_interpolate(
774 chip->pc_temp_ocv_lut->ocv[row1][i - 1],
775 chip->pc_temp_ocv_lut->temp[i - 1],
776 chip->pc_temp_ocv_lut->ocv[row1][i],
777 chip->pc_temp_ocv_lut->temp[i],
778 batt_temp_degc);
779
780 ocvrow2 = linear_interpolate(
781 chip->pc_temp_ocv_lut->ocv[row2][i - 1],
782 chip->pc_temp_ocv_lut->temp[i - 1],
783 chip->pc_temp_ocv_lut->ocv[row2][i],
784 chip->pc_temp_ocv_lut->temp[i],
785 batt_temp_degc);
786
787 ocv = linear_interpolate(
788 ocvrow1,
789 chip->pc_temp_ocv_lut->percent[row1],
790 ocvrow2,
791 chip->pc_temp_ocv_lut->percent[row2],
792 pc);
793
794 return ocv;
795}
796
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700797static int interpolate_pc(struct pm8921_bms_chip *chip,
798 int batt_temp, int ocv)
799{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700800 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801 int rows = chip->pc_temp_ocv_lut->rows;
802 int cols = chip->pc_temp_ocv_lut->cols;
803
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800804 /* batt_temp is in tenths of degC - convert it to degC for lookups */
805 batt_temp = batt_temp/10;
806
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700807 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700808 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700809 batt_temp = chip->pc_temp_ocv_lut->temp[0];
810 }
811 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700812 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700813 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
814 }
815
816 for (j = 0; j < cols; j++)
817 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
818 break;
819 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
820 /* found an exact match for temp in the table */
821 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
822 return chip->pc_temp_ocv_lut->percent[0];
823 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
824 return chip->pc_temp_ocv_lut->percent[rows - 1];
825 for (i = 0; i < rows; i++) {
826 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
827 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
828 return
829 chip->pc_temp_ocv_lut->percent[i];
830 pc = linear_interpolate(
831 chip->pc_temp_ocv_lut->percent[i],
832 chip->pc_temp_ocv_lut->ocv[i][j],
833 chip->pc_temp_ocv_lut->percent[i - 1],
834 chip->pc_temp_ocv_lut->ocv[i - 1][j],
835 ocv);
836 return pc;
837 }
838 }
839 }
840
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700841 /*
842 * batt_temp is within temperature for
843 * column j-1 and j
844 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
846 return chip->pc_temp_ocv_lut->percent[0];
847 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
848 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700849
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700850 pcj_minus_one = 0;
851 pcj = 0;
852 for (i = 0; i < rows-1; i++) {
853 if (pcj == 0
854 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
855 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
856 pcj = linear_interpolate(
857 chip->pc_temp_ocv_lut->percent[i],
858 chip->pc_temp_ocv_lut->ocv[i][j],
859 chip->pc_temp_ocv_lut->percent[i + 1],
860 chip->pc_temp_ocv_lut->ocv[i+1][j],
861 ocv);
862 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700863
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700864 if (pcj_minus_one == 0
865 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
866 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700868 pcj_minus_one = linear_interpolate(
869 chip->pc_temp_ocv_lut->percent[i],
870 chip->pc_temp_ocv_lut->ocv[i][j-1],
871 chip->pc_temp_ocv_lut->percent[i + 1],
872 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
873 ocv);
874 }
875
876 if (pcj && pcj_minus_one) {
877 pc = linear_interpolate(
878 pcj_minus_one,
879 chip->pc_temp_ocv_lut->temp[j-1],
880 pcj,
881 chip->pc_temp_ocv_lut->temp[j],
882 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883 return pc;
884 }
885 }
886
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700887 if (pcj)
888 return pcj;
889
890 if (pcj_minus_one)
891 return pcj_minus_one;
892
893 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
894 ocv, batt_temp);
895 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896}
897
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800898#define BMS_MODE_BIT BIT(6)
899#define EN_VBAT_BIT BIT(5)
900#define OVERRIDE_MODE_DELAY_MS 20
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -0700901int override_mode_simultaneous_battery_voltage_and_current(int *ibat_ua,
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800902 int *vbat_uv)
903{
904 int16_t vsense_raw;
905 int16_t vbat_raw;
906 int vsense_uv;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800907 int usb_chg;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800908
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800909 mutex_lock(&the_chip->bms_output_lock);
910
911 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x00);
912 pm_bms_masked_write(the_chip, BMS_CONTROL,
913 BMS_MODE_BIT | EN_VBAT_BIT, BMS_MODE_BIT | EN_VBAT_BIT);
914
915 msleep(OVERRIDE_MODE_DELAY_MS);
916
917 pm_bms_lock_output_data(the_chip);
918 pm_bms_read_output_data(the_chip, VSENSE_AVG, &vsense_raw);
919 pm_bms_read_output_data(the_chip, VBATT_AVG, &vbat_raw);
920 pm_bms_unlock_output_data(the_chip);
921 pm_bms_masked_write(the_chip, BMS_CONTROL,
922 BMS_MODE_BIT | EN_VBAT_BIT, 0);
923
924 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x0B);
925
926 mutex_unlock(&the_chip->bms_output_lock);
927
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800928 usb_chg = usb_chg_plugged_in();
929
930 convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800931 convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
932 *ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
933
934 pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
935 " ibat_ua = %d vbat_uv = %d\n",
936 (uint16_t)vsense_raw, (uint16_t)vbat_raw,
937 *ibat_ua, *vbat_uv);
938 return 0;
939}
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800940
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700941#define MBG_TRANSIENT_ERROR_RAW 51
942static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip,
943 struct pm8921_soc_params *raw)
944{
945 /* in 8921 parts the PON ocv is taken when the MBG is not settled.
946 * decrease the pon ocv by 15mV raw value to account for it
947 * Since a 1/3rd of vbatt is supplied to the adc the raw value
948 * needs to be adjusted by 5mV worth bits
949 */
950 if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW)
951 raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
952}
953
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800954static int read_soc_params_raw(struct pm8921_bms_chip *chip,
955 struct pm8921_soc_params *raw)
956{
957 int usb_chg;
958
959 mutex_lock(&chip->bms_output_lock);
960 pm_bms_lock_output_data(chip);
961
962 pm_bms_read_output_data(chip,
963 LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
964 read_cc(chip, &raw->cc);
965
966 pm_bms_unlock_output_data(chip);
967 mutex_unlock(&chip->bms_output_lock);
968
969 usb_chg = usb_chg_plugged_in();
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700970
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700971 if (chip->prev_last_good_ocv_raw == 0) {
972 chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
973 adjust_pon_ocv_raw(chip, raw);
974 convert_vbatt_raw_to_uv(chip, usb_chg,
975 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700976 chip->last_ocv_uv = raw->last_good_ocv_uv;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700977 pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700978 } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700979 chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
980 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800981 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700982 chip->last_ocv_uv = raw->last_good_ocv_uv;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -0700983 /* forget the old cc value upon ocv */
984 chip->last_cc_uah = 0;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700985 } else {
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700986 raw->last_good_ocv_uv = chip->last_ocv_uv;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700987 }
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800988
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700989 /* fake a high OCV if we are just done charging */
990 if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
991 chip->ocv_reading_at_100 = 0;
992 chip->cc_reading_at_100 = 0;
993 } else {
994 /*
995 * force 100% ocv by selecting the highest voltage the
996 * battery could ever reach
997 */
998 raw->last_good_ocv_uv = chip->max_voltage_uv;
999 chip->last_ocv_uv = chip->max_voltage_uv;
1000 }
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001001 pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
1002 pr_debug("1p25 = %duV\n", chip->xoadc_v125);
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001003 pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
1004 raw->last_good_ocv_raw, raw->last_good_ocv_uv);
1005 pr_debug("cc_raw= 0x%x\n", raw->cc);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001006 return 0;
1007}
1008
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001009static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp)
1010{
1011 int rbatt, scalefactor;
1012
Abhijeet Dharmapurikar0711c522012-05-22 18:45:25 -07001013 rbatt = chip->default_rbatt_mohm;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001014 pr_debug("rbatt before scaling = %d\n", rbatt);
1015 if (chip->rbatt_sf_lut == NULL) {
1016 pr_debug("RBATT = %d\n", rbatt);
1017 return rbatt;
1018 }
Abhijeet Dharmapurikar12a891c2012-04-12 23:52:06 -07001019 /* Convert the batt_temp to DegC from deciDegC */
1020 batt_temp = batt_temp / 10;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001021 scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut,
1022 batt_temp, soc_rbatt);
1023 pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
1024 scalefactor, batt_temp, soc_rbatt);
1025 rbatt = (rbatt * scalefactor) / 100;
1026
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -07001027 rbatt += the_chip->rconn_mohm;
1028 pr_debug("adding rconn_mohm = %d rbatt = %d\n",
1029 the_chip->rconn_mohm, rbatt);
1030
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07001031 if (is_between(20, 10, soc_rbatt))
1032 rbatt = rbatt
1033 + ((20 - soc_rbatt) * chip->delta_rbatt_mohm) / 10;
1034 else
1035 if (is_between(10, 0, soc_rbatt))
1036 rbatt = rbatt + chip->delta_rbatt_mohm;
1037
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001038 pr_debug("RBATT = %d\n", rbatt);
1039 return rbatt;
1040}
1041
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001042static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001043 int chargecycles)
1044{
1045 int initfcc, result, scalefactor = 0;
1046
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001047 if (chip->adjusted_fcc_temp_lut == NULL) {
1048 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001049
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001050 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001051
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001052 /* Multiply the initial FCC value by the scale factor. */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001053 result = (initfcc * scalefactor * 1000) / 100;
1054 pr_debug("fcc = %d uAh\n", result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001055 return result;
1056 } else {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001057 return 1000 * interpolate_fcc_adjusted(chip, batt_temp);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001058 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001059}
1060
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001061static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
1062{
1063 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001064 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001065
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001066 rc = pm8xxx_adc_read(chip->vbat_channel, &result);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001067 if (rc) {
1068 pr_err("error reading adc channel = %d, rc = %d\n",
1069 chip->vbat_channel, rc);
1070 return rc;
1071 }
1072 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
1073 result.measurement);
1074 *uvolts = (int)result.physical;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001075 return 0;
1076}
1077
1078static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
1079{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001080 int vbatt, rbatt, ibatt_ua, rc;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001081
1082 rc = get_battery_uvolts(chip, &vbatt);
1083 if (rc) {
1084 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001085 return rc;
1086 }
1087
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001088 rc = pm8921_bms_get_battery_current(&ibatt_ua);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001089 if (rc) {
1090 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001091 return rc;
1092 }
1093
Abhijeet Dharmapurikar0711c522012-05-22 18:45:25 -07001094 rbatt = chip->default_rbatt_mohm;
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001095 *ocv = vbatt + (ibatt_ua * rbatt)/1000;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001096 return 0;
1097}
1098
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001099static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
1100 int chargecycles)
1101{
1102 int pc, scalefactor;
1103
1104 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
1105 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
1106 pc, ocv_uv, batt_temp);
1107
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001108 scalefactor = interpolate_scalingfactor(chip,
1109 chip->pc_sf_lut, chargecycles, pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001110 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
1111
1112 /* Multiply the initial FCC value by the scale factor. */
1113 pc = (pc * scalefactor) / 100;
1114 return pc;
1115}
1116
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001117/**
1118 * calculate_cc_uah -
1119 * @chip: the bms chip pointer
1120 * @cc: the cc reading from bms h/w
1121 * @val: return value
1122 * @coulumb_counter: adjusted coulumb counter for 100%
1123 *
1124 * RETURNS: in val pointer coulumb counter based charger in uAh
1125 * (micro Amp hour)
1126 */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001127static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001128{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001129 int64_t cc_voltage_uv, cc_nvh, cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001130
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001131 cc_voltage_uv = cc;
1132 cc_voltage_uv -= chip->cc_reading_at_100;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001133 pr_debug("cc = %d. after subtracting 0x%x cc = %lld\n",
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001134 cc, chip->cc_reading_at_100,
1135 cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07001136 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001137 cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001138 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001139 cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
1140 pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
1141 cc_uah = div_s64(cc_nvh, chip->r_sense);
1142 *val = cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001143}
1144
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001145static int calculate_uuc_uah_at_given_current(struct pm8921_bms_chip *chip,
1146 int batt_temp, int chargecycles,
1147 int rbatt, int fcc_uah, int i_ma)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001148{
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001149 int unusable_uv, pc_unusable, uuc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001150
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001151 /* calculate unusable charge with itest */
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001152 unusable_uv = (rbatt * i_ma) + (chip->v_cutoff * 1000);
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001153 pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);
1154 uuc = (fcc_uah * pc_unusable) / 100;
1155 pr_debug("For i_ma = %d, unusable_uv = %d unusable_pc = %d uuc = %d\n",
1156 i_ma, unusable_uv, pc_unusable, uuc);
1157 return uuc;
1158}
1159
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001160static void calculate_iavg_ua(struct pm8921_bms_chip *chip, int cc_uah,
1161 int *iavg_ua, int *delta_time_us)
1162{
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001163 int delta_cc_uah;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001164 struct timeval now;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001165
1166 delta_cc_uah = cc_uah - chip->last_cc_uah;
1167 do_gettimeofday(&now);
1168 if (chip->t.tv_sec != 0) {
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001169 *delta_time_us = (now.tv_sec - chip->t.tv_sec) * USEC_PER_SEC
1170 + now.tv_usec - chip->t.tv_usec;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001171 } else {
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001172 /* calculation for the first time */
1173 *delta_time_us = 0;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001174 }
1175
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001176 if (*delta_time_us != 0)
1177 *iavg_ua = div_s64((s64)delta_cc_uah * 3600 * 1000000,
1178 *delta_time_us);
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001179 else
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001180 *iavg_ua = 0;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001181
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001182 pr_debug("t.tv_sec = %d, now.tv_sec = %d delta_us = %d iavg_ua = %d\n",
1183 (int)chip->t.tv_sec, (int)now.tv_sec,
1184 *delta_time_us, (int)*iavg_ua);
1185 /* remember cc_uah */
1186 chip->last_cc_uah = cc_uah;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001187
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001188 /* remember this time */
1189 chip->t = now;
1190}
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001191
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001192#define IAVG_SAMPLES 16
1193#define CHARGING_IAVG_MA 300
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001194static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
1195 int rbatt, int fcc_uah, int cc_uah,
1196 int soc_rbatt, int batt_temp, int chargecycles,
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001197 int iavg_ua, int delta_time_us)
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001198{
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001199 int uuc_uah_iavg;
1200 int i;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001201 int iavg_ma = iavg_ua / 1000;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001202 static int iavg_samples[IAVG_SAMPLES];
1203 static int iavg_index;
1204 static int iavg_num_samples;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001205
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001206 if (delta_time_us == 0) {
1207 /*
1208 * this means uuc is being for the first time,
1209 * iavg_ua will be 0 which will lead to unreasonably low uuc
1210 * hence use the instantenous current instead of iavg_ua
1211 * value for the first time
1212 */
1213 pm8921_bms_get_battery_current(&iavg_ua);
1214 iavg_ma = iavg_ua / 1000;
1215 }
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001216
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001217 /* if the time since last sample is small ignore it */
1218 if (delta_time_us == 0 || delta_time_us > USEC_PER_SEC) {
1219 /*
1220 * if we are charging use a nominal avg current so that we keep
1221 * a reasonable UUC while charging
1222 */
1223 if (iavg_ma < 0)
1224 iavg_ma = CHARGING_IAVG_MA;
1225 iavg_samples[iavg_index] = iavg_ma;
1226 iavg_index = (iavg_index + 1) % IAVG_SAMPLES;
1227 iavg_num_samples++;
1228 if (iavg_num_samples >= IAVG_SAMPLES)
1229 iavg_num_samples = IAVG_SAMPLES;
1230 }
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001231
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001232 /* now that this sample is added calcualte the average */
1233 iavg_ma = 0;
1234 if (iavg_num_samples != 0) {
1235 for (i = 0; i < iavg_num_samples; i++) {
1236 pr_debug("iavg_samples[%d] = %d\n", i, iavg_samples[i]);
1237 iavg_ma += iavg_samples[i];
1238 }
1239
1240 iavg_ma = DIV_ROUND_CLOSEST(iavg_ma, iavg_num_samples);
1241 }
1242
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001243 uuc_uah_iavg = calculate_uuc_uah_at_given_current(chip,
1244 batt_temp, chargecycles,
1245 rbatt, fcc_uah, iavg_ma);
1246 pr_debug("iavg = %d uuc_iavg = %d\n", iavg_ma, uuc_uah_iavg);
1247
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001248 return uuc_uah_iavg;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001249}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -07001250
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001251/* calculate remainging charge at the time of ocv */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001252static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001253 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001254 int fcc_uah, int batt_temp,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001255 int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001256{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001257 int ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001258
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001259 ocv = raw->last_good_ocv_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001260 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001261 pr_debug("ocv = %d pc = %d\n", ocv, pc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001262 return (fcc_uah * pc) / 100;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001263}
1264
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001265static void calculate_soc_params(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001266 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001267 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001268 int *fcc_uah,
1269 int *unusable_charge_uah,
1270 int *remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001271 int *cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001272 int *rbatt,
1273 int *iavg_ua,
1274 int *delta_time_us)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001275{
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001276 int soc_rbatt;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001277
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001278 *fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
1279 pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
1280 *fcc_uah, batt_temp, chargecycles);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001281
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001282
1283 /* calculate remainging charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001284 *remaining_charge_uah = calculate_remaining_charge_uah(chip, raw,
1285 *fcc_uah, batt_temp, chargecycles);
1286 pr_debug("RC = %uuAh\n", *remaining_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001287
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001288 /* calculate cc micro_volt_hour */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001289 calculate_cc_uah(chip, raw->cc, cc_uah);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001290 pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001291 *cc_uah, raw->cc,
1292 (int64_t)raw->cc - chip->cc_reading_at_100,
1293 chip->cc_reading_at_100);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001294
1295 soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
1296 if (soc_rbatt < 0)
1297 soc_rbatt = 0;
1298 *rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
1299
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001300 calculate_iavg_ua(chip, *cc_uah, iavg_ua, delta_time_us);
1301
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001302 *unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt,
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001303 *fcc_uah, *cc_uah, soc_rbatt,
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001304 batt_temp, chargecycles, *iavg_ua,
1305 *delta_time_us);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001306 pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001307}
1308
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001309static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001310 struct pm8921_soc_params *raw,
1311 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001312 int *ret_fcc_uah)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001313{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001314 int fcc_uah, unusable_charge_uah;
1315 int remaining_charge_uah;
1316 int cc_uah;
1317 int real_fcc_uah;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001318 int rbatt;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001319 int iavg_ua;
1320 int delta_time_us;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001321
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001322 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1323 &fcc_uah,
1324 &unusable_charge_uah,
1325 &remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001326 &cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001327 &rbatt,
1328 &iavg_ua,
1329 &delta_time_us);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001330
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001331 real_fcc_uah = remaining_charge_uah - cc_uah;
1332 *ret_fcc_uah = fcc_uah;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001333 pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001334 real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
1335 return real_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001336}
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001337
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -07001338int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
1339 int *vbat_uv)
1340{
1341 int rc;
1342
1343 if (the_chip == NULL) {
1344 pr_err("Called too early\n");
1345 return -EINVAL;
1346 }
1347
1348 if (pm8921_is_batfet_closed()) {
1349 return override_mode_simultaneous_battery_voltage_and_current(
1350 ibat_ua,
1351 vbat_uv);
1352 } else {
1353 pr_debug("batfet is open using separate vbat and ibat meas\n");
1354 rc = get_battery_uvolts(the_chip, vbat_uv);
1355 if (rc < 0) {
1356 pr_err("adc vbat failed err = %d\n", rc);
1357 return rc;
1358 }
1359 rc = pm8921_bms_get_battery_current(ibat_ua);
1360 if (rc < 0) {
1361 pr_err("bms ibat failed err = %d\n", rc);
1362 return rc;
1363 }
1364 }
1365
1366 return 0;
1367}
1368EXPORT_SYMBOL(pm8921_bms_get_simultaneous_battery_voltage_and_current);
1369
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001370static int bound_soc(int soc)
1371{
1372 soc = max(0, soc);
1373 soc = min(100, soc);
1374 return soc;
1375}
1376
1377static int last_soc_est = -EINVAL;
1378static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp,
1379 int rbatt , int fcc_uah, int uuc_uah, int cc_uah)
1380{
1381 int ibat_ua = 0, vbat_uv = 0;
1382 int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
1383 int delta_ocv_uv = 0;
1384 int n = 0;
1385 int rc_new_uah = 0;
1386 int pc_new = 0;
1387 int soc_new = 0;
1388 int m = 0;
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -07001389 int rc = 0;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001390
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -07001391 rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(
1392 &ibat_ua,
1393 &vbat_uv);
1394 if (rc < 0) {
1395 pr_err("simultaneous vbat ibat failed err = %d\n", rc);
1396 goto out;
1397 }
1398
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001399
1400 if (ibat_ua < 0)
1401 goto out;
1402 ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;
1403 pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);
1404 soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,
1405 (s64)fcc_uah - uuc_uah);
1406 soc_est = bound_soc(soc_est);
1407
1408 /*
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001409 * do not adjust
1410 * if soc is same as what bms calculated
1411 * if soc_est is between 45 and 25, this is the flat portion of the
1412 * curve where soc_est is not so accurate. We generally don't want to
1413 * adjust when soc_est is inaccurate except for the cases when soc is
1414 * way far off (higher than 50 or lesser than 20).
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001415 */
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001416 if (soc_est == soc
1417 || (is_between(45, 25, soc_est) && is_between(50, 20, soc)))
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001418 goto out;
1419
1420 if (last_soc_est == -EINVAL)
1421 last_soc_est = soc;
1422
1423 n = min(200, max(1 , soc + soc_est + last_soc_est));
1424 /* remember the last soc_est in last_soc_est */
1425 last_soc_est = soc_est;
1426
1427 pc = calculate_pc(chip, chip->last_ocv_uv,
1428 batt_temp, last_chargecycles);
1429 if (pc > 0) {
1430 pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),
1431 batt_temp, last_chargecycles);
1432 while (pc_new == pc) {
1433 /* start taking 10mV steps */
1434 m = m + 10;
1435 pc_new = calculate_pc(chip,
1436 chip->last_ocv_uv - (m * 1000),
1437 batt_temp, last_chargecycles);
1438 }
1439 } else {
1440 /*
1441 * pc is already at the lowest point,
1442 * assume 1 millivolt translates to 1% pc
1443 */
1444 pc = 1;
1445 pc_new = 0;
1446 m = 1;
1447 }
1448
1449 delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,
1450 n * (pc - pc_new));
1451 chip->last_ocv_uv -= delta_ocv_uv;
1452
1453 if (chip->last_ocv_uv >= chip->max_voltage_uv)
1454 chip->last_ocv_uv = chip->max_voltage_uv;
1455
1456 /* calculate the soc based on this new ocv */
1457 pc_new = calculate_pc(chip, chip->last_ocv_uv,
1458 batt_temp, last_chargecycles);
1459 rc_new_uah = (fcc_uah * pc_new) / 100;
1460 soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);
1461 soc_new = bound_soc(soc_new);
1462
1463 /*
1464 * if soc_new is ZERO force it higher so that phone doesnt report soc=0
1465 * soc = 0 should happen only when soc_est == 0
1466 */
1467 if (soc_new == 0 && soc_est != 0)
1468 soc_new = 1;
1469
1470 soc = soc_new;
1471
1472out:
1473 pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "
1474 "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "
1475 "pc_new = %d, soc_new = %d\n",
1476 ibat_ua, vbat_uv, ocv_est_uv, pc_est,
1477 soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
1478 pc_new, soc_new);
1479
1480 return soc;
1481}
1482
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001483#define IGNORE_SOC_TEMP_DECIDEG 50
1484static void backup_soc(struct pm8921_bms_chip *chip, int batt_temp, int soc)
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001485{
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001486 if (soc == 0)
1487 soc = 0xFF;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001488
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001489 if (batt_temp < IGNORE_SOC_TEMP_DECIDEG)
1490 soc = 0;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001491
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001492 pm8xxx_writeb(the_chip->dev->parent, TEMP_SOC_STORAGE, soc);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001493}
1494
1495static void read_shutdown_soc(struct pm8921_bms_chip *chip)
1496{
1497 int rc;
1498 u8 temp;
1499
1500 rc = pm8xxx_readb(chip->dev->parent, TEMP_SOC_STORAGE, &temp);
1501 if (rc)
1502 pr_err("failed to read addr = %d %d\n", TEMP_SOC_STORAGE, rc);
1503 else
1504 chip->shutdown_soc = temp;
1505
1506 pr_debug("shutdown_soc = %d\n", chip->shutdown_soc);
1507}
1508
1509void pm8921_bms_invalidate_shutdown_soc(void)
1510{
1511 pr_debug("Invalidating shutdown soc - the battery was removed\n");
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001512 mutex_lock(&soc_invalidation_mutex);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001513 shutdown_soc_invalid = 1;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001514 last_soc = -EINVAL;
1515 if (the_chip) {
1516 /* reset to pon ocv undoing what the adjusting did */
1517 if (the_chip->pon_ocv_uv) {
1518 the_chip->last_ocv_uv = the_chip->pon_ocv_uv;
1519 pr_debug("resetting ocv to pon_ocv = %d\n",
1520 the_chip->pon_ocv_uv);
1521 }
1522 }
1523 mutex_unlock(&soc_invalidation_mutex);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001524}
1525EXPORT_SYMBOL(pm8921_bms_invalidate_shutdown_soc);
1526
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001527static int adjust_remaining_charge_for_shutdown_soc(
1528 struct pm8921_bms_chip *chip,
1529 int batt_temp,
1530 int chargecycles,
1531 int fcc_uah,
1532 int uuc_uah,
1533 int cc_uah,
1534 int remaining_charge_uah)
1535{
1536 s64 rc;
1537 int pc;
1538 int ocv, ocv_uv;
1539 int batt_temp_degc = batt_temp / 10;
1540 int new_pc;
1541 int shutdown_soc;
1542
1543 mutex_lock(&soc_invalidation_mutex);
1544 shutdown_soc = chip->shutdown_soc;
1545 /*
1546 * value of zero means the shutdown soc should not be used, the battery
1547 * was removed for extended period, the coincell capacitor could have
1548 * drained
1549 */
1550 if (shutdown_soc == 0) {
1551 rc = remaining_charge_uah;
1552 goto out;
1553 }
1554
1555 /*
1556 * shutdown_soc_invalid means the shutdown soc should not be used,
1557 * the battery was removed for a small period
1558 */
1559 if (shutdown_soc_invalid) {
1560 rc = remaining_charge_uah;
1561 goto out;
1562 }
1563
1564 /* value of 0xFF means shutdown soc was 0% */
1565 if (shutdown_soc == 0xFF)
1566 shutdown_soc = 0;
1567
1568 pr_debug("shutdown_soc = %d forcing it now\n", shutdown_soc);
1569
1570 rc = (s64)shutdown_soc * (fcc_uah - uuc_uah);
1571 rc = div_s64(rc, 100) + cc_uah + uuc_uah;
1572 pc = DIV_ROUND_CLOSEST((int)rc * 100, fcc_uah);
1573 pc = clamp(pc, 0, 100);
1574
1575 ocv = interpolate_ocv(chip, batt_temp_degc, pc);
1576
1577 pr_debug("To force shutdown_soc = %d, rc = %d, pc = %d, ocv mv = %d\n",
1578 shutdown_soc, (int)rc, pc, ocv);
1579 new_pc = interpolate_pc(chip, batt_temp_degc, ocv);
1580 pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv);
1581
1582 while (abs(new_pc - pc) > 1) {
1583 int delta_mv = 5;
1584
1585 if (new_pc > pc)
1586 delta_mv = -1 * delta_mv;
1587
1588 ocv = ocv + delta_mv;
1589 new_pc = interpolate_pc(chip, batt_temp_degc, ocv);
1590 pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv);
1591 }
1592
1593 ocv_uv = ocv * 1000;
1594
1595 chip->pon_ocv_uv = chip->last_ocv_uv;
1596 chip->last_ocv_uv = ocv_uv;
1597out:
1598 mutex_unlock(&soc_invalidation_mutex);
1599 return (int)rc;
1600}
1601
1602#define SHOW_SHUTDOWN_SOC_MS 30000
1603static void shutdown_soc_work(struct work_struct *work)
1604{
1605 struct pm8921_bms_chip *chip = container_of(work,
1606 struct pm8921_bms_chip, shutdown_soc_work.work);
1607
1608 pr_debug("not forcing shutdown soc anymore\n");
1609 /* it has been 30 seconds since init, no need to show shutdown soc */
1610 chip->shutdown_soc_timer_expired = 1;
1611}
1612
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001613/*
1614 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1615 * - coloumb counter charge
1616 * - unusable charge (due to battery resistance)
1617 * SOC% = (remaining usable charge/ fcc - usable_charge);
1618 */
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001619static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001620 struct pm8921_soc_params *raw,
1621 int batt_temp, int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001622{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001623 int remaining_usable_charge_uah, fcc_uah, unusable_charge_uah;
1624 int remaining_charge_uah, soc;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001625 int cc_uah;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001626 int rbatt;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001627 int iavg_ua;
1628 int delta_time_us;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001629
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001630 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1631 &fcc_uah,
1632 &unusable_charge_uah,
1633 &remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001634 &cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001635 &rbatt,
1636 &iavg_ua,
1637 &delta_time_us);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001638
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001639 if (delta_time_us == 0)
1640 /*
1641 * soc for the first time - use shutdown soc
1642 * to adjust pon ocv
1643 */
1644 remaining_charge_uah = adjust_remaining_charge_for_shutdown_soc(
1645 chip,
1646 batt_temp, chargecycles,
1647 fcc_uah, unusable_charge_uah,
1648 cc_uah, remaining_charge_uah);
1649
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001650 /* calculate remaining usable charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001651 remaining_usable_charge_uah = remaining_charge_uah
1652 - cc_uah
1653 - unusable_charge_uah;
1654
1655 pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
Abhijeet Dharmapurikarbbae88312012-02-14 18:56:42 -08001656 if (fcc_uah - unusable_charge_uah <= 0) {
1657 pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
1658 fcc_uah, unusable_charge_uah);
1659 soc = 0;
1660 } else {
Abhijeet Dharmapurikar4ff213c2012-07-11 21:28:29 -07001661 soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
1662 (fcc_uah - unusable_charge_uah));
Abhijeet Dharmapurikarbbae88312012-02-14 18:56:42 -08001663 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001664
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001665 if (soc > 100)
1666 soc = 100;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001667 pr_debug("SOC = %d%%\n", soc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001668
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001669 if (bms_fake_battery != -EINVAL) {
1670 pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
1671 return bms_fake_battery;
1672 }
1673
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001674 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001675 pr_err("bad rem_usb_chg = %d rem_chg %d,"
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001676 "cc_uah %d, unusb_chg %d\n",
1677 remaining_usable_charge_uah,
1678 remaining_charge_uah,
1679 cc_uah, unusable_charge_uah);
1680
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001681 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001682 "chargecycles = %d, batt_temp = %d"
1683 "fcc = %d soc =%d\n",
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001684 chip->last_ocv_uv, chargecycles, batt_temp,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001685 fcc_uah, soc);
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -08001686 soc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001687 }
1688
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001689 soc = adjust_soc(chip, soc, batt_temp, rbatt,
1690 fcc_uah, unusable_charge_uah, cc_uah);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001691
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001692 if (last_soc == -EINVAL || soc <= last_soc) {
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001693 last_soc = soc;
1694 } else {
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001695 /*
1696 * soc > last_soc
1697 * the device must be charging for reporting a higher soc, if
1698 * not ignore this soc and continue reporting the last_soc
1699 */
1700 if (the_chip->start_percent != -EINVAL)
1701 last_soc = soc;
1702 else
1703 pr_debug("soc = %d reporting last_soc = %d\n", soc,
1704 last_soc);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001705 }
1706
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001707 if (chip->shutdown_soc != 0
1708 && !shutdown_soc_invalid
1709 && !chip->shutdown_soc_timer_expired) {
1710 /*
1711 * force shutdown soc if it is valid and the shutdown soc show
1712 * timer has not expired
1713 */
1714 if (chip->shutdown_soc != 0xFF)
1715 last_soc = chip->shutdown_soc;
1716 else
1717 last_soc = 0;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001718
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001719 pr_debug("Forcing SHUTDOWN_SOC = %d\n", last_soc);
1720 }
1721
1722 backup_soc(chip, batt_temp, last_soc);
1723 pr_debug("Reported SOC = %d\n", last_soc);
1724
1725 return last_soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001726}
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001727
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001728#define MIN_DELTA_625_UV 1000
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001729static void calib_hkadc(struct pm8921_bms_chip *chip)
1730{
1731 int voltage, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001732 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001733 int usb_chg;
1734 int this_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001735
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07001736 mutex_lock(&chip->calib_mutex);
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001737 rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001738 if (rc) {
1739 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07001740 goto out;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001741 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08001742 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001743
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001744 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1745 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001746
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001747 chip->xoadc_v125 = voltage;
1748
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001749 rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001750 if (rc) {
1751 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07001752 goto out;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001753 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08001754 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001755
1756 usb_chg = usb_chg_plugged_in();
1757 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
1758 "usb_chg = %d\n",
1759 result.adc_code, voltage, result.measurement,
1760 usb_chg);
1761
1762 if (usb_chg)
1763 chip->xoadc_v0625_usb_present = voltage;
1764 else
1765 chip->xoadc_v0625_usb_absent = voltage;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001766
1767 chip->xoadc_v0625 = voltage;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001768 if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) {
1769 this_delta = chip->xoadc_v0625_usb_present
1770 - chip->xoadc_v0625_usb_absent;
1771 pr_debug("this_delta= %duV\n", this_delta);
1772 if (this_delta > MIN_DELTA_625_UV)
1773 last_usb_cal_delta_uv = this_delta;
1774 pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n",
1775 chip->xoadc_v0625_usb_present,
1776 chip->xoadc_v0625_usb_absent,
1777 last_usb_cal_delta_uv);
1778 }
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07001779out:
1780 mutex_unlock(&chip->calib_mutex);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001781}
1782
1783static void calibrate_hkadc_work(struct work_struct *work)
1784{
1785 struct pm8921_bms_chip *chip = container_of(work,
1786 struct pm8921_bms_chip, calib_hkadc_work);
1787
1788 calib_hkadc(chip);
1789}
1790
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001791void pm8921_bms_calibrate_hkadc(void)
1792{
1793 schedule_work(&the_chip->calib_hkadc_work);
1794}
1795
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07001796#define HKADC_CALIB_DELAY_MS 600000
1797static void calibrate_hkadc_delayed_work(struct work_struct *work)
1798{
1799 struct pm8921_bms_chip *chip = container_of(work,
1800 struct pm8921_bms_chip,
1801 calib_hkadc_delayed_work.work);
1802
1803 calib_hkadc(chip);
1804 schedule_delayed_work(&chip->calib_hkadc_delayed_work,
1805 round_jiffies_relative(msecs_to_jiffies
1806 (HKADC_CALIB_DELAY_MS)));
1807}
1808
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001809int pm8921_bms_get_vsense_avg(int *result)
1810{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001811 int rc = -EINVAL;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001812
1813 if (the_chip) {
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001814 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001815 pm_bms_lock_output_data(the_chip);
1816 rc = read_vsense_avg(the_chip, result);
1817 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001818 mutex_unlock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001819 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001820
1821 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001822 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001823}
1824EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1825
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001826int pm8921_bms_get_battery_current(int *result_ua)
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001827{
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001828 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001829
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001830 if (!the_chip) {
1831 pr_err("called before initialization\n");
1832 return -EINVAL;
1833 }
1834 if (the_chip->r_sense == 0) {
1835 pr_err("r_sense is zero\n");
1836 return -EINVAL;
1837 }
1838
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001839 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001840 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001841 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001842 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001843 mutex_unlock(&the_chip->bms_output_lock);
1844 pr_debug("vsense=%duV\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001845 /* cast for signed division */
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001846 *result_ua = vsense * 1000 / (int)the_chip->r_sense;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001847 pr_debug("ibat=%duA\n", *result_ua);
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001848 return 0;
1849}
1850EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1851
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001852int pm8921_bms_get_percent_charge(void)
1853{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001854 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001855 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001856 struct pm8921_soc_params raw;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001857 int soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001858
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001859 if (!the_chip) {
1860 pr_err("called before initialization\n");
1861 return -EINVAL;
1862 }
1863
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001864 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001865 if (rc) {
1866 pr_err("error reading adc channel = %d, rc = %d\n",
1867 the_chip->batt_temp_channel, rc);
1868 return rc;
1869 }
1870 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1871 result.measurement);
1872 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001873
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001874 mutex_lock(&the_chip->last_ocv_uv_mutex);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001875 read_soc_params_raw(the_chip, &raw);
1876
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001877 soc = calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001878 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001879 mutex_unlock(&the_chip->last_ocv_uv_mutex);
1880 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001881}
1882EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1883
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001884int pm8921_bms_get_rbatt(void)
1885{
1886 int batt_temp, rc;
1887 struct pm8xxx_adc_chan_result result;
1888 struct pm8921_soc_params raw;
1889 int fcc_uah;
1890 int unusable_charge_uah;
1891 int remaining_charge_uah;
1892 int cc_uah;
1893 int rbatt;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001894 int iavg_ua;
1895 int delta_time_us;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001896
1897 if (!the_chip) {
1898 pr_err("called before initialization\n");
1899 return -EINVAL;
1900 }
1901
1902 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1903 if (rc) {
1904 pr_err("error reading adc channel = %d, rc = %d\n",
1905 the_chip->batt_temp_channel, rc);
1906 return rc;
1907 }
1908 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1909 result.measurement);
1910 batt_temp = (int)result.physical;
1911
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001912 mutex_lock(&the_chip->last_ocv_uv_mutex);
1913
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001914 read_soc_params_raw(the_chip, &raw);
1915
1916 calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles,
1917 &fcc_uah,
1918 &unusable_charge_uah,
1919 &remaining_charge_uah,
1920 &cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001921 &rbatt,
1922 &iavg_ua,
1923 &delta_time_us);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001924 mutex_unlock(&the_chip->last_ocv_uv_mutex);
1925
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001926 return rbatt;
1927}
1928EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt);
1929
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001930int pm8921_bms_get_fcc(void)
1931{
1932 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001933 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001934
1935 if (!the_chip) {
1936 pr_err("called before initialization\n");
1937 return -EINVAL;
1938 }
1939
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001940 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001941 if (rc) {
1942 pr_err("error reading adc channel = %d, rc = %d\n",
1943 the_chip->batt_temp_channel, rc);
1944 return rc;
1945 }
1946 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1947 result.measurement);
1948 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001949 return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001950}
1951EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1952
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001953#define IBAT_TOL_MASK 0x0F
1954#define OCV_TOL_MASK 0xF0
1955#define IBAT_TOL_DEFAULT 0x03
1956#define IBAT_TOL_NOCHG 0x0F
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07001957#define OCV_TOL_DEFAULT 0x20
1958#define OCV_TOL_NO_OCV 0x00
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001959void pm8921_bms_charging_began(void)
1960{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001961 int batt_temp, rc;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001962 struct pm8xxx_adc_chan_result result;
1963 struct pm8921_soc_params raw;
1964
1965 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1966 if (rc) {
1967 pr_err("error reading adc channel = %d, rc = %d\n",
1968 the_chip->batt_temp_channel, rc);
1969 return;
1970 }
1971 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1972 result.measurement);
1973 batt_temp = (int)result.physical;
1974
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001975 mutex_lock(&the_chip->last_ocv_uv_mutex);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001976 read_soc_params_raw(the_chip, &raw);
1977
1978 the_chip->start_percent = calculate_state_of_charge(the_chip, &raw,
1979 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001980 mutex_unlock(&the_chip->last_ocv_uv_mutex);
1981
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001982 bms_start_percent = the_chip->start_percent;
1983 bms_start_ocv_uv = raw.last_good_ocv_uv;
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001984 calculate_cc_uah(the_chip, raw.cc, &bms_start_cc_uah);
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001985 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
1986 IBAT_TOL_MASK, IBAT_TOL_DEFAULT);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001987 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001988}
1989EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1990
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001991#define DELTA_FCC_PERCENT 3
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08001992#define MIN_START_PERCENT_FOR_LEARNING 30
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001993void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001994{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001995 int batt_temp, rc;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001996 struct pm8xxx_adc_chan_result result;
1997 struct pm8921_soc_params raw;
1998
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001999 if (the_chip == NULL)
2000 return;
2001
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002002 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
2003 if (rc) {
2004 pr_err("error reading adc channel = %d, rc = %d\n",
2005 the_chip->batt_temp_channel, rc);
2006 return;
2007 }
2008 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
2009 result.measurement);
2010 batt_temp = (int)result.physical;
2011
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002012 mutex_lock(&the_chip->last_ocv_uv_mutex);
2013
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002014 read_soc_params_raw(the_chip, &raw);
2015
Abhijeet Dharmapurikar57aeb922012-01-25 13:22:26 -08002016 calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah);
2017
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002018 bms_end_ocv_uv = raw.last_good_ocv_uv;
2019
Abhijeet Dharmapurikare88f2462012-04-25 18:22:38 -07002020 if (is_battery_full && the_chip->enable_fcc_learning
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002021 && the_chip->start_percent <= MIN_START_PERCENT_FOR_LEARNING) {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002022 int fcc_uah, new_fcc_uah, delta_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07002023
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002024 new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002025 batt_temp, last_chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002026 &fcc_uah);
2027 delta_fcc_uah = new_fcc_uah - fcc_uah;
2028 if (delta_fcc_uah < 0)
2029 delta_fcc_uah = -delta_fcc_uah;
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002030
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002031 if (delta_fcc_uah * 100 > (DELTA_FCC_PERCENT * fcc_uah)) {
2032 /* new_fcc_uah is outside the scope limit it */
2033 if (new_fcc_uah > fcc_uah)
2034 new_fcc_uah
Abhijeet Dharmapurikar873ffd12012-04-17 00:06:15 -07002035 = (fcc_uah +
2036 (DELTA_FCC_PERCENT * fcc_uah) / 100);
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002037 else
2038 new_fcc_uah
Abhijeet Dharmapurikar873ffd12012-04-17 00:06:15 -07002039 = (fcc_uah -
2040 (DELTA_FCC_PERCENT * fcc_uah) / 100);
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002041
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002042 pr_debug("delta_fcc=%d > %d percent of fcc=%d"
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002043 "restring it to %d\n",
2044 delta_fcc_uah, DELTA_FCC_PERCENT,
2045 fcc_uah, new_fcc_uah);
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002046 }
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08002047
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002048 last_real_fcc_mah = new_fcc_uah/1000;
2049 last_real_fcc_batt_temp = batt_temp;
2050 readjust_fcc_table();
2051
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002052 }
2053
2054 if (is_battery_full) {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002055 the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
2056 the_chip->cc_reading_at_100 = raw.cc;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002057
2058 the_chip->last_ocv_uv = the_chip->max_voltage_uv;
2059 raw.last_good_ocv_uv = the_chip->max_voltage_uv;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07002060 /*
2061 * since we are treating this as an ocv event
2062 * forget the old cc value
2063 */
2064 the_chip->last_cc_uah = 0;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002065 pr_debug("EOC ocv_reading = 0x%x cc = 0x%x\n",
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08002066 the_chip->ocv_reading_at_100,
2067 the_chip->cc_reading_at_100);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07002068 }
2069
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002070 the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
2071 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002072 mutex_unlock(&the_chip->last_ocv_uv_mutex);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002073
2074 bms_end_percent = the_chip->end_percent;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002075
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002076 if (the_chip->end_percent > the_chip->start_percent) {
Abhijeet Dharmapurikar8a113f82012-01-19 10:58:45 -08002077 last_charge_increase +=
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002078 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002079 if (last_charge_increase > 100) {
2080 last_chargecycles++;
2081 last_charge_increase = last_charge_increase % 100;
2082 }
2083 }
2084 pr_debug("end_percent = %u%% last_charge_increase = %d"
2085 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002086 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002087 last_charge_increase,
2088 last_chargecycles);
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08002089 the_chip->start_percent = -EINVAL;
2090 the_chip->end_percent = -EINVAL;
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08002091 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
2092 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002093}
2094EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
2095
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002096int pm8921_bms_stop_ocv_updates(struct pm8921_bms_chip *chip)
2097{
2098 pr_debug("stopping ocv updates\n");
2099 return pm_bms_masked_write(chip, BMS_TOLERANCES,
2100 OCV_TOL_MASK, OCV_TOL_NO_OCV);
2101}
2102EXPORT_SYMBOL_GPL(pm8921_bms_stop_ocv_updates);
2103
2104int pm8921_bms_start_ocv_updates(struct pm8921_bms_chip *chip)
2105{
2106 pr_debug("stopping ocv updates\n");
2107 return pm_bms_masked_write(chip, BMS_TOLERANCES,
2108 OCV_TOL_MASK, OCV_TOL_DEFAULT);
2109}
2110EXPORT_SYMBOL_GPL(pm8921_bms_start_ocv_updates);
2111
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002112static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
2113{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002114 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002115 return IRQ_HANDLED;
2116}
2117
2118static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
2119{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002120 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002121 return IRQ_HANDLED;
2122}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002124static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
2125{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002126 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002127 return IRQ_HANDLED;
2128}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002129
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002130static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
2131{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002132 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002133 return IRQ_HANDLED;
2134}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002136static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
2137{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002138 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002139
2140 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002141 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002142 return IRQ_HANDLED;
2143}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002144
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002145static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
2146{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002147 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002148
2149 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002150 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002151 return IRQ_HANDLED;
2152}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002153
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002154static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
2155{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002156 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002157 return IRQ_HANDLED;
2158}
2159
2160struct pm_bms_irq_init_data {
2161 unsigned int irq_id;
2162 char *name;
2163 unsigned long flags;
2164 irqreturn_t (*handler)(int, void *);
2165};
2166
2167#define BMS_IRQ(_id, _flags, _handler) \
2168{ \
2169 .irq_id = _id, \
2170 .name = #_id, \
2171 .flags = _flags, \
2172 .handler = _handler, \
2173}
2174
2175struct pm_bms_irq_init_data bms_irq_data[] = {
2176 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
2177 pm8921_bms_sbi_write_ok_handler),
2178 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
2179 pm8921_bms_cc_thr_handler),
2180 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
2181 pm8921_bms_vsense_thr_handler),
2182 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
2183 pm8921_bms_vsense_for_r_handler),
2184 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
2185 pm8921_bms_ocv_for_r_handler),
2186 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
2187 pm8921_bms_good_ocv_handler),
2188 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
2189 pm8921_bms_vsense_avg_handler),
2190};
2191
2192static void free_irqs(struct pm8921_bms_chip *chip)
2193{
2194 int i;
2195
2196 for (i = 0; i < PM_BMS_MAX_INTS; i++)
2197 if (chip->pmic_bms_irq[i]) {
2198 free_irq(chip->pmic_bms_irq[i], NULL);
2199 chip->pmic_bms_irq[i] = 0;
2200 }
2201}
2202
2203static int __devinit request_irqs(struct pm8921_bms_chip *chip,
2204 struct platform_device *pdev)
2205{
2206 struct resource *res;
2207 int ret, i;
2208
2209 ret = 0;
2210 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
2211
2212 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2213 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
2214 bms_irq_data[i].name);
2215 if (res == NULL) {
2216 pr_err("couldn't find %s\n", bms_irq_data[i].name);
2217 goto err_out;
2218 }
2219 ret = request_irq(res->start, bms_irq_data[i].handler,
2220 bms_irq_data[i].flags,
2221 bms_irq_data[i].name, chip);
2222 if (ret < 0) {
2223 pr_err("couldn't request %d (%s) %d\n", res->start,
2224 bms_irq_data[i].name, ret);
2225 goto err_out;
2226 }
2227 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
2228 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
2229 }
2230 return 0;
2231
2232err_out:
2233 free_irqs(chip);
2234 return -EINVAL;
2235}
2236
2237#define EN_BMS_BIT BIT(7)
2238#define EN_PON_HS_BIT BIT(0)
2239static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
2240{
2241 int rc;
2242
2243 rc = pm_bms_masked_write(chip, BMS_CONTROL,
2244 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
2245 if (rc) {
2246 pr_err("failed to enable pon and bms addr = %d %d",
2247 BMS_CONTROL, rc);
2248 }
2249
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08002250 /* The charger will call start charge later if usb is present */
2251 pm_bms_masked_write(chip, BMS_TOLERANCES,
2252 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002253 return 0;
2254}
2255
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002256static void check_initial_ocv(struct pm8921_bms_chip *chip)
2257{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002258 int ocv_uv, rc;
2259 int16_t ocv_raw;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002260 int usb_chg;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002261
2262 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002263 * Check if a ocv is available in bms hw,
2264 * if not compute it here at boot time and save it
2265 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002266 */
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002267 ocv_uv = 0;
2268 pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002269 usb_chg = usb_chg_plugged_in();
2270 rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002271 if (rc || ocv_uv == 0) {
2272 rc = adc_based_ocv(chip, &ocv_uv);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002273 if (rc) {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002274 pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
2275 ocv_uv = DEFAULT_OCV_MICROVOLTS;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002276 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002277 }
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002278 chip->last_ocv_uv = ocv_uv;
2279 pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, chip->last_ocv_uv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002280}
2281
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002282static int64_t read_battery_id(struct pm8921_bms_chip *chip)
2283{
2284 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002285 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002286
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002287 rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002288 if (rc) {
2289 pr_err("error reading batt id channel = %d, rc = %d\n",
2290 chip->vbat_channel, rc);
2291 return rc;
2292 }
2293 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
2294 result.measurement);
David Keitel8f2601b2012-02-14 22:31:07 -08002295 return result.adc_code;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002296}
2297
David Keitel8f2601b2012-02-14 22:31:07 -08002298#define PALLADIUM_ID_MIN 0x7F40
2299#define PALLADIUM_ID_MAX 0x7F5A
2300#define DESAY_5200_ID_MIN 0x7F7F
2301#define DESAY_5200_ID_MAX 0x802F
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002302static int set_battery_data(struct pm8921_bms_chip *chip)
2303{
2304 int64_t battery_id;
2305
David Keitel35e11872012-02-17 17:40:42 -08002306 if (chip->batt_type == BATT_DESAY)
2307 goto desay;
2308 else if (chip->batt_type == BATT_PALLADIUM)
2309 goto palladium;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002310
David Keitel35e11872012-02-17 17:40:42 -08002311 battery_id = read_battery_id(chip);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002312 if (battery_id < 0) {
2313 pr_err("cannot read battery id err = %lld\n", battery_id);
2314 return battery_id;
2315 }
2316
2317 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
David Keitel35e11872012-02-17 17:40:42 -08002318 goto palladium;
2319 } else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
2320 battery_id)) {
2321 goto desay;
2322 } else {
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002323 pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
2324 battery_id);
2325 goto palladium;
David Keitel35e11872012-02-17 17:40:42 -08002326 }
2327
2328palladium:
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002329 chip->fcc = palladium_1500_data.fcc;
2330 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
2331 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
2332 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
2333 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002334 chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
2335 chip->default_rbatt_mohm
2336 = palladium_1500_data.default_rbatt_mohm;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07002337 chip->delta_rbatt_mohm = palladium_1500_data.delta_rbatt_mohm;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002338 return 0;
David Keitel35e11872012-02-17 17:40:42 -08002339desay:
David Keitel8f2601b2012-02-14 22:31:07 -08002340 chip->fcc = desay_5200_data.fcc;
2341 chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
David Keitel8f2601b2012-02-14 22:31:07 -08002342 chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
2343 chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002344 chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
2345 chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07002346 chip->delta_rbatt_mohm = desay_5200_data.delta_rbatt_mohm;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002347 return 0;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002348}
2349
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002350enum bms_request_operation {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002351 CALC_FCC,
2352 CALC_PC,
2353 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002354 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002355 CALIB_CCADC,
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002356 GET_VBAT_VSENSE_SIMULTANEOUS,
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002357 STOP_OCV,
2358 START_OCV,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002359};
2360
2361static int test_batt_temp = 5;
2362static int test_chargecycle = 150;
2363static int test_ocv = 3900000;
2364enum {
2365 TEST_BATT_TEMP,
2366 TEST_CHARGE_CYCLE,
2367 TEST_OCV,
2368};
2369static int get_test_param(void *data, u64 * val)
2370{
2371 switch ((int)data) {
2372 case TEST_BATT_TEMP:
2373 *val = test_batt_temp;
2374 break;
2375 case TEST_CHARGE_CYCLE:
2376 *val = test_chargecycle;
2377 break;
2378 case TEST_OCV:
2379 *val = test_ocv;
2380 break;
2381 default:
2382 return -EINVAL;
2383 }
2384 return 0;
2385}
2386static int set_test_param(void *data, u64 val)
2387{
2388 switch ((int)data) {
2389 case TEST_BATT_TEMP:
2390 test_batt_temp = (int)val;
2391 break;
2392 case TEST_CHARGE_CYCLE:
2393 test_chargecycle = (int)val;
2394 break;
2395 case TEST_OCV:
2396 test_ocv = (int)val;
2397 break;
2398 default:
2399 return -EINVAL;
2400 }
2401 return 0;
2402}
2403DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
2404
2405static int get_calc(void *data, u64 * val)
2406{
2407 int param = (int)data;
2408 int ret = 0;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002409 int ibat_ua, vbat_uv;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002410 struct pm8921_soc_params raw;
2411
2412 read_soc_params_raw(the_chip, &raw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002413
2414 *val = 0;
2415
2416 /* global irq number passed in via data */
2417 switch (param) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002418 case CALC_FCC:
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002419 *val = calculate_fcc_uah(the_chip, test_batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002420 test_chargecycle);
2421 break;
2422 case CALC_PC:
2423 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
2424 test_chargecycle);
2425 break;
2426 case CALC_SOC:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002427 *val = calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002428 test_batt_temp, test_chargecycle);
2429 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002430 case CALIB_HKADC:
2431 /* reading this will trigger calibration */
2432 *val = 0;
2433 calib_hkadc(the_chip);
2434 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002435 case CALIB_CCADC:
2436 /* reading this will trigger calibration */
2437 *val = 0;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002438 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002439 break;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002440 case GET_VBAT_VSENSE_SIMULTANEOUS:
2441 /* reading this will call simultaneous vbat and vsense */
2442 *val =
2443 pm8921_bms_get_simultaneous_battery_voltage_and_current(
2444 &ibat_ua,
2445 &vbat_uv);
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002446 default:
2447 ret = -EINVAL;
2448 }
2449 return ret;
2450}
2451
2452static int set_calc(void *data, u64 val)
2453{
2454 int param = (int)data;
2455 int ret = 0;
2456
2457 switch (param) {
2458 case STOP_OCV:
2459 pm8921_bms_stop_ocv_updates(the_chip);
2460 break;
2461 case START_OCV:
2462 pm8921_bms_start_ocv_updates(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002463 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002464 default:
2465 ret = -EINVAL;
2466 }
2467 return ret;
2468}
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002469DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, set_calc, "%llu\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002470
2471static int get_reading(void *data, u64 * val)
2472{
2473 int param = (int)data;
2474 int ret = 0;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002475 struct pm8921_soc_params raw;
2476
2477 read_soc_params_raw(the_chip, &raw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002478
2479 *val = 0;
2480
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002481 switch (param) {
2482 case CC_MSB:
2483 case CC_LSB:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002484 *val = raw.cc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002485 break;
2486 case LAST_GOOD_OCV_VALUE:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002487 *val = raw.last_good_ocv_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002488 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002489 case VSENSE_AVG:
2490 read_vsense_avg(the_chip, (uint *)val);
2491 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002492 default:
2493 ret = -EINVAL;
2494 }
2495 return ret;
2496}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002497DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002498
2499static int get_rt_status(void *data, u64 * val)
2500{
2501 int i = (int)data;
2502 int ret;
2503
2504 /* global irq number passed in via data */
2505 ret = pm_bms_get_rt_status(the_chip, i);
2506 *val = ret;
2507 return 0;
2508}
2509DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2510
2511static int get_reg(void *data, u64 * val)
2512{
2513 int addr = (int)data;
2514 int ret;
2515 u8 temp;
2516
2517 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2518 if (ret) {
2519 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2520 addr, temp, ret);
2521 return -EAGAIN;
2522 }
2523 *val = temp;
2524 return 0;
2525}
2526
2527static int set_reg(void *data, u64 val)
2528{
2529 int addr = (int)data;
2530 int ret;
2531 u8 temp;
2532
2533 temp = (u8) val;
2534 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2535 if (ret) {
2536 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2537 addr, temp, ret);
2538 return -EAGAIN;
2539 }
2540 return 0;
2541}
2542DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2543
2544static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2545{
2546 int i;
2547
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002548 chip->dent = debugfs_create_dir("pm8921-bms", NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002549
2550 if (IS_ERR(chip->dent)) {
2551 pr_err("pmic bms couldnt create debugfs dir\n");
2552 return;
2553 }
2554
2555 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2556 (void *)BMS_CONTROL, &reg_fops);
2557 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2558 (void *)BMS_OUTPUT0, &reg_fops);
2559 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2560 (void *)BMS_OUTPUT1, &reg_fops);
2561 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2562 (void *)BMS_TEST1, &reg_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002563
2564 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2565 (void *)TEST_BATT_TEMP, &temp_fops);
2566 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2567 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2568 debugfs_create_file("test_ocv", 0644, chip->dent,
2569 (void *)TEST_OCV, &temp_fops);
2570
2571 debugfs_create_file("read_cc", 0644, chip->dent,
2572 (void *)CC_MSB, &reading_fops);
2573 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2574 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2575 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2576 (void *)VBATT_FOR_RBATT, &reading_fops);
2577 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2578 (void *)VSENSE_FOR_RBATT, &reading_fops);
2579 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2580 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002581 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2582 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002583
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002584 debugfs_create_file("show_fcc", 0644, chip->dent,
2585 (void *)CALC_FCC, &calc_fops);
2586 debugfs_create_file("show_pc", 0644, chip->dent,
2587 (void *)CALC_PC, &calc_fops);
2588 debugfs_create_file("show_soc", 0644, chip->dent,
2589 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002590 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2591 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002592 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2593 (void *)CALIB_CCADC, &calc_fops);
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002594 debugfs_create_file("stop_ocv", 0644, chip->dent,
2595 (void *)STOP_OCV, &calc_fops);
2596 debugfs_create_file("start_ocv", 0644, chip->dent,
2597 (void *)START_OCV, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002598
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002599 debugfs_create_file("simultaneous", 0644, chip->dent,
2600 (void *)GET_VBAT_VSENSE_SIMULTANEOUS, &calc_fops);
2601
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002602 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2603 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2604 debugfs_create_file(bms_irq_data[i].name, 0444,
2605 chip->dent,
2606 (void *)bms_irq_data[i].irq_id,
2607 &rt_fops);
2608 }
2609}
2610
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -07002611#define REG_SBI_CONFIG 0x04F
2612#define PAGE3_ENABLE_MASK 0x6
2613#define PROGRAM_REV_MASK 0x0F
2614#define PROGRAM_REV 0x9
2615static int read_ocv_trim(struct pm8921_bms_chip *chip)
2616{
2617 int rc;
2618 u8 reg, sbi_config;
2619
2620 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
2621 if (rc) {
2622 pr_err("error = %d reading sbi config reg\n", rc);
2623 return rc;
2624 }
2625
2626 reg = sbi_config | PAGE3_ENABLE_MASK;
2627 rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
2628 if (rc) {
2629 pr_err("error = %d writing sbi config reg\n", rc);
2630 return rc;
2631 }
2632
2633 rc = pm8xxx_readb(chip->dev->parent, TEST_PROGRAM_REV, &reg);
2634 if (rc)
2635 pr_err("Error %d reading %d addr %d\n",
2636 rc, reg, TEST_PROGRAM_REV);
2637 pr_err("program rev reg is 0x%x\n", reg);
2638 reg &= PROGRAM_REV_MASK;
2639
2640 /* If the revision is equal or higher do not adjust trim delta */
2641 if (reg >= PROGRAM_REV) {
2642 chip->amux_2_trim_delta = 0;
2643 goto restore_sbi_config;
2644 }
2645
2646 rc = pm8xxx_readb(chip->dev->parent, AMUX_TRIM_2, &reg);
2647 if (rc) {
2648 pr_err("error = %d reading trim reg\n", rc);
2649 return rc;
2650 }
2651
2652 pr_err("trim reg is 0x%x\n", reg);
2653 chip->amux_2_trim_delta = abs(0x49 - reg);
2654 pr_err("trim delta is %d\n", chip->amux_2_trim_delta);
2655
2656restore_sbi_config:
2657 rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
2658 if (rc) {
2659 pr_err("error = %d writing sbi config reg\n", rc);
2660 return rc;
2661 }
2662
2663 return 0;
2664}
2665
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002666static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2667{
2668 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002669 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002670 struct pm8921_bms_chip *chip;
2671 const struct pm8921_bms_platform_data *pdata
2672 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002673
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002674 if (!pdata) {
2675 pr_err("missing platform data\n");
2676 return -EINVAL;
2677 }
2678
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002679 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002680 if (!chip) {
2681 pr_err("Cannot allocate pm_bms_chip\n");
2682 return -ENOMEM;
2683 }
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002684
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002685 mutex_init(&chip->bms_output_lock);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002686 mutex_init(&chip->last_ocv_uv_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002687 chip->dev = &pdev->dev;
2688 chip->r_sense = pdata->r_sense;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07002689 chip->v_cutoff = pdata->v_cutoff;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -08002690 chip->max_voltage_uv = pdata->max_voltage_uv;
David Keitel35e11872012-02-17 17:40:42 -08002691 chip->batt_type = pdata->battery_type;
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -07002692 chip->rconn_mohm = pdata->rconn_mohm;
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08002693 chip->start_percent = -EINVAL;
2694 chip->end_percent = -EINVAL;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002695 rc = set_battery_data(chip);
2696 if (rc) {
2697 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002698 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002699 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002700
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002701 if (chip->pc_temp_ocv_lut == NULL) {
2702 pr_err("temp ocv lut table is NULL\n");
2703 rc = -EINVAL;
2704 goto free_chip;
2705 }
2706
2707 /* set defaults in the battery data */
2708 if (chip->default_rbatt_mohm <= 0)
2709 chip->default_rbatt_mohm = DEFAULT_RBATT_MOHMS;
2710
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002711 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2712 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002713 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2714 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002715 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002716 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikare88f2462012-04-25 18:22:38 -07002717 chip->enable_fcc_learning = pdata->enable_fcc_learning;
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002718
2719 mutex_init(&chip->calib_mutex);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002720 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002721 INIT_DELAYED_WORK(&chip->calib_hkadc_delayed_work,
2722 calibrate_hkadc_delayed_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002723
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002724 rc = request_irqs(chip, pdev);
2725 if (rc) {
2726 pr_err("couldn't register interrupts rc = %d\n", rc);
2727 goto free_chip;
2728 }
2729
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002730 rc = pm8921_bms_hw_init(chip);
2731 if (rc) {
2732 pr_err("couldn't init hardware rc = %d\n", rc);
2733 goto free_irqs;
2734 }
2735
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07002736 read_shutdown_soc(chip);
2737
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002738 platform_set_drvdata(pdev, chip);
2739 the_chip = chip;
2740 create_debugfs_entries(chip);
2741
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -07002742 rc = read_ocv_trim(chip);
2743 if (rc) {
2744 pr_err("couldn't adjust ocv_trim rc= %d\n", rc);
2745 goto free_irqs;
2746 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002747 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002748
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002749 /* start periodic hkadc calibration */
2750 schedule_delayed_work(&chip->calib_hkadc_delayed_work, 0);
2751
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002752 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2753 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2754 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002755
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002756 get_battery_uvolts(chip, &vbatt);
2757 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2758 pm8921_bms_get_percent_charge(),
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002759 vbatt, chip->last_ocv_uv);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07002760
2761 INIT_DELAYED_WORK(&chip->shutdown_soc_work, shutdown_soc_work);
2762 schedule_delayed_work(&chip->shutdown_soc_work,
2763 round_jiffies_relative(msecs_to_jiffies
2764 (SHOW_SHUTDOWN_SOC_MS)));
2765
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002766 return 0;
2767
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002768free_irqs:
2769 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002770free_chip:
2771 kfree(chip);
2772 return rc;
2773}
2774
2775static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2776{
2777 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2778
2779 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002780 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002781 platform_set_drvdata(pdev, NULL);
2782 the_chip = NULL;
2783 kfree(chip);
2784 return 0;
2785}
2786
2787static struct platform_driver pm8921_bms_driver = {
2788 .probe = pm8921_bms_probe,
2789 .remove = __devexit_p(pm8921_bms_remove),
2790 .driver = {
2791 .name = PM8921_BMS_DEV_NAME,
2792 .owner = THIS_MODULE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002793 },
2794};
2795
2796static int __init pm8921_bms_init(void)
2797{
2798 return platform_driver_register(&pm8921_bms_driver);
2799}
2800
2801static void __exit pm8921_bms_exit(void)
2802{
2803 platform_driver_unregister(&pm8921_bms_driver);
2804}
2805
2806late_initcall(pm8921_bms_init);
2807module_exit(pm8921_bms_exit);
2808
2809MODULE_LICENSE("GPL v2");
2810MODULE_DESCRIPTION("PMIC8921 bms driver");
2811MODULE_VERSION("1.0");
2812MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);