hwmon: qpnp-adc: Add PMIC 2.0 ADC bringup fixes

VADC/IADC peripheral is supported from PMIC 2.0 onwards.
Add version check to allow VADC/IADC reads if the
version is supported. Add probe defereal api for clients
who need to know if the ADC driver is ready. There is a
bug where the completion is not initialized. Fix it
by initializing it. Add support to disable the peripheral
after the ADC is read and enable it before starting a
conversion.

Add scaling functions to support reading the die temperature,
XO_THERM, batt_therm and batt_id. Add the ratiometric
calibration routine that uses the vref/gnd for calibration.
The ratiometric calibration is used for calibrating xo_therm,
batt_therm, batt_id.

Update the gain value used for calibration on the IADC
peripheral. PMIC 2.0 IADC peripheral uses 17.857mV instead
of 25mV for its gain calibration.

Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
(cherry picked from commit 5ace110e2165fba66227e18154d58b0e6cbb24b2)

Change-Id: I37b02de53ea2bed913c30261624c31a1ae57131c
Signed-off-by: Sudhir Sharma <sudsha@codeaurora.org>
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index c122270..f908181 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -26,6 +26,7 @@
 #include <linux/spmi.h>
 #include <linux/of_irq.h>
 #include <linux/interrupt.h>
+#include <linux/completion.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/platform_device.h>
 
@@ -33,6 +34,491 @@
 #define QPNP_VADC_MIN_ADC_CODE			0x6000
 /* Max ADC code represents full-scale range of 1.8V */
 #define QPNP_VADC_MAX_ADC_CODE			0xA800
+#define KELVINMIL_DEGMIL	273160
+
+/* Units for temperature below (on x axis) is in 0.1DegC as
+   required by the battery driver. Note the resolution used
+   here to compute the table was done for DegC to milli-volts.
+   In consideration to limit the size of the table for the given
+   temperature range below, the result is linearly interpolated
+   and provided to the battery driver in the units desired for
+   their framework which is 0.1DegC. True resolution of 0.1DegC
+   will result in the below table size to increase by 10 times */
+static const struct qpnp_vadc_map_pt adcmap_btm_threshold[] = {
+	{-300,	1642},
+	{-200,	1544},
+	{-100,	1414},
+	{0,	1260},
+	{10,	1244},
+	{20,	1228},
+	{30,	1212},
+	{40,	1195},
+	{50,	1179},
+	{60,	1162},
+	{70,	1146},
+	{80,	1129},
+	{90,	1113},
+	{100,	1097},
+	{110,	1080},
+	{120,	1064},
+	{130,	1048},
+	{140,	1032},
+	{150,	1016},
+	{160,	1000},
+	{170,	985},
+	{180,	969},
+	{190,	954},
+	{200,	939},
+	{210,	924},
+	{220,	909},
+	{230,	894},
+	{240,	880},
+	{250,	866},
+	{260,	852},
+	{270,	838},
+	{280,	824},
+	{290,	811},
+	{300,	798},
+	{310,	785},
+	{320,	773},
+	{330,	760},
+	{340,	748},
+	{350,	736},
+	{360,	725},
+	{370,	713},
+	{380,	702},
+	{390,	691},
+	{400,	681},
+	{410,	670},
+	{420,	660},
+	{430,	650},
+	{440,	640},
+	{450,	631},
+	{460,	622},
+	{470,	613},
+	{480,	604},
+	{490,	595},
+	{500,	587},
+	{510,	579},
+	{520,	571},
+	{530,	563},
+	{540,	556},
+	{550,	548},
+	{560,	541},
+	{570,	534},
+	{580,	527},
+	{590,	521},
+	{600,	514},
+	{610,	508},
+	{620,	502},
+	{630,	496},
+	{640,	490},
+	{650,	485},
+	{660,	281},
+	{670,	274},
+	{680,	267},
+	{690,	260},
+	{700,	254},
+	{710,	247},
+	{720,	241},
+	{730,	235},
+	{740,	229},
+	{750,	224},
+	{760,	218},
+	{770,	213},
+	{780,	208},
+	{790,	203}
+};
+
+static const struct qpnp_vadc_map_pt adcmap_ntcg_104ef_104fb[] = {
+	{696483,	-40960},
+	{649148,	-39936},
+	{605368,	-38912},
+	{564809,	-37888},
+	{527215,	-36864},
+	{492322,	-35840},
+	{460007,	-34816},
+	{429982,	-33792},
+	{402099,	-32768},
+	{376192,	-31744},
+	{352075,	-30720},
+	{329714,	-29696},
+	{308876,	-28672},
+	{289480,	-27648},
+	{271417,	-26624},
+	{254574,	-25600},
+	{238903,	-24576},
+	{224276,	-23552},
+	{210631,	-22528},
+	{197896,	-21504},
+	{186007,	-20480},
+	{174899,	-19456},
+	{164521,	-18432},
+	{154818,	-17408},
+	{145744,	-16384},
+	{137265,	-15360},
+	{129307,	-14336},
+	{121866,	-13312},
+	{114896,	-12288},
+	{108365,	-11264},
+	{102252,	-10240},
+	{96499,		-9216},
+	{91111,		-8192},
+	{86055,		-7168},
+	{81308,		-6144},
+	{76857,		-5120},
+	{72660,		-4096},
+	{68722,		-3072},
+	{65020,		-2048},
+	{61538,		-1024},
+	{58261,		0},
+	{55177,		1024},
+	{52274,		2048},
+	{49538,		3072},
+	{46962,		4096},
+	{44531,		5120},
+	{42243,		6144},
+	{40083,		7168},
+	{38045,		8192},
+	{36122,		9216},
+	{34308,		10240},
+	{32592,		11264},
+	{30972,		12288},
+	{29442,		13312},
+	{27995,		14336},
+	{26624,		15360},
+	{25333,		16384},
+	{24109,		17408},
+	{22951,		18432},
+	{21854,		19456},
+	{20807,		20480},
+	{19831,		21504},
+	{18899,		22528},
+	{18016,		23552},
+	{17178,		24576},
+	{16384,		25600},
+	{15631,		26624},
+	{14916,		27648},
+	{14237,		28672},
+	{13593,		29696},
+	{12976,		30720},
+	{12400,		31744},
+	{11848,		32768},
+	{11324,		33792},
+	{10825,		34816},
+	{10354,		35840},
+	{9900,		36864},
+	{9471,		37888},
+	{9062,		38912},
+	{8674,		39936},
+	{8306,		40960},
+	{7951,		41984},
+	{7616,		43008},
+	{7296,		44032},
+	{6991,		45056},
+	{6701,		46080},
+	{6424,		47104},
+	{6160,		48128},
+	{5908,		49152},
+	{5667,		50176},
+	{5439,		51200},
+	{5219,		52224},
+	{5010,		53248},
+	{4810,		54272},
+	{4619,		55296},
+	{4440,		56320},
+	{4263,		57344},
+	{4097,		58368},
+	{3938,		59392},
+	{3785,		60416},
+	{3637,		61440},
+	{3501,		62464},
+	{3368,		63488},
+	{3240,		64512},
+	{3118,		65536},
+	{2998,		66560},
+	{2889,		67584},
+	{2782,		68608},
+	{2680,		69632},
+	{2581,		70656},
+	{2490,		71680},
+	{2397,		72704},
+	{2310,		73728},
+	{2227,		74752},
+	{2147,		75776},
+	{2064,		76800},
+	{1998,		77824},
+	{1927,		78848},
+	{1860,		79872},
+	{1795,		80896},
+	{1736,		81920},
+	{1673,		82944},
+	{1615,		83968},
+	{1560,		84992},
+	{1507,		86016},
+	{1456,		87040},
+	{1407,		88064},
+	{1360,		89088},
+	{1314,		90112},
+	{1271,		91136},
+	{1228,		92160},
+	{1189,		93184},
+	{1150,		94208},
+	{1112,		95232},
+	{1076,		96256},
+	{1042,		97280},
+	{1008,		98304},
+	{976,		99328},
+	{945,		100352},
+	{915,		101376},
+	{886,		102400},
+	{859,		103424},
+	{832,		104448},
+	{807,		105472},
+	{782,		106496},
+	{756,		107520},
+	{735,		108544},
+	{712,		109568},
+	{691,		110592},
+	{670,		111616},
+	{650,		112640},
+	{631,		113664},
+	{612,		114688},
+	{594,		115712},
+	{577,		116736},
+	{560,		117760},
+	{544,		118784},
+	{528,		119808},
+	{513,		120832},
+	{498,		121856},
+	{483,		122880},
+	{470,		123904},
+	{457,		124928},
+	{444,		125952},
+	{431,		126976},
+	{419,		128000}
+};
+
+static int32_t qpnp_adc_map_linear(const struct qpnp_vadc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if ((pts == NULL) || (output == NULL))
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].x < pts[1].x)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].x < input)) {
+			/* table entry is less than measured
+				value and table is descending, stop */
+			break;
+		} else if ((descending == 0) &&
+				(pts[i].x > input)) {
+			/* table entry is greater than measured
+				value and table is ascending, stop */
+			break;
+		} else {
+			i++;
+		}
+	}
+
+	if (i == 0)
+		*output = pts[0].y;
+	else if (i == tablesize)
+		*output = pts[tablesize-1].y;
+	else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((int32_t) ((pts[i].y - pts[i-1].y)*
+			(input - pts[i-1].x))/
+			(pts[i].x - pts[i-1].x))+
+			pts[i-1].y);
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_adc_map_batt_therm(const struct qpnp_vadc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if ((pts == NULL) || (output == NULL))
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].y < pts[1].y)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].y < input)) {
+			/* table entry is less than measured
+				value and table is descending, stop */
+			break;
+		} else if ((descending == 0) && (pts[i].y > input)) {
+			/* table entry is greater than measured
+				value and table is ascending, stop */
+			break;
+		} else {
+			i++;
+		}
+	}
+
+	if (i == 0) {
+		*output = pts[0].x;
+	} else if (i == tablesize) {
+		*output = pts[tablesize-1].x;
+	} else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((int32_t) ((pts[i].x - pts[i-1].x)*
+			(input - pts[i-1].y))/
+			(pts[i].y - pts[i-1].y))+
+			pts[i-1].x);
+	}
+
+	return 0;
+}
+
+static int64_t qpnp_adc_scale_ratiometric_calib(int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties)
+{
+	int64_t adc_voltage = 0;
+	bool negative_offset = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties)
+		return -EINVAL;
+
+	adc_voltage = (adc_code -
+		chan_properties->adc_graph[CALIB_RATIOMETRIC].adc_gnd)
+		* adc_properties->adc_vdd_reference;
+	if (adc_voltage < 0) {
+		negative_offset = 1;
+		adc_voltage = -adc_voltage;
+	}
+	do_div(adc_voltage,
+		chan_properties->adc_graph[CALIB_RATIOMETRIC].dy);
+	if (negative_offset)
+		adc_voltage = -adc_voltage;
+
+	return adc_voltage;
+}
+
+int32_t qpnp_adc_scale_pmic_therm(int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t pmic_voltage = 0;
+	bool negative_offset = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	pmic_voltage = (adc_code -
+		chan_properties->adc_graph[CALIB_ABSOLUTE].adc_gnd)
+		* chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
+	if (pmic_voltage < 0) {
+		negative_offset = 1;
+		pmic_voltage = -pmic_voltage;
+	}
+	do_div(pmic_voltage,
+		chan_properties->adc_graph[CALIB_ABSOLUTE].dy);
+	if (negative_offset)
+		pmic_voltage = -pmic_voltage;
+	pmic_voltage += chan_properties->adc_graph[CALIB_ABSOLUTE].dx;
+
+	if (pmic_voltage > 0) {
+		/* 2mV/K */
+		adc_chan_result->measurement = pmic_voltage*
+			chan_properties->offset_gain_denominator;
+
+		do_div(adc_chan_result->measurement,
+			chan_properties->offset_gain_numerator * 2);
+	} else {
+		adc_chan_result->measurement = 0;
+	}
+	/* Change to .001 deg C */
+	adc_chan_result->measurement -= KELVINMIL_DEGMIL;
+	adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_scale_pmic_therm);
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t qpnp_adc_tdkntcg_therm(int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t xo_thm = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	xo_thm = qpnp_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+	xo_thm <<= 4;
+	qpnp_adc_map_linear(adcmap_ntcg_104ef_104fb,
+		ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
+		xo_thm, &adc_chan_result->physical);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_tdkntcg_therm);
+
+int32_t qpnp_adc_scale_batt_therm(int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+
+	return qpnp_adc_map_batt_therm(
+			adcmap_btm_threshold,
+			ARRAY_SIZE(adcmap_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_scale_batt_therm);
+
+int32_t qpnp_adc_scale_batt_id(int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t batt_id_voltage = 0;
+
+	batt_id_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
+			adc_properties, chan_properties);
+	adc_chan_result->physical = batt_id_voltage;
+	adc_chan_result->physical = adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qpnp_adc_scale_batt_id);
 
 int32_t qpnp_adc_scale_default(int32_t adc_code,
 		const struct qpnp_adc_properties *adc_properties,
@@ -203,7 +689,7 @@
 				"qcom,calibration-type", NULL);
 		if (!strncmp(calibration_param, "absolute", 8))
 			calib_type = CALIB_ABSOLUTE;
-		else if (!strncmp(calibration_param, "historical", 9))
+		else if (!strncmp(calibration_param, "ratiometric", 11))
 			calib_type = CALIB_RATIOMETRIC;
 		else {
 			pr_err("%s: Invalid calibration property\n", __func__);
@@ -252,6 +738,8 @@
 		return -ENXIO;
 	}
 
+	init_completion(&adc_qpnp->adc_rslt_completion);
+
 	mutex_init(&adc_qpnp->adc_lock);
 
 	return 0;
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index b689255..aa375d7 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -33,6 +33,15 @@
 #include <linux/platform_device.h>
 
 /* QPNP IADC register definition */
+#define QPNP_IADC_REVISION1				0x0
+#define QPNP_IADC_REVISION2				0x1
+#define QPNP_IADC_REVISION3				0x2
+#define QPNP_IADC_REVISION4				0x3
+#define QPNP_IADC_PERPH_TYPE				0x4
+#define QPNP_IADC_PERH_SUBTYPE				0x5
+
+#define QPNP_IADC_SUPPORTED_REVISION2			1
+
 #define QPNP_STATUS1					0x8
 #define QPNP_STATUS1_OP_MODE				4
 #define QPNP_STATUS1_MULTI_MEAS_EN			BIT(3)
@@ -67,13 +76,14 @@
 #define QPNP_INT_CLR_FIFO_NOT_EMPTY_INT_EN		BIT(1)
 #define QPNP_INT_CLR_EOC_INT_EN_CLR			BIT(0)
 #define QPNP_INT_CLR_MASK				0x1f
-#define QPNP_MODE_CTL					0x40
+#define QPNP_IADC_MODE_CTL				0x40
 #define QPNP_OP_MODE_SHIFT				4
 #define QPNP_USE_BMS_DATA				BIT(4)
 #define QPNP_VADC_SYNCH_EN				BIT(2)
 #define QPNP_OFFSET_RMV_EN				BIT(1)
 #define QPNP_ADC_TRIM_EN				BIT(0)
-#define QPNP_EN_CTL1					0x46
+#define QPNP_IADC_EN_CTL1				0x46
+#define QPNP_IADC_ADC_EN				BIT(7)
 #define QPNP_ADC_CH_SEL_CTL				0x48
 #define QPNP_ADC_DIG_PARAM				0x50
 #define QPNP_ADC_CLK_SEL_MASK				0x3
@@ -101,13 +111,6 @@
 #define QPNP_DATA1					0x61
 #define QPNP_CONV_TIMEOUT_ERR				2
 
-#define QPNP_IADC_MODE_CTL				0x40
-#define QPNP_IADC_USE_BMS_DATA				BIT(4)
-#define QPNP_IADC_RESERVED_BIT3				BIT(3)
-#define QPNP_IADC_VADC_SYNC_EN				BIT(2)
-#define QPNP_IADC_OFFSET_RMV_EN				BIT(1)
-#define QPNP_IADC_ADC_TRIM_EN				BIT(0)
-
 #define QPNP_IADC_ADC_CH_SEL_CTL			0x48
 #define QPNP_IADC_ADC_CHX_SEL_SHIFT			3
 
@@ -121,16 +124,18 @@
 #define QPNP_IADC_DATA0					0x60
 #define QPNP_IADC_DATA1					0x61
 
-#define QPNP_ADC_CONV_TIME_MIN				2000
-#define QPNP_ADC_CONV_TIME_MAX				2200
+#define QPNP_ADC_CONV_TIME_MIN				8000
+#define QPNP_ADC_CONV_TIME_MAX				8200
 
-#define QPNP_ADC_GAIN_CALCULATION			2500
+#define QPNP_ADC_GAIN_CALCULATION_UV			17857
+#define QPNP_IADC_RSENSE_MILLI_FACTOR			1000
 
 struct qpnp_iadc_drv {
 	struct qpnp_adc_drv		*adc;
 	int32_t				rsense;
 	struct device			*iadc_hwmon;
 	bool				iadc_init_calib;
+	bool				iadc_initialized;
 	struct sensor_device_attribute		sens_attr[0];
 };
 
@@ -223,6 +228,31 @@
 	return IRQ_HANDLED;
 }
 
+static int32_t qpnp_iadc_enable(bool state)
+{
+	int rc = 0;
+	u8 data = 0;
+
+	data = QPNP_IADC_ADC_EN;
+	if (state) {
+		rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
+					data);
+		if (rc < 0) {
+			pr_err("IADC enable failed\n");
+			return rc;
+		}
+	} else {
+		rc = qpnp_iadc_write_reg(QPNP_IADC_EN_CTL1,
+					(~data & QPNP_IADC_ADC_EN));
+		if (rc < 0) {
+			pr_err("IADC disable failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
 static int32_t qpnp_iadc_read_conversion_result(int32_t *data)
 {
 	uint8_t rslt_lsb, rslt_msb;
@@ -242,12 +272,9 @@
 
 	*data = (rslt_msb << 8) | rslt_lsb;
 
-	rc = qpnp_vadc_check_result(data);
-	if (rc < 0) {
-		pr_err("VADC data check failed\n");
+	rc = qpnp_iadc_enable(false);
+	if (rc)
 		return rc;
-	}
-
 	return 0;
 }
 
@@ -259,10 +286,7 @@
 	u8 qpnp_iadc_conv_req = 0, qpnp_iadc_dig_param_reg = 0;
 	int32_t rc = 0;
 
-	qpnp_iadc_mode_reg |= (QPNP_IADC_USE_BMS_DATA | QPNP_IADC_USE_BMS_DATA
-			| QPNP_IADC_OFFSET_RMV_EN | QPNP_IADC_ADC_TRIM_EN);
-
-	qpnp_iadc_ch_sel_reg = channel << QPNP_IADC_ADC_CHX_SEL_SHIFT;
+	qpnp_iadc_ch_sel_reg = channel;
 
 	qpnp_iadc_dig_param_reg |= iadc->adc->amux_prop->decimation <<
 					QPNP_IADC_DEC_RATIO_SEL;
@@ -310,6 +334,10 @@
 		return rc;
 	}
 
+	rc = qpnp_iadc_enable(true);
+	if (rc)
+		return rc;
+
 	rc = qpnp_iadc_write_reg(QPNP_CONV_REQ, qpnp_iadc_conv_req);
 	if (rc) {
 		pr_err("qpnp adc read adc failed with %d\n", rc);
@@ -353,17 +381,53 @@
 	return rc;
 }
 
+static int32_t qpnp_iadc_version_check(void)
+{
+	uint8_t revision;
+	int rc;
+
+	rc = qpnp_iadc_read_reg(QPNP_IADC_REVISION2, &revision);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (revision < QPNP_IADC_SUPPORTED_REVISION2) {
+		pr_err("IADC Version not supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int32_t qpnp_iadc_is_ready(void)
+{
+	struct qpnp_iadc_drv *iadc = qpnp_iadc;
+
+	if (!iadc || !iadc->iadc_initialized)
+		return -EPROBE_DEFER;
+	else
+		return 0;
+}
+EXPORT_SYMBOL(qpnp_iadc_is_ready);
+
 int32_t qpnp_iadc_read(enum qpnp_iadc_channels channel,
 						int32_t *result)
 {
 	struct qpnp_iadc_drv *iadc = qpnp_iadc;
 	int32_t vsense_mv = 0, rc;
 
+	if (!iadc || !iadc->iadc_initialized)
+		return -EPROBE_DEFER;
+
 	mutex_lock(&iadc->adc->adc_lock);
 
 	if (!iadc->iadc_init_calib) {
+		rc = qpnp_iadc_version_check();
+		if (rc)
+			goto fail;
 		rc = qpnp_iadc_init_calib();
-		if (!rc) {
+		if (rc) {
 			pr_err("Calibration failed\n");
 			goto fail;
 		} else
@@ -376,12 +440,11 @@
 		goto fail;
 	}
 
-	vsense_mv = ((*result - iadc->adc->calib.offset)/
-			(iadc->adc->calib.gain - iadc->adc->calib.offset))
-			* QPNP_ADC_GAIN_CALCULATION;
+	*result = ((vsense_mv - iadc->adc->calib.offset) *
+				QPNP_ADC_GAIN_CALCULATION_UV)/
+			(iadc->adc->calib.gain - iadc->adc->calib.offset);
 
-	*result = (vsense_mv/qpnp_iadc->rsense);
-
+	*result = (*result / (qpnp_iadc->rsense));
 fail:
 	mutex_unlock(&iadc->adc->adc_lock);
 
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index 9e8a2e2..a0e7ab9 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -32,6 +32,15 @@
 #include <linux/platform_device.h>
 
 /* QPNP VADC register definition */
+#define QPNP_VADC_REVISION1				0x0
+#define QPNP_VADC_REVISION2				0x1
+#define QPNP_VADC_REVISION3				0x2
+#define QPNP_VADC_REVISION4				0x3
+#define QPNP_VADC_PERPH_TYPE				0x4
+#define QPNP_VADC_PERH_SUBTYPE				0x5
+
+#define QPNP_VADC_SUPPORTED_REVISION2			1
+
 #define QPNP_VADC_STATUS1					0x8
 #define QPNP_VADC_STATUS1_OP_MODE				4
 #define QPNP_VADC_STATUS1_MEAS_INTERVAL_EN_STS			BIT(2)
@@ -98,6 +107,8 @@
 	struct dentry			*dent;
 	struct device			*vadc_hwmon;
 	bool				vadc_init_calib;
+	bool				vadc_initialized;
+	int				max_channels_available;
 	struct sensor_device_attribute		sens_attr[0];
 };
 
@@ -105,6 +116,9 @@
 
 static struct qpnp_vadc_scale_fn vadc_scale_fn[] = {
 	[SCALE_DEFAULT] = {qpnp_adc_scale_default},
+	[SCALE_BATT_THERM] = {qpnp_adc_scale_batt_therm},
+	[SCALE_PMIC_THERM] = {qpnp_adc_scale_pmic_therm},
+	[SCALE_XOTHERM] = {qpnp_adc_tdkntcg_therm},
 };
 
 static int32_t qpnp_vadc_read_reg(int16_t reg, u8 *data)
@@ -206,32 +220,21 @@
 	u8 mode_ctrl = 0;
 	int rc = 0;
 
-	if (vadc->vadc_init_calib) {
-		/* Configure interrupt if calibration is complete */
-		rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
-				QPNP_VADC_INT_EOC_BIT);
-		if (rc < 0) {
-			pr_err("Configure error for interrupt setup\n");
-			return rc;
-		}
+	rc = qpnp_vadc_write_reg(QPNP_VADC_INT_EN_SET,
+			QPNP_VADC_INT_EOC_BIT);
+	if (rc < 0) {
+		pr_err("Configure error for interrupt setup\n");
+		return rc;
 	}
 
 	/* Mode selection */
-	rc = qpnp_vadc_read_reg(QPNP_VADC_MODE_CTL, &mode_ctrl);
-	if (rc < 0) {
-		pr_err("Mode configure read error\n");
-		return rc;
-	}
-	mode_ctrl |= chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT;
+	mode_ctrl = chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT;
 	rc = qpnp_vadc_write_reg(QPNP_VADC_MODE_CTL, mode_ctrl);
 	if (rc < 0) {
 		pr_err("Mode configure write error\n");
 		return rc;
 	}
 
-	rc = qpnp_vadc_enable(true);
-	if (rc)
-		return rc;
 
 	/* Channel selection */
 	rc = qpnp_vadc_write_reg(QPNP_VADC_ADC_CH_SEL_CTL,
@@ -242,12 +245,7 @@
 	}
 
 	/* Digital parameter setup */
-	rc = qpnp_vadc_read_reg(QPNP_VADC_ADC_DIG_PARAM, &decimation);
-	if (rc < 0) {
-		pr_err("Digital parameter configure read error\n");
-		return rc;
-	}
-	decimation |= chan_prop->decimation <<
+	decimation = chan_prop->decimation <<
 				QPNP_VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
 	rc = qpnp_vadc_write_reg(QPNP_VADC_ADC_DIG_PARAM, decimation);
 	if (rc < 0) {
@@ -296,6 +294,12 @@
 		}
 	}
 
+	INIT_COMPLETION(vadc->adc->adc_rslt_completion);
+
+	rc = qpnp_vadc_enable(true);
+	if (rc)
+		return rc;
+
 	/* Request conversion */
 	rc = qpnp_vadc_write_reg(QPNP_VADC_CONV_REQ, QPNP_VADC_CONV_REQ_SET);
 	if (rc < 0) {
@@ -402,6 +406,25 @@
 	return IRQ_HANDLED;
 }
 
+static int32_t qpnp_vadc_version_check(void)
+{
+	uint8_t revision;
+	int rc;
+
+	rc = qpnp_vadc_read_reg(QPNP_VADC_REVISION2, &revision);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (revision < QPNP_VADC_SUPPORTED_REVISION2) {
+		pr_err("VADC Version not supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static uint32_t qpnp_vadc_calib_device(void)
 {
 	struct qpnp_vadc_drv *vadc = qpnp_vadc;
@@ -421,8 +444,7 @@
 		goto calib_fail;
 	}
 
-	while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
-					QPNP_VADC_STATUS1_EOC)) {
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
 		rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
 		if (rc < 0)
 			return rc;
@@ -449,8 +471,7 @@
 	}
 
 	status1 = 0;
-	while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
-					QPNP_VADC_STATUS1_EOC)) {
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
 		rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
 		if (rc < 0)
 			return rc;
@@ -459,7 +480,7 @@
 					QPNP_VADC_CONV_TIME_MAX);
 	}
 
-	rc = qpnp_vadc_read_conversion_result(&calib_read_1);
+	rc = qpnp_vadc_read_conversion_result(&calib_read_2);
 	if (rc) {
 		pr_err("qpnp adc read adc failed with %d\n", rc);
 		goto calib_fail;
@@ -467,6 +488,7 @@
 
 	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy =
 					(calib_read_1 - calib_read_2);
+
 	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dx
 						= QPNP_ADC_625_UV;
 	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].adc_vref =
@@ -486,8 +508,7 @@
 	}
 
 	status1 = 0;
-	while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
-					QPNP_VADC_STATUS1_EOC)) {
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
 		rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
 		if (rc < 0)
 			return rc;
@@ -502,7 +523,7 @@
 		goto calib_fail;
 	}
 
-	conv.amux_channel = VDD_VADC;
+	conv.amux_channel = GND_REF;
 	conv.decimation = DECIMATION_TYPE2;
 	conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
 	conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
@@ -514,8 +535,7 @@
 	}
 
 	status1 = 0;
-	while (status1 != (~QPNP_VADC_STATUS1_REQ_STS |
-					QPNP_VADC_STATUS1_EOC)) {
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
 		rc = qpnp_vadc_read_reg(QPNP_VADC_STATUS1, &status1);
 		if (rc < 0)
 			return rc;
@@ -524,7 +544,7 @@
 					QPNP_VADC_CONV_TIME_MAX);
 	}
 
-	rc = qpnp_vadc_read_conversion_result(&calib_read_1);
+	rc = qpnp_vadc_read_conversion_result(&calib_read_2);
 	if (rc) {
 		pr_err("qpnp adc read adc failed with %d\n", rc);
 		goto calib_fail;
@@ -543,14 +563,31 @@
 	return rc;
 }
 
+int32_t qpnp_vadc_is_ready(void)
+{
+	struct qpnp_vadc_drv *vadc = qpnp_vadc;
+
+	if (!vadc || !vadc->vadc_initialized)
+		return -EPROBE_DEFER;
+	else
+		return 0;
+}
+EXPORT_SYMBOL(qpnp_vadc_is_ready);
+
 int32_t qpnp_vadc_conv_seq_request(enum qpnp_vadc_trigger trigger_channel,
 					enum qpnp_vadc_channels channel,
 					struct qpnp_vadc_result *result)
 {
 	struct qpnp_vadc_drv *vadc = qpnp_vadc;
-	int rc = 0, scale_type, amux_prescaling;
+	int rc = 0, scale_type, amux_prescaling, dt_index = 0;
+
+	if (!vadc || !vadc->vadc_initialized)
+		return -EPROBE_DEFER;
 
 	if (!vadc->vadc_init_calib) {
+		rc = qpnp_vadc_version_check();
+		if (rc)
+			return rc;
 		rc = qpnp_vadc_calib_device();
 		if (rc) {
 			pr_err("Calibration failed\n");
@@ -562,12 +599,20 @@
 	mutex_lock(&vadc->adc->adc_lock);
 
 	vadc->adc->amux_prop->amux_channel = channel;
+
+	while (vadc->adc->adc_channels[dt_index].channel_num
+			!= channel || dt_index > vadc->max_channels_available)
+		dt_index++;
+
+	if (dt_index > vadc->max_channels_available)
+		goto fail_unlock;
+
 	vadc->adc->amux_prop->decimation =
-			vadc->adc->adc_channels[channel].adc_decimation;
+			vadc->adc->adc_channels[dt_index].adc_decimation;
 	vadc->adc->amux_prop->hw_settle_time =
-			vadc->adc->adc_channels[channel].hw_settle_time;
+			vadc->adc->adc_channels[dt_index].hw_settle_time;
 	vadc->adc->amux_prop->fast_avg_setup =
-			vadc->adc->adc_channels[channel].fast_avg_setup;
+			vadc->adc->adc_channels[dt_index].fast_avg_setup;
 
 	if (trigger_channel < ADC_SEQ_NONE)
 		vadc->adc->amux_prop->mode_sel = (ADC_OP_CONVERSION_SEQUENCER
@@ -602,14 +647,15 @@
 		goto fail_unlock;
 	}
 
-	amux_prescaling = vadc->adc->adc_channels[channel].chan_path_prescaling;
+	amux_prescaling =
+		vadc->adc->adc_channels[dt_index].chan_path_prescaling;
 
 	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
 		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
 	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
 		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
 
-	scale_type = vadc->adc->adc_channels[channel].adc_scale_fn;
+	scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
 	if (scale_type >= SCALE_NONE) {
 		rc = -EBADF;
 		goto fail_unlock;
@@ -642,8 +688,10 @@
 
 	rc = qpnp_vadc_read(attr->index, &result);
 
-	if (rc)
+	if (rc) {
+		pr_err("VADC read error with %d\n", rc);
 		return 0;
+	}
 
 	return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
 		"Result:%lld Raw:%d\n", result.physical, result.adc_code);
@@ -738,6 +786,8 @@
 		dev_err(&spmi->dev,
 			"failed to request adc irq with error %d\n", rc);
 		return rc;
+	} else {
+		enable_irq_wake(vadc->adc->adc_irq);
 	}
 
 	qpnp_vadc = vadc;
@@ -749,6 +799,8 @@
 	}
 	vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->spmi->dev);
 	vadc->vadc_init_calib = false;
+	vadc->vadc_initialized = true;
+	vadc->max_channels_available = count_adc_channel_list;
 
 	rc = qpnp_vadc_configure_interrupt();
 	if (rc) {
@@ -777,6 +829,7 @@
 		i++;
 	}
 	free_irq(vadc->adc->adc_irq, vadc);
+	vadc->vadc_initialized = false;
 	dev_set_drvdata(&spmi->dev, NULL);
 
 	return 0;