blob: 1778d628de353c975b41fd8455147b207b847810 [file] [log] [blame]
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#define pr_fmt(fmt) "%s: " fmt, __func__
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/platform_device.h>
18#include <linux/errno.h>
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -080019#include <linux/power_supply.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/mfd/pm8xxx/pm8921-bms.h>
21#include <linux/mfd/pm8xxx/core.h>
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -070022#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -080023#include <linux/mfd/pm8xxx/ccadc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024#include <linux/interrupt.h>
25#include <linux/bitops.h>
26#include <linux/debugfs.h>
27#include <linux/slab.h>
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070028#include <linux/delay.h>
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080029#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#define BMS_CONTROL 0x224
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -080032#define BMS_S1_DELAY 0x225
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033#define BMS_OUTPUT0 0x230
34#define BMS_OUTPUT1 0x231
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -080035#define BMS_TOLERANCES 0x232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036#define BMS_TEST1 0x237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070038#define ADC_ARB_SECP_CNTRL 0x190
39#define ADC_ARB_SECP_AMUX_CNTRL 0x191
40#define ADC_ARB_SECP_ANA_PARAM 0x192
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070041#define ADC_ARB_SECP_DIG_PARAM 0x193
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070042#define ADC_ARB_SECP_RSV 0x194
43#define ADC_ARB_SECP_DATA1 0x195
44#define ADC_ARB_SECP_DATA0 0x196
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070046#define ADC_ARB_BMS_CNTRL 0x18D
47
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048enum pmic_bms_interrupts {
49 PM8921_BMS_SBI_WRITE_OK,
50 PM8921_BMS_CC_THR,
51 PM8921_BMS_VSENSE_THR,
52 PM8921_BMS_VSENSE_FOR_R,
53 PM8921_BMS_OCV_FOR_R,
54 PM8921_BMS_GOOD_OCV,
55 PM8921_BMS_VSENSE_AVG,
56 PM_BMS_MAX_INTS,
57};
58
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080059struct pm8921_soc_params {
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -080060 uint16_t last_good_ocv_raw;
61 int cc;
62
63 int last_good_ocv_uv;
64};
65
66struct pm8921_rbatt_params {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080067 uint16_t ocv_for_rbatt_raw;
68 uint16_t vsense_for_rbatt_raw;
69 uint16_t vbatt_for_rbatt_raw;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080070
71 int ocv_for_rbatt_uv;
72 int vsense_for_rbatt_uv;
73 int vbatt_for_rbatt_uv;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -080074};
75
76/**
77 * struct pm8921_bms_chip -
78 * @bms_output_lock: lock to prevent concurrent bms reads
79 * @bms_100_lock: lock to prevent concurrent updates to values that force
80 * 100% charge
81 *
82 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083struct pm8921_bms_chip {
84 struct device *dev;
85 struct dentry *dent;
86 unsigned int r_sense;
87 unsigned int i_test;
88 unsigned int v_failure;
89 unsigned int fcc;
90 struct single_row_lut *fcc_temp_lut;
91 struct single_row_lut *fcc_sf_lut;
92 struct pc_temp_ocv_lut *pc_temp_ocv_lut;
93 struct pc_sf_lut *pc_sf_lut;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -070094 struct work_struct calib_hkadc_work;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -070095 struct delayed_work calib_ccadc_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096 unsigned int calib_delay_ms;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -070097 unsigned int revision;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -080098 unsigned int xoadc_v0625_usb_present;
99 unsigned int xoadc_v0625_usb_absent;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700100 unsigned int xoadc_v0625;
101 unsigned int xoadc_v125;
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -0700102 unsigned int batt_temp_channel;
103 unsigned int vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700104 unsigned int ref625mv_channel;
105 unsigned int ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -0700106 unsigned int batt_id_channel;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107 unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
108 DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800109 struct mutex bms_output_lock;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800110 spinlock_t bms_100_lock;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700111 struct single_row_lut *adjusted_fcc_temp_lut;
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -0700112 unsigned int charging_began;
113 unsigned int start_percent;
114 unsigned int end_percent;
David Keitel35e11872012-02-17 17:40:42 -0800115 enum battery_type batt_type;
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -0800116 uint16_t ocv_reading_at_100;
117 int cc_reading_at_100;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -0800118 int max_voltage_uv;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800119
120 int batt_temp_suspend;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121};
122
123static struct pm8921_bms_chip *the_chip;
124
125#define DEFAULT_RBATT_MOHMS 128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126#define DEFAULT_OCV_MICROVOLTS 3900000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127#define DEFAULT_CHARGE_CYCLES 0
128
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800129static int last_usb_cal_delta_uv = 1800;
130module_param(last_usb_cal_delta_uv, int, 0644);
131
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700132static int last_chargecycles = DEFAULT_CHARGE_CYCLES;
133static int last_charge_increase;
134module_param(last_chargecycles, int, 0644);
135module_param(last_charge_increase, int, 0644);
136
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137static int last_rbatt = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138static int last_ocv_uv = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139static int last_soc = -EINVAL;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800140static int last_real_fcc_mah = -EINVAL;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700141static int last_real_fcc_batt_temp = -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700143static int bms_ops_set(const char *val, const struct kernel_param *kp)
144{
145 if (*(int *)kp->arg == -EINVAL)
146 return param_set_int(val, kp);
147 else
148 return 0;
149}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700151static struct kernel_param_ops bms_param_ops = {
152 .set = bms_ops_set,
153 .get = param_get_int,
154};
155
156module_param_cb(last_rbatt, &bms_param_ops, &last_rbatt, 0644);
157module_param_cb(last_ocv_uv, &bms_param_ops, &last_ocv_uv, 0644);
158module_param_cb(last_soc, &bms_param_ops, &last_soc, 0644);
159
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800160/*
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800161 * bms_fake_battery is set in setups where a battery emulator is used instead
162 * of a real battery. This makes the bms driver report a different/fake value
163 * regardless of the calculated state of charge.
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800164 */
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -0800165static int bms_fake_battery = -EINVAL;
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -0800166module_param(bms_fake_battery, int, 0644);
167
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800168/* bms_start_XXX and bms_end_XXX are read only */
169static int bms_start_percent;
170static int bms_start_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800171static int bms_start_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800172static int bms_end_percent;
173static int bms_end_ocv_uv;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800174static int bms_end_cc_uah;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800175
176static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
177{
178 return -EINVAL;
179}
180
181static struct kernel_param_ops bms_ro_param_ops = {
182 .set = bms_ro_ops_set,
183 .get = param_get_int,
184};
185module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
186module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800187module_param_cb(bms_start_cc_uah, &bms_ro_param_ops, &bms_start_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800188
189module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
190module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800191module_param_cb(bms_end_cc_uah, &bms_ro_param_ops, &bms_end_cc_uah, 0644);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -0800192
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700193static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
194static void readjust_fcc_table(void)
195{
196 struct single_row_lut *temp, *old;
197 int i, fcc, ratio;
198
199 if (!the_chip->fcc_temp_lut) {
200 pr_err("The static fcc lut table is NULL\n");
201 return;
202 }
203
204 temp = kzalloc(sizeof(struct single_row_lut), GFP_KERNEL);
205 if (!temp) {
206 pr_err("Cannot allocate memory for adjusted fcc table\n");
207 return;
208 }
209
210 fcc = interpolate_fcc(the_chip, last_real_fcc_batt_temp);
211
212 temp->cols = the_chip->fcc_temp_lut->cols;
213 for (i = 0; i < the_chip->fcc_temp_lut->cols; i++) {
214 temp->x[i] = the_chip->fcc_temp_lut->x[i];
215 ratio = div_u64(the_chip->fcc_temp_lut->y[i] * 1000, fcc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800216 temp->y[i] = (ratio * last_real_fcc_mah);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700217 temp->y[i] /= 1000;
218 pr_debug("temp=%d, staticfcc=%d, adjfcc=%d, ratio=%d\n",
219 temp->x[i], the_chip->fcc_temp_lut->y[i],
220 temp->y[i], ratio);
221 }
222
223 old = the_chip->adjusted_fcc_temp_lut;
224 the_chip->adjusted_fcc_temp_lut = temp;
225 kfree(old);
226}
227
228static int bms_last_real_fcc_set(const char *val,
229 const struct kernel_param *kp)
230{
231 int rc = 0;
232
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800233 if (last_real_fcc_mah == -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700234 rc = param_set_int(val, kp);
235 if (rc) {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800236 pr_err("Failed to set last_real_fcc_mah rc=%d\n", rc);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700237 return rc;
238 }
239 if (last_real_fcc_batt_temp != -EINVAL)
240 readjust_fcc_table();
241 return rc;
242}
243static struct kernel_param_ops bms_last_real_fcc_param_ops = {
244 .set = bms_last_real_fcc_set,
245 .get = param_get_int,
246};
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800247module_param_cb(last_real_fcc_mah, &bms_last_real_fcc_param_ops,
248 &last_real_fcc_mah, 0644);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700249
250static int bms_last_real_fcc_batt_temp_set(const char *val,
251 const struct kernel_param *kp)
252{
253 int rc = 0;
254
255 if (last_real_fcc_batt_temp == -EINVAL)
256 rc = param_set_int(val, kp);
257 if (rc) {
258 pr_err("Failed to set last_real_fcc_batt_temp rc=%d\n", rc);
259 return rc;
260 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800261 if (last_real_fcc_mah != -EINVAL)
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700262 readjust_fcc_table();
263 return rc;
264}
265
266static struct kernel_param_ops bms_last_real_fcc_batt_temp_param_ops = {
267 .set = bms_last_real_fcc_batt_temp_set,
268 .get = param_get_int,
269};
270module_param_cb(last_real_fcc_batt_temp, &bms_last_real_fcc_batt_temp_param_ops,
271 &last_real_fcc_batt_temp, 0644);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272
273static int pm_bms_get_rt_status(struct pm8921_bms_chip *chip, int irq_id)
274{
275 return pm8xxx_read_irq_stat(chip->dev->parent,
276 chip->pmic_bms_irq[irq_id]);
277}
278
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700279static void pm8921_bms_enable_irq(struct pm8921_bms_chip *chip, int interrupt)
280{
281 if (!__test_and_set_bit(interrupt, chip->enabled_irqs)) {
282 dev_dbg(chip->dev, "%s %d\n", __func__,
283 chip->pmic_bms_irq[interrupt]);
284 enable_irq(chip->pmic_bms_irq[interrupt]);
285 }
286}
287
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288static void pm8921_bms_disable_irq(struct pm8921_bms_chip *chip, int interrupt)
289{
290 if (__test_and_clear_bit(interrupt, chip->enabled_irqs)) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700291 pr_debug("%d\n", chip->pmic_bms_irq[interrupt]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292 disable_irq_nosync(chip->pmic_bms_irq[interrupt]);
293 }
294}
295
296static int pm_bms_masked_write(struct pm8921_bms_chip *chip, u16 addr,
297 u8 mask, u8 val)
298{
299 int rc;
300 u8 reg;
301
302 rc = pm8xxx_readb(chip->dev->parent, addr, &reg);
303 if (rc) {
304 pr_err("read failed addr = %03X, rc = %d\n", addr, rc);
305 return rc;
306 }
307 reg &= ~mask;
308 reg |= val & mask;
309 rc = pm8xxx_writeb(chip->dev->parent, addr, reg);
310 if (rc) {
311 pr_err("write failed addr = %03X, rc = %d\n", addr, rc);
312 return rc;
313 }
314 return 0;
315}
316
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800317static int usb_chg_plugged_in(void)
318{
319 union power_supply_propval ret = {0,};
320 static struct power_supply *psy;
321
322 if (psy == NULL) {
323 psy = power_supply_get_by_name("usb");
324 if (psy == NULL)
325 return 0;
326 }
327
328 if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
329 return 0;
330
331 return ret.intval;
332}
333
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700334#define HOLD_OREG_DATA BIT(1)
335static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
336{
337 int rc;
338
339 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
340 HOLD_OREG_DATA);
341 if (rc) {
342 pr_err("couldnt lock bms output rc = %d\n", rc);
343 return rc;
344 }
345 return 0;
346}
347
348static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
349{
350 int rc;
351
352 rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
353 if (rc) {
354 pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
355 return rc;
356 }
357 return 0;
358}
359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360#define SELECT_OUTPUT_DATA 0x1C
361#define SELECT_OUTPUT_TYPE_SHIFT 2
362#define OCV_FOR_RBATT 0x0
363#define VSENSE_FOR_RBATT 0x1
364#define VBATT_FOR_RBATT 0x2
365#define CC_MSB 0x3
366#define CC_LSB 0x4
367#define LAST_GOOD_OCV_VALUE 0x5
368#define VSENSE_AVG 0x6
369#define VBATT_AVG 0x7
370
371static int pm_bms_read_output_data(struct pm8921_bms_chip *chip, int type,
372 int16_t *result)
373{
374 int rc;
375 u8 reg;
376
377 if (!result) {
378 pr_err("result pointer null\n");
379 return -EINVAL;
380 }
381 *result = 0;
382 if (type < OCV_FOR_RBATT || type > VBATT_AVG) {
383 pr_err("invalid type %d asked to read\n", type);
384 return -EINVAL;
385 }
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -0700386
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
388 type << SELECT_OUTPUT_TYPE_SHIFT);
389 if (rc) {
390 pr_err("fail to select %d type in BMS_CONTROL rc = %d\n",
391 type, rc);
392 return rc;
393 }
394
395 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT0, &reg);
396 if (rc) {
397 pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
398 type, rc);
399 return rc;
400 }
401 *result = reg;
402 rc = pm8xxx_readb(chip->dev->parent, BMS_OUTPUT1, &reg);
403 if (rc) {
404 pr_err("fail to read BMS_OUTPUT1 for type %d rc = %d\n",
405 type, rc);
406 return rc;
407 }
408 *result |= reg << 8;
409 pr_debug("type %d result %x", type, *result);
410 return 0;
411}
412
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700413#define V_PER_BIT_MUL_FACTOR 97656
414#define V_PER_BIT_DIV_FACTOR 1000
415#define XOADC_INTRINSIC_OFFSET 0x6000
416static int xoadc_reading_to_microvolt(unsigned int a)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700417{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700418 if (a <= XOADC_INTRINSIC_OFFSET)
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700419 return 0;
420
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700421 return (a - XOADC_INTRINSIC_OFFSET)
422 * V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR;
Abhijeet Dharmapurikard76311f2011-07-19 13:35:01 -0700423}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700425#define XOADC_CALIB_UV 625000
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700426#define VBATT_MUL_FACTOR 3
427static int adjust_xo_vbatt_reading(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800428 int usb_chg, unsigned int uv)
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700429{
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800430 s64 numerator, denominator;
431 int local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700432
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700433 if (uv == 0)
434 return 0;
435
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800436 /* dont adjust if not calibrated */
437 if (chip->xoadc_v0625 == 0 || chip->xoadc_v125 == 0) {
438 pr_debug("No cal yet return %d\n", VBATT_MUL_FACTOR * uv);
439 return VBATT_MUL_FACTOR * uv;
440 }
441
442 if (usb_chg)
443 local_delta = last_usb_cal_delta_uv;
444 else
445 local_delta = 0;
446
447 pr_debug("using delta = %d\n", local_delta);
448 numerator = ((s64)uv - chip->xoadc_v0625 - local_delta)
449 * XOADC_CALIB_UV;
450 denominator = (s64)chip->xoadc_v125 - chip->xoadc_v0625 - local_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700451 if (denominator == 0)
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700452 return uv * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800453 return (XOADC_CALIB_UV + local_delta + div_s64(numerator, denominator))
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700454 * VBATT_MUL_FACTOR;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700455}
456
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700457#define CC_RESOLUTION_N_V1 1085069
458#define CC_RESOLUTION_D_V1 100000
459#define CC_RESOLUTION_N_V2 868056
460#define CC_RESOLUTION_D_V2 10000
461static s64 cc_to_microvolt_v1(s64 cc)
462{
463 return div_s64(cc * CC_RESOLUTION_N_V1, CC_RESOLUTION_D_V1);
464}
465
466static s64 cc_to_microvolt_v2(s64 cc)
467{
468 return div_s64(cc * CC_RESOLUTION_N_V2, CC_RESOLUTION_D_V2);
469}
470
471static s64 cc_to_microvolt(struct pm8921_bms_chip *chip, s64 cc)
472{
473 /*
474 * resolution (the value of a single bit) was changed after revision 2.0
475 * for more accurate readings
476 */
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -0700477 return (chip->revision < PM8XXX_REVISION_8921_2p0) ?
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700478 cc_to_microvolt_v1((s64)cc) :
479 cc_to_microvolt_v2((s64)cc);
480}
481
482#define CC_READING_TICKS 55
483#define SLEEP_CLK_HZ 32768
484#define SECONDS_PER_HOUR 3600
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800485/**
486 * ccmicrovolt_to_nvh -
487 * @cc_uv: coulumb counter converted to uV
488 *
489 * RETURNS: coulumb counter based charge in nVh
490 * (nano Volt Hour)
491 */
492static s64 ccmicrovolt_to_nvh(s64 cc_uv)
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700493{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800494 return div_s64(cc_uv * CC_READING_TICKS * 1000,
Abhijeet Dharmapurikareed550b2011-07-18 22:35:24 -0700495 SLEEP_CLK_HZ * SECONDS_PER_HOUR);
496}
497
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700498/* returns the signed value read from the hardware */
499static int read_cc(struct pm8921_bms_chip *chip, int *result)
500{
501 int rc;
502 uint16_t msw, lsw;
503
504 rc = pm_bms_read_output_data(chip, CC_LSB, &lsw);
505 if (rc) {
506 pr_err("fail to read CC_LSB rc = %d\n", rc);
507 return rc;
508 }
509 rc = pm_bms_read_output_data(chip, CC_MSB, &msw);
510 if (rc) {
511 pr_err("fail to read CC_MSB rc = %d\n", rc);
512 return rc;
513 }
514 *result = msw << 16 | lsw;
515 pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
516 return 0;
517}
518
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800519static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800520 int usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800521 uint16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522{
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -0700523 *result = xoadc_reading_to_microvolt(reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800524 pr_debug("raw = %04x vbatt = %u\n", reading, *result);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800525 *result = adjust_xo_vbatt_reading(chip, usb_chg, *result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800526 pr_debug("after adj vbatt = %u\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 return 0;
528}
529
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800530static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
531 int16_t reading, int *result)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532{
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800533 *result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800534 pr_debug("raw = %04x vsense = %d\n", reading, *result);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -0800535 *result = pm8xxx_cc_adjust_for_gain(*result);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800536 pr_debug("after adj vsense = %d\n", *result);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 return 0;
538}
539
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700540static int read_vsense_avg(struct pm8921_bms_chip *chip, int *result)
541{
542 int rc;
543 int16_t reading;
544
545 rc = pm_bms_read_output_data(chip, VSENSE_AVG, &reading);
546 if (rc) {
547 pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
548 return rc;
549 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800550
551 convert_vsense_to_uv(chip, reading, result);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -0700552 return 0;
553}
554
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
556{
557 if (y0 == y1 || x == x0)
558 return y0;
559 if (x1 == x0 || x == x1)
560 return y1;
561
562 return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
563}
564
565static int interpolate_single_lut(struct single_row_lut *lut, int x)
566{
567 int i, result;
568
569 if (x < lut->x[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700570 pr_debug("x %d less than known range return y = %d lut = %pS\n",
571 x, lut->y[0], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 return lut->y[0];
573 }
574 if (x > lut->x[lut->cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700575 pr_debug("x %d more than known range return y = %d lut = %pS\n",
576 x, lut->y[lut->cols - 1], lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577 return lut->y[lut->cols - 1];
578 }
579
580 for (i = 0; i < lut->cols; i++)
581 if (x <= lut->x[i])
582 break;
583 if (x == lut->x[i]) {
584 result = lut->y[i];
585 } else {
586 result = linear_interpolate(
587 lut->y[i - 1],
588 lut->x[i - 1],
589 lut->y[i],
590 lut->x[i],
591 x);
592 }
593 return result;
594}
595
596static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
597{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800598 /* batt_temp is in tenths of degC - convert it to degC for lookups */
599 batt_temp = batt_temp/10;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600 return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
601}
602
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700603static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
604{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800605 /* batt_temp is in tenths of degC - convert it to degC for lookups */
606 batt_temp = batt_temp/10;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700607 return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
608}
609
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700610static int interpolate_scalingfactor_fcc(struct pm8921_bms_chip *chip,
611 int cycles)
612{
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700613 /*
614 * sf table could be null when no battery aging data is available, in
615 * that case return 100%
616 */
617 if (chip->fcc_sf_lut)
618 return interpolate_single_lut(chip->fcc_sf_lut, cycles);
619 else
620 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621}
622
623static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
624 int cycles, int pc)
625{
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700626 int i, scalefactorrow1, scalefactorrow2, scalefactor;
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700627 int rows, cols;
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -0700628 int row1 = 0;
629 int row2 = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630
Abhijeet Dharmapurikar205bbc02011-11-04 14:47:36 -0700631 /*
632 * sf table could be null when no battery aging data is available, in
633 * that case return 100%
634 */
635 if (!chip->pc_sf_lut)
636 return 100;
637
638 rows = chip->pc_sf_lut->rows;
639 cols = chip->pc_sf_lut->cols;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640 if (pc > chip->pc_sf_lut->percent[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700641 pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642 row1 = 0;
643 row2 = 0;
644 }
645 if (pc < chip->pc_sf_lut->percent[rows - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700646 pr_debug("pc %d less than known pc ranges for sf", pc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647 row1 = rows - 1;
648 row2 = rows - 1;
649 }
650 for (i = 0; i < rows; i++) {
651 if (pc == chip->pc_sf_lut->percent[i]) {
652 row1 = i;
653 row2 = i;
654 break;
655 }
656 if (pc > chip->pc_sf_lut->percent[i]) {
657 row1 = i - 1;
658 row2 = i;
659 break;
660 }
661 }
662
663 if (cycles < chip->pc_sf_lut->cycles[0])
664 cycles = chip->pc_sf_lut->cycles[0];
665 if (cycles > chip->pc_sf_lut->cycles[cols - 1])
666 cycles = chip->pc_sf_lut->cycles[cols - 1];
667
668 for (i = 0; i < cols; i++)
669 if (cycles <= chip->pc_sf_lut->cycles[i])
670 break;
671 if (cycles == chip->pc_sf_lut->cycles[i]) {
672 scalefactor = linear_interpolate(
673 chip->pc_sf_lut->sf[row1][i],
674 chip->pc_sf_lut->percent[row1],
675 chip->pc_sf_lut->sf[row2][i],
676 chip->pc_sf_lut->percent[row2],
677 pc);
678 return scalefactor;
679 }
680
681 scalefactorrow1 = linear_interpolate(
682 chip->pc_sf_lut->sf[row1][i - 1],
683 chip->pc_sf_lut->cycles[i - 1],
684 chip->pc_sf_lut->sf[row1][i],
685 chip->pc_sf_lut->cycles[i],
686 cycles);
687
688 scalefactorrow2 = linear_interpolate(
689 chip->pc_sf_lut->sf[row2][i - 1],
690 chip->pc_sf_lut->cycles[i - 1],
691 chip->pc_sf_lut->sf[row2][i],
692 chip->pc_sf_lut->cycles[i],
693 cycles);
694
695 scalefactor = linear_interpolate(
696 scalefactorrow1,
697 chip->pc_sf_lut->percent[row1],
698 scalefactorrow2,
699 chip->pc_sf_lut->percent[row2],
700 pc);
701
702 return scalefactor;
703}
704
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700705static int is_between(int left, int right, int value)
706{
707 if (left >= right && left >= value && value >= right)
708 return 1;
709 if (left <= right && left <= value && value <= right)
710 return 1;
711
712 return 0;
713}
714
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715static int interpolate_pc(struct pm8921_bms_chip *chip,
716 int batt_temp, int ocv)
717{
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700718 int i, j, pcj, pcj_minus_one, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719 int rows = chip->pc_temp_ocv_lut->rows;
720 int cols = chip->pc_temp_ocv_lut->cols;
721
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800722 /* batt_temp is in tenths of degC - convert it to degC for lookups */
723 batt_temp = batt_temp/10;
724
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725 if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700726 pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727 batt_temp = chip->pc_temp_ocv_lut->temp[0];
728 }
729 if (batt_temp > chip->pc_temp_ocv_lut->temp[cols - 1]) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700730 pr_debug("batt_temp %d > known temp range for pc\n", batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731 batt_temp = chip->pc_temp_ocv_lut->temp[cols - 1];
732 }
733
734 for (j = 0; j < cols; j++)
735 if (batt_temp <= chip->pc_temp_ocv_lut->temp[j])
736 break;
737 if (batt_temp == chip->pc_temp_ocv_lut->temp[j]) {
738 /* found an exact match for temp in the table */
739 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
740 return chip->pc_temp_ocv_lut->percent[0];
741 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j])
742 return chip->pc_temp_ocv_lut->percent[rows - 1];
743 for (i = 0; i < rows; i++) {
744 if (ocv >= chip->pc_temp_ocv_lut->ocv[i][j]) {
745 if (ocv == chip->pc_temp_ocv_lut->ocv[i][j])
746 return
747 chip->pc_temp_ocv_lut->percent[i];
748 pc = linear_interpolate(
749 chip->pc_temp_ocv_lut->percent[i],
750 chip->pc_temp_ocv_lut->ocv[i][j],
751 chip->pc_temp_ocv_lut->percent[i - 1],
752 chip->pc_temp_ocv_lut->ocv[i - 1][j],
753 ocv);
754 return pc;
755 }
756 }
757 }
758
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700759 /*
760 * batt_temp is within temperature for
761 * column j-1 and j
762 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 if (ocv >= chip->pc_temp_ocv_lut->ocv[0][j])
764 return chip->pc_temp_ocv_lut->percent[0];
765 if (ocv <= chip->pc_temp_ocv_lut->ocv[rows - 1][j - 1])
766 return chip->pc_temp_ocv_lut->percent[rows - 1];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700767
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700768 pcj_minus_one = 0;
769 pcj = 0;
770 for (i = 0; i < rows-1; i++) {
771 if (pcj == 0
772 && is_between(chip->pc_temp_ocv_lut->ocv[i][j],
773 chip->pc_temp_ocv_lut->ocv[i+1][j], ocv)) {
774 pcj = linear_interpolate(
775 chip->pc_temp_ocv_lut->percent[i],
776 chip->pc_temp_ocv_lut->ocv[i][j],
777 chip->pc_temp_ocv_lut->percent[i + 1],
778 chip->pc_temp_ocv_lut->ocv[i+1][j],
779 ocv);
780 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700781
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700782 if (pcj_minus_one == 0
783 && is_between(chip->pc_temp_ocv_lut->ocv[i][j-1],
784 chip->pc_temp_ocv_lut->ocv[i+1][j-1], ocv)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700786 pcj_minus_one = linear_interpolate(
787 chip->pc_temp_ocv_lut->percent[i],
788 chip->pc_temp_ocv_lut->ocv[i][j-1],
789 chip->pc_temp_ocv_lut->percent[i + 1],
790 chip->pc_temp_ocv_lut->ocv[i+1][j-1],
791 ocv);
792 }
793
794 if (pcj && pcj_minus_one) {
795 pc = linear_interpolate(
796 pcj_minus_one,
797 chip->pc_temp_ocv_lut->temp[j-1],
798 pcj,
799 chip->pc_temp_ocv_lut->temp[j],
800 batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801 return pc;
802 }
803 }
804
Abhijeet Dharmapurikar78d812f2011-07-21 19:57:21 -0700805 if (pcj)
806 return pcj;
807
808 if (pcj_minus_one)
809 return pcj_minus_one;
810
811 pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%",
812 ocv, batt_temp);
813 return 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700814}
815
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800816#define BMS_MODE_BIT BIT(6)
817#define EN_VBAT_BIT BIT(5)
818#define OVERRIDE_MODE_DELAY_MS 20
819int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
820 int *vbat_uv)
821{
822 int16_t vsense_raw;
823 int16_t vbat_raw;
824 int vsense_uv;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800825 int usb_chg;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800826
827 if (the_chip == NULL) {
828 pr_err("Called to early\n");
829 return -EINVAL;
830 }
831
832 mutex_lock(&the_chip->bms_output_lock);
833
834 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x00);
835 pm_bms_masked_write(the_chip, BMS_CONTROL,
836 BMS_MODE_BIT | EN_VBAT_BIT, BMS_MODE_BIT | EN_VBAT_BIT);
837
838 msleep(OVERRIDE_MODE_DELAY_MS);
839
840 pm_bms_lock_output_data(the_chip);
841 pm_bms_read_output_data(the_chip, VSENSE_AVG, &vsense_raw);
842 pm_bms_read_output_data(the_chip, VBATT_AVG, &vbat_raw);
843 pm_bms_unlock_output_data(the_chip);
844 pm_bms_masked_write(the_chip, BMS_CONTROL,
845 BMS_MODE_BIT | EN_VBAT_BIT, 0);
846
847 pm8xxx_writeb(the_chip->dev->parent, BMS_S1_DELAY, 0x0B);
848
849 mutex_unlock(&the_chip->bms_output_lock);
850
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800851 usb_chg = usb_chg_plugged_in();
852
853 convert_vbatt_raw_to_uv(the_chip, usb_chg, vbat_raw, vbat_uv);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800854 convert_vsense_to_uv(the_chip, vsense_raw, &vsense_uv);
855 *ibat_ua = vsense_uv * 1000 / (int)the_chip->r_sense;
856
857 pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x"
858 " ibat_ua = %d vbat_uv = %d\n",
859 (uint16_t)vsense_raw, (uint16_t)vbat_raw,
860 *ibat_ua, *vbat_uv);
861 return 0;
862}
863EXPORT_SYMBOL(pm8921_bms_get_simultaneous_battery_voltage_and_current);
864
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800865static int read_rbatt_params_raw(struct pm8921_bms_chip *chip,
866 struct pm8921_rbatt_params *raw)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867{
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800868 int usb_chg;
869
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800870 mutex_lock(&chip->bms_output_lock);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800871 pm_bms_lock_output_data(chip);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700872
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800873 pm_bms_read_output_data(chip,
874 OCV_FOR_RBATT, &raw->ocv_for_rbatt_raw);
875 pm_bms_read_output_data(chip,
876 VBATT_FOR_RBATT, &raw->vbatt_for_rbatt_raw);
877 pm_bms_read_output_data(chip,
878 VSENSE_FOR_RBATT, &raw->vsense_for_rbatt_raw);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -0700879
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800880 pm_bms_unlock_output_data(chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -0800881 mutex_unlock(&chip->bms_output_lock);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800882
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800883 usb_chg = usb_chg_plugged_in();
884 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800885 raw->vbatt_for_rbatt_raw, &raw->vbatt_for_rbatt_uv);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800886 convert_vbatt_raw_to_uv(chip, usb_chg,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800887 raw->ocv_for_rbatt_raw, &raw->ocv_for_rbatt_uv);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800888 convert_vsense_to_uv(chip, raw->vsense_for_rbatt_raw,
889 &raw->vsense_for_rbatt_uv);
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -0800890
891 pr_debug("vbatt_for_rbatt_raw = 0x%x, vbatt_for_rbatt= %duV\n",
892 raw->vbatt_for_rbatt_raw, raw->vbatt_for_rbatt_uv);
893 pr_debug("ocv_for_rbatt_raw = 0x%x, ocv_for_rbatt= %duV\n",
894 raw->ocv_for_rbatt_raw, raw->ocv_for_rbatt_uv);
895 pr_debug("vsense_for_rbatt_raw = 0x%x, vsense_for_rbatt= %duV\n",
896 raw->vsense_for_rbatt_raw, raw->vsense_for_rbatt_uv);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800897 return 0;
898}
899
900static int read_soc_params_raw(struct pm8921_bms_chip *chip,
901 struct pm8921_soc_params *raw)
902{
903 int usb_chg;
904
905 mutex_lock(&chip->bms_output_lock);
906 pm_bms_lock_output_data(chip);
907
908 pm_bms_read_output_data(chip,
909 LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
910 read_cc(chip, &raw->cc);
911
912 pm_bms_unlock_output_data(chip);
913 mutex_unlock(&chip->bms_output_lock);
914
915 usb_chg = usb_chg_plugged_in();
916 convert_vbatt_raw_to_uv(chip, usb_chg,
917 raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
918
919 if (raw->last_good_ocv_uv)
920 last_ocv_uv = raw->last_good_ocv_uv;
921
922 pr_debug("0p625 = %duV\n", chip->xoadc_v0625);
923 pr_debug("1p25 = %duV\n", chip->xoadc_v125);
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -0800924 pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n",
925 raw->last_good_ocv_raw, raw->last_good_ocv_uv);
926 pr_debug("cc_raw= 0x%x\n", raw->cc);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800927 return 0;
928}
929
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -0800930static int calculate_rbatt_resume(struct pm8921_bms_chip *chip,
931 struct pm8921_rbatt_params *raw)
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800932{
933 unsigned int r_batt;
934
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -0800935 if (raw->ocv_for_rbatt_uv <= 0
936 || raw->ocv_for_rbatt_uv <= raw->vbatt_for_rbatt_uv
937 || raw->vsense_for_rbatt_raw <= 0) {
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -0700938 pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
939 "vsen = %d\n",
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800940 raw->ocv_for_rbatt_uv,
941 raw->vbatt_for_rbatt_uv,
942 raw->vsense_for_rbatt_raw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943 return -EINVAL;
944 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -0800945 r_batt = ((raw->ocv_for_rbatt_uv - raw->vbatt_for_rbatt_uv)
946 * chip->r_sense) / raw->vsense_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700947 pr_debug("r_batt = %umilliOhms", r_batt);
948 return r_batt;
949}
950
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800951static int calculate_fcc_uah(struct pm8921_bms_chip *chip, int batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952 int chargecycles)
953{
954 int initfcc, result, scalefactor = 0;
955
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700956 if (chip->adjusted_fcc_temp_lut == NULL) {
957 initfcc = interpolate_fcc(chip, batt_temp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700958
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700959 scalefactor = interpolate_scalingfactor_fcc(chip, chargecycles);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700960
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700961 /* Multiply the initial FCC value by the scale factor. */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800962 result = (initfcc * scalefactor * 1000) / 100;
963 pr_debug("fcc = %d uAh\n", result);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700964 return result;
965 } else {
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -0800966 return 1000 * interpolate_fcc_adjusted(chip, batt_temp);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -0700967 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700968}
969
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700970static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
971{
972 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700973 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700974
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -0700975 rc = pm8xxx_adc_read(chip->vbat_channel, &result);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700976 if (rc) {
977 pr_err("error reading adc channel = %d, rc = %d\n",
978 chip->vbat_channel, rc);
979 return rc;
980 }
981 pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
982 result.measurement);
983 *uvolts = (int)result.physical;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700984 return 0;
985}
986
987static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
988{
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800989 int vbatt, rbatt, ibatt_ua, rc;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700990
991 rc = get_battery_uvolts(chip, &vbatt);
992 if (rc) {
993 pr_err("failed to read vbatt from adc rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700994 return rc;
995 }
996
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -0800997 rc = pm8921_bms_get_battery_current(&ibatt_ua);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -0700998 if (rc) {
999 pr_err("failed to read batt current rc = %d\n", rc);
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001000 return rc;
1001 }
1002
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001003 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001004 *ocv = vbatt + (ibatt_ua * rbatt)/1000;
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001005 return 0;
1006}
1007
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001008static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
1009 int chargecycles)
1010{
1011 int pc, scalefactor;
1012
1013 pc = interpolate_pc(chip, batt_temp, ocv_uv / 1000);
1014 pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
1015 pc, ocv_uv, batt_temp);
1016
1017 scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
1018 pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
1019
1020 /* Multiply the initial FCC value by the scale factor. */
1021 pc = (pc * scalefactor) / 100;
1022 return pc;
1023}
1024
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001025/**
1026 * calculate_cc_uah -
1027 * @chip: the bms chip pointer
1028 * @cc: the cc reading from bms h/w
1029 * @val: return value
1030 * @coulumb_counter: adjusted coulumb counter for 100%
1031 *
1032 * RETURNS: in val pointer coulumb counter based charger in uAh
1033 * (micro Amp hour)
1034 */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001035static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001037 int64_t cc_voltage_uv, cc_nvh, cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001038
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001039 cc_voltage_uv = cc;
1040 cc_voltage_uv -= chip->cc_reading_at_100;
1041 pr_debug("cc = %d. after subtracting %d cc = %lld\n",
1042 cc, chip->cc_reading_at_100,
1043 cc_voltage_uv);
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07001044 cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001045 cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001046 pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001047 cc_nvh = ccmicrovolt_to_nvh(cc_voltage_uv);
1048 pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh);
1049 cc_uah = div_s64(cc_nvh, chip->r_sense);
1050 *val = cc_uah;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001051}
1052
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001053static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001054 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001055 int fcc_uah, int batt_temp, int chargecycles)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001056{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001057 int rbatt, voltage_unusable_uv, pc_unusable;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001058
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001059 rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001060
1061 /* calculate unusable charge */
1062 voltage_unusable_uv = (rbatt * chip->i_test)
1063 + (chip->v_failure * 1000);
1064 pc_unusable = calculate_pc(chip, voltage_unusable_uv,
1065 batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001066 pr_debug("rbatt = %umilliOhms unusable_v =%d unusable_pc = %d\n",
1067 rbatt, voltage_unusable_uv, pc_unusable);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001068 return (fcc_uah * pc_unusable) / 100;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001069}
Abhijeet Dharmapurikar52aa9682011-07-20 14:05:47 -07001070
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001071/* calculate remainging charge at the time of ocv */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001072static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001073 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001074 int fcc_uah, int batt_temp,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001075 int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001076{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001077 int ocv, pc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001078
1079 /* calculate remainging charge */
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001080 ocv = 0;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001081 if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
1082 chip->ocv_reading_at_100 = 0;
1083 chip->cc_reading_at_100 = 0;
1084 ocv = raw->last_good_ocv_uv;
1085 } else {
1086 /*
1087 * force 100% ocv by selecting the highest voltage the
1088 * battery could every reach
1089 */
1090 ocv = chip->max_voltage_uv;
1091 }
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001092
1093 if (ocv == 0) {
1094 ocv = last_ocv_uv;
1095 pr_debug("ocv not available using last_ocv_uv=%d\n", ocv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001096 }
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001097
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098 pc = calculate_pc(chip, ocv, batt_temp, chargecycles);
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001099 pr_debug("ocv = %d pc = %d\n", ocv, pc);
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001100 return (fcc_uah * pc) / 100;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001101}
1102
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001103static void calculate_soc_params(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001104 struct pm8921_soc_params *raw,
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001105 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001106 int *fcc_uah,
1107 int *unusable_charge_uah,
1108 int *remaining_charge_uah,
1109 int *cc_uah)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001110{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001111 unsigned long flags;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001112
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001113 *fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
1114 pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
1115 *fcc_uah, batt_temp, chargecycles);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001116
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001117 *unusable_charge_uah = calculate_unusable_charge_uah(chip, raw,
1118 *fcc_uah, batt_temp, chargecycles);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001119
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001120 pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001121
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001122 spin_lock_irqsave(&chip->bms_100_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001123 /* calculate remainging charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001124 *remaining_charge_uah = calculate_remaining_charge_uah(chip, raw,
1125 *fcc_uah, batt_temp, chargecycles);
1126 pr_debug("RC = %uuAh\n", *remaining_charge_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001127
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001128 /* calculate cc micro_volt_hour */
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001129 calculate_cc_uah(chip, raw->cc, cc_uah);
1130 pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %d\n",
1131 *cc_uah, raw->cc,
1132 (int64_t)raw->cc - chip->cc_reading_at_100,
1133 chip->cc_reading_at_100);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001134 spin_unlock_irqrestore(&chip->bms_100_lock, flags);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001135}
1136
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001137static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001138 struct pm8921_soc_params *raw,
1139 int batt_temp, int chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001140 int *ret_fcc_uah)
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001141{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001142 int fcc_uah, unusable_charge_uah;
1143 int remaining_charge_uah;
1144 int cc_uah;
1145 int real_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001146
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001147 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1148 &fcc_uah,
1149 &unusable_charge_uah,
1150 &remaining_charge_uah,
1151 &cc_uah);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001152
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001153 real_fcc_uah = remaining_charge_uah - cc_uah;
1154 *ret_fcc_uah = fcc_uah;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001155 pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001156 real_fcc_uah, remaining_charge_uah, cc_uah, fcc_uah);
1157 return real_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001158}
1159/*
1160 * Remaining Usable Charge = remaining_charge (charge at ocv instance)
1161 * - coloumb counter charge
1162 * - unusable charge (due to battery resistance)
1163 * SOC% = (remaining usable charge/ fcc - usable_charge);
1164 */
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001165static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001166 struct pm8921_soc_params *raw,
1167 int batt_temp, int chargecycles)
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001168{
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001169 int remaining_usable_charge_uah, fcc_uah, unusable_charge_uah;
1170 int remaining_charge_uah, soc;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001171 int update_userspace = 1;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001172 int cc_uah;
Abhijeet Dharmapurikar020b37c2011-07-20 14:14:51 -07001173
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001174 calculate_soc_params(chip, raw, batt_temp, chargecycles,
1175 &fcc_uah,
1176 &unusable_charge_uah,
1177 &remaining_charge_uah,
1178 &cc_uah);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001179
1180 /* calculate remaining usable charge */
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001181 remaining_usable_charge_uah = remaining_charge_uah
1182 - cc_uah
1183 - unusable_charge_uah;
1184
1185 pr_debug("RUC = %duAh\n", remaining_usable_charge_uah);
Abhijeet Dharmapurikarbbae88312012-02-14 18:56:42 -08001186 if (fcc_uah - unusable_charge_uah <= 0) {
1187 pr_warn("FCC = %duAh, UUC = %duAh forcing soc = 0\n",
1188 fcc_uah, unusable_charge_uah);
1189 soc = 0;
1190 } else {
1191 soc = (remaining_usable_charge_uah * 100)
1192 / (fcc_uah - unusable_charge_uah);
1193 }
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001194
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001195 if (soc > 100)
1196 soc = 100;
1197 pr_debug("SOC = %u%%\n", soc);
1198
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001199 if (bms_fake_battery != -EINVAL) {
1200 pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery);
1201 return bms_fake_battery;
1202 }
1203
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001204 if (soc < 0) {
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001205 pr_err("bad rem_usb_chg = %d rem_chg %d,"
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001206 "cc_uah %d, unusb_chg %d\n",
1207 remaining_usable_charge_uah,
1208 remaining_charge_uah,
1209 cc_uah, unusable_charge_uah);
1210
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07001211 pr_err("for bad rem_usb_chg last_ocv_uv = %d"
Abhijeet Dharmapurikaraf6f9e22011-10-03 19:19:02 -07001212 "chargecycles = %d, batt_temp = %d"
1213 "fcc = %d soc =%d\n",
1214 last_ocv_uv, chargecycles, batt_temp,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001215 fcc_uah, soc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001216 update_userspace = 0;
Abhijeet Dharmapurikar95971a82011-12-06 18:23:57 -08001217 soc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001218 }
1219
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001220 if (last_soc == -EINVAL || soc <= last_soc) {
1221 last_soc = update_userspace ? soc : last_soc;
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001222 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001223 }
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001224
1225 /*
1226 * soc > last_soc
1227 * the device must be charging for reporting a higher soc, if not ignore
1228 * this soc and continue reporting the last_soc
1229 */
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08001230 if (the_chip->start_percent != -EINVAL) {
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001231 last_soc = soc;
1232 } else {
1233 pr_debug("soc = %d reporting last_soc = %d\n", soc, last_soc);
1234 soc = last_soc;
1235 }
1236
Abhijeet Dharmapurikar986bd8a2012-01-05 15:15:15 -08001237 return soc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001238}
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001239#define MIN_DELTA_625_UV 1000
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001240static void calib_hkadc(struct pm8921_bms_chip *chip)
1241{
1242 int voltage, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001243 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001244 int usb_chg;
1245 int this_delta;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001246
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001247 rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001248 if (rc) {
1249 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1250 return;
1251 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08001252 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001253
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07001254 pr_debug("result 1.25v = 0x%x, voltage = %duV adc_meas = %lld\n",
1255 result.adc_code, voltage, result.measurement);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001256
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001257 chip->xoadc_v125 = voltage;
1258
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001259 rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001260 if (rc) {
1261 pr_err("ADC failed for 1.25volts rc = %d\n", rc);
1262 return;
1263 }
Abhijeet Dharmapurikar619cb0c2011-12-15 15:28:14 -08001264 voltage = xoadc_reading_to_microvolt(result.adc_code);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001265
1266 usb_chg = usb_chg_plugged_in();
1267 pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld "
1268 "usb_chg = %d\n",
1269 result.adc_code, voltage, result.measurement,
1270 usb_chg);
1271
1272 if (usb_chg)
1273 chip->xoadc_v0625_usb_present = voltage;
1274 else
1275 chip->xoadc_v0625_usb_absent = voltage;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001276
1277 chip->xoadc_v0625 = voltage;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001278 if (chip->xoadc_v0625_usb_present && chip->xoadc_v0625_usb_absent) {
1279 this_delta = chip->xoadc_v0625_usb_present
1280 - chip->xoadc_v0625_usb_absent;
1281 pr_debug("this_delta= %duV\n", this_delta);
1282 if (this_delta > MIN_DELTA_625_UV)
1283 last_usb_cal_delta_uv = this_delta;
1284 pr_debug("625V_present= %d, 625V_absent= %d, delta = %duV\n",
1285 chip->xoadc_v0625_usb_present,
1286 chip->xoadc_v0625_usb_absent,
1287 last_usb_cal_delta_uv);
1288 }
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001289}
1290
1291static void calibrate_hkadc_work(struct work_struct *work)
1292{
1293 struct pm8921_bms_chip *chip = container_of(work,
1294 struct pm8921_bms_chip, calib_hkadc_work);
1295
1296 calib_hkadc(chip);
1297}
1298
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001299void pm8921_bms_calibrate_hkadc(void)
1300{
1301 schedule_work(&the_chip->calib_hkadc_work);
1302}
1303
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001304static void calibrate_ccadc_work(struct work_struct *work)
1305{
1306 struct pm8921_bms_chip *chip = container_of(work,
1307 struct pm8921_bms_chip, calib_ccadc_work.work);
1308
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001309 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1b8e8292012-01-17 11:01:44 -08001310 calib_hkadc(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001311 schedule_delayed_work(&chip->calib_ccadc_work,
1312 round_jiffies_relative(msecs_to_jiffies
1313 (chip->calib_delay_ms)));
1314}
1315
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001316int pm8921_bms_get_vsense_avg(int *result)
1317{
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001318 int rc = -EINVAL;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001319
1320 if (the_chip) {
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001321 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001322 pm_bms_lock_output_data(the_chip);
1323 rc = read_vsense_avg(the_chip, result);
1324 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001325 mutex_unlock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001326 }
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001327
1328 pr_err("called before initialization\n");
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001329 return rc;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001330}
1331EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
1332
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001333int pm8921_bms_get_battery_current(int *result_ua)
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001334{
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001335 int vsense;
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001336
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001337 if (!the_chip) {
1338 pr_err("called before initialization\n");
1339 return -EINVAL;
1340 }
1341 if (the_chip->r_sense == 0) {
1342 pr_err("r_sense is zero\n");
1343 return -EINVAL;
1344 }
1345
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001346 mutex_lock(&the_chip->bms_output_lock);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001347 pm_bms_lock_output_data(the_chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001348 read_vsense_avg(the_chip, &vsense);
Abhijeet Dharmapurikar3f95fa82011-10-05 19:14:32 -07001349 pm_bms_unlock_output_data(the_chip);
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001350 mutex_unlock(&the_chip->bms_output_lock);
1351 pr_debug("vsense=%duV\n", vsense);
Abhijeet Dharmapurikara7a1c6b2011-08-17 10:04:58 -07001352 /* cast for signed division */
Siddartha Mohanadoss37e6fc02011-11-16 16:57:03 -08001353 *result_ua = vsense * 1000 / (int)the_chip->r_sense;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001354 pr_debug("ibat=%duA\n", *result_ua);
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001355 return 0;
1356}
1357EXPORT_SYMBOL(pm8921_bms_get_battery_current);
1358
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001359int pm8921_bms_get_percent_charge(void)
1360{
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001361 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001362 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001363 struct pm8921_soc_params raw;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001364
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001365 if (!the_chip) {
1366 pr_err("called before initialization\n");
1367 return -EINVAL;
1368 }
1369
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001370 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07001371 if (rc) {
1372 pr_err("error reading adc channel = %d, rc = %d\n",
1373 the_chip->batt_temp_channel, rc);
1374 return rc;
1375 }
1376 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1377 result.measurement);
1378 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001379
1380 read_soc_params_raw(the_chip, &raw);
1381
1382 return calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001383 batt_temp, last_chargecycles);
1384}
1385EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
1386
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001387int pm8921_bms_get_fcc(void)
1388{
1389 int batt_temp, rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001390 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001391
1392 if (!the_chip) {
1393 pr_err("called before initialization\n");
1394 return -EINVAL;
1395 }
1396
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001397 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001398 if (rc) {
1399 pr_err("error reading adc channel = %d, rc = %d\n",
1400 the_chip->batt_temp_channel, rc);
1401 return rc;
1402 }
1403 pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
1404 result.measurement);
1405 batt_temp = (int)result.physical;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001406 return calculate_fcc_uah(the_chip, batt_temp, last_chargecycles);
Abhijeet Dharmapurikar325bdc92011-09-07 20:45:49 -07001407}
1408EXPORT_SYMBOL_GPL(pm8921_bms_get_fcc);
1409
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001410#define IBAT_TOL_MASK 0x0F
1411#define OCV_TOL_MASK 0xF0
1412#define IBAT_TOL_DEFAULT 0x03
1413#define IBAT_TOL_NOCHG 0x0F
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001414void pm8921_bms_charging_began(void)
1415{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001416 int batt_temp, rc;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001417 struct pm8xxx_adc_chan_result result;
1418 struct pm8921_soc_params raw;
1419
1420 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1421 if (rc) {
1422 pr_err("error reading adc channel = %d, rc = %d\n",
1423 the_chip->batt_temp_channel, rc);
1424 return;
1425 }
1426 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1427 result.measurement);
1428 batt_temp = (int)result.physical;
1429
1430 read_soc_params_raw(the_chip, &raw);
1431
1432 the_chip->start_percent = calculate_state_of_charge(the_chip, &raw,
1433 batt_temp, last_chargecycles);
1434 bms_start_percent = the_chip->start_percent;
1435 bms_start_ocv_uv = raw.last_good_ocv_uv;
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001436 calculate_cc_uah(the_chip, raw.cc, &bms_start_cc_uah);
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001437 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
1438 IBAT_TOL_MASK, IBAT_TOL_DEFAULT);
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001439 pr_debug("start_percent = %u%%\n", the_chip->start_percent);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001440}
1441EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
1442
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001443#define DELTA_FCC_PERCENT 3
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001444void pm8921_bms_charging_end(int is_battery_full)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001445{
Abhijeet Dharmapurikarda8c3d52012-01-06 14:23:13 -08001446 int batt_temp, rc;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001447 struct pm8xxx_adc_chan_result result;
1448 struct pm8921_soc_params raw;
1449
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001450 if (the_chip == NULL)
1451 return;
1452
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001453 rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
1454 if (rc) {
1455 pr_err("error reading adc channel = %d, rc = %d\n",
1456 the_chip->batt_temp_channel, rc);
1457 return;
1458 }
1459 pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
1460 result.measurement);
1461 batt_temp = (int)result.physical;
1462
1463 read_soc_params_raw(the_chip, &raw);
1464
Abhijeet Dharmapurikar57aeb922012-01-25 13:22:26 -08001465 calculate_cc_uah(the_chip, raw.cc, &bms_end_cc_uah);
1466
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001467 if (is_battery_full) {
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001468 unsigned long flags;
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001469 int fcc_uah, new_fcc_uah, delta_fcc_uah;
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001470
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001471 new_fcc_uah = calculate_real_fcc_uah(the_chip, &raw,
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001472 batt_temp, last_chargecycles,
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001473 &fcc_uah);
1474 delta_fcc_uah = new_fcc_uah - fcc_uah;
1475 if (delta_fcc_uah < 0)
1476 delta_fcc_uah = -delta_fcc_uah;
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001477
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001478 if (delta_fcc_uah * 100 <= (DELTA_FCC_PERCENT * fcc_uah)) {
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001479 pr_debug("delta_fcc=%d < %d percent of fcc=%d\n",
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001480 delta_fcc_uah, DELTA_FCC_PERCENT, fcc_uah);
1481 last_real_fcc_mah = new_fcc_uah/1000;
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001482 last_real_fcc_batt_temp = batt_temp;
1483 readjust_fcc_table();
1484 } else {
1485 pr_debug("delta_fcc=%d > %d percent of fcc=%d"
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001486 "will not update real fcc\n",
1487 delta_fcc_uah, DELTA_FCC_PERCENT, fcc_uah);
Abhijeet Dharmapurikar8309df42011-11-16 21:04:09 -08001488 }
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001489
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001490 spin_lock_irqsave(&the_chip->bms_100_lock, flags);
1491 the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
1492 the_chip->cc_reading_at_100 = raw.cc;
1493 spin_unlock_irqrestore(&the_chip->bms_100_lock, flags);
1494 pr_debug("EOC ocv_reading = 0x%x cc = %d\n",
Abhijeet Dharmapurikar57b06b52011-11-07 19:14:08 -08001495 the_chip->ocv_reading_at_100,
1496 the_chip->cc_reading_at_100);
Abhijeet Dharmapurikarfad72352011-09-25 23:06:05 -07001497 }
1498
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001499 the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
1500 batt_temp, last_chargecycles);
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001501
1502 bms_end_percent = the_chip->end_percent;
1503 bms_end_ocv_uv = raw.last_good_ocv_uv;
Abhijeet Dharmapurikarab833312011-11-22 18:26:09 -08001504
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001505 if (the_chip->end_percent > the_chip->start_percent) {
Abhijeet Dharmapurikar8a113f82012-01-19 10:58:45 -08001506 last_charge_increase +=
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001507 the_chip->end_percent - the_chip->start_percent;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001508 if (last_charge_increase > 100) {
1509 last_chargecycles++;
1510 last_charge_increase = last_charge_increase % 100;
1511 }
1512 }
1513 pr_debug("end_percent = %u%% last_charge_increase = %d"
1514 "last_chargecycles = %d\n",
Abhijeet Dharmapurikar2ce2cc12011-11-02 15:40:29 -07001515 the_chip->end_percent,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001516 last_charge_increase,
1517 last_chargecycles);
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08001518 the_chip->start_percent = -EINVAL;
1519 the_chip->end_percent = -EINVAL;
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001520 pm_bms_masked_write(the_chip, BMS_TOLERANCES,
1521 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001522}
1523EXPORT_SYMBOL_GPL(pm8921_bms_charging_end);
1524
1525static irqreturn_t pm8921_bms_sbi_write_ok_handler(int irq, void *data)
1526{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001527 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001528 return IRQ_HANDLED;
1529}
1530
1531static irqreturn_t pm8921_bms_cc_thr_handler(int irq, void *data)
1532{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001533 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001534 return IRQ_HANDLED;
1535}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001536
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001537static irqreturn_t pm8921_bms_vsense_thr_handler(int irq, void *data)
1538{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001539 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001540 return IRQ_HANDLED;
1541}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001542
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001543static irqreturn_t pm8921_bms_vsense_for_r_handler(int irq, void *data)
1544{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001545 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001546 return IRQ_HANDLED;
1547}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001548
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001549static irqreturn_t pm8921_bms_ocv_for_r_handler(int irq, void *data)
1550{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001551 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001552
1553 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001554 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001555 return IRQ_HANDLED;
1556}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001557
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001558static irqreturn_t pm8921_bms_good_ocv_handler(int irq, void *data)
1559{
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001560 struct pm8921_bms_chip *chip = data;
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001561
1562 pr_debug("irq = %d triggered", irq);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001563 schedule_work(&chip->calib_hkadc_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001564 return IRQ_HANDLED;
1565}
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001566
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001567static irqreturn_t pm8921_bms_vsense_avg_handler(int irq, void *data)
1568{
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07001569 pr_debug("irq = %d triggered", irq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001570 return IRQ_HANDLED;
1571}
1572
1573struct pm_bms_irq_init_data {
1574 unsigned int irq_id;
1575 char *name;
1576 unsigned long flags;
1577 irqreturn_t (*handler)(int, void *);
1578};
1579
1580#define BMS_IRQ(_id, _flags, _handler) \
1581{ \
1582 .irq_id = _id, \
1583 .name = #_id, \
1584 .flags = _flags, \
1585 .handler = _handler, \
1586}
1587
1588struct pm_bms_irq_init_data bms_irq_data[] = {
1589 BMS_IRQ(PM8921_BMS_SBI_WRITE_OK, IRQF_TRIGGER_RISING,
1590 pm8921_bms_sbi_write_ok_handler),
1591 BMS_IRQ(PM8921_BMS_CC_THR, IRQF_TRIGGER_RISING,
1592 pm8921_bms_cc_thr_handler),
1593 BMS_IRQ(PM8921_BMS_VSENSE_THR, IRQF_TRIGGER_RISING,
1594 pm8921_bms_vsense_thr_handler),
1595 BMS_IRQ(PM8921_BMS_VSENSE_FOR_R, IRQF_TRIGGER_RISING,
1596 pm8921_bms_vsense_for_r_handler),
1597 BMS_IRQ(PM8921_BMS_OCV_FOR_R, IRQF_TRIGGER_RISING,
1598 pm8921_bms_ocv_for_r_handler),
1599 BMS_IRQ(PM8921_BMS_GOOD_OCV, IRQF_TRIGGER_RISING,
1600 pm8921_bms_good_ocv_handler),
1601 BMS_IRQ(PM8921_BMS_VSENSE_AVG, IRQF_TRIGGER_RISING,
1602 pm8921_bms_vsense_avg_handler),
1603};
1604
1605static void free_irqs(struct pm8921_bms_chip *chip)
1606{
1607 int i;
1608
1609 for (i = 0; i < PM_BMS_MAX_INTS; i++)
1610 if (chip->pmic_bms_irq[i]) {
1611 free_irq(chip->pmic_bms_irq[i], NULL);
1612 chip->pmic_bms_irq[i] = 0;
1613 }
1614}
1615
1616static int __devinit request_irqs(struct pm8921_bms_chip *chip,
1617 struct platform_device *pdev)
1618{
1619 struct resource *res;
1620 int ret, i;
1621
1622 ret = 0;
1623 bitmap_fill(chip->enabled_irqs, PM_BMS_MAX_INTS);
1624
1625 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
1626 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1627 bms_irq_data[i].name);
1628 if (res == NULL) {
1629 pr_err("couldn't find %s\n", bms_irq_data[i].name);
1630 goto err_out;
1631 }
1632 ret = request_irq(res->start, bms_irq_data[i].handler,
1633 bms_irq_data[i].flags,
1634 bms_irq_data[i].name, chip);
1635 if (ret < 0) {
1636 pr_err("couldn't request %d (%s) %d\n", res->start,
1637 bms_irq_data[i].name, ret);
1638 goto err_out;
1639 }
1640 chip->pmic_bms_irq[bms_irq_data[i].irq_id] = res->start;
1641 pm8921_bms_disable_irq(chip, bms_irq_data[i].irq_id);
1642 }
1643 return 0;
1644
1645err_out:
1646 free_irqs(chip);
1647 return -EINVAL;
1648}
1649
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001650static int pm8921_bms_suspend(struct device *dev)
1651{
1652 int rc;
1653 struct pm8xxx_adc_chan_result result;
1654 struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
1655
1656 chip->batt_temp_suspend = 0;
1657 rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
1658 if (rc) {
1659 pr_err("error reading adc channel = %d, rc = %d\n",
1660 chip->batt_temp_channel, rc);
1661 }
1662 chip->batt_temp_suspend = (int)result.physical;
1663 return 0;
1664}
1665
1666static int pm8921_bms_resume(struct device *dev)
1667{
1668 struct pm8921_rbatt_params raw;
1669 struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
1670 int rbatt;
1671
1672 read_rbatt_params_raw(chip, &raw);
1673 rbatt = calculate_rbatt_resume(chip, &raw);
1674 if (rbatt > 0) /* TODO Check for rbatt values bound based on cycles */
1675 last_rbatt = rbatt;
1676 chip->batt_temp_suspend = -EINVAL;
1677 return 0;
1678}
1679
1680static const struct dev_pm_ops pm8921_pm_ops = {
1681 .suspend = pm8921_bms_suspend,
1682 .resume = pm8921_bms_resume,
1683};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001684#define EN_BMS_BIT BIT(7)
1685#define EN_PON_HS_BIT BIT(0)
1686static int __devinit pm8921_bms_hw_init(struct pm8921_bms_chip *chip)
1687{
1688 int rc;
1689
1690 rc = pm_bms_masked_write(chip, BMS_CONTROL,
1691 EN_BMS_BIT | EN_PON_HS_BIT, EN_BMS_BIT | EN_PON_HS_BIT);
1692 if (rc) {
1693 pr_err("failed to enable pon and bms addr = %d %d",
1694 BMS_CONTROL, rc);
1695 }
1696
Abhijeet Dharmapurikarcad865a2012-02-09 17:14:02 -08001697 /* The charger will call start charge later if usb is present */
1698 pm_bms_masked_write(chip, BMS_TOLERANCES,
1699 IBAT_TOL_MASK, IBAT_TOL_NOCHG);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001700 return 0;
1701}
1702
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001703static void check_initial_ocv(struct pm8921_bms_chip *chip)
1704{
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001705 int ocv_uv, rc;
1706 int16_t ocv_raw;
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001707 int usb_chg;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001708
1709 /*
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001710 * Check if a ocv is available in bms hw,
1711 * if not compute it here at boot time and save it
1712 * in the last_ocv_uv.
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001713 */
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001714 ocv_uv = 0;
1715 pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
Abhijeet Dharmapurikaracca4182012-02-07 12:52:19 -08001716 usb_chg = usb_chg_plugged_in();
1717 rc = convert_vbatt_raw_to_uv(chip, usb_chg, ocv_raw, &ocv_uv);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001718 if (rc || ocv_uv == 0) {
1719 rc = adc_based_ocv(chip, &ocv_uv);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001720 if (rc) {
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001721 pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
1722 ocv_uv = DEFAULT_OCV_MICROVOLTS;
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07001723 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001724 last_ocv_uv = ocv_uv;
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001725 }
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001726 pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, last_ocv_uv);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07001727}
1728
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001729static int64_t read_battery_id(struct pm8921_bms_chip *chip)
1730{
1731 int rc;
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001732 struct pm8xxx_adc_chan_result result;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001733
Siddartha Mohanadossaf91d902011-10-20 10:23:34 -07001734 rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001735 if (rc) {
1736 pr_err("error reading batt id channel = %d, rc = %d\n",
1737 chip->vbat_channel, rc);
1738 return rc;
1739 }
1740 pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical,
1741 result.measurement);
David Keitel8f2601b2012-02-14 22:31:07 -08001742 return result.adc_code;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001743}
1744
David Keitel8f2601b2012-02-14 22:31:07 -08001745#define PALLADIUM_ID_MIN 0x7F40
1746#define PALLADIUM_ID_MAX 0x7F5A
1747#define DESAY_5200_ID_MIN 0x7F7F
1748#define DESAY_5200_ID_MAX 0x802F
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001749static int set_battery_data(struct pm8921_bms_chip *chip)
1750{
1751 int64_t battery_id;
1752
David Keitel35e11872012-02-17 17:40:42 -08001753 if (chip->batt_type == BATT_DESAY)
1754 goto desay;
1755 else if (chip->batt_type == BATT_PALLADIUM)
1756 goto palladium;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001757
David Keitel35e11872012-02-17 17:40:42 -08001758 battery_id = read_battery_id(chip);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001759 if (battery_id < 0) {
1760 pr_err("cannot read battery id err = %lld\n", battery_id);
1761 return battery_id;
1762 }
1763
1764 if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) {
David Keitel35e11872012-02-17 17:40:42 -08001765 goto palladium;
1766 } else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX,
1767 battery_id)) {
1768 goto desay;
1769 } else {
1770 goto unknown;
1771 }
1772
1773palladium:
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001774 chip->fcc = palladium_1500_data.fcc;
1775 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1776 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1777 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1778 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1779 return 0;
David Keitel35e11872012-02-17 17:40:42 -08001780desay:
David Keitel8f2601b2012-02-14 22:31:07 -08001781 chip->fcc = desay_5200_data.fcc;
1782 chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
1783 chip->fcc_sf_lut = desay_5200_data.fcc_sf_lut;
1784 chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
1785 chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
1786 return 0;
David Keitel35e11872012-02-17 17:40:42 -08001787unknown:
David Keitel8f2601b2012-02-14 22:31:07 -08001788 pr_warn("invalid battery id, palladium 1500 assumed batt_id %llx\n",
1789 battery_id);
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001790 chip->fcc = palladium_1500_data.fcc;
1791 chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
1792 chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
1793 chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
1794 chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
1795 return 0;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07001796}
1797
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001798enum bms_request_operation {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001799 CALC_RBATT,
1800 CALC_FCC,
1801 CALC_PC,
1802 CALC_SOC,
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001803 CALIB_HKADC,
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001804 CALIB_CCADC,
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001805 GET_VBAT_VSENSE_SIMULTANEOUS,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001806};
1807
1808static int test_batt_temp = 5;
1809static int test_chargecycle = 150;
1810static int test_ocv = 3900000;
1811enum {
1812 TEST_BATT_TEMP,
1813 TEST_CHARGE_CYCLE,
1814 TEST_OCV,
1815};
1816static int get_test_param(void *data, u64 * val)
1817{
1818 switch ((int)data) {
1819 case TEST_BATT_TEMP:
1820 *val = test_batt_temp;
1821 break;
1822 case TEST_CHARGE_CYCLE:
1823 *val = test_chargecycle;
1824 break;
1825 case TEST_OCV:
1826 *val = test_ocv;
1827 break;
1828 default:
1829 return -EINVAL;
1830 }
1831 return 0;
1832}
1833static int set_test_param(void *data, u64 val)
1834{
1835 switch ((int)data) {
1836 case TEST_BATT_TEMP:
1837 test_batt_temp = (int)val;
1838 break;
1839 case TEST_CHARGE_CYCLE:
1840 test_chargecycle = (int)val;
1841 break;
1842 case TEST_OCV:
1843 test_ocv = (int)val;
1844 break;
1845 default:
1846 return -EINVAL;
1847 }
1848 return 0;
1849}
1850DEFINE_SIMPLE_ATTRIBUTE(temp_fops, get_test_param, set_test_param, "%llu\n");
1851
1852static int get_calc(void *data, u64 * val)
1853{
1854 int param = (int)data;
1855 int ret = 0;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001856 int ibat_ua, vbat_uv;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001857 struct pm8921_soc_params raw;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001858 struct pm8921_rbatt_params rraw;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001859
1860 read_soc_params_raw(the_chip, &raw);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001861 read_rbatt_params_raw(the_chip, &rraw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001862
1863 *val = 0;
1864
1865 /* global irq number passed in via data */
1866 switch (param) {
1867 case CALC_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001868 *val = calculate_rbatt_resume(the_chip, &rraw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001869 break;
1870 case CALC_FCC:
Abhijeet Dharmapurikar9c1e3992011-12-18 15:03:20 -08001871 *val = calculate_fcc_uah(the_chip, test_batt_temp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001872 test_chargecycle);
1873 break;
1874 case CALC_PC:
1875 *val = calculate_pc(the_chip, test_ocv, test_batt_temp,
1876 test_chargecycle);
1877 break;
1878 case CALC_SOC:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001879 *val = calculate_state_of_charge(the_chip, &raw,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001880 test_batt_temp, test_chargecycle);
1881 break;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07001882 case CALIB_HKADC:
1883 /* reading this will trigger calibration */
1884 *val = 0;
1885 calib_hkadc(the_chip);
1886 break;
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001887 case CALIB_CCADC:
1888 /* reading this will trigger calibration */
1889 *val = 0;
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001890 pm8xxx_calib_ccadc();
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07001891 break;
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08001892 case GET_VBAT_VSENSE_SIMULTANEOUS:
1893 /* reading this will call simultaneous vbat and vsense */
1894 *val =
1895 pm8921_bms_get_simultaneous_battery_voltage_and_current(
1896 &ibat_ua,
1897 &vbat_uv);
1898 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001899 default:
1900 ret = -EINVAL;
1901 }
1902 return ret;
1903}
1904DEFINE_SIMPLE_ATTRIBUTE(calc_fops, get_calc, NULL, "%llu\n");
1905
1906static int get_reading(void *data, u64 * val)
1907{
1908 int param = (int)data;
1909 int ret = 0;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001910 struct pm8921_soc_params raw;
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001911 struct pm8921_rbatt_params rraw;
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001912
1913 read_soc_params_raw(the_chip, &raw);
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001914 read_rbatt_params_raw(the_chip, &rraw);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001915
1916 *val = 0;
1917
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001918 switch (param) {
1919 case CC_MSB:
1920 case CC_LSB:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001921 *val = raw.cc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001922 break;
1923 case LAST_GOOD_OCV_VALUE:
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08001924 *val = raw.last_good_ocv_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001925 break;
1926 case VBATT_FOR_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001927 *val = rraw.vbatt_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001928 break;
1929 case VSENSE_FOR_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001930 *val = rraw.vsense_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001931 break;
1932 case OCV_FOR_RBATT:
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08001933 *val = rraw.ocv_for_rbatt_uv;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001934 break;
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07001935 case VSENSE_AVG:
1936 read_vsense_avg(the_chip, (uint *)val);
1937 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001938 default:
1939 ret = -EINVAL;
1940 }
1941 return ret;
1942}
Abhijeet Dharmapurikare0c3e5f2011-07-11 21:17:23 -07001943DEFINE_SIMPLE_ATTRIBUTE(reading_fops, get_reading, NULL, "%lld\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001944
1945static int get_rt_status(void *data, u64 * val)
1946{
1947 int i = (int)data;
1948 int ret;
1949
1950 /* global irq number passed in via data */
1951 ret = pm_bms_get_rt_status(the_chip, i);
1952 *val = ret;
1953 return 0;
1954}
1955DEFINE_SIMPLE_ATTRIBUTE(rt_fops, get_rt_status, NULL, "%llu\n");
1956
1957static int get_reg(void *data, u64 * val)
1958{
1959 int addr = (int)data;
1960 int ret;
1961 u8 temp;
1962
1963 ret = pm8xxx_readb(the_chip->dev->parent, addr, &temp);
1964 if (ret) {
1965 pr_err("pm8xxx_readb to %x value = %d errored = %d\n",
1966 addr, temp, ret);
1967 return -EAGAIN;
1968 }
1969 *val = temp;
1970 return 0;
1971}
1972
1973static int set_reg(void *data, u64 val)
1974{
1975 int addr = (int)data;
1976 int ret;
1977 u8 temp;
1978
1979 temp = (u8) val;
1980 ret = pm8xxx_writeb(the_chip->dev->parent, addr, temp);
1981 if (ret) {
1982 pr_err("pm8xxx_writeb to %x value = %d errored = %d\n",
1983 addr, temp, ret);
1984 return -EAGAIN;
1985 }
1986 return 0;
1987}
1988DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_reg, set_reg, "0x%02llx\n");
1989
1990static void create_debugfs_entries(struct pm8921_bms_chip *chip)
1991{
1992 int i;
1993
Abhijeet Dharmapurikar82d93982011-11-09 15:52:25 -08001994 chip->dent = debugfs_create_dir("pm8921-bms", NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001995
1996 if (IS_ERR(chip->dent)) {
1997 pr_err("pmic bms couldnt create debugfs dir\n");
1998 return;
1999 }
2000
2001 debugfs_create_file("BMS_CONTROL", 0644, chip->dent,
2002 (void *)BMS_CONTROL, &reg_fops);
2003 debugfs_create_file("BMS_OUTPUT0", 0644, chip->dent,
2004 (void *)BMS_OUTPUT0, &reg_fops);
2005 debugfs_create_file("BMS_OUTPUT1", 0644, chip->dent,
2006 (void *)BMS_OUTPUT1, &reg_fops);
2007 debugfs_create_file("BMS_TEST1", 0644, chip->dent,
2008 (void *)BMS_TEST1, &reg_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002009
2010 debugfs_create_file("test_batt_temp", 0644, chip->dent,
2011 (void *)TEST_BATT_TEMP, &temp_fops);
2012 debugfs_create_file("test_chargecycle", 0644, chip->dent,
2013 (void *)TEST_CHARGE_CYCLE, &temp_fops);
2014 debugfs_create_file("test_ocv", 0644, chip->dent,
2015 (void *)TEST_OCV, &temp_fops);
2016
2017 debugfs_create_file("read_cc", 0644, chip->dent,
2018 (void *)CC_MSB, &reading_fops);
2019 debugfs_create_file("read_last_good_ocv", 0644, chip->dent,
2020 (void *)LAST_GOOD_OCV_VALUE, &reading_fops);
2021 debugfs_create_file("read_vbatt_for_rbatt", 0644, chip->dent,
2022 (void *)VBATT_FOR_RBATT, &reading_fops);
2023 debugfs_create_file("read_vsense_for_rbatt", 0644, chip->dent,
2024 (void *)VSENSE_FOR_RBATT, &reading_fops);
2025 debugfs_create_file("read_ocv_for_rbatt", 0644, chip->dent,
2026 (void *)OCV_FOR_RBATT, &reading_fops);
Abhijeet Dharmapurikar2fb6f082011-06-28 17:10:27 -07002027 debugfs_create_file("read_vsense_avg", 0644, chip->dent,
2028 (void *)VSENSE_AVG, &reading_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002029
2030 debugfs_create_file("show_rbatt", 0644, chip->dent,
2031 (void *)CALC_RBATT, &calc_fops);
2032 debugfs_create_file("show_fcc", 0644, chip->dent,
2033 (void *)CALC_FCC, &calc_fops);
2034 debugfs_create_file("show_pc", 0644, chip->dent,
2035 (void *)CALC_PC, &calc_fops);
2036 debugfs_create_file("show_soc", 0644, chip->dent,
2037 (void *)CALC_SOC, &calc_fops);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002038 debugfs_create_file("calib_hkadc", 0644, chip->dent,
2039 (void *)CALIB_HKADC, &calc_fops);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002040 debugfs_create_file("calib_ccadc", 0644, chip->dent,
2041 (void *)CALIB_CCADC, &calc_fops);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002042
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002043 debugfs_create_file("simultaneous", 0644, chip->dent,
2044 (void *)GET_VBAT_VSENSE_SIMULTANEOUS, &calc_fops);
2045
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002046 for (i = 0; i < ARRAY_SIZE(bms_irq_data); i++) {
2047 if (chip->pmic_bms_irq[bms_irq_data[i].irq_id])
2048 debugfs_create_file(bms_irq_data[i].name, 0444,
2049 chip->dent,
2050 (void *)bms_irq_data[i].irq_id,
2051 &rt_fops);
2052 }
2053}
2054
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002055static int __devinit pm8921_bms_probe(struct platform_device *pdev)
2056{
2057 int rc = 0;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002058 int vbatt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002059 struct pm8921_bms_chip *chip;
2060 const struct pm8921_bms_platform_data *pdata
2061 = pdev->dev.platform_data;
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002062
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002063 if (!pdata) {
2064 pr_err("missing platform data\n");
2065 return -EINVAL;
2066 }
2067
Abhijeet Dharmapurikar15e853d2011-06-27 20:55:47 -07002068 chip = kzalloc(sizeof(struct pm8921_bms_chip), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002069 if (!chip) {
2070 pr_err("Cannot allocate pm_bms_chip\n");
2071 return -ENOMEM;
2072 }
Abhijeet Dharmapurikarc9857ca2012-02-07 15:56:06 -08002073 mutex_init(&chip->bms_output_lock);
Abhijeet Dharmapurikar228acb92011-11-21 19:21:48 -08002074 spin_lock_init(&chip->bms_100_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002075 chip->dev = &pdev->dev;
2076 chip->r_sense = pdata->r_sense;
2077 chip->i_test = pdata->i_test;
2078 chip->v_failure = pdata->v_failure;
2079 chip->calib_delay_ms = pdata->calib_delay_ms;
Abhijeet Dharmapurikara93ede82011-11-17 12:20:03 -08002080 chip->max_voltage_uv = pdata->max_voltage_uv;
David Keitel35e11872012-02-17 17:40:42 -08002081 chip->batt_type = pdata->battery_type;
Abhijeet Dharmapurikardba91f42011-12-21 16:56:13 -08002082 chip->start_percent = -EINVAL;
2083 chip->end_percent = -EINVAL;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002084 rc = set_battery_data(chip);
2085 if (rc) {
2086 pr_err("%s bad battery data %d\n", __func__, rc);
Abhijeet Dharmapurikar4a4456e2011-09-27 15:10:38 -07002087 goto free_chip;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002088 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002089
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002090 chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
2091 chip->vbat_channel = pdata->bms_cdata.vbat_channel;
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002092 chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
2093 chip->ref1p25v_channel = pdata->bms_cdata.ref1p25v_channel;
Abhijeet Dharmapurikare922bbb2011-09-01 13:05:06 -07002094 chip->batt_id_channel = pdata->bms_cdata.batt_id_channel;
Abhijeet Dharmapurikarfa77f1a2011-06-27 21:11:51 -07002095 chip->revision = pm8xxx_get_revision(chip->dev->parent);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002096 INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
Abhijeet Dharmapurikarc448d982011-06-28 20:48:36 -07002097
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002098 rc = request_irqs(chip, pdev);
2099 if (rc) {
2100 pr_err("couldn't register interrupts rc = %d\n", rc);
2101 goto free_chip;
2102 }
2103
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002104 rc = pm8921_bms_hw_init(chip);
2105 if (rc) {
2106 pr_err("couldn't init hardware rc = %d\n", rc);
2107 goto free_irqs;
2108 }
2109
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002110 platform_set_drvdata(pdev, chip);
2111 the_chip = chip;
2112 create_debugfs_entries(chip);
2113
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002114 check_initial_ocv(chip);
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002115
2116 INIT_DELAYED_WORK(&chip->calib_ccadc_work, calibrate_ccadc_work);
2117 /* begin calibration only on chips > 2.0 */
2118 if (chip->revision >= PM8XXX_REVISION_8921_2p0)
Abhijeet Dharmapurikarccfc4f32012-01-16 17:35:18 -08002119 schedule_delayed_work(&chip->calib_ccadc_work, 0);
Abhijeet Dharmapurikarc3532222011-07-20 14:48:02 -07002120
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002121 /* initial hkadc calibration */
2122 schedule_work(&chip->calib_hkadc_work);
Abhijeet Dharmapurikar76d4db92011-07-19 11:54:52 -07002123 /* enable the vbatt reading interrupts for scheduling hkadc calib */
2124 pm8921_bms_enable_irq(chip, PM8921_BMS_GOOD_OCV);
2125 pm8921_bms_enable_irq(chip, PM8921_BMS_OCV_FOR_R);
Abhijeet Dharmapurikar21ebadd2011-09-28 21:14:16 -07002126
Abhijeet Dharmapurikarc23264c2011-10-28 19:34:56 -07002127 get_battery_uvolts(chip, &vbatt);
2128 pr_info("OK battery_capacity_at_boot=%d volt = %d ocv = %d\n",
2129 pm8921_bms_get_percent_charge(),
2130 vbatt, last_ocv_uv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002131 return 0;
2132
Abhijeet Dharmapurikar1834c6d2011-10-28 20:00:02 -07002133free_irqs:
2134 free_irqs(chip);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002135free_chip:
2136 kfree(chip);
2137 return rc;
2138}
2139
2140static int __devexit pm8921_bms_remove(struct platform_device *pdev)
2141{
2142 struct pm8921_bms_chip *chip = platform_get_drvdata(pdev);
2143
2144 free_irqs(chip);
Abhijeet Dharmapurikar05d42742011-10-18 19:26:03 -07002145 kfree(chip->adjusted_fcc_temp_lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002146 platform_set_drvdata(pdev, NULL);
2147 the_chip = NULL;
2148 kfree(chip);
2149 return 0;
2150}
2151
2152static struct platform_driver pm8921_bms_driver = {
2153 .probe = pm8921_bms_probe,
2154 .remove = __devexit_p(pm8921_bms_remove),
2155 .driver = {
2156 .name = PM8921_BMS_DEV_NAME,
2157 .owner = THIS_MODULE,
Abhijeet Dharmapurikar1a7561a2012-02-26 22:06:36 -08002158 .pm = &pm8921_pm_ops
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002159 },
2160};
2161
2162static int __init pm8921_bms_init(void)
2163{
2164 return platform_driver_register(&pm8921_bms_driver);
2165}
2166
2167static void __exit pm8921_bms_exit(void)
2168{
2169 platform_driver_unregister(&pm8921_bms_driver);
2170}
2171
2172late_initcall(pm8921_bms_init);
2173module_exit(pm8921_bms_exit);
2174
2175MODULE_LICENSE("GPL v2");
2176MODULE_DESCRIPTION("PMIC8921 bms driver");
2177MODULE_VERSION("1.0");
2178MODULE_ALIAS("platform:" PM8921_BMS_DEV_NAME);