Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5f888f7..ef3cb2e 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -28,6 +28,14 @@
 	tristate
 	default n
 
+config SENSORS_WPCE775X
+	bool "Winbond WPCE775X"
+	depends on I2C
+	default n
+	help
+	  This driver provides support for the Winbond WPCE775XX Embedded
+	  Controller, which provides lcd backlight, LEDs, and Battery control.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	default n
@@ -767,6 +775,19 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called max6650.
 
+config SENSORS_MSM_ADC
+	tristate "MSM ADC Driver for current measurement"
+	depends on ARCH_MSM7X30 || ARCH_MSM8X60 || ARCH_FSM9XXX
+	default n
+	help
+	  Provides interface for measuring the ADC's on AMUX channels of XOADC,
+	  MPP's and the XOTHERM on pmic8058 for msm8x60 and provides post processing
+	  of the channel for the ADC Raw Data. For reading LTC and EPM ADC channels
+	  say yes here to include support for measuring current in real-time
+	  from various power-rails on the Fluid board.  The ADC circuit
+	  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_PC87360
 	tristate "National Semiconductor PC87360 family"
 	select HWMON_VID
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 28061cf..eacfcb5 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -118,6 +118,8 @@
 obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_WPCE775X)	+= wpce775x.o
+obj-$(CONFIG_SENSORS_MSM_ADC)	+= msm_adc.o m_adcproc.o
 
 # PMBus drivers
 obj-$(CONFIG_PMBUS)		+= pmbus_core.o
diff --git a/drivers/hwmon/m_adcproc.c b/drivers/hwmon/m_adcproc.c
new file mode 100644
index 0000000..70e505e
--- /dev/null
+++ b/drivers/hwmon/m_adcproc.c
@@ -0,0 +1,469 @@
+/* Copyright (c) 2010-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/msm_adc.h>
+
+#define KELVINMIL_DEGMIL	273160
+
+static const struct adc_map_pt adcmap_batttherm[] = {
+	{2020,	-30},
+	{1923,	-20},
+	{1796,	-10},
+	{1640,	  0},
+	{1459,	 10},
+	{1260,	 20},
+	{1159,	 25},
+	{1059,	 30},
+	{871,	 40},
+	{706,	 50},
+	{567,	 60},
+	{453,	 70},
+	{364,	 80}
+};
+
+static const struct adc_map_pt adcmap_msmtherm[] = {
+	{2150,	-30},
+	{2107,	-20},
+	{2037,	-10},
+	{1929,	  0},
+	{1776,	 10},
+	{1579,	 20},
+	{1467,	 25},
+	{1349,	 30},
+	{1108,	 40},
+	{878,	 50},
+	{677,	 60},
+	{513,	 70},
+	{385,	 80},
+	{287,	 90},
+	{215,	100},
+	{186,	110},
+	{107,	120}
+};
+
+static const struct adc_map_pt adcmap_ntcg104ef104fb[] = {
+	{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
+	adc_map_linear(const struct 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 scale_default(int32_t adc_code,
+				const struct adc_properties *adc_properties,
+				const struct chan_properties *chan_properties,
+				struct adc_chan_result *adc_chan_result)
+{
+	bool negative_rawfromoffset = 0;
+	int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
+
+	if (!chan_properties->gain_numerator ||
+		!chan_properties->gain_denominator)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	if (rawfromoffset < 0) {
+		if (adc_properties->bipolar) {
+			rawfromoffset = (rawfromoffset ^ -1) +  1;
+			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->dx*
+					chan_properties->gain_denominator;
+
+	/* do_div only perform positive integer division! */
+	do_div(adc_chan_result->measurement, chan_properties->adc_graph->dy*
+					chan_properties->gain_numerator);
+
+	if (negative_rawfromoffset)
+		adc_chan_result->measurement =
+		(adc_chan_result->measurement ^ -1) + 1;
+
+	/* 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;
+}
+
+int32_t scale_batt_therm(int32_t adc_code,
+				const struct adc_properties *adc_properties,
+				const struct chan_properties *chan_properties,
+				struct adc_chan_result *adc_chan_result)
+{
+	scale_default(adc_code, adc_properties, chan_properties,
+			adc_chan_result);
+	/* convert mV ---> degC using the table */
+	return adc_map_linear(
+			adcmap_batttherm,
+			sizeof(adcmap_batttherm)/sizeof(adcmap_batttherm[0]),
+			adc_chan_result->physical,
+			&adc_chan_result->physical);
+}
+
+int32_t scale_msm_therm(int32_t adc_code,
+		const struct adc_properties *adc_properties,
+		const struct chan_properties *chan_properties,
+		struct adc_chan_result *adc_chan_result)
+{
+	scale_default(adc_code, adc_properties, chan_properties,
+			adc_chan_result);
+	/* convert mV ---> degC using the table */
+	return adc_map_linear(
+			adcmap_msmtherm,
+			sizeof(adcmap_msmtherm)/sizeof(adcmap_msmtherm[0]),
+			adc_chan_result->physical,
+			&adc_chan_result->physical);
+}
+
+int32_t scale_pmic_therm(int32_t adc_code,
+				const struct adc_properties *adc_properties,
+				const struct chan_properties *chan_properties,
+				struct adc_chan_result *adc_chan_result)
+{
+	/* 2mV/K */
+	int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
+
+	if (!chan_properties->gain_numerator ||
+		!chan_properties->gain_denominator)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	if (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->dx*
+					chan_properties->gain_denominator*1000;
+		do_div(adc_chan_result->measurement,
+			chan_properties->adc_graph->dy*
+			chan_properties->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;
+}
+
+/* Scales the ADC code to 0.001 degrees C using the map
+ * table for the XO thermistor.
+ */
+int32_t tdkntcgtherm(int32_t adc_code,
+			const struct adc_properties *adc_properties,
+			const struct chan_properties *chan_properties,
+			struct adc_chan_result *adc_chan_result)
+{
+	int32_t offset = chan_properties->adc_graph->offset,
+		dy = chan_properties->adc_graph->dy,
+		dx = chan_properties->adc_graph->dx,
+		fullscale_calibrated_adc_code;
+
+	uint32_t rt_r25;
+	uint32_t num1, num2, denom;
+
+	adc_chan_result->adc_code = adc_code;
+	fullscale_calibrated_adc_code = dy + offset;
+	/* The above is a short cut in math that would reduce a lot of
+	   computation whereas the below expression
+		(adc_properties->adc_reference*dy+dx*offset+(dx>>1))/dx
+	   is a more generic formula when the 2 reference voltages are
+	   different than 0 and full scale voltage. */
+
+	if ((dy == 0) || (dx == 0) ||
+			(offset >= fullscale_calibrated_adc_code)) {
+		return -EINVAL;
+	} else {
+		if (adc_code >= fullscale_calibrated_adc_code) {
+			rt_r25 = (uint32_t)-1;
+		} else if (adc_code <= offset) {
+			rt_r25 = 0;
+		} else {
+	/* The formula used is (adc_code of current reading - offset)/
+	 * (the calibrated fullscale adc code - adc_code of current reading).
+	 * For this channel, at this time, chan_properties->gain_numerator =
+	 * chan_properties->gain_denominator = 1, so no need to incorporate
+	 * into the formula even though we could and multiply/divide by 1
+	 * which yields the same result but expensive on computation. */
+		num1 = (adc_code - offset) << 14;
+		num2 = (fullscale_calibrated_adc_code - adc_code) >> 1;
+		denom = fullscale_calibrated_adc_code - adc_code;
+
+			if ((int)denom <= 0)
+				rt_r25 = 0x7FFFFFFF;
+			else
+				rt_r25 = (num1 + num2) / denom;
+		}
+
+		if (rt_r25 > 0x7FFFFFFF)
+			rt_r25 = 0x7FFFFFFF;
+
+		adc_map_linear(adcmap_ntcg104ef104fb,
+		sizeof(adcmap_ntcg104ef104fb)/sizeof(adcmap_ntcg104ef104fb[0]),
+		(int32_t)rt_r25, &adc_chan_result->physical);
+	}
+
+	return 0;
+}
+
+int32_t scale_xtern_chgr_cur(int32_t adc_code,
+			const struct adc_properties *adc_properties,
+			const struct chan_properties *chan_properties,
+			struct adc_chan_result *adc_chan_result)
+{
+	int32_t rawfromoffset = adc_code - chan_properties->adc_graph->offset;
+
+	if (!chan_properties->gain_numerator ||
+		!chan_properties->gain_denominator)
+		return -EINVAL;
+
+	adc_chan_result->adc_code = adc_code;
+	if (rawfromoffset > 0) {
+		if (rawfromoffset >= 1 << adc_properties->bitresolution)
+			rawfromoffset = (1 << adc_properties->bitresolution)
+									- 1;
+		adc_chan_result->measurement = ((int64_t)rawfromoffset * 5)*
+						chan_properties->adc_graph->dx*
+					chan_properties->gain_denominator;
+		do_div(adc_chan_result->measurement,
+					chan_properties->adc_graph->dy*
+					chan_properties->gain_numerator);
+	} else {
+		adc_chan_result->measurement = 0;
+	}
+	adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+	return 0;
+}
diff --git a/drivers/hwmon/msm_adc.c b/drivers/hwmon/msm_adc.c
new file mode 100644
index 0000000..42bcd07
--- /dev/null
+++ b/drivers/hwmon/msm_adc.c
@@ -0,0 +1,1538 @@
+/* Copyright (c) 2010-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/init.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/msm_adc.h>
+#include <linux/pmic8058-xoadc.h>
+#include <linux/slab.h>
+#include <linux/semaphore.h>
+
+#include <mach/dal.h>
+
+#define MSM_ADC_DRIVER_NAME		"msm_adc"
+#define MSM_ADC_MAX_FNAME		15
+
+#define MSM_ADC_DALRPC_DEVICEID		0x02000067
+#define MSM_ADC_DALRPC_PORT_NAME	"DAL00"
+#define MSM_ADC_DALRPC_CPU		SMD_APPS_MODEM
+
+#define MSM_ADC_DALRPC_CMD_REQ_CONV	9
+#define MSM_ADC_DALRPC_CMD_INPUT_PROP	11
+
+#define MSM_ADC_DALRC_CONV_TIMEOUT	(5 * HZ)  /* 5 seconds */
+
+enum dal_error {
+	DAL_ERROR_INVALID_DEVICE_IDX = 1,
+	DAL_ERROR_INVALID_CHANNEL_IDX,
+	DAL_ERROR_NULL_POINTER,
+	DAL_ERROR_DEVICE_QUEUE_FULL,
+	DAL_ERROR_INVALID_PROPERTY_LENGTH,
+	DAL_ERROR_REMOTE_EVENT_POOL_FULL
+};
+
+enum dal_result_status {
+	DAL_RESULT_STATUS_INVALID,
+	DAL_RESULT_STATUS_VALID
+};
+
+struct dal_conv_state {
+	struct dal_conv_slot		context[MSM_ADC_DEV_MAX_INFLIGHT];
+	struct list_head		slots;
+	struct mutex			list_lock;
+	struct semaphore		slot_count;
+};
+
+struct adc_dev {
+	char				*name;
+	uint32_t			nchans;
+	struct dal_conv_state		conv;
+	struct dal_translation		transl;
+	struct sensor_device_attribute	*sens_attr;
+	char				**fnames;
+};
+
+struct msm_adc_drv {
+	/*  Common to both XOADC and EPM  */
+	struct platform_device		*pdev;
+	struct device			*hwmon;
+	struct miscdevice		misc;
+	/*  XOADC variables  */
+	struct sensor_device_attribute	*sens_attr;
+	struct workqueue_struct		*wq;
+	atomic_t			online;
+	atomic_t			total_outst;
+	wait_queue_head_t		total_outst_wait;
+
+	/*  EPM variables  */
+	void				*dev_h;
+	struct adc_dev			*devs[MSM_ADC_MAX_NUM_DEVS];
+	struct mutex			prop_lock;
+	atomic_t			rpc_online;
+	atomic_t			rpc_total_outst;
+	wait_queue_head_t		rpc_total_outst_wait;
+};
+
+static bool epm_init;
+static bool epm_fluid_enabled;
+
+/* Needed to support file_op interfaces */
+static struct msm_adc_drv *msm_adc_drv;
+
+static ssize_t msm_adc_show_curr(struct device *dev,
+				struct device_attribute *devattr, char *buf);
+
+static int msm_rpc_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+				uint32_t chan, struct adc_chan_result *result);
+
+static int msm_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+				uint32_t chan, struct adc_chan_result *result);
+
+static int msm_adc_open(struct inode *inode, struct file *file)
+{
+	struct msm_client_data *client;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct platform_device *pdev = msm_adc->pdev;
+
+	client = kzalloc(sizeof(struct msm_client_data), GFP_KERNEL);
+	if (!client) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(client);
+		return -EACCES;
+	}
+
+	mutex_init(&client->lock);
+	INIT_LIST_HEAD(&client->complete_list);
+	init_waitqueue_head(&client->data_wait);
+	init_waitqueue_head(&client->outst_wait);
+
+	client->online = 1;
+
+	file->private_data = client;
+
+	return nonseekable_open(inode, file);
+}
+
+static inline void msm_adc_restore_slot(struct dal_conv_state *conv_s,
+					struct dal_conv_slot *slot)
+{
+	mutex_lock(&conv_s->list_lock);
+	list_add(&slot->list, &conv_s->slots);
+	mutex_unlock(&conv_s->list_lock);
+
+	up(&conv_s->slot_count);
+}
+
+static int no_pending_client_requests(struct msm_client_data *client)
+{
+	mutex_lock(&client->lock);
+
+	if (client->num_outstanding == 0) {
+		mutex_unlock(&client->lock);
+		return 1;
+	}
+
+	mutex_unlock(&client->lock);
+
+	return 0;
+}
+
+static int data_avail(struct msm_client_data *client, uint32_t *pending)
+{
+	uint32_t completed;
+
+	mutex_lock(&client->lock);
+	completed = client->num_complete;
+	mutex_unlock(&client->lock);
+
+	if (completed > 0) {
+		if (pending != NULL)
+			*pending = completed;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int msm_adc_release(struct inode *inode, struct file *file)
+{
+	struct msm_client_data *client = file->private_data;
+	struct adc_conv_slot *slot, *tmp;
+	int rc;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+
+	module_put(THIS_MODULE);
+
+	mutex_lock(&client->lock);
+
+	/* prevent any further requests while we teardown the client */
+	client->online = 0;
+
+	mutex_unlock(&client->lock);
+
+	/*
+	 * We may still have outstanding transactions in flight from this
+	 * client that have not completed. Make sure they're completed
+	 * before removing the client.
+	 */
+	rc = wait_event_interruptible(client->outst_wait,
+				      no_pending_client_requests(client));
+	if (rc) {
+		pr_err("%s: wait_event_interruptible failed rc = %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	/*
+	 * All transactions have completed. Add slot resources back to the
+	 * appropriate devices.
+	 */
+	list_for_each_entry_safe(slot, tmp, &client->complete_list, list) {
+		slot->client = NULL;
+		list_del(&slot->list);
+		channel[slot->conv.result.chan].adc_access_fn->adc_restore_slot(
+		channel[slot->conv.result.chan].adc_dev_instance, slot);
+	}
+
+	kfree(client);
+
+	return 0;
+}
+
+static int msm_adc_translate_dal_to_hwmon(struct msm_adc_drv *msm_adc,
+					  uint32_t chan,
+					  struct adc_dev_spec *dest)
+{
+	struct dal_translation *transl;
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	int i;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		transl = &msm_adc->devs[i]->transl;
+		if (chan >= transl->hwmon_start &&
+		    chan <= transl->hwmon_end) {
+			dest->dal.dev_idx = transl->dal_dev_idx;
+			dest->hwmon_dev_idx = transl->hwmon_dev_idx;
+			dest->dal.chan_idx = chan - transl->hwmon_start;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int msm_adc_translate_hwmon_to_dal(struct msm_adc_drv *msm_adc,
+					  struct adc_dev_spec *source,
+					  uint32_t *chan)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct dal_translation *transl;
+	int i;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		transl = &msm_adc->devs[i]->transl;
+		if (source->dal.dev_idx != transl->dal_dev_idx)
+			continue;
+		*chan = transl->hwmon_start + source->dal.chan_idx;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int msm_adc_getinputproperties(struct msm_adc_drv *msm_adc,
+					  const char *lookup_name,
+					  struct adc_dev_spec *result)
+{
+	struct device *dev = &msm_adc->pdev->dev;
+	int rc;
+
+	mutex_lock(&msm_adc->prop_lock);
+
+	rc = dalrpc_fcn_8(MSM_ADC_DALRPC_CMD_INPUT_PROP, msm_adc->dev_h,
+			  lookup_name, strlen(lookup_name) + 1,
+			  &result->dal, sizeof(struct dal_dev_spec));
+	if (rc) {
+		dev_err(dev, "DAL getprop request failed: rc = %d\n", rc);
+		mutex_unlock(&msm_adc->prop_lock);
+		return -EIO;
+	}
+
+	mutex_unlock(&msm_adc->prop_lock);
+	return rc;
+}
+
+static int msm_adc_lookup(struct msm_adc_drv *msm_adc,
+			  struct msm_adc_lookup *lookup)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct adc_dev_spec target;
+	int rc = 0, i = 0;
+	uint32_t len = 0;
+
+	len = strnlen(lookup->name, MSM_ADC_MAX_CHAN_STR);
+	while (i < pdata->num_chan_supported) {
+		if (strncmp(lookup->name, pdata->channel[i].name, len))
+			i++;
+		else
+			break;
+	}
+
+	if (pdata->num_chan_supported > 0 && i < pdata->num_chan_supported) {
+		lookup->chan_idx = i;
+	} else if (msm_adc->dev_h) {
+		rc = msm_adc_getinputproperties(msm_adc, lookup->name, &target);
+		if (rc) {
+			pr_err("%s: Lookup failed for %s\n", __func__,
+				lookup->name);
+			return rc;
+		}
+		rc = msm_adc_translate_hwmon_to_dal(msm_adc, &target,
+						&lookup->chan_idx);
+		if (rc)
+			pr_err("%s: Translation failed for %s\n", __func__,
+						lookup->name);
+	} else {
+		pr_err("%s: Lookup failed for %s\n", __func__, lookup->name);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_adc_aio_conversion(struct msm_adc_drv *msm_adc,
+				  struct adc_chan_result *request,
+				  struct msm_client_data *client)
+{
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[request->chan];
+	struct adc_conv_slot *slot;
+
+	/* we could block here, but only for a bounded time */
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+									&slot);
+
+	if (slot) {
+		atomic_inc(&msm_adc->total_outst);
+		mutex_lock(&client->lock);
+		client->num_outstanding++;
+		mutex_unlock(&client->lock);
+
+		/* indicates non blocking request to callback handler */
+		slot->blocking = 0;
+		slot->compk = NULL;/*For kernel space usage; n/a for usr space*/
+		slot->conv.result.chan = client->adc_chan = request->chan;
+		slot->client = client;
+		slot->adc_request = START_OF_CONV;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		queue_work(msm_adc->wq, &slot->work);
+		return 0;
+	}
+	return -EBUSY;
+}
+
+static int msm_adc_fluid_hw_deinit(struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+
+	if (!epm_init)
+		return -EINVAL;
+
+	if (pdata->gpio_config == APROC_CONFIG &&
+		epm_fluid_enabled && pdata->adc_fluid_disable != NULL) {
+		pdata->adc_fluid_disable();
+		epm_fluid_enabled = false;
+	}
+
+	return 0;
+}
+
+static int msm_adc_fluid_hw_init(struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+
+	if (!epm_init)
+		return -EINVAL;
+
+	if (!pdata->adc_fluid_enable)
+		return -ENODEV;
+
+	printk(KERN_DEBUG "msm_adc_fluid_hw_init: Calling adc_fluid_enable.\n");
+
+	if (pdata->gpio_config == APROC_CONFIG && !epm_fluid_enabled) {
+		pdata->adc_fluid_enable();
+		epm_fluid_enabled = true;
+	}
+
+  /* return success for now but check for errors from hw init configuration */
+	return 0;
+}
+
+static int msm_adc_poll_complete(struct msm_adc_drv *msm_adc,
+			     struct msm_client_data *client, uint32_t *pending)
+{
+	int rc;
+
+	/*
+	 * Don't proceed if there there's nothing queued on this client.
+	 * We could deadlock otherwise in a single threaded scenario.
+	 */
+	if (no_pending_client_requests(client) && !data_avail(client, pending))
+		return -EDEADLK;
+
+	rc = wait_event_interruptible(client->data_wait,
+				data_avail(client, pending));
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int msm_adc_read_result(struct msm_adc_drv *msm_adc,
+			       struct msm_client_data *client,
+			       struct adc_chan_result *result)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+	struct adc_conv_slot *slot;
+	int rc = 0;
+
+	mutex_lock(&client->lock);
+
+	slot = list_first_entry(&client->complete_list,
+				struct adc_conv_slot, list);
+	if (!slot) {
+		mutex_unlock(&client->lock);
+		return -ENOMSG;
+	}
+
+	slot->client = NULL;
+	list_del(&slot->list);
+
+	client->num_complete--;
+
+	mutex_unlock(&client->lock);
+
+	*result = slot->conv.result;
+
+	/* restore this slot to reserve */
+	channel[slot->conv.result.chan].adc_access_fn->adc_restore_slot(
+		channel[slot->conv.result.chan].adc_dev_instance, slot);
+
+	return rc;
+}
+
+static long msm_adc_ioctl(struct file *file, unsigned int cmd,
+					     unsigned long arg)
+{
+	struct msm_client_data *client = file->private_data;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct platform_device *pdev = msm_adc->pdev;
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	uint32_t block_res = 0;
+
+	int rc;
+
+	switch (cmd) {
+	case MSM_ADC_REQUEST:
+		{
+			struct adc_chan_result conv;
+
+			if (copy_from_user(&conv, (void __user *)arg,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+
+			if (conv.chan < pdata->num_chan_supported) {
+				rc = msm_adc_blocking_conversion(msm_adc,
+							conv.chan, &conv);
+			} else {
+				if (!msm_adc->dev_h)
+					return -EAGAIN;
+
+				rc = msm_rpc_adc_blocking_conversion(msm_adc,
+							conv.chan, &conv);
+			}
+			if (rc) {
+				dev_dbg(&pdev->dev, "BLK conversion failed\n");
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &conv,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_AIO_REQUEST_BLOCK_RES:
+		block_res = 1;
+	case MSM_ADC_AIO_REQUEST:
+		{
+			struct adc_chan_result conv;
+
+			if (copy_from_user(&conv, (void __user *)arg,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+
+			if (conv.chan >= pdata->num_chan_supported)
+				return -EINVAL;
+
+			rc = msm_adc_aio_conversion(msm_adc, &conv, client);
+			if (rc) {
+				dev_dbg(&pdev->dev, "AIO conversion failed\n");
+				return rc;
+			}
+			if (copy_to_user((void __user *)arg, &conv,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_AIO_POLL:
+		{
+			uint32_t completed;
+
+			rc = msm_adc_poll_complete(msm_adc, client, &completed);
+			if (rc) {
+				dev_dbg(&pdev->dev, "poll request failed\n");
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &completed,
+					sizeof(uint32_t)))
+				return -EFAULT;
+
+			break;
+		}
+	case MSM_ADC_AIO_READ:
+		{
+			struct adc_chan_result result;
+
+			rc = msm_adc_read_result(msm_adc, client, &result);
+			if (rc) {
+				dev_dbg(&pdev->dev, "read result failed\n");
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &result,
+					sizeof(struct adc_chan_result)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_LOOKUP:
+		{
+			struct msm_adc_lookup lookup;
+
+			if (copy_from_user(&lookup, (void __user *)arg,
+					sizeof(struct msm_adc_lookup)))
+				return -EFAULT;
+
+			rc = msm_adc_lookup(msm_adc, &lookup);
+			if (rc) {
+				dev_dbg(&pdev->dev, "No such channel: %s\n",
+						lookup.name);
+				return rc;
+			}
+
+			if (copy_to_user((void __user *)arg, &lookup,
+					sizeof(struct msm_adc_lookup)))
+				return -EFAULT;
+			break;
+		}
+	case MSM_ADC_FLUID_INIT:
+		{
+			uint32_t result;
+
+			result = msm_adc_fluid_hw_init(msm_adc);
+
+			if (copy_to_user((void __user *)arg, &result,
+						sizeof(uint32_t)))	{
+				printk(KERN_ERR "MSM_ADC_FLUID_INIT: "
+					"copy_to_user returned an error.\n");
+				return -EFAULT;
+			}
+			printk(KERN_DEBUG "MSM_ADC_FLUID_INIT: Success.\n");
+			break;
+		}
+	case MSM_ADC_FLUID_DEINIT:
+		{
+			uint32_t result;
+
+			result = msm_adc_fluid_hw_deinit(msm_adc);
+
+			if (copy_to_user((void __user *)arg, &result,
+						sizeof(uint32_t)))
+				return -EFAULT;
+			break;
+		}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+const struct file_operations msm_adc_fops = {
+	.open = msm_adc_open,
+	.release = msm_adc_release,
+	.unlocked_ioctl = msm_adc_ioctl,
+};
+
+static ssize_t msm_adc_show_curr(struct device *dev,
+				 struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct msm_adc_drv *msm_adc = dev_get_drvdata(dev);
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct adc_chan_result result;
+	int rc;
+
+#ifdef CONFIG_PMIC8058_XOADC
+	rc = pm8058_xoadc_registered();
+	if (rc <= 0)
+		return -ENODEV;
+#endif
+	if (attr->index < pdata->num_chan_supported) {
+		rc = msm_adc_blocking_conversion(msm_adc,
+					attr->index, &result);
+	} else {
+		if (pdata->gpio_config == APROC_CONFIG && !epm_fluid_enabled
+					&& pdata->adc_fluid_enable != NULL) {
+			printk(KERN_DEBUG "This is to read ADC value for "
+				"Fluid EPM and init. Do it only once.\n");
+			pdata->adc_fluid_enable();
+			epm_fluid_enabled = true;
+		}
+		rc = msm_rpc_adc_blocking_conversion(msm_adc,
+					attr->index, &result);
+	}
+	if (rc)
+		return 0;
+
+	return sprintf(buf, "Result: %lld Raw: %d\n", result.physical,
+		result.adc_code);
+}
+
+static int msm_rpc_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+		uint32_t hwmon_chan, struct adc_chan_result *result)
+{
+	struct msm_adc_platform_data *pdata = msm_adc->pdev->dev.platform_data;
+	struct dal_conv_request params;
+	struct device *dev = &msm_adc->pdev->dev;
+	struct adc_dev *adc_dev;
+	struct dal_conv_state *conv_s;
+	struct dal_conv_slot *slot;
+	struct adc_dev_spec dest;
+	int timeout, rc = 0;
+
+	if (pdata->gpio_config == APROC_CONFIG &&
+			pdata->adc_gpio_enable != NULL)
+		pdata->adc_gpio_enable(hwmon_chan-pdata->num_chan_supported);
+
+	rc = msm_adc_translate_dal_to_hwmon(msm_adc, hwmon_chan, &dest);
+	if (rc) {
+		dev_err(dev, "%s: translation from chan %u failed\n",
+							__func__, hwmon_chan);
+		if (pdata->gpio_config == APROC_CONFIG &&
+				pdata->adc_gpio_disable != NULL)
+			pdata->adc_gpio_disable(hwmon_chan
+					-pdata->num_chan_supported);
+		return -EINVAL;
+	}
+
+	adc_dev = msm_adc->devs[dest.hwmon_dev_idx];
+	conv_s = &adc_dev->conv;
+
+	down(&conv_s->slot_count);
+
+	mutex_lock(&conv_s->list_lock);
+
+	slot = list_first_entry(&conv_s->slots, struct dal_conv_slot, list);
+	list_del(&slot->list);
+	BUG_ON(!slot);
+
+	mutex_unlock(&conv_s->list_lock);
+
+	/* indicates blocking request to callback handler */
+	slot->blocking = 1;
+
+	params.target.dev_idx = dest.dal.dev_idx;
+	params.target.chan_idx = dest.dal.chan_idx;
+	params.cb_h = slot->cb_h;
+
+	rc = dalrpc_fcn_8(MSM_ADC_DALRPC_CMD_REQ_CONV, msm_adc->dev_h,
+			&params, sizeof(params), NULL, 0);
+	if (rc) {
+		dev_err(dev, "%s: Conversion for device = %u channel = %u"
+			     " failed\n", __func__, params.target.dev_idx,
+						    params.target.chan_idx);
+
+		rc = -EIO;
+		goto blk_conv_err;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(&slot->comp,
+					      MSM_ADC_DALRC_CONV_TIMEOUT);
+	if (timeout == 0) {
+		dev_err(dev, "read for device = %u channel = %u timed out\n",
+				params.target.dev_idx, params.target.chan_idx);
+		rc = -ETIMEDOUT;
+		goto blk_conv_err;
+	} else if (timeout < 0) {
+		rc = -EINTR;
+		goto blk_conv_err;
+	}
+
+	result->physical = (int64_t)slot->result.physical;
+
+	if (slot->result.status == DAL_RESULT_STATUS_INVALID)
+		rc = -ENODATA;
+
+blk_conv_err:
+	if (pdata->gpio_config == APROC_CONFIG &&
+			pdata->adc_gpio_disable != NULL)
+		pdata->adc_gpio_disable(hwmon_chan-pdata->num_chan_supported);
+	msm_adc_restore_slot(conv_s, slot);
+
+	return rc;
+}
+
+static int msm_adc_blocking_conversion(struct msm_adc_drv *msm_adc,
+			uint32_t hwmon_chan, struct adc_chan_result *result)
+{
+	struct adc_conv_slot *slot;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[hwmon_chan];
+
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+									&slot);
+	if (slot) {
+		slot->conv.result.chan = hwmon_chan;
+		/* indicates blocking request to callback handler */
+		slot->blocking = 1;
+		slot->adc_request = START_OF_CONV;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		queue_work(msm_adc_drv->wq, &slot->work);
+
+		wait_for_completion_interruptible(&slot->comp);
+		*result = slot->conv.result;
+		channel->adc_access_fn->adc_restore_slot(
+					channel->adc_dev_instance, slot);
+		return 0;
+	}
+	return -EBUSY;
+}
+
+int32_t adc_channel_open(uint32_t channel, void **h)
+{
+	struct msm_client_data *client;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct msm_adc_platform_data *pdata;
+	struct platform_device *pdev;
+	int i = 0;
+
+	if (!msm_adc_drv)
+		return -EFAULT;
+
+#ifdef CONFIG_PMIC8058_XOADC
+	if (pm8058_xoadc_registered() <= 0)
+		return -ENODEV;
+#endif
+	pdata = msm_adc->pdev->dev.platform_data;
+	pdev = msm_adc->pdev;
+
+	while (i < pdata->num_chan_supported) {
+		if (channel == pdata->channel[i].channel_name)
+			break;
+		else
+			i++;
+	}
+
+	if (i == pdata->num_chan_supported)
+		return -EBADF; /* unknown channel */
+
+	client = kzalloc(sizeof(struct msm_client_data), GFP_KERNEL);
+	if (!client) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(client);
+		return -EACCES;
+	}
+
+	mutex_init(&client->lock);
+	INIT_LIST_HEAD(&client->complete_list);
+	init_waitqueue_head(&client->data_wait);
+	init_waitqueue_head(&client->outst_wait);
+
+	client->online = 1;
+	client->adc_chan = i;
+	*h = (void *)client;
+	return 0;
+}
+
+int32_t adc_channel_close(void *h)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+
+	kfree(client);
+	return 0;
+}
+
+int32_t adc_channel_request_conv(void *h, struct completion *conv_complete_evt)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[client->adc_chan];
+	struct adc_conv_slot *slot;
+
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+									&slot);
+
+	if (slot) {
+		atomic_inc(&msm_adc_drv->total_outst);
+		mutex_lock(&client->lock);
+		client->num_outstanding++;
+		mutex_unlock(&client->lock);
+
+		slot->conv.result.chan = client->adc_chan;
+		slot->blocking = 0;
+		slot->compk = conv_complete_evt;
+		slot->client = client;
+		slot->adc_request = START_OF_CONV;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		queue_work(msm_adc_drv->wq, &slot->work);
+		return 0;
+	}
+	return -EBUSY;
+}
+
+int32_t adc_channel_read_result(void *h, struct adc_chan_result *chan_result)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+	struct adc_conv_slot *slot;
+	int rc = 0;
+
+	mutex_lock(&client->lock);
+
+	slot = list_first_entry(&client->complete_list,
+				struct adc_conv_slot, list);
+	if (!slot) {
+		mutex_unlock(&client->lock);
+		return -ENOMSG;
+	}
+
+	slot->client = NULL;
+	list_del(&slot->list);
+
+	client->num_complete--;
+
+	mutex_unlock(&client->lock);
+
+	*chan_result = slot->conv.result;
+
+	/* restore this slot to reserve */
+	channel[slot->conv.result.chan].adc_access_fn->adc_restore_slot(
+		channel[slot->conv.result.chan].adc_dev_instance, slot);
+
+	return rc;
+}
+
+int32_t adc_calib_request(void *h, struct completion *calib_complete_evt)
+{
+	struct msm_client_data *client = (struct msm_client_data *)h;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[client->adc_chan];
+	struct adc_conv_slot *slot;
+	int rc, calib_status;
+
+	channel->adc_access_fn->adc_slot_request(channel->adc_dev_instance,
+				&slot);
+	if (slot) {
+		slot->conv.result.chan = client->adc_chan;
+		slot->blocking = 0;
+		slot->compk = calib_complete_evt;
+		slot->adc_request = START_OF_CALIBRATION;
+		slot->chan_path = channel->chan_path_type;
+		slot->chan_adc_config = channel->adc_config_type;
+		slot->chan_adc_calib = channel->adc_calib_type;
+		rc = channel->adc_access_fn->adc_calibrate(
+			channel->adc_dev_instance, slot, &calib_status);
+
+		if (calib_status == CALIB_NOT_REQUIRED) {
+			channel->adc_access_fn->adc_restore_slot(
+					channel->adc_dev_instance, slot);
+			/* client will always wait in case when
+				calibration is not required */
+			complete(calib_complete_evt);
+		} else {
+			atomic_inc(&msm_adc_drv->total_outst);
+			mutex_lock(&client->lock);
+			client->num_outstanding++;
+			mutex_unlock(&client->lock);
+		}
+
+		return rc;
+	}
+	return -EBUSY;
+}
+
+static void msm_rpc_adc_conv_cb(void *context, u32 param,
+			    void *evt_buf, u32 len)
+{
+	struct dal_adc_result *result = evt_buf;
+	struct dal_conv_slot *slot = context;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+
+	memcpy(&slot->result, result, sizeof(slot->result));
+
+	/* for blocking requests, signal complete */
+	if (slot->blocking)
+		complete(&slot->comp);
+
+	/* for non-blocking requests, add slot to the client completed list */
+	else {
+		struct msm_client_data *client = slot->client;
+
+		mutex_lock(&client->lock);
+
+		list_add(&slot->list, &client->complete_list);
+		client->num_complete++;
+		client->num_outstanding--;
+
+		/*
+		 * if the client release has been invoked and this is call
+		 * corresponds to the last request, then signal release
+		 * to complete.
+		 */
+		if (slot->client->online == 0 && client->num_outstanding == 0)
+			wake_up_interruptible_all(&client->outst_wait);
+
+		mutex_unlock(&client->lock);
+
+		wake_up_interruptible_all(&client->data_wait);
+
+		atomic_dec(&msm_adc->total_outst);
+
+		/* verify driver remove has not been invoked */
+		if (atomic_read(&msm_adc->online) == 0 &&
+				atomic_read(&msm_adc->total_outst) == 0)
+			wake_up_interruptible_all(&msm_adc->total_outst_wait);
+	}
+}
+
+void msm_adc_conv_cb(void *context, u32 param,
+			    void *evt_buf, u32 len)
+{
+	struct adc_conv_slot *slot = context;
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+
+	switch (slot->adc_request) {
+	case START_OF_CONV:
+		slot->adc_request = END_OF_CONV;
+	break;
+	case START_OF_CALIBRATION:
+		slot->adc_request = END_OF_CALIBRATION;
+	break;
+	case END_OF_CALIBRATION:
+	case END_OF_CONV:
+	break;
+	}
+	queue_work(msm_adc->wq, &slot->work);
+}
+
+static void msm_adc_teardown_device_conv(struct platform_device *pdev,
+				    struct adc_dev *adc_dev)
+{
+	struct dal_conv_state *conv_s = &adc_dev->conv;
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+	struct dal_conv_slot *slot;
+	int i;
+
+	for (i = 0; i < MSM_ADC_DEV_MAX_INFLIGHT; i++) {
+		slot = &conv_s->context[i];
+		if (slot->cb_h) {
+			dalrpc_dealloc_cb(msm_adc->dev_h, slot->cb_h);
+			slot->cb_h = NULL;
+		}
+	}
+}
+
+static void msm_rpc_adc_teardown_device(struct platform_device *pdev,
+				    struct adc_dev *adc_dev)
+{
+	struct dal_translation *transl = &adc_dev->transl;
+	int i, num_chans = transl->hwmon_end - transl->hwmon_start + 1;
+
+	if (adc_dev->sens_attr)
+		for (i = 0; i < num_chans; i++)
+			device_remove_file(&pdev->dev,
+					&adc_dev->sens_attr[i].dev_attr);
+
+	msm_adc_teardown_device_conv(pdev, adc_dev);
+
+	kfree(adc_dev->fnames);
+	kfree(adc_dev->sens_attr);
+	kfree(adc_dev);
+}
+
+static void msm_rpc_adc_teardown_devices(struct platform_device *pdev)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+	int i, rc = 0;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		if (msm_adc->devs[i]) {
+			msm_rpc_adc_teardown_device(pdev, msm_adc->devs[i]);
+			msm_adc->devs[i] = NULL;
+		} else
+			break;
+	}
+
+	if (msm_adc->dev_h) {
+		rc = daldevice_detach(msm_adc->dev_h);
+		if (rc)
+			dev_err(&pdev->dev, "Cannot detach from dal device\n");
+		msm_adc->dev_h = NULL;
+	}
+
+}
+
+static void msm_adc_teardown_device(struct platform_device *pdev,
+				    struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int i, num_chans = pdata->num_chan_supported;
+
+	if (pdata->num_chan_supported > 0) {
+		if (msm_adc->sens_attr)
+			for (i = 0; i < num_chans; i++)
+				device_remove_file(&pdev->dev,
+					&msm_adc->sens_attr[i].dev_attr);
+		kfree(msm_adc->sens_attr);
+	}
+}
+
+static void msm_adc_teardown(struct platform_device *pdev)
+{
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+
+	if (!msm_adc)
+		return;
+
+	misc_deregister(&msm_adc->misc);
+
+	if (msm_adc->hwmon)
+		hwmon_device_unregister(msm_adc->hwmon);
+
+	msm_rpc_adc_teardown_devices(pdev);
+	msm_adc_teardown_device(pdev, msm_adc);
+
+	kfree(msm_adc);
+	platform_set_drvdata(pdev, NULL);
+}
+
+static int __devinit msm_adc_device_conv_init(struct msm_adc_drv *msm_adc,
+					      struct adc_dev *adc_dev)
+{
+	struct platform_device *pdev = msm_adc->pdev;
+	struct dal_conv_state *conv_s = &adc_dev->conv;
+	struct dal_conv_slot *slot = conv_s->context;
+	int rc, i;
+
+	sema_init(&conv_s->slot_count, MSM_ADC_DEV_MAX_INFLIGHT);
+	mutex_init(&conv_s->list_lock);
+	INIT_LIST_HEAD(&conv_s->slots);
+
+	for (i = 0; i < MSM_ADC_DEV_MAX_INFLIGHT; i++) {
+		list_add(&slot->list, &conv_s->slots);
+		slot->cb_h = dalrpc_alloc_cb(msm_adc->dev_h,
+					     msm_rpc_adc_conv_cb, slot);
+		if (!slot->cb_h) {
+			dev_err(&pdev->dev, "Unable to allocate DAL callback"
+							" for slot %d\n", i);
+			rc = -ENOMEM;
+			goto dal_err_cb;
+		}
+		init_completion(&slot->comp);
+		slot->idx = i;
+		slot++;
+	}
+
+	return 0;
+
+dal_err_cb:
+	msm_adc_teardown_device_conv(pdev, adc_dev);
+
+	return rc;
+}
+
+static struct sensor_device_attribute msm_rpc_adc_curr_in_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, msm_adc_show_curr, NULL, 0);
+
+static int __devinit msm_rpc_adc_device_init_hwmon(struct platform_device *pdev,
+						struct adc_dev *adc_dev)
+{
+	struct dal_translation *transl = &adc_dev->transl;
+	int i, rc, num_chans = transl->hwmon_end - transl->hwmon_start + 1;
+	const char prefix[] = "curr", postfix[] = "_input";
+	char tmpbuf[5];
+
+	adc_dev->fnames = kzalloc(num_chans * MSM_ADC_MAX_FNAME +
+				  num_chans * sizeof(char *), GFP_KERNEL);
+	if (!adc_dev->fnames) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_dev->sens_attr = kzalloc(num_chans *
+			    sizeof(struct sensor_device_attribute), GFP_KERNEL);
+	if (!adc_dev->sens_attr) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto hwmon_err_fnames;
+	}
+
+	for (i = 0; i < num_chans; i++) {
+		adc_dev->fnames[i] = (char *)adc_dev->fnames +
+			i * MSM_ADC_MAX_FNAME + num_chans * sizeof(char *);
+		strcpy(adc_dev->fnames[i], prefix);
+		sprintf(tmpbuf, "%d", transl->hwmon_start + i);
+		strcat(adc_dev->fnames[i], tmpbuf);
+		strcat(adc_dev->fnames[i], postfix);
+
+		msm_rpc_adc_curr_in_attr.index = transl->hwmon_start + i;
+		msm_rpc_adc_curr_in_attr.dev_attr.attr.name =
+					adc_dev->fnames[i];
+		memcpy(&adc_dev->sens_attr[i], &msm_rpc_adc_curr_in_attr,
+					sizeof(msm_rpc_adc_curr_in_attr));
+
+		rc = device_create_file(&pdev->dev,
+				&adc_dev->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed for "
+						"dal dev %u chan %d\n",
+					    adc_dev->transl.dal_dev_idx, i);
+			goto hwmon_err_sens;
+		}
+	}
+
+	return 0;
+
+hwmon_err_sens:
+	kfree(adc_dev->sens_attr);
+hwmon_err_fnames:
+	kfree(adc_dev->fnames);
+
+	return rc;
+}
+
+static int __devinit msm_rpc_adc_device_init(struct platform_device *pdev)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+	struct adc_dev *adc_dev;
+	struct adc_dev_spec target;
+	int i, rc;
+	int hwmon_cntr = pdata->num_chan_supported;
+
+	for (i = 0; i < pdata->num_adc; i++) {
+		adc_dev = kzalloc(sizeof(struct adc_dev), GFP_KERNEL);
+		if (!adc_dev) {
+			dev_err(&pdev->dev, "Unable to allocate memory\n");
+			rc = -ENOMEM;
+			goto dev_init_err;
+		}
+
+		msm_adc->devs[i] = adc_dev;
+		adc_dev->name = pdata->dev_names[i];
+
+		rc = msm_adc_device_conv_init(msm_adc, adc_dev);
+		if (rc) {
+			dev_err(&pdev->dev, "DAL device[%s] failed conv init\n",
+							adc_dev->name);
+			goto dev_init_err;
+		}
+
+		/* DAL device lookup */
+		rc = msm_adc_getinputproperties(msm_adc, adc_dev->name,
+								&target);
+		if (rc) {
+			dev_err(&pdev->dev, "No such DAL device[%s]\n",
+							adc_dev->name);
+			goto dev_init_err;
+		}
+
+		adc_dev->transl.dal_dev_idx = target.dal.dev_idx;
+		adc_dev->transl.hwmon_dev_idx = i;
+		adc_dev->nchans = target.dal.chan_idx;
+		adc_dev->transl.hwmon_start = hwmon_cntr;
+		adc_dev->transl.hwmon_end = hwmon_cntr + adc_dev->nchans - 1;
+		hwmon_cntr += adc_dev->nchans;
+
+		rc = msm_rpc_adc_device_init_hwmon(pdev, adc_dev);
+		if (rc)
+			goto dev_init_err;
+	}
+
+	return 0;
+
+dev_init_err:
+	msm_rpc_adc_teardown_devices(pdev);
+	return rc;
+}
+
+static int __devinit msm_rpc_adc_init(struct platform_device *pdev1)
+{
+	struct msm_adc_drv *msm_adc = msm_adc_drv;
+	struct platform_device *pdev = msm_adc->pdev;
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	int rc = 0;
+
+	dev_dbg(&pdev->dev, "msm_rpc_adc_init called\n");
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&msm_adc->prop_lock);
+
+	rc = daldevice_attach(MSM_ADC_DALRPC_DEVICEID,
+			MSM_ADC_DALRPC_PORT_NAME,
+			MSM_ADC_DALRPC_CPU,
+			&msm_adc->dev_h);
+	if (rc) {
+		dev_err(&pdev->dev, "Cannot attach to dal device\n");
+		return rc;
+	}
+
+	dev_dbg(&pdev->dev, "Attach to dal device Succeeded\n");
+
+	rc = msm_rpc_adc_device_init(pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "msm_adc_dev_init failed\n");
+		goto err_cleanup;
+	}
+
+	init_waitqueue_head(&msm_adc->rpc_total_outst_wait);
+	atomic_set(&msm_adc->rpc_online, 1);
+	atomic_set(&msm_adc->rpc_total_outst, 0);
+	epm_init = true;
+	pr_info("msm_adc successfully registered\n");
+
+	return 0;
+
+err_cleanup:
+	msm_rpc_adc_teardown_devices(pdev);
+
+	return rc;
+}
+
+/*
+ * Process the deferred job
+ */
+void msm_adc_wq_work(struct work_struct *work)
+{
+	struct adc_properties *adc_properties;
+	struct adc_conv_slot *slot = container_of(work,
+						struct adc_conv_slot, work);
+	uint32_t idx = slot->conv.result.chan;
+	struct msm_adc_platform_data *pdata =
+					msm_adc_drv->pdev->dev.platform_data;
+	struct msm_adc_channels *channel = &pdata->channel[idx];
+	int32_t adc_code;
+
+	switch (slot->adc_request) {
+	case START_OF_CONV:
+			channel->adc_access_fn->adc_select_chan_and_start_conv(
+					channel->adc_dev_instance, slot);
+	break;
+	case END_OF_CONV:
+		adc_properties = channel->adc_access_fn->adc_get_properties(
+						channel->adc_dev_instance);
+		if (channel->adc_access_fn->adc_read_adc_code)
+			channel->adc_access_fn->adc_read_adc_code(
+					channel->adc_dev_instance, &adc_code);
+		if (channel->chan_processor)
+			channel->chan_processor(adc_code, adc_properties,
+				&slot->chan_properties, &slot->conv.result);
+		/* Intentionally a fall thru here.  Calibraton does not need
+		to perform channel processing, etc.  However, both
+		end of conversion and end of calibration requires the below
+		fall thru code to be executed. */
+	case END_OF_CALIBRATION:
+		/* for blocking requests, signal complete */
+		if (slot->blocking)
+			complete(&slot->comp);
+		else {
+			struct msm_client_data *client = slot->client;
+
+			mutex_lock(&client->lock);
+
+			if (slot->adc_request == END_OF_CONV) {
+				list_add(&slot->list, &client->complete_list);
+				client->num_complete++;
+			}
+			client->num_outstanding--;
+
+		/*
+		 * if the client release has been invoked and this is call
+		 * corresponds to the last request, then signal release
+		 * to complete.
+		 */
+			if (slot->client->online == 0 &&
+						client->num_outstanding == 0)
+				wake_up_interruptible_all(&client->outst_wait);
+
+			mutex_unlock(&client->lock);
+
+			wake_up_interruptible_all(&client->data_wait);
+
+			atomic_dec(&msm_adc_drv->total_outst);
+
+			/* verify driver remove has not been invoked */
+			if (atomic_read(&msm_adc_drv->online) == 0 &&
+				atomic_read(&msm_adc_drv->total_outst) == 0)
+				wake_up_interruptible_all(
+					&msm_adc_drv->total_outst_wait);
+
+			if (slot->compk) /* Kernel space request */
+				complete(slot->compk);
+			if (slot->adc_request == END_OF_CALIBRATION)
+				channel->adc_access_fn->adc_restore_slot(
+					channel->adc_dev_instance, slot);
+		}
+	break;
+	case START_OF_CALIBRATION: /* code here to please code reviewers
+					to satisfy silly compiler warnings */
+	break;
+	}
+}
+
+static struct sensor_device_attribute msm_adc_curr_in_attr =
+	SENSOR_ATTR(NULL, S_IRUGO, msm_adc_show_curr, NULL, 0);
+
+static int __devinit msm_adc_init_hwmon(struct platform_device *pdev,
+					       struct msm_adc_drv *msm_adc)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_channels *channel = pdata->channel;
+	int i, rc, num_chans = pdata->num_chan_supported;
+
+	if (!channel)
+		return -EINVAL;
+
+	msm_adc->sens_attr = kzalloc(num_chans *
+			    sizeof(struct sensor_device_attribute), GFP_KERNEL);
+	if (!msm_adc->sens_attr) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		rc = -ENOMEM;
+		goto hwmon_err_sens;
+	}
+
+	for (i = 0; i < num_chans; i++) {
+		msm_adc_curr_in_attr.index = i;
+		msm_adc_curr_in_attr.dev_attr.attr.name = channel[i].name;
+		memcpy(&msm_adc->sens_attr[i], &msm_adc_curr_in_attr,
+						sizeof(msm_adc_curr_in_attr));
+
+		rc = device_create_file(&pdev->dev,
+				&msm_adc->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev, "device_create_file failed for "
+					    "dal dev %s\n",
+					    channel[i].name);
+			goto hwmon_err_sens;
+		}
+	}
+
+	return 0;
+
+hwmon_err_sens:
+	kfree(msm_adc->sens_attr);
+
+	return rc;
+}
+
+static struct platform_driver msm_adc_rpcrouter_remote_driver = {
+	.probe          = msm_rpc_adc_init,
+	.driver         = {
+		.name   = MSM_ADC_DALRPC_PORT_NAME,
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int msm_adc_probe(struct platform_device *pdev)
+{
+	struct msm_adc_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_adc_drv *msm_adc;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "no platform data?\n");
+		return -EINVAL;
+	}
+
+	msm_adc = kzalloc(sizeof(struct msm_adc_drv), GFP_KERNEL);
+	if (!msm_adc) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, msm_adc);
+	msm_adc_drv = msm_adc;
+	msm_adc->pdev = pdev;
+
+	if (pdata->target_hw == MSM_8x60 || pdata->target_hw == FSM_9xxx) {
+		rc = msm_adc_init_hwmon(pdev, msm_adc);
+		if (rc) {
+			dev_err(&pdev->dev, "msm_adc_dev_init failed\n");
+			goto err_cleanup;
+		}
+	}
+
+	msm_adc->hwmon = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(msm_adc->hwmon)) {
+		dev_err(&pdev->dev, "hwmon_device_register failed\n");
+		rc = PTR_ERR(msm_adc->hwmon);
+		goto err_cleanup;
+	}
+
+	msm_adc->misc.name = MSM_ADC_DRIVER_NAME;
+	msm_adc->misc.minor = MISC_DYNAMIC_MINOR;
+	msm_adc->misc.fops = &msm_adc_fops;
+
+	if (misc_register(&msm_adc->misc)) {
+		dev_err(&pdev->dev, "Unable to register misc device!\n");
+		goto err_cleanup;
+	}
+
+	init_waitqueue_head(&msm_adc->total_outst_wait);
+	atomic_set(&msm_adc->online, 1);
+	atomic_set(&msm_adc->total_outst, 0);
+
+	msm_adc->wq = create_singlethread_workqueue("msm_adc");
+	if (!msm_adc->wq)
+		goto err_cleanup;
+
+	if (pdata->num_adc > 0) {
+		if (pdata->target_hw == MSM_8x60)
+			platform_driver_register(
+				&msm_adc_rpcrouter_remote_driver);
+		else
+			msm_rpc_adc_init(pdev);
+	}
+
+	pr_info("msm_adc successfully registered\n");
+
+	return 0;
+
+err_cleanup:
+	msm_adc_teardown(pdev);
+
+	return rc;
+}
+
+static int __devexit msm_adc_remove(struct platform_device *pdev)
+{
+	int rc;
+
+	struct msm_adc_drv *msm_adc = platform_get_drvdata(pdev);
+
+	atomic_set(&msm_adc->online, 0);
+
+	atomic_set(&msm_adc->rpc_online, 0);
+
+	misc_deregister(&msm_adc->misc);
+
+	hwmon_device_unregister(msm_adc->hwmon);
+	msm_adc->hwmon = NULL;
+
+	/*
+	 * We may still have outstanding transactions in flight that have not
+	 * completed. Make sure they're completed before tearing down.
+	 */
+	rc = wait_event_interruptible(msm_adc->total_outst_wait,
+				      atomic_read(&msm_adc->total_outst) == 0);
+	if (rc) {
+		pr_err("%s: wait_event_interruptible failed rc = %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	rc = wait_event_interruptible(msm_adc->rpc_total_outst_wait,
+	      atomic_read(&msm_adc->rpc_total_outst) == 0);
+	if (rc) {
+		pr_err("%s: wait_event_interruptible failed rc = %d\n",
+								__func__, rc);
+		return rc;
+	}
+
+	msm_adc_teardown(pdev);
+
+	pr_info("msm_adc unregistered\n");
+
+	return 0;
+}
+
+static struct platform_driver msm_adc_driver = {
+	.probe = msm_adc_probe,
+	.remove = __devexit_p(msm_adc_remove),
+	.driver = {
+		.name = MSM_ADC_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_adc_init(void)
+{
+	return platform_driver_register(&msm_adc_driver);
+}
+module_init(msm_adc_init);
+
+static void __exit msm_adc_exit(void)
+{
+	platform_driver_unregister(&msm_adc_driver);
+}
+module_exit(msm_adc_exit);
+
+MODULE_DESCRIPTION("MSM ADC Driver");
+MODULE_ALIAS("platform:msm_adc");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
diff --git a/drivers/hwmon/wpce775x.c b/drivers/hwmon/wpce775x.c
new file mode 100644
index 0000000..2d00700
--- /dev/null
+++ b/drivers/hwmon/wpce775x.c
@@ -0,0 +1,167 @@
+/* Quanta EC driver for the Winbond Embedded Controller
+ *
+ * Copyright (C) 2009 Quanta Computer Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#define EC_ID_NAME          "qci-i2cec"
+#define EC_BUFFER_LEN		16
+#define EC_CMD_POWER_OFF	0xAC
+#define EC_CMD_RESTART	0xAB
+
+static struct i2c_client *g_i2cec_client;
+
+/* General structure to hold the driver data */
+struct i2cec_drv_data {
+		struct i2c_client *i2cec_client;
+		struct work_struct work;
+		char ec_data[EC_BUFFER_LEN+1];
+};
+
+static int __devinit wpce_probe(struct i2c_client *client,
+	const struct i2c_device_id *id);
+static int __devexit wpce_remove(struct i2c_client *kbd);
+
+#ifdef CONFIG_PM
+static int wpce_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int wpce_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static struct dev_pm_ops wpce_pm_ops = {
+	.suspend  = wpce_suspend,
+	.resume   = wpce_resume,
+};
+#endif
+
+static const struct i2c_device_id wpce_idtable[] = {
+       { EC_ID_NAME, 0 },
+       { }
+};
+
+static struct i2c_driver wpce_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = EC_ID_NAME,
+#ifdef CONFIG_PM
+		.pm = &wpce_pm_ops,
+#endif
+	},
+	.probe	  = wpce_probe,
+	.remove	  = __devexit_p(wpce_remove),
+	.id_table   = wpce_idtable,
+};
+
+static int __devinit wpce_probe(struct i2c_client *client,
+				    const struct i2c_device_id *id)
+{
+	int err = -ENOMEM;
+	struct i2cec_drv_data *context = 0;
+
+	/* there is no need to call i2c_check_functionality() since it is the
+	client's job to use the interface (I2C vs SMBUS) appropriate for it. */
+	client->driver = &wpce_driver;
+	context = kzalloc(sizeof(struct i2cec_drv_data), GFP_KERNEL);
+	if (!context)
+		return err;
+
+	context->i2cec_client = client;
+	g_i2cec_client = client;
+	i2c_set_clientdata(context->i2cec_client, context);
+
+	return 0;
+}
+
+static int __devexit wpce_remove(struct i2c_client *dev)
+{
+	struct i2cec_drv_data *context = i2c_get_clientdata(dev);
+	g_i2cec_client = NULL;
+	kfree(context);
+
+	return 0;
+}
+
+static int __init wpce_init(void)
+{
+	return i2c_add_driver(&wpce_driver);
+}
+
+static void __exit wpce_exit(void)
+{
+	i2c_del_driver(&wpce_driver);
+}
+
+struct i2c_client *wpce_get_i2c_client(void)
+{
+	return g_i2cec_client;
+}
+EXPORT_SYMBOL_GPL(wpce_get_i2c_client);
+
+void wpce_poweroff(void)
+{
+	if (g_i2cec_client == NULL)
+		return;
+	i2c_smbus_write_byte(g_i2cec_client, EC_CMD_POWER_OFF);
+}
+EXPORT_SYMBOL_GPL(wpce_poweroff);
+
+void wpce_restart(void)
+{
+	if (g_i2cec_client == NULL)
+		return;
+	i2c_smbus_write_byte(g_i2cec_client, EC_CMD_RESTART);
+}
+EXPORT_SYMBOL_GPL(wpce_restart);
+
+int wpce_i2c_transfer(struct i2c_msg *msg)
+{
+	if (g_i2cec_client == NULL)
+		return -1;
+	msg->addr = g_i2cec_client->addr;
+	return i2c_transfer(g_i2cec_client->adapter, msg, 1);
+}
+EXPORT_SYMBOL_GPL(wpce_i2c_transfer);
+
+int wpce_smbus_write_word_data(u8 command, u16 value)
+{
+	if (g_i2cec_client == NULL)
+		return -1;
+	return i2c_smbus_write_word_data(g_i2cec_client, command, value);
+}
+EXPORT_SYMBOL_GPL(wpce_smbus_write_word_data);
+
+int wpce_smbus_write_byte_data(u8 command, u8 value)
+{
+	if (g_i2cec_client == NULL)
+		return -1;
+	return i2c_smbus_write_byte_data(g_i2cec_client, command, value);
+}
+EXPORT_SYMBOL_GPL(wpce_smbus_write_byte_data);
+
+module_init(wpce_init);
+module_exit(wpce_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Bridge Driver");
+MODULE_LICENSE("GPL v2");