Merge "target: msm8952: Add target support for qm215"
diff --git a/dev/qpnp_wled/include/qpnp_lcdb.h b/dev/qpnp_wled/include/qpnp_lcdb.h
index 0eef610..2f09b97 100644
--- a/dev/qpnp_wled/include/qpnp_lcdb.h
+++ b/dev/qpnp_wled/include/qpnp_lcdb.h
@@ -47,7 +47,9 @@
 #define QPNP_LCDB_MODULE_RDY_MASK		0x80
 
 #define LCDB_LDO_INIT_VOLTAGE_UV		4000000
+#define LCDB_LDO_MAX_VOLTAGE_UV			6000000
 #define LCDB_NCP_INIT_VOLTAGE_UV		4000000
+#define LCDB_NCP_MAX_VOLTAGE_UV			6000000
 #define LCDB_BOOST_INIT_VOLTAGE_UV		4700000
 #define LDO_VREG_STEP_SIZE_UV			50000
 #define NCP_VREG_STEP_SIZE_UV			50000
@@ -59,6 +61,9 @@
 
 #define LCDB_ENABLE_SHIFT			7
 #define LCDB_PWRUP_DLY_SHIFT			2
+#define LCDB_STEP_UV				500000
+
+#define min(a, b) (a) < (b) ? a : b
 
 struct qpnp_lcdb {
 	uint32_t lcdb_base;
diff --git a/dev/qpnp_wled/qpnp_lcdb.c b/dev/qpnp_wled/qpnp_lcdb.c
index 8676a7b..ffdd726 100644
--- a/dev/qpnp_wled/qpnp_lcdb.c
+++ b/dev/qpnp_wled/qpnp_lcdb.c
@@ -39,34 +39,22 @@
 
 struct qpnp_lcdb *lcdb;
 
-int qpnp_lcdb_enable(bool state)
-{
-	if (!lcdb) {
-		dprintf(CRITICAL, "%s: lcdb is not initialized yet\n", __func__);
-		return ERROR;
-	}
-
-	pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_ENABLE_CTL_REG,
-		QPNP_LCDB_ENABLE_CTL_MASK, (state << LCDB_ENABLE_SHIFT));
-
-	return 0;
-}
-
-static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb)
+static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb, uint32_t ldo_v,
+								uint32_t ncp_v)
 {
 	int rc = -1;
 	uint32_t new_uV, boost_ref_volt;
 	uint8_t val;
 
-	/* Set LDO Voltage */
-	if (lcdb->ldo_min_volt < lcdb->ldo_init_volt) {
+	/* Set LDO voltage */
+	if (ldo_v < lcdb->ldo_init_volt) {
 		dprintf(CRITICAL, "qpnp_ldo_set_voltage failed, min_uV %d is "
 			"less than the minimum supported voltage %d\n",
-			lcdb->ldo_min_volt, lcdb->ldo_init_volt);
+			ldo_v, lcdb->ldo_init_volt);
 		return rc;
 	}
 
-	val = ((lcdb->ldo_min_volt - lcdb->ldo_init_volt) +
+	val = ((ldo_v - lcdb->ldo_init_volt) +
 		LDO_VREG_STEP_SIZE_UV - 1) / LDO_VREG_STEP_SIZE_UV;
 	new_uV = val * LDO_VREG_STEP_SIZE_UV + lcdb->ldo_init_volt;
 	if (new_uV > lcdb->ldo_max_volt) {
@@ -88,14 +76,14 @@
 	udelay(2);
 
 	/* Set NCP voltage */
-	if (lcdb->ncp_min_volt < lcdb->ncp_init_volt) {
+	if (ncp_v < lcdb->ncp_init_volt) {
 		dprintf(CRITICAL, "qpnp_ncp_set_voltage failed, min_uV %d is "
 			"less than the minimum supported voltage %d\n",
-			lcdb->ncp_min_volt, lcdb->ncp_init_volt);
+			ncp_v, lcdb->ncp_init_volt);
 		return rc;
 	}
 
-	val = ((lcdb->ncp_min_volt - lcdb->ncp_init_volt) +
+	val = ((ncp_v - lcdb->ncp_init_volt) +
 			NCP_VREG_STEP_SIZE_UV - 1) / NCP_VREG_STEP_SIZE_UV;
 	new_uV = val * NCP_VREG_STEP_SIZE_UV + lcdb->ncp_init_volt;
 	if (new_uV > lcdb->ncp_max_volt) {
@@ -134,12 +122,32 @@
 	return 0;
 }
 
-static int qpnp_lcdb_config(struct qpnp_lcdb *lcdb)
+static int qpnp_lcdb_step_voltage(struct qpnp_lcdb *lcdb,
+					uint32_t step_start_uv)
 {
 	int rc = 0;
+	uint32_t target_ldo_v, target_ncp_v, i;
+
+	for (i = step_start_uv; i <= LCDB_LDO_MAX_VOLTAGE_UV;
+						i += LCDB_STEP_UV) {
+		target_ldo_v = min(lcdb->ldo_min_volt, i);
+		target_ncp_v = min(lcdb->ncp_min_volt, i);
+		rc = qpnp_lcdb_set_voltage(lcdb, target_ldo_v, target_ncp_v);
+
+		if (lcdb->ldo_min_volt <= i && lcdb->ncp_min_volt <= i)
+			break;
+
+		/* 1ms wait */
+		mdelay(1);
+	}
+
+	return rc;
+}
+
+static int qpnp_lcdb_config(struct qpnp_lcdb *lcdb)
+{
 	uint8_t reg = 0;
 
-	/* TODO: Set power up & down delay register */
 	if (lcdb->lcdb_pwrup_dly_ms > QPNP_LCDB_PWRUP_DLY_MAX_MS)
 		lcdb->lcdb_pwrup_dly_ms = QPNP_LCDB_PWRUP_DLY_MAX_MS;
 
@@ -164,10 +172,7 @@
 	pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_MODULE_RDY_REG,
 		QPNP_LCDB_MODULE_RDY_MASK, LCDB_MODULE_RDY);
 
-	/* Set regulator voltage */
-	rc = qpnp_lcdb_set_voltage(lcdb);
-
-	return rc;
+	return 0;
 }
 
 static int qpnp_lcdb_setup(struct qpnp_lcdb *lcdb,
@@ -187,6 +192,49 @@
 	return 0;
 }
 
+int qpnp_lcdb_enable(bool state)
+{
+	int rc = 0;
+	uint32_t target_ldo_v, target_ncp_v;
+
+	if (!lcdb) {
+		dprintf(CRITICAL, "%s: lcdb is not initialized yet\n", __func__);
+		return ERROR;
+	}
+
+	if (state) {
+		/*
+		 * Set LDO/NCP voltage step wise (in steps of 500mV) as a part
+		 * of SW WA to prevent loss in accuracy in ADC measurement of
+		 * VBATT voltage.
+		 * Set minimum of spec voltage or 4.5V.
+		 */
+		target_ldo_v = min(lcdb->ldo_min_volt, (lcdb->ldo_init_volt +
+					LCDB_STEP_UV));
+		target_ncp_v = min(lcdb->ncp_min_volt, (lcdb->ncp_init_volt +
+					LCDB_STEP_UV));
+		rc = qpnp_lcdb_set_voltage(lcdb, target_ldo_v, target_ncp_v);
+
+		/* Enable LCDB */
+		pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_ENABLE_CTL_REG,
+			QPNP_LCDB_ENABLE_CTL_MASK, (state << LCDB_ENABLE_SHIFT));
+
+		/* Initial delay of 10ms */
+		mdelay(10);
+
+		/* Start voltage stepping from 5V onwards */
+		if (lcdb->ldo_min_volt > (lcdb->ldo_init_volt + LCDB_STEP_UV) ||
+			lcdb->ncp_min_volt > (lcdb->ncp_init_volt + LCDB_STEP_UV))
+			rc = qpnp_lcdb_step_voltage(lcdb, target_ldo_v +
+								LCDB_STEP_UV);
+	} else {
+		pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_ENABLE_CTL_REG,
+			QPNP_LCDB_ENABLE_CTL_MASK, (state << LCDB_ENABLE_SHIFT));
+	}
+
+	return rc;
+}
+
 int qpnp_lcdb_init(struct qpnp_wled_config_data *config)
 {
 	int rc;