All of lore.kernel.org
 help / color / mirror / Atom feed
From: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
To: linux-iio@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, linux-samsung-soc@vger.kernel.org,
	dianders@chromium.org, gregkh@linuxfoundation.org,
	naveenkrishna.ch@gmail.com, lars@metafoo.de
Subject: [PATCH] iio: adc: add exynos5 adc driver under iio framwork
Date: Thu, 24 Jan 2013 10:28:34 +0530	[thread overview]
Message-ID: <1359003514-1646-1-git-send-email-ch.naveen@samsung.com> (raw)
In-Reply-To: <1358775470-21278-1-git-send-email-ch.naveen@samsung.com>

This patch adds driver for ADC IP found on EXYNOS5250 and EXYNOS5410
from Samsung. Also adds the Documentation for device tree bindings.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v1:

1. Fixed comments from Lars
2. Added support for ADC on EXYNOS5410

Changes since v2:

1. Changed the instance name for (struct iio_dev *) to indio_dev
2. Changed devm_request_irq to request_irq

Few doubts regarding the mappings and child device handling.
Kindly, suggest me better methods.

Changes since v3:

1. Added clk_prepare_disable and regulator_disable calls in _remove()
2. Moved init_completion before irq_request
3. Added NULL pointer check for devm_request_and_ioremap() return value.
4. Use number of channels as per the ADC version 
5. Change the define ADC_V1_CHANNEL to ADC_CHANNEL
6. Update the Documentation to include EXYNOS5410 compatible

Doug, i've used
	chan = iio_channel_get(dev_name(&pdev->dev), "adc3");
in ntc thermistor driver during probe and
	iio_read_channel_raw(chan, &val);
for read.

But, then the drivers become kind of coupled. Right.

Lars, Is there an other way.

Thanks for the comments

 .../bindings/arm/samsung/exynos5-adc.txt           |   38 ++
 drivers/iio/adc/Kconfig                            |    7 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/exynos5_adc.c                      |  475 ++++++++++++++++++++
 4 files changed, 521 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt
 create mode 100644 drivers/iio/adc/exynos5_adc.c

diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt
new file mode 100644
index 0000000..0f281d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt
@@ -0,0 +1,38 @@
+Samsung Exynos5 Analog to Digital Converter bindings
+
+Required properties:
+- compatible:		Must be "samsung,exynos5250-adc" for exynos5250 controllers.
+			Must be "samsung,exynos5410-adc" for exynos5410 controllers.
+- reg:			Contains ADC register address range (base address and
+			length).
+- interrupts: 		Contains the interrupt information for the timer. The
+			format is being dependent on which interrupt controller
+			the Samsung device uses.
+
+Note: child nodes can be added for auto probing from device tree.
+
+Example: adding device info in dtsi file
+
+adc@12D10000 {
+	compatible = "samsung,exynos5250-adc";
+	reg = <0x12D10000 0x100>;
+	interrupts = <0 106 0>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges;
+};
+
+
+Example: Adding child nodes in dts file
+
+adc@12D10000 {
+
+	/* NTC thermistor is a hwmon device */
+	ncp15wb473@0 {
+		compatible = "ntc,ncp15wb473";
+		reg = <0x0>;
+		pullup-uV = <1800000>;
+		pullup-ohm = <47000>;
+		pulldown-ohm = <0>;
+	};
+};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index fe822a1..33ceabf 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -91,6 +91,13 @@ config AT91_ADC
 	help
 	  Say yes here to build support for Atmel AT91 ADC.
 
+config EXYNOS5_ADC
+	bool "Exynos5 ADC driver support"
+	help
+	  Core support for the ADC block found in the Samsung EXYNOS5 series
+	  of SoCs for drivers such as the touchscreen and hwmon to use to share
+	  this resource.
+
 config LP8788_ADC
 	bool "LP8788 ADC driver"
 	depends on MFD_LP8788
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 2d5f100..5b4a4f6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
 obj-$(CONFIG_AD7793) += ad7793.o
 obj-$(CONFIG_AD7887) += ad7887.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_EXYNOS5_ADC) += exynos5_adc.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
diff --git a/drivers/iio/adc/exynos5_adc.c b/drivers/iio/adc/exynos5_adc.c
new file mode 100644
index 0000000..4963649
--- /dev/null
+++ b/drivers/iio/adc/exynos5_adc.c
@@ -0,0 +1,475 @@
+/*
+ *  exynos5_adc.c - Support for ADC in EXYNOS5 SoCs
+ *
+ *  8 ~ 10 channel, 10/12-bit ADC
+ *
+ *  Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.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.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+enum adc_version {
+	ADC_V1,
+	ADC_V2
+};
+
+/* EXYNOS5250 ADC_V1 registers definitions */
+#define ADC_V1_CON(x)		((x) + 0x00)
+#define ADC_V1_DLY(x)		((x) + 0x08)
+#define ADC_V1_DATX(x)		((x) + 0x0C)
+#define ADC_V1_INTCLR(x)	((x) + 0x18)
+#define ADC_V1_MUX(x)		((x) + 0x1c)
+
+/* EXYNOS5410 ADC_V2 registers definitions */
+#define ADC_V2_CON1(x)		((x) + 0x00)
+#define ADC_V2_CON2(x)		((x) + 0x04)
+#define ADC_V2_STAT(x)		((x) + 0x08)
+#define ADC_V2_INT_EN(x)	((x) + 0x10)
+#define ADC_V2_INT_ST(x)	((x) + 0x14)
+#define ADC_V2_VER(x)		((x) + 0x20)
+
+/* Bit definitions for ADC_V1 */
+#define ADC_V1_CON_RES		(1u << 16)
+#define ADC_V1_CON_PRSCEN	(1u << 14)
+#define ADC_V1_CON_PRSCLV(x)	(((x) & 0xFF) << 6)
+#define ADC_V1_CON_STANDBY	(1u << 2)
+
+/* Bit definitions for ADC_V2 */
+#define ADC_V2_CON1_SOFT_RESET	(1u << 2)
+
+#define ADC_V2_CON2_OSEL	(1u << 10)
+#define ADC_V2_CON2_ESEL	(1u << 9)
+#define ADC_V2_CON2_HIGHF	(1u << 8)
+#define ADC_V2_CON2_C_TIME(x)	(((x) & 7) << 4)
+#define ADC_V2_CON2_ACH_SEL(x)	(((x) & 0xF) << 0)
+#define ADC_V2_CON2_ACH_MASK	0xF
+
+/* Bit definitions common for ADC_V1 and ADC_V2 */
+#define ADC_V1_CON_EN_START		(1u << 0)
+#define ADC_V1_DATX_MASK	0xFFF
+
+struct exynos5_adc {
+	void __iomem		*regs;
+	struct clk		*clk;
+	unsigned int		irq;
+	struct regulator	*vdd;
+
+	struct completion	completion;
+
+	struct iio_map		*map;
+	u32			value;
+	unsigned int            version;
+};
+
+static const struct of_device_id exynos5_adc_match[] = {
+	{ .compatible = "samsung,exynos5250-adc", .data = (void *)ADC_V1 },
+	{ .compatible = "samsung,exynos5410-adc", .data = (void *)ADC_V2 },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_adc_match);
+
+static inline unsigned int exynos5_adc_get_version(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+
+	match = of_match_node(exynos5_adc_match, pdev->dev.of_node);
+	return (unsigned int)match->data;
+}
+
+/* default maps used by iio consumer (ex: ntc-thermistor driver) */
+static struct iio_map exynos5_adc_iio_map[] = {
+	{
+		.consumer_dev_name = "0.ncp15wb473",
+		.consumer_channel = "adc3",
+		.adc_channel_label = "adc3",
+	},
+	{
+		.consumer_dev_name = "1.ncp15wb473",
+		.consumer_channel = "adc4",
+		.adc_channel_label = "adc4",
+	},
+	{
+		.consumer_dev_name = "2.ncp15wb473",
+		.consumer_channel = "adc5",
+		.adc_channel_label = "adc5",
+	},
+	{
+		.consumer_dev_name = "3.ncp15wb473",
+		.consumer_channel = "adc6",
+		.adc_channel_label = "adc6",
+	},
+	{},
+};
+
+static int exynos5_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val,
+				int *val2,
+				long mask)
+{
+	struct exynos5_adc *info = iio_priv(indio_dev);
+	u32 con1, con2;
+
+	if (mask == IIO_CHAN_INFO_RAW) {
+		mutex_lock(&indio_dev->mlock);
+
+		/* Select the channel to be used and Trigger conversion */
+		if (info->version == ADC_V2) {
+			con2 = readl(ADC_V2_CON2(info->regs));
+			con2 &= ~ADC_V2_CON2_ACH_MASK;
+			con2 |= ADC_V2_CON2_ACH_SEL(chan->address);
+			writel(con2, ADC_V2_CON2(info->regs));
+
+			con1 = readl(ADC_V2_CON1(info->regs));
+			writel(con1 | ADC_V1_CON_EN_START,
+					ADC_V2_CON1(info->regs));
+		} else {
+			writel(chan->address, ADC_V1_MUX(info->regs));
+
+			con1 = readl(ADC_V1_CON(info->regs));
+			writel(con1 | ADC_V1_CON_EN_START,
+					ADC_V1_CON(info->regs));
+		}
+
+		wait_for_completion(&info->completion);
+		*val = info->value;
+
+		mutex_unlock(&indio_dev->mlock);
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static irqreturn_t exynos5_adc_isr(int irq, void *dev_id)
+{
+	struct exynos5_adc *info = (struct exynos5_adc *)dev_id;
+
+	/* Read value */
+	info->value = readl(ADC_V1_DATX(info->regs)) &
+						ADC_V1_DATX_MASK;
+	/* clear irq */
+	if (info->version == ADC_V2)
+		writel(1, ADC_V2_INT_ST(info->regs));
+	else
+		writel(1, ADC_V1_INTCLR(info->regs));
+
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static int exynos5_adc_reg_access(struct iio_dev *indio_dev,
+			      unsigned reg, unsigned writeval,
+			      unsigned *readval)
+{
+	struct exynos5_adc *info = iio_priv(indio_dev);
+	u32 ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	if (readval != NULL) {
+		ret = readl(info->regs + reg);
+		*readval = ret;
+	} else
+		ret = -EINVAL;
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static const struct iio_info exynos5_adc_iio_info = {
+	.read_raw = &exynos5_read_raw,
+	.debugfs_reg_access = &exynos5_adc_reg_access,
+	.driver_module = THIS_MODULE,
+};
+
+#define ADC_CHANNEL(_index, _id) {			\
+	.type = IIO_VOLTAGE,				\
+	.indexed = 1,					\
+	.channel = _index,				\
+	.address = _index,				\
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,	\
+	.datasheet_name = _id,				\
+}
+
+static const struct iio_chan_spec exynos5_adc_iio_channels[] = {
+	ADC_CHANNEL(0, "adc0"),
+	ADC_CHANNEL(1, "adc1"),
+	ADC_CHANNEL(2, "adc2"),
+	ADC_CHANNEL(3, "adc3"),
+	ADC_CHANNEL(4, "adc4"),
+	ADC_CHANNEL(5, "adc5"),
+	ADC_CHANNEL(6, "adc6"),
+	ADC_CHANNEL(7, "adc7"),
+	ADC_CHANNEL(8, "adc8"),
+	ADC_CHANNEL(9, "adc9"),
+};
+
+static int exynos5_adc_remove_devices(struct device *dev, void *c)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	platform_device_unregister(pdev);
+
+	return 0;
+}
+
+static void exynos5_adc_hw_init(struct exynos5_adc *info)
+{
+	u32 con1, con2;
+
+	if (info->version == ADC_V2) {
+		con1 = ADC_V2_CON1_SOFT_RESET;
+		writel(con1, ADC_V2_CON1(info->regs));
+
+		con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
+			ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
+		writel(con2, ADC_V2_CON2(info->regs));
+
+		/* Enable interrupts */
+		writel(1, ADC_V2_INT_EN(info->regs));
+	} else {
+		/* set default prescaler values and Enable prescaler */
+		con1 =  ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
+
+		/* Enable 12-bit ADC resolution */
+		con1 |= ADC_V1_CON_RES;
+		writel(con1, ADC_V1_CON(info->regs));
+	}
+}
+
+static int exynos5_adc_probe(struct platform_device *pdev)
+{
+	struct exynos5_adc *info = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev = NULL;
+	struct resource	*mem;
+	int ret = -ENODEV;
+	int irq;
+
+	if (!np)
+		return ret;
+
+	indio_dev = iio_device_alloc(sizeof(struct exynos5_adc));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	info = iio_priv(indio_dev);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	info->regs = devm_request_and_ioremap(&pdev->dev, mem);
+	if (!info->regs)
+		return -ENOMEM;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		ret = irq;
+		goto err_iio;
+	}
+
+	info->irq = irq;
+
+	init_completion(&info->completion);
+
+	ret = request_irq(info->irq, exynos5_adc_isr,
+					0, dev_name(&pdev->dev), info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+							info->irq);
+		goto err_iio;
+	}
+
+	info->clk = devm_clk_get(&pdev->dev, "adc");
+	if (IS_ERR(info->clk)) {
+		dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
+							PTR_ERR(info->clk));
+		ret = PTR_ERR(info->clk);
+		goto err_irq;
+	}
+
+	info->vdd = devm_regulator_get(&pdev->dev, "vdd");
+	if (IS_ERR(info->vdd)) {
+		dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
+							PTR_ERR(info->vdd));
+		ret = PTR_ERR(info->vdd);
+		goto err_irq;
+	}
+
+	info->version = exynos5_adc_get_version(pdev);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &exynos5_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = exynos5_adc_iio_channels;
+
+	if (info->version == ADC_V1)
+		/* ADC core in EXYNOS5250 has 8 channels */
+		indio_dev->num_channels =
+				ARRAY_SIZE(exynos5_adc_iio_channels) - 2;
+	else
+		/* ADC core in EXYNOS5410 has 10 channels */
+		indio_dev->num_channels =
+				ARRAY_SIZE(exynos5_adc_iio_channels);
+
+	info->map = exynos5_adc_iio_map;
+
+	ret = iio_map_array_register(indio_dev, info->map);
+	if (ret) {
+		dev_err(&indio_dev->dev, "failed mapping iio, err: %d\n", ret);
+		goto err_irq;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto err_map;
+
+	ret = regulator_enable(info->vdd);
+	if (ret)
+		goto err_iio_dev;
+
+	clk_prepare_enable(info->clk);
+
+	exynos5_adc_hw_init(info);
+
+	ret = of_platform_populate(np, exynos5_adc_match, NULL, &pdev->dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed adding child nodes\n");
+		goto err_of_populate;
+	}
+
+	return 0;
+
+err_of_populate:
+	device_for_each_child(&pdev->dev, NULL, exynos5_adc_remove_devices);
+	clk_disable_unprepare(info->clk);
+	regulator_disable(info->vdd);
+err_iio_dev:
+	iio_device_unregister(indio_dev);
+err_map:
+	iio_map_array_unregister(indio_dev, info->map);
+err_irq:
+	free_irq(info->irq, info);
+err_iio:
+	iio_device_free(indio_dev);
+	return ret;
+}
+
+static int exynos5_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct exynos5_adc *info = iio_priv(indio_dev);
+
+	clk_disable_unprepare(info->clk);
+	regulator_disable(info->vdd);
+	iio_device_unregister(indio_dev);
+	iio_map_array_unregister(indio_dev, info->map);
+	free_irq(info->irq, info);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos5_adc_suspend(struct device *dev)
+{
+	struct exynos5_adc *info = dev_get_data(dev);
+	u32 con;
+
+	if (info->version == ADC_V2) {
+		con = readl(ADC_V2_CON1(info->regs));
+		con &= ~ADC_V1_CON_EN_START;
+		writel(con, ADC_V2_CON1(info->regs));
+	} else {
+		con = readl(ADC_V1_CON(info->regs));
+		con |= ADC_V1_CON_STANDBY;
+		writel(con, ADC_V1_CON(info->regs));
+	}
+
+	clk_disable_unprepare(info->clk);
+	regulator_disable(info->vdd);
+
+	return 0;
+}
+
+static int exynos5_adc_resume(struct device *dev)
+{
+	struct exynos5_adc *info = dev_get_data(dev);
+	int ret;
+
+	ret = regulator_enable(info->vdd);
+	if (ret)
+		return ret;
+
+	clk_prepare_enable(info->clk);
+
+	exynos5_adc_hw_init(info);
+
+	return 0;
+}
+
+#else
+#define exynos5_adc_suspend NULL
+#define exynos5_adc_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(exynos5_adc_pm_ops,
+			exynos5_adc_suspend,
+			exynos5_adc_resume);
+
+static struct platform_driver exynos5_adc_driver = {
+	.probe		= exynos5_adc_probe,
+	.remove		= exynos5_adc_remove,
+	.driver		= {
+		.name	= "exynos5-adc",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(exynos5_adc_match),
+		.pm	= &exynos5_adc_pm_ops,
+	},
+};
+
+module_platform_driver(exynos5_adc_driver);
+
+MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


WARNING: multiple messages have this Message-ID (diff)
From: Naveen Krishna Chatradhi <ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
To: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
	naveenkrishna.ch-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org
Subject: [PATCH] iio: adc: add exynos5 adc driver under iio framwork
Date: Thu, 24 Jan 2013 10:28:34 +0530	[thread overview]
Message-ID: <1359003514-1646-1-git-send-email-ch.naveen@samsung.com> (raw)
In-Reply-To: <1358775470-21278-1-git-send-email-ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

This patch adds driver for ADC IP found on EXYNOS5250 and EXYNOS5410
from Samsung. Also adds the Documentation for device tree bindings.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
Changes since v1:

1. Fixed comments from Lars
2. Added support for ADC on EXYNOS5410

Changes since v2:

1. Changed the instance name for (struct iio_dev *) to indio_dev
2. Changed devm_request_irq to request_irq

Few doubts regarding the mappings and child device handling.
Kindly, suggest me better methods.

Changes since v3:

1. Added clk_prepare_disable and regulator_disable calls in _remove()
2. Moved init_completion before irq_request
3. Added NULL pointer check for devm_request_and_ioremap() return value.
4. Use number of channels as per the ADC version 
5. Change the define ADC_V1_CHANNEL to ADC_CHANNEL
6. Update the Documentation to include EXYNOS5410 compatible

Doug, i've used
	chan = iio_channel_get(dev_name(&pdev->dev), "adc3");
in ntc thermistor driver during probe and
	iio_read_channel_raw(chan, &val);
for read.

But, then the drivers become kind of coupled. Right.

Lars, Is there an other way.

Thanks for the comments

 .../bindings/arm/samsung/exynos5-adc.txt           |   38 ++
 drivers/iio/adc/Kconfig                            |    7 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/exynos5_adc.c                      |  475 ++++++++++++++++++++
 4 files changed, 521 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt
 create mode 100644 drivers/iio/adc/exynos5_adc.c

diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt
new file mode 100644
index 0000000..0f281d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/samsung/exynos5-adc.txt
@@ -0,0 +1,38 @@
+Samsung Exynos5 Analog to Digital Converter bindings
+
+Required properties:
+- compatible:		Must be "samsung,exynos5250-adc" for exynos5250 controllers.
+			Must be "samsung,exynos5410-adc" for exynos5410 controllers.
+- reg:			Contains ADC register address range (base address and
+			length).
+- interrupts: 		Contains the interrupt information for the timer. The
+			format is being dependent on which interrupt controller
+			the Samsung device uses.
+
+Note: child nodes can be added for auto probing from device tree.
+
+Example: adding device info in dtsi file
+
+adc@12D10000 {
+	compatible = "samsung,exynos5250-adc";
+	reg = <0x12D10000 0x100>;
+	interrupts = <0 106 0>;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges;
+};
+
+
+Example: Adding child nodes in dts file
+
+adc@12D10000 {
+
+	/* NTC thermistor is a hwmon device */
+	ncp15wb473@0 {
+		compatible = "ntc,ncp15wb473";
+		reg = <0x0>;
+		pullup-uV = <1800000>;
+		pullup-ohm = <47000>;
+		pulldown-ohm = <0>;
+	};
+};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index fe822a1..33ceabf 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -91,6 +91,13 @@ config AT91_ADC
 	help
 	  Say yes here to build support for Atmel AT91 ADC.
 
+config EXYNOS5_ADC
+	bool "Exynos5 ADC driver support"
+	help
+	  Core support for the ADC block found in the Samsung EXYNOS5 series
+	  of SoCs for drivers such as the touchscreen and hwmon to use to share
+	  this resource.
+
 config LP8788_ADC
 	bool "LP8788 ADC driver"
 	depends on MFD_LP8788
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 2d5f100..5b4a4f6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
 obj-$(CONFIG_AD7793) += ad7793.o
 obj-$(CONFIG_AD7887) += ad7887.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_EXYNOS5_ADC) += exynos5_adc.o
 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
diff --git a/drivers/iio/adc/exynos5_adc.c b/drivers/iio/adc/exynos5_adc.c
new file mode 100644
index 0000000..4963649
--- /dev/null
+++ b/drivers/iio/adc/exynos5_adc.c
@@ -0,0 +1,475 @@
+/*
+ *  exynos5_adc.c - Support for ADC in EXYNOS5 SoCs
+ *
+ *  8 ~ 10 channel, 10/12-bit ADC
+ *
+ *  Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
+ *
+ *  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.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+enum adc_version {
+	ADC_V1,
+	ADC_V2
+};
+
+/* EXYNOS5250 ADC_V1 registers definitions */
+#define ADC_V1_CON(x)		((x) + 0x00)
+#define ADC_V1_DLY(x)		((x) + 0x08)
+#define ADC_V1_DATX(x)		((x) + 0x0C)
+#define ADC_V1_INTCLR(x)	((x) + 0x18)
+#define ADC_V1_MUX(x)		((x) + 0x1c)
+
+/* EXYNOS5410 ADC_V2 registers definitions */
+#define ADC_V2_CON1(x)		((x) + 0x00)
+#define ADC_V2_CON2(x)		((x) + 0x04)
+#define ADC_V2_STAT(x)		((x) + 0x08)
+#define ADC_V2_INT_EN(x)	((x) + 0x10)
+#define ADC_V2_INT_ST(x)	((x) + 0x14)
+#define ADC_V2_VER(x)		((x) + 0x20)
+
+/* Bit definitions for ADC_V1 */
+#define ADC_V1_CON_RES		(1u << 16)
+#define ADC_V1_CON_PRSCEN	(1u << 14)
+#define ADC_V1_CON_PRSCLV(x)	(((x) & 0xFF) << 6)
+#define ADC_V1_CON_STANDBY	(1u << 2)
+
+/* Bit definitions for ADC_V2 */
+#define ADC_V2_CON1_SOFT_RESET	(1u << 2)
+
+#define ADC_V2_CON2_OSEL	(1u << 10)
+#define ADC_V2_CON2_ESEL	(1u << 9)
+#define ADC_V2_CON2_HIGHF	(1u << 8)
+#define ADC_V2_CON2_C_TIME(x)	(((x) & 7) << 4)
+#define ADC_V2_CON2_ACH_SEL(x)	(((x) & 0xF) << 0)
+#define ADC_V2_CON2_ACH_MASK	0xF
+
+/* Bit definitions common for ADC_V1 and ADC_V2 */
+#define ADC_V1_CON_EN_START		(1u << 0)
+#define ADC_V1_DATX_MASK	0xFFF
+
+struct exynos5_adc {
+	void __iomem		*regs;
+	struct clk		*clk;
+	unsigned int		irq;
+	struct regulator	*vdd;
+
+	struct completion	completion;
+
+	struct iio_map		*map;
+	u32			value;
+	unsigned int            version;
+};
+
+static const struct of_device_id exynos5_adc_match[] = {
+	{ .compatible = "samsung,exynos5250-adc", .data = (void *)ADC_V1 },
+	{ .compatible = "samsung,exynos5410-adc", .data = (void *)ADC_V2 },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_adc_match);
+
+static inline unsigned int exynos5_adc_get_version(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+
+	match = of_match_node(exynos5_adc_match, pdev->dev.of_node);
+	return (unsigned int)match->data;
+}
+
+/* default maps used by iio consumer (ex: ntc-thermistor driver) */
+static struct iio_map exynos5_adc_iio_map[] = {
+	{
+		.consumer_dev_name = "0.ncp15wb473",
+		.consumer_channel = "adc3",
+		.adc_channel_label = "adc3",
+	},
+	{
+		.consumer_dev_name = "1.ncp15wb473",
+		.consumer_channel = "adc4",
+		.adc_channel_label = "adc4",
+	},
+	{
+		.consumer_dev_name = "2.ncp15wb473",
+		.consumer_channel = "adc5",
+		.adc_channel_label = "adc5",
+	},
+	{
+		.consumer_dev_name = "3.ncp15wb473",
+		.consumer_channel = "adc6",
+		.adc_channel_label = "adc6",
+	},
+	{},
+};
+
+static int exynos5_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val,
+				int *val2,
+				long mask)
+{
+	struct exynos5_adc *info = iio_priv(indio_dev);
+	u32 con1, con2;
+
+	if (mask == IIO_CHAN_INFO_RAW) {
+		mutex_lock(&indio_dev->mlock);
+
+		/* Select the channel to be used and Trigger conversion */
+		if (info->version == ADC_V2) {
+			con2 = readl(ADC_V2_CON2(info->regs));
+			con2 &= ~ADC_V2_CON2_ACH_MASK;
+			con2 |= ADC_V2_CON2_ACH_SEL(chan->address);
+			writel(con2, ADC_V2_CON2(info->regs));
+
+			con1 = readl(ADC_V2_CON1(info->regs));
+			writel(con1 | ADC_V1_CON_EN_START,
+					ADC_V2_CON1(info->regs));
+		} else {
+			writel(chan->address, ADC_V1_MUX(info->regs));
+
+			con1 = readl(ADC_V1_CON(info->regs));
+			writel(con1 | ADC_V1_CON_EN_START,
+					ADC_V1_CON(info->regs));
+		}
+
+		wait_for_completion(&info->completion);
+		*val = info->value;
+
+		mutex_unlock(&indio_dev->mlock);
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static irqreturn_t exynos5_adc_isr(int irq, void *dev_id)
+{
+	struct exynos5_adc *info = (struct exynos5_adc *)dev_id;
+
+	/* Read value */
+	info->value = readl(ADC_V1_DATX(info->regs)) &
+						ADC_V1_DATX_MASK;
+	/* clear irq */
+	if (info->version == ADC_V2)
+		writel(1, ADC_V2_INT_ST(info->regs));
+	else
+		writel(1, ADC_V1_INTCLR(info->regs));
+
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static int exynos5_adc_reg_access(struct iio_dev *indio_dev,
+			      unsigned reg, unsigned writeval,
+			      unsigned *readval)
+{
+	struct exynos5_adc *info = iio_priv(indio_dev);
+	u32 ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	if (readval != NULL) {
+		ret = readl(info->regs + reg);
+		*readval = ret;
+	} else
+		ret = -EINVAL;
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static const struct iio_info exynos5_adc_iio_info = {
+	.read_raw = &exynos5_read_raw,
+	.debugfs_reg_access = &exynos5_adc_reg_access,
+	.driver_module = THIS_MODULE,
+};
+
+#define ADC_CHANNEL(_index, _id) {			\
+	.type = IIO_VOLTAGE,				\
+	.indexed = 1,					\
+	.channel = _index,				\
+	.address = _index,				\
+	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,	\
+	.datasheet_name = _id,				\
+}
+
+static const struct iio_chan_spec exynos5_adc_iio_channels[] = {
+	ADC_CHANNEL(0, "adc0"),
+	ADC_CHANNEL(1, "adc1"),
+	ADC_CHANNEL(2, "adc2"),
+	ADC_CHANNEL(3, "adc3"),
+	ADC_CHANNEL(4, "adc4"),
+	ADC_CHANNEL(5, "adc5"),
+	ADC_CHANNEL(6, "adc6"),
+	ADC_CHANNEL(7, "adc7"),
+	ADC_CHANNEL(8, "adc8"),
+	ADC_CHANNEL(9, "adc9"),
+};
+
+static int exynos5_adc_remove_devices(struct device *dev, void *c)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	platform_device_unregister(pdev);
+
+	return 0;
+}
+
+static void exynos5_adc_hw_init(struct exynos5_adc *info)
+{
+	u32 con1, con2;
+
+	if (info->version == ADC_V2) {
+		con1 = ADC_V2_CON1_SOFT_RESET;
+		writel(con1, ADC_V2_CON1(info->regs));
+
+		con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
+			ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
+		writel(con2, ADC_V2_CON2(info->regs));
+
+		/* Enable interrupts */
+		writel(1, ADC_V2_INT_EN(info->regs));
+	} else {
+		/* set default prescaler values and Enable prescaler */
+		con1 =  ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
+
+		/* Enable 12-bit ADC resolution */
+		con1 |= ADC_V1_CON_RES;
+		writel(con1, ADC_V1_CON(info->regs));
+	}
+}
+
+static int exynos5_adc_probe(struct platform_device *pdev)
+{
+	struct exynos5_adc *info = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev = NULL;
+	struct resource	*mem;
+	int ret = -ENODEV;
+	int irq;
+
+	if (!np)
+		return ret;
+
+	indio_dev = iio_device_alloc(sizeof(struct exynos5_adc));
+	if (!indio_dev) {
+		dev_err(&pdev->dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	info = iio_priv(indio_dev);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	info->regs = devm_request_and_ioremap(&pdev->dev, mem);
+	if (!info->regs)
+		return -ENOMEM;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		ret = irq;
+		goto err_iio;
+	}
+
+	info->irq = irq;
+
+	init_completion(&info->completion);
+
+	ret = request_irq(info->irq, exynos5_adc_isr,
+					0, dev_name(&pdev->dev), info);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+							info->irq);
+		goto err_iio;
+	}
+
+	info->clk = devm_clk_get(&pdev->dev, "adc");
+	if (IS_ERR(info->clk)) {
+		dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
+							PTR_ERR(info->clk));
+		ret = PTR_ERR(info->clk);
+		goto err_irq;
+	}
+
+	info->vdd = devm_regulator_get(&pdev->dev, "vdd");
+	if (IS_ERR(info->vdd)) {
+		dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
+							PTR_ERR(info->vdd));
+		ret = PTR_ERR(info->vdd);
+		goto err_irq;
+	}
+
+	info->version = exynos5_adc_get_version(pdev);
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	indio_dev->name = dev_name(&pdev->dev);
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->dev.of_node = pdev->dev.of_node;
+	indio_dev->info = &exynos5_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = exynos5_adc_iio_channels;
+
+	if (info->version == ADC_V1)
+		/* ADC core in EXYNOS5250 has 8 channels */
+		indio_dev->num_channels =
+				ARRAY_SIZE(exynos5_adc_iio_channels) - 2;
+	else
+		/* ADC core in EXYNOS5410 has 10 channels */
+		indio_dev->num_channels =
+				ARRAY_SIZE(exynos5_adc_iio_channels);
+
+	info->map = exynos5_adc_iio_map;
+
+	ret = iio_map_array_register(indio_dev, info->map);
+	if (ret) {
+		dev_err(&indio_dev->dev, "failed mapping iio, err: %d\n", ret);
+		goto err_irq;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto err_map;
+
+	ret = regulator_enable(info->vdd);
+	if (ret)
+		goto err_iio_dev;
+
+	clk_prepare_enable(info->clk);
+
+	exynos5_adc_hw_init(info);
+
+	ret = of_platform_populate(np, exynos5_adc_match, NULL, &pdev->dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed adding child nodes\n");
+		goto err_of_populate;
+	}
+
+	return 0;
+
+err_of_populate:
+	device_for_each_child(&pdev->dev, NULL, exynos5_adc_remove_devices);
+	clk_disable_unprepare(info->clk);
+	regulator_disable(info->vdd);
+err_iio_dev:
+	iio_device_unregister(indio_dev);
+err_map:
+	iio_map_array_unregister(indio_dev, info->map);
+err_irq:
+	free_irq(info->irq, info);
+err_iio:
+	iio_device_free(indio_dev);
+	return ret;
+}
+
+static int exynos5_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct exynos5_adc *info = iio_priv(indio_dev);
+
+	clk_disable_unprepare(info->clk);
+	regulator_disable(info->vdd);
+	iio_device_unregister(indio_dev);
+	iio_map_array_unregister(indio_dev, info->map);
+	free_irq(info->irq, info);
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos5_adc_suspend(struct device *dev)
+{
+	struct exynos5_adc *info = dev_get_data(dev);
+	u32 con;
+
+	if (info->version == ADC_V2) {
+		con = readl(ADC_V2_CON1(info->regs));
+		con &= ~ADC_V1_CON_EN_START;
+		writel(con, ADC_V2_CON1(info->regs));
+	} else {
+		con = readl(ADC_V1_CON(info->regs));
+		con |= ADC_V1_CON_STANDBY;
+		writel(con, ADC_V1_CON(info->regs));
+	}
+
+	clk_disable_unprepare(info->clk);
+	regulator_disable(info->vdd);
+
+	return 0;
+}
+
+static int exynos5_adc_resume(struct device *dev)
+{
+	struct exynos5_adc *info = dev_get_data(dev);
+	int ret;
+
+	ret = regulator_enable(info->vdd);
+	if (ret)
+		return ret;
+
+	clk_prepare_enable(info->clk);
+
+	exynos5_adc_hw_init(info);
+
+	return 0;
+}
+
+#else
+#define exynos5_adc_suspend NULL
+#define exynos5_adc_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(exynos5_adc_pm_ops,
+			exynos5_adc_suspend,
+			exynos5_adc_resume);
+
+static struct platform_driver exynos5_adc_driver = {
+	.probe		= exynos5_adc_probe,
+	.remove		= exynos5_adc_remove,
+	.driver		= {
+		.name	= "exynos5-adc",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(exynos5_adc_match),
+		.pm	= &exynos5_adc_pm_ops,
+	},
+};
+
+module_platform_driver(exynos5_adc_driver);
+
+MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5

  parent reply	other threads:[~2013-01-24  4:59 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-01-21 13:37 [PATCH] iio: adc: add exynos5 adc driver under iio framwork Naveen Krishna Chatradhi
2013-01-21 13:37 ` Naveen Krishna Chatradhi
2013-01-22  9:44 ` Lars-Peter Clausen
2013-01-22  9:44   ` Lars-Peter Clausen
2013-01-22 14:03   ` Naveen Krishna Ch
2013-01-22 14:03     ` Naveen Krishna Ch
2013-01-22 14:27 ` Naveen Krishna Chatradhi
2013-01-23  4:58 ` Naveen Krishna Chatradhi
2013-01-23 12:52   ` Lars-Peter Clausen
2013-01-24  0:42     ` Doug Anderson
2013-01-24  0:42       ` Doug Anderson
2013-01-24  9:54       ` Lars-Peter Clausen
2013-01-24  9:54         ` Lars-Peter Clausen
2013-01-24 14:20         ` Naveen Krishna Ch
2013-01-24 14:20           ` Naveen Krishna Ch
2013-01-24 14:20           ` Naveen Krishna Ch
2013-01-24 18:11           ` Lars-Peter Clausen
2013-01-24 18:11             ` Lars-Peter Clausen
2013-01-24 16:12         ` Doug Anderson
2013-01-24 18:19           ` Lars-Peter Clausen
2013-01-24 18:19             ` Lars-Peter Clausen
2013-01-24 19:15             ` Tomasz Figa
2013-01-24 19:15               ` Tomasz Figa
2013-01-24 19:30               ` Lars-Peter Clausen
2013-02-12 21:07   ` Guenter Roeck
2013-02-13  2:48     ` Naveen Krishna Ch
2013-02-13  2:48       ` Naveen Krishna Ch
2013-02-13  2:48       ` Naveen Krishna Ch
2013-02-13 11:05       ` Naveen Krishna Ch
2013-02-13 11:05         ` Naveen Krishna Ch
2013-02-13 11:05         ` Naveen Krishna Ch
2013-02-13 13:16       ` Naveen Krishna Ch
2013-02-13 13:16         ` Naveen Krishna Ch
2013-02-13 13:30         ` Lars-Peter Clausen
2013-02-13 13:30           ` Lars-Peter Clausen
2013-02-13 13:53           ` Naveen Krishna Ch
2013-02-13 13:53             ` Naveen Krishna Ch
2013-02-13 13:53             ` Naveen Krishna Ch
2013-02-13 14:05             ` Lars-Peter Clausen
2013-02-13 14:05               ` Lars-Peter Clausen
2013-02-13 15:51         ` Guenter Roeck
2013-02-13 15:51           ` Guenter Roeck
2013-02-13 15:51           ` Guenter Roeck
2013-01-24  4:58 ` Naveen Krishna Chatradhi [this message]
2013-01-24  4:58   ` [PATCH] " Naveen Krishna Chatradhi
2013-01-26 10:57   ` Jonathan Cameron
2013-01-26 10:57     ` Jonathan Cameron
2013-01-30  6:02     ` Naveen Krishna Ch
2013-01-30  6:02       ` Naveen Krishna Ch
2013-01-24  5:05 ` Naveen Krishna Chatradhi
2013-02-12  1:22   ` Olof Johansson
2013-02-14 12:11 ` [PATCH v6] iio: adc: add exynos " Naveen Krishna Chatradhi
2013-02-14 12:11   ` Naveen Krishna Chatradhi
2013-02-14 20:55   ` Lars-Peter Clausen
2013-02-14 20:55     ` Lars-Peter Clausen
2013-02-15  6:56   ` [PATCH v7] " Naveen Krishna Chatradhi
2013-02-15 13:13     ` Lars-Peter Clausen
2013-02-15 13:17       ` Naveen Krishna Ch
2013-02-15 13:17         ` Naveen Krishna Ch
2013-02-15 13:17         ` Naveen Krishna Ch
2013-02-15 13:26         ` Lars-Peter Clausen
2013-02-15 13:26           ` Lars-Peter Clausen
2013-02-15 13:35           ` Naveen Krishna Ch
2013-02-15 13:35             ` Naveen Krishna Ch
2013-03-03 12:16             ` Jonathan Cameron
2013-03-03 12:16               ` Jonathan Cameron
2013-03-03 12:16               ` Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1359003514-1646-1-git-send-email-ch.naveen@samsung.com \
    --to=ch.naveen@samsung.com \
    --cc=dianders@chromium.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=naveenkrishna.ch@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.