Merge "power: qpnp-qg: Add a workaround to force recharge"
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
index f87f7db..efa67f5 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-qg.txt
@@ -41,6 +41,16 @@
 		    when this interrupt fires. If not specified, the
 		    default value is 3200 mV.
 
+- qcom,vbatt-empty-cold-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: The battery voltage threshold (in mV) at which the
+		    vbatt-empty interrupt fires. This threshold is only
+		    applied at cold temperature specified by
+		    'qcom,cold-temp-threshold'. The SOC is forced to 0
+		    when this interrupt fires. If not specified, the
+		    default value is 3000 mV.
+
 - qcom,vbatt-cutoff-mv
 	Usage:      optional
 	Value type: <u32>
@@ -56,6 +66,16 @@
 		    the action when this interrupt fires. If not specified
 		    the default value is 3500 mV.
 
+- qcom,vbatt-low-cold-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: The battery voltage threshold (in mV) at which the
+		    the VBAT_LOW interrupt fires. The threshold is only
+		    applied at cold temperature specified by
+		    'qcom,cold-temp-threshold'. Software can take necessary
+		    the action when this interrupt fires. If not specified
+		    the default value is 3800 mV.
+
 - qcom,qg-iterm-ma
 	Usage:      optional
 	Value type: <u32>
@@ -164,6 +184,13 @@
 		    improve the user experience. This is applicable only if
 		    "qcom,hold-soc-while-full" is specified.
 
+- qcom,cold-temp-threshold
+	Usage:      optional
+	Value type: <u32>
+	Definition: Temperature threshold in decidegree at which the low
+		    temperature specific configuration as applied. If not
+		    specified, the default value is 0 degree centigrade.
+
 ==========================================================
 Second Level Nodes - Peripherals managed by QGAUGE driver
 ==========================================================
diff --git a/arch/arm64/boot/dts/qcom/pmi632.dtsi b/arch/arm64/boot/dts/qcom/pmi632.dtsi
index 8bb9dbe..b8f8b68 100644
--- a/arch/arm64/boot/dts/qcom/pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi632.dtsi
@@ -29,7 +29,9 @@
 		pmi632_pon: qcom,power-on@800 {
 			compatible = "qcom,qpnp-power-on";
 			reg = <0x800 0x100>;
+
 			qcom,secondary-pon-reset;
+			qcom,s3-src = "kpdpwr";
 		};
 
 		pmi632_vadc: vadc@3100 {
@@ -464,9 +466,6 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
-			qcom,vbatt-empty-mv = <3300>;
-			qcom,vbatt-low-mv = <3500>;
-			qcom,vbatt-cutoff-mv = <3400>;
 			qcom,qg-iterm-ma = <100>;
 			qcom,hold-soc-while-full;
 			qcom,linearize-soc;
@@ -476,11 +475,12 @@
 			qcom,qgauge@4800 {
 				status = "okay";
 				reg = <0x4800 0x100>;
-				interrupts = <0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x2 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x3 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x48 0x4 IRQ_TYPE_EDGE_BOTH>;
+				interrupts =
+					<0x2 0x48 0x0 IRQ_TYPE_EDGE_BOTH>,
+					<0x2 0x48 0x1 IRQ_TYPE_EDGE_BOTH>,
+					<0x2 0x48 0x2 IRQ_TYPE_EDGE_RISING>,
+					<0x2 0x48 0x3 IRQ_TYPE_EDGE_RISING>,
+					<0x2 0x48 0x4 IRQ_TYPE_EDGE_RISING>;
 				interrupt-names = "qg-batt-missing",
 						  "qg-vbat-low",
 						  "qg-vbat-empty",
diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h
index 55dba4e..6e44f33 100644
--- a/drivers/power/supply/qcom/qg-core.h
+++ b/drivers/power/supply/qcom/qg-core.h
@@ -29,7 +29,9 @@
 
 struct qg_dt {
 	int			vbatt_empty_mv;
+	int			vbatt_empty_cold_mv;
 	int			vbatt_low_mv;
+	int			vbatt_low_cold_mv;
 	int			vbatt_cutoff_mv;
 	int			iterm_ma;
 	int			s2_fifo_length;
@@ -44,6 +46,7 @@
 	int			delta_soc;
 	int			rbat_conn_mohm;
 	int			ignore_shutdown_soc_secs;
+	int			cold_temp_threshold;
 	bool			hold_soc_while_full;
 	bool			linearize_soc;
 };
@@ -147,6 +150,7 @@
 
 enum qg_wa_flags {
 	QG_VBAT_LOW_WA = BIT(0),
+	QG_RECHARGE_SOC_WA = BIT(1),
 };
 
 
diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c
index 660f6f1..c4d5043 100644
--- a/drivers/power/supply/qcom/qg-soc.c
+++ b/drivers/power/supply/qcom/qg-soc.c
@@ -24,11 +24,15 @@
 
 #define DEFAULT_UPDATE_TIME_MS			64000
 #define SOC_SCALE_HYST_MS			2000
-#define SOC_SCALE_LOW_TEMP_THRESHOLD		100
 
 static int qg_delta_soc_interval_ms = 20000;
 module_param_named(
-	delta_soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+	soc_interval_ms, qg_delta_soc_interval_ms, int, 0600
+);
+
+static int qg_delta_soc_cold_interval_ms = 4000;
+module_param_named(
+	soc_cold_interval_ms, qg_delta_soc_cold_interval_ms, int, 0600
 );
 
 static void get_next_update_time(struct qpnp_qg *chip)
@@ -52,9 +56,11 @@
 	rc = qg_get_battery_temp(chip, &batt_temp);
 	if (rc < 0)
 		pr_err("Failed to read battery temperature rc=%d\n", rc);
+	else if (batt_temp < chip->dt.cold_temp_threshold)
+		min_delta_soc_interval_ms = qg_delta_soc_cold_interval_ms;
 
-	if (batt_temp < SOC_SCALE_LOW_TEMP_THRESHOLD)
-		min_delta_soc_interval_ms = min_delta_soc_interval_ms / 2;
+	if (!min_delta_soc_interval_ms)
+		min_delta_soc_interval_ms = 1000;	/* 1 second */
 
 	chip->next_wakeup_ms = (full_time_ms / (soc_points + 1))
 					- SOC_SCALE_HYST_MS;
diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c
index effb093..f7a6b7a 100644
--- a/drivers/power/supply/qcom/qpnp-qg.c
+++ b/drivers/power/supply/qcom/qpnp-qg.c
@@ -114,9 +114,11 @@
 	return rc;
 }
 
+#define DEFAULT_S3_FIFO_LENGTH		3
 static int qg_update_fifo_length(struct qpnp_qg *chip, u8 length)
 {
 	int rc;
+	u8 s3_entry_fifo_length = 0;
 
 	if (!length || length > 8) {
 		pr_err("Invalid FIFO length %d\n", length);
@@ -128,6 +130,21 @@
 	if (rc < 0)
 		pr_err("Failed to write S2 FIFO length, rc=%d\n", rc);
 
+	/* update the S3 FIFO length, when S2 length is updated */
+	if (length > 3)
+		s3_entry_fifo_length = (chip->dt.s3_entry_fifo_length > 0) ?
+			chip->dt.s3_entry_fifo_length : DEFAULT_S3_FIFO_LENGTH;
+	else	/* Use S3 length as 1 for any S2 length <= 3 */
+		s3_entry_fifo_length = 1;
+
+	rc = qg_masked_write(chip,
+			chip->qg_base + QG_S3_SLEEP_OCV_IBAT_CTL1_REG,
+			SLEEP_IBAT_QUALIFIED_LENGTH_MASK,
+			s3_entry_fifo_length - 1);
+	if (rc < 0)
+		pr_err("Failed to write S3-entry fifo-length, rc=%d\n",
+						rc);
+
 	return rc;
 }
 
@@ -412,8 +429,19 @@
 #define VBAT_LOW_HYST_UV		50000 /* 50mV */
 static int qg_vbat_low_wa(struct qpnp_qg *chip)
 {
-	int rc, i;
-	u32 vbat_low_uv = chip->dt.vbatt_low_mv * 1000 + VBAT_LOW_HYST_UV;
+	int rc, i, temp = 0;
+	u32 vbat_low_uv = 0;
+
+	rc = qg_get_battery_temp(chip, &temp);
+	if (rc < 0) {
+		pr_err("Failed to read batt_temp rc=%d\n", rc);
+		temp = 250;
+	}
+
+	vbat_low_uv = 1000 * ((temp < chip->dt.cold_temp_threshold) ?
+				chip->dt.vbatt_low_cold_mv :
+				chip->dt.vbatt_low_mv);
+	vbat_low_uv += VBAT_LOW_HYST_UV;
 
 	if (!(chip->wa_flags & QG_VBAT_LOW_WA) || !chip->vbat_low)
 		return 0;
@@ -459,6 +487,73 @@
 	return rc;
 }
 
+static int qg_vbat_thresholds_config(struct qpnp_qg *chip)
+{
+	int rc, temp = 0, vbat_mv;
+	u8 reg;
+
+	rc = qg_get_battery_temp(chip, &temp);
+	if (rc < 0) {
+		pr_err("Failed to read batt_temp rc=%d\n", rc);
+		return rc;
+	}
+
+	vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+			chip->dt.vbatt_empty_cold_mv :
+			chip->dt.vbatt_empty_mv;
+
+	rc = qg_read(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to read vbat-empty, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (vbat_mv == (reg * 50))	/* No change */
+		goto config_vbat_low;
+
+	reg = vbat_mv / 50;
+	rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+		return rc;
+	}
+
+	qg_dbg(chip, QG_DEBUG_STATUS,
+		"VBAT EMPTY threshold updated to %dmV temp=%d\n",
+						vbat_mv, temp);
+
+config_vbat_low:
+	vbat_mv = (temp < chip->dt.cold_temp_threshold) ?
+			chip->dt.vbatt_low_cold_mv :
+			chip->dt.vbatt_low_mv;
+
+	rc = qg_read(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to read vbat-low, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (vbat_mv == (reg * 50))	/* No change */
+		return 0;
+
+	reg = vbat_mv / 50;
+	rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
+					&reg, 1);
+	if (rc < 0) {
+		pr_err("Failed to write vbat-low, rc=%d\n", rc);
+		return rc;
+	}
+
+	qg_dbg(chip, QG_DEBUG_STATUS,
+		"VBAT LOW threshold updated to %dmV temp=%d\n",
+						vbat_mv, temp);
+
+	return rc;
+}
+
 #define MIN_FIFO_FULL_TIME_MS			12000
 static int process_rt_fifo_data(struct qpnp_qg *chip,
 				bool vbat_low, bool update_smb)
@@ -609,6 +704,10 @@
 		goto done;
 	}
 
+	rc = qg_vbat_thresholds_config(chip);
+	if (rc < 0)
+		pr_err("Failed to apply VBAT EMPTY config rc=%d\n", rc);
+
 	rc = qg_vbat_low_wa(chip);
 	if (rc < 0) {
 		pr_err("Failed to apply VBAT LOW WA, rc=%d\n", rc);
@@ -1081,12 +1180,26 @@
 			chip->charge_full = true;
 			qg_dbg(chip, QG_DEBUG_STATUS, "Setting charge_full (0->1) @ msoc=%d\n",
 					chip->msoc);
-		} else {
+		} else if (health != POWER_SUPPLY_HEALTH_GOOD) {
+			/* terminated in JEITA */
 			qg_dbg(chip, QG_DEBUG_STATUS, "Terminated charging @ msoc=%d\n",
 					chip->msoc);
 		}
 	} else if ((!chip->charge_done || chip->msoc < recharge_soc)
 				&& chip->charge_full) {
+
+		if (chip->wa_flags & QG_RECHARGE_SOC_WA) {
+			/* Force recharge */
+			prop.intval = 0;
+			rc = power_supply_set_property(chip->batt_psy,
+				POWER_SUPPLY_PROP_RECHARGE_SOC, &prop);
+			if (rc < 0)
+				pr_err("Failed to force recharge rc=%d\n", rc);
+			else
+				qg_dbg(chip, QG_DEBUG_STATUS,
+					"Forced recharge\n");
+		}
+
 		/*
 		 * If recharge or discharge has started and
 		 * if linearize soc dtsi property defined
@@ -1668,6 +1781,7 @@
 {
 	switch (chip->pmic_rev_id->pmic_subtype) {
 	case PMI632_SUBTYPE:
+		chip->wa_flags |= QG_RECHARGE_SOC_WA;
 		if (chip->pmic_rev_id->rev4 == PMI632_V1P0_REV4)
 			chip->wa_flags |= QG_VBAT_LOW_WA;
 		break;
@@ -1840,31 +1954,25 @@
 		}
 	}
 
-	/* vbat low */
+	/* vbat based configs */
 	if (chip->dt.vbatt_low_mv < 0)
 		chip->dt.vbatt_low_mv = 0;
 	else if (chip->dt.vbatt_low_mv > 12750)
 		chip->dt.vbatt_low_mv = 12750;
 
-	reg = chip->dt.vbatt_low_mv / 50;
-	rc = qg_write(chip, chip->qg_base + QG_VBAT_LOW_THRESHOLD_REG,
-					&reg, 1);
-	if (rc < 0) {
-		pr_err("Failed to write vbat-low, rc=%d\n", rc);
-		return rc;
-	}
-
-	/* vbat empty */
 	if (chip->dt.vbatt_empty_mv < 0)
 		chip->dt.vbatt_empty_mv = 0;
 	else if (chip->dt.vbatt_empty_mv > 12750)
 		chip->dt.vbatt_empty_mv = 12750;
 
-	reg = chip->dt.vbatt_empty_mv / 50;
-	rc = qg_write(chip, chip->qg_base + QG_VBAT_EMPTY_THRESHOLD_REG,
-					&reg, 1);
+	if (chip->dt.vbatt_empty_cold_mv < 0)
+		chip->dt.vbatt_empty_cold_mv = 0;
+	else if (chip->dt.vbatt_empty_cold_mv > 12750)
+		chip->dt.vbatt_empty_cold_mv = 12750;
+
+	rc = qg_vbat_thresholds_config(chip);
 	if (rc < 0) {
-		pr_err("Failed to write vbat-empty, rc=%d\n", rc);
+		pr_err("Failed to configure VBAT empty/low rc=%d\n", rc);
 		return rc;
 	}
 
@@ -1962,8 +2070,10 @@
 }
 
 #define DEFAULT_VBATT_EMPTY_MV		3200
+#define DEFAULT_VBATT_EMPTY_COLD_MV	3000
 #define DEFAULT_VBATT_CUTOFF_MV		3400
 #define DEFAULT_VBATT_LOW_MV		3500
+#define DEFAULT_VBATT_LOW_COLD_MV	3800
 #define DEFAULT_ITERM_MA		100
 #define DEFAULT_S2_FIFO_LENGTH		5
 #define DEFAULT_S2_VBAT_LOW_LENGTH	2
@@ -1971,6 +2081,7 @@
 #define DEFAULT_S2_ACC_INTVL_MS		100
 #define DEFAULT_DELTA_SOC		1
 #define DEFAULT_SHUTDOWN_SOC_SECS	360
+#define DEFAULT_COLD_TEMP_THRESHOLD	0
 static int qg_parse_dt(struct qpnp_qg *chip)
 {
 	int rc = 0;
@@ -2103,12 +2214,30 @@
 	else
 		chip->dt.vbatt_empty_mv = temp;
 
+	rc = of_property_read_u32(node, "qcom,vbatt-empty-cold-mv", &temp);
+	if (rc < 0)
+		chip->dt.vbatt_empty_cold_mv = DEFAULT_VBATT_EMPTY_COLD_MV;
+	else
+		chip->dt.vbatt_empty_cold_mv = temp;
+
+	rc = of_property_read_u32(node, "qcom,cold-temp-threshold", &temp);
+	if (rc < 0)
+		chip->dt.cold_temp_threshold = DEFAULT_COLD_TEMP_THRESHOLD;
+	else
+		chip->dt.cold_temp_threshold = temp;
+
 	rc = of_property_read_u32(node, "qcom,vbatt-low-mv", &temp);
 	if (rc < 0)
 		chip->dt.vbatt_low_mv = DEFAULT_VBATT_LOW_MV;
 	else
 		chip->dt.vbatt_low_mv = temp;
 
+	rc = of_property_read_u32(node, "qcom,vbatt-low-cold-mv", &temp);
+	if (rc < 0)
+		chip->dt.vbatt_low_cold_mv = DEFAULT_VBATT_LOW_COLD_MV;
+	else
+		chip->dt.vbatt_low_cold_mv = temp;
+
 	rc = of_property_read_u32(node, "qcom,vbatt-cutoff-mv", &temp);
 	if (rc < 0)
 		chip->dt.vbatt_cutoff_mv = DEFAULT_VBATT_CUTOFF_MV;
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 20dd78e..7a3a4dc 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -949,6 +949,7 @@
  *************************/
 static enum power_supply_property smb5_batt_props[] = {
 	POWER_SUPPLY_PROP_INPUT_SUSPEND,
+	POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_HEALTH,
 	POWER_SUPPLY_PROP_PRESENT,
@@ -995,6 +996,10 @@
 	case POWER_SUPPLY_PROP_INPUT_SUSPEND:
 		rc = smblib_get_prop_input_suspend(chg, val);
 		break;
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+		val->intval = !get_client_vote(chg->chg_disable_votable,
+					      USER_VOTER);
+		break;
 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
 		rc = smblib_get_prop_batt_charge_type(chg, val);
 		break;
@@ -1093,6 +1098,9 @@
 	case POWER_SUPPLY_PROP_INPUT_SUSPEND:
 		rc = smblib_set_prop_input_suspend(chg, val);
 		break;
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+		vote(chg->chg_disable_votable, USER_VOTER, !val->intval, 0);
+		break;
 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 		rc = smblib_set_prop_system_temp_level(chg, val);
 		break;
@@ -1147,6 +1155,17 @@
 		chg->die_health = val->intval;
 		power_supply_changed(chg->batt_psy);
 		break;
+	case POWER_SUPPLY_PROP_RECHARGE_SOC:
+		if (chg->smb_version == PMI632_SUBTYPE) {
+			/* toggle charging to force recharge */
+			vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+					true, 0);
+			/* charge disable delay */
+			msleep(50);
+			vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER,
+					false, 0);
+		}
+		break;
 	default:
 		rc = -EINVAL;
 	}
@@ -1169,6 +1188,7 @@
 	case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
 	case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
 	case POWER_SUPPLY_PROP_DIE_HEALTH:
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
 		return 1;
 	default:
 		break;
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 7ee4a3a..668ce5f 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -67,6 +67,7 @@
 #define HW_LIMIT_VOTER			"HW_LIMIT_VOTER"
 #define DYNAMIC_RP_VOTER		"DYNAMIC_RP_VOTER"
 #define DEFAULT_100MA_VOTER		"DEFAULT_100MA_VOTER"
+#define FORCE_RECHARGE_VOTER		"FORCE_RECHARGE_VOTER"
 
 #define BOOST_BACK_STORM_COUNT	3
 #define WEAK_CHG_STORM_COUNT	8