PMIC: Add ADC Driver for Dialog DA9052

Add DA9052 ADC driver from Dialog.
Modify Kconfig/Makefile for DA9052 ADC driver.

Signed-off-by: Zhou Jingyu <Jingyu.Zhou@freescale.com>
Acked-by: Lily Zhang <r58066@freescale.com>
Signed-off-by: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0b62c3c..8abd62d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,16 @@
 
 comment "Native drivers"
 
+config SENSORS_DA9052
+	tristate "Dialog DA9052 HWMon"
+	depends on PMIC_DIALOG
+	help
+	  Say y here to support the ADC found on Dialog Semiconductor DA9052
+	  PMIC.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called da9052-adc.
+
 config SENSORS_ABITUGURU
 	tristate "Abit uGuru (rev 1 & 2)"
 	depends on X86 && DMI && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 3c9ccef..9a41234 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -123,6 +123,7 @@
 obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_DA9052)	+= da9052-adc.o
 
 obj-$(CONFIG_PMBUS)		+= pmbus/
 
diff --git a/drivers/hwmon/da9052-adc.c b/drivers/hwmon/da9052-adc.c
new file mode 100644
index 0000000..57985c2
--- /dev/null
+++ b/drivers/hwmon/da9052-adc.c
@@ -0,0 +1,644 @@
+/*
+ * da9052-adc.c  --  ADC Driver for Dialog DA9052
+ *
+ * Copyright(c) 2009 Dialog Semiconductor Ltd.
+ *
+ * Author: Dialog Semiconductor Ltd <dchen@diasemi.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+#include <linux/mfd/da9052/adc.h>
+
+#define DRIVER_NAME "da9052-adc"
+
+static const char * const input_names[] = {
+	[DA9052_ADC_VDDOUT]	=	"VDDOUT",
+	[DA9052_ADC_ICH]	=	"CHARGING CURRENT",
+	[DA9052_ADC_TBAT]	=	"BATTERY TEMP",
+	[DA9052_ADC_VBAT]	=	"BATTERY VOLTAGE",
+	[DA9052_ADC_ADCIN4]	=	"ADC INPUT 4",
+	[DA9052_ADC_ADCIN5]	=	"ADC INPUT 5",
+	[DA9052_ADC_ADCIN6]	=	"ADC INPUT 6",
+	[DA9052_ADC_TSI]	=	"TSI",
+	[DA9052_ADC_TJUNC]	=	"BATTERY JUNCTION TEMP",
+	[DA9052_ADC_VBBAT]	=	"BACK-UP BATTERY TEMP",
+};
+
+
+int da9052_manual_read(struct da9052 *da9052,
+			unsigned char channel)
+{
+	unsigned char man_timeout_cnt = DA9052_ADC_MAX_MANCONV_RETRY_COUNT;
+	struct da9052_ssc_msg msg;
+	unsigned short calc_data;
+	unsigned int ret;
+	u16 data = 0;
+
+	msg.addr = DA9052_ADCMAN_REG;
+	msg.data = channel;
+	msg.data =  (msg.data | DA9052_ADCMAN_MANCONV);
+
+	mutex_lock(&da9052->manconv_lock);
+	da9052_lock(da9052);
+
+	ret = da9052->write(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+
+	/* Wait for the event */
+	do {
+		msg.addr = DA9052_ADCCONT_REG;
+		msg.data = 0;
+		da9052_lock(da9052);
+		ret = da9052->read(da9052, &msg);
+		if (ret)
+			goto err_ssc_comm;
+		da9052_unlock(da9052);
+
+		if (DA9052_ADCCONT_ADCMODE & msg.data)
+			msleep(1);
+		else
+			msleep(10);
+
+		msg.addr = DA9052_ADCMAN_REG;
+		msg.data = 0;
+		da9052_lock(da9052);
+		ret = da9052->read(da9052, &msg);
+		if (ret)
+			goto err_ssc_comm;
+		da9052_unlock(da9052);
+
+		/* Counter to avoid endless while loop */
+		man_timeout_cnt--;
+		if (man_timeout_cnt == 1) {
+			if (!(msg.data & DA9052_ADCMAN_MANCONV))
+				break;
+			else
+				goto err_ssc_comm;
+		}
+	/* Wait until the MAN_CONV bit is cleared to zero */
+	} while (msg.data & DA9052_ADCMAN_MANCONV);
+
+	msg.addr = DA9052_ADCRESH_REG;
+	msg.data = 0;
+
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+
+	calc_data = (unsigned short)msg.data;
+	data = (calc_data << 2);
+
+	msg.addr = DA9052_ADCRESL_REG;
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+
+	/* Clear first 14 bits before ORing */
+	calc_data = (unsigned short)msg.data & 0x0003;
+	data |= calc_data;
+
+	mutex_unlock(&da9052->manconv_lock);
+
+	return data;
+err_ssc_comm:
+	mutex_unlock(&da9052->manconv_lock);
+	da9052_unlock(da9052);
+	return -EIO;
+}
+EXPORT_SYMBOL(da9052_manual_read);
+
+int da9052_read_tjunc(struct da9052 *da9052, char *buf)
+{
+	struct da9052_ssc_msg msg;
+	unsigned char temp;
+	int ret;
+
+	msg.addr = DA9052_TJUNCRES_REG;
+	msg.data = 0;
+
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+
+	temp = msg.data;
+
+	msg.addr = DA9052_TOFFSET_REG;
+	msg.data = 0;
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+	/* Calculate Junction temperature */
+	temp = (temp - msg.data);
+	*buf = temp;
+	return 0;
+err_ssc_comm:
+	da9052_unlock(da9052);
+	return -EIO;
+}
+EXPORT_SYMBOL(da9052_read_tjunc);
+
+int da9052_read_tbat_ich(struct da9052 *da9052, char *data, int channel_no)
+{
+	struct da9052_ssc_msg msg;
+	int ret;
+
+	/* Read TBAT conversion result */
+	switch (channel_no) {
+	case DA9052_ADC_TBAT:
+		msg.addr = DA9052_TBATRES_REG;
+	break;
+	case DA9052_ADC_ICH:
+		msg.addr = DA9052_ICHGAV_REG;
+	break;
+	default:
+		return -EINVAL;
+	}
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+	*data = msg.data;
+	printk(KERN_INFO"msg.data 1= %d\n", msg.data);
+	msg.data = 28;
+	da9052_lock(da9052);
+	ret = da9052->write(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+	printk(KERN_INFO"msg.data2 = %d\n", msg.data);
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+	printk(KERN_INFO"msg.data3 = %d\n", msg.data);
+	return 0;
+
+err_ssc_comm:
+	da9052_unlock(da9052);
+	return ret;
+}
+EXPORT_SYMBOL(da9052_read_tbat_ich);
+
+static int da9052_start_adc(struct da9052 *da9052, unsigned channel)
+{
+	struct da9052_ssc_msg msg;
+	int ret;
+
+	msg.addr = DA9052_ADCCONT_REG;
+	msg.data = 0;
+
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+
+	if (channel == DA9052_ADC_VDDOUT)
+		msg.data = (msg.data | DA9052_ADCCONT_AUTOVDDEN);
+	else if (channel == DA9052_ADC_ADCIN4)
+		msg.data = (msg.data | DA9052_ADCCONT_AUTOAD4EN);
+	else if (channel == DA9052_ADC_ADCIN5)
+		msg.data = (msg.data | DA9052_ADCCONT_AUTOAD5EN);
+	else if (channel == DA9052_ADC_ADCIN6)
+		msg.data = (msg.data | DA9052_ADCCONT_AUTOAD6EN);
+	else
+		return -EINVAL;
+
+	ret = da9052->write(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+	return 0;
+
+err_ssc_comm:
+	da9052_unlock(da9052);
+	return -EIO;
+}
+
+static int da9052_stop_adc(struct da9052 *da9052, unsigned channel)
+{
+	int ret;
+	struct da9052_ssc_msg msg;
+
+	msg.addr = DA9052_ADCCONT_REG;
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+
+	if (channel == DA9052_ADC_VDDOUT)
+		msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOVDDEN));
+	else if (channel == DA9052_ADC_ADCIN4)
+		msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD4EN));
+	else if (channel == DA9052_ADC_ADCIN5)
+		msg.data = (msg.data & ~(DA9052_ADCCONT_AUTOAD5EN));
+	else if (channel == DA9052_ADC_ADCIN6)
+		msg.data =  (msg.data & ~(DA9052_ADCCONT_AUTOAD6EN));
+	else
+		return -EINVAL;
+
+	ret = da9052->write(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+
+	return 0;
+err_ssc_comm:
+	da9052_unlock(da9052);
+	return -EIO;
+}
+
+static ssize_t da9052_adc_read_start_stop(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+	struct da9052_ssc_msg msg;
+	int channel = to_sensor_dev_attr(devattr)->index;
+	int ret;
+
+	ret = da9052_start_adc(priv->da9052, channel);
+	if (ret < 0)
+		return ret;
+
+	/* Read the ADC converted value */
+	switch (channel) {
+	case DA9052_ADC_VDDOUT:
+		msg.addr = DA9052_VDDRES_REG;
+	break;
+#if (DA9052_ADC_CONF_ADC4 == 1)
+	case DA9052_ADC_ADCIN4:
+		msg.addr = DA9052_ADCIN4RES_REG;
+	break;
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+	case DA9052_ADC_ADCIN5:
+		msg.addr = DA9052_ADCIN5RES_REG;
+	break;
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+	case DA9052_ADC_ADCIN6:
+		msg.addr = DA9052_ADCIN6RES_REG;
+	break;
+#endif
+	default:
+		return -EINVAL;
+	}
+	msg.data = 0;
+	da9052_lock(priv->da9052);
+	ret = priv->da9052->read(priv->da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(priv->da9052);
+
+	ret = da9052_stop_adc(priv->da9052, channel);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%u\n", msg.data);
+
+err_ssc_comm:
+	da9052_unlock(priv->da9052);
+	return ret;
+}
+
+static ssize_t da9052_adc_read_ich(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_ICH);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%u\n", *buf);
+}
+
+static ssize_t da9052_adc_read_tbat(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = da9052_read_tbat_ich(priv->da9052, buf, DA9052_ADC_TBAT);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%u\n", *buf);
+}
+
+static ssize_t da9052_adc_read_vbat(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+	s32 ret;
+
+	ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBAT);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%u\n", ret);
+}
+
+static ssize_t da9052_adc_read_tjunc(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+	int ret;
+	ret = da9052_read_tjunc(priv->da9052, buf);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%u\n", *buf);
+}
+
+static ssize_t da9052_adc_read_vbbat(struct device *dev,
+	struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+	s32 ret;
+
+	ret = da9052_manual_read(priv->da9052, DA9052_ADC_VBBAT);
+	if (ret < 0)
+		return ret;
+	return sprintf(buf, "%u\n", ret);
+}
+
+static int da9052_adc_hw_init(struct da9052 *da9052)
+{
+	struct da9052_ssc_msg msg;
+	int ret;
+
+	/* ADC channel 4 and 5 are by default enabled */
+#if (DA9052_ADC_CONF_ADC4 == 1)
+	msg.addr = DA9052_GPIO0001_REG;
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+
+	msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN));
+	ret = da9052->write(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+#endif
+
+#if (DA9052_ADC_CONF_ADC5 == 1)
+	msg.addr = DA9052_GPIO0001_REG;
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+
+	msg.data = (msg.data & ~(DA9052_GPIO0001_GPIO0PIN));
+	ret = da9052->write(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+#endif
+
+#if (DA9052_ADC_CONF_ADC6 == 1)
+	msg.addr = DA9052_GPIO0203_REG;
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret)
+		goto err_ssc_comm;
+
+	msg.data = (msg.data & ~(DA9052_GPIO0203_GPIO2PIN));
+	ret = da9052->write(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+#endif
+#if 0
+	/* By default configure the Measurement sequence interval to 10ms */
+	msg.addr = DA9052_ADCCONT_REG;
+	msg.data = 0;
+	da9052_lock(da9052);
+	ret = da9052->read(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+
+	/* Set the ADC MODE bit for 10msec sampling timer */
+	msg.data = (msg.data & ~(DA9052_ADCCONT_ADCMODE));
+	ret = da9052->write(da9052, &msg);
+	if (ret != 0)
+		goto err_ssc_comm;
+	da9052_unlock(da9052);
+#endif
+	return 0;
+err_ssc_comm:
+	da9052_unlock(da9052);
+	return -EIO;
+}
+
+static ssize_t da9052_adc_show_name(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	return sprintf(buf, "da9052-adc\n");
+}
+
+static ssize_t show_label(struct device *dev,
+			  struct device_attribute *devattr, char *buf)
+{
+	int channel = to_sensor_dev_attr(devattr)->index;
+
+	return sprintf(buf, "%s\n", input_names[channel]);
+}
+#define DA9052_ADC_CHANNELS(id, name) \
+	static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \
+				  NULL, name)
+
+DA9052_ADC_CHANNELS(0, DA9052_ADC_VDDOUT);
+DA9052_ADC_CHANNELS(1, DA9052_ADC_ICH);
+DA9052_ADC_CHANNELS(2, DA9052_ADC_TBAT);
+DA9052_ADC_CHANNELS(3, DA9052_ADC_VBAT);
+#if (DA9052_ADC_CONF_ADC4 == 1)
+DA9052_ADC_CHANNELS(4, DA9052_ADC_ADCIN4);
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+DA9052_ADC_CHANNELS(5, DA9052_ADC_ADCIN5);
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+DA9052_ADC_CHANNELS(6, DA9052_ADC_ADCIN6);
+#endif
+DA9052_ADC_CHANNELS(7, DA9052_ADC_TSI);
+DA9052_ADC_CHANNELS(8, DA9052_ADC_TJUNC);
+DA9052_ADC_CHANNELS(9, DA9052_ADC_VBBAT);
+
+
+static DEVICE_ATTR(name, S_IRUGO, da9052_adc_show_name, NULL);
+static SENSOR_DEVICE_ATTR(read_vddout, S_IRUGO,
+				da9052_adc_read_start_stop, NULL,
+				DA9052_ADC_VDDOUT);
+static SENSOR_DEVICE_ATTR(read_ich, S_IRUGO, da9052_adc_read_ich, NULL,
+				DA9052_ADC_ICH);
+static SENSOR_DEVICE_ATTR(read_tbat, S_IRUGO, da9052_adc_read_tbat, NULL,
+				DA9052_ADC_TBAT);
+static SENSOR_DEVICE_ATTR(read_vbat, S_IRUGO, da9052_adc_read_vbat, NULL,
+				DA9052_ADC_VBAT);
+#if (DA9052_ADC_CONF_ADC4 == 1)
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
+				DA9052_ADC_ADCIN4);
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
+				DA9052_ADC_ADCIN5);
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_adc_read_start_stop, NULL,
+				DA9052_ADC_ADCIN6);
+#endif
+static SENSOR_DEVICE_ATTR(read_tjunc, S_IRUGO, da9052_adc_read_tjunc, NULL,
+				DA9052_ADC_TJUNC);
+static SENSOR_DEVICE_ATTR(read_vbbat, S_IRUGO, da9052_adc_read_vbbat, NULL,
+				DA9052_ADC_VBBAT);
+
+static struct attribute *da9052_attr[] = {
+	&dev_attr_name.attr,
+	&sensor_dev_attr_read_vddout.dev_attr.attr,
+	&sensor_dev_attr_in0_label.dev_attr.attr,
+	&sensor_dev_attr_read_ich.dev_attr.attr,
+	&sensor_dev_attr_in1_label.dev_attr.attr,
+	&sensor_dev_attr_read_tbat.dev_attr.attr,
+	&sensor_dev_attr_in2_label.dev_attr.attr,
+	&sensor_dev_attr_read_vbat.dev_attr.attr,
+	&sensor_dev_attr_in3_label.dev_attr.attr,
+#if (DA9052_ADC_CONF_ADC4 == 1)
+	&sensor_dev_attr_in4_input.dev_attr.attr,
+	&sensor_dev_attr_in4_label.dev_attr.attr,
+#endif
+#if (DA9052_ADC_CONF_ADC5 == 1)
+	&sensor_dev_attr_in5_input.dev_attr.attr,
+	&sensor_dev_attr_in5_label.dev_attr.attr,
+#endif
+#if (DA9052_ADC_CONF_ADC6 == 1)
+	&sensor_dev_attr_in6_input.dev_attr.attr,
+	&sensor_dev_attr_in6_label.dev_attr.attr,
+#endif
+	&sensor_dev_attr_in7_label.dev_attr.attr,
+	&sensor_dev_attr_read_tjunc.dev_attr.attr,
+	&sensor_dev_attr_in8_label.dev_attr.attr,
+	&sensor_dev_attr_read_vbbat.dev_attr.attr,
+	&sensor_dev_attr_in9_label.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group da9052_group = {
+	.attrs = da9052_attr,
+};
+
+static int __init da9052_adc_probe(struct platform_device *pdev)
+{
+	struct da9052_adc_priv *priv;
+	int ret;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->da9052 = dev_get_drvdata(pdev->dev.parent);
+
+	platform_set_drvdata(pdev, priv);
+
+	/* Register sysfs hooks */
+	ret = sysfs_create_group(&pdev->dev.kobj, &da9052_group);
+	if (ret)
+		goto out_err_create1;
+
+	priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(priv->hwmon_dev)) {
+		ret = PTR_ERR(priv->hwmon_dev);
+		goto out_err_create2;
+	}
+	/* Initializes the hardware for ADC module */
+	da9052_adc_hw_init(priv->da9052);
+
+	/* Initialize mutex required for ADC Manual read */
+	mutex_init(&priv->da9052->manconv_lock);
+
+	return 0;
+
+out_err_create2:
+	sysfs_remove_group(&pdev->dev.kobj, &da9052_group);
+out_err_create1:
+	platform_set_drvdata(pdev, NULL);
+	kfree(priv);
+
+	return ret;
+}
+
+static int __devexit da9052_adc_remove(struct platform_device *pdev)
+{
+	struct da9052_adc_priv *priv = platform_get_drvdata(pdev);
+
+	mutex_destroy(&priv->da9052->manconv_lock);
+
+	hwmon_device_unregister(priv->hwmon_dev);
+
+	sysfs_remove_group(&pdev->dev.kobj, &da9052_group);
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver da9052_adc_driver = {
+	.remove		= __devexit_p(da9052_adc_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= DRIVER_NAME,
+	},
+};
+
+static int __init da9052_adc_init(void)
+{
+	return platform_driver_probe(&da9052_adc_driver, da9052_adc_probe);
+}
+module_init(da9052_adc_init);
+
+static void __exit da9052_adc_exit(void)
+{
+	platform_driver_unregister(&da9052_adc_driver);
+}
+module_exit(da9052_adc_exit);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("DA9052 ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);