| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2 |  * | 
 | 3 |  * This program is free software; you can redistribute it and/or modify | 
 | 4 |  * it under the terms of the GNU General Public License version 2 and | 
 | 5 |  * only version 2 as published by the Free Software Foundation. | 
 | 6 |  * | 
 | 7 |  * This program is distributed in the hope that it will be useful, | 
 | 8 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 9 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 10 |  * GNU General Public License for more details. | 
 | 11 |  */ | 
 | 12 |  | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 13 | #define pr_fmt(fmt)	"BMS: %s: " fmt, __func__ | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 14 |  | 
 | 15 | #include <linux/module.h> | 
 | 16 | #include <linux/types.h> | 
 | 17 | #include <linux/init.h> | 
 | 18 | #include <linux/slab.h> | 
 | 19 | #include <linux/err.h> | 
 | 20 | #include <linux/of.h> | 
 | 21 | #include <linux/of_device.h> | 
 | 22 | #include <linux/power_supply.h> | 
 | 23 | #include <linux/spmi.h> | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 24 | #include <linux/rtc.h> | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 25 | #include <linux/delay.h> | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 26 | #include <linux/qpnp/qpnp-adc.h> | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 27 | #include <linux/mfd/pm8xxx/batterydata-lib.h> | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 28 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 29 | /* BMS Register Offsets */ | 
 | 30 | #define BMS1_REVISION1			0x0 | 
 | 31 | #define BMS1_REVISION2			0x1 | 
 | 32 | #define BMS1_STATUS1			0x8 | 
 | 33 | #define BMS1_MODE_CTL			0X40 | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 34 | /* Coulomb counter clear registers */ | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 35 | #define BMS1_CC_DATA_CTL		0x42 | 
| Xiaozhe Shi | a045a56 | 2012-11-28 16:55:39 -0800 | [diff] [blame] | 36 | #define BMS1_CC_CLEAR_CTL		0x43 | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 37 | /* OCV limit registers */ | 
 | 38 | #define BMS1_OCV_USE_LOW_LIMIT_THR0	0x48 | 
 | 39 | #define BMS1_OCV_USE_LOW_LIMIT_THR1	0x49 | 
 | 40 | #define BMS1_OCV_USE_HIGH_LIMIT_THR0	0x4A | 
 | 41 | #define BMS1_OCV_USE_HIGH_LIMIT_THR1	0x4B | 
 | 42 | #define BMS1_OCV_USE_LIMIT_CTL		0x4C | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 43 | /* Delay control */ | 
 | 44 | #define BMS1_S1_DELAY_CTL		0x5A | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 45 | /* CC interrupt threshold */ | 
 | 46 | #define BMS1_CC_THR0			0x7A | 
 | 47 | #define BMS1_CC_THR1			0x7B | 
 | 48 | #define BMS1_CC_THR2			0x7C | 
 | 49 | #define BMS1_CC_THR3			0x7D | 
 | 50 | #define BMS1_CC_THR4			0x7E | 
 | 51 | /* OCV for r registers */ | 
 | 52 | #define BMS1_OCV_FOR_R_DATA0		0x80 | 
 | 53 | #define BMS1_OCV_FOR_R_DATA1		0x81 | 
 | 54 | #define BMS1_VSENSE_FOR_R_DATA0		0x82 | 
 | 55 | #define BMS1_VSENSE_FOR_R_DATA1		0x83 | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 56 | /* Coulomb counter data */ | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 57 | #define BMS1_CC_DATA0			0x8A | 
 | 58 | #define BMS1_CC_DATA1			0x8B | 
 | 59 | #define BMS1_CC_DATA2			0x8C | 
 | 60 | #define BMS1_CC_DATA3			0x8D | 
 | 61 | #define BMS1_CC_DATA4			0x8E | 
 | 62 | /* OCV for soc data */ | 
 | 63 | #define BMS1_OCV_FOR_SOC_DATA0		0x90 | 
 | 64 | #define BMS1_OCV_FOR_SOC_DATA1		0x91 | 
 | 65 | #define BMS1_VSENSE_PON_DATA0		0x94 | 
 | 66 | #define BMS1_VSENSE_PON_DATA1		0x95 | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 67 | #define BMS1_VSENSE_AVG_DATA0		0x98 | 
 | 68 | #define BMS1_VSENSE_AVG_DATA1		0x99 | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 69 | #define BMS1_VBAT_AVG_DATA0		0x9E | 
 | 70 | #define BMS1_VBAT_AVG_DATA1		0x9F | 
 | 71 | /* Extra bms registers */ | 
 | 72 | #define BMS1_BMS_DATA_REG_0		0xB0 | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 73 | #define IAVG_STORAGE_REG		0xB1 | 
 | 74 | #define SOC_STORAGE_REG			0xB2 | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 75 | #define BMS1_BMS_DATA_REG_3		0xB3 | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 76 | /* IADC Channel Select */ | 
 | 77 | #define IADC1_BMS_ADC_CH_SEL_CTL	0x48 | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 78 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 79 | /* Configuration for saving of shutdown soc/iavg */ | 
 | 80 | #define IGNORE_SOC_TEMP_DECIDEG		50 | 
 | 81 | #define IAVG_STEP_SIZE_MA		50 | 
 | 82 | #define IAVG_START			600 | 
 | 83 | #define SOC_ZERO			0xFF | 
 | 84 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 85 | #define IAVG_SAMPLES 16 | 
 | 86 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 87 | #define QPNP_BMS_DEV_NAME "qcom,qpnp-bms" | 
 | 88 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 89 | struct soc_params { | 
 | 90 | 	int		fcc_uah; | 
 | 91 | 	int		cc_uah; | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 92 | 	int		rbatt_mohm; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 93 | 	int		iavg_ua; | 
 | 94 | 	int		uuc_uah; | 
 | 95 | 	int		ocv_charge_uah; | 
 | 96 | }; | 
 | 97 |  | 
 | 98 | struct raw_soc_params { | 
 | 99 | 	uint16_t	last_good_ocv_raw; | 
 | 100 | 	int64_t		cc; | 
 | 101 | 	int		last_good_ocv_uv; | 
 | 102 | }; | 
 | 103 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 104 | struct qpnp_bms_chip { | 
 | 105 | 	struct device			*dev; | 
 | 106 | 	struct power_supply		bms_psy; | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 107 | 	struct power_supply		*batt_psy; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 108 | 	struct spmi_device		*spmi; | 
 | 109 | 	u16				base; | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 110 | 	u16				iadc_base; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 111 |  | 
 | 112 | 	u8				revision1; | 
 | 113 | 	u8				revision2; | 
 | 114 | 	int				charger_status; | 
 | 115 | 	bool				online; | 
 | 116 | 	/* platform data */ | 
 | 117 | 	unsigned int			r_sense_mohm; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 118 | 	unsigned int			v_cutoff_uv; | 
 | 119 | 	unsigned int			max_voltage_uv; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 120 | 	unsigned int			r_conn_mohm; | 
 | 121 | 	int				shutdown_soc_valid_limit; | 
 | 122 | 	int				adjust_soc_low_threshold; | 
 | 123 | 	int				adjust_soc_high_threshold; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 124 | 	int				chg_term_ua; | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 125 | 	enum battery_type		batt_type; | 
 | 126 | 	unsigned int			fcc; | 
 | 127 | 	struct single_row_lut		*fcc_temp_lut; | 
 | 128 | 	struct single_row_lut		*fcc_sf_lut; | 
 | 129 | 	struct pc_temp_ocv_lut		*pc_temp_ocv_lut; | 
 | 130 | 	struct sf_lut			*pc_sf_lut; | 
 | 131 | 	struct sf_lut			*rbatt_sf_lut; | 
 | 132 | 	int				default_rbatt_mohm; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 133 |  | 
 | 134 | 	struct delayed_work		calculate_soc_delayed_work; | 
 | 135 |  | 
 | 136 | 	struct mutex			bms_output_lock; | 
 | 137 | 	struct mutex			last_ocv_uv_mutex; | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 138 | 	struct mutex			soc_invalidation_mutex; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 139 |  | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 140 | 	bool				use_external_rsense; | 
 | 141 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 142 | 	unsigned int			start_percent; | 
 | 143 | 	unsigned int			end_percent; | 
 | 144 | 	bool				ignore_shutdown_soc; | 
 | 145 | 	int				shutdown_soc_invalid; | 
 | 146 | 	int				shutdown_soc; | 
 | 147 | 	int				shutdown_iavg_ma; | 
 | 148 |  | 
 | 149 | 	int				low_soc_calc_threshold; | 
 | 150 | 	int				low_soc_calculate_soc_ms; | 
 | 151 | 	int				calculate_soc_ms; | 
 | 152 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 153 | 	uint16_t			ocv_reading_at_100; | 
 | 154 | 	int64_t				cc_reading_at_100; | 
 | 155 | 	uint16_t			prev_last_good_ocv_raw; | 
 | 156 | 	int				last_ocv_uv; | 
 | 157 | 	int				last_cc_uah; | 
 | 158 | 	unsigned long			tm_sec; | 
 | 159 | 	bool				first_time_calc_soc; | 
 | 160 | 	bool				first_time_calc_uuc; | 
 | 161 | 	int				pon_ocv_uv; | 
 | 162 |  | 
 | 163 | 	int				iavg_samples_ma[IAVG_SAMPLES]; | 
 | 164 | 	int				iavg_index; | 
 | 165 | 	int				iavg_num_samples; | 
 | 166 | 	struct timespec			t_soc_queried; | 
 | 167 | 	int				last_soc; | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 168 | 	int				last_soc_est; | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 169 |  | 
 | 170 | 	int				charge_time_us; | 
 | 171 | 	int				catch_up_time_us; | 
 | 172 | 	struct single_row_lut		*adjusted_fcc_temp_lut; | 
 | 173 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 174 | 	unsigned int			vadc_v0625; | 
 | 175 | 	unsigned int			vadc_v1250; | 
 | 176 |  | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 177 | 	int				ibat_max_ua; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 178 | 	int				prev_iavg_ua; | 
 | 179 | 	int				prev_uuc_iavg_ma; | 
 | 180 | 	int				prev_pc_unusable; | 
 | 181 | 	int				ibat_at_cv_ua; | 
 | 182 | 	int				soc_at_cv; | 
 | 183 | 	int				prev_chg_soc; | 
 | 184 | 	int				calculated_soc; | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 185 | 	int				prev_voltage_based_soc; | 
 | 186 | 	bool				use_voltage_soc; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 187 | }; | 
 | 188 |  | 
 | 189 | static struct of_device_id qpnp_bms_match_table[] = { | 
 | 190 | 	{ .compatible = QPNP_BMS_DEV_NAME }, | 
 | 191 | 	{} | 
 | 192 | }; | 
 | 193 |  | 
 | 194 | static char *qpnp_bms_supplicants[] = { | 
 | 195 | 	"battery" | 
 | 196 | }; | 
 | 197 |  | 
 | 198 | static enum power_supply_property msm_bms_power_props[] = { | 
 | 199 | 	POWER_SUPPLY_PROP_STATUS, | 
 | 200 | 	POWER_SUPPLY_PROP_ONLINE, | 
 | 201 | 	POWER_SUPPLY_PROP_CAPACITY, | 
 | 202 | 	POWER_SUPPLY_PROP_CURRENT_NOW, | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 203 | 	POWER_SUPPLY_PROP_CURRENT_MAX, | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 204 | 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | 
 | 205 | }; | 
 | 206 |  | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 207 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 208 | static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val, | 
 | 209 | 			u16 base, int count) | 
 | 210 | { | 
 | 211 | 	int rc; | 
 | 212 | 	struct spmi_device *spmi = chip->spmi; | 
 | 213 |  | 
 | 214 | 	rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 215 | 	if (rc) { | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 216 | 		pr_err("SPMI read failed rc=%d\n", rc); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 217 | 		return rc; | 
 | 218 | 	} | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 219 | 	return 0; | 
 | 220 | } | 
 | 221 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 222 | static int qpnp_write_wrapper(struct qpnp_bms_chip *chip, u8 *val, | 
 | 223 | 			u16 base, int count) | 
 | 224 | { | 
 | 225 | 	int rc; | 
 | 226 | 	struct spmi_device *spmi = chip->spmi; | 
 | 227 |  | 
 | 228 | 	rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, base, val, count); | 
 | 229 | 	if (rc) { | 
 | 230 | 		pr_err("SPMI write failed rc=%d\n", rc); | 
 | 231 | 		return rc; | 
 | 232 | 	} | 
 | 233 | 	return 0; | 
 | 234 | } | 
 | 235 |  | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 236 | static int qpnp_masked_write_base(struct qpnp_bms_chip *chip, u16 addr, | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 237 | 							u8 mask, u8 val) | 
 | 238 | { | 
 | 239 | 	int rc; | 
 | 240 | 	u8 reg; | 
 | 241 |  | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 242 | 	rc = qpnp_read_wrapper(chip, ®, addr, 1); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 243 | 	if (rc) { | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 244 | 		pr_err("read failed addr = %03X, rc = %d\n", addr, rc); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 245 | 		return rc; | 
 | 246 | 	} | 
 | 247 | 	reg &= ~mask; | 
 | 248 | 	reg |= val & mask; | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 249 | 	rc = qpnp_write_wrapper(chip, ®, addr, 1); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 250 | 	if (rc) { | 
 | 251 | 		pr_err("write failed addr = %03X, val = %02x, mask = %02x, reg = %02x, rc = %d\n", | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 252 | 					addr, val, mask, reg, rc); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 253 | 		return rc; | 
 | 254 | 	} | 
 | 255 | 	return 0; | 
 | 256 | } | 
 | 257 |  | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 258 | static int qpnp_masked_write_iadc(struct qpnp_bms_chip *chip, u16 addr, | 
 | 259 | 							u8 mask, u8 val) | 
 | 260 | { | 
 | 261 | 	return qpnp_masked_write_base(chip, chip->iadc_base + addr, mask, val); | 
 | 262 | } | 
 | 263 |  | 
 | 264 | static int qpnp_masked_write(struct qpnp_bms_chip *chip, u16 addr, | 
 | 265 | 							u8 mask, u8 val) | 
 | 266 | { | 
 | 267 | 	return qpnp_masked_write_base(chip, chip->base + addr, mask, val); | 
 | 268 | } | 
 | 269 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 270 | #define HOLD_OREG_DATA		BIT(0) | 
 | 271 | static int lock_output_data(struct qpnp_bms_chip *chip) | 
 | 272 | { | 
 | 273 | 	int rc; | 
 | 274 |  | 
 | 275 | 	rc = qpnp_masked_write(chip, BMS1_CC_DATA_CTL, | 
 | 276 | 				HOLD_OREG_DATA, HOLD_OREG_DATA); | 
 | 277 | 	if (rc) { | 
 | 278 | 		pr_err("couldnt lock bms output rc = %d\n", rc); | 
 | 279 | 		return rc; | 
 | 280 | 	} | 
 | 281 | 	return 0; | 
 | 282 | } | 
 | 283 |  | 
 | 284 | static int unlock_output_data(struct qpnp_bms_chip *chip) | 
 | 285 | { | 
 | 286 | 	int rc; | 
 | 287 |  | 
 | 288 | 	rc = qpnp_masked_write(chip, BMS1_CC_DATA_CTL, HOLD_OREG_DATA, 0); | 
 | 289 | 	if (rc) { | 
 | 290 | 		pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc); | 
 | 291 | 		return rc; | 
 | 292 | 	} | 
 | 293 | 	return 0; | 
 | 294 | } | 
 | 295 |  | 
 | 296 | #define V_PER_BIT_MUL_FACTOR	97656 | 
 | 297 | #define V_PER_BIT_DIV_FACTOR	1000 | 
 | 298 | #define VADC_INTRINSIC_OFFSET	0x6000 | 
 | 299 |  | 
 | 300 | static int vadc_reading_to_uv(unsigned int reading) | 
 | 301 | { | 
 | 302 | 	if (reading <= VADC_INTRINSIC_OFFSET) | 
 | 303 | 		return 0; | 
 | 304 |  | 
 | 305 | 	return (reading - VADC_INTRINSIC_OFFSET) | 
 | 306 | 			* V_PER_BIT_MUL_FACTOR / V_PER_BIT_DIV_FACTOR; | 
 | 307 | } | 
 | 308 |  | 
 | 309 | #define VADC_CALIB_UV		625000 | 
 | 310 | #define VBATT_MUL_FACTOR	3 | 
 | 311 |  | 
 | 312 | static int adjust_vbatt_reading(struct qpnp_bms_chip *chip, | 
 | 313 | 						unsigned int reading_uv) | 
 | 314 | { | 
 | 315 | 	s64 numerator, denominator; | 
 | 316 |  | 
 | 317 | 	if (reading_uv == 0) | 
 | 318 | 		return 0; | 
 | 319 |  | 
 | 320 | 	/* don't adjust if not calibrated */ | 
 | 321 | 	if (chip->vadc_v0625 == 0 || chip->vadc_v1250 == 0) { | 
 | 322 | 		pr_debug("No cal yet return %d\n", | 
 | 323 | 				VBATT_MUL_FACTOR * reading_uv); | 
 | 324 | 		return VBATT_MUL_FACTOR * reading_uv; | 
 | 325 | 	} | 
 | 326 |  | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 327 | 	numerator = ((s64)reading_uv - chip->vadc_v0625) * VADC_CALIB_UV; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 328 | 	denominator =  (s64)chip->vadc_v1250 - chip->vadc_v0625; | 
 | 329 | 	if (denominator == 0) | 
 | 330 | 		return reading_uv * VBATT_MUL_FACTOR; | 
 | 331 | 	return (VADC_CALIB_UV + div_s64(numerator, denominator)) | 
 | 332 | 						* VBATT_MUL_FACTOR; | 
 | 333 | } | 
 | 334 |  | 
 | 335 | static inline int convert_vbatt_raw_to_uv(struct qpnp_bms_chip *chip, | 
 | 336 | 					uint16_t reading) | 
 | 337 | { | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 338 | 	int uv; | 
 | 339 |  | 
 | 340 | 	uv = vadc_reading_to_uv(reading); | 
 | 341 | 	pr_debug("%u raw converted into %d uv\n", reading, uv); | 
 | 342 | 	uv = adjust_vbatt_reading(chip, uv); | 
 | 343 | 	pr_debug("adjusted into %d uv\n", uv); | 
 | 344 | 	return uv; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 345 | } | 
 | 346 |  | 
 | 347 | #define CC_READING_RESOLUTION_N	542535 | 
 | 348 | #define CC_READING_RESOLUTION_D	100000 | 
 | 349 | static int cc_reading_to_uv(int16_t reading) | 
 | 350 | { | 
 | 351 | 	return div_s64(reading * CC_READING_RESOLUTION_N, | 
 | 352 | 					CC_READING_RESOLUTION_D); | 
 | 353 | } | 
 | 354 |  | 
 | 355 | #define QPNP_ADC_GAIN_NV				17857LL | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 356 | static s64 cc_adjust_for_gain(s64 uv, uint16_t gain) | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 357 | { | 
 | 358 | 	s64 result_uv; | 
 | 359 |  | 
 | 360 | 	pr_debug("adjusting_uv = %lld\n", uv); | 
| Xiaozhe Shi | 820a47a | 2012-11-27 13:23:27 -0800 | [diff] [blame] | 361 | 	if (gain == 0) { | 
 | 362 | 		pr_debug("gain is %d, not adjusting\n", gain); | 
 | 363 | 		return uv; | 
 | 364 | 	} | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 365 | 	pr_debug("adjusting by factor: %lld/%hu = %lld%%\n", | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 366 | 			QPNP_ADC_GAIN_NV, gain, | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 367 | 			div_s64(QPNP_ADC_GAIN_NV * 100LL, (s64)gain)); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 368 |  | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 369 | 	result_uv = div_s64(uv * QPNP_ADC_GAIN_NV, (s64)gain); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 370 | 	pr_debug("result_uv = %lld\n", result_uv); | 
 | 371 | 	return result_uv; | 
 | 372 | } | 
 | 373 |  | 
 | 374 | static int convert_vsense_to_uv(struct qpnp_bms_chip *chip, | 
 | 375 | 					int16_t reading) | 
 | 376 | { | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 377 | 	struct qpnp_iadc_calib calibration; | 
 | 378 |  | 
 | 379 | 	qpnp_iadc_get_gain_and_offset(&calibration); | 
 | 380 | 	return cc_adjust_for_gain(cc_reading_to_uv(reading), | 
 | 381 | 			calibration.gain_raw); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 382 | } | 
 | 383 |  | 
 | 384 | static int read_vsense_avg(struct qpnp_bms_chip *chip, int *result_uv) | 
 | 385 | { | 
 | 386 | 	int rc; | 
 | 387 | 	int16_t reading; | 
 | 388 |  | 
 | 389 | 	rc = qpnp_read_wrapper(chip, (u8 *)&reading, | 
 | 390 | 			chip->base + BMS1_VSENSE_AVG_DATA0, 2); | 
 | 391 |  | 
 | 392 | 	if (rc) { | 
 | 393 | 		pr_err("fail to read VSENSE_AVG rc = %d\n", rc); | 
 | 394 | 		return rc; | 
 | 395 | 	} | 
 | 396 |  | 
 | 397 | 	*result_uv = convert_vsense_to_uv(chip, reading); | 
 | 398 | 	return 0; | 
 | 399 | } | 
 | 400 |  | 
 | 401 | static int get_battery_current(struct qpnp_bms_chip *chip, int *result_ua) | 
 | 402 | { | 
 | 403 | 	int vsense_uv = 0; | 
 | 404 |  | 
 | 405 | 	if (chip->r_sense_mohm == 0) { | 
 | 406 | 		pr_err("r_sense is zero\n"); | 
 | 407 | 		return -EINVAL; | 
 | 408 | 	} | 
 | 409 |  | 
 | 410 | 	mutex_lock(&chip->bms_output_lock); | 
 | 411 | 	lock_output_data(chip); | 
 | 412 | 	read_vsense_avg(chip, &vsense_uv); | 
 | 413 | 	unlock_output_data(chip); | 
 | 414 | 	mutex_unlock(&chip->bms_output_lock); | 
 | 415 |  | 
 | 416 | 	pr_debug("vsense_uv=%duV\n", vsense_uv); | 
 | 417 | 	/* cast for signed division */ | 
 | 418 | 	*result_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm; | 
 | 419 | 	pr_debug("ibat=%duA\n", *result_ua); | 
 | 420 | 	return 0; | 
 | 421 | } | 
 | 422 |  | 
 | 423 | static int get_battery_voltage(int *result_uv) | 
 | 424 | { | 
 | 425 | 	int rc; | 
 | 426 | 	struct qpnp_vadc_result adc_result; | 
 | 427 |  | 
 | 428 | 	rc = qpnp_vadc_read(VBAT_SNS, &adc_result); | 
 | 429 | 	if (rc) { | 
 | 430 | 		pr_err("error reading adc channel = %d, rc = %d\n", | 
 | 431 | 					VBAT_SNS, rc); | 
 | 432 | 		return rc; | 
 | 433 | 	} | 
 | 434 | 	pr_debug("mvolts phy = %lld meas = 0x%llx\n", adc_result.physical, | 
 | 435 | 						adc_result.measurement); | 
 | 436 | 	*result_uv = (int)adc_result.physical; | 
 | 437 | 	return 0; | 
 | 438 | } | 
 | 439 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 440 | #define CC_36_BIT_MASK 0xFFFFFFFFFLL | 
 | 441 |  | 
 | 442 | static int read_cc_raw(struct qpnp_bms_chip *chip, int64_t *reading) | 
 | 443 | { | 
 | 444 | 	int64_t raw_reading; | 
 | 445 | 	int rc; | 
 | 446 |  | 
 | 447 | 	rc = qpnp_read_wrapper(chip, (u8 *)&raw_reading, | 
 | 448 | 			chip->base + BMS1_CC_DATA0, 5); | 
 | 449 | 	if (rc) { | 
 | 450 | 		pr_err("Error reading cc: rc = %d\n", rc); | 
 | 451 | 		return -ENXIO; | 
 | 452 | 	} | 
 | 453 |  | 
 | 454 | 	raw_reading = raw_reading & CC_36_BIT_MASK; | 
 | 455 | 	/* convert 36 bit signed value into 64 signed value */ | 
 | 456 | 	*reading = (raw_reading >> 35) == 0LL ? | 
 | 457 | 		raw_reading : ((-1LL ^ CC_36_BIT_MASK) | raw_reading); | 
 | 458 | 	pr_debug("before conversion: %llx, after conversion: %llx\n", | 
 | 459 | 			raw_reading, *reading); | 
 | 460 |  | 
 | 461 | 	return 0; | 
 | 462 | } | 
 | 463 |  | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 464 | static int calib_vadc(struct qpnp_bms_chip *chip) | 
 | 465 | { | 
 | 466 | 	int rc; | 
 | 467 | 	struct qpnp_vadc_result result; | 
 | 468 |  | 
 | 469 | 	rc = qpnp_vadc_read(REF_625MV, &result); | 
 | 470 | 	if (rc) { | 
 | 471 | 		pr_debug("vadc read failed with rc = %d\n", rc); | 
 | 472 | 		return rc; | 
 | 473 | 	} | 
 | 474 | 	chip->vadc_v0625 = result.physical; | 
 | 475 |  | 
 | 476 | 	rc = qpnp_vadc_read(REF_125V, &result); | 
 | 477 | 	if (rc) { | 
 | 478 | 		pr_debug("vadc read failed with rc = %d\n", rc); | 
 | 479 | 		return rc; | 
 | 480 | 	} | 
 | 481 | 	chip->vadc_v1250 = result.physical; | 
 | 482 | 	pr_debug("vadc calib: 0625 = %d, 1250 = %d\n", | 
 | 483 | 			chip->vadc_v0625, chip->vadc_v1250); | 
 | 484 | 	return 0; | 
 | 485 | } | 
 | 486 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 487 | static void convert_and_store_ocv(struct qpnp_bms_chip *chip, | 
 | 488 | 				struct raw_soc_params *raw) | 
 | 489 | { | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 490 | 	int rc; | 
 | 491 |  | 
 | 492 | 	pr_debug("prev_last_good_ocv_raw = %d, last_good_ocv_raw = %d\n", | 
 | 493 | 			chip->prev_last_good_ocv_raw, | 
 | 494 | 			raw->last_good_ocv_raw); | 
 | 495 | 	rc = calib_vadc(chip); | 
 | 496 | 	if (rc) | 
 | 497 | 		pr_err("Vadc reference voltage read failed, rc = %d\n", rc); | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 498 | 	chip->prev_last_good_ocv_raw = raw->last_good_ocv_raw; | 
 | 499 | 	raw->last_good_ocv_uv = convert_vbatt_raw_to_uv(chip, | 
 | 500 | 					raw->last_good_ocv_raw); | 
 | 501 | 	chip->last_ocv_uv = raw->last_good_ocv_uv; | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 502 | 	pr_debug("last_good_ocv_uv = %d\n", raw->last_good_ocv_uv); | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 503 | } | 
 | 504 |  | 
| Xiaozhe Shi | a045a56 | 2012-11-28 16:55:39 -0800 | [diff] [blame] | 505 | #define CLEAR_CC			BIT(7) | 
 | 506 | #define CLEAR_SW_CC			BIT(6) | 
 | 507 | /** | 
 | 508 |  * reset both cc and sw-cc. | 
 | 509 |  * note: this should only be ever called from one thread | 
 | 510 |  * or there may be a race condition where CC is never enabled | 
 | 511 |  * again | 
 | 512 |  */ | 
 | 513 | static void reset_cc(struct qpnp_bms_chip *chip) | 
 | 514 | { | 
 | 515 | 	int rc; | 
 | 516 |  | 
 | 517 | 	pr_debug("resetting cc manually\n"); | 
 | 518 | 	rc = qpnp_masked_write(chip, BMS1_CC_CLEAR_CTL, | 
 | 519 | 				CLEAR_CC | CLEAR_SW_CC, | 
 | 520 | 				CLEAR_CC | CLEAR_SW_CC); | 
 | 521 | 	if (rc) | 
 | 522 | 		pr_err("cc reset failed: %d\n", rc); | 
 | 523 |  | 
 | 524 | 	/* wait for 100us for cc to reset */ | 
 | 525 | 	udelay(100); | 
 | 526 |  | 
 | 527 | 	rc = qpnp_masked_write(chip, BMS1_CC_CLEAR_CTL, | 
 | 528 | 				CLEAR_CC | CLEAR_SW_CC, 0); | 
 | 529 | 	if (rc) | 
 | 530 | 		pr_err("cc reenable failed: %d\n", rc); | 
 | 531 | } | 
 | 532 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 533 | static int read_soc_params_raw(struct qpnp_bms_chip *chip, | 
 | 534 | 				struct raw_soc_params *raw) | 
 | 535 | { | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 536 | 	int rc; | 
 | 537 |  | 
 | 538 | 	mutex_lock(&chip->bms_output_lock); | 
| Xiaozhe Shi | a045a56 | 2012-11-28 16:55:39 -0800 | [diff] [blame] | 539 |  | 
 | 540 | 	if (chip->prev_last_good_ocv_raw == 0) { | 
 | 541 | 		/* software workaround for BMS 1.0 | 
 | 542 | 		 * The coulomb counter does not reset upon PON, so reset it | 
 | 543 | 		 * manually upon probe. */ | 
 | 544 | 		if (chip->revision1 == 0 && chip->revision2 == 0) | 
 | 545 | 			reset_cc(chip); | 
 | 546 | 	} | 
 | 547 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 548 | 	lock_output_data(chip); | 
 | 549 |  | 
 | 550 | 	rc = qpnp_read_wrapper(chip, (u8 *)&raw->last_good_ocv_raw, | 
 | 551 | 			chip->base + BMS1_OCV_FOR_SOC_DATA0, 2); | 
 | 552 | 	if (rc) { | 
 | 553 | 		pr_err("Error reading ocv: rc = %d\n", rc); | 
 | 554 | 		return -ENXIO; | 
 | 555 | 	} | 
 | 556 |  | 
 | 557 | 	rc = read_cc_raw(chip, &raw->cc); | 
 | 558 | 	if (rc) { | 
 | 559 | 		pr_err("Failed to read raw cc data, rc = %d\n", rc); | 
 | 560 | 		return rc; | 
 | 561 | 	} | 
 | 562 |  | 
 | 563 | 	unlock_output_data(chip); | 
 | 564 | 	mutex_unlock(&chip->bms_output_lock); | 
 | 565 |  | 
 | 566 | 	if (chip->prev_last_good_ocv_raw == 0) { | 
 | 567 | 		convert_and_store_ocv(chip, raw); | 
 | 568 | 		pr_debug("PON_OCV_UV = %d\n", chip->last_ocv_uv); | 
 | 569 | 	} else if (chip->prev_last_good_ocv_raw != raw->last_good_ocv_raw) { | 
 | 570 | 		convert_and_store_ocv(chip, raw); | 
 | 571 | 		/* forget the old cc value upon ocv */ | 
 | 572 | 		chip->last_cc_uah = 0; | 
 | 573 | 	} else { | 
 | 574 | 		raw->last_good_ocv_uv = chip->last_ocv_uv; | 
 | 575 | 	} | 
 | 576 |  | 
 | 577 | 	/* fake a high OCV if done charging */ | 
 | 578 | 	if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) { | 
 | 579 | 		chip->ocv_reading_at_100 = 0; | 
 | 580 | 		chip->cc_reading_at_100 = 0; | 
 | 581 | 	} else { | 
 | 582 | 		/* | 
 | 583 | 		 * force 100% ocv by selecting the highest voltage the | 
 | 584 | 		 * battery could ever reach | 
 | 585 | 		 */ | 
 | 586 | 		raw->last_good_ocv_uv = chip->max_voltage_uv; | 
 | 587 | 		chip->last_ocv_uv = chip->max_voltage_uv; | 
 | 588 | 	} | 
 | 589 | 	pr_debug("last_good_ocv_raw= 0x%x, last_good_ocv_uv= %duV\n", | 
 | 590 | 			raw->last_good_ocv_raw, raw->last_good_ocv_uv); | 
 | 591 | 	pr_debug("cc_raw= 0x%llx\n", raw->cc); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 592 | 	return 0; | 
 | 593 | } | 
 | 594 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 595 | static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv, | 
 | 596 | 							int batt_temp) | 
 | 597 | { | 
 | 598 | 	int pc; | 
 | 599 |  | 
 | 600 | 	pc = interpolate_pc(chip->pc_temp_ocv_lut, | 
 | 601 | 			batt_temp / 10, ocv_uv / 1000); | 
 | 602 | 	pr_debug("pc = %u %% for ocv = %d uv batt_temp = %d\n", | 
 | 603 | 					pc, ocv_uv, batt_temp); | 
 | 604 | 	/* Multiply the initial FCC value by the scale factor. */ | 
 | 605 | 	return pc; | 
 | 606 | } | 
 | 607 |  | 
 | 608 | static int calculate_fcc(struct qpnp_bms_chip *chip, int batt_temp) | 
 | 609 | { | 
 | 610 | 	int fcc_uah; | 
 | 611 |  | 
 | 612 | 	if (chip->adjusted_fcc_temp_lut == NULL) { | 
 | 613 | 		/* interpolate_fcc returns a mv value. */ | 
 | 614 | 		fcc_uah = interpolate_fcc(chip->fcc_temp_lut, | 
 | 615 | 						batt_temp) * 1000; | 
 | 616 | 		pr_debug("fcc = %d uAh\n", fcc_uah); | 
 | 617 | 		return fcc_uah; | 
 | 618 | 	} else { | 
 | 619 | 		return 1000 * interpolate_fcc(chip->adjusted_fcc_temp_lut, | 
 | 620 | 				batt_temp); | 
 | 621 | 	} | 
 | 622 | } | 
 | 623 |  | 
 | 624 | /* calculate remaining charge at the time of ocv */ | 
 | 625 | static int calculate_ocv_charge(struct qpnp_bms_chip *chip, | 
 | 626 | 						struct raw_soc_params *raw, | 
 | 627 | 						int fcc_uah, | 
 | 628 | 						int batt_temp) | 
 | 629 | { | 
 | 630 | 	int  ocv_uv, pc; | 
 | 631 |  | 
 | 632 | 	ocv_uv = raw->last_good_ocv_uv; | 
 | 633 | 	pc = calculate_pc(chip, ocv_uv, batt_temp); | 
 | 634 | 	pr_debug("ocv_uv = %d pc = %d\n", ocv_uv, pc); | 
 | 635 | 	return (fcc_uah * pc) / 100; | 
 | 636 | } | 
 | 637 |  | 
 | 638 | #define CC_RESOLUTION_N		542535 | 
 | 639 | #define CC_RESOLUTION_D		100000 | 
 | 640 |  | 
 | 641 | static s64 cc_to_uv(s64 cc) | 
 | 642 | { | 
 | 643 | 	return div_s64(cc * CC_RESOLUTION_N, CC_RESOLUTION_D); | 
 | 644 | } | 
 | 645 |  | 
 | 646 | #define CC_READING_TICKS	56 | 
 | 647 | #define SLEEP_CLK_HZ		32764 | 
 | 648 | #define SECONDS_PER_HOUR	3600 | 
 | 649 |  | 
 | 650 | static s64 cc_uv_to_nvh(s64 cc_uv) | 
 | 651 | { | 
 | 652 | 	return div_s64(cc_uv * CC_READING_TICKS * 1000, | 
 | 653 | 			SLEEP_CLK_HZ * SECONDS_PER_HOUR); | 
 | 654 | } | 
 | 655 |  | 
 | 656 | /** | 
 | 657 |  * calculate_cc- | 
 | 658 |  * @chip:		the bms chip pointer | 
 | 659 |  * @cc:			the cc reading from bms h/w | 
 | 660 |  * @val:		return value | 
 | 661 |  * @coulomb_counter:	adjusted coulomb counter for 100% | 
 | 662 |  * | 
 | 663 |  * RETURNS: in val pointer coulomb counter based charger in uAh | 
 | 664 |  *          (micro Amp hour) | 
 | 665 |  */ | 
 | 666 | static int calculate_cc(struct qpnp_bms_chip *chip, int64_t cc) | 
 | 667 | { | 
 | 668 | 	int64_t cc_voltage_uv, cc_nvh, cc_uah; | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 669 | 	struct qpnp_iadc_calib calibration; | 
 | 670 |  | 
 | 671 | 	qpnp_iadc_get_gain_and_offset(&calibration); | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 672 | 	cc_voltage_uv = cc; | 
 | 673 | 	cc_voltage_uv -= chip->cc_reading_at_100; | 
 | 674 | 	pr_debug("cc = %lld. after subtracting 0x%llx cc = %lld\n", | 
 | 675 | 					cc, chip->cc_reading_at_100, | 
 | 676 | 					cc_voltage_uv); | 
 | 677 | 	cc_voltage_uv = cc_to_uv(cc_voltage_uv); | 
| Xiaozhe Shi | 4e37665 | 2012-10-25 12:38:50 -0700 | [diff] [blame] | 678 | 	cc_voltage_uv = cc_adjust_for_gain(cc_voltage_uv, calibration.gain_raw); | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 679 | 	pr_debug("cc_voltage_uv = %lld uv\n", cc_voltage_uv); | 
 | 680 | 	cc_nvh = cc_uv_to_nvh(cc_voltage_uv); | 
 | 681 | 	pr_debug("cc_nvh = %lld nano_volt_hour\n", cc_nvh); | 
 | 682 | 	cc_uah = div_s64(cc_nvh, chip->r_sense_mohm); | 
 | 683 | 	/* cc_raw had 4 bits of extra precision. | 
 | 684 | 	   By now it should be within 32 bit range */ | 
 | 685 | 	return (int)cc_uah; | 
 | 686 | } | 
 | 687 |  | 
 | 688 | static int get_rbatt(struct qpnp_bms_chip *chip, | 
 | 689 | 					int soc_rbatt_mohm, int batt_temp) | 
 | 690 | { | 
 | 691 | 	int rbatt_mohm, scalefactor; | 
 | 692 |  | 
 | 693 | 	rbatt_mohm = chip->default_rbatt_mohm; | 
 | 694 | 	pr_debug("rbatt before scaling = %d\n", rbatt_mohm); | 
 | 695 | 	if (chip->rbatt_sf_lut == NULL)  { | 
 | 696 | 		pr_debug("RBATT = %d\n", rbatt_mohm); | 
 | 697 | 		return rbatt_mohm; | 
 | 698 | 	} | 
 | 699 | 	/* Convert the batt_temp to DegC from deciDegC */ | 
 | 700 | 	batt_temp = batt_temp / 10; | 
 | 701 | 	scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut, | 
 | 702 | 						batt_temp, soc_rbatt_mohm); | 
 | 703 | 	pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n", | 
 | 704 | 				scalefactor, batt_temp, soc_rbatt_mohm); | 
 | 705 | 	rbatt_mohm = (rbatt_mohm * scalefactor) / 100; | 
 | 706 |  | 
 | 707 | 	rbatt_mohm += chip->r_conn_mohm; | 
 | 708 | 	pr_debug("adding r_conn_mohm = %d rbatt = %d\n", | 
 | 709 | 				chip->r_conn_mohm, rbatt_mohm); | 
 | 710 |  | 
 | 711 | 	pr_debug("RBATT = %d\n", rbatt_mohm); | 
 | 712 | 	return rbatt_mohm; | 
 | 713 | } | 
 | 714 |  | 
 | 715 | static void calculate_iavg(struct qpnp_bms_chip *chip, int cc_uah, | 
 | 716 | 				int *iavg_ua) | 
 | 717 | { | 
 | 718 | 	int delta_cc_uah, delta_time_s, rc; | 
 | 719 | 	struct rtc_time tm; | 
 | 720 | 	struct rtc_device *rtc; | 
 | 721 | 	unsigned long now_tm_sec = 0; | 
 | 722 |  | 
 | 723 | 	rc = 0; | 
 | 724 | 	/* if anything fails report the previous iavg_ua */ | 
 | 725 | 	*iavg_ua = chip->prev_iavg_ua; | 
 | 726 |  | 
 | 727 | 	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); | 
 | 728 | 	if (rtc == NULL) { | 
 | 729 | 		pr_err("%s: unable to open rtc device (%s)\n", | 
 | 730 | 			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE); | 
 | 731 | 		goto out; | 
 | 732 | 	} | 
 | 733 |  | 
 | 734 | 	rc = rtc_read_time(rtc, &tm); | 
 | 735 | 	if (rc) { | 
 | 736 | 		pr_err("Error reading rtc device (%s) : %d\n", | 
 | 737 | 			CONFIG_RTC_HCTOSYS_DEVICE, rc); | 
 | 738 | 		goto out; | 
 | 739 | 	} | 
 | 740 |  | 
 | 741 | 	rc = rtc_valid_tm(&tm); | 
 | 742 | 	if (rc) { | 
 | 743 | 		pr_err("Invalid RTC time (%s): %d\n", | 
 | 744 | 			CONFIG_RTC_HCTOSYS_DEVICE, rc); | 
 | 745 | 		goto out; | 
 | 746 | 	} | 
 | 747 | 	rtc_tm_to_time(&tm, &now_tm_sec); | 
 | 748 |  | 
 | 749 | 	if (chip->tm_sec == 0) { | 
 | 750 | 		get_battery_current(chip, iavg_ua); | 
 | 751 | 		goto out; | 
 | 752 | 	} | 
 | 753 |  | 
 | 754 | 	delta_time_s = (now_tm_sec - chip->tm_sec); | 
 | 755 |  | 
 | 756 | 	/* use the previous iavg if called within 15 seconds */ | 
 | 757 | 	if (delta_time_s < 15) { | 
 | 758 | 		*iavg_ua = chip->prev_iavg_ua; | 
 | 759 | 		goto out; | 
 | 760 | 	} | 
 | 761 |  | 
 | 762 | 	delta_cc_uah = cc_uah - chip->last_cc_uah; | 
 | 763 |  | 
 | 764 | 	*iavg_ua = div_s64((s64)delta_cc_uah * 3600, delta_time_s); | 
 | 765 |  | 
 | 766 | 	pr_debug("tm_sec = %ld, now_tm_sec = %ld delta_s = %d delta_cc = %d iavg_ua = %d\n", | 
 | 767 | 				chip->tm_sec, now_tm_sec, | 
 | 768 | 				delta_time_s, delta_cc_uah, (int)*iavg_ua); | 
 | 769 |  | 
 | 770 | out: | 
 | 771 | 	/* remember the iavg */ | 
 | 772 | 	chip->prev_iavg_ua = *iavg_ua; | 
 | 773 |  | 
 | 774 | 	/* remember cc_uah */ | 
 | 775 | 	chip->last_cc_uah = cc_uah; | 
 | 776 |  | 
 | 777 | 	/* remember this time */ | 
 | 778 | 	chip->tm_sec = now_tm_sec; | 
 | 779 | } | 
 | 780 |  | 
 | 781 | static int calculate_termination_uuc(struct qpnp_bms_chip *chip, | 
 | 782 | 					struct soc_params *params, | 
 | 783 | 					int batt_temp, int uuc_iavg_ma, | 
 | 784 | 					int *ret_pc_unusable) | 
 | 785 | { | 
 | 786 | 	int unusable_uv, pc_unusable, uuc_uah; | 
 | 787 | 	int i = 0; | 
 | 788 | 	int ocv_mv; | 
 | 789 | 	int batt_temp_degc = batt_temp / 10; | 
 | 790 | 	int rbatt_mohm; | 
 | 791 | 	int delta_uv; | 
 | 792 | 	int prev_delta_uv = 0; | 
 | 793 | 	int prev_rbatt_mohm = 0; | 
 | 794 | 	int uuc_rbatt_mohm; | 
 | 795 |  | 
 | 796 | 	for (i = 0; i <= 100; i++) { | 
 | 797 | 		ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, | 
 | 798 | 				batt_temp_degc, i); | 
 | 799 | 		rbatt_mohm = get_rbatt(chip, i, batt_temp); | 
 | 800 | 		unusable_uv = (rbatt_mohm * uuc_iavg_ma) | 
 | 801 | 							+ (chip->v_cutoff_uv); | 
 | 802 | 		delta_uv = ocv_mv * 1000 - unusable_uv; | 
 | 803 |  | 
 | 804 | 		pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n", | 
 | 805 | 				i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv); | 
 | 806 |  | 
 | 807 | 		if (delta_uv > 0) | 
 | 808 | 			break; | 
 | 809 |  | 
 | 810 | 		prev_delta_uv = delta_uv; | 
 | 811 | 		prev_rbatt_mohm = rbatt_mohm; | 
 | 812 | 	} | 
 | 813 |  | 
 | 814 | 	uuc_rbatt_mohm = linear_interpolate(rbatt_mohm, delta_uv, | 
 | 815 | 					prev_rbatt_mohm, prev_delta_uv, | 
 | 816 | 					0); | 
 | 817 |  | 
 | 818 | 	unusable_uv = (uuc_rbatt_mohm * uuc_iavg_ma) + (chip->v_cutoff_uv); | 
 | 819 |  | 
 | 820 | 	pc_unusable = calculate_pc(chip, unusable_uv, batt_temp); | 
 | 821 | 	uuc_uah = (params->fcc_uah * pc_unusable) / 100; | 
 | 822 | 	pr_debug("For uuc_iavg_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n", | 
 | 823 | 					uuc_iavg_ma, | 
 | 824 | 					uuc_rbatt_mohm, unusable_uv, | 
 | 825 | 					pc_unusable, uuc_uah); | 
 | 826 | 	*ret_pc_unusable = pc_unusable; | 
 | 827 | 	return uuc_uah; | 
 | 828 | } | 
 | 829 |  | 
 | 830 | static int adjust_uuc(struct qpnp_bms_chip *chip, | 
 | 831 | 			struct soc_params *params, | 
 | 832 | 			int new_pc_unusable, | 
 | 833 | 			int new_uuc_uah, | 
 | 834 | 			int batt_temp) | 
 | 835 | { | 
 | 836 | 	int new_unusable_mv, new_iavg_ma; | 
 | 837 | 	int batt_temp_degc = batt_temp / 10; | 
 | 838 |  | 
 | 839 | 	if (chip->prev_pc_unusable == -EINVAL | 
 | 840 | 		|| abs(chip->prev_pc_unusable - new_pc_unusable) <= 1) { | 
 | 841 | 		chip->prev_pc_unusable = new_pc_unusable; | 
 | 842 | 		return new_uuc_uah; | 
 | 843 | 	} | 
 | 844 |  | 
 | 845 | 	/* the uuc is trying to change more than 1% restrict it */ | 
 | 846 | 	if (new_pc_unusable > chip->prev_pc_unusable) | 
 | 847 | 		chip->prev_pc_unusable++; | 
 | 848 | 	else | 
 | 849 | 		chip->prev_pc_unusable--; | 
 | 850 |  | 
 | 851 | 	new_uuc_uah = (params->fcc_uah * chip->prev_pc_unusable) / 100; | 
 | 852 |  | 
 | 853 | 	/* also find update the iavg_ma accordingly */ | 
 | 854 | 	new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut, | 
 | 855 | 			batt_temp_degc, chip->prev_pc_unusable); | 
 | 856 | 	if (new_unusable_mv < chip->v_cutoff_uv/1000) | 
 | 857 | 		new_unusable_mv = chip->v_cutoff_uv/1000; | 
 | 858 |  | 
 | 859 | 	new_iavg_ma = (new_unusable_mv * 1000 - chip->v_cutoff_uv) | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 860 | 						/ params->rbatt_mohm; | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 861 | 	if (new_iavg_ma == 0) | 
 | 862 | 		new_iavg_ma = 1; | 
 | 863 | 	chip->prev_uuc_iavg_ma = new_iavg_ma; | 
 | 864 | 	pr_debug("Restricting UUC to %d (%d%%) unusable_mv = %d iavg_ma = %d\n", | 
 | 865 | 					new_uuc_uah, chip->prev_pc_unusable, | 
 | 866 | 					new_unusable_mv, new_iavg_ma); | 
 | 867 |  | 
 | 868 | 	return new_uuc_uah; | 
 | 869 | } | 
 | 870 |  | 
 | 871 | #define CHARGING_IAVG_MA 250 | 
 | 872 | #define MIN_SECONDS_FOR_VALID_SAMPLE	20 | 
 | 873 | static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip, | 
 | 874 | 					struct soc_params *params, | 
 | 875 | 					int batt_temp) | 
 | 876 | { | 
 | 877 | 	int uuc_uah_iavg; | 
 | 878 | 	int i; | 
 | 879 | 	int uuc_iavg_ma = params->iavg_ua / 1000; | 
 | 880 | 	int pc_unusable; | 
 | 881 |  | 
 | 882 | 	/* | 
 | 883 | 	 * if called first time, fill all the samples with | 
 | 884 | 	 * the shutdown_iavg_ma | 
 | 885 | 	 */ | 
 | 886 | 	if (chip->first_time_calc_uuc && chip->shutdown_iavg_ma != 0) { | 
 | 887 | 		pr_debug("Using shutdown_iavg_ma = %d in all samples\n", | 
 | 888 | 				chip->shutdown_iavg_ma); | 
 | 889 | 		for (i = 0; i < IAVG_SAMPLES; i++) | 
 | 890 | 			chip->iavg_samples_ma[i] = chip->shutdown_iavg_ma; | 
 | 891 |  | 
 | 892 | 		chip->iavg_index = 0; | 
 | 893 | 		chip->iavg_num_samples = IAVG_SAMPLES; | 
 | 894 | 	} | 
 | 895 |  | 
 | 896 | 	/* | 
 | 897 | 	 * if charging use a nominal avg current to keep | 
 | 898 | 	 * a reasonable UUC while charging | 
 | 899 | 	 */ | 
 | 900 | 	if (uuc_iavg_ma < 0) | 
 | 901 | 		uuc_iavg_ma = CHARGING_IAVG_MA; | 
 | 902 | 	chip->iavg_samples_ma[chip->iavg_index] = uuc_iavg_ma; | 
 | 903 | 	chip->iavg_index = (chip->iavg_index + 1) % IAVG_SAMPLES; | 
 | 904 | 	chip->iavg_num_samples++; | 
 | 905 | 	if (chip->iavg_num_samples >= IAVG_SAMPLES) | 
 | 906 | 		chip->iavg_num_samples = IAVG_SAMPLES; | 
 | 907 |  | 
 | 908 | 	/* now that this sample is added calcualte the average */ | 
 | 909 | 	uuc_iavg_ma = 0; | 
 | 910 | 	if (chip->iavg_num_samples != 0) { | 
 | 911 | 		for (i = 0; i < chip->iavg_num_samples; i++) { | 
 | 912 | 			pr_debug("iavg_samples_ma[%d] = %d\n", i, | 
 | 913 | 					chip->iavg_samples_ma[i]); | 
 | 914 | 			uuc_iavg_ma += chip->iavg_samples_ma[i]; | 
 | 915 | 		} | 
 | 916 |  | 
 | 917 | 		uuc_iavg_ma = DIV_ROUND_CLOSEST(uuc_iavg_ma, | 
 | 918 | 						chip->iavg_num_samples); | 
 | 919 | 	} | 
 | 920 |  | 
 | 921 | 	uuc_uah_iavg = calculate_termination_uuc(chip, params, uuc_iavg_ma, | 
 | 922 | 						batt_temp, &pc_unusable); | 
 | 923 | 	pr_debug("uuc_iavg_ma = %d uuc with iavg = %d\n", | 
 | 924 | 						uuc_iavg_ma, uuc_uah_iavg); | 
 | 925 |  | 
 | 926 | 	chip->prev_uuc_iavg_ma = uuc_iavg_ma; | 
 | 927 | 	/* restrict the uuc such that it can increase only by one percent */ | 
 | 928 | 	uuc_uah_iavg = adjust_uuc(chip, params, pc_unusable, | 
 | 929 | 					uuc_uah_iavg, batt_temp); | 
 | 930 |  | 
 | 931 | 	chip->first_time_calc_uuc = 0; | 
 | 932 | 	return uuc_uah_iavg; | 
 | 933 | } | 
 | 934 |  | 
 | 935 | static void find_ocv_for_soc(struct qpnp_bms_chip *chip, | 
 | 936 | 				struct soc_params *params, | 
 | 937 | 				int batt_temp, | 
 | 938 | 				int shutdown_soc, | 
 | 939 | 				int *ret_ocv_uv) | 
 | 940 | { | 
 | 941 | 	s64 ocv_charge_uah; | 
 | 942 | 	int pc, new_pc; | 
 | 943 | 	int batt_temp_degc = batt_temp / 10; | 
 | 944 | 	int ocv_uv; | 
 | 945 |  | 
 | 946 | 	ocv_charge_uah = (s64)shutdown_soc | 
 | 947 | 				* (params->fcc_uah - params->uuc_uah); | 
 | 948 | 	ocv_charge_uah = div_s64(ocv_charge_uah, 100) | 
 | 949 | 				+ params->cc_uah + params->uuc_uah; | 
 | 950 | 	pc = DIV_ROUND_CLOSEST((int)ocv_charge_uah * 100, params->fcc_uah); | 
 | 951 | 	pc = clamp(pc, 0, 100); | 
 | 952 |  | 
 | 953 | 	ocv_uv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc); | 
 | 954 |  | 
 | 955 | 	pr_debug("s_soc = %d, fcc = %d uuc = %d rc = %d, pc = %d, ocv mv = %d\n", | 
 | 956 | 					shutdown_soc, params->fcc_uah, | 
 | 957 | 					params->uuc_uah, (int)ocv_charge_uah, | 
 | 958 | 					pc, ocv_uv); | 
 | 959 | 	new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv_uv); | 
 | 960 | 	pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv_uv); | 
 | 961 |  | 
 | 962 | 	while (abs(new_pc - pc) > 1) { | 
 | 963 | 		int delta_mv = 5; | 
 | 964 |  | 
 | 965 | 		if (new_pc > pc) | 
 | 966 | 			delta_mv = -1 * delta_mv; | 
 | 967 |  | 
 | 968 | 		ocv_uv = ocv_uv + delta_mv; | 
 | 969 | 		new_pc = interpolate_pc(chip->pc_temp_ocv_lut, | 
 | 970 | 				batt_temp_degc, ocv_uv); | 
 | 971 | 		pr_debug("test revlookup pc = %d for ocv = %d\n", | 
 | 972 | 				new_pc, ocv_uv); | 
 | 973 | 	} | 
 | 974 |  | 
 | 975 | 	*ret_ocv_uv = ocv_uv * 1000; | 
 | 976 | 	params->ocv_charge_uah = (int)ocv_charge_uah; | 
 | 977 | } | 
 | 978 |  | 
 | 979 | static void calculate_soc_params(struct qpnp_bms_chip *chip, | 
 | 980 | 						struct raw_soc_params *raw, | 
 | 981 | 						struct soc_params *params, | 
 | 982 | 						int batt_temp) | 
 | 983 | { | 
 | 984 | 	int soc_rbatt; | 
 | 985 |  | 
 | 986 | 	params->fcc_uah = calculate_fcc(chip, batt_temp); | 
 | 987 | 	pr_debug("FCC = %uuAh batt_temp = %d\n", params->fcc_uah, batt_temp); | 
 | 988 |  | 
 | 989 | 	/* calculate remainging charge */ | 
 | 990 | 	params->ocv_charge_uah = calculate_ocv_charge( | 
 | 991 | 						chip, raw, | 
 | 992 | 						params->fcc_uah, | 
 | 993 | 						batt_temp); | 
 | 994 | 	pr_debug("ocv_charge_uah = %uuAh\n", params->ocv_charge_uah); | 
 | 995 |  | 
 | 996 | 	/* calculate cc micro_volt_hour */ | 
 | 997 | 	params->cc_uah = calculate_cc(chip, raw->cc); | 
 | 998 | 	pr_debug("cc_uah = %duAh raw->cc = %llx cc = %lld after subtracting %llx\n", | 
 | 999 | 				params->cc_uah, raw->cc, | 
 | 1000 | 				(int64_t)raw->cc - chip->cc_reading_at_100, | 
 | 1001 | 				chip->cc_reading_at_100); | 
 | 1002 |  | 
 | 1003 | 	soc_rbatt = ((params->ocv_charge_uah - params->cc_uah) * 100) | 
 | 1004 | 							/ params->fcc_uah; | 
 | 1005 | 	if (soc_rbatt < 0) | 
 | 1006 | 		soc_rbatt = 0; | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 1007 | 	params->rbatt_mohm = get_rbatt(chip, soc_rbatt, batt_temp); | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1008 |  | 
 | 1009 | 	calculate_iavg(chip, params->cc_uah, ¶ms->iavg_ua); | 
 | 1010 |  | 
 | 1011 | 	params->uuc_uah = calculate_unusable_charge_uah(chip, params, | 
 | 1012 | 							batt_temp); | 
 | 1013 | 	pr_debug("UUC = %uuAh\n", params->uuc_uah); | 
 | 1014 | } | 
 | 1015 |  | 
 | 1016 | static bool is_shutdown_soc_within_limits(struct qpnp_bms_chip *chip, int soc) | 
 | 1017 | { | 
 | 1018 | 	if (chip->shutdown_soc_invalid) { | 
 | 1019 | 		pr_debug("NOT forcing shutdown soc = %d\n", chip->shutdown_soc); | 
 | 1020 | 		return 0; | 
 | 1021 | 	} | 
 | 1022 |  | 
 | 1023 | 	if (abs(chip->shutdown_soc - soc) > chip->shutdown_soc_valid_limit) { | 
 | 1024 | 		pr_debug("rejecting shutdown soc = %d, soc = %d limit = %d\n", | 
 | 1025 | 			chip->shutdown_soc, soc, | 
 | 1026 | 			chip->shutdown_soc_valid_limit); | 
 | 1027 | 		chip->shutdown_soc_invalid = 1; | 
 | 1028 | 		return 0; | 
 | 1029 | 	} | 
 | 1030 |  | 
 | 1031 | 	return 1; | 
 | 1032 | } | 
 | 1033 |  | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1034 | #define BMS_OVERRIDE_MODE_EN_BIT	BIT(7) | 
 | 1035 | #define EN_VBAT_BIT			BIT(0) | 
 | 1036 | #define OVERRIDE_MODE_DELAY_MS		20 | 
 | 1037 | static int override_mode_batt_v_and_i( | 
 | 1038 | 		struct qpnp_bms_chip *chip, int *ibat_ua, int *vbat_uv) | 
 | 1039 | { | 
 | 1040 | 	int16_t vsense_raw, vbat_raw; | 
 | 1041 | 	int vsense_uv, rc; | 
 | 1042 | 	u8 delay; | 
 | 1043 |  | 
 | 1044 | 	mutex_lock(&chip->bms_output_lock); | 
 | 1045 |  | 
 | 1046 | 	delay = 0x00; | 
 | 1047 | 	rc = qpnp_write_wrapper(chip, &delay, | 
 | 1048 | 			chip->base + BMS1_S1_DELAY_CTL, 1); | 
 | 1049 | 	if (rc) | 
 | 1050 | 		pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc); | 
 | 1051 |  | 
 | 1052 | 	rc = qpnp_masked_write(chip, BMS1_MODE_CTL, | 
 | 1053 | 			BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT, | 
 | 1054 | 			BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT); | 
 | 1055 | 	if (rc) | 
 | 1056 | 		pr_err("unable to write into BMS1_MODE_CTL, rc: %d\n", rc); | 
 | 1057 |  | 
 | 1058 | 	msleep(OVERRIDE_MODE_DELAY_MS); | 
 | 1059 |  | 
 | 1060 | 	lock_output_data(chip); | 
 | 1061 | 	qpnp_read_wrapper(chip, (u8 *)&vsense_raw, | 
 | 1062 | 			chip->base + BMS1_VSENSE_AVG_DATA0, 2); | 
 | 1063 | 	qpnp_read_wrapper(chip, (u8 *)&vbat_raw, | 
 | 1064 | 			chip->base + BMS1_VBAT_AVG_DATA0, 2); | 
 | 1065 | 	unlock_output_data(chip); | 
 | 1066 |  | 
 | 1067 | 	rc = qpnp_masked_write(chip, BMS1_MODE_CTL, | 
 | 1068 | 			BMS_OVERRIDE_MODE_EN_BIT | EN_VBAT_BIT, 0); | 
 | 1069 |  | 
 | 1070 | 	delay = 0x0B; | 
 | 1071 | 	rc = qpnp_write_wrapper(chip, &delay, | 
 | 1072 | 			chip->base + BMS1_S1_DELAY_CTL, 1); | 
 | 1073 | 	if (rc) | 
 | 1074 | 		pr_err("unable to write into BMS1_S1_DELAY, rc: %d\n", rc); | 
 | 1075 |  | 
 | 1076 | 	mutex_unlock(&chip->bms_output_lock); | 
 | 1077 |  | 
 | 1078 | 	*vbat_uv = convert_vbatt_raw_to_uv(chip, vbat_raw); | 
 | 1079 | 	vsense_uv = convert_vsense_to_uv(chip, vsense_raw); | 
 | 1080 | 	*ibat_ua = vsense_uv * 1000 / (int)chip->r_sense_mohm; | 
 | 1081 |  | 
 | 1082 | 	pr_debug("vsense_raw = 0x%x vbat_raw = 0x%x ibat_ua = %d vbat_uv = %d\n", | 
 | 1083 | 			(uint16_t)vsense_raw, (uint16_t)vbat_raw, | 
 | 1084 | 			*ibat_ua, *vbat_uv); | 
 | 1085 | 	return 0; | 
 | 1086 | } | 
 | 1087 |  | 
| Xiaozhe Shi | ba3bdd3 | 2012-11-29 14:50:53 -0800 | [diff] [blame] | 1088 | static bool is_batfet_open(struct qpnp_bms_chip *chip) | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1089 | { | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1090 | 	union power_supply_propval ret = {0,}; | 
 | 1091 |  | 
 | 1092 | 	if (chip->batt_psy == NULL) | 
 | 1093 | 		chip->batt_psy = power_supply_get_by_name("battery"); | 
 | 1094 | 	if (chip->batt_psy) { | 
 | 1095 | 		/* if battery has been registered, use the status property */ | 
 | 1096 | 		chip->batt_psy->get_property(chip->batt_psy, | 
 | 1097 | 					POWER_SUPPLY_PROP_STATUS, &ret); | 
| Xiaozhe Shi | ba3bdd3 | 2012-11-29 14:50:53 -0800 | [diff] [blame] | 1098 | 		return ret.intval == POWER_SUPPLY_STATUS_FULL; | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1099 | 	} | 
 | 1100 |  | 
| Xiaozhe Shi | ba3bdd3 | 2012-11-29 14:50:53 -0800 | [diff] [blame] | 1101 | 	/* Default to true if the battery power supply is not registered. */ | 
 | 1102 | 	pr_debug("battery power supply is not registered\n"); | 
 | 1103 | 	return true; | 
 | 1104 | } | 
 | 1105 |  | 
 | 1106 | static int get_simultaneous_batt_v_and_i(struct qpnp_bms_chip *chip, | 
 | 1107 | 					int *ibat_ua, int *vbat_uv) | 
 | 1108 | { | 
 | 1109 | 	int rc; | 
 | 1110 |  | 
 | 1111 | 	if (is_batfet_open(chip)) { | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1112 | 		pr_debug("batfet is open using separate vbat and ibat meas\n"); | 
 | 1113 | 		rc = get_battery_voltage(vbat_uv); | 
 | 1114 | 		if (rc < 0) { | 
 | 1115 | 			pr_err("adc vbat failed err = %d\n", rc); | 
 | 1116 | 			return rc; | 
 | 1117 | 		} | 
 | 1118 | 		rc = get_battery_current(chip, ibat_ua); | 
 | 1119 | 		if (rc < 0) { | 
 | 1120 | 			pr_err("bms ibat failed err = %d\n", rc); | 
 | 1121 | 			return rc; | 
 | 1122 | 		} | 
 | 1123 | 	} else { | 
 | 1124 | 		return override_mode_batt_v_and_i(chip, ibat_ua, vbat_uv); | 
 | 1125 | 	} | 
 | 1126 |  | 
 | 1127 | 	return 0; | 
 | 1128 | } | 
 | 1129 |  | 
 | 1130 | static int bound_soc(int soc) | 
 | 1131 | { | 
 | 1132 | 	soc = max(0, soc); | 
 | 1133 | 	soc = min(100, soc); | 
 | 1134 | 	return soc; | 
 | 1135 | } | 
 | 1136 |  | 
 | 1137 | static int charging_adjustments(struct qpnp_bms_chip *chip, | 
 | 1138 | 				struct soc_params *params, int soc, | 
 | 1139 | 				int vbat_uv, int ibat_ua, int batt_temp) | 
 | 1140 | { | 
| Xiaozhe Shi | 41bc1f1 | 2012-09-26 16:55:22 -0700 | [diff] [blame] | 1141 | 	int chg_soc; | 
 | 1142 |  | 
 | 1143 | 	if (chip->soc_at_cv == -EINVAL) { | 
 | 1144 | 		/* In constant current charging return the calc soc */ | 
 | 1145 | 		if (vbat_uv <= chip->max_voltage_uv) | 
 | 1146 | 			pr_debug("CC CHG SOC %d\n", soc); | 
 | 1147 |  | 
 | 1148 | 		/* Note the CC to CV point */ | 
 | 1149 | 		if (vbat_uv >= chip->max_voltage_uv) { | 
 | 1150 | 			chip->soc_at_cv = soc; | 
 | 1151 | 			chip->prev_chg_soc = soc; | 
 | 1152 | 			chip->ibat_at_cv_ua = ibat_ua; | 
 | 1153 | 			pr_debug("CC_TO_CV ibat_ua = %d CHG SOC %d\n", | 
 | 1154 | 					ibat_ua, soc); | 
 | 1155 | 		} | 
 | 1156 | 		return soc; | 
 | 1157 | 	} | 
 | 1158 |  | 
 | 1159 | 	/* | 
 | 1160 | 	 * battery is in CV phase - begin liner inerpolation of soc based on | 
 | 1161 | 	 * battery charge current | 
 | 1162 | 	 */ | 
 | 1163 |  | 
 | 1164 | 	/* | 
 | 1165 | 	 * if voltage lessened (possibly because of a system load) | 
 | 1166 | 	 * keep reporting the prev chg soc | 
 | 1167 | 	 */ | 
 | 1168 | 	if (vbat_uv <= chip->max_voltage_uv) { | 
 | 1169 | 		pr_debug("vbat %d < max = %d CC CHG SOC %d\n", | 
 | 1170 | 			vbat_uv, chip->max_voltage_uv, chip->prev_chg_soc); | 
 | 1171 | 		return chip->prev_chg_soc; | 
 | 1172 | 	} | 
 | 1173 |  | 
 | 1174 | 	chg_soc = linear_interpolate(chip->soc_at_cv, chip->ibat_at_cv_ua, | 
 | 1175 | 					100, -100000, | 
 | 1176 | 					ibat_ua); | 
| Xiaozhe Shi | 78d0c53 | 2012-12-10 13:02:14 -0800 | [diff] [blame] | 1177 | 	chg_soc = bound_soc(chg_soc); | 
| Xiaozhe Shi | 41bc1f1 | 2012-09-26 16:55:22 -0700 | [diff] [blame] | 1178 |  | 
 | 1179 | 	/* always report a higher soc */ | 
 | 1180 | 	if (chg_soc > chip->prev_chg_soc) { | 
 | 1181 | 		int new_ocv_uv; | 
 | 1182 |  | 
 | 1183 | 		chip->prev_chg_soc = chg_soc; | 
 | 1184 |  | 
 | 1185 | 		find_ocv_for_soc(chip, params, batt_temp, chg_soc, &new_ocv_uv); | 
 | 1186 | 		chip->last_ocv_uv = new_ocv_uv; | 
 | 1187 | 		pr_debug("CC CHG ADJ OCV = %d CHG SOC %d\n", | 
 | 1188 | 				new_ocv_uv, | 
 | 1189 | 				chip->prev_chg_soc); | 
 | 1190 | 	} | 
 | 1191 |  | 
 | 1192 | 	pr_debug("Reporting CHG SOC %d\n", chip->prev_chg_soc); | 
 | 1193 | 	return chip->prev_chg_soc; | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1194 | } | 
 | 1195 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1196 | static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params, | 
 | 1197 | 							int soc, int batt_temp) | 
 | 1198 | { | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1199 | 	int ibat_ua = 0, vbat_uv = 0; | 
 | 1200 | 	int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0; | 
 | 1201 | 	int delta_ocv_uv = 0; | 
 | 1202 | 	int n = 0; | 
 | 1203 | 	int rc_new_uah = 0; | 
 | 1204 | 	int pc_new = 0; | 
 | 1205 | 	int soc_new = 0; | 
 | 1206 | 	int slope = 0; | 
 | 1207 | 	int rc = 0; | 
 | 1208 | 	int delta_ocv_uv_limit = 0; | 
 | 1209 |  | 
 | 1210 | 	rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv); | 
 | 1211 | 	if (rc < 0) { | 
 | 1212 | 		pr_err("simultaneous vbat ibat failed err = %d\n", rc); | 
 | 1213 | 		goto out; | 
 | 1214 | 	} | 
 | 1215 |  | 
 | 1216 | 	delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000); | 
 | 1217 |  | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 1218 | 	ocv_est_uv = vbat_uv + (ibat_ua * params->rbatt_mohm)/1000; | 
 | 1219 |  | 
 | 1220 | 	chip->ibat_max_ua = (ocv_est_uv - chip->v_cutoff_uv) * 1000 | 
 | 1221 | 					/ (params->rbatt_mohm); | 
 | 1222 |  | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1223 | 	pc_est = calculate_pc(chip, ocv_est_uv, batt_temp); | 
 | 1224 | 	soc_est = div_s64((s64)params->fcc_uah * pc_est - params->uuc_uah*100, | 
 | 1225 | 				(s64)params->fcc_uah - params->uuc_uah); | 
 | 1226 | 	soc_est = bound_soc(soc_est); | 
 | 1227 |  | 
| Xiaozhe Shi | ba3bdd3 | 2012-11-29 14:50:53 -0800 | [diff] [blame] | 1228 | 	if (ibat_ua < 0 && !is_batfet_open(chip)) { | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1229 | 		soc = charging_adjustments(chip, params, soc, vbat_uv, ibat_ua, | 
 | 1230 | 				batt_temp); | 
 | 1231 | 		goto out; | 
 | 1232 | 	} | 
 | 1233 |  | 
 | 1234 | 	/* | 
 | 1235 | 	 * do not adjust | 
 | 1236 | 	 * if soc is same as what bms calculated | 
 | 1237 | 	 * if soc_est is between 45 and 25, this is the flat portion of the | 
 | 1238 | 	 * curve where soc_est is not so accurate. We generally don't want to | 
 | 1239 | 	 * adjust when soc_est is inaccurate except for the cases when soc is | 
 | 1240 | 	 * way far off (higher than 50 or lesser than 20). | 
 | 1241 | 	 * Also don't adjust soc if it is above 90 becuase it might be pulled | 
 | 1242 | 	 * low and cause a bad user experience | 
 | 1243 | 	 */ | 
 | 1244 | 	if (soc_est == soc | 
 | 1245 | 		|| (is_between(45, chip->adjust_soc_low_threshold, soc_est) | 
 | 1246 | 		&& is_between(50, chip->adjust_soc_low_threshold - 5, soc)) | 
 | 1247 | 		|| soc >= 90) | 
 | 1248 | 		goto out; | 
 | 1249 |  | 
 | 1250 | 	if (chip->last_soc_est == -EINVAL) | 
 | 1251 | 		chip->last_soc_est = soc; | 
 | 1252 |  | 
 | 1253 | 	n = min(200, max(1 , soc + soc_est + chip->last_soc_est)); | 
 | 1254 | 	chip->last_soc_est = soc_est; | 
 | 1255 |  | 
 | 1256 | 	pc = calculate_pc(chip, chip->last_ocv_uv, batt_temp); | 
 | 1257 | 	if (pc > 0) { | 
 | 1258 | 		pc_new = calculate_pc(chip, | 
 | 1259 | 				chip->last_ocv_uv - (++slope * 1000), | 
 | 1260 | 				batt_temp); | 
 | 1261 | 		while (pc_new == pc) { | 
 | 1262 | 			/* start taking 10mV steps */ | 
 | 1263 | 			slope = slope + 10; | 
 | 1264 | 			pc_new = calculate_pc(chip, | 
 | 1265 | 				chip->last_ocv_uv - (slope * 1000), | 
 | 1266 | 				batt_temp); | 
 | 1267 | 		} | 
 | 1268 | 	} else { | 
 | 1269 | 		/* | 
 | 1270 | 		 * pc is already at the lowest point, | 
 | 1271 | 		 * assume 1 millivolt translates to 1% pc | 
 | 1272 | 		 */ | 
 | 1273 | 		pc = 1; | 
 | 1274 | 		pc_new = 0; | 
 | 1275 | 		slope = 1; | 
 | 1276 | 	} | 
 | 1277 |  | 
 | 1278 | 	delta_ocv_uv = div_s64((soc - soc_est) * (s64)slope * 1000, | 
 | 1279 | 							n * (pc - pc_new)); | 
 | 1280 |  | 
 | 1281 | 	if (abs(delta_ocv_uv) > delta_ocv_uv_limit) { | 
 | 1282 | 		pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv, | 
 | 1283 | 				delta_ocv_uv_limit); | 
 | 1284 |  | 
 | 1285 | 		if (delta_ocv_uv > 0) | 
 | 1286 | 			delta_ocv_uv = delta_ocv_uv_limit; | 
 | 1287 | 		else | 
 | 1288 | 			delta_ocv_uv = -1 * delta_ocv_uv_limit; | 
 | 1289 | 		pr_debug("new delta ocv = %d\n", delta_ocv_uv); | 
 | 1290 | 	} | 
 | 1291 |  | 
 | 1292 | 	chip->last_ocv_uv -= delta_ocv_uv; | 
 | 1293 |  | 
 | 1294 | 	if (chip->last_ocv_uv >= chip->max_voltage_uv) | 
 | 1295 | 		chip->last_ocv_uv = chip->max_voltage_uv; | 
 | 1296 |  | 
 | 1297 | 	/* calculate the soc based on this new ocv */ | 
 | 1298 | 	pc_new = calculate_pc(chip, chip->last_ocv_uv, batt_temp); | 
 | 1299 | 	rc_new_uah = (params->fcc_uah * pc_new) / 100; | 
 | 1300 | 	soc_new = (rc_new_uah - params->cc_uah - params->uuc_uah)*100 | 
 | 1301 | 					/ (params->fcc_uah - params->uuc_uah); | 
 | 1302 | 	soc_new = bound_soc(soc_new); | 
 | 1303 |  | 
 | 1304 | 	/* | 
 | 1305 | 	 * if soc_new is ZERO force it higher so that phone doesnt report soc=0 | 
 | 1306 | 	 * soc = 0 should happen only when soc_est == 0 | 
 | 1307 | 	 */ | 
 | 1308 | 	if (soc_new == 0 && soc_est != 0) | 
 | 1309 | 		soc_new = 1; | 
 | 1310 |  | 
 | 1311 | 	soc = soc_new; | 
 | 1312 |  | 
 | 1313 | out: | 
 | 1314 | 	pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, pc_new = %d, soc_new = %d, rbatt = %d, slope = %d\n", | 
 | 1315 | 		ibat_ua, vbat_uv, ocv_est_uv, pc_est, | 
 | 1316 | 		soc_est, n, delta_ocv_uv, chip->last_ocv_uv, | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 1317 | 		pc_new, soc_new, params->rbatt_mohm, slope); | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 1318 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1319 | 	return soc; | 
 | 1320 | } | 
 | 1321 |  | 
| Xiaozhe Shi | 2542c60 | 2012-11-28 10:08:07 -0800 | [diff] [blame] | 1322 | static int clamp_soc_based_on_voltage(struct qpnp_bms_chip *chip, int soc) | 
 | 1323 | { | 
 | 1324 | 	int rc, vbat_uv; | 
 | 1325 | 	struct qpnp_vadc_result result; | 
 | 1326 |  | 
 | 1327 | 	rc = qpnp_vadc_read(VBAT_SNS, &result); | 
 | 1328 | 	if (rc) { | 
 | 1329 | 		pr_err("error reading vbat_sns adc channel = %d, rc = %d\n", | 
 | 1330 | 						VBAT_SNS, rc); | 
 | 1331 | 		return rc; | 
 | 1332 | 	} | 
 | 1333 |  | 
 | 1334 | 	vbat_uv = (int)result.physical; | 
 | 1335 | 	if (soc == 0 && vbat_uv > chip->v_cutoff_uv) { | 
 | 1336 | 		pr_debug("clamping soc to 1, vbat (%d) > cutoff (%d)\n", | 
 | 1337 | 						vbat_uv, chip->v_cutoff_uv); | 
 | 1338 | 		return 1; | 
 | 1339 | 	} else if (soc > 0 && vbat_uv < chip->v_cutoff_uv) { | 
 | 1340 | 		pr_debug("forcing soc to 0, vbat (%d) < cutoff (%d)\n", | 
 | 1341 | 						vbat_uv, chip->v_cutoff_uv); | 
 | 1342 | 		return 0; | 
 | 1343 | 	} else { | 
 | 1344 | 		pr_debug("not clamping, using soc = %d, vbat = %d and cutoff = %d\n", | 
 | 1345 | 				soc, vbat_uv, chip->v_cutoff_uv); | 
 | 1346 | 		return soc; | 
 | 1347 | 	} | 
 | 1348 | } | 
 | 1349 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1350 | static int calculate_state_of_charge(struct qpnp_bms_chip *chip, | 
 | 1351 | 					struct raw_soc_params *raw, | 
 | 1352 | 					int batt_temp) | 
 | 1353 | { | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1354 | 	int soc, new_ocv_uv; | 
 | 1355 | 	int shutdown_soc, new_calculated_soc, remaining_usable_charge_uah; | 
 | 1356 | 	struct soc_params params; | 
 | 1357 |  | 
 | 1358 | 	calculate_soc_params(chip, raw, ¶ms, batt_temp); | 
 | 1359 | 	/* calculate remaining usable charge */ | 
 | 1360 | 	remaining_usable_charge_uah = params.ocv_charge_uah | 
 | 1361 | 					- params.cc_uah | 
 | 1362 | 					- params.uuc_uah; | 
 | 1363 |  | 
 | 1364 | 	pr_debug("RUC = %duAh\n", remaining_usable_charge_uah); | 
 | 1365 | 	if (params.fcc_uah - params.uuc_uah <= 0) { | 
| Xiaozhe Shi | cb386a2 | 2012-11-29 12:11:42 -0800 | [diff] [blame] | 1366 | 		pr_debug("FCC = %duAh, UUC = %duAh forcing soc = 0\n", | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1367 | 						params.fcc_uah, | 
 | 1368 | 						params.uuc_uah); | 
 | 1369 | 		soc = 0; | 
 | 1370 | 	} else { | 
 | 1371 | 		soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100), | 
 | 1372 | 					(params.fcc_uah | 
 | 1373 | 						- params.uuc_uah)); | 
 | 1374 | 	} | 
 | 1375 |  | 
 | 1376 | 	if (chip->first_time_calc_soc && soc < 0) { | 
 | 1377 | 		/* | 
 | 1378 | 		 * first time calcualtion and the pon ocv  is too low resulting | 
 | 1379 | 		 * in a bad soc. Adjust ocv to get 0 soc | 
 | 1380 | 		 */ | 
 | 1381 | 		pr_debug("soc is %d, adjusting pon ocv to make it 0\n", soc); | 
 | 1382 | 		find_ocv_for_soc(chip, ¶ms, batt_temp, 0, &new_ocv_uv); | 
 | 1383 | 		chip->last_ocv_uv = new_ocv_uv; | 
 | 1384 |  | 
 | 1385 | 		remaining_usable_charge_uah = params.ocv_charge_uah | 
 | 1386 | 					- params.cc_uah | 
 | 1387 | 					- params.uuc_uah; | 
 | 1388 |  | 
 | 1389 | 		soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100), | 
 | 1390 | 					(params.fcc_uah | 
 | 1391 | 						- params.uuc_uah)); | 
 | 1392 | 		pr_debug("DONE for O soc is %d, pon ocv adjusted to %duV\n", | 
 | 1393 | 				soc, chip->last_ocv_uv); | 
 | 1394 | 	} | 
 | 1395 |  | 
 | 1396 | 	if (soc > 100) | 
 | 1397 | 		soc = 100; | 
 | 1398 |  | 
 | 1399 | 	if (soc < 0) { | 
| Xiaozhe Shi | cb386a2 | 2012-11-29 12:11:42 -0800 | [diff] [blame] | 1400 | 		pr_debug("bad rem_usb_chg = %d rem_chg %d, cc_uah %d, unusb_chg %d\n", | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1401 | 				remaining_usable_charge_uah, | 
 | 1402 | 				params.ocv_charge_uah, | 
 | 1403 | 				params.cc_uah, params.uuc_uah); | 
 | 1404 |  | 
| Xiaozhe Shi | cb386a2 | 2012-11-29 12:11:42 -0800 | [diff] [blame] | 1405 | 		pr_debug("for bad rem_usb_chg last_ocv_uv = %d batt_temp = %d fcc = %d soc =%d\n", | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1406 | 				chip->last_ocv_uv, batt_temp, | 
 | 1407 | 				params.fcc_uah, soc); | 
 | 1408 | 		soc = 0; | 
 | 1409 | 	} | 
 | 1410 |  | 
 | 1411 | 	mutex_lock(&chip->soc_invalidation_mutex); | 
 | 1412 | 	shutdown_soc = chip->shutdown_soc; | 
 | 1413 |  | 
 | 1414 | 	if (chip->first_time_calc_soc && soc != shutdown_soc | 
 | 1415 | 			&& is_shutdown_soc_within_limits(chip, soc)) { | 
 | 1416 | 		/* | 
 | 1417 | 		 * soc for the first time - use shutdown soc | 
 | 1418 | 		 * to adjust pon ocv since it is a small percent away from | 
 | 1419 | 		 * the real soc | 
 | 1420 | 		 */ | 
 | 1421 | 		pr_debug("soc = %d before forcing shutdown_soc = %d\n", | 
 | 1422 | 							soc, shutdown_soc); | 
 | 1423 | 		find_ocv_for_soc(chip, ¶ms, batt_temp, | 
 | 1424 | 					shutdown_soc, &new_ocv_uv); | 
 | 1425 | 		chip->pon_ocv_uv = chip->last_ocv_uv; | 
 | 1426 | 		chip->last_ocv_uv = new_ocv_uv; | 
 | 1427 |  | 
 | 1428 | 		remaining_usable_charge_uah = params.ocv_charge_uah | 
 | 1429 | 					- params.cc_uah | 
 | 1430 | 					- params.uuc_uah; | 
 | 1431 |  | 
 | 1432 | 		soc = DIV_ROUND_CLOSEST((remaining_usable_charge_uah * 100), | 
 | 1433 | 					(params.fcc_uah | 
 | 1434 | 						- params.uuc_uah)); | 
 | 1435 |  | 
 | 1436 | 		pr_debug("DONE for shutdown_soc = %d soc is %d, adjusted ocv to %duV\n", | 
 | 1437 | 				shutdown_soc, soc, chip->last_ocv_uv); | 
 | 1438 | 	} | 
 | 1439 | 	mutex_unlock(&chip->soc_invalidation_mutex); | 
 | 1440 |  | 
 | 1441 | 	pr_debug("SOC before adjustment = %d\n", soc); | 
 | 1442 | 	new_calculated_soc = adjust_soc(chip, ¶ms, soc, batt_temp); | 
 | 1443 |  | 
| Xiaozhe Shi | 2542c60 | 2012-11-28 10:08:07 -0800 | [diff] [blame] | 1444 | 	/* clamp soc due to BMS HW inaccuracies in pm8941v2.0 */ | 
 | 1445 | 	if (chip->revision1 == 0 && chip->revision2 == 0) | 
 | 1446 | 		new_calculated_soc = clamp_soc_based_on_voltage(chip, | 
 | 1447 | 						new_calculated_soc); | 
 | 1448 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1449 | 	if (new_calculated_soc != chip->calculated_soc | 
 | 1450 | 			&& chip->bms_psy.name != NULL) { | 
 | 1451 | 		power_supply_changed(&chip->bms_psy); | 
 | 1452 | 		pr_debug("power supply changed\n"); | 
 | 1453 | 	} | 
 | 1454 |  | 
 | 1455 | 	chip->calculated_soc = new_calculated_soc; | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1456 | 	pr_debug("CC based calculated SOC = %d\n", chip->calculated_soc); | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1457 | 	chip->first_time_calc_soc = 0; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1458 | 	return chip->calculated_soc; | 
 | 1459 | } | 
 | 1460 |  | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1461 | static int read_vbat(struct qpnp_bms_chip *chip) | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 1462 | { | 
 | 1463 | 	int rc; | 
 | 1464 | 	struct qpnp_vadc_result result; | 
 | 1465 |  | 
 | 1466 | 	rc = qpnp_vadc_read(VBAT_SNS, &result); | 
 | 1467 | 	if (rc) { | 
 | 1468 | 		pr_err("error reading vadc VBAT_SNS = %d, rc = %d\n", | 
 | 1469 | 					VBAT_SNS, rc); | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1470 | 		return rc; | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 1471 | 	} | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1472 | 	pr_debug("read %duv from vadc\n", (int)result.physical); | 
 | 1473 | 	return (int)result.physical; | 
 | 1474 | } | 
 | 1475 |  | 
 | 1476 | static int calculate_soc_from_voltage(struct qpnp_bms_chip *chip) | 
 | 1477 | { | 
 | 1478 | 	int voltage_range_uv, voltage_remaining_uv, voltage_based_soc; | 
 | 1479 | 	int vbat_uv; | 
 | 1480 |  | 
 | 1481 | 	vbat_uv = read_vbat(chip); | 
 | 1482 |  | 
 | 1483 | 	voltage_range_uv = chip->max_voltage_uv - chip->v_cutoff_uv; | 
 | 1484 | 	voltage_remaining_uv = vbat_uv - chip->v_cutoff_uv; | 
 | 1485 | 	voltage_based_soc = voltage_remaining_uv * 100 / voltage_range_uv; | 
 | 1486 |  | 
 | 1487 | 	voltage_based_soc = clamp(voltage_based_soc, 0, 100); | 
 | 1488 |  | 
 | 1489 | 	if (chip->prev_voltage_based_soc != voltage_based_soc | 
 | 1490 | 				&& chip->bms_psy.name != NULL) { | 
 | 1491 | 		power_supply_changed(&chip->bms_psy); | 
 | 1492 | 		pr_debug("power supply changed\n"); | 
 | 1493 | 	} | 
 | 1494 | 	chip->prev_voltage_based_soc = voltage_based_soc; | 
 | 1495 |  | 
 | 1496 | 	pr_debug("vbat used = %duv\n", vbat_uv); | 
 | 1497 | 	pr_debug("Calculated voltage based soc = %d\n", voltage_based_soc); | 
 | 1498 | 	return voltage_based_soc; | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 1499 | } | 
 | 1500 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1501 | static void calculate_soc_work(struct work_struct *work) | 
 | 1502 | { | 
 | 1503 | 	struct qpnp_bms_chip *chip = container_of(work, | 
 | 1504 | 				struct qpnp_bms_chip, | 
 | 1505 | 				calculate_soc_delayed_work.work); | 
 | 1506 | 	int batt_temp, rc, soc; | 
 | 1507 | 	struct qpnp_vadc_result result; | 
 | 1508 | 	struct raw_soc_params raw; | 
 | 1509 |  | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1510 | 	if (chip->use_voltage_soc) { | 
 | 1511 | 		soc = calculate_soc_from_voltage(chip); | 
 | 1512 | 	} else { | 
 | 1513 | 		rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result); | 
 | 1514 | 		if (rc) { | 
 | 1515 | 			pr_err("error reading vadc LR_MUX1_BATT_THERM = %d, rc = %d\n", | 
 | 1516 | 						LR_MUX1_BATT_THERM, rc); | 
 | 1517 | 			return; | 
 | 1518 | 		} | 
 | 1519 | 		pr_debug("batt_temp phy = %lld meas = 0x%llx\n", | 
 | 1520 | 						result.physical, | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1521 | 						result.measurement); | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1522 | 		batt_temp = (int)result.physical; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1523 |  | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1524 | 		mutex_lock(&chip->last_ocv_uv_mutex); | 
 | 1525 | 		read_soc_params_raw(chip, &raw); | 
 | 1526 | 		soc = calculate_state_of_charge(chip, &raw, batt_temp); | 
 | 1527 | 		mutex_unlock(&chip->last_ocv_uv_mutex); | 
 | 1528 | 	} | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1529 |  | 
 | 1530 | 	if (soc < chip->low_soc_calc_threshold) | 
 | 1531 | 		schedule_delayed_work(&chip->calculate_soc_delayed_work, | 
 | 1532 | 			round_jiffies_relative(msecs_to_jiffies | 
 | 1533 | 			(chip->low_soc_calculate_soc_ms))); | 
 | 1534 | 	else | 
 | 1535 | 		schedule_delayed_work(&chip->calculate_soc_delayed_work, | 
 | 1536 | 			round_jiffies_relative(msecs_to_jiffies | 
 | 1537 | 			(chip->calculate_soc_ms))); | 
 | 1538 | } | 
 | 1539 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1540 | static void backup_soc_and_iavg(struct qpnp_bms_chip *chip, int batt_temp, | 
 | 1541 | 				int soc) | 
 | 1542 | { | 
 | 1543 | 	u8 temp; | 
 | 1544 | 	int rc; | 
 | 1545 | 	int iavg_ma = chip->prev_uuc_iavg_ma; | 
 | 1546 |  | 
 | 1547 | 	if (iavg_ma > IAVG_START) | 
 | 1548 | 		temp = (iavg_ma - IAVG_START) / IAVG_STEP_SIZE_MA; | 
 | 1549 | 	else | 
 | 1550 | 		temp = 0; | 
 | 1551 |  | 
 | 1552 | 	rc = qpnp_write_wrapper(chip, &temp, | 
 | 1553 | 			chip->base + IAVG_STORAGE_REG, 1); | 
 | 1554 |  | 
 | 1555 | 	if (soc == 0) | 
 | 1556 | 		temp = SOC_ZERO; | 
 | 1557 | 	else | 
 | 1558 | 		temp = soc; | 
 | 1559 |  | 
 | 1560 | 	/* don't store soc if temperature is below 5degC */ | 
 | 1561 | 	if (batt_temp > IGNORE_SOC_TEMP_DECIDEG) | 
 | 1562 | 		rc = qpnp_write_wrapper(chip, &temp, | 
 | 1563 | 				chip->base + SOC_STORAGE_REG, 1); | 
 | 1564 | } | 
 | 1565 |  | 
 | 1566 | #define SOC_CATCHUP_SEC_MAX		600 | 
 | 1567 | #define SOC_CATCHUP_SEC_PER_PERCENT	60 | 
 | 1568 | #define MAX_CATCHUP_SOC	(SOC_CATCHUP_SEC_MAX/SOC_CATCHUP_SEC_PER_PERCENT) | 
 | 1569 | static int scale_soc_while_chg(struct qpnp_bms_chip *chip, | 
 | 1570 | 				int delta_time_us, int new_soc, int prev_soc) | 
 | 1571 | { | 
 | 1572 | 	int chg_time_sec; | 
 | 1573 | 	int catch_up_sec; | 
 | 1574 | 	int scaled_soc; | 
 | 1575 | 	int numerator; | 
 | 1576 |  | 
 | 1577 | 	/* | 
 | 1578 | 	 * The device must be charging for reporting a higher soc, if | 
 | 1579 | 	 * not ignore this soc and continue reporting the prev_soc. | 
 | 1580 | 	 * Also don't report a high value immediately slowly scale the | 
 | 1581 | 	 * value from prev_soc to the new soc based on a charge time | 
 | 1582 | 	 * weighted average | 
 | 1583 | 	 */ | 
 | 1584 |  | 
 | 1585 | 	/* if not charging, return last soc */ | 
 | 1586 | 	if (chip->start_percent == -EINVAL) | 
 | 1587 | 		return prev_soc; | 
 | 1588 |  | 
 | 1589 | 	chg_time_sec = DIV_ROUND_UP(chip->charge_time_us, USEC_PER_SEC); | 
 | 1590 | 	catch_up_sec = DIV_ROUND_UP(chip->catch_up_time_us, USEC_PER_SEC); | 
 | 1591 | 	pr_debug("cts= %d catch_up_sec = %d\n", chg_time_sec, catch_up_sec); | 
 | 1592 |  | 
 | 1593 | 	/* | 
 | 1594 | 	 * if charging for more than catch_up time, simply return | 
 | 1595 | 	 * new soc | 
 | 1596 | 	 */ | 
 | 1597 | 	if (chg_time_sec > catch_up_sec) | 
 | 1598 | 		return new_soc; | 
 | 1599 |  | 
 | 1600 | 	numerator = (catch_up_sec - chg_time_sec) * prev_soc | 
 | 1601 | 			+ chg_time_sec * new_soc; | 
 | 1602 | 	scaled_soc = numerator / catch_up_sec; | 
 | 1603 |  | 
 | 1604 | 	pr_debug("cts = %d new_soc = %d prev_soc = %d scaled_soc = %d\n", | 
 | 1605 | 			chg_time_sec, new_soc, prev_soc, scaled_soc); | 
 | 1606 |  | 
 | 1607 | 	return scaled_soc; | 
 | 1608 | } | 
 | 1609 |  | 
 | 1610 | /* | 
 | 1611 |  * bms_fake_battery is set in setups where a battery emulator is used instead | 
 | 1612 |  * of a real battery. This makes the bms driver report a different/fake value | 
 | 1613 |  * regardless of the calculated state of charge. | 
 | 1614 |  */ | 
 | 1615 | static int bms_fake_battery = -EINVAL; | 
 | 1616 | module_param(bms_fake_battery, int, 0644); | 
 | 1617 |  | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1618 | static int report_voltage_based_soc(struct qpnp_bms_chip *chip) | 
 | 1619 | { | 
 | 1620 | 	pr_debug("Reported voltage based soc = %d\n", | 
 | 1621 | 			chip->prev_voltage_based_soc); | 
 | 1622 | 	return chip->prev_voltage_based_soc; | 
 | 1623 | } | 
 | 1624 |  | 
 | 1625 | static int report_cc_based_soc(struct qpnp_bms_chip *chip) | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1626 | { | 
 | 1627 | 	int soc; | 
 | 1628 | 	int delta_time_us; | 
 | 1629 | 	struct timespec now; | 
 | 1630 | 	struct qpnp_vadc_result result; | 
 | 1631 | 	int batt_temp; | 
 | 1632 | 	int rc; | 
 | 1633 |  | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1634 | 	soc = chip->calculated_soc; | 
 | 1635 |  | 
 | 1636 | 	rc = qpnp_vadc_read(LR_MUX1_BATT_THERM, &result); | 
 | 1637 |  | 
 | 1638 | 	if (rc) { | 
 | 1639 | 		pr_err("error reading adc channel = %d, rc = %d\n", | 
 | 1640 | 					LR_MUX1_BATT_THERM, rc); | 
 | 1641 | 		return rc; | 
 | 1642 | 	} | 
 | 1643 | 	pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical, | 
 | 1644 | 						result.measurement); | 
 | 1645 | 	batt_temp = (int)result.physical; | 
 | 1646 |  | 
 | 1647 | 	do_posix_clock_monotonic_gettime(&now); | 
 | 1648 | 	if (chip->t_soc_queried.tv_sec != 0) { | 
 | 1649 | 		delta_time_us | 
 | 1650 | 		= (now.tv_sec - chip->t_soc_queried.tv_sec) * USEC_PER_SEC | 
 | 1651 | 			+ (now.tv_nsec - chip->t_soc_queried.tv_nsec) / 1000; | 
 | 1652 | 	} else { | 
 | 1653 | 		/* calculation for the first time */ | 
 | 1654 | 		delta_time_us = 0; | 
 | 1655 | 	} | 
 | 1656 |  | 
 | 1657 | 	/* | 
 | 1658 | 	 * account for charge time - limit it to SOC_CATCHUP_SEC to | 
 | 1659 | 	 * avoid overflows when charging continues for extended periods | 
 | 1660 | 	 */ | 
 | 1661 | 	if (chip->start_percent != -EINVAL) { | 
 | 1662 | 		if (chip->charge_time_us == 0) { | 
 | 1663 | 			/* | 
 | 1664 | 			 * calculating soc for the first time | 
 | 1665 | 			 * after start of chg. Initialize catchup time | 
 | 1666 | 			 */ | 
 | 1667 | 			if (abs(soc - chip->last_soc) < MAX_CATCHUP_SOC) | 
 | 1668 | 				chip->catch_up_time_us = | 
 | 1669 | 				(soc - chip->last_soc) | 
 | 1670 | 					* SOC_CATCHUP_SEC_PER_PERCENT | 
 | 1671 | 					* USEC_PER_SEC; | 
 | 1672 | 			else | 
 | 1673 | 				chip->catch_up_time_us = | 
 | 1674 | 				SOC_CATCHUP_SEC_MAX * USEC_PER_SEC; | 
 | 1675 |  | 
 | 1676 | 			if (chip->catch_up_time_us < 0) | 
 | 1677 | 				chip->catch_up_time_us = 0; | 
 | 1678 | 		} | 
 | 1679 |  | 
 | 1680 | 		/* add charge time */ | 
 | 1681 | 		if (chip->charge_time_us < SOC_CATCHUP_SEC_MAX * USEC_PER_SEC) | 
 | 1682 | 			chip->charge_time_us += delta_time_us; | 
 | 1683 |  | 
 | 1684 | 		/* end catchup if calculated soc and last soc are same */ | 
 | 1685 | 		if (chip->last_soc == soc) | 
 | 1686 | 			chip->catch_up_time_us = 0; | 
 | 1687 | 	} | 
 | 1688 |  | 
 | 1689 | 	/* last_soc < soc  ... scale and catch up */ | 
| Xiaozhe Shi | a81851f | 2012-11-01 16:36:58 -0700 | [diff] [blame] | 1690 | 	if (chip->last_soc != -EINVAL && chip->last_soc < soc | 
 | 1691 | 			&& soc != 100 && chip->catch_up_time_us != 0) | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 1692 | 		soc = scale_soc_while_chg(chip, delta_time_us, | 
 | 1693 | 						soc, chip->last_soc); | 
 | 1694 |  | 
 | 1695 | 	pr_debug("last_soc = %d, calculated_soc = %d, soc = %d\n", | 
 | 1696 | 			chip->last_soc, chip->calculated_soc, soc); | 
 | 1697 | 	chip->last_soc = soc; | 
 | 1698 | 	backup_soc_and_iavg(chip, batt_temp, chip->last_soc); | 
 | 1699 | 	pr_debug("Reported SOC = %d\n", chip->last_soc); | 
 | 1700 | 	chip->t_soc_queried = now; | 
 | 1701 |  | 
 | 1702 | 	return chip->last_soc; | 
 | 1703 | } | 
 | 1704 |  | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1705 | static int report_state_of_charge(struct qpnp_bms_chip *chip) | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 1706 | { | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1707 | 	if (bms_fake_battery != -EINVAL) { | 
 | 1708 | 		pr_debug("Returning Fake SOC = %d%%\n", bms_fake_battery); | 
 | 1709 | 		return bms_fake_battery; | 
 | 1710 | 	} else if (chip->use_voltage_soc) | 
 | 1711 | 		return report_voltage_based_soc(chip); | 
 | 1712 | 	else | 
 | 1713 | 		return report_cc_based_soc(chip); | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 1714 | } | 
 | 1715 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1716 | /* Returns capacity as a SoC percentage between 0 and 100 */ | 
 | 1717 | static int get_prop_bms_capacity(struct qpnp_bms_chip *chip) | 
 | 1718 | { | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1719 | 	return report_state_of_charge(chip); | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1720 | } | 
 | 1721 |  | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 1722 | /* Returns estimated max current that the battery can supply in uA */ | 
 | 1723 | static int get_prop_bms_current_max(struct qpnp_bms_chip *chip) | 
 | 1724 | { | 
 | 1725 | 	return chip->ibat_max_ua; | 
 | 1726 | } | 
 | 1727 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1728 | /* Returns instantaneous current in uA */ | 
 | 1729 | static int get_prop_bms_current_now(struct qpnp_bms_chip *chip) | 
 | 1730 | { | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1731 | 	int rc, result_ua; | 
 | 1732 |  | 
 | 1733 | 	rc = get_battery_current(chip, &result_ua); | 
 | 1734 | 	if (rc) { | 
 | 1735 | 		pr_err("failed to get current: %d\n", rc); | 
 | 1736 | 		return rc; | 
 | 1737 | 	} | 
 | 1738 | 	return result_ua; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1739 | } | 
 | 1740 |  | 
 | 1741 | /* Returns full charge design in uAh */ | 
 | 1742 | static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip) | 
 | 1743 | { | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1744 | 	return chip->fcc; | 
 | 1745 | } | 
 | 1746 |  | 
 | 1747 | static bool get_prop_bms_online(struct qpnp_bms_chip *chip) | 
 | 1748 | { | 
 | 1749 | 	return chip->online; | 
 | 1750 | } | 
 | 1751 |  | 
 | 1752 | static int get_prop_bms_status(struct qpnp_bms_chip *chip) | 
 | 1753 | { | 
 | 1754 | 	return chip->charger_status; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1755 | } | 
 | 1756 |  | 
 | 1757 | static void set_prop_bms_online(struct qpnp_bms_chip *chip, bool online) | 
 | 1758 | { | 
 | 1759 | 	chip->online = online; | 
 | 1760 | } | 
 | 1761 |  | 
 | 1762 | static void set_prop_bms_status(struct qpnp_bms_chip *chip, int status) | 
 | 1763 | { | 
 | 1764 | 	chip->charger_status = status; | 
 | 1765 | } | 
 | 1766 |  | 
 | 1767 | static void qpnp_bms_external_power_changed(struct power_supply *psy) | 
 | 1768 | { | 
 | 1769 | } | 
 | 1770 |  | 
 | 1771 | static int qpnp_bms_power_get_property(struct power_supply *psy, | 
 | 1772 | 					enum power_supply_property psp, | 
 | 1773 | 					union power_supply_propval *val) | 
 | 1774 | { | 
 | 1775 | 	struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip, | 
 | 1776 | 								bms_psy); | 
 | 1777 |  | 
 | 1778 | 	switch (psp) { | 
 | 1779 | 	case POWER_SUPPLY_PROP_CAPACITY: | 
 | 1780 | 		val->intval = get_prop_bms_capacity(chip); | 
 | 1781 | 		break; | 
 | 1782 | 	case POWER_SUPPLY_PROP_CURRENT_NOW: | 
 | 1783 | 		val->intval = get_prop_bms_current_now(chip); | 
 | 1784 | 		break; | 
| Xiaozhe Shi | 904f1f7 | 2012-12-04 12:47:21 -0800 | [diff] [blame] | 1785 | 	case POWER_SUPPLY_PROP_CURRENT_MAX: | 
 | 1786 | 		val->intval = get_prop_bms_current_max(chip); | 
 | 1787 | 		break; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1788 | 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | 
 | 1789 | 		val->intval = get_prop_bms_charge_full_design(chip); | 
 | 1790 | 		break; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1791 | 	case POWER_SUPPLY_PROP_STATUS: | 
 | 1792 | 		val->intval = get_prop_bms_status(chip); | 
 | 1793 | 		break; | 
 | 1794 | 	case POWER_SUPPLY_PROP_ONLINE: | 
 | 1795 | 		val->intval = get_prop_bms_online(chip); | 
 | 1796 | 		break; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1797 | 	default: | 
 | 1798 | 		return -EINVAL; | 
 | 1799 | 	} | 
 | 1800 | 	return 0; | 
 | 1801 | } | 
 | 1802 |  | 
 | 1803 | static int qpnp_bms_power_set_property(struct power_supply *psy, | 
 | 1804 | 					enum power_supply_property psp, | 
 | 1805 | 					const union power_supply_propval *val) | 
 | 1806 | { | 
 | 1807 | 	struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip, | 
 | 1808 | 								bms_psy); | 
 | 1809 |  | 
 | 1810 | 	switch (psp) { | 
 | 1811 | 	case POWER_SUPPLY_PROP_ONLINE: | 
 | 1812 | 		set_prop_bms_online(chip, val->intval); | 
 | 1813 | 		break; | 
 | 1814 | 	case POWER_SUPPLY_PROP_STATUS: | 
 | 1815 | 		set_prop_bms_status(chip, (bool)val->intval); | 
 | 1816 | 		break; | 
 | 1817 | 	default: | 
 | 1818 | 		return -EINVAL; | 
 | 1819 | 	} | 
 | 1820 | 	return 0; | 
 | 1821 | } | 
 | 1822 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1823 | static void read_shutdown_soc_and_iavg(struct qpnp_bms_chip *chip) | 
 | 1824 | { | 
 | 1825 | 	int rc; | 
 | 1826 | 	u8 temp; | 
 | 1827 |  | 
 | 1828 | 	if (chip->ignore_shutdown_soc) { | 
 | 1829 | 		chip->shutdown_soc_invalid = 1; | 
 | 1830 | 		chip->shutdown_soc = 0; | 
 | 1831 | 		chip->shutdown_iavg_ma = 0; | 
 | 1832 | 	} else { | 
 | 1833 | 		rc = qpnp_read_wrapper(chip, &temp, | 
 | 1834 | 				chip->base + IAVG_STORAGE_REG, 1); | 
 | 1835 | 		if (rc) { | 
 | 1836 | 			pr_err("failed to read addr = %d %d assuming %d\n", | 
 | 1837 | 					chip->base + IAVG_STORAGE_REG, rc, | 
 | 1838 | 					IAVG_START); | 
 | 1839 | 			chip->shutdown_iavg_ma = IAVG_START; | 
 | 1840 | 		} else { | 
 | 1841 | 			if (temp == 0) { | 
 | 1842 | 				chip->shutdown_iavg_ma = IAVG_START; | 
 | 1843 | 			} else { | 
 | 1844 | 				chip->shutdown_iavg_ma = IAVG_START | 
 | 1845 | 					+ IAVG_STEP_SIZE_MA * (temp + 1); | 
 | 1846 | 			} | 
 | 1847 | 		} | 
 | 1848 |  | 
 | 1849 | 		rc = qpnp_read_wrapper(chip, &temp, | 
 | 1850 | 				chip->base + SOC_STORAGE_REG, 1); | 
 | 1851 | 		if (rc) { | 
 | 1852 | 			pr_err("failed to read addr = %d %d\n", | 
 | 1853 | 					chip->base + SOC_STORAGE_REG, rc); | 
 | 1854 | 		} else { | 
 | 1855 | 			chip->shutdown_soc = temp; | 
 | 1856 |  | 
 | 1857 | 			if (chip->shutdown_soc == 0) { | 
 | 1858 | 				pr_debug("No shutdown soc available\n"); | 
 | 1859 | 				chip->shutdown_soc_invalid = 1; | 
 | 1860 | 				chip->shutdown_iavg_ma = 0; | 
 | 1861 | 			} else if (chip->shutdown_soc == SOC_ZERO) { | 
 | 1862 | 				chip->shutdown_soc = 0; | 
 | 1863 | 			} | 
 | 1864 | 		} | 
 | 1865 | 	} | 
 | 1866 |  | 
 | 1867 | 	pr_debug("shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d\n", | 
 | 1868 | 			chip->shutdown_soc, | 
 | 1869 | 			chip->shutdown_iavg_ma, | 
 | 1870 | 			chip->shutdown_soc_invalid); | 
 | 1871 | } | 
 | 1872 |  | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 1873 | #define PALLADIUM_ID_MIN	0x7F40 | 
 | 1874 | #define PALLADIUM_ID_MAX	0x7F5A | 
 | 1875 | #define DESAY_5200_ID_MIN	0x7F7F | 
 | 1876 | #define DESAY_5200_ID_MAX	0x802F | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1877 | static int32_t read_battery_id(struct qpnp_bms_chip *chip) | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 1878 | { | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1879 | 	int rc; | 
 | 1880 | 	struct qpnp_vadc_result result; | 
 | 1881 |  | 
 | 1882 | 	rc = qpnp_vadc_read(LR_MUX2_BAT_ID, &result); | 
 | 1883 | 	if (rc) { | 
 | 1884 | 		pr_err("error reading batt id channel = %d, rc = %d\n", | 
 | 1885 | 					LR_MUX2_BAT_ID, rc); | 
 | 1886 | 		return rc; | 
 | 1887 | 	} | 
 | 1888 | 	pr_debug("batt_id phy = %lld meas = 0x%llx\n", result.physical, | 
 | 1889 | 						result.measurement); | 
 | 1890 | 	pr_debug("raw_code = 0x%x\n", result.adc_code); | 
 | 1891 | 	return result.adc_code; | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 1892 | } | 
 | 1893 |  | 
 | 1894 | static int set_battery_data(struct qpnp_bms_chip *chip) | 
 | 1895 | { | 
 | 1896 | 	int64_t battery_id; | 
 | 1897 |  | 
 | 1898 | 	if (chip->batt_type == BATT_DESAY) | 
 | 1899 | 		goto desay; | 
 | 1900 | 	else if (chip->batt_type == BATT_PALLADIUM) | 
 | 1901 | 		goto palladium; | 
 | 1902 |  | 
 | 1903 | 	battery_id = read_battery_id(chip); | 
 | 1904 | 	if (battery_id < 0) { | 
 | 1905 | 		pr_err("cannot read battery id err = %lld\n", battery_id); | 
 | 1906 | 		return battery_id; | 
 | 1907 | 	} | 
 | 1908 |  | 
 | 1909 | 	if (is_between(PALLADIUM_ID_MIN, PALLADIUM_ID_MAX, battery_id)) { | 
 | 1910 | 		goto palladium; | 
 | 1911 | 	} else if (is_between(DESAY_5200_ID_MIN, DESAY_5200_ID_MAX, | 
 | 1912 | 				battery_id)) { | 
 | 1913 | 		goto desay; | 
 | 1914 | 	} else { | 
 | 1915 | 		pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n", | 
 | 1916 | 				battery_id); | 
 | 1917 | 		goto palladium; | 
 | 1918 | 	} | 
 | 1919 |  | 
 | 1920 | palladium: | 
 | 1921 | 		chip->fcc = palladium_1500_data.fcc; | 
 | 1922 | 		chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut; | 
 | 1923 | 		chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut; | 
 | 1924 | 		chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut; | 
 | 1925 | 		chip->pc_sf_lut = palladium_1500_data.pc_sf_lut; | 
 | 1926 | 		chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut; | 
 | 1927 | 		chip->default_rbatt_mohm | 
 | 1928 | 				= palladium_1500_data.default_rbatt_mohm; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1929 | 		goto check_lut; | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 1930 | desay: | 
 | 1931 | 		chip->fcc = desay_5200_data.fcc; | 
 | 1932 | 		chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut; | 
 | 1933 | 		chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut; | 
 | 1934 | 		chip->pc_sf_lut = desay_5200_data.pc_sf_lut; | 
 | 1935 | 		chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut; | 
 | 1936 | 		chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1937 | 		goto check_lut; | 
 | 1938 | check_lut: | 
 | 1939 | 		if (chip->pc_temp_ocv_lut == NULL) { | 
 | 1940 | 			pr_err("temp ocv lut table is NULL\n"); | 
 | 1941 | 			return -EINVAL; | 
 | 1942 | 		} | 
| Xiaozhe Shi | 73a6569 | 2012-09-18 17:51:57 -0700 | [diff] [blame] | 1943 | 		return 0; | 
 | 1944 | } | 
 | 1945 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1946 | #define SPMI_PROP_READ(chip_prop, qpnp_spmi_property, retval)		\ | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1947 | do {									\ | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1948 | 	retval = of_property_read_u32(chip->spmi->dev.of_node,		\ | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1949 | 				"qcom,bms-" qpnp_spmi_property,		\ | 
 | 1950 | 					&chip->chip_prop);		\ | 
 | 1951 | 	if (retval) {							\ | 
 | 1952 | 		pr_err("Error reading " #qpnp_spmi_property		\ | 
 | 1953 | 						" property %d\n", rc);	\ | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1954 | 		return -EINVAL;						\ | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 1955 | 	}								\ | 
 | 1956 | } while (0) | 
 | 1957 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1958 | static inline int bms_read_properties(struct qpnp_bms_chip *chip) | 
 | 1959 | { | 
 | 1960 | 	int rc; | 
 | 1961 |  | 
 | 1962 | 	SPMI_PROP_READ(r_sense_mohm, "r-sense-mohm", rc); | 
 | 1963 | 	SPMI_PROP_READ(v_cutoff_uv, "v-cutoff-uv", rc); | 
 | 1964 | 	SPMI_PROP_READ(max_voltage_uv, "max-voltage-uv", rc); | 
 | 1965 | 	SPMI_PROP_READ(r_conn_mohm, "r-conn-mohm", rc); | 
 | 1966 | 	SPMI_PROP_READ(chg_term_ua, "chg-term-ua", rc); | 
 | 1967 | 	SPMI_PROP_READ(shutdown_soc_valid_limit, | 
 | 1968 | 			"shutdown-soc-valid-limit", rc); | 
 | 1969 | 	SPMI_PROP_READ(adjust_soc_high_threshold, | 
 | 1970 | 			"adjust-soc-high-threshold", rc); | 
 | 1971 | 	SPMI_PROP_READ(adjust_soc_low_threshold, | 
 | 1972 | 			"adjust-soc-low-threshold", rc); | 
 | 1973 | 	SPMI_PROP_READ(batt_type, "batt-type", rc); | 
 | 1974 | 	SPMI_PROP_READ(low_soc_calc_threshold, | 
 | 1975 | 			"low-soc-calculate-soc-threshold", rc); | 
 | 1976 | 	SPMI_PROP_READ(low_soc_calculate_soc_ms, | 
 | 1977 | 			"low-soc-calculate-soc-ms", rc); | 
 | 1978 | 	SPMI_PROP_READ(calculate_soc_ms, "calculate-soc-ms", rc); | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 1979 | 	chip->use_external_rsense = of_property_read_bool( | 
 | 1980 | 			chip->spmi->dev.of_node, | 
 | 1981 | 			"qcom,bms-use-external-rsense"); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1982 | 	chip->ignore_shutdown_soc = of_property_read_bool( | 
 | 1983 | 			chip->spmi->dev.of_node, | 
 | 1984 | 			"qcom,bms-ignore-shutdown-soc"); | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 1985 | 	chip->use_voltage_soc = of_property_read_bool(chip->spmi->dev.of_node, | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 1986 | 			"qcom,bms-use-voltage-soc"); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 1987 |  | 
 | 1988 | 	if (chip->adjust_soc_low_threshold >= 45) | 
 | 1989 | 		chip->adjust_soc_low_threshold = 45; | 
 | 1990 |  | 
 | 1991 | 	pr_debug("dts data: r_sense_mohm:%d, v_cutoff_uv:%d, max_v:%d\n", | 
 | 1992 | 			chip->r_sense_mohm, chip->v_cutoff_uv, | 
 | 1993 | 			chip->max_voltage_uv); | 
 | 1994 | 	pr_debug("r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d\n", | 
 | 1995 | 			chip->r_conn_mohm, chip->shutdown_soc_valid_limit, | 
 | 1996 | 			chip->adjust_soc_low_threshold); | 
 | 1997 | 	pr_debug("adjust_soc_high:%d, chg_term_ua:%d, batt_type:%d\n", | 
 | 1998 | 			chip->adjust_soc_high_threshold, chip->chg_term_ua, | 
 | 1999 | 			chip->batt_type); | 
| Xiaozhe Shi | 781b0a2 | 2012-11-05 17:18:27 -0800 | [diff] [blame] | 2000 | 	pr_debug("ignore_shutdown_soc:%d, use_voltage_soc:%d\n", | 
| Xiaozhe Shi | 79d6c1d | 2012-11-26 13:19:50 -0800 | [diff] [blame] | 2001 | 			chip->ignore_shutdown_soc, chip->use_voltage_soc); | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 2002 | 	pr_debug("use external rsense: %d\n", chip->use_external_rsense); | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2003 | 	return 0; | 
 | 2004 | } | 
 | 2005 |  | 
 | 2006 | static inline void bms_initialize_constants(struct qpnp_bms_chip *chip) | 
 | 2007 | { | 
 | 2008 | 	chip->start_percent = -EINVAL; | 
 | 2009 | 	chip->end_percent = -EINVAL; | 
 | 2010 | 	chip->prev_pc_unusable = -EINVAL; | 
 | 2011 | 	chip->soc_at_cv = -EINVAL; | 
 | 2012 | 	chip->calculated_soc = -EINVAL; | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 2013 | 	chip->last_soc = -EINVAL; | 
| Xiaozhe Shi | 7edde5d | 2012-09-26 11:23:09 -0700 | [diff] [blame] | 2014 | 	chip->last_soc_est = -EINVAL; | 
| Xiaozhe Shi | e118c69 | 2012-09-24 15:17:43 -0700 | [diff] [blame] | 2015 | 	chip->first_time_calc_soc = 1; | 
 | 2016 | 	chip->first_time_calc_uuc = 1; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2017 | } | 
 | 2018 |  | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2019 | #define REG_OFFSET_PERP_TYPE			0x04 | 
 | 2020 | #define REG_OFFSET_PERP_SUBTYPE			0x05 | 
 | 2021 | #define BMS_BMS_TYPE				0xD | 
 | 2022 | #define BMS_BMS_SUBTYPE				0x1 | 
 | 2023 | #define BMS_IADC_TYPE				0x8 | 
 | 2024 | #define BMS_IADC_SUBTYPE			0x3 | 
 | 2025 |  | 
 | 2026 | static int register_spmi(struct qpnp_bms_chip *chip, struct spmi_device *spmi) | 
 | 2027 | { | 
 | 2028 | 	struct spmi_resource *spmi_resource; | 
 | 2029 | 	struct resource *resource; | 
 | 2030 | 	int rc; | 
 | 2031 | 	u8 type, subtype; | 
 | 2032 |  | 
 | 2033 | 	chip->dev = &(spmi->dev); | 
 | 2034 | 	chip->spmi = spmi; | 
 | 2035 |  | 
 | 2036 | 	spmi_for_each_container_dev(spmi_resource, spmi) { | 
 | 2037 | 		if (!spmi_resource) { | 
 | 2038 | 			pr_err("qpnp_bms: spmi resource absent\n"); | 
 | 2039 | 			return -ENXIO; | 
 | 2040 | 		} | 
 | 2041 |  | 
 | 2042 | 		resource = spmi_get_resource(spmi, spmi_resource, | 
 | 2043 | 						IORESOURCE_MEM, 0); | 
 | 2044 | 		if (!(resource && resource->start)) { | 
 | 2045 | 			pr_err("node %s IO resource absent!\n", | 
 | 2046 | 				spmi->dev.of_node->full_name); | 
 | 2047 | 			return -ENXIO; | 
 | 2048 | 		} | 
 | 2049 |  | 
 | 2050 | 		rc = qpnp_read_wrapper(chip, &type, | 
 | 2051 | 				resource->start + REG_OFFSET_PERP_TYPE, 1); | 
 | 2052 | 		if (rc) { | 
 | 2053 | 			pr_err("Peripheral type read failed rc=%d\n", rc); | 
 | 2054 | 			return rc; | 
 | 2055 | 		} | 
 | 2056 | 		rc = qpnp_read_wrapper(chip, &subtype, | 
 | 2057 | 				resource->start + REG_OFFSET_PERP_SUBTYPE, 1); | 
 | 2058 | 		if (rc) { | 
 | 2059 | 			pr_err("Peripheral subtype read failed rc=%d\n", rc); | 
 | 2060 | 			return rc; | 
 | 2061 | 		} | 
 | 2062 |  | 
 | 2063 | 		if (type == BMS_BMS_TYPE && subtype == BMS_BMS_SUBTYPE) { | 
 | 2064 | 			chip->base = resource->start; | 
 | 2065 | 		} else if (type == BMS_IADC_TYPE | 
 | 2066 | 					&& subtype == BMS_IADC_SUBTYPE) { | 
 | 2067 | 			chip->iadc_base = resource->start; | 
 | 2068 | 		} else { | 
 | 2069 | 			pr_err("Invalid peripheral start=0x%x type=0x%x, subtype=0x%x\n", | 
 | 2070 | 					resource->start, type, subtype); | 
 | 2071 | 		} | 
 | 2072 | 	} | 
 | 2073 |  | 
 | 2074 | 	if (chip->base == 0) { | 
 | 2075 | 		dev_err(&spmi->dev, "BMS peripheral was not registered\n"); | 
 | 2076 | 		return -EINVAL; | 
 | 2077 | 	} | 
 | 2078 | 	if (chip->iadc_base == 0) { | 
 | 2079 | 		dev_err(&spmi->dev, "BMS_IADC peripheral was not registered\n"); | 
 | 2080 | 		return -EINVAL; | 
 | 2081 | 	} | 
 | 2082 |  | 
 | 2083 | 	return 0; | 
 | 2084 | } | 
 | 2085 |  | 
 | 2086 | #define ADC_CH_SEL_MASK			0x7 | 
 | 2087 | static int read_iadc_channel_select(struct qpnp_bms_chip *chip) | 
 | 2088 | { | 
 | 2089 | 	u8 iadc_channel_select; | 
 | 2090 | 	int rc; | 
 | 2091 |  | 
 | 2092 | 	rc = qpnp_read_wrapper(chip, &iadc_channel_select, | 
 | 2093 | 			chip->iadc_base + IADC1_BMS_ADC_CH_SEL_CTL, 1); | 
 | 2094 | 	if (rc) { | 
 | 2095 | 		pr_err("Error reading bms_iadc channel register %d\n", rc); | 
 | 2096 | 		return rc; | 
 | 2097 | 	} | 
 | 2098 |  | 
 | 2099 | 	iadc_channel_select &= ADC_CH_SEL_MASK; | 
 | 2100 | 	if (iadc_channel_select == INTERNAL_RSENSE) { | 
 | 2101 | 		pr_debug("Internal rsense used\n"); | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 2102 | 		if (chip->use_external_rsense) { | 
 | 2103 | 			pr_debug("Changing rsense to external\n"); | 
 | 2104 | 			rc = qpnp_masked_write_iadc(chip, | 
 | 2105 | 					IADC1_BMS_ADC_CH_SEL_CTL, | 
 | 2106 | 					ADC_CH_SEL_MASK, | 
 | 2107 | 					EXTERNAL_RSENSE); | 
 | 2108 | 			if (rc) { | 
 | 2109 | 				pr_err("Unable to set IADC1_BMS channel %x to %x: %d\n", | 
 | 2110 | 						IADC1_BMS_ADC_CH_SEL_CTL, | 
 | 2111 | 						EXTERNAL_RSENSE, rc); | 
 | 2112 | 				return rc; | 
 | 2113 | 			} | 
 | 2114 | 			reset_cc(chip); | 
 | 2115 | 		} | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2116 | 	} else if (iadc_channel_select == EXTERNAL_RSENSE) { | 
 | 2117 | 		pr_debug("External rsense used\n"); | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 2118 | 		if (!chip->use_external_rsense) { | 
 | 2119 | 			pr_debug("Changing rsense to internal\n"); | 
 | 2120 | 			rc = qpnp_masked_write_iadc(chip, | 
 | 2121 | 					IADC1_BMS_ADC_CH_SEL_CTL, | 
 | 2122 | 					ADC_CH_SEL_MASK, | 
 | 2123 | 					INTERNAL_RSENSE); | 
 | 2124 | 			if (rc) { | 
 | 2125 | 				pr_err("Unable to set IADC1_BMS channel %x to %x: %d\n", | 
 | 2126 | 						IADC1_BMS_ADC_CH_SEL_CTL, | 
 | 2127 | 						INTERNAL_RSENSE, rc); | 
 | 2128 | 				return rc; | 
 | 2129 | 			} | 
 | 2130 | 			reset_cc(chip); | 
 | 2131 | 		} | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2132 | 	} else { | 
 | 2133 | 		pr_err("IADC1_BMS_IADC configured incorrectly. Selected channel = %d\n", | 
 | 2134 | 							iadc_channel_select); | 
 | 2135 | 		return -EINVAL; | 
 | 2136 | 	} | 
 | 2137 | 	return 0; | 
 | 2138 | } | 
 | 2139 |  | 
 | 2140 | static int __devinit qpnp_bms_probe(struct spmi_device *spmi) | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2141 | { | 
 | 2142 | 	struct qpnp_bms_chip *chip; | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2143 | 	int rc, vbatt; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2144 |  | 
 | 2145 | 	chip = kzalloc(sizeof *chip, GFP_KERNEL); | 
 | 2146 |  | 
 | 2147 | 	if (chip == NULL) { | 
 | 2148 | 		pr_err("kzalloc() failed.\n"); | 
 | 2149 | 		return -ENOMEM; | 
 | 2150 | 	} | 
 | 2151 |  | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2152 | 	rc = qpnp_vadc_is_ready(); | 
 | 2153 | 	if (rc) { | 
 | 2154 | 		pr_info("vadc not ready: %d, deferring probe\n", rc); | 
 | 2155 | 		goto error_read; | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2156 | 	} | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2157 |  | 
 | 2158 | 	rc = qpnp_iadc_is_ready(); | 
 | 2159 | 	if (rc) { | 
 | 2160 | 		pr_info("iadc not ready: %d, deferring probe\n", rc); | 
 | 2161 | 		goto error_read; | 
 | 2162 | 	} | 
 | 2163 |  | 
 | 2164 | 	rc = register_spmi(chip, spmi); | 
 | 2165 | 	if (rc) { | 
 | 2166 | 		pr_err("error registering spmi resource %d\n", rc); | 
 | 2167 | 		goto error_resource; | 
 | 2168 | 	} | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2169 |  | 
 | 2170 | 	rc = qpnp_read_wrapper(chip, &chip->revision1, | 
 | 2171 | 			chip->base + BMS1_REVISION1, 1); | 
 | 2172 | 	if (rc) { | 
 | 2173 | 		pr_err("error reading version register %d\n", rc); | 
 | 2174 | 		goto error_read; | 
 | 2175 | 	} | 
 | 2176 |  | 
 | 2177 | 	rc = qpnp_read_wrapper(chip, &chip->revision2, | 
 | 2178 | 			chip->base + BMS1_REVISION2, 1); | 
 | 2179 | 	if (rc) { | 
 | 2180 | 		pr_err("Error reading version register %d\n", rc); | 
 | 2181 | 		goto error_read; | 
 | 2182 | 	} | 
| Xiaozhe Shi | a045a56 | 2012-11-28 16:55:39 -0800 | [diff] [blame] | 2183 | 	pr_debug("BMS version: %hhu.%hhu\n", chip->revision2, chip->revision1); | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2184 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2185 | 	rc = bms_read_properties(chip); | 
 | 2186 | 	if (rc) { | 
 | 2187 | 		pr_err("Unable to read all bms properties, rc = %d\n", rc); | 
 | 2188 | 		goto error_read; | 
 | 2189 | 	} | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2190 |  | 
| Xiaozhe Shi | dffbe69 | 2012-12-11 15:35:46 -0800 | [diff] [blame] | 2191 | 	rc = read_iadc_channel_select(chip); | 
 | 2192 | 	if (rc) { | 
 | 2193 | 		pr_err("Unable to get iadc selected channel = %d\n", rc); | 
 | 2194 | 		goto error_read; | 
 | 2195 | 	} | 
 | 2196 |  | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2197 | 	rc = set_battery_data(chip); | 
 | 2198 | 	if (rc) { | 
 | 2199 | 		pr_err("Bad battery data %d\n", rc); | 
 | 2200 | 		goto error_read; | 
 | 2201 | 	} | 
 | 2202 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2203 | 	bms_initialize_constants(chip); | 
 | 2204 |  | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2205 | 	mutex_init(&chip->bms_output_lock); | 
 | 2206 | 	mutex_init(&chip->last_ocv_uv_mutex); | 
 | 2207 | 	mutex_init(&chip->soc_invalidation_mutex); | 
 | 2208 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2209 | 	INIT_DELAYED_WORK(&chip->calculate_soc_delayed_work, | 
 | 2210 | 			calculate_soc_work); | 
 | 2211 |  | 
 | 2212 | 	read_shutdown_soc_and_iavg(chip); | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2213 |  | 
 | 2214 | 	dev_set_drvdata(&spmi->dev, chip); | 
 | 2215 | 	device_init_wakeup(&spmi->dev, 1); | 
 | 2216 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2217 | 	calculate_soc_work(&(chip->calculate_soc_delayed_work.work)); | 
 | 2218 |  | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2219 | 	/* setup & register the battery power supply */ | 
 | 2220 | 	chip->bms_psy.name = "bms"; | 
 | 2221 | 	chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS; | 
 | 2222 | 	chip->bms_psy.properties = msm_bms_power_props; | 
 | 2223 | 	chip->bms_psy.num_properties = ARRAY_SIZE(msm_bms_power_props); | 
 | 2224 | 	chip->bms_psy.get_property = qpnp_bms_power_get_property; | 
 | 2225 | 	chip->bms_psy.set_property = qpnp_bms_power_set_property; | 
 | 2226 | 	chip->bms_psy.external_power_changed = | 
 | 2227 | 		qpnp_bms_external_power_changed; | 
 | 2228 | 	chip->bms_psy.supplied_to = qpnp_bms_supplicants; | 
 | 2229 | 	chip->bms_psy.num_supplicants = ARRAY_SIZE(qpnp_bms_supplicants); | 
 | 2230 |  | 
 | 2231 | 	rc = power_supply_register(chip->dev, &chip->bms_psy); | 
 | 2232 |  | 
 | 2233 | 	if (rc < 0) { | 
 | 2234 | 		pr_err("power_supply_register bms failed rc = %d\n", rc); | 
 | 2235 | 		goto unregister_dc; | 
 | 2236 | 	} | 
 | 2237 |  | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2238 | 	vbatt = 0; | 
 | 2239 | 	get_battery_voltage(&vbatt); | 
 | 2240 |  | 
| Xiaozhe Shi | 8cf085e | 2012-11-26 11:36:36 -0800 | [diff] [blame] | 2241 | 	pr_debug("OK battery_capacity_at_boot=%d vbatt = %d\n", | 
| Xiaozhe Shi | cd7e530 | 2012-10-17 12:29:53 -0700 | [diff] [blame] | 2242 | 				get_prop_bms_capacity(chip), | 
 | 2243 | 				vbatt); | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2244 | 	pr_info("probe success\n"); | 
 | 2245 | 	return 0; | 
 | 2246 |  | 
 | 2247 | unregister_dc: | 
 | 2248 | 	power_supply_unregister(&chip->bms_psy); | 
 | 2249 | 	dev_set_drvdata(&spmi->dev, NULL); | 
| Xiaozhe Shi | c40b397 | 2012-11-30 14:11:16 -0800 | [diff] [blame] | 2250 | error_resource: | 
| Xiaozhe Shi | b19f703 | 2012-08-16 12:14:16 -0700 | [diff] [blame] | 2251 | error_read: | 
 | 2252 | 	kfree(chip); | 
 | 2253 | 	return rc; | 
 | 2254 | } | 
 | 2255 |  | 
 | 2256 | static int __devexit | 
 | 2257 | qpnp_bms_remove(struct spmi_device *spmi) | 
 | 2258 | { | 
 | 2259 | 	struct qpnp_bms_chip *chip = dev_get_drvdata(&spmi->dev); | 
 | 2260 |  | 
 | 2261 | 	dev_set_drvdata(&spmi->dev, NULL); | 
 | 2262 | 	kfree(chip); | 
 | 2263 | 	return 0; | 
 | 2264 | } | 
 | 2265 |  | 
 | 2266 | static struct spmi_driver qpnp_bms_driver = { | 
 | 2267 | 	.probe		= qpnp_bms_probe, | 
 | 2268 | 	.remove		= __devexit_p(qpnp_bms_remove), | 
 | 2269 | 	.driver		= { | 
 | 2270 | 		.name		= QPNP_BMS_DEV_NAME, | 
 | 2271 | 		.owner		= THIS_MODULE, | 
 | 2272 | 		.of_match_table	= qpnp_bms_match_table, | 
 | 2273 | 	}, | 
 | 2274 | }; | 
 | 2275 |  | 
 | 2276 | static int __init qpnp_bms_init(void) | 
 | 2277 | { | 
 | 2278 | 	pr_info("QPNP BMS INIT\n"); | 
 | 2279 | 	return spmi_driver_register(&qpnp_bms_driver); | 
 | 2280 | } | 
 | 2281 |  | 
 | 2282 | static void __exit qpnp_bms_exit(void) | 
 | 2283 | { | 
 | 2284 | 	pr_info("QPNP BMS EXIT\n"); | 
 | 2285 | 	return spmi_driver_unregister(&qpnp_bms_driver); | 
 | 2286 | } | 
 | 2287 |  | 
 | 2288 | module_init(qpnp_bms_init); | 
 | 2289 | module_exit(qpnp_bms_exit); | 
 | 2290 |  | 
 | 2291 | MODULE_DESCRIPTION("QPNP BMS Driver"); | 
 | 2292 | MODULE_LICENSE("GPL v2"); | 
 | 2293 | MODULE_ALIAS("platform:" QPNP_BMS_DEV_NAME); |