blob: 5ddca3e5a5c5e49d92330598c45ff055ee042287 [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>
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -070031#include <linux/rtc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
33#define BMS_CONTROL 0x224
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080034#define BMS_S1_DELAY 0x225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#define BMS_OUTPUT0 0x230
36#define BMS_OUTPUT1 0x231
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -080037#define BMS_TOLERANCES 0x232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#define BMS_TEST1 0x237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070040#define ADC_ARB_SECP_CNTRL 0x190
41#define ADC_ARB_SECP_AMUX_CNTRL 0x191
42#define ADC_ARB_SECP_ANA_PARAM 0x192
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070043#define ADC_ARB_SECP_DIG_PARAM 0x193
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070044#define ADC_ARB_SECP_RSV 0x194
45#define ADC_ARB_SECP_DATA1 0x195
46#define ADC_ARB_SECP_DATA0 0x196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070048#define ADC_ARB_BMS_CNTRL 0x18D
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -070049#define AMUX_TRIM_2 0x322
50#define TEST_PROGRAM_REV 0x339
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070051
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -070052#define TEMP_SOC_STORAGE 0x107
53
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -070054#define TEMP_IAVG_STORAGE 0x105
55#define TEMP_IAVG_STORAGE_USE_MASK 0x0F
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057enum pmic_bms_interrupts {
58 PM8921_BMS_SBI_WRITE_OK,
59 PM8921_BMS_CC_THR,
60 PM8921_BMS_VSENSE_THR,
61 PM8921_BMS_VSENSE_FOR_R,
62 PM8921_BMS_OCV_FOR_R,
63 PM8921_BMS_GOOD_OCV,
64 PM8921_BMS_VSENSE_AVG,
65 PM_BMS_MAX_INTS,
66};
67
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080068struct pm8921_soc_params {
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -080069 uint16_t last_good_ocv_raw;
70 int cc;
71
72 int last_good_ocv_uv;
73};
74
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080075/**
76 * struct pm8921_bms_chip -
77 * @bms_output_lock: lock to prevent concurrent bms reads
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080078 *
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -070079 * @last_ocv_uv_mutex: mutex to protect simultaneous invocations of calculate
80 * state of charge, note that last_ocv_uv could be
81 * changed as soc is adjusted. This mutex protects
82 * simultaneous updates of last_ocv_uv as well. This mutex
83 * also protects changes to *_at_100 variables used in
84 * faking 100% SOC.
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080085 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086struct pm8921_bms_chip {
87 struct device *dev;
88 struct dentry *dent;
89 unsigned int r_sense;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -070090 unsigned int v_cutoff;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 unsigned int fcc;
92 struct single_row_lut *fcc_temp_lut;
93 struct single_row_lut *fcc_sf_lut;
94 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -080095 struct sf_lut *pc_sf_lut;
96 struct sf_lut *rbatt_sf_lut;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -070097 int delta_rbatt_mohm;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070098 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -070099 struct delayed_work calib_hkadc_delayed_work;
100 struct mutex calib_mutex;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -0700101 unsigned int revision;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800102 unsigned int xoadc_v0625_usb_present;
103 unsigned int xoadc_v0625_usb_absent;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700104 unsigned int xoadc_v0625;
105 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700106 unsigned int batt_temp_channel;
107 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700108 unsigned int ref625mv_channel;
109 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -0700110 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
112 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800113 struct mutex bms_output_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700114 struct single_row_lut *adjusted_fcc_temp_lut;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -0700115 unsigned int charging_began;
116 unsigned int start_percent;
117 unsigned int end_percent;
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -0700118 int charge_time_us;
119 int catch_up_time_us;
David Keitel35e11872012-02-17 17:40:42 -0800120 enum battery_type batt_type;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800121 uint16_t ocv_reading_at_100;
122 int cc_reading_at_100;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -0800123 int max_voltage_uv;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800124
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800125 int default_rbatt_mohm;
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700126 int amux_2_trim_delta;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700127 uint16_t prev_last_good_ocv_raw;
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -0700128 unsigned int rconn_mohm;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700129 struct mutex last_ocv_uv_mutex;
130 int last_ocv_uv;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700131 int pon_ocv_uv;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -0700132 int last_cc_uah;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -0700133 unsigned long tm_sec;
Abhijeet Dharmapurikare88f2462012-04-25 18:22:38 -0700134 int enable_fcc_learning;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -0700135 int shutdown_soc;
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -0700136 int shutdown_iavg_ua;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700137 int shutdown_soc_timer_expired;
138 struct delayed_work shutdown_soc_work;
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -0700139 struct delayed_work calculate_soc_delayed_work;
140 struct timespec t_soc_queried;
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -0700141 int shutdown_soc_valid_limit;
Abhijeet Dharmapurikar1f4cc512012-07-21 00:10:39 -0700142 int ignore_shutdown_soc;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -0700143 int prev_iavg_ua;
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -0700144 int prev_uuc_iavg_ma;
Abhijeet Dharmapurikar720b00e2012-08-01 21:33:09 -0700145 int adjust_soc_low_threshold;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146};
147
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700148/*
149 * protects against simultaneous adjustment of ocv based on shutdown soc and
150 * invalidating the shutdown soc
151 */
152static DEFINE_MUTEX(soc_invalidation_mutex);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -0700153static int shutdown_soc_invalid;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154static struct pm8921_bms_chip *the_chip;
155
156#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158#define DEFAULT_CHARGE_CYCLES 0
159
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800160static int last_usb_cal_delta_uv = 1800;
161module_param(last_usb_cal_delta_uv, int, 0644);
162
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700163static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
164static int last_charge_increase;
165module_param(last_chargecycles, int, 0644);
166module_param(last_charge_increase, int, 0644);
167
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -0700168static int calculated_soc = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169static int last_soc = -EINVAL;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800170static int last_real_fcc_mah = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700171static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700173static int bms_ops_set(const char *val, const struct kernel_param *kp)
174{
175 if (*(int *)kp->arg == -EINVAL)
176 return param_set_int(val, kp);
177 else
178 return 0;
179}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700181static struct kernel_param_ops bms_param_ops = {
182 .set = bms_ops_set,
183 .get = param_get_int,
184};
185
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700186module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
187
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800188/*
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800189 * bms_fake_battery is set in setups where a battery emulator is used instead
190 * of a real battery. This makes the bms driver report a different/fake value
191 * regardless of the calculated state of charge.
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800192 */
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800193static int bms_fake_battery = -EINVAL;
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800194module_param(bms_fake_battery, int, 0644);
195
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800196/* bms_start_XXX and bms_end_XXX are read only */
197static int bms_start_percent;
198static int bms_start_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800199static int bms_start_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800200static int bms_end_percent;
201static int bms_end_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800202static int bms_end_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800203
204static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
205{
206 return -EINVAL;
207}
208
209static struct kernel_param_ops bms_ro_param_ops = {
210 .set = bms_ro_ops_set,
211 .get = param_get_int,
212};
213module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
214module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800215module_param_cb(bms_start_cc_uah, &bms_ro_param_ops, &bms_start_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800216
217module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
218module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800219module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800220
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700221static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
222static void readjust_fcc_table(void)
223{
224 struct single_row_lut *temp, *old;
225 int i, fcc, ratio;
226
227 if (!the_chip->fcc_temp_lut) {
228 pr_err("The static fcc lut table is NULL\n");
229 return;
230 }
231
232 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
233 if (!temp) {
234 pr_err("Cannot allocate memory for adjusted fcc table\n");
235 return;
236 }
237
238 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
239
240 temp->cols = the_chip->fcc_temp_lut->cols;
241 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
242 temp->x[i] = the_chip->fcc_temp_lut->x[i];
243 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800244 temp->y[i] = (ratio * last_real_fcc_mah);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700245 temp->y[i] /= 1000;
246 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
247 temp->x[i], the_chip->fcc_temp_lut->y[i],
248 temp->y[i], ratio);
249 }
250
251 old = the_chip->adjusted_fcc_temp_lut;
252 the_chip->adjusted_fcc_temp_lut = temp;
253 kfree(old);
254}
255
256static int bms_last_real_fcc_set(const char *val,
257 const struct kernel_param *kp)
258{
259 int rc = 0;
260
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800261 if (last_real_fcc_mah == -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700262 rc = param_set_int(val, kp);
263 if (rc) {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800264 pr_err("Failed to set last_real_fcc_mah rc=%d\n", rc);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700265 return rc;
266 }
267 if (last_real_fcc_batt_temp != -EINVAL)
268 readjust_fcc_table();
269 return rc;
270}
271static struct kernel_param_ops bms_last_real_fcc_param_ops = {
272 .set = bms_last_real_fcc_set,
273 .get = param_get_int,
274};
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800275module_param_cb(last_real_fcc_mah, &bms_last_real_fcc_param_ops,
276 &last_real_fcc_mah, 0644);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700277
278static int bms_last_real_fcc_batt_temp_set(const char *val,
279 const struct kernel_param *kp)
280{
281 int rc = 0;
282
283 if (last_real_fcc_batt_temp == -EINVAL)
284 rc = param_set_int(val, kp);
285 if (rc) {
286 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
287 return rc;
288 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800289 if (last_real_fcc_mah != -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700290 readjust_fcc_table();
291 return rc;
292}
293
294static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
295 .set = bms_last_real_fcc_batt_temp_set,
296 .get = param_get_int,
297};
298module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
299 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300
301static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
302{
303 return pm8xxx_read_irq_stat(chip->dev->parent,
304 chip->pmic_bms_irq[irq_id]);
305}
306
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700307static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
308{
309 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
310 dev_dbg(chip->dev, "%s %d\n", __func__,
311 chip->pmic_bms_irq[interrupt]);
312 enable_irq(chip->pmic_bms_irq[interrupt]);
313 }
314}
315
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
317{
318 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700319 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
321 }
322}
323
324static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
325 u8 mask, u8 val)
326{
327 int rc;
328 u8 reg;
329
330 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
331 if (rc) {
332 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
333 return rc;
334 }
335 reg &= ~mask;
336 reg |= val & mask;
337 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
338 if (rc) {
339 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
340 return rc;
341 }
342 return 0;
343}
344
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800345static int usb_chg_plugged_in(void)
346{
Abhijeet Dharmapurikar3da61762012-07-12 15:56:58 -0700347 int val = pm8921_is_usb_chg_plugged_in();
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800348
Abhijeet Dharmapurikar3da61762012-07-12 15:56:58 -0700349 /* treat as if usb is not present in case of error */
350 if (val == -EINVAL)
351 val = 0;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800352
Abhijeet Dharmapurikar3da61762012-07-12 15:56:58 -0700353 return val;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800354}
355
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700356#define HOLD_OREG_DATA BIT(1)
357static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
358{
359 int rc;
360
361 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
362 HOLD_OREG_DATA);
363 if (rc) {
364 pr_err("couldnt lock bms output rc = %d\n", rc);
365 return rc;
366 }
367 return 0;
368}
369
370static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
371{
372 int rc;
373
374 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
375 if (rc) {
376 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
377 return rc;
378 }
379 return 0;
380}
381
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382#define SELECT_OUTPUT_DATA 0x1C
383#define SELECT_OUTPUT_TYPE_SHIFT 2
384#define OCV_FOR_RBATT 0x0
385#define VSENSE_FOR_RBATT 0x1
386#define VBATT_FOR_RBATT 0x2
387#define CC_MSB 0x3
388#define CC_LSB 0x4
389#define LAST_GOOD_OCV_VALUE 0x5
390#define VSENSE_AVG 0x6
391#define VBATT_AVG 0x7
392
393static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
394 int16_t *result)
395{
396 int rc;
397 u8 reg;
398
399 if (!result) {
400 pr_err("result pointer null\n");
401 return -EINVAL;
402 }
403 *result = 0;
404 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
405 pr_err("invalid type %d asked to read\n", type);
406 return -EINVAL;
407 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700408
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
410 type << SELECT_OUTPUT_TYPE_SHIFT);
411 if (rc) {
412 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
413 type, rc);
414 return rc;
415 }
416
417 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
418 if (rc) {
419 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
420 type, rc);
421 return rc;
422 }
423 *result = reg;
424 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
425 if (rc) {
426 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
427 type, rc);
428 return rc;
429 }
430 *result |= reg << 8;
431 pr_debug("type %d result %x", type, *result);
432 return 0;
433}
434
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700435#define V_PER_BIT_MUL_FACTOR 97656
436#define V_PER_BIT_DIV_FACTOR 1000
437#define XOADC_INTRINSIC_OFFSET 0x6000
438static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700439{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700440 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700441 return 0;
442
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700443 return (a - XOADC_INTRINSIC_OFFSET)
444 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700445}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700447#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700448#define VBATT_MUL_FACTOR 3
449static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800450 int usb_chg, unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700451{
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800452 s64 numerator, denominator;
453 int local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700454
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700455 if (uv == 0)
456 return 0;
457
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800458 /* dont adjust if not calibrated */
459 if (chip->xoadc_v0625 == 0 || chip->xoadc_v125 == 0) {
460 pr_debug("No cal yet return %d\n", VBATT_MUL_FACTOR * uv);
461 return VBATT_MUL_FACTOR * uv;
462 }
463
464 if (usb_chg)
465 local_delta = last_usb_cal_delta_uv;
466 else
467 local_delta = 0;
468
469 pr_debug("using delta = %d\n", local_delta);
470 numerator = ((s64)uv - chip->xoadc_v0625 - local_delta)
471 * XOADC_CALIB_UV;
472 denominator = (s64)chip->xoadc_v125 - chip->xoadc_v0625 - local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700473 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700474 return uv * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800475 return (XOADC_CALIB_UV + local_delta + div_s64(numerator, denominator))
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700476 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700477}
478
Abhijeet Dharmapurikar5adb2532012-06-21 16:15:43 -0700479#define CC_RESOLUTION_N 868056
480#define CC_RESOLUTION_D 10000
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700481
482static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
483{
Abhijeet Dharmapurikar5adb2532012-06-21 16:15:43 -0700484 return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D);
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700485}
486
Abhijeet Dharmapurikar2feabe32012-07-23 21:42:17 -0700487#define CC_READING_TICKS 56
488#define SLEEP_CLK_HZ 32764
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700489#define SECONDS_PER_HOUR 3600
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800490/**
491 * ccmicrovolt_to_nvh -
492 * @cc_uv: coulumb counter converted to uV
493 *
494 * RETURNS: coulumb counter based charge in nVh
495 * (nano Volt Hour)
496 */
497static s64 ccmicrovolt_to_nvh(s64 cc_uv)
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700498{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800499 return div_s64(cc_uv * CC_READING_TICKS * 1000,
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700500 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
501}
502
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503/* returns the signed value read from the hardware */
504static int read_cc(struct pm8921_bms_chip *chip, int *result)
505{
506 int rc;
507 uint16_t msw, lsw;
508
509 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
510 if (rc) {
511 pr_err("fail to read CC_LSB rc = %d\n", rc);
512 return rc;
513 }
514 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
515 if (rc) {
516 pr_err("fail to read CC_MSB rc = %d\n", rc);
517 return rc;
518 }
519 *result = msw << 16 | lsw;
520 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
521 return 0;
522}
523
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700524static int adjust_xo_vbatt_reading_for_mbg(struct pm8921_bms_chip *chip,
525 int result)
526{
527 int64_t numerator;
528 int64_t denominator;
529
530 if (chip->amux_2_trim_delta == 0)
531 return result;
532
533 numerator = (s64)result * 1000000;
534 denominator = (1000000 + (410 * (s64)chip->amux_2_trim_delta));
535 return div_s64(numerator, denominator);
536}
537
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800538static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800539 int usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800540 uint16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700542 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800543 pr_debug("raw = %04x vbatt = %u\n", reading, *result);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800544 *result = adjust_xo_vbatt_reading(chip, usb_chg, *result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800545 pr_debug("after adj vbatt = %u\n", *result);
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700546 *result = adjust_xo_vbatt_reading_for_mbg(chip, *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 return 0;
548}
549
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800550static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
551 int16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552{
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800553 *result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800554 pr_debug("raw = %04x vsense = %d\n", reading, *result);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800555 *result = pm8xxx_cc_adjust_for_gain(*result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800556 pr_debug("after adj vsense = %d\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 return 0;
558}
559
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700560static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
561{
562 int rc;
563 int16_t reading;
564
565 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
566 if (rc) {
567 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
568 return rc;
569 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800570
571 convert_vsense_to_uv(chip, reading, result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700572 return 0;
573}
574
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
576{
577 if (y0 == y1 || x == x0)
578 return y0;
579 if (x1 == x0 || x == x1)
580 return y1;
581
582 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
583}
584
585static int interpolate_single_lut(struct single_row_lut *lut, int x)
586{
587 int i, result;
588
589 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700590 pr_debug("x %d less than known range return y = %d lut = %pS\n",
591 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592 return lut->y[0];
593 }
594 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700595 pr_debug("x %d more than known range return y = %d lut = %pS\n",
596 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597 return lut->y[lut->cols - 1];
598 }
599
600 for (i = 0; i < lut->cols; i++)
601 if (x <= lut->x[i])
602 break;
603 if (x == lut->x[i]) {
604 result = lut->y[i];
605 } else {
606 result = linear_interpolate(
607 lut->y[i - 1],
608 lut->x[i - 1],
609 lut->y[i],
610 lut->x[i],
611 x);
612 }
613 return result;
614}
615
616static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
617{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800618 /* batt_temp is in tenths of degC - convert it to degC for lookups */
619 batt_temp = batt_temp/10;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700620 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
621}
622
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700623static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
624{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800625 /* batt_temp is in tenths of degC - convert it to degC for lookups */
626 batt_temp = batt_temp/10;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700627 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
628}
629
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
631 int cycles)
632{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700633 /*
634 * sf table could be null when no battery aging data is available, in
635 * that case return 100%
636 */
637 if (chip->fcc_sf_lut)
638 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
639 else
640 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641}
642
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800643static int interpolate_scalingfactor(struct pm8921_bms_chip *chip,
644 struct sf_lut *sf_lut,
645 int row_entry, int pc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700646{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700647 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700648 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700649 int row1 = 0;
650 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700652 /*
653 * sf table could be null when no battery aging data is available, in
654 * that case return 100%
655 */
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800656 if (!sf_lut)
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700657 return 100;
658
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800659 rows = sf_lut->rows;
660 cols = sf_lut->cols;
661 if (pc > sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700662 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700663 row1 = 0;
664 row2 = 0;
665 }
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800666 if (pc < sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700667 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668 row1 = rows - 1;
669 row2 = rows - 1;
670 }
671 for (i = 0; i < rows; i++) {
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800672 if (pc == sf_lut->percent[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 row1 = i;
674 row2 = i;
675 break;
676 }
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800677 if (pc > sf_lut->percent[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678 row1 = i - 1;
679 row2 = i;
680 break;
681 }
682 }
683
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800684 if (row_entry < sf_lut->row_entries[0])
685 row_entry = sf_lut->row_entries[0];
686 if (row_entry > sf_lut->row_entries[cols - 1])
687 row_entry = sf_lut->row_entries[cols - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700688
689 for (i = 0; i < cols; i++)
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800690 if (row_entry <= sf_lut->row_entries[i])
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691 break;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800692 if (row_entry == sf_lut->row_entries[i]) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700693 scalefactor = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800694 sf_lut->sf[row1][i],
695 sf_lut->percent[row1],
696 sf_lut->sf[row2][i],
697 sf_lut->percent[row2],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700698 pc);
699 return scalefactor;
700 }
701
702 scalefactorrow1 = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800703 sf_lut->sf[row1][i - 1],
704 sf_lut->row_entries[i - 1],
705 sf_lut->sf[row1][i],
706 sf_lut->row_entries[i],
707 row_entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700708
709 scalefactorrow2 = linear_interpolate(
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800710 sf_lut->sf[row2][i - 1],
711 sf_lut->row_entries[i - 1],
712 sf_lut->sf[row2][i],
713 sf_lut->row_entries[i],
714 row_entry);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715
716 scalefactor = linear_interpolate(
717 scalefactorrow1,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800718 sf_lut->percent[row1],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719 scalefactorrow2,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -0800720 sf_lut->percent[row2],
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721 pc);
722
723 return scalefactor;
724}
725
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700726static int is_between(int left, int right, int value)
727{
728 if (left >= right && left >= value && value >= right)
729 return 1;
730 if (left <= right && left <= value && value <= right)
731 return 1;
732
733 return 0;
734}
735
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700736/* get ocv given a soc -- reverse lookup */
737static int interpolate_ocv(struct pm8921_bms_chip *chip,
738 int batt_temp_degc, int pc)
739{
740 int i, ocvrow1, ocvrow2, ocv;
741 int rows, cols;
742 int row1 = 0;
743 int row2 = 0;
744
745 rows = chip->pc_temp_ocv_lut->rows;
746 cols = chip->pc_temp_ocv_lut->cols;
747 if (pc > chip->pc_temp_ocv_lut->percent[0]) {
748 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
749 row1 = 0;
750 row2 = 0;
751 }
752 if (pc < chip->pc_temp_ocv_lut->percent[rows - 1]) {
753 pr_debug("pc %d less than known pc ranges for sf\n", pc);
754 row1 = rows - 1;
755 row2 = rows - 1;
756 }
757 for (i = 0; i < rows; i++) {
758 if (pc == chip->pc_temp_ocv_lut->percent[i]) {
759 row1 = i;
760 row2 = i;
761 break;
762 }
763 if (pc > chip->pc_temp_ocv_lut->percent[i]) {
764 row1 = i - 1;
765 row2 = i;
766 break;
767 }
768 }
769
770 if (batt_temp_degc < chip->pc_temp_ocv_lut->temp[0])
771 batt_temp_degc = chip->pc_temp_ocv_lut->temp[0];
772 if (batt_temp_degc > chip->pc_temp_ocv_lut->temp[cols - 1])
773 batt_temp_degc = chip->pc_temp_ocv_lut->temp[cols - 1];
774
775 for (i = 0; i < cols; i++)
776 if (batt_temp_degc <= chip->pc_temp_ocv_lut->temp[i])
777 break;
778 if (batt_temp_degc == chip->pc_temp_ocv_lut->temp[i]) {
779 ocv = linear_interpolate(
780 chip->pc_temp_ocv_lut->ocv[row1][i],
781 chip->pc_temp_ocv_lut->percent[row1],
782 chip->pc_temp_ocv_lut->ocv[row2][i],
783 chip->pc_temp_ocv_lut->percent[row2],
784 pc);
785 return ocv;
786 }
787
788 ocvrow1 = linear_interpolate(
789 chip->pc_temp_ocv_lut->ocv[row1][i - 1],
790 chip->pc_temp_ocv_lut->temp[i - 1],
791 chip->pc_temp_ocv_lut->ocv[row1][i],
792 chip->pc_temp_ocv_lut->temp[i],
793 batt_temp_degc);
794
795 ocvrow2 = linear_interpolate(
796 chip->pc_temp_ocv_lut->ocv[row2][i - 1],
797 chip->pc_temp_ocv_lut->temp[i - 1],
798 chip->pc_temp_ocv_lut->ocv[row2][i],
799 chip->pc_temp_ocv_lut->temp[i],
800 batt_temp_degc);
801
802 ocv = linear_interpolate(
803 ocvrow1,
804 chip->pc_temp_ocv_lut->percent[row1],
805 ocvrow2,
806 chip->pc_temp_ocv_lut->percent[row2],
807 pc);
808
809 return ocv;
810}
811
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812static int interpolate_pc(struct pm8921_bms_chip *chip,
813 int batt_temp, int ocv)
814{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700815 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700816 int rows = chip->pc_temp_ocv_lut->rows;
817 int cols = chip->pc_temp_ocv_lut->cols;
818
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800819 /* batt_temp is in tenths of degC - convert it to degC for lookups */
820 batt_temp = batt_temp/10;
821
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700823 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700824 batt_temp = chip->pc_temp_ocv_lut->temp[0];
825 }
826 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700827 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700828 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
829 }
830
831 for (j = 0; j < cols; j++)
832 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
833 break;
834 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
835 /* found an exact match for temp in the table */
836 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
837 return chip->pc_temp_ocv_lut->percent[0];
838 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
839 return chip->pc_temp_ocv_lut->percent[rows - 1];
840 for (i = 0; i < rows; i++) {
841 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
842 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
843 return
844 chip->pc_temp_ocv_lut->percent[i];
845 pc = linear_interpolate(
846 chip->pc_temp_ocv_lut->percent[i],
847 chip->pc_temp_ocv_lut->ocv[i][j],
848 chip->pc_temp_ocv_lut->percent[i - 1],
849 chip->pc_temp_ocv_lut->ocv[i - 1][j],
850 ocv);
851 return pc;
852 }
853 }
854 }
855
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700856 /*
857 * batt_temp is within temperature for
858 * column j-1 and j
859 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
861 return chip->pc_temp_ocv_lut->percent[0];
862 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
863 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700864
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700865 pcj_minus_one = 0;
866 pcj = 0;
867 for (i = 0; i < rows-1; i++) {
868 if (pcj == 0
869 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
870 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
871 pcj = linear_interpolate(
872 chip->pc_temp_ocv_lut->percent[i],
873 chip->pc_temp_ocv_lut->ocv[i][j],
874 chip->pc_temp_ocv_lut->percent[i + 1],
875 chip->pc_temp_ocv_lut->ocv[i+1][j],
876 ocv);
877 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700878
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700879 if (pcj_minus_one == 0
880 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
881 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700882
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700883 pcj_minus_one = linear_interpolate(
884 chip->pc_temp_ocv_lut->percent[i],
885 chip->pc_temp_ocv_lut->ocv[i][j-1],
886 chip->pc_temp_ocv_lut->percent[i + 1],
887 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
888 ocv);
889 }
890
891 if (pcj && pcj_minus_one) {
892 pc = linear_interpolate(
893 pcj_minus_one,
894 chip->pc_temp_ocv_lut->temp[j-1],
895 pcj,
896 chip->pc_temp_ocv_lut->temp[j],
897 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700898 return pc;
899 }
900 }
901
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700902 if (pcj)
903 return pcj;
904
905 if (pcj_minus_one)
906 return pcj_minus_one;
907
908 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
909 ocv, batt_temp);
910 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700911}
912
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800913#define BMS_MODE_BIT BIT(6)
914#define EN_VBAT_BIT BIT(5)
915#define OVERRIDE_MODE_DELAY_MS 20
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -0700916int override_mode_simultaneous_battery_voltage_and_current(int *ibat_ua,
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800917 int *vbat_uv)
918{
919 int16_t vsense_raw;
920 int16_t vbat_raw;
921 int vsense_uv;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800922 int usb_chg;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800923
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800924 mutex_lock(&the_chip->bms_output_lock);
925
926 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x00);
927 pm_bms_masked_write(the_chip, BMS_CONTROL,
928 BMS_MODE_BIT | EN_VBAT_BIT, BMS_MODE_BIT | EN_VBAT_BIT);
929
930 msleep(OVERRIDE_MODE_DELAY_MS);
931
932 pm_bms_lock_output_data(the_chip);
933 pm_bms_read_output_data(the_chip, VSENSE_AVG, &vsense_raw);
934 pm_bms_read_output_data(the_chip, VBATT_AVG, &vbat_raw);
935 pm_bms_unlock_output_data(the_chip);
936 pm_bms_masked_write(the_chip, BMS_CONTROL,
937 BMS_MODE_BIT | EN_VBAT_BIT, 0);
938
939 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x0B);
940
941 mutex_unlock(&the_chip->bms_output_lock);
942
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800943 usb_chg = usb_chg_plugged_in();
944
945 convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800946 convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
947 *ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
948
949 pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
950 " ibat_ua = %d vbat_uv = %d\n",
951 (uint16_t)vsense_raw, (uint16_t)vbat_raw,
952 *ibat_ua, *vbat_uv);
953 return 0;
954}
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800955
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700956#define MBG_TRANSIENT_ERROR_RAW 51
957static void adjust_pon_ocv_raw(struct pm8921_bms_chip *chip,
958 struct pm8921_soc_params *raw)
959{
960 /* in 8921 parts the PON ocv is taken when the MBG is not settled.
961 * decrease the pon ocv by 15mV raw value to account for it
962 * Since a 1/3rd of vbatt is supplied to the adc the raw value
963 * needs to be adjusted by 5mV worth bits
964 */
965 if (raw->last_good_ocv_raw >= MBG_TRANSIENT_ERROR_RAW)
966 raw->last_good_ocv_raw -= MBG_TRANSIENT_ERROR_RAW;
967}
968
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800969static int read_soc_params_raw(struct pm8921_bms_chip *chip,
970 struct pm8921_soc_params *raw)
971{
972 int usb_chg;
973
974 mutex_lock(&chip->bms_output_lock);
975 pm_bms_lock_output_data(chip);
976
977 pm_bms_read_output_data(chip,
978 LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
979 read_cc(chip, &raw->cc);
980
981 pm_bms_unlock_output_data(chip);
982 mutex_unlock(&chip->bms_output_lock);
983
984 usb_chg = usb_chg_plugged_in();
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700985
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700986 if (chip->prev_last_good_ocv_raw == 0) {
987 chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
988 adjust_pon_ocv_raw(chip, raw);
989 convert_vbatt_raw_to_uv(chip, usb_chg,
990 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700991 chip->last_ocv_uv = raw->last_good_ocv_uv;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -0700992 pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv);
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -0700993 } else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) {
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -0700994 chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw;
995 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800996 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -0700997 chip->last_ocv_uv = raw->last_good_ocv_uv;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -0700998 /* forget the old cc value upon ocv */
999 chip->last_cc_uah = 0;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -07001000 } else {
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001001 raw->last_good_ocv_uv = chip->last_ocv_uv;
Abhijeet Dharmapurikarb7092f12012-03-14 21:19:50 -07001002 }
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001003
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001004 /* fake a high OCV if we are just done charging */
1005 if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
1006 chip->ocv_reading_at_100 = 0;
1007 chip->cc_reading_at_100 = 0;
1008 } else {
1009 /*
1010 * force 100% ocv by selecting the highest voltage the
1011 * battery could ever reach
1012 */
1013 raw->last_good_ocv_uv = chip->max_voltage_uv;
1014 chip->last_ocv_uv = chip->max_voltage_uv;
1015 }
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001016 pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
1017 pr_debug("1p25 = %duV\n", chip->xoadc_v125);
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001018 pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
1019 raw->last_good_ocv_raw, raw->last_good_ocv_uv);
1020 pr_debug("cc_raw= 0x%x\n", raw->cc);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001021 return 0;
1022}
1023
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001024static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp)
1025{
1026 int rbatt, scalefactor;
1027
Abhijeet Dharmapurikar0711c522012-05-22 18:45:25 -07001028 rbatt = chip->default_rbatt_mohm;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001029 pr_debug("rbatt before scaling = %d\n", rbatt);
1030 if (chip->rbatt_sf_lut == NULL) {
1031 pr_debug("RBATT = %d\n", rbatt);
1032 return rbatt;
1033 }
Abhijeet Dharmapurikar12a891c2012-04-12 23:52:06 -07001034 /* Convert the batt_temp to DegC from deciDegC */
1035 batt_temp = batt_temp / 10;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001036 scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut,
1037 batt_temp, soc_rbatt);
1038 pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
1039 scalefactor, batt_temp, soc_rbatt);
1040 rbatt = (rbatt * scalefactor) / 100;
1041
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -07001042 rbatt += the_chip->rconn_mohm;
1043 pr_debug("adding rconn_mohm = %d rbatt = %d\n",
1044 the_chip->rconn_mohm, rbatt);
1045
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07001046 if (is_between(20, 10, soc_rbatt))
1047 rbatt = rbatt
1048 + ((20 - soc_rbatt) * chip->delta_rbatt_mohm) / 10;
1049 else
1050 if (is_between(10, 0, soc_rbatt))
1051 rbatt = rbatt + chip->delta_rbatt_mohm;
1052
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001053 pr_debug("RBATT = %d\n", rbatt);
1054 return rbatt;
1055}
1056
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001057static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001058 int chargecycles)
1059{
1060 int initfcc, result, scalefactor = 0;
1061
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001062 if (chip->adjusted_fcc_temp_lut == NULL) {
1063 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001064
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001065 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001066
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001067 /* Multiply the initial FCC value by the scale factor. */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001068 result = (initfcc * scalefactor * 1000) / 100;
1069 pr_debug("fcc = %d uAh\n", result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001070 return result;
1071 } else {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001072 return 1000 * interpolate_fcc_adjusted(chip, batt_temp);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001073 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001074}
1075
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001076static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
1077{
1078 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001079 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001080
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001081 rc = pm8xxx_adc_read(chip->vbat_channel, &result);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001082 if (rc) {
1083 pr_err("error reading adc channel = %d, rc = %d\n",
1084 chip->vbat_channel, rc);
1085 return rc;
1086 }
1087 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
1088 result.measurement);
1089 *uvolts = (int)result.physical;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001090 return 0;
1091}
1092
1093static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
1094{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001095 int vbatt, rbatt, ibatt_ua, rc;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001096
1097 rc = get_battery_uvolts(chip, &vbatt);
1098 if (rc) {
1099 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001100 return rc;
1101 }
1102
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001103 rc = pm8921_bms_get_battery_current(&ibatt_ua);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001104 if (rc) {
1105 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001106 return rc;
1107 }
1108
Abhijeet Dharmapurikar0711c522012-05-22 18:45:25 -07001109 rbatt = chip->default_rbatt_mohm;
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001110 *ocv = vbatt + (ibatt_ua * rbatt)/1000;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001111 return 0;
1112}
1113
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001114static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
1115 int chargecycles)
1116{
1117 int pc, scalefactor;
1118
1119 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
1120 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
1121 pc, ocv_uv, batt_temp);
1122
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001123 scalefactor = interpolate_scalingfactor(chip,
1124 chip->pc_sf_lut, chargecycles, pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001125 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
1126
1127 /* Multiply the initial FCC value by the scale factor. */
1128 pc = (pc * scalefactor) / 100;
1129 return pc;
1130}
1131
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001132/**
1133 * calculate_cc_uah -
1134 * @chip: the bms chip pointer
1135 * @cc: the cc reading from bms h/w
1136 * @val: return value
1137 * @coulumb_counter: adjusted coulumb counter for 100%
1138 *
1139 * RETURNS: in val pointer coulumb counter based charger in uAh
1140 * (micro Amp hour)
1141 */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001142static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001143{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001144 int64_t cc_voltage_uv, cc_nvh, cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001145
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001146 cc_voltage_uv = cc;
1147 cc_voltage_uv -= chip->cc_reading_at_100;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001148 pr_debug("cc = %d. after subtracting 0x%x cc = %lld\n",
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001149 cc, chip->cc_reading_at_100,
1150 cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07001151 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001152 cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001153 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001154 cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
1155 pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
1156 cc_uah = div_s64(cc_nvh, chip->r_sense);
1157 *val = cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001158}
1159
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001160static int calculate_uuc_uah_at_given_current(struct pm8921_bms_chip *chip,
1161 int batt_temp, int chargecycles,
1162 int rbatt, int fcc_uah, int i_ma)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001163{
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001164 int unusable_uv, pc_unusable, uuc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001165
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001166 /* calculate unusable charge with itest */
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001167 unusable_uv = (rbatt * i_ma) + (chip->v_cutoff * 1000);
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001168 pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);
1169 uuc = (fcc_uah * pc_unusable) / 100;
1170 pr_debug("For i_ma = %d, unusable_uv = %d unusable_pc = %d uuc = %d\n",
1171 i_ma, unusable_uv, pc_unusable, uuc);
1172 return uuc;
1173}
1174
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001175static void calculate_iavg_ua(struct pm8921_bms_chip *chip, int cc_uah,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001176 int *iavg_ua, int *delta_time_s)
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001177{
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001178 int delta_cc_uah;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001179 struct rtc_time tm;
1180 struct rtc_device *rtc;
1181 unsigned long now_tm_sec = 0;
1182 int rc = 0;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001183
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001184 /* if anything fails report the previous iavg_ua */
1185 *iavg_ua = chip->prev_iavg_ua;
1186
1187 rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
1188 if (rtc == NULL) {
1189 pr_err("%s: unable to open rtc device (%s)\n",
1190 __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
1191 goto out;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001192 }
1193
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001194 rc = rtc_read_time(rtc, &tm);
1195 if (rc) {
1196 pr_err("Error reading rtc device (%s) : %d\n",
1197 CONFIG_RTC_HCTOSYS_DEVICE, rc);
1198 goto out;
1199 }
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001200
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001201 rc = rtc_valid_tm(&tm);
1202 if (rc) {
1203 pr_err("Invalid RTC time (%s): %d\n",
1204 CONFIG_RTC_HCTOSYS_DEVICE, rc);
1205 goto out;
1206 }
1207 rtc_tm_to_time(&tm, &now_tm_sec);
1208
1209 if (chip->tm_sec == 0) {
1210 *delta_time_s = 0;
1211 pm8921_bms_get_battery_current(iavg_ua);
1212 goto out;
1213 }
1214
1215 *delta_time_s = (now_tm_sec - chip->tm_sec);
1216
1217 /* use the previous iavg if called within 15 seconds */
1218 if (*delta_time_s < 15) {
1219 *iavg_ua = chip->prev_iavg_ua;
1220 goto out;
1221 }
1222
1223 delta_cc_uah = cc_uah - chip->last_cc_uah;
1224
1225 *iavg_ua = div_s64((s64)delta_cc_uah * 3600, *delta_time_s);
1226
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001227 pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n",
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001228 chip->tm_sec, now_tm_sec,
1229 *delta_time_s, delta_cc_uah, (int)*iavg_ua);
1230
1231out:
1232 /* remember the iavg */
1233 chip->prev_iavg_ua = *iavg_ua;
1234
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001235 /* remember cc_uah */
1236 chip->last_cc_uah = cc_uah;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001237
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001238 /* remember this time */
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001239 chip->tm_sec = now_tm_sec;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001240}
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001241
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001242#define IAVG_SAMPLES 16
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001243#define CHARGING_IAVG_MA 250
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001244#define MIN_SECONDS_FOR_VALID_SAMPLE 20
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001245static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
1246 int rbatt, int fcc_uah, int cc_uah,
1247 int soc_rbatt, int batt_temp, int chargecycles,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001248 int iavg_ua, int delta_time_s)
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001249{
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001250 int uuc_uah_iavg;
1251 int i;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001252 int iavg_ma = iavg_ua / 1000;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001253 static int iavg_samples[IAVG_SAMPLES];
1254 static int iavg_index;
1255 static int iavg_num_samples;
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001256 static int firsttime = 1;
1257
1258 /*
1259 * if we are called first time fill all the
1260 * samples with the the shutdown_iavg_ua
1261 */
1262 if (firsttime && chip->shutdown_iavg_ua != 0) {
1263 pr_emerg("Using shutdown_iavg_ua = %d in all samples\n",
1264 chip->shutdown_iavg_ua);
1265 for (i = 0; i < IAVG_SAMPLES; i++)
1266 iavg_samples[i] = chip->shutdown_iavg_ua;
1267
1268 iavg_index = 0;
1269 iavg_num_samples = IAVG_SAMPLES;
1270 }
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001271
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001272 /*
1273 * if we are charging use a nominal avg current so that we keep
1274 * a reasonable UUC while charging
1275 */
1276 if (iavg_ma < 0)
1277 iavg_ma = CHARGING_IAVG_MA;
1278 iavg_samples[iavg_index] = iavg_ma;
1279 iavg_index = (iavg_index + 1) % IAVG_SAMPLES;
1280 iavg_num_samples++;
1281 if (iavg_num_samples >= IAVG_SAMPLES)
1282 iavg_num_samples = IAVG_SAMPLES;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001283
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001284 /* now that this sample is added calcualte the average */
1285 iavg_ma = 0;
1286 if (iavg_num_samples != 0) {
1287 for (i = 0; i < iavg_num_samples; i++) {
1288 pr_debug("iavg_samples[%d] = %d\n", i, iavg_samples[i]);
1289 iavg_ma += iavg_samples[i];
1290 }
1291
1292 iavg_ma = DIV_ROUND_CLOSEST(iavg_ma, iavg_num_samples);
1293 }
1294
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001295 uuc_uah_iavg = calculate_uuc_uah_at_given_current(chip,
1296 batt_temp, chargecycles,
1297 rbatt, fcc_uah, iavg_ma);
1298 pr_debug("iavg = %d uuc_iavg = %d\n", iavg_ma, uuc_uah_iavg);
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001299 chip->prev_uuc_iavg_ma = iavg_ma;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001300
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001301 firsttime = 0;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001302 return uuc_uah_iavg;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001303}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -07001304
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001305/* calculate remainging charge at the time of ocv */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001306static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001307 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001308 int fcc_uah, int batt_temp,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001309 int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001310{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001311 int ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001313 ocv = raw->last_good_ocv_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001314 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001315 pr_debug("ocv = %d pc = %d\n", ocv, pc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001316 return (fcc_uah * pc) / 100;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001317}
1318
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001319static void calculate_soc_params(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001320 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001321 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001322 int *fcc_uah,
1323 int *unusable_charge_uah,
1324 int *remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001325 int *cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001326 int *rbatt,
1327 int *iavg_ua,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001328 int *delta_time_s)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001329{
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001330 int soc_rbatt;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001331
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001332 *fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
1333 pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
1334 *fcc_uah, batt_temp, chargecycles);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001335
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001336
1337 /* calculate remainging charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001338 *remaining_charge_uah = calculate_remaining_charge_uah(chip, raw,
1339 *fcc_uah, batt_temp, chargecycles);
1340 pr_debug("RC = %uuAh\n", *remaining_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001341
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001342 /* calculate cc micro_volt_hour */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001343 calculate_cc_uah(chip, raw->cc, cc_uah);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001344 pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %x\n",
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001345 *cc_uah, raw->cc,
1346 (int64_t)raw->cc - chip->cc_reading_at_100,
1347 chip->cc_reading_at_100);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001348
1349 soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
1350 if (soc_rbatt < 0)
1351 soc_rbatt = 0;
1352 *rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
1353
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001354 calculate_iavg_ua(chip, *cc_uah, iavg_ua, delta_time_s);
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001355
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001356 *unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt,
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07001357 *fcc_uah, *cc_uah, soc_rbatt,
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001358 batt_temp, chargecycles, *iavg_ua,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001359 *delta_time_s);
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001360 pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001361}
1362
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001363static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001364 struct pm8921_soc_params *raw,
1365 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001366 int *ret_fcc_uah)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001367{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001368 int fcc_uah, unusable_charge_uah;
1369 int remaining_charge_uah;
1370 int cc_uah;
1371 int real_fcc_uah;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001372 int rbatt;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001373 int iavg_ua;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001374 int delta_time_s;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001375
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001376 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1377 &fcc_uah,
1378 &unusable_charge_uah,
1379 &remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001380 &cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001381 &rbatt,
1382 &iavg_ua,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001383 &delta_time_s);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001384
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001385 real_fcc_uah = remaining_charge_uah - cc_uah;
1386 *ret_fcc_uah = fcc_uah;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001387 pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001388 real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
1389 return real_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001390}
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001391
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -07001392int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
1393 int *vbat_uv)
1394{
1395 int rc;
1396
1397 if (the_chip == NULL) {
1398 pr_err("Called too early\n");
1399 return -EINVAL;
1400 }
1401
1402 if (pm8921_is_batfet_closed()) {
1403 return override_mode_simultaneous_battery_voltage_and_current(
1404 ibat_ua,
1405 vbat_uv);
1406 } else {
1407 pr_debug("batfet is open using separate vbat and ibat meas\n");
1408 rc = get_battery_uvolts(the_chip, vbat_uv);
1409 if (rc < 0) {
1410 pr_err("adc vbat failed err = %d\n", rc);
1411 return rc;
1412 }
1413 rc = pm8921_bms_get_battery_current(ibat_ua);
1414 if (rc < 0) {
1415 pr_err("bms ibat failed err = %d\n", rc);
1416 return rc;
1417 }
1418 }
1419
1420 return 0;
1421}
1422EXPORT_SYMBOL(pm8921_bms_get_simultaneous_battery_voltage_and_current);
1423
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001424static int bound_soc(int soc)
1425{
1426 soc = max(0, soc);
1427 soc = min(100, soc);
1428 return soc;
1429}
1430
1431static int last_soc_est = -EINVAL;
1432static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp,
1433 int rbatt , int fcc_uah, int uuc_uah, int cc_uah)
1434{
1435 int ibat_ua = 0, vbat_uv = 0;
1436 int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
1437 int delta_ocv_uv = 0;
1438 int n = 0;
1439 int rc_new_uah = 0;
1440 int pc_new = 0;
1441 int soc_new = 0;
1442 int m = 0;
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -07001443 int rc = 0;
Abhijeet Dharmapurikar720b00e2012-08-01 21:33:09 -07001444 int delta_ocv_uv_limit = 0;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001445
Abhijeet Dharmapurikarb2680132012-05-15 16:32:51 -07001446 rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(
1447 &ibat_ua,
1448 &vbat_uv);
1449 if (rc < 0) {
1450 pr_err("simultaneous vbat ibat failed err = %d\n", rc);
1451 goto out;
1452 }
1453
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001454
1455 if (ibat_ua < 0)
1456 goto out;
Abhijeet Dharmapurikar720b00e2012-08-01 21:33:09 -07001457
1458 delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
1459
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001460 ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;
1461 pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);
1462 soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,
1463 (s64)fcc_uah - uuc_uah);
1464 soc_est = bound_soc(soc_est);
1465
1466 /*
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001467 * do not adjust
1468 * if soc is same as what bms calculated
1469 * if soc_est is between 45 and 25, this is the flat portion of the
1470 * curve where soc_est is not so accurate. We generally don't want to
1471 * adjust when soc_est is inaccurate except for the cases when soc is
1472 * way far off (higher than 50 or lesser than 20).
Abhijeet Dharmapurikar52daf652012-07-19 11:50:47 -07001473 * Also don't adjust soc if it is above 90 becuase we might pull it low
1474 * and cause a bad user experience
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001475 */
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07001476 if (soc_est == soc
Abhijeet Dharmapurikar720b00e2012-08-01 21:33:09 -07001477 || (is_between(45, chip->adjust_soc_low_threshold, soc_est)
1478 && is_between(50, chip->adjust_soc_low_threshold - 5, soc))
Abhijeet Dharmapurikar52daf652012-07-19 11:50:47 -07001479 || soc >= 90)
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001480 goto out;
1481
1482 if (last_soc_est == -EINVAL)
1483 last_soc_est = soc;
1484
1485 n = min(200, max(1 , soc + soc_est + last_soc_est));
1486 /* remember the last soc_est in last_soc_est */
1487 last_soc_est = soc_est;
1488
1489 pc = calculate_pc(chip, chip->last_ocv_uv,
1490 batt_temp, last_chargecycles);
1491 if (pc > 0) {
1492 pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),
1493 batt_temp, last_chargecycles);
1494 while (pc_new == pc) {
1495 /* start taking 10mV steps */
1496 m = m + 10;
1497 pc_new = calculate_pc(chip,
1498 chip->last_ocv_uv - (m * 1000),
1499 batt_temp, last_chargecycles);
1500 }
1501 } else {
1502 /*
1503 * pc is already at the lowest point,
1504 * assume 1 millivolt translates to 1% pc
1505 */
1506 pc = 1;
1507 pc_new = 0;
1508 m = 1;
1509 }
1510
1511 delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,
1512 n * (pc - pc_new));
Abhijeet Dharmapurikar720b00e2012-08-01 21:33:09 -07001513
1514 if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {
1515 pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,
1516 delta_ocv_uv_limit);
1517
1518 if (delta_ocv_uv > 0)
1519 delta_ocv_uv = delta_ocv_uv_limit;
1520 else
1521 delta_ocv_uv = -1 * delta_ocv_uv_limit;
1522 pr_debug("new delta ocv = %d\n", delta_ocv_uv);
1523 }
1524
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001525 chip->last_ocv_uv -= delta_ocv_uv;
1526
1527 if (chip->last_ocv_uv >= chip->max_voltage_uv)
1528 chip->last_ocv_uv = chip->max_voltage_uv;
1529
1530 /* calculate the soc based on this new ocv */
1531 pc_new = calculate_pc(chip, chip->last_ocv_uv,
1532 batt_temp, last_chargecycles);
1533 rc_new_uah = (fcc_uah * pc_new) / 100;
1534 soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);
1535 soc_new = bound_soc(soc_new);
1536
1537 /*
1538 * if soc_new is ZERO force it higher so that phone doesnt report soc=0
1539 * soc = 0 should happen only when soc_est == 0
1540 */
1541 if (soc_new == 0 && soc_est != 0)
1542 soc_new = 1;
1543
1544 soc = soc_new;
1545
1546out:
1547 pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "
1548 "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "
1549 "pc_new = %d, soc_new = %d\n",
1550 ibat_ua, vbat_uv, ocv_est_uv, pc_est,
1551 soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
1552 pc_new, soc_new);
1553
1554 return soc;
1555}
1556
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001557#define IGNORE_SOC_TEMP_DECIDEG 50
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001558#define IAVG_STEP_SIZE_MA 50
1559#define IAVG_START 600
1560#define SOC_ZERO 0xFF
1561static void backup_soc_and_iavg(struct pm8921_bms_chip *chip, int batt_temp,
1562 int soc)
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001563{
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001564 u8 temp;
1565 int iavg_ma = chip->prev_uuc_iavg_ma;
1566
1567 if (iavg_ma > IAVG_START)
1568 temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA;
1569 else
1570 temp = 0;
1571
1572 pm_bms_masked_write(chip, TEMP_IAVG_STORAGE,
1573 TEMP_IAVG_STORAGE_USE_MASK, temp);
1574
1575 /* since only 6 bits are available for SOC, we store half the soc */
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001576 if (soc == 0)
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001577 temp = SOC_ZERO;
1578 else
1579 temp = soc;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001580
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001581 /* don't store soc if temperature is below 5degC */
1582 if (batt_temp > IGNORE_SOC_TEMP_DECIDEG)
1583 pm8xxx_writeb(the_chip->dev->parent, TEMP_SOC_STORAGE, temp);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001584}
1585
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001586static void read_shutdown_soc_and_iavg(struct pm8921_bms_chip *chip)
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001587{
1588 int rc;
1589 u8 temp;
1590
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001591 rc = pm8xxx_readb(chip->dev->parent, TEMP_IAVG_STORAGE, &temp);
1592 if (rc) {
1593 pr_err("failed to read addr = %d %d assuming %d\n",
1594 TEMP_IAVG_STORAGE, rc, IAVG_START);
1595 chip->shutdown_iavg_ua = IAVG_START;
1596 } else {
1597 temp &= TEMP_IAVG_STORAGE_USE_MASK;
1598
1599 if (temp == 0) {
1600 chip->shutdown_iavg_ua = IAVG_START;
1601 } else {
1602 chip->shutdown_iavg_ua = IAVG_START
1603 + IAVG_STEP_SIZE_MA * (temp + 1);
1604 }
1605 }
1606
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001607 rc = pm8xxx_readb(chip->dev->parent, TEMP_SOC_STORAGE, &temp);
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001608 if (rc) {
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001609 pr_err("failed to read addr = %d %d\n", TEMP_SOC_STORAGE, rc);
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001610 } else {
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001611 chip->shutdown_soc = temp;
1612
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001613 if (chip->shutdown_soc == 0) {
1614 pr_debug("No shutdown soc available\n");
1615 shutdown_soc_invalid = 1;
1616 chip->shutdown_iavg_ua = 0;
1617 } else if (chip->shutdown_soc == SOC_ZERO) {
1618 chip->shutdown_soc = 0;
1619 }
1620 }
Abhijeet Dharmapurikar1f4cc512012-07-21 00:10:39 -07001621
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001622 if (chip->ignore_shutdown_soc) {
1623 shutdown_soc_invalid = 1;
1624 chip->shutdown_soc = 0;
1625 chip->shutdown_iavg_ua = 0;
1626 }
1627
1628 pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d\n",
1629 chip->shutdown_soc,
1630 chip->shutdown_iavg_ua,
1631 shutdown_soc_invalid);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07001632}
1633
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001634static void find_ocv_for_soc(struct pm8921_bms_chip *chip,
1635 int batt_temp,
1636 int chargecycles,
1637 int fcc_uah,
1638 int uuc_uah,
1639 int cc_uah,
1640 int shutdown_soc,
1641 int *rc_uah,
1642 int *ocv_uv)
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001643{
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001644 s64 rc;
1645 int pc, new_pc;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001646 int batt_temp_degc = batt_temp / 10;
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001647 int ocv;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001648
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001649 rc = (s64)shutdown_soc * (fcc_uah - uuc_uah);
1650 rc = div_s64(rc, 100) + cc_uah + uuc_uah;
1651 pc = DIV_ROUND_CLOSEST((int)rc * 100, fcc_uah);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001652 pc = clamp(pc, 0, 100);
1653
1654 ocv = interpolate_ocv(chip, batt_temp_degc, pc);
1655
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001656 pr_debug("s_soc = %d, fcc = %d uuc = %d rc = %d, pc = %d, ocv mv = %d\n",
1657 shutdown_soc, fcc_uah, uuc_uah, (int)rc, pc, ocv);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001658 new_pc = interpolate_pc(chip, batt_temp_degc, ocv);
1659 pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv);
1660
1661 while (abs(new_pc - pc) > 1) {
1662 int delta_mv = 5;
1663
1664 if (new_pc > pc)
1665 delta_mv = -1 * delta_mv;
1666
1667 ocv = ocv + delta_mv;
1668 new_pc = interpolate_pc(chip, batt_temp_degc, ocv);
1669 pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv);
1670 }
1671
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001672 *ocv_uv = ocv * 1000;
1673 *rc_uah = (int)rc;
1674}
1675
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001676static void adjust_rc_and_uuc_for_specific_soc(
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001677 struct pm8921_bms_chip *chip,
1678 int batt_temp,
1679 int chargecycles,
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001680 int soc,
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001681 int fcc_uah,
1682 int uuc_uah,
1683 int cc_uah,
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001684 int rc_uah,
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001685 int rbatt,
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001686 int *ret_ocv,
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001687 int *ret_rc,
1688 int *ret_uuc,
1689 int *ret_rbatt)
1690{
1691 int new_rbatt;
1692 int ocv_uv;
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001693 int soc_rbatt;
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001694 int iavg_ma;
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001695 int num_tries = 0;
1696
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001697 iavg_ma = chip->prev_uuc_iavg_ma;
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001698
1699recalculate_ocv:
1700
1701 find_ocv_for_soc(chip, batt_temp, chargecycles,
1702 fcc_uah, uuc_uah, cc_uah,
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001703 soc,
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001704 &rc_uah, &ocv_uv);
1705
1706 soc_rbatt = div_s64((rc_uah - cc_uah) * 100, fcc_uah);
1707 new_rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001708 pr_debug("for f_soc = %d rc_uah = %d ocv_uv = %d, soc_rbatt = %d rbatt = %d\n",
1709 soc, rc_uah, ocv_uv, soc_rbatt, new_rbatt);
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001710 if (abs(new_rbatt - rbatt) > 20 && num_tries < 10) {
1711 rbatt = new_rbatt;
1712 uuc_uah = calculate_uuc_uah_at_given_current(chip,
1713 batt_temp, chargecycles,
1714 new_rbatt, fcc_uah, iavg_ma);
1715
1716 pr_debug("rbatt not settled uuc = %d for rbatt = %d iavg_ma = %d num_tries = %d\n",
1717 uuc_uah, rbatt, iavg_ma, num_tries);
1718 num_tries++;
1719 goto recalculate_ocv;
1720 }
1721 pr_debug("DONE for s_soc = %d rc_uah = %d ocv_uv = %d, rbatt = %d\n",
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001722 soc, rc_uah, ocv_uv, new_rbatt);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001723
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001724 *ret_ocv = ocv_uv;
Abhijeet Dharmapurikar091c9ec2012-07-18 23:47:57 -07001725 *ret_rbatt = rbatt;
1726 *ret_rc = rc_uah;
1727 *ret_uuc = uuc_uah;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001728}
1729
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07001730#define SOC_CATCHUP_SEC_MAX 600
1731#define SOC_CATCHUP_SEC_PER_PERCENT 60
1732#define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT)
1733static int scale_soc_while_chg(struct pm8921_bms_chip *chip,
1734 int delta_time_us, int new_soc, int prev_soc)
1735{
1736 int chg_time_sec;
1737 int catch_up_sec;
1738 int scaled_soc;
1739 int numerator;
1740
1741 /*
1742 * The device must be charging for reporting a higher soc, if
1743 * not ignore this soc and continue reporting the prev_soc.
1744 * Also don't report a high value immediately slowly scale the
1745 * value from prev_soc to the new soc based on a charge time
1746 * weighted average
1747 */
1748
1749 /* if we are not charging return last soc */
1750 if (the_chip->start_percent == -EINVAL)
1751 return prev_soc;
1752
1753 /* if soc is called in quick succession return the last soc */
1754 if (delta_time_us < USEC_PER_SEC)
1755 return prev_soc;
1756
1757 chg_time_sec = DIV_ROUND_UP(the_chip->charge_time_us, USEC_PER_SEC);
1758 catch_up_sec = DIV_ROUND_UP(the_chip->catch_up_time_us, USEC_PER_SEC);
1759 pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec);
1760
1761 /*
1762 * if we have been charging for more than catch_up time simply return
1763 * new soc
1764 */
1765 if (chg_time_sec > catch_up_sec)
1766 return new_soc;
1767
1768 numerator = (catch_up_sec - chg_time_sec) * prev_soc
1769 + chg_time_sec * new_soc;
1770 scaled_soc = numerator / catch_up_sec;
1771
1772 pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n",
1773 chg_time_sec, new_soc, prev_soc, scaled_soc);
1774
1775 return scaled_soc;
1776}
1777
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07001778#define SHOW_SHUTDOWN_SOC_MS 30000
1779static void shutdown_soc_work(struct work_struct *work)
1780{
1781 struct pm8921_bms_chip *chip = container_of(work,
1782 struct pm8921_bms_chip, shutdown_soc_work.work);
1783
1784 pr_debug("not forcing shutdown soc anymore\n");
1785 /* it has been 30 seconds since init, no need to show shutdown soc */
1786 chip->shutdown_soc_timer_expired = 1;
1787}
1788
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001789static bool is_shutdown_soc_within_limits(struct pm8921_bms_chip *chip, int soc)
1790{
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001791 if (shutdown_soc_invalid) {
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001792 pr_debug("NOT forcing shutdown soc = %d\n", chip->shutdown_soc);
1793 return 0;
1794 }
1795
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07001796 if (abs(chip->shutdown_soc - soc) > chip->shutdown_soc_valid_limit) {
1797 pr_debug("rejecting shutdown soc = %d, soc = %d limit = %d\n",
1798 chip->shutdown_soc, soc,
1799 chip->shutdown_soc_valid_limit);
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001800 shutdown_soc_invalid = 1;
1801 return 0;
1802 }
1803
1804 return 1;
1805}
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001806/*
1807 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1808 * - coloumb counter charge
1809 * - unusable charge (due to battery resistance)
1810 * SOC% = (remaining usable charge/ fcc - usable_charge);
1811 */
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001812static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001813 struct pm8921_soc_params *raw,
1814 int batt_temp, int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001815{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001816 int remaining_usable_charge_uah, fcc_uah, unusable_charge_uah;
1817 int remaining_charge_uah, soc;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001818 int cc_uah;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001819 int rbatt;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001820 int iavg_ua;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001821 int delta_time_s;
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001822 int new_ocv;
Abhijeet Dharmapurikar6f42a232012-07-17 22:11:13 -07001823 int new_rc_uah;
1824 int new_ucc_uah;
1825 int new_rbatt;
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001826 int shutdown_soc;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001827 static int firsttime = 1;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001828
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001829 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1830 &fcc_uah,
1831 &unusable_charge_uah,
1832 &remaining_charge_uah,
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08001833 &cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07001834 &rbatt,
1835 &iavg_ua,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001836 &delta_time_s);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001837
1838 /* calculate remaining usable charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001839 remaining_usable_charge_uah = remaining_charge_uah
1840 - cc_uah
1841 - unusable_charge_uah;
1842
1843 pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
Abhijeet Dharmapurikarbbae88312012-02-14 18:56:42 -08001844 if (fcc_uah - unusable_charge_uah <= 0) {
1845 pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
1846 fcc_uah, unusable_charge_uah);
1847 soc = 0;
1848 } else {
Abhijeet Dharmapurikar4ff213c2012-07-11 21:28:29 -07001849 soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
1850 (fcc_uah - unusable_charge_uah));
Abhijeet Dharmapurikarbbae88312012-02-14 18:56:42 -08001851 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001852
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001853 if (firsttime && soc < 0) {
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001854 /*
1855 * first time calcualtion and the pon ocv is too low resulting
1856 * in a bad soc. Adjust ocv such that we get 0 soc
1857 */
1858 pr_debug("soc is %d, adjusting pon ocv to make it 0\n", soc);
1859 adjust_rc_and_uuc_for_specific_soc(
1860 chip,
1861 batt_temp, chargecycles,
1862 0,
1863 fcc_uah, unusable_charge_uah,
1864 cc_uah, remaining_charge_uah,
1865 rbatt,
1866 &new_ocv,
1867 &new_rc_uah, &new_ucc_uah,
1868 &new_rbatt);
1869 chip->last_ocv_uv = new_ocv;
1870 remaining_charge_uah = new_rc_uah;
1871 unusable_charge_uah = new_ucc_uah;
1872 rbatt = new_rbatt;
1873
1874 remaining_usable_charge_uah = remaining_charge_uah
1875 - cc_uah
1876 - unusable_charge_uah;
1877
1878 soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
1879 (fcc_uah - unusable_charge_uah));
1880 pr_debug("DONE for O soc is %d, pon ocv adjusted to %duV\n",
1881 soc, chip->last_ocv_uv);
1882 }
1883
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001884 if (soc > 100)
1885 soc = 100;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001886
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001887 if (bms_fake_battery != -EINVAL) {
1888 pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
1889 return bms_fake_battery;
1890 }
1891
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001892 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001893 pr_err("bad rem_usb_chg = %d rem_chg %d,"
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001894 "cc_uah %d, unusb_chg %d\n",
1895 remaining_usable_charge_uah,
1896 remaining_charge_uah,
1897 cc_uah, unusable_charge_uah);
1898
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001899 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001900 "chargecycles = %d, batt_temp = %d"
1901 "fcc = %d soc =%d\n",
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07001902 chip->last_ocv_uv, chargecycles, batt_temp,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001903 fcc_uah, soc);
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -08001904 soc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001905 }
1906
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001907 mutex_lock(&soc_invalidation_mutex);
1908 shutdown_soc = chip->shutdown_soc;
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001909
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001910 if (firsttime && soc != shutdown_soc
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001911 && is_shutdown_soc_within_limits(chip, soc)) {
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001912 /*
1913 * soc for the first time - use shutdown soc
1914 * to adjust pon ocv since it is a small percent away from
1915 * the real soc
1916 */
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001917 pr_debug("soc = %d before forcing shutdown_soc = %d\n",
1918 soc, shutdown_soc);
1919 adjust_rc_and_uuc_for_specific_soc(
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001920 chip,
1921 batt_temp, chargecycles,
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001922 shutdown_soc,
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001923 fcc_uah, unusable_charge_uah,
1924 cc_uah, remaining_charge_uah,
1925 rbatt,
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001926 &new_ocv,
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001927 &new_rc_uah, &new_ucc_uah,
1928 &new_rbatt);
1929
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001930 chip->pon_ocv_uv = chip->last_ocv_uv;
1931 chip->last_ocv_uv = new_ocv;
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001932 remaining_charge_uah = new_rc_uah;
1933 unusable_charge_uah = new_ucc_uah;
1934 rbatt = new_rbatt;
1935
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001936 remaining_usable_charge_uah = remaining_charge_uah
1937 - cc_uah
1938 - unusable_charge_uah;
1939
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001940 soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100),
1941 (fcc_uah - unusable_charge_uah));
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001942
1943 pr_debug("DONE for shutdown_soc = %d soc is %d, adjusted ocv to %duV\n",
1944 shutdown_soc, soc, chip->last_ocv_uv);
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001945 }
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07001946 mutex_unlock(&soc_invalidation_mutex);
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07001947
1948 calculated_soc = adjust_soc(chip, soc, batt_temp,
1949 rbatt, fcc_uah, unusable_charge_uah, cc_uah);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001950
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07001951 pr_debug("calculated SOC = %d\n", calculated_soc);
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07001952 firsttime = 0;
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07001953 return calculated_soc;
1954}
1955
1956#define CALCULATE_SOC_MS 20000
1957static void calculate_soc_work(struct work_struct *work)
1958{
1959 struct pm8921_bms_chip *chip = container_of(work,
1960 struct pm8921_bms_chip,
1961 calculate_soc_delayed_work.work);
1962 int batt_temp, rc;
1963 struct pm8xxx_adc_chan_result result;
1964 struct pm8921_soc_params raw;
1965 int soc;
1966
1967 rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
1968 if (rc) {
1969 pr_err("error reading adc channel = %d, rc = %d\n",
1970 chip->batt_temp_channel, rc);
1971 return;
1972 }
1973 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1974 result.measurement);
1975 batt_temp = (int)result.physical;
1976
1977 mutex_lock(&chip->last_ocv_uv_mutex);
1978 read_soc_params_raw(chip, &raw);
1979
1980 soc = calculate_state_of_charge(chip, &raw,
1981 batt_temp, last_chargecycles);
1982 mutex_unlock(&chip->last_ocv_uv_mutex);
1983
1984 schedule_delayed_work(&chip->calculate_soc_delayed_work,
1985 round_jiffies_relative(msecs_to_jiffies
1986 (CALCULATE_SOC_MS)));
1987}
1988
1989static int report_state_of_charge(struct pm8921_bms_chip *chip)
1990{
1991 int soc = calculated_soc;
1992 int delta_time_us;
1993 struct timespec now;
1994 struct pm8xxx_adc_chan_result result;
1995 int batt_temp;
1996 int rc;
1997
1998 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1999 if (rc) {
2000 pr_err("error reading adc channel = %d, rc = %d\n",
2001 the_chip->batt_temp_channel, rc);
2002 return rc;
2003 }
2004 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
2005 result.measurement);
2006 batt_temp = (int)result.physical;
2007
2008 do_posix_clock_monotonic_gettime(&now);
2009 if (chip->t_soc_queried.tv_sec != 0) {
2010 delta_time_us
2011 = (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC
2012 + (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000;
2013 } else {
2014 /* calculation for the first time */
2015 delta_time_us = 0;
2016 }
2017
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07002018 /*
2019 * account for charge time - limit it to SOC_CATCHUP_SEC to
2020 * avoid overflows when charging continues for extended periods
2021 */
2022 if (the_chip->start_percent != -EINVAL) {
2023 if (the_chip->charge_time_us == 0) {
2024 /*
2025 * calculating soc for the first time
2026 * after start of chg. Initialize catchup time
2027 */
2028 if (abs(soc - last_soc) < MAX_CATCHUP_SOC)
2029 the_chip->catch_up_time_us =
2030 (soc - last_soc) * SOC_CATCHUP_SEC_PER_PERCENT
2031 * USEC_PER_SEC;
2032 else
2033 the_chip->catch_up_time_us =
2034 SOC_CATCHUP_SEC_MAX * USEC_PER_SEC;
2035
2036 if (the_chip->catch_up_time_us < 0)
2037 the_chip->catch_up_time_us = 0;
2038 }
2039
2040 /* add charge time */
2041 if (the_chip->charge_time_us
2042 < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC)
2043 chip->charge_time_us += delta_time_us;
2044
2045 /* end catchup if calculated soc and last soc are same */
2046 if (last_soc == soc)
2047 the_chip->catch_up_time_us = 0;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002048 }
2049
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07002050 /* last_soc < soc ... scale and catch up */
2051 if (last_soc != -EINVAL && last_soc < soc && soc != 100)
2052 soc = scale_soc_while_chg(chip, delta_time_us, soc, last_soc);
2053
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07002054 if (chip->shutdown_soc != 0
2055 && !shutdown_soc_invalid
2056 && !chip->shutdown_soc_timer_expired) {
2057 /*
2058 * force shutdown soc if it is valid and the shutdown soc show
2059 * timer has not expired
2060 */
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07002061 soc = chip->shutdown_soc;
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07002062
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07002063 pr_debug("Forcing SHUTDOWN_SOC = %d\n", soc);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07002064 }
2065
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07002066 last_soc = soc;
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07002067 backup_soc_and_iavg(chip, batt_temp, last_soc);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07002068 pr_debug("Reported SOC = %d\n", last_soc);
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07002069 chip->t_soc_queried = now;
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07002070
2071 return last_soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002072}
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07002073
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07002074void pm8921_bms_invalidate_shutdown_soc(void)
2075{
2076 int calculate_soc = 0;
2077 struct pm8921_bms_chip *chip = the_chip;
2078 int batt_temp, rc;
2079 struct pm8xxx_adc_chan_result result;
2080 struct pm8921_soc_params raw;
2081 int soc;
2082
2083 pr_debug("Invalidating shutdown soc - the battery was removed\n");
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07002084 if (shutdown_soc_invalid)
2085 return;
2086
Abhijeet Dharmapurikard74be6b2012-07-21 00:02:42 -07002087 mutex_lock(&soc_invalidation_mutex);
2088 shutdown_soc_invalid = 1;
2089 last_soc = -EINVAL;
2090 if (the_chip) {
2091 /* reset to pon ocv undoing what the adjusting did */
2092 if (the_chip->pon_ocv_uv) {
2093 the_chip->last_ocv_uv = the_chip->pon_ocv_uv;
2094 calculate_soc = 1;
2095 pr_debug("resetting ocv to pon_ocv = %d\n",
2096 the_chip->pon_ocv_uv);
2097 }
2098 }
2099 mutex_unlock(&soc_invalidation_mutex);
2100 if (!calculate_soc)
2101 return;
2102
2103 rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
2104 if (rc) {
2105 pr_err("error reading adc channel = %d, rc = %d\n",
2106 chip->batt_temp_channel, rc);
2107 return;
2108 }
2109 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
2110 result.measurement);
2111 batt_temp = (int)result.physical;
2112
2113 mutex_lock(&chip->last_ocv_uv_mutex);
2114 read_soc_params_raw(chip, &raw);
2115
2116 soc = calculate_state_of_charge(chip, &raw,
2117 batt_temp, last_chargecycles);
2118 mutex_unlock(&chip->last_ocv_uv_mutex);
2119}
2120EXPORT_SYMBOL(pm8921_bms_invalidate_shutdown_soc);
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07002121
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002122#define MIN_DELTA_625_UV 1000
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002123static void calib_hkadc(struct pm8921_bms_chip *chip)
2124{
2125 int voltage, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002126 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002127 int usb_chg;
2128 int this_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002129
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002130 mutex_lock(&chip->calib_mutex);
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002131 rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002132 if (rc) {
2133 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002134 goto out;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002135 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08002136 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002137
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002138 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
2139 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002140
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002141 chip->xoadc_v125 = voltage;
2142
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002143 rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002144 if (rc) {
2145 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002146 goto out;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002147 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08002148 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002149
2150 usb_chg = usb_chg_plugged_in();
2151 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
2152 "usb_chg = %d\n",
2153 result.adc_code, voltage, result.measurement,
2154 usb_chg);
2155
2156 if (usb_chg)
2157 chip->xoadc_v0625_usb_present = voltage;
2158 else
2159 chip->xoadc_v0625_usb_absent = voltage;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002160
2161 chip->xoadc_v0625 = voltage;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002162 if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) {
2163 this_delta = chip->xoadc_v0625_usb_present
2164 - chip->xoadc_v0625_usb_absent;
2165 pr_debug("this_delta= %duV\n", this_delta);
2166 if (this_delta > MIN_DELTA_625_UV)
2167 last_usb_cal_delta_uv = this_delta;
2168 pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n",
2169 chip->xoadc_v0625_usb_present,
2170 chip->xoadc_v0625_usb_absent,
2171 last_usb_cal_delta_uv);
2172 }
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002173out:
2174 mutex_unlock(&chip->calib_mutex);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002175}
2176
2177static void calibrate_hkadc_work(struct work_struct *work)
2178{
2179 struct pm8921_bms_chip *chip = container_of(work,
2180 struct pm8921_bms_chip, calib_hkadc_work);
2181
2182 calib_hkadc(chip);
2183}
2184
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08002185void pm8921_bms_calibrate_hkadc(void)
2186{
2187 schedule_work(&the_chip->calib_hkadc_work);
2188}
2189
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07002190#define HKADC_CALIB_DELAY_MS 600000
2191static void calibrate_hkadc_delayed_work(struct work_struct *work)
2192{
2193 struct pm8921_bms_chip *chip = container_of(work,
2194 struct pm8921_bms_chip,
2195 calib_hkadc_delayed_work.work);
2196
2197 calib_hkadc(chip);
2198 schedule_delayed_work(&chip->calib_hkadc_delayed_work,
2199 round_jiffies_relative(msecs_to_jiffies
2200 (HKADC_CALIB_DELAY_MS)));
2201}
2202
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002203int pm8921_bms_get_vsense_avg(int *result)
2204{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002205 int rc = -EINVAL;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002206
2207 if (the_chip) {
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002208 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002209 pm_bms_lock_output_data(the_chip);
2210 rc = read_vsense_avg(the_chip, result);
2211 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002212 mutex_unlock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002213 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002214
2215 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002216 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002217}
2218EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
2219
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08002220int pm8921_bms_get_battery_current(int *result_ua)
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002221{
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002222 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002223
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002224 if (!the_chip) {
2225 pr_err("called before initialization\n");
2226 return -EINVAL;
2227 }
2228 if (the_chip->r_sense == 0) {
2229 pr_err("r_sense is zero\n");
2230 return -EINVAL;
2231 }
2232
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002233 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002234 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002235 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07002236 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002237 mutex_unlock(&the_chip->bms_output_lock);
2238 pr_debug("vsense=%duV\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07002239 /* cast for signed division */
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08002240 *result_ua = vsense * 1000 / (int)the_chip->r_sense;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002241 pr_debug("ibat=%duA\n", *result_ua);
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002242 return 0;
2243}
2244EXPORT_SYMBOL(pm8921_bms_get_battery_current);
2245
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002246int pm8921_bms_get_percent_charge(void)
2247{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002248 if (!the_chip) {
2249 pr_err("called before initialization\n");
2250 return -EINVAL;
2251 }
2252
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07002253 return report_state_of_charge(the_chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002254}
2255EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
2256
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002257int pm8921_bms_get_rbatt(void)
2258{
2259 int batt_temp, rc;
2260 struct pm8xxx_adc_chan_result result;
2261 struct pm8921_soc_params raw;
2262 int fcc_uah;
2263 int unusable_charge_uah;
2264 int remaining_charge_uah;
2265 int cc_uah;
2266 int rbatt;
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07002267 int iavg_ua;
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07002268 int delta_time_s;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002269
2270 if (!the_chip) {
2271 pr_err("called before initialization\n");
2272 return -EINVAL;
2273 }
2274
2275 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
2276 if (rc) {
2277 pr_err("error reading adc channel = %d, rc = %d\n",
2278 the_chip->batt_temp_channel, rc);
2279 return rc;
2280 }
2281 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
2282 result.measurement);
2283 batt_temp = (int)result.physical;
2284
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002285 mutex_lock(&the_chip->last_ocv_uv_mutex);
2286
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002287 read_soc_params_raw(the_chip, &raw);
2288
2289 calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles,
2290 &fcc_uah,
2291 &unusable_charge_uah,
2292 &remaining_charge_uah,
2293 &cc_uah,
Abhijeet Dharmapurikar30f30f72012-04-26 17:34:18 -07002294 &rbatt,
2295 &iavg_ua,
Abhijeet Dharmapurikar16582152012-07-14 19:55:17 -07002296 &delta_time_s);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002297 mutex_unlock(&the_chip->last_ocv_uv_mutex);
2298
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002299 return rbatt;
2300}
2301EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt);
2302
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07002303int pm8921_bms_get_fcc(void)
2304{
2305 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002306 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07002307
2308 if (!the_chip) {
2309 pr_err("called before initialization\n");
2310 return -EINVAL;
2311 }
2312
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002313 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07002314 if (rc) {
2315 pr_err("error reading adc channel = %d, rc = %d\n",
2316 the_chip->batt_temp_channel, rc);
2317 return rc;
2318 }
2319 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
2320 result.measurement);
2321 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002322 return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07002323}
2324EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
2325
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08002326#define IBAT_TOL_MASK 0x0F
2327#define OCV_TOL_MASK 0xF0
2328#define IBAT_TOL_DEFAULT 0x03
2329#define IBAT_TOL_NOCHG 0x0F
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002330#define OCV_TOL_DEFAULT 0x20
2331#define OCV_TOL_NO_OCV 0x00
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002332void pm8921_bms_charging_began(void)
2333{
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002334 struct pm8921_soc_params raw;
2335
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002336 mutex_lock(&the_chip->last_ocv_uv_mutex);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002337 read_soc_params_raw(the_chip, &raw);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002338 mutex_unlock(&the_chip->last_ocv_uv_mutex);
2339
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07002340 the_chip->start_percent = report_state_of_charge(the_chip);
2341
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002342 bms_start_percent = the_chip->start_percent;
2343 bms_start_ocv_uv = raw.last_good_ocv_uv;
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08002344 calculate_cc_uah(the_chip, raw.cc, &bms_start_cc_uah);
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08002345 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
2346 IBAT_TOL_MASK, IBAT_TOL_DEFAULT);
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07002347 the_chip->charge_time_us = 0;
2348 the_chip->catch_up_time_us = 0;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002349 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002350}
2351EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
2352
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002353#define DELTA_FCC_PERCENT 3
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002354#define MIN_START_PERCENT_FOR_LEARNING 30
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07002355void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002356{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08002357 int batt_temp, rc;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002358 struct pm8xxx_adc_chan_result result;
2359 struct pm8921_soc_params raw;
2360
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002361 if (the_chip == NULL)
2362 return;
2363
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002364 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
2365 if (rc) {
2366 pr_err("error reading adc channel = %d, rc = %d\n",
2367 the_chip->batt_temp_channel, rc);
2368 return;
2369 }
2370 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
2371 result.measurement);
2372 batt_temp = (int)result.physical;
2373
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002374 mutex_lock(&the_chip->last_ocv_uv_mutex);
2375
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002376 read_soc_params_raw(the_chip, &raw);
2377
Abhijeet Dharmapurikar57aeb922012-01-25 13:22:26 -08002378 calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah);
2379
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002380 bms_end_ocv_uv = raw.last_good_ocv_uv;
2381
Abhijeet Dharmapurikare88f2462012-04-25 18:22:38 -07002382 if (is_battery_full && the_chip->enable_fcc_learning
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002383 && the_chip->start_percent <= MIN_START_PERCENT_FOR_LEARNING) {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002384 int fcc_uah, new_fcc_uah, delta_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07002385
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002386 new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002387 batt_temp, last_chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002388 &fcc_uah);
2389 delta_fcc_uah = new_fcc_uah - fcc_uah;
2390 if (delta_fcc_uah < 0)
2391 delta_fcc_uah = -delta_fcc_uah;
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002392
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002393 if (delta_fcc_uah * 100 > (DELTA_FCC_PERCENT * fcc_uah)) {
2394 /* new_fcc_uah is outside the scope limit it */
2395 if (new_fcc_uah > fcc_uah)
2396 new_fcc_uah
Abhijeet Dharmapurikar873ffd12012-04-17 00:06:15 -07002397 = (fcc_uah +
2398 (DELTA_FCC_PERCENT * fcc_uah) / 100);
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002399 else
2400 new_fcc_uah
Abhijeet Dharmapurikar873ffd12012-04-17 00:06:15 -07002401 = (fcc_uah -
2402 (DELTA_FCC_PERCENT * fcc_uah) / 100);
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002403
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002404 pr_debug("delta_fcc=%d > %d percent of fcc=%d"
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002405 "restring it to %d\n",
2406 delta_fcc_uah, DELTA_FCC_PERCENT,
2407 fcc_uah, new_fcc_uah);
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08002408 }
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08002409
Abhijeet Dharmapurikarfe36d232012-03-06 20:17:54 -08002410 last_real_fcc_mah = new_fcc_uah/1000;
2411 last_real_fcc_batt_temp = batt_temp;
2412 readjust_fcc_table();
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002413 }
2414
2415 if (is_battery_full) {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002416 the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
2417 the_chip->cc_reading_at_100 = raw.cc;
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002418
2419 the_chip->last_ocv_uv = the_chip->max_voltage_uv;
2420 raw.last_good_ocv_uv = the_chip->max_voltage_uv;
Abhijeet Dharmapurikarb40b96f2012-04-24 13:19:25 -07002421 /*
2422 * since we are treating this as an ocv event
2423 * forget the old cc value
2424 */
2425 the_chip->last_cc_uah = 0;
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07002426 pr_debug("EOC BATT_FULL ocv_reading = 0x%x cc = 0x%x\n",
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08002427 the_chip->ocv_reading_at_100,
2428 the_chip->cc_reading_at_100);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07002429 }
2430
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002431 the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
2432 batt_temp, last_chargecycles);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002433 mutex_unlock(&the_chip->last_ocv_uv_mutex);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002434
2435 bms_end_percent = the_chip->end_percent;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08002436
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002437 if (the_chip->end_percent > the_chip->start_percent) {
Abhijeet Dharmapurikar8a113f82012-01-19 10:58:45 -08002438 last_charge_increase +=
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002439 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002440 if (last_charge_increase > 100) {
2441 last_chargecycles++;
2442 last_charge_increase = last_charge_increase % 100;
2443 }
2444 }
2445 pr_debug("end_percent = %u%% last_charge_increase = %d"
2446 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07002447 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002448 last_charge_increase,
2449 last_chargecycles);
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08002450 the_chip->start_percent = -EINVAL;
2451 the_chip->end_percent = -EINVAL;
Abhijeet Dharmapurikar647d0f82012-08-13 18:39:15 -07002452 the_chip->charge_time_us = 0;
2453 the_chip->catch_up_time_us = 0;
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08002454 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
2455 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002456}
2457EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
2458
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002459int pm8921_bms_stop_ocv_updates(struct pm8921_bms_chip *chip)
2460{
2461 pr_debug("stopping ocv updates\n");
2462 return pm_bms_masked_write(chip, BMS_TOLERANCES,
2463 OCV_TOL_MASK, OCV_TOL_NO_OCV);
2464}
2465EXPORT_SYMBOL_GPL(pm8921_bms_stop_ocv_updates);
2466
2467int pm8921_bms_start_ocv_updates(struct pm8921_bms_chip *chip)
2468{
2469 pr_debug("stopping ocv updates\n");
2470 return pm_bms_masked_write(chip, BMS_TOLERANCES,
2471 OCV_TOL_MASK, OCV_TOL_DEFAULT);
2472}
2473EXPORT_SYMBOL_GPL(pm8921_bms_start_ocv_updates);
2474
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002475static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
2476{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002477 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002478 return IRQ_HANDLED;
2479}
2480
2481static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
2482{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002483 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002484 return IRQ_HANDLED;
2485}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002486
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002487static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
2488{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002489 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002490 return IRQ_HANDLED;
2491}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002492
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002493static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
2494{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002495 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002496 return IRQ_HANDLED;
2497}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002498
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002499static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
2500{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002501 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002502
2503 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002504 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002505 return IRQ_HANDLED;
2506}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002507
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002508static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
2509{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002510 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002511
2512 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002513 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002514 return IRQ_HANDLED;
2515}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002516
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002517static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
2518{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002519 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002520 return IRQ_HANDLED;
2521}
2522
2523struct pm_bms_irq_init_data {
2524 unsigned int irq_id;
2525 char *name;
2526 unsigned long flags;
2527 irqreturn_t (*handler)(int, void *);
2528};
2529
2530#define BMS_IRQ(_id, _flags, _handler) \
2531{ \
2532 .irq_id = _id, \
2533 .name = #_id, \
2534 .flags = _flags, \
2535 .handler = _handler, \
2536}
2537
2538struct pm_bms_irq_init_data bms_irq_data[] = {
2539 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
2540 pm8921_bms_sbi_write_ok_handler),
2541 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
2542 pm8921_bms_cc_thr_handler),
2543 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
2544 pm8921_bms_vsense_thr_handler),
2545 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
2546 pm8921_bms_vsense_for_r_handler),
2547 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
2548 pm8921_bms_ocv_for_r_handler),
2549 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
2550 pm8921_bms_good_ocv_handler),
2551 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
2552 pm8921_bms_vsense_avg_handler),
2553};
2554
2555static void free_irqs(struct pm8921_bms_chip *chip)
2556{
2557 int i;
2558
2559 for (i = 0; i < PM_BMS_MAX_INTS; i++)
2560 if (chip->pmic_bms_irq[i]) {
2561 free_irq(chip->pmic_bms_irq[i], NULL);
2562 chip->pmic_bms_irq[i] = 0;
2563 }
2564}
2565
2566static int __devinit request_irqs(struct pm8921_bms_chip *chip,
2567 struct platform_device *pdev)
2568{
2569 struct resource *res;
2570 int ret, i;
2571
2572 ret = 0;
2573 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
2574
2575 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2576 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
2577 bms_irq_data[i].name);
2578 if (res == NULL) {
2579 pr_err("couldn't find %s\n", bms_irq_data[i].name);
2580 goto err_out;
2581 }
2582 ret = request_irq(res->start, bms_irq_data[i].handler,
2583 bms_irq_data[i].flags,
2584 bms_irq_data[i].name, chip);
2585 if (ret < 0) {
2586 pr_err("couldn't request %d (%s) %d\n", res->start,
2587 bms_irq_data[i].name, ret);
2588 goto err_out;
2589 }
2590 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
2591 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
2592 }
2593 return 0;
2594
2595err_out:
2596 free_irqs(chip);
2597 return -EINVAL;
2598}
2599
2600#define EN_BMS_BIT BIT(7)
2601#define EN_PON_HS_BIT BIT(0)
2602static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
2603{
2604 int rc;
2605
2606 rc = pm_bms_masked_write(chip, BMS_CONTROL,
2607 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
2608 if (rc) {
2609 pr_err("failed to enable pon and bms addr = %d %d",
2610 BMS_CONTROL, rc);
2611 }
2612
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08002613 /* The charger will call start charge later if usb is present */
2614 pm_bms_masked_write(chip, BMS_TOLERANCES,
2615 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002616 return 0;
2617}
2618
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002619static void check_initial_ocv(struct pm8921_bms_chip *chip)
2620{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002621 int ocv_uv, rc;
2622 int16_t ocv_raw;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002623 int usb_chg;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002624
2625 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002626 * Check if a ocv is available in bms hw,
2627 * if not compute it here at boot time and save it
2628 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002629 */
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002630 ocv_uv = 0;
2631 pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08002632 usb_chg = usb_chg_plugged_in();
2633 rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002634 if (rc || ocv_uv == 0) {
2635 rc = adc_based_ocv(chip, &ocv_uv);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002636 if (rc) {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002637 pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
2638 ocv_uv = DEFAULT_OCV_MICROVOLTS;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002639 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002640 }
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07002641 chip->last_ocv_uv = ocv_uv;
2642 pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, chip->last_ocv_uv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002643}
2644
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002645static int64_t read_battery_id(struct pm8921_bms_chip *chip)
2646{
2647 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002648 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002649
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07002650 rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002651 if (rc) {
2652 pr_err("error reading batt id channel = %d, rc = %d\n",
2653 chip->vbat_channel, rc);
2654 return rc;
2655 }
2656 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
2657 result.measurement);
David Keitel8f2601b2012-02-14 22:31:07 -08002658 return result.adc_code;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002659}
2660
David Keitel8f2601b2012-02-14 22:31:07 -08002661#define PALLADIUM_ID_MIN 0x7F40
2662#define PALLADIUM_ID_MAX 0x7F5A
2663#define DESAY_5200_ID_MIN 0x7F7F
2664#define DESAY_5200_ID_MAX 0x802F
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002665static int set_battery_data(struct pm8921_bms_chip *chip)
2666{
2667 int64_t battery_id;
2668
David Keitel35e11872012-02-17 17:40:42 -08002669 if (chip->batt_type == BATT_DESAY)
2670 goto desay;
2671 else if (chip->batt_type == BATT_PALLADIUM)
2672 goto palladium;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002673
David Keitel35e11872012-02-17 17:40:42 -08002674 battery_id = read_battery_id(chip);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002675 if (battery_id < 0) {
2676 pr_err("cannot read battery id err = %lld\n", battery_id);
2677 return battery_id;
2678 }
2679
2680 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
David Keitel35e11872012-02-17 17:40:42 -08002681 goto palladium;
2682 } else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
2683 battery_id)) {
2684 goto desay;
2685 } else {
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002686 pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
2687 battery_id);
2688 goto palladium;
David Keitel35e11872012-02-17 17:40:42 -08002689 }
2690
2691palladium:
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002692 chip->fcc = palladium_1500_data.fcc;
2693 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
2694 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
2695 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
2696 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002697 chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
2698 chip->default_rbatt_mohm
2699 = palladium_1500_data.default_rbatt_mohm;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07002700 chip->delta_rbatt_mohm = palladium_1500_data.delta_rbatt_mohm;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002701 return 0;
David Keitel35e11872012-02-17 17:40:42 -08002702desay:
David Keitel8f2601b2012-02-14 22:31:07 -08002703 chip->fcc = desay_5200_data.fcc;
2704 chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
David Keitel8f2601b2012-02-14 22:31:07 -08002705 chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
2706 chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08002707 chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
2708 chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
Abhijeet Dharmapurikarded189b2012-03-30 10:12:28 -07002709 chip->delta_rbatt_mohm = desay_5200_data.delta_rbatt_mohm;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002710 return 0;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002711}
2712
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002713enum bms_request_operation {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002714 CALC_FCC,
2715 CALC_PC,
2716 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002717 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002718 CALIB_CCADC,
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002719 GET_VBAT_VSENSE_SIMULTANEOUS,
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002720 STOP_OCV,
2721 START_OCV,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002722};
2723
2724static int test_batt_temp = 5;
2725static int test_chargecycle = 150;
2726static int test_ocv = 3900000;
2727enum {
2728 TEST_BATT_TEMP,
2729 TEST_CHARGE_CYCLE,
2730 TEST_OCV,
2731};
2732static int get_test_param(void *data, u64 * val)
2733{
2734 switch ((int)data) {
2735 case TEST_BATT_TEMP:
2736 *val = test_batt_temp;
2737 break;
2738 case TEST_CHARGE_CYCLE:
2739 *val = test_chargecycle;
2740 break;
2741 case TEST_OCV:
2742 *val = test_ocv;
2743 break;
2744 default:
2745 return -EINVAL;
2746 }
2747 return 0;
2748}
2749static int set_test_param(void *data, u64 val)
2750{
2751 switch ((int)data) {
2752 case TEST_BATT_TEMP:
2753 test_batt_temp = (int)val;
2754 break;
2755 case TEST_CHARGE_CYCLE:
2756 test_chargecycle = (int)val;
2757 break;
2758 case TEST_OCV:
2759 test_ocv = (int)val;
2760 break;
2761 default:
2762 return -EINVAL;
2763 }
2764 return 0;
2765}
2766DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
2767
2768static int get_calc(void *data, u64 * val)
2769{
2770 int param = (int)data;
2771 int ret = 0;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002772 int ibat_ua, vbat_uv;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002773 struct pm8921_soc_params raw;
2774
2775 read_soc_params_raw(the_chip, &raw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002776
2777 *val = 0;
2778
2779 /* global irq number passed in via data */
2780 switch (param) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002781 case CALC_FCC:
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08002782 *val = calculate_fcc_uah(the_chip, test_batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002783 test_chargecycle);
2784 break;
2785 case CALC_PC:
2786 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
2787 test_chargecycle);
2788 break;
2789 case CALC_SOC:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002790 *val = calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002791 test_batt_temp, test_chargecycle);
2792 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002793 case CALIB_HKADC:
2794 /* reading this will trigger calibration */
2795 *val = 0;
2796 calib_hkadc(the_chip);
2797 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002798 case CALIB_CCADC:
2799 /* reading this will trigger calibration */
2800 *val = 0;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002801 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002802 break;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002803 case GET_VBAT_VSENSE_SIMULTANEOUS:
2804 /* reading this will call simultaneous vbat and vsense */
2805 *val =
2806 pm8921_bms_get_simultaneous_battery_voltage_and_current(
2807 &ibat_ua,
2808 &vbat_uv);
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002809 default:
2810 ret = -EINVAL;
2811 }
2812 return ret;
2813}
2814
2815static int set_calc(void *data, u64 val)
2816{
2817 int param = (int)data;
2818 int ret = 0;
2819
2820 switch (param) {
2821 case STOP_OCV:
2822 pm8921_bms_stop_ocv_updates(the_chip);
2823 break;
2824 case START_OCV:
2825 pm8921_bms_start_ocv_updates(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002826 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002827 default:
2828 ret = -EINVAL;
2829 }
2830 return ret;
2831}
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002832DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, set_calc, "%llu\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002833
2834static int get_reading(void *data, u64 * val)
2835{
2836 int param = (int)data;
2837 int ret = 0;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002838 struct pm8921_soc_params raw;
2839
2840 read_soc_params_raw(the_chip, &raw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002841
2842 *val = 0;
2843
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002844 switch (param) {
2845 case CC_MSB:
2846 case CC_LSB:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002847 *val = raw.cc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002848 break;
2849 case LAST_GOOD_OCV_VALUE:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002850 *val = raw.last_good_ocv_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002851 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002852 case VSENSE_AVG:
2853 read_vsense_avg(the_chip, (uint *)val);
2854 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002855 default:
2856 ret = -EINVAL;
2857 }
2858 return ret;
2859}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07002860DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002861
2862static int get_rt_status(void *data, u64 * val)
2863{
2864 int i = (int)data;
2865 int ret;
2866
2867 /* global irq number passed in via data */
2868 ret = pm_bms_get_rt_status(the_chip, i);
2869 *val = ret;
2870 return 0;
2871}
2872DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
2873
2874static int get_reg(void *data, u64 * val)
2875{
2876 int addr = (int)data;
2877 int ret;
2878 u8 temp;
2879
2880 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
2881 if (ret) {
2882 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
2883 addr, temp, ret);
2884 return -EAGAIN;
2885 }
2886 *val = temp;
2887 return 0;
2888}
2889
2890static int set_reg(void *data, u64 val)
2891{
2892 int addr = (int)data;
2893 int ret;
2894 u8 temp;
2895
2896 temp = (u8) val;
2897 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
2898 if (ret) {
2899 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
2900 addr, temp, ret);
2901 return -EAGAIN;
2902 }
2903 return 0;
2904}
2905DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
2906
2907static void create_debugfs_entries(struct pm8921_bms_chip *chip)
2908{
2909 int i;
2910
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08002911 chip->dent = debugfs_create_dir("pm8921-bms", NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002912
2913 if (IS_ERR(chip->dent)) {
2914 pr_err("pmic bms couldnt create debugfs dir\n");
2915 return;
2916 }
2917
2918 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2919 (void *)BMS_CONTROL, &reg_fops);
2920 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2921 (void *)BMS_OUTPUT0, &reg_fops);
2922 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2923 (void *)BMS_OUTPUT1, &reg_fops);
2924 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2925 (void *)BMS_TEST1, &reg_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002926
2927 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2928 (void *)TEST_BATT_TEMP, &temp_fops);
2929 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2930 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2931 debugfs_create_file("test_ocv", 0644, chip->dent,
2932 (void *)TEST_OCV, &temp_fops);
2933
2934 debugfs_create_file("read_cc", 0644, chip->dent,
2935 (void *)CC_MSB, &reading_fops);
2936 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2937 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2938 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2939 (void *)VBATT_FOR_RBATT, &reading_fops);
2940 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2941 (void *)VSENSE_FOR_RBATT, &reading_fops);
2942 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2943 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002944 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2945 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002946
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002947 debugfs_create_file("show_fcc", 0644, chip->dent,
2948 (void *)CALC_FCC, &calc_fops);
2949 debugfs_create_file("show_pc", 0644, chip->dent,
2950 (void *)CALC_PC, &calc_fops);
2951 debugfs_create_file("show_soc", 0644, chip->dent,
2952 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002953 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2954 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002955 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2956 (void *)CALIB_CCADC, &calc_fops);
Abhijeet Dharmapurikarc99f5862012-03-14 19:33:16 -07002957 debugfs_create_file("stop_ocv", 0644, chip->dent,
2958 (void *)STOP_OCV, &calc_fops);
2959 debugfs_create_file("start_ocv", 0644, chip->dent,
2960 (void *)START_OCV, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002961
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002962 debugfs_create_file("simultaneous", 0644, chip->dent,
2963 (void *)GET_VBAT_VSENSE_SIMULTANEOUS, &calc_fops);
2964
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002965 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2966 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2967 debugfs_create_file(bms_irq_data[i].name, 0444,
2968 chip->dent,
2969 (void *)bms_irq_data[i].irq_id,
2970 &rt_fops);
2971 }
2972}
2973
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -07002974#define REG_SBI_CONFIG 0x04F
2975#define PAGE3_ENABLE_MASK 0x6
2976#define PROGRAM_REV_MASK 0x0F
2977#define PROGRAM_REV 0x9
2978static int read_ocv_trim(struct pm8921_bms_chip *chip)
2979{
2980 int rc;
2981 u8 reg, sbi_config;
2982
2983 rc = pm8xxx_readb(chip->dev->parent, REG_SBI_CONFIG, &sbi_config);
2984 if (rc) {
2985 pr_err("error = %d reading sbi config reg\n", rc);
2986 return rc;
2987 }
2988
2989 reg = sbi_config | PAGE3_ENABLE_MASK;
2990 rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, reg);
2991 if (rc) {
2992 pr_err("error = %d writing sbi config reg\n", rc);
2993 return rc;
2994 }
2995
2996 rc = pm8xxx_readb(chip->dev->parent, TEST_PROGRAM_REV, &reg);
2997 if (rc)
2998 pr_err("Error %d reading %d addr %d\n",
2999 rc, reg, TEST_PROGRAM_REV);
3000 pr_err("program rev reg is 0x%x\n", reg);
3001 reg &= PROGRAM_REV_MASK;
3002
3003 /* If the revision is equal or higher do not adjust trim delta */
3004 if (reg >= PROGRAM_REV) {
3005 chip->amux_2_trim_delta = 0;
3006 goto restore_sbi_config;
3007 }
3008
3009 rc = pm8xxx_readb(chip->dev->parent, AMUX_TRIM_2, &reg);
3010 if (rc) {
3011 pr_err("error = %d reading trim reg\n", rc);
3012 return rc;
3013 }
3014
3015 pr_err("trim reg is 0x%x\n", reg);
3016 chip->amux_2_trim_delta = abs(0x49 - reg);
3017 pr_err("trim delta is %d\n", chip->amux_2_trim_delta);
3018
3019restore_sbi_config:
3020 rc = pm8xxx_writeb(chip->dev->parent, REG_SBI_CONFIG, sbi_config);
3021 if (rc) {
3022 pr_err("error = %d writing sbi config reg\n", rc);
3023 return rc;
3024 }
3025
3026 return 0;
3027}
3028
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003029static int __devinit pm8921_bms_probe(struct platform_device *pdev)
3030{
3031 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07003032 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003033 struct pm8921_bms_chip *chip;
3034 const struct pm8921_bms_platform_data *pdata
3035 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07003036
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003037 if (!pdata) {
3038 pr_err("missing platform data\n");
3039 return -EINVAL;
3040 }
3041
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07003042 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003043 if (!chip) {
3044 pr_err("Cannot allocate pm_bms_chip\n");
3045 return -ENOMEM;
3046 }
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07003047
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08003048 mutex_init(&chip->bms_output_lock);
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07003049 mutex_init(&chip->last_ocv_uv_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003050 chip->dev = &pdev->dev;
3051 chip->r_sense = pdata->r_sense;
Abhijeet Dharmapurikara51c87b2012-07-13 20:13:14 -07003052 chip->v_cutoff = pdata->v_cutoff;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -08003053 chip->max_voltage_uv = pdata->max_voltage_uv;
David Keitel35e11872012-02-17 17:40:42 -08003054 chip->batt_type = pdata->battery_type;
Abhijeet Dharmapurikarbaffba42012-03-22 14:41:10 -07003055 chip->rconn_mohm = pdata->rconn_mohm;
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08003056 chip->start_percent = -EINVAL;
3057 chip->end_percent = -EINVAL;
Abhijeet Dharmapurikar602dc272012-07-19 21:34:56 -07003058 chip->shutdown_soc_valid_limit = pdata->shutdown_soc_valid_limit;
Abhijeet Dharmapurikar720b00e2012-08-01 21:33:09 -07003059 chip->adjust_soc_low_threshold = pdata->adjust_soc_low_threshold;
3060 if (chip->adjust_soc_low_threshold >= 45)
3061 chip->adjust_soc_low_threshold = 45;
3062
Abhijeet Dharmapurikar1f4cc512012-07-21 00:10:39 -07003063 chip->ignore_shutdown_soc = pdata->ignore_shutdown_soc;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07003064 rc = set_battery_data(chip);
3065 if (rc) {
3066 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07003067 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07003068 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003069
Abhijeet Dharmapurikarf6fdcaa02012-02-27 00:10:10 -08003070 if (chip->pc_temp_ocv_lut == NULL) {
3071 pr_err("temp ocv lut table is NULL\n");
3072 rc = -EINVAL;
3073 goto free_chip;
3074 }
3075
3076 /* set defaults in the battery data */
3077 if (chip->default_rbatt_mohm <= 0)
3078 chip->default_rbatt_mohm = DEFAULT_RBATT_MOHMS;
3079
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07003080 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
3081 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07003082 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
3083 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07003084 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07003085 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikare88f2462012-04-25 18:22:38 -07003086 chip->enable_fcc_learning = pdata->enable_fcc_learning;
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07003087
3088 mutex_init(&chip->calib_mutex);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07003089 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07003090 INIT_DELAYED_WORK(&chip->calib_hkadc_delayed_work,
3091 calibrate_hkadc_delayed_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07003092
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07003093 INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work,
3094 calculate_soc_work);
3095
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003096 rc = request_irqs(chip, pdev);
3097 if (rc) {
3098 pr_err("couldn't register interrupts rc = %d\n", rc);
3099 goto free_chip;
3100 }
3101
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07003102 rc = pm8921_bms_hw_init(chip);
3103 if (rc) {
3104 pr_err("couldn't init hardware rc = %d\n", rc);
3105 goto free_irqs;
3106 }
3107
Abhijeet Dharmapurikara7daba52012-07-27 20:40:51 -07003108 read_shutdown_soc_and_iavg(chip);
Abhijeet Dharmapurikared2d87e2012-06-26 20:15:36 -07003109
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003110 platform_set_drvdata(pdev, chip);
3111 the_chip = chip;
3112 create_debugfs_entries(chip);
3113
Abhijeet Dharmapurikarfd8655c2012-03-18 16:41:58 -07003114 rc = read_ocv_trim(chip);
3115 if (rc) {
3116 pr_err("couldn't adjust ocv_trim rc= %d\n", rc);
3117 goto free_irqs;
3118 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07003119 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07003120
Abhijeet Dharmapurikar9c0e6042012-07-12 14:55:13 -07003121 /* start periodic hkadc calibration */
3122 schedule_delayed_work(&chip->calib_hkadc_delayed_work, 0);
3123
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07003124 /* enable the vbatt reading interrupts for scheduling hkadc calib */
3125 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
3126 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07003127
Abhijeet Dharmapurikar41b46cd2012-07-18 19:38:43 -07003128 calculate_soc_work(&(chip->calculate_soc_delayed_work.work));
3129
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07003130 get_battery_uvolts(chip, &vbatt);
3131 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
3132 pm8921_bms_get_percent_charge(),
Abhijeet Dharmapurikar10bc0fd2012-04-18 21:03:50 -07003133 vbatt, chip->last_ocv_uv);
Abhijeet Dharmapurikar807249a2012-07-12 20:26:27 -07003134
3135 INIT_DELAYED_WORK(&chip->shutdown_soc_work, shutdown_soc_work);
3136 schedule_delayed_work(&chip->shutdown_soc_work,
3137 round_jiffies_relative(msecs_to_jiffies
3138 (SHOW_SHUTDOWN_SOC_MS)));
3139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003140 return 0;
3141
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07003142free_irqs:
3143 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003144free_chip:
3145 kfree(chip);
3146 return rc;
3147}
3148
3149static int __devexit pm8921_bms_remove(struct platform_device *pdev)
3150{
3151 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
3152
3153 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07003154 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003155 platform_set_drvdata(pdev, NULL);
3156 the_chip = NULL;
3157 kfree(chip);
3158 return 0;
3159}
3160
3161static struct platform_driver pm8921_bms_driver = {
3162 .probe = pm8921_bms_probe,
3163 .remove = __devexit_p(pm8921_bms_remove),
3164 .driver = {
3165 .name = PM8921_BMS_DEV_NAME,
3166 .owner = THIS_MODULE,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003167 },
3168};
3169
3170static int __init pm8921_bms_init(void)
3171{
3172 return platform_driver_register(&pm8921_bms_driver);
3173}
3174
3175static void __exit pm8921_bms_exit(void)
3176{
3177 platform_driver_unregister(&pm8921_bms_driver);
3178}
3179
3180late_initcall(pm8921_bms_init);
3181module_exit(pm8921_bms_exit);
3182
3183MODULE_LICENSE("GPL v2");
3184MODULE_DESCRIPTION("PMIC8921 bms driver");
3185MODULE_VERSION("1.0");
3186MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);