power: qpnp-bms: Implement RDS resistance reading

Up until now, qpnp-bms did not actually support using internal
rsense. When configured to use internal rsense, the BMS driver would
simply use the external rsense value provided in place of the RDS
resistance.

Implement reading the RDS resistance from the IADC at BMS probe time,
and save it as the canonical rsense resistance value. Also, expose
the IADC function qpnp_iadc_get_rsense to facilitate reading the iadc
rsense trim registers.

Change-Id: Iab0834e6228c0263cd055bdf6ac5c2561a375a85
Signed-off-by: Xiaozhe Shi <xiaozhes@codeaurora.org>
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index c28c61d..11f351c 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -537,6 +537,7 @@
 
 	return rc;
 }
+EXPORT_SYMBOL(qpnp_iadc_get_rsense);
 
 int32_t qpnp_check_pmic_temp(void)
 {
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 6e7b20b..7bbdaa4 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -2101,6 +2101,7 @@
 static int read_iadc_channel_select(struct qpnp_bms_chip *chip)
 {
 	u8 iadc_channel_select;
+	int32_t rds_rsense_nohm;
 	int rc;
 
 	rc = qpnp_read_wrapper(chip, &iadc_channel_select,
@@ -2111,10 +2112,17 @@
 	}
 
 	iadc_channel_select &= ADC_CH_SEL_MASK;
-	if (iadc_channel_select == INTERNAL_RSENSE) {
-		pr_debug("Internal rsense used\n");
-		if (chip->use_external_rsense) {
-			pr_debug("Changing rsense to external\n");
+	if (iadc_channel_select != EXTERNAL_RSENSE
+			&& iadc_channel_select != INTERNAL_RSENSE) {
+		pr_err("IADC1_BMS_IADC configured incorrectly. Selected channel = %d\n",
+						iadc_channel_select);
+		return -EINVAL;
+	}
+
+	if (chip->use_external_rsense) {
+		pr_debug("External rsense selected\n");
+		if (iadc_channel_select == INTERNAL_RSENSE) {
+			pr_debug("Internal rsense detected; Changing rsense to external\n");
 			rc = qpnp_masked_write_iadc(chip,
 					IADC1_BMS_ADC_CH_SEL_CTL,
 					ADC_CH_SEL_MASK,
@@ -2127,10 +2135,10 @@
 			}
 			reset_cc(chip);
 		}
-	} else if (iadc_channel_select == EXTERNAL_RSENSE) {
-		pr_debug("External rsense used\n");
-		if (!chip->use_external_rsense) {
-			pr_debug("Changing rsense to internal\n");
+	} else {
+		pr_debug("Internal rsense selected\n");
+		if (iadc_channel_select == EXTERNAL_RSENSE) {
+			pr_debug("External rsense detected; Changing rsense to internal\n");
 			rc = qpnp_masked_write_iadc(chip,
 					IADC1_BMS_ADC_CH_SEL_CTL,
 					ADC_CH_SEL_MASK,
@@ -2143,10 +2151,16 @@
 			}
 			reset_cc(chip);
 		}
-	} else {
-		pr_err("IADC1_BMS_IADC configured incorrectly. Selected channel = %d\n",
-							iadc_channel_select);
-		return -EINVAL;
+
+		rc = qpnp_iadc_get_rsense(&rds_rsense_nohm);
+		if (rc) {
+			pr_err("Unable to read RDS resistance value from IADC; rc = %d\n",
+								rc);
+			return rc;
+		}
+		chip->r_sense_mohm = rds_rsense_nohm/1000000;
+		pr_debug("rds_rsense = %d nOhm, saved as %d mOhm\n",
+					rds_rsense_nohm, chip->r_sense_mohm);
 	}
 	return 0;
 }
@@ -2252,9 +2266,9 @@
 	vbatt = 0;
 	get_battery_voltage(&vbatt);
 
-	pr_info("probe success: soc =%d vbatt = %d ocv = %d\n",
+	pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_mohm = %u\n",
 				get_prop_bms_capacity(chip),
-				vbatt, chip->last_ocv_uv);
+				vbatt, chip->last_ocv_uv, chip->r_sense_mohm);
 	return 0;
 
 unregister_dc:
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 3ab7b9d..903cc3f 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1228,6 +1228,12 @@
 int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
 				struct qpnp_iadc_result *result);
 /**
+ * qpnp_iadc_get_rsense() - Reads the RDS resistance value from the
+			trim registers.
+ * @rsense:	RDS resistance in nOhms.
+ */
+int32_t qpnp_iadc_get_rsense(int32_t *rsense);
+/**
  * qpnp_iadc_get_gain_and_offset() - Performs gain calibration
  *				over 17.8571mV and offset over selected
  *				channel. Channel can be internal rsense,
@@ -1314,6 +1320,8 @@
 { return -ENXIO; }
 static inline int32_t qpnp_adc_tm_is_ready(void)
 { return -ENXIO; }
+static inline int32_t qpnp_iadc_get_rsense(int32_t *rsense)
+{ return -ENXIO; }
 #endif
 
 #endif