hwmon: pm8921-adc: Add sysfs interface

-Add sysfs interface to read PM8921 HK/XOADC
and thermistor temperature measurements mapped
through MPP's.
-Move PM8921 ADC from directory /drivers/mfd to
/drivers/hwmon for userspace clients to read ADC
through hwmon.

CRs-Fixed: 302365
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>

Conflicts:

	drivers/mfd/Kconfig
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index ef3cb2e..f9d671e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -788,6 +788,15 @@
 	  internally uses an array of LTC2499 and EPM ADCs in a differential
 	  configuration to provide a flat set of channels that can be addressed.
 
+config SENSORS_PM8921_ADC
+	tristate "Support for Qualcomm PM8921 ADC"
+	depends on MFD_PM8921_CORE
+	help
+	  This is the ADC arbiter driver for Qualcomm PM8921 Chip.
+
+	  The driver supports reading the HKADC, XOADC and support to set and receive
+	  temperature threshold notifications using the Battery temperature module.
+
 config SENSORS_PC87360
 	tristate "National Semiconductor PC87360 family"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index eacfcb5..c25ffcb 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -120,6 +120,7 @@
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
 obj-$(CONFIG_SENSORS_WPCE775X)	+= wpce775x.o
 obj-$(CONFIG_SENSORS_MSM_ADC)	+= msm_adc.o m_adcproc.o
+obj-$(CONFIG_SENSORS_PM8921_ADC)	+= pm8921-adc.o msmproc_adc.o
 
 # PMBus drivers
 obj-$(CONFIG_PMBUS)		+= pmbus_core.o
diff --git a/drivers/hwmon/msmproc_adc.c b/drivers/hwmon/msmproc_adc.c
new file mode 100644
index 0000000..a0ee748
--- /dev/null
+++ b/drivers/hwmon/msmproc_adc.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#define KELVINMIL_DEGMIL	273160
+
+static const struct pm8921_adc_map_pt adcmap_batttherm[] = {
+	{41001,	-30},
+	{40017,	-20},
+	{38721,	-10},
+	{37186,	  0},
+	{35554,	 10},
+	{33980,	 20},
+	{33253,	 25},
+	{32580,	 30},
+	{31412,	 40},
+	{30481,	 50},
+	{29759,	 60},
+	{29209,	 70},
+	{28794,	 80}
+};
+
+static const struct pm8921_adc_map_pt adcmap_btm_threshold[] = {
+	{-30,	41001},
+	{-20,	40017},
+	{-10,	38721},
+	{0,	37186},
+	{10,	35554},
+	{11,	35392},
+	{12,	35230},
+	{13,	35070},
+	{14,	34910},
+	{15,	34751},
+	{16,	34594},
+	{17,	34438},
+	{18,	34284},
+	{19,	34131},
+	{20,	33980},
+	{21,	33830},
+	{22,	33683},
+	{23,	33538},
+	{24,	33394},
+	{25,	33253},
+	{26,	33114},
+	{27,	32977},
+	{28,	32842},
+	{29,	32710},
+	{30,	32580},
+	{31,	32452},
+	{32,	32327},
+	{33,	32204},
+	{34,	32084},
+	{35,	31966},
+	{36,	31850},
+	{37,	31737},
+	{38,	31627},
+	{39,	31518},
+	{40,	31412},
+	{41,	31309},
+	{42,	31208},
+	{43,	31109},
+	{44,	31013},
+	{45,	30918},
+	{46,	30827},
+	{47,	30737},
+	{48,	30649},
+	{49,	30564},
+	{50,	30481},
+	{51,	30400},
+	{52,	30321},
+	{53,	30244},
+	{54,	30169},
+	{55,	30096},
+	{56,	30025},
+	{57,	29956},
+	{58,	29889},
+	{59,	29823},
+	{60,	29759},
+	{61,	29697},
+	{62,	29637},
+	{63,	29578},
+	{64,	29521},
+	{65,	29465},
+	{66,	29411},
+	{67,	29359},
+	{68,	29308},
+	{69,	29258},
+	{70,	29209},
+	{71,	29162},
+	{72,	29117},
+	{73,	29072},
+	{74,	29029},
+	{75,	28987},
+	{76,	28946},
+	{77,	28906},
+	{78,	28868},
+	{79,	28830},
+	{80,	28794}
+};
+
+static const struct pm8921_adc_map_pt adcmap_pa_therm[] = {
+	{41350,	-30},
+	{41282,	-29},
+	{41211,	-28},
+	{41137,	-27},
+	{41060,	-26},
+	{40980,	-25},
+	{40897,	-24},
+	{40811,	-23},
+	{40721,	-22},
+	{40629,	-21},
+	{40533,	-20},
+	{40434,	-19},
+	{40331,	-18},
+	{40226,	-17},
+	{40116,	-16},
+	{40004,	-15},
+	{39888,	-14},
+	{39769,	-13},
+	{39647,	-12},
+	{39521,	-11},
+	{39392,	-10},
+	{39260,	-9},
+	{39124,	-8},
+	{38986,	-7},
+	{38845,	-6},
+	{38700,	-5},
+	{38553,	-4},
+	{38403,	-3},
+	{38250,	-2},
+	{38094,	-1},
+	{37936,	0},
+	{37776,	1},
+	{37613,	2},
+	{37448,	3},
+	{37281,	4},
+	{37112,	5},
+	{36942,	6},
+	{36770,	7},
+	{36596,	8},
+	{36421,	9},
+	{36245,	10},
+	{36068,	11},
+	{35890,	12},
+	{35712,	13},
+	{35532,	14},
+	{35353,	15},
+	{35173,	16},
+	{34993,	17},
+	{34813,	18},
+	{34634,	19},
+	{34455,	20},
+	{34276,	21},
+	{34098,	22},
+	{33921,	23},
+	{33745,	24},
+	{33569,	25},
+	{33395,	26},
+	{33223,	27},
+	{33051,	28},
+	{32881,	29},
+	{32713,	30},
+	{32547,	31},
+	{32382,	32},
+	{32219,	33},
+	{32058,	34},
+	{31899,	35},
+	{31743,	36},
+	{31588,	37},
+	{31436,	38},
+	{31285,	39},
+	{31138,	40},
+	{30992,	41},
+	{30849,	42},
+	{30708,	43},
+	{30570,	44},
+	{30434,	45},
+	{30300,	46},
+	{30169,	47},
+	{30041,	48},
+	{29915,	49},
+	{29791,	50},
+	{29670,	51},
+	{29551,	52},
+	{29435,	53},
+	{29321,	54},
+	{29210,	55},
+	{29101,	56},
+	{28994,	57},
+	{28890,	58},
+	{28788,	59},
+	{28688,	60},
+	{28590,	61},
+	{28495,	62},
+	{28402,	63},
+	{28311,	64},
+	{28222,	65},
+	{28136,	66},
+	{28051,	67},
+	{27968,	68},
+	{27888,	69},
+	{27809,	70},
+	{27732,	71},
+	{27658,	72},
+	{27584,	73},
+	{27513,	74},
+	{27444,	75},
+	{27376,	76},
+	{27310,	77},
+	{27245,	78},
+	{27183,	79},
+	{27121,	80},
+	{27062,	81},
+	{27004,	82},
+	{26947,	83},
+	{26892,	84},
+	{26838,	85},
+	{26785,	86},
+	{26734,	87},
+	{26684,	88},
+	{26636,	89},
+	{26588,	90}
+};
+
+static const struct pm8921_adc_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 pm8921_adc_map_linear(const struct pm8921_adc_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;
+}
+
+int32_t pm8921_adc_scale_default(int32_t adc_code,
+		const struct pm8921_adc_properties *adc_properties,
+		const struct pm8921_adc_chan_properties *chan_properties,
+		struct pm8921_adc_chan_result *adc_chan_result)
+{
+	bool negative_rawfromoffset = 0;
+	int32_t rawfromoffset = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	rawfromoffset = adc_code -
+			chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+
+	adc_chan_result->adc_code = adc_code;
+	if (rawfromoffset < 0) {
+		if (adc_properties->bipolar) {
+			rawfromoffset = -rawfromoffset;
+			negative_rawfromoffset = 1;
+		} else {
+			rawfromoffset = 0;
+		}
+	}
+
+	if (rawfromoffset >= 1 << adc_properties->bitresolution)
+		rawfromoffset = (1 << adc_properties->bitresolution) - 1;
+
+	adc_chan_result->measurement = (int64_t)rawfromoffset *
+		chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
+				chan_properties->offset_gain_denominator;
+
+	/* do_div only perform positive integer division! */
+	do_div(adc_chan_result->measurement,
+		chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
+				chan_properties->offset_gain_numerator);
+
+	if (negative_rawfromoffset)
+		adc_chan_result->measurement = -adc_chan_result->measurement;
+
+	/* Note: adc_chan_result->measurement is in the unit of
+	 * adc_properties.adc_reference. For generic channel processing,
+	 * channel measurement is a scale/ratio relative to the adc
+	 * reference input */
+	adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_default);
+
+int32_t pm8921_adc_scale_batt_therm(int32_t adc_code,
+		const struct pm8921_adc_properties *adc_properties,
+		const struct pm8921_adc_chan_properties *chan_properties,
+		struct pm8921_adc_chan_result *adc_chan_result)
+{
+	/* convert mV ---> degC using the table */
+	return pm8921_adc_map_linear(
+			adcmap_batttherm,
+			ARRAY_SIZE(adcmap_batttherm),
+			adc_code,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_batt_therm);
+
+int32_t pm8921_adc_scale_pa_therm(int32_t adc_code,
+		const struct pm8921_adc_properties *adc_properties,
+		const struct pm8921_adc_chan_properties *chan_properties,
+		struct pm8921_adc_chan_result *adc_chan_result)
+{
+	/* convert mV ---> degC using the table */
+	return pm8921_adc_map_linear(
+			adcmap_pa_therm,
+			ARRAY_SIZE(adcmap_pa_therm),
+			adc_code,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_pa_therm);
+
+int32_t pm8921_adc_scale_pmic_therm(int32_t adc_code,
+		const struct pm8921_adc_properties *adc_properties,
+		const struct pm8921_adc_chan_properties *chan_properties,
+		struct pm8921_adc_chan_result *adc_chan_result)
+{
+	int32_t rawfromoffset;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	rawfromoffset = adc_code -
+			chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+	if (rawfromoffset > 0) {
+		if (rawfromoffset >= 1 << adc_properties->bitresolution)
+			rawfromoffset = (1 << adc_properties->bitresolution)
+									- 1;
+		/* 2mV/K */
+		adc_chan_result->measurement = (int64_t)rawfromoffset*
+			chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
+			chan_properties->offset_gain_denominator * 1000;
+
+		do_div(adc_chan_result->measurement,
+			chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
+			chan_properties->offset_gain_numerator*2);
+	} else {
+		adc_chan_result->measurement = 0;
+	}
+	/* Note: adc_chan_result->measurement is in the unit of
+		adc_properties.adc_reference */
+	adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
+	/* Change to .001 deg C */
+	adc_chan_result->physical -= KELVINMIL_DEGMIL;
+	adc_chan_result->measurement <<= 1;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_scale_pmic_therm);
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t pm8921_adc_tdkntcg_therm(int32_t adc_code,
+		const struct pm8921_adc_properties *adc_properties,
+		const struct pm8921_adc_chan_properties *chan_properties,
+		struct pm8921_adc_chan_result *adc_chan_result)
+{
+	int32_t rt_r25;
+	int32_t offset = chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+
+	rt_r25 = adc_code - offset;
+
+	pm8921_adc_map_linear(adcmap_ntcg_104ef_104fb,
+		ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
+		rt_r25, &adc_chan_result->physical);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_tdkntcg_therm);
+
+int32_t pm8921_adc_batt_scaler(struct pm8921_adc_arb_btm_param *btm_param)
+{
+	int rc;
+
+	rc = pm8921_adc_map_linear(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		btm_param->low_thr_temp,
+		&btm_param->low_thr_voltage);
+
+	if (!rc) {
+		rc = pm8921_adc_map_linear(
+			adcmap_btm_threshold,
+			ARRAY_SIZE(adcmap_btm_threshold),
+			btm_param->high_thr_temp,
+			&btm_param->high_thr_voltage);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_batt_scaler);
diff --git a/drivers/hwmon/pm8921-adc.c b/drivers/hwmon/pm8921-adc.c
new file mode 100644
index 0000000..5ab6296
--- /dev/null
+++ b/drivers/hwmon/pm8921-adc.c
@@ -0,0 +1,1306 @@
+/*
+ * Copyright (c) 2011, Code Aurora Forum. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Qualcomm's PM8921 ADC Arbiter driver
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/mpp.h>
+#include <linux/debugfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+/* User Bank register set */
+#define PM8921_ADC_ARB_USRP_CNTRL1			0x197
+#define PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB		BIT(0)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV1			BIT(1)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV2			BIT(2)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV3			BIT(3)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV4			BIT(4)
+#define PM8921_ADC_ARB_USRP_CNTRL1_RSV5			BIT(5)
+#define PM8921_ADC_ARB_USRP_CNTRL1_EOC			BIT(6)
+#define PM8921_ADC_ARB_USRP_CNTRL1_REQ			BIT(7)
+
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL			0x198
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_RSV0		BIT(0)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_RSV1		BIT(1)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_PREMUX0		BIT(2)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_PREMUX1		BIT(3)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL0		BIT(4)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL1		BIT(5)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL2		BIT(6)
+#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL3		BIT(7)
+
+#define PM8921_ADC_ARB_USRP_ANA_PARAM			0x199
+#define PM8921_ADC_ARB_USRP_DIG_PARAM			0x19A
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0	BIT(0)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1	BIT(1)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_CLK_RATE0		BIT(2)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_CLK_RATE1		BIT(3)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_EOC		BIT(4)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0		BIT(5)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1		BIT(6)
+#define PM8921_ADC_ARB_USRP_DIG_PARAM_EN		BIT(7)
+
+#define PM8921_ADC_ARB_USRP_RSV				0x19B
+#define PM8921_ADC_ARB_USRP_RSV_RST			BIT(0)
+#define PM8921_ADC_ARB_USRP_RSV_DTEST0			BIT(1)
+#define PM8921_ADC_ARB_USRP_RSV_DTEST1			BIT(2)
+#define PM8921_ADC_ARB_USRP_RSV_OP			BIT(3)
+#define PM8921_ADC_ARB_USRP_RSV_IP_SEL0			BIT(4)
+#define PM8921_ADC_ARB_USRP_RSV_IP_SEL1			BIT(5)
+#define PM8921_ADC_ARB_USRP_RSV_IP_SEL2			BIT(6)
+#define PM8921_ADC_ARB_USRP_RSV_TRM			BIT(7)
+
+#define PM8921_ADC_ARB_USRP_DATA0			0x19D
+#define PM8921_ADC_ARB_USRP_DATA1			0x19C
+
+#define PM8921_ADC_ARB_BTM_CNTRL1			0x17e
+#define PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM		BIT(0)
+#define PM8921_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE		BIT(1)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1	BIT(2)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2	BIT(3)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3	BIT(4)
+#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4	BIT(5)
+#define PM8921_ADC_ARB_BTM_CNTRL1_EOC			BIT(6)
+#define PM8921_ADC_ARB_BTM_CNTRL1_REQ			BIT(7)
+
+#define PM8921_ADC_ARB_BTM_CNTRL2			0x18c
+#define PM8921_ADC_ARB_BTM_AMUX_CNTRL			0x17f
+#define PM8921_ADC_ARB_BTM_ANA_PARAM			0x180
+#define PM8921_ADC_ARB_BTM_DIG_PARAM			0x181
+#define PM8921_ADC_ARB_BTM_RSV				0x182
+#define PM8921_ADC_ARB_BTM_DATA1			0x183
+#define PM8921_ADC_ARB_BTM_DATA0			0x184
+#define PM8921_ADC_ARB_BTM_BAT_COOL_THR1		0x185
+#define PM8921_ADC_ARB_BTM_BAT_COOL_THR0		0x186
+#define PM8921_ADC_ARB_BTM_BAT_WARM_THR1		0x187
+#define PM8921_ADC_ARB_BTM_BAT_WARM_THR0		0x188
+
+#define PM8921_ADC_ARB_ANA_DIG				0xa0
+#define PM8921_ADC_BTM_RSV				0x10
+#define PM8921_ADC_AMUX_MPP_SEL				2
+#define PM8921_ADC_AMUX_SEL				4
+#define PM8921_ADC_RSV_IP_SEL				4
+#define PM8921_ADC_BTM_CHANNEL_SEL			4
+#define PM8921_MAX_CHANNEL_PROPERTIES			2
+#define PM8921_ADC_IRQ_0				0
+#define PM8921_ADC_IRQ_1				1
+#define PM8921_ADC_IRQ_2				2
+#define PM8921_ADC_BTM_INTERVAL_SEL_MASK		0xF
+#define PM8921_ADC_BTM_INTERVAL_SEL_SHIFT		2
+#define PM8921_ADC_BTM_DECIMATION_SEL			5
+#define PM8921_ADC_MUL					10
+#define PM8921_ADC_CONV_TIME_MIN			2000
+#define PM8921_ADC_CONV_TIME_MAX			2100
+#define PM8921_ADC_MPP_SETTLE_TIME_MIN			200
+#define PM8921_ADC_MPP_SETTLE_TIME_MAX			200
+#define PM8921_ADC_PA_THERM_VREG_UV_MIN			1800000
+#define PM8921_ADC_PA_THERM_VREG_UV_MAX			1800000
+#define PM8921_ADC_PA_THERM_VREG_UA_LOAD		100000
+
+struct pm8921_adc {
+	struct device				*dev;
+	struct pm8921_adc_properties		*adc_prop;
+	int					adc_irq;
+	struct mutex				adc_lock;
+	struct mutex				mpp_adc_lock;
+	spinlock_t				btm_lock;
+	uint32_t				adc_num_channel;
+	uint32_t				adc_num_board_channel;
+	struct completion			adc_rslt_completion;
+	struct pm8921_adc_amux			*adc_channel;
+	struct pm8921_adc_amux_properties	*conv;
+	struct pm8921_adc_arb_btm_param		*batt;
+	int					btm_warm_irq;
+	int					btm_cool_irq;
+	struct dentry				*dent;
+	struct work_struct			warm_work;
+	struct work_struct			cool_work;
+	uint32_t				mpp_base;
+	struct sensor_device_attribute		*sens_attr;
+	struct device				*hwmon;
+};
+
+struct pm8921_adc_amux_properties {
+	uint32_t				amux_channel;
+	uint32_t				decimation;
+	uint32_t				amux_ip_rsv;
+	uint32_t				amux_mpp_channel;
+	struct pm8921_adc_chan_properties	*chan_prop;
+};
+
+static const struct pm8921_adc_scaling_ratio pm8921_amux_scaling_ratio[] = {
+	{1, 1},
+	{1, 3},
+	{1, 4},
+	{1, 6}
+};
+
+static struct pm8921_adc *pmic_adc;
+
+static struct pm8921_adc_scale_fn adc_scale_fn[] = {
+	[ADC_SCALE_DEFAULT] = {pm8921_adc_scale_default},
+	[ADC_SCALE_BATT_THERM] = {pm8921_adc_scale_batt_therm},
+	[ADC_SCALE_PA_THERM] = {pm8921_adc_scale_pa_therm},
+	[ADC_SCALE_PMIC_THERM] = {pm8921_adc_scale_pmic_therm},
+	[ADC_SCALE_XOTHERM] = {pm8921_adc_tdkntcg_therm},
+};
+
+/* MPP 8 is mapped to AMUX8 and is common between remote processor's */
+
+static struct pm8xxx_mpp_config_data pm8921_adc_mpp_config = {
+	.type		= PM8XXX_MPP_TYPE_A_INPUT,
+	/* AMUX6 is dedicated to be used for apps processor */
+	.level		= PM8XXX_MPP_AIN_AMUX_CH6,
+	.control	= PM8XXX_MPP_AOUT_CTRL_DISABLE,
+};
+
+/* MPP Configuration for default settings */
+static struct pm8xxx_mpp_config_data pm8921_adc_mpp_unconfig = {
+	.type		= PM8XXX_MPP_TYPE_SINK,
+	.level		= PM8XXX_MPP_AIN_AMUX_CH5,
+	.control	= PM8XXX_MPP_AOUT_CTRL_DISABLE,
+};
+
+static bool pm8921_adc_calib_first_adc;
+static bool pm8921_adc_initialized, pm8921_adc_calib_device_init;
+
+static int32_t pm8921_adc_arb_cntrl(uint32_t arb_cntrl)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int i, rc;
+	u8 data_arb_cntrl = 0;
+
+	if (arb_cntrl)
+		data_arb_cntrl |= PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB;
+
+	/* Write twice to the CNTRL register for the arbiter settings
+	   to take into effect */
+	for (i = 0; i < 2; i++) {
+		rc = pm8xxx_writeb(adc_pmic->dev->parent,
+				PM8921_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
+		if (rc < 0) {
+			pr_err("PM8921 arb cntrl write failed with %d\n", rc);
+			return rc;
+		}
+	}
+
+	if (arb_cntrl) {
+		data_arb_cntrl |= PM8921_ADC_ARB_USRP_CNTRL1_REQ;
+		rc = pm8xxx_writeb(adc_pmic->dev->parent,
+			PM8921_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
+	}
+
+	return 0;
+}
+
+static int32_t pm8921_adc_patherm_power(bool on)
+{
+	static struct regulator *pa_therm;
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int rc = 0;
+	if (on) {
+		pa_therm = regulator_get(adc_pmic->dev,
+						"pa_therm");
+		if (IS_ERR(pa_therm)) {
+			rc = PTR_ERR(pa_therm);
+			pr_err("failed to request pa_therm vreg "
+					"with error %d\n", rc);
+			return rc;
+		}
+
+		rc = regulator_set_voltage(pa_therm,
+				PM8921_ADC_PA_THERM_VREG_UV_MIN,
+				PM8921_ADC_PA_THERM_VREG_UV_MAX);
+		if (rc < 0) {
+			pr_err("failed to set the voltage for "
+					"pa_therm with error %d\n", rc);
+			goto fail;
+		}
+
+		rc = regulator_set_optimum_mode(pa_therm,
+				PM8921_ADC_PA_THERM_VREG_UA_LOAD);
+		if (rc < 0) {
+			pr_err("failed to set optimum mode for "
+					"pa_therm with error %d\n", rc);
+			goto fail;
+		}
+
+		if (regulator_enable(pa_therm)) {
+			pr_err("failed to enable pa_therm vreg with "
+						"error %d\n", rc);
+			goto fail;
+		}
+	} else {
+		if (pa_therm != NULL) {
+			regulator_disable(pa_therm);
+			regulator_put(pa_therm);
+		}
+	}
+
+	return rc;
+fail:
+	regulator_put(pa_therm);
+	return rc;
+}
+
+static int32_t pm8921_adc_channel_power_enable(uint32_t channel,
+							bool power_cntrl)
+{
+	int rc = 0;
+
+	switch (channel)
+	case ADC_MPP_1_AMUX8:
+		pm8921_adc_patherm_power(power_cntrl);
+
+	return rc;
+}
+
+
+static uint32_t pm8921_adc_read_reg(uint32_t reg, u8 *data)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int rc;
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent, reg, data);
+	if (rc < 0) {
+		pr_err("PM8921 adc read reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static uint32_t pm8921_adc_write_reg(uint32_t reg, u8 data)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int rc;
+
+	rc = pm8xxx_writeb(adc_pmic->dev->parent, reg, data);
+	if (rc < 0) {
+		pr_err("PM8921 adc write reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int32_t pm8921_adc_configure(
+				struct pm8921_adc_amux_properties *chan_prop)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	u8 data_amux_chan = 0, data_arb_rsv = 0, data_dig_param = 0;
+	int rc;
+
+	data_amux_chan |= chan_prop->amux_channel << PM8921_ADC_AMUX_SEL;
+
+	if (chan_prop->amux_mpp_channel)
+		data_amux_chan |= chan_prop->amux_mpp_channel <<
+					PM8921_ADC_AMUX_MPP_SEL;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_AMUX_CNTRL,
+							data_amux_chan);
+	if (rc < 0)
+		return rc;
+
+	data_arb_rsv &= (PM8921_ADC_ARB_USRP_RSV_RST |
+		PM8921_ADC_ARB_USRP_RSV_DTEST0 |
+		PM8921_ADC_ARB_USRP_RSV_DTEST1 |
+		PM8921_ADC_ARB_USRP_RSV_OP |
+		PM8921_ADC_ARB_USRP_RSV_TRM);
+	data_arb_rsv |= chan_prop->amux_ip_rsv << PM8921_ADC_RSV_IP_SEL;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_RSV, data_arb_rsv);
+	if (rc < 0)
+		return rc;
+
+	rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_DIG_PARAM,
+							&data_dig_param);
+	if (rc < 0)
+		return rc;
+
+	/* Default 2.4Mhz clock rate */
+	/* Client chooses the decimation */
+	switch (chan_prop->decimation) {
+	case ADC_DECIMATION_TYPE1:
+		data_dig_param |= PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
+		break;
+	case ADC_DECIMATION_TYPE2:
+		data_dig_param |= (PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0
+				| PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1);
+		break;
+	default:
+		data_dig_param |= PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
+		break;
+	}
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_DIG_PARAM,
+						PM8921_ADC_ARB_ANA_DIG);
+	if (rc < 0)
+		return rc;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_ANA_PARAM,
+						PM8921_ADC_ARB_ANA_DIG);
+	if (rc < 0)
+		return rc;
+
+	if (!pm8921_adc_calib_first_adc)
+		enable_irq(adc_pmic->adc_irq);
+
+	rc = pm8921_adc_arb_cntrl(1);
+	if (rc < 0) {
+		pr_err("Configuring ADC Arbiter"
+				"enable failed with %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static uint32_t pm8921_adc_read_adc_code(int32_t *data)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	uint8_t rslt_lsb, rslt_msb;
+	int32_t rc, max_ideal_adc_code = 1 << adc_pmic->adc_prop->bitresolution;
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent,
+				PM8921_ADC_ARB_USRP_DATA0, &rslt_lsb);
+	if (rc < 0) {
+		pr_err("PM8921 adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = pm8xxx_readb(adc_pmic->dev->parent,
+				PM8921_ADC_ARB_USRP_DATA1, &rslt_msb);
+	if (rc < 0) {
+		pr_err("PM8921 adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	*data = (rslt_msb << 8) | rslt_lsb;
+
+	/* Use the midpoint to determine underflow or overflow */
+	if (*data > max_ideal_adc_code + (max_ideal_adc_code >> 1))
+		*data |= ((1 << (8 * sizeof(*data) -
+			adc_pmic->adc_prop->bitresolution)) - 1) <<
+			adc_pmic->adc_prop->bitresolution;
+
+	/* Default value for switching off the arbiter after reading
+	   the ADC value. Bit 0 set to 0. */
+	rc = pm8921_adc_arb_cntrl(0);
+	if (rc < 0) {
+		pr_err("%s: Configuring ADC Arbiter disable"
+					"failed\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void pm8921_adc_btm_warm_scheduler_fn(struct work_struct *work)
+{
+	struct pm8921_adc *adc_pmic = container_of(work, struct pm8921_adc,
+					warm_work);
+	unsigned long flags = 0;
+	bool warm_status;
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+	warm_status = irq_read_line(adc_pmic->btm_warm_irq);
+	if (adc_pmic->batt->btm_warm_fn != NULL)
+		adc_pmic->batt->btm_warm_fn(warm_status);
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+}
+
+static void pm8921_adc_btm_cool_scheduler_fn(struct work_struct *work)
+{
+	struct pm8921_adc *adc_pmic = container_of(work, struct pm8921_adc,
+					cool_work);
+	unsigned long flags = 0;
+	bool cool_status;
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+	cool_status = irq_read_line(adc_pmic->btm_cool_irq);
+	if (adc_pmic->batt->btm_cool_fn != NULL)
+		adc_pmic->batt->btm_cool_fn(cool_status);
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+}
+
+static irqreturn_t pm8921_adc_isr(int irq, void *dev_id)
+{
+	struct pm8921_adc *adc_8921 = dev_id;
+
+	disable_irq_nosync(adc_8921->adc_irq);
+
+	if (pm8921_adc_calib_first_adc)
+		return IRQ_HANDLED;
+	/* TODO Handle spurius interrupt condition */
+	complete(&adc_8921->adc_rslt_completion);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_btm_warm_isr(int irq, void *dev_id)
+{
+	struct pm8921_adc *btm_8921 = dev_id;
+
+	schedule_work(&btm_8921->warm_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pm8921_btm_cool_isr(int irq, void *dev_id)
+{
+	struct pm8921_adc *btm_8921 = dev_id;
+
+	schedule_work(&btm_8921->cool_work);
+
+	return IRQ_HANDLED;
+}
+
+static uint32_t pm8921_adc_calib_device(void)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	struct pm8921_adc_amux_properties conv;
+	int rc, offset_adc, slope_adc, calib_read_1, calib_read_2;
+	u8 data_arb_usrp_cntrl1 = 0;
+
+	conv.amux_channel = CHANNEL_125V;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV1;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8921_adc_calib_first_adc = true;
+	rc = pm8921_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8921_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8921_ADC_CONV_TIME_MIN,
+					PM8921_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8921_adc_read_adc_code(&calib_read_1);
+	if (rc) {
+		pr_err("pm8921_adc read adc failed with %d\n", rc);
+		pm8921_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8921_adc_calib_first_adc = false;
+
+	conv.amux_channel = CHANNEL_625MV;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV1;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8921_adc_calib_first_adc = true;
+	rc = pm8921_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8921_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8921_ADC_CONV_TIME_MIN,
+					PM8921_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8921_adc_read_adc_code(&calib_read_2);
+	if (rc) {
+		pr_err("pm8921_adc read adc failed with %d\n", rc);
+		pm8921_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8921_adc_calib_first_adc = false;
+
+	slope_adc = (((calib_read_1 - calib_read_2) << PM8921_ADC_MUL)/
+					PM8921_CHANNEL_ADC_625_MV);
+	offset_adc = calib_read_2 -
+			((slope_adc * PM8921_CHANNEL_ADC_625_MV) >>
+							PM8921_ADC_MUL);
+
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].offset
+								= offset_adc;
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dy =
+					(calib_read_1 - calib_read_2);
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dx
+						= PM8921_CHANNEL_ADC_625_MV;
+	rc = pm8921_adc_arb_cntrl(0);
+	if (rc < 0) {
+		pr_err("%s: Configuring ADC Arbiter disable"
+					"failed\n", __func__);
+		return rc;
+	}
+	/* Ratiometric Calibration */
+	conv.amux_channel = CHANNEL_MUXOFF;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV5;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8921_adc_calib_first_adc = true;
+	rc = pm8921_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8921_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8921_ADC_CONV_TIME_MIN,
+					PM8921_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8921_adc_read_adc_code(&calib_read_1);
+	if (rc) {
+		pr_err("pm8921_adc read adc failed with %d\n", rc);
+		pm8921_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8921_adc_calib_first_adc = false;
+
+	conv.amux_channel = CHANNEL_MUXOFF;
+	conv.decimation = ADC_DECIMATION_TYPE2;
+	conv.amux_ip_rsv = AMUX_RSV4;
+	conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
+	pm8921_adc_calib_first_adc = true;
+	rc = pm8921_adc_configure(&conv);
+	if (rc) {
+		pr_err("pm8921_adc configure failed with %d\n", rc);
+		goto calib_fail;
+	}
+
+	while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
+					PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
+		rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
+					&data_arb_usrp_cntrl1);
+		if (rc < 0)
+			return rc;
+		usleep_range(PM8921_ADC_CONV_TIME_MIN,
+					PM8921_ADC_CONV_TIME_MAX);
+	}
+	data_arb_usrp_cntrl1 = 0;
+
+	rc = pm8921_adc_read_adc_code(&calib_read_2);
+	if (rc) {
+		pr_err("pm8921_adc read adc failed with %d\n", rc);
+		pm8921_adc_calib_first_adc = false;
+		goto calib_fail;
+	}
+	pm8921_adc_calib_first_adc = false;
+
+	slope_adc = (((calib_read_1 - calib_read_2) << PM8921_ADC_MUL)/
+				adc_pmic->adc_prop->adc_vdd_reference);
+	offset_adc = calib_read_2 -
+			((slope_adc * adc_pmic->adc_prop->adc_vdd_reference)
+							>> PM8921_ADC_MUL);
+
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].offset
+								= offset_adc;
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dy =
+					(calib_read_1 - calib_read_2);
+	adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dx =
+					adc_pmic->adc_prop->adc_vdd_reference;
+calib_fail:
+	rc = pm8921_adc_arb_cntrl(0);
+	if (rc < 0) {
+		pr_err("%s: Configuring ADC Arbiter disable"
+					"failed\n", __func__);
+	}
+
+	return rc;
+}
+
+uint32_t pm8921_adc_read(enum pm8921_adc_channels channel,
+				struct pm8921_adc_chan_result *result)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int i = 0, rc = 0, rc_fail, amux_prescaling, scale_type;
+	enum pm8921_adc_premux_mpp_scale_type mpp_scale;
+
+	if (!pm8921_adc_initialized)
+		return -ENODEV;
+
+	if (!pm8921_adc_calib_device_init) {
+		if (pm8921_adc_calib_device() == 0)
+			pm8921_adc_calib_device_init = true;
+	}
+
+	mutex_lock(&adc_pmic->adc_lock);
+
+	for (i = 0; i < adc_pmic->adc_num_channel; i++) {
+		if (channel == adc_pmic->adc_channel[i].channel_name)
+			break;
+	}
+
+	if (i == adc_pmic->adc_num_channel) {
+		rc = -EBADF;
+		goto fail_unlock;
+	}
+
+	if (channel < PM8921_CHANNEL_MPP_SCALE1_IDX) {
+		mpp_scale = PREMUX_MPP_SCALE_0;
+		adc_pmic->conv->amux_channel = channel;
+	} else if (channel >= PM8921_CHANNEL_MPP_SCALE1_IDX) {
+		mpp_scale = PREMUX_MPP_SCALE_1;
+		adc_pmic->conv->amux_channel = channel %
+				PM8921_CHANNEL_MPP_SCALE1_IDX;
+	} else if (channel >= PM8921_CHANNEL_MPP_SCALE3_IDX) {
+		mpp_scale = PREMUX_MPP_SCALE_1_DIV3;
+		adc_pmic->conv->amux_channel = channel %
+				PM8921_CHANNEL_MPP_SCALE3_IDX;
+	}
+
+	adc_pmic->conv->amux_mpp_channel = mpp_scale;
+	adc_pmic->conv->amux_ip_rsv = adc_pmic->adc_channel[i].adc_rsv;
+	adc_pmic->conv->decimation = adc_pmic->adc_channel[i].adc_decimation;
+	amux_prescaling = adc_pmic->adc_channel[i].chan_path_prescaling;
+
+	adc_pmic->conv->chan_prop->offset_gain_numerator =
+		pm8921_amux_scaling_ratio[amux_prescaling].num;
+	adc_pmic->conv->chan_prop->offset_gain_denominator =
+		 pm8921_amux_scaling_ratio[amux_prescaling].den;
+
+	rc = pm8921_adc_channel_power_enable(channel, true);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	rc = pm8921_adc_configure(adc_pmic->conv);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	wait_for_completion(&adc_pmic->adc_rslt_completion);
+
+	rc = pm8921_adc_read_adc_code(&result->adc_code);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	scale_type = adc_pmic->adc_channel[i].adc_scale_fn;
+	if (scale_type >= ADC_SCALE_NONE) {
+		rc = -EBADF;
+		goto fail;
+	}
+
+	adc_scale_fn[scale_type].chan(result->adc_code,
+			adc_pmic->adc_prop, adc_pmic->conv->chan_prop, result);
+
+	rc = pm8921_adc_channel_power_enable(channel, false);
+	if (rc) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	mutex_unlock(&adc_pmic->adc_lock);
+
+	return 0;
+fail:
+	rc_fail = pm8921_adc_channel_power_enable(channel, false);
+	if (rc_fail)
+		pr_err("pm8921 adc power disable failed\n");
+fail_unlock:
+	mutex_unlock(&adc_pmic->adc_lock);
+	pr_err("pm8921 adc error with %d\n", rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_read);
+
+uint32_t pm8921_adc_mpp_config_read(uint32_t mpp_num,
+			enum pm8921_adc_channels channel,
+			struct pm8921_adc_chan_result *result)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int rc = 0;
+
+	if (!adc_pmic->mpp_base) {
+		rc = -EINVAL;
+		pr_info("PM8921 MPP base invalid with error %d\n", rc);
+		return rc;
+	}
+
+	if (mpp_num == PM8921_AMUX_MPP_8) {
+		rc = -EINVAL;
+		pr_info("PM8921 MPP8 is already configured "
+			"to AMUX8. Use pm8921_adc_read() instead.\n");
+		return rc;
+	}
+
+	mutex_lock(&adc_pmic->mpp_adc_lock);
+
+	rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
+					&pm8921_adc_mpp_config);
+	if (rc < 0) {
+		pr_err("pm8921 adc mpp config error with %d\n", rc);
+		goto fail;
+	}
+
+	usleep_range(PM8921_ADC_MPP_SETTLE_TIME_MIN,
+					PM8921_ADC_MPP_SETTLE_TIME_MAX);
+
+	rc = pm8921_adc_read(channel, result);
+	if (rc < 0)
+		pr_err("pm8921 adc read error with %d\n", rc);
+
+	rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
+					&pm8921_adc_mpp_unconfig);
+	if (rc < 0)
+		pr_err("pm8921 adc mpp config error with %d\n", rc);
+fail:
+	mutex_unlock(&adc_pmic->mpp_adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_mpp_config_read);
+
+uint32_t pm8921_adc_btm_configure(struct pm8921_adc_arb_btm_param *btm_param)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	u8 data_btm_cool_thr0, data_btm_cool_thr1;
+	u8 data_btm_warm_thr0, data_btm_warm_thr1;
+	u8 arb_btm_cntrl1;
+	unsigned long flags = 0;
+	int rc;
+
+	if (adc_pmic == NULL) {
+		pr_err("PMIC ADC not valid\n");
+		return -EINVAL;
+	}
+
+	if ((btm_param->btm_cool_fn == NULL) &&
+		(btm_param->btm_warm_fn == NULL)) {
+		pr_err("No BTM warm/cool notification??\n");
+		return -EINVAL;
+	}
+
+	rc = pm8921_adc_batt_scaler(btm_param);
+	if (rc < 0) {
+		pr_err("Failed to lookup the BTM thresholds\n");
+		return rc;
+	}
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+
+	data_btm_cool_thr0 = ((btm_param->low_thr_voltage << 24) >> 24);
+	data_btm_cool_thr1 = ((btm_param->low_thr_voltage << 16) >> 24);
+	data_btm_warm_thr0 = ((btm_param->high_thr_voltage << 24) >> 24);
+	data_btm_warm_thr1 = ((btm_param->high_thr_voltage << 16) >> 24);
+
+	if (btm_param->btm_cool_fn != NULL) {
+		rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_COOL_THR0,
+							data_btm_cool_thr0);
+		if (rc < 0)
+			goto write_err;
+
+		rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_COOL_THR1,
+							data_btm_cool_thr1);
+		if (rc < 0)
+			goto write_err;
+
+		adc_pmic->batt->btm_cool_fn = btm_param->btm_cool_fn;
+	}
+
+	if (btm_param->btm_warm_fn != NULL) {
+		rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_WARM_THR0,
+							data_btm_warm_thr0);
+		if (rc < 0)
+			goto write_err;
+
+		rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_WARM_THR1,
+							data_btm_warm_thr1);
+		if (rc < 0)
+			goto write_err;
+
+		adc_pmic->batt->btm_warm_fn = btm_param->btm_warm_fn;
+	}
+
+	rc = pm8921_adc_read_reg(PM8921_ADC_ARB_BTM_CNTRL1, &arb_btm_cntrl1);
+	if (rc < 0)
+		goto bail_out;
+
+	btm_param->interval &= PM8921_ADC_BTM_INTERVAL_SEL_MASK;
+	arb_btm_cntrl1 |=
+		btm_param->interval << PM8921_ADC_BTM_INTERVAL_SEL_SHIFT;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1, arb_btm_cntrl1);
+	if (rc < 0)
+		goto write_err;
+
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+
+	return rc;
+bail_out:
+write_err:
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+	pr_debug("%s: with error code %d\n", __func__, rc);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_btm_configure);
+
+static uint32_t pm8921_adc_btm_read(uint32_t channel)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int rc, i;
+	u8 arb_btm_dig_param, arb_btm_ana_param, arb_btm_rsv;
+	u8 arb_btm_amux_cntrl, data_arb_btm_cntrl = 0;
+	unsigned long flags;
+
+	arb_btm_amux_cntrl = channel << PM8921_ADC_BTM_CHANNEL_SEL;
+	arb_btm_rsv = adc_pmic->adc_channel[channel].adc_rsv;
+	arb_btm_dig_param = arb_btm_ana_param = PM8921_ADC_ARB_ANA_DIG;
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_AMUX_CNTRL,
+						arb_btm_amux_cntrl);
+	if (rc < 0)
+		goto write_err;
+
+	arb_btm_rsv = PM8921_ADC_BTM_RSV;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_RSV, arb_btm_rsv);
+	if (rc < 0)
+		goto write_err;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_DIG_PARAM,
+						arb_btm_dig_param);
+	if (rc < 0)
+		goto write_err;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_ANA_PARAM,
+						arb_btm_ana_param);
+	if (rc < 0)
+		goto write_err;
+
+	data_arb_btm_cntrl |= PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM;
+
+	for (i = 0; i < 2; i++) {
+		rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
+						data_arb_btm_cntrl);
+		if (rc < 0)
+			goto write_err;
+	}
+
+	data_arb_btm_cntrl |= PM8921_ADC_ARB_BTM_CNTRL1_REQ
+				| PM8921_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE;
+
+	rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
+					data_arb_btm_cntrl);
+	if (rc < 0)
+		goto write_err;
+
+	if (pmic_adc->batt->btm_warm_fn != NULL)
+		enable_irq(adc_pmic->btm_warm_irq);
+
+	if (pmic_adc->batt->btm_cool_fn != NULL)
+		enable_irq(adc_pmic->btm_cool_irq);
+
+write_err:
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+	return rc;
+}
+
+uint32_t pm8921_adc_btm_start(void)
+{
+	return pm8921_adc_btm_read(CHANNEL_BATT_THERM);
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_btm_start);
+
+uint32_t pm8921_adc_btm_end(void)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int i, rc;
+	u8 data_arb_btm_cntrl;
+	unsigned long flags;
+
+	disable_irq_nosync(adc_pmic->btm_warm_irq);
+	disable_irq_nosync(adc_pmic->btm_cool_irq);
+
+	spin_lock_irqsave(&adc_pmic->btm_lock, flags);
+	/* Set BTM registers to Disable mode */
+	rc = pm8921_adc_read_reg(PM8921_ADC_ARB_BTM_CNTRL1,
+						&data_arb_btm_cntrl);
+	if (rc < 0) {
+		spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+		return rc;
+	}
+
+	data_arb_btm_cntrl |= ~PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM;
+	/* Write twice to the CNTRL register for the arbiter settings
+	   to take into effect */
+	for (i = 0; i < 2; i++) {
+		rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
+							data_arb_btm_cntrl);
+		if (rc < 0) {
+			spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+			return rc;
+		}
+	}
+
+	spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pm8921_adc_btm_end);
+
+static ssize_t pm8921_adc_show(struct device *dev,
+			struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	struct pm8921_adc_chan_result result;
+	int rc = -1;
+
+	if (attr->index < adc_pmic->adc_num_channel)
+		rc = pm8921_adc_read(attr->index, &result);
+
+	if (rc)
+		return 0;
+
+	return snprintf(buf, sizeof(struct pm8921_adc_chan_result),
+				"Result:%lld Raw:%d\n",
+				result.physical, result.adc_code);
+}
+
+static int get_adc(void *data, u64 *val)
+{
+	struct pm8921_adc_chan_result result;
+	int i = (int)data;
+	int rc;
+
+	rc = pm8921_adc_read(i, &result);
+	if (!rc)
+		pr_info("ADC value raw:%x physical:%lld\n",
+			result.adc_code, result.physical);
+	*val = result.physical;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_adc, NULL, "%llu\n");
+
+static int get_mpp_adc(void *data, u64 *val)
+{
+	struct pm8921_adc_chan_result result;
+	int i = (int)data;
+	int rc;
+
+	rc = pm8921_adc_mpp_config_read(i,
+		ADC_MPP_1_AMUX6, &result);
+	if (!rc)
+		pr_info("ADC MPP value raw:%x physical:%lld\n",
+			result.adc_code, result.physical);
+	*val = result.physical;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(reg_mpp_fops, get_mpp_adc, NULL, "%llu\n");
+
+#ifdef CONFIG_DEBUG_FS
+static void create_debugfs_entries(void)
+{
+	pmic_adc->dent = debugfs_create_dir("pm8921_adc", NULL);
+
+	if (IS_ERR(pmic_adc->dent)) {
+		pr_err("pmic adc debugfs dir not created\n");
+		return;
+	}
+
+	debugfs_create_file("vbat", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_VBAT, &reg_fops);
+	debugfs_create_file("625mv", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_625MV, &reg_fops);
+	debugfs_create_file("125v", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_125V, &reg_fops);
+	debugfs_create_file("die_temp", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_DIE_TEMP, &reg_fops);
+	debugfs_create_file("vcoin", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_VCOIN, &reg_fops);
+	debugfs_create_file("dc_in", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_DCIN, &reg_fops);
+	debugfs_create_file("vph_pwr", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_VPH_PWR, &reg_fops);
+	debugfs_create_file("usb_in", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_USBIN, &reg_fops);
+	debugfs_create_file("batt_therm", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_BATT_THERM, &reg_fops);
+	debugfs_create_file("batt_id", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_BATT_ID, &reg_fops);
+	debugfs_create_file("chg_temp", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_CHG_TEMP, &reg_fops);
+	debugfs_create_file("charger_current", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_ICHG, &reg_fops);
+	debugfs_create_file("ibat", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_IBAT, &reg_fops);
+	debugfs_create_file("pa_therm1", 0644, pmic_adc->dent,
+			    (void *)ADC_MPP_1_AMUX8, &reg_fops);
+	debugfs_create_file("xo_therm", 0644, pmic_adc->dent,
+			    (void *)CHANNEL_MUXOFF, &reg_fops);
+	debugfs_create_file("pa_therm0", 0644, pmic_adc->dent,
+			    (void *)ADC_MPP_1_AMUX3, &reg_fops);
+}
+#else
+static inline void create_debugfs_entries(void)
+{
+}
+#endif
+static struct sensor_device_attribute pm8921_adc_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, pm8921_adc_show, NULL, 0);
+
+static int32_t pm8921_adc_init_hwmon(struct platform_device *pdev)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int rc = 0, i;
+
+	adc_pmic->sens_attr = kzalloc(pmic_adc->adc_num_board_channel *
+		sizeof(struct sensor_device_attribute), GFP_KERNEL);
+
+	if (!adc_pmic->sens_attr) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto hwmon_err_sens;
+	}
+
+	for (i = 0; i < pmic_adc->adc_num_board_channel; i++) {
+		pm8921_adc_attr.index = adc_pmic->adc_channel[i].channel_name;
+		pm8921_adc_attr.dev_attr.attr.name =
+						adc_pmic->adc_channel[i].name;
+		memcpy(&adc_pmic->sens_attr[i], &pm8921_adc_attr,
+						sizeof(pm8921_adc_attr));
+		rc = device_create_file(&pdev->dev,
+				&adc_pmic->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed for "
+					    "dev %s\n",
+					    adc_pmic->adc_channel[i].name);
+			goto hwmon_err_sens;
+		}
+	}
+	return 0;
+
+hwmon_err_sens:
+	pr_info("Init HWMON failed for pm8921_adc with %d\n", rc);
+	return rc;
+}
+
+static int __devexit pm8921_adc_teardown(struct platform_device *pdev)
+{
+	struct pm8921_adc *adc_pmic = pmic_adc;
+	int i;
+
+	free_irq(adc_pmic->adc_irq, adc_pmic);
+	free_irq(adc_pmic->btm_warm_irq, adc_pmic);
+	free_irq(adc_pmic->btm_cool_irq, adc_pmic);
+	platform_set_drvdata(pdev, NULL);
+	pmic_adc = NULL;
+	kfree(adc_pmic->conv->chan_prop);
+	kfree(adc_pmic->adc_channel);
+	kfree(adc_pmic->batt);
+	for (i = 0; i < adc_pmic->adc_num_board_channel; i++)
+		device_remove_file(adc_pmic->dev,
+				&adc_pmic->sens_attr[i].dev_attr);
+	kfree(adc_pmic->sens_attr);
+	kfree(adc_pmic);
+	pm8921_adc_initialized = false;
+
+	return 0;
+}
+
+static int __devinit pm8921_adc_probe(struct platform_device *pdev)
+{
+	const struct pm8921_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct pm8921_adc *adc_pmic;
+	struct pm8921_adc_amux_properties *adc_amux_prop;
+	struct pm8921_adc_chan_properties *adc_pmic_chanprop;
+	struct pm8921_adc_arb_btm_param *adc_btm;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	adc_pmic = kzalloc(sizeof(struct pm8921_adc),
+						GFP_KERNEL);
+	if (!adc_pmic) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_amux_prop = kzalloc(sizeof(struct pm8921_adc_amux_properties),
+						GFP_KERNEL);
+	if (!adc_amux_prop) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_pmic_chanprop = kzalloc(sizeof(struct pm8921_adc_chan_properties),
+						GFP_KERNEL);
+	if (!adc_pmic_chanprop) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_btm = kzalloc(sizeof(struct pm8921_adc_arb_btm_param),
+						GFP_KERNEL);
+	if (!adc_btm) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_pmic->dev = &pdev->dev;
+	adc_pmic->adc_prop = pdata->adc_prop;
+	adc_pmic->conv = adc_amux_prop;
+	adc_pmic->conv->chan_prop = adc_pmic_chanprop;
+	adc_pmic->batt = adc_btm;
+
+	init_completion(&adc_pmic->adc_rslt_completion);
+	adc_pmic->adc_channel = pdata->adc_channel;
+	adc_pmic->adc_num_board_channel = pdata->adc_num_board_channel;
+	adc_pmic->adc_num_channel = ADC_MPP_2_CHANNEL_NONE;
+	adc_pmic->mpp_base = pdata->adc_mpp_base;
+
+	mutex_init(&adc_pmic->adc_lock);
+	mutex_init(&adc_pmic->mpp_adc_lock);
+	spin_lock_init(&adc_pmic->btm_lock);
+
+	adc_pmic->adc_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_0);
+	if (adc_pmic->adc_irq < 0) {
+		rc = -ENXIO;
+		goto err_cleanup;
+	}
+
+	rc = request_irq(adc_pmic->adc_irq,
+				pm8921_adc_isr,
+		IRQF_TRIGGER_RISING, "pm8921_adc_interrupt", adc_pmic);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to request adc irq "
+						"with error %d\n", rc);
+		goto err_cleanup;
+	}
+
+	disable_irq_nosync(adc_pmic->adc_irq);
+
+	adc_pmic->btm_warm_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_1);
+	if (adc_pmic->btm_warm_irq < 0) {
+		rc = -ENXIO;
+		goto err_cleanup;
+	}
+
+	rc = request_irq(adc_pmic->btm_warm_irq,
+				pm8921_btm_warm_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			"pm8921_btm_warm_interrupt", adc_pmic);
+	if (rc) {
+		pr_err("btm warm irq failed %d with interrupt number %d\n",
+						rc, adc_pmic->btm_warm_irq);
+		dev_err(&pdev->dev, "failed to request btm irq\n");
+		goto err_cleanup;
+	}
+
+	disable_irq_nosync(adc_pmic->btm_warm_irq);
+
+	adc_pmic->btm_cool_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_2);
+	if (adc_pmic->btm_cool_irq < 0) {
+		rc = -ENXIO;
+		goto err_cleanup;
+	}
+
+	rc = request_irq(adc_pmic->btm_cool_irq,
+				pm8921_btm_cool_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			"pm8921_btm_cool_interrupt", adc_pmic);
+	if (rc) {
+		pr_err("btm cool irq failed with return %d and number %d\n",
+						rc, adc_pmic->btm_cool_irq);
+		dev_err(&pdev->dev, "failed to request btm irq\n");
+		goto err_cleanup;
+	}
+
+	disable_irq_nosync(adc_pmic->btm_cool_irq);
+	platform_set_drvdata(pdev, adc_pmic);
+	pmic_adc = adc_pmic;
+
+	INIT_WORK(&adc_pmic->warm_work, pm8921_adc_btm_warm_scheduler_fn);
+	INIT_WORK(&adc_pmic->cool_work, pm8921_adc_btm_cool_scheduler_fn);
+	create_debugfs_entries();
+	pm8921_adc_calib_first_adc = false;
+	pm8921_adc_calib_device_init = false;
+	pm8921_adc_initialized = true;
+
+	rc = pm8921_adc_init_hwmon(pdev);
+	if (rc) {
+		pr_err("pm8921 adc init hwmon failed with %d\n", rc);
+		goto err_cleanup;
+	}
+	adc_pmic->hwmon = hwmon_device_register(adc_pmic->dev);
+	return 0;
+
+err_cleanup:
+	pm8921_adc_teardown(pdev);
+	return rc;
+}
+
+static struct platform_driver pm8921_adc_driver = {
+	.probe	= pm8921_adc_probe,
+	.remove	= __devexit_p(pm8921_adc_teardown),
+	.driver	= {
+		.name	= PM8921_ADC_DEV_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init pm8921_adc_init(void)
+{
+	return platform_driver_register(&pm8921_adc_driver);
+}
+module_init(pm8921_adc_init);
+
+static void __exit pm8921_adc_exit(void)
+{
+	platform_driver_unregister(&pm8921_adc_driver);
+}
+module_exit(pm8921_adc_exit);
+
+MODULE_ALIAS("platform:" PM8921_ADC_DEV_NAME);
+MODULE_DESCRIPTION("PMIC8921 ADC driver");
+MODULE_LICENSE("GPL v2");