power: pm8921-bms: detect power supply

When the state of charge (SOC) falls below 10% make sure that the
battery voltage too has fallen. If the battery voltage stays high
it means that the device is powered using a fake battery/power
supply.

Report a default soc to the userspace to avoid shutdowns due to a
low SOC.

CRs-Fixed: 309411
Change-Id: Ibd5e76cf7ed926722290840140fde4c592700eba
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index da7c6fd..d0c1392 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -647,6 +647,49 @@
 	return result;
 }
 
+static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
+{
+	int rc;
+	struct pm8921_adc_chan_result result;
+
+	rc = pm8921_adc_read(chip->vbat_channel, &result);
+	if (rc) {
+		pr_err("error reading adc channel = %d, rc = %d\n",
+					chip->vbat_channel, rc);
+		return rc;
+	}
+	pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
+						result.measurement);
+	*uvolts = (int)result.physical;
+	*uvolts = *uvolts * 1000;
+	return 0;
+}
+
+static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
+{
+	int vbatt, rbatt, ibatt, rc;
+
+	rc = get_battery_uvolts(chip, &vbatt);
+	if (rc) {
+		pr_err("failed to read vbatt from adc rc = %d\n", rc);
+		last_ocv_uv = DEFAULT_OCV_MICROVOLTS;
+		return rc;
+	}
+
+	rc =  pm8921_bms_get_battery_current(&ibatt);
+	if (rc) {
+		pr_err("failed to read batt current rc = %d\n", rc);
+		last_ocv_uv = DEFAULT_OCV_MICROVOLTS;
+		return rc;
+	}
+
+	rbatt = calculate_rbatt(the_chip);
+	if (rbatt < 0)
+		rbatt = DEFAULT_RBATT_MOHMS;
+	*ocv = vbatt + ibatt * rbatt;
+	return 0;
+}
+
 static int calculate_pc(struct pm8921_bms_chip *chip, int ocv_uv, int batt_temp,
 							int chargecycles)
 {
@@ -680,24 +723,6 @@
 	*val = cc_mah;
 }
 
-static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
-{
-	int rc;
-	struct pm8921_adc_chan_result result;
-
-	rc = pm8921_adc_read(chip->vbat_channel, &result);
-	if (rc) {
-		pr_err("error reading adc channel = %d, rc = %d\n",
-					chip->vbat_channel, rc);
-		return rc;
-	}
-	pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
-						result.measurement);
-	*uvolts = (int)result.physical;
-	*uvolts = *uvolts * 1000;
-	return 0;
-}
-
 static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
 				 int fcc, int batt_temp, int chargecycles)
 {
@@ -798,6 +823,9 @@
  *				- unusable charge (due to battery resistance)
  * SOC% = (remaining usable charge/ fcc - usable_charge);
  */
+#define BMS_BATT_NOMINAL	3700000
+#define MIN_OPERABLE_SOC	10
+#define BATTERY_POWER_SUPPLY_SOC	53
 static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
 						int batt_temp, int chargecycles)
 {
@@ -815,25 +843,43 @@
 	/* calculate remaining usable charge */
 	remaining_usable_charge = remaining_charge - cc_mah - unusable_charge;
 	pr_debug("RUC = %dmAh\n", remaining_usable_charge);
-	if (remaining_usable_charge < 0) {
+	soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
+	if (soc > 100)
+		soc = 100;
+	pr_debug("SOC = %u%%\n", soc);
+
+	if (soc < MIN_OPERABLE_SOC) {
+		int ocv = 0, rc;
+
+		rc = adc_based_ocv(chip, &ocv);
+		if (rc == 0 && ocv >= BMS_BATT_NOMINAL) {
+			/*
+			 * The ocv doesnt seem to have dropped for
+			 * soc to go negative.
+			 * The setup must be using a power supply
+			 * instead of real batteries.
+			 * Fake high enough soc to prevent userspace
+			 * shutdown for low battery
+			 */
+			soc = BATTERY_POWER_SUPPLY_SOC;
+			pr_debug("Adjusting SOC to %d\n",
+						BATTERY_POWER_SUPPLY_SOC);
+		}
+	}
+
+	if (soc < 0) {
 		pr_err("bad rem_usb_chg = %d rem_chg %d,"
 				"cc_mah %lld, unusb_chg %d\n",
 				remaining_usable_charge, remaining_charge,
 				cc_mah, unusable_charge);
 		pr_err("for bad rem_usb_chg last_ocv_uv = %d"
-				"chargecycles = %d, batt_temp = %d\n",
-				last_ocv_uv, chargecycles, batt_temp);
+				"chargecycles = %d, batt_temp = %d"
+				"fcc = %d soc =%d\n",
+				last_ocv_uv, chargecycles, batt_temp,
+				fcc, soc);
 		update_userspace = 0;
 	}
 
-	soc = (remaining_usable_charge * 100) / (fcc - unusable_charge);
-	if (soc > 100 || soc < 0) {
-		pr_err("bad soc rem_usb_chg %d fcc %d unusb_chg %d\n",
-				remaining_usable_charge, fcc, unusable_charge);
-		update_userspace = 0;
-	}
-	pr_debug("SOC = %u%%\n", soc);
-
 	if (update_userspace) {
 		last_soc = soc;
 	}
@@ -1163,7 +1209,7 @@
 
 static void check_initial_ocv(struct pm8921_bms_chip *chip)
 {
-	int ocv, vbatt, rbatt, ibatt, rc;
+	int ocv, rc;
 
 	/*
 	 * Check if a last_good_ocv is available,
@@ -1171,24 +1217,8 @@
 	 */
 	rc = read_last_good_ocv(chip, &ocv);
 	if (rc || ocv == 0) {
-		rc = get_battery_uvolts(chip, &vbatt);
-		if (rc) {
-			pr_err("failed to read vbatt from adc rc = %d\n", rc);
-			last_ocv_uv = DEFAULT_OCV_MICROVOLTS;
-			return;
-		}
-
-		rc =  pm8921_bms_get_battery_current(&ibatt);
-		if (rc) {
-			pr_err("failed to read batt current rc = %d\n", rc);
-			last_ocv_uv = DEFAULT_OCV_MICROVOLTS;
-			return;
-		}
-
-		rbatt = calculate_rbatt(the_chip);
-		if (rbatt < 0)
-			rbatt = DEFAULT_RBATT_MOHMS;
-		last_ocv_uv = vbatt + ibatt * rbatt;
+		rc = adc_based_ocv(chip, &last_ocv_uv);
+		pr_err("failed to read ocv from adc and bms rc = %d\n", rc);
 	}
 	pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
 }