linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean
@ 2014-07-22  2:04 Chanwoo Choi
  2014-07-22  2:04 ` [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability Chanwoo Choi
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Chanwoo Choi @ 2014-07-22  2:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset support Exynos3250 ADC (Analog Digital Converter) because
Exynos3250 has additional special clock for ADC IP.

Changes from v7:
- Add acked message by Arnd Bergmann
- Use two compatible string for Exynos3250 ADC as following:
  : compatible = "samsung,exynos3250-adc", "samsung,exynos-adc-v2;

Changes from v6:
- Use "exynos3250-adc" compatible string instead of "exynos3250-adc-v2"
- Use "sclk" clock name instead of "sclk_adc"
- Remove un-necessary macro for exyno-adc-data-v2 structure.
- Remove '(void *)' cast and mark the exynos-adc-data structure as 'const'
- Change the number of ADC channels (Exynos3250 has only two channels for ADC)

Changes from v5:
- Add acked message by Kukjin Kim
- Add reviewed messgae by Tomasz Figa
- Fix typo (for for -> for)

Changes from v4:
- Use 'exynos_adc_data' structure instead of 'exynos_adc_ops' structure
  and remove enum variable of ADC version
- Fix wrong name of special clock (sclk_tsadc -> sclk_adc)
- Add reviewed message by Naveen Krishna Chatradhi
- Add functions for ADC clock control

Changes from v3:
- Add new 'exynos_adc_ops' structure to improve readability according to
 Tomasz Figa comment[1]
 [1] https://lkml.org/lkml/2014/4/16/238
- Add new 'exynos3250-adc-v2' compatible string to support Exynos3250 ADC
- Fix wrong compaitlbe string of ADC in Exynos3250 dtsi file

Changes from v2:
- Check return value of clock function to deal with error exception
- Fix minor coding style to improve readability

Changes from v1:
- Add new "samsung,exynos-adc-v3" compatible to support Exynos3250 ADC
- Add a patch about DT binding documentation

Chanwoo Choi (4):
  iio: adc: exynos_adc: Add exynos_adc_data structure to improve
    readability
  iio: adc: exynos_adc: Control special clock of ADC to support
    Exynos3250 ADC
  iio: devicetree: Add DT binding documentation for Exynos3250 ADC
  ARM: dts: Fix wrong compatible string for Exynos3250 ADC

 .../devicetree/bindings/arm/samsung/exynos-adc.txt |  25 +-
 arch/arm/boot/dts/exynos3250.dtsi                  |   5 +-
 drivers/iio/adc/exynos_adc.c                       | 335 +++++++++++++++------
 3 files changed, 275 insertions(+), 90 deletions(-)

-- 
1.8.0

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability
  2014-07-22  2:04 [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
@ 2014-07-22  2:04 ` Chanwoo Choi
  2014-07-23 20:55   ` Jonathan Cameron
  2014-07-22  2:04 ` [PATCHv8 2/4] iio: adc: exynos_adc: Control special clock of ADC to support Exynos3250 ADC Chanwoo Choi
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Chanwoo Choi @ 2014-07-22  2:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset add 'exynos_adc_data' structure which includes some functions
to control ADC operation and specific data according to ADC version (v1 or v2).

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/iio/adc/exynos_adc.c | 226 ++++++++++++++++++++++++++++---------------
 1 file changed, 147 insertions(+), 79 deletions(-)

diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 010578f..dde4ca8 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -39,11 +39,6 @@
 #include <linux/iio/machine.h>
 #include <linux/iio/driver.h>
 
-enum adc_version {
-	ADC_V1,
-	ADC_V2
-};
-
 /* EXYNOS4412/5250 ADC_V1 registers definitions */
 #define ADC_V1_CON(x)		((x) + 0x00)
 #define ADC_V1_DLY(x)		((x) + 0x08)
@@ -85,6 +80,7 @@ enum adc_version {
 #define EXYNOS_ADC_TIMEOUT	(msecs_to_jiffies(100))
 
 struct exynos_adc {
+	struct exynos_adc_data	*data;
 	void __iomem		*regs;
 	void __iomem		*enable_reg;
 	struct clk		*clk;
@@ -97,43 +93,139 @@ struct exynos_adc {
 	unsigned int            version;
 };
 
-static const struct of_device_id exynos_adc_match[] = {
-	{ .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 },
-	{ .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 },
-	{},
+struct exynos_adc_data {
+	int num_channels;
+
+	void (*init_hw)(struct exynos_adc *info);
+	void (*exit_hw)(struct exynos_adc *info);
+	void (*clear_irq)(struct exynos_adc *info);
+	void (*start_conv)(struct exynos_adc *info, unsigned long addr);
 };
-MODULE_DEVICE_TABLE(of, exynos_adc_match);
 
-static inline unsigned int exynos_adc_get_version(struct platform_device *pdev)
+static void exynos_adc_v1_init_hw(struct exynos_adc *info)
 {
-	const struct of_device_id *match;
+	u32 con1;
 
-	match = of_match_node(exynos_adc_match, pdev->dev.of_node);
-	return (unsigned int)match->data;
+	writel(1, info->enable_reg);
+
+	/* 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 void exynos_adc_v1_exit_hw(struct exynos_adc *info)
+{
+	u32 con;
+
+	writel(0, info->enable_reg);
+
+	con = readl(ADC_V1_CON(info->regs));
+	con |= ADC_V1_CON_STANDBY;
+	writel(con, ADC_V1_CON(info->regs));
+}
+
+static void exynos_adc_v1_clear_irq(struct exynos_adc *info)
+{
+	writel(1, ADC_V1_INTCLR(info->regs));
 }
 
-static void exynos_adc_hw_init(struct exynos_adc *info)
+static void exynos_adc_v1_start_conv(struct exynos_adc *info,
+				     unsigned long addr)
+{
+	u32 con1;
+
+	writel(addr, ADC_V1_MUX(info->regs));
+
+	con1 = readl(ADC_V1_CON(info->regs));
+	writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+static const struct exynos_adc_data const exynos_adc_v1_data = {
+	.num_channels	= MAX_ADC_V1_CHANNELS,
+
+	.init_hw	= exynos_adc_v1_init_hw,
+	.exit_hw	= exynos_adc_v1_exit_hw,
+	.clear_irq	= exynos_adc_v1_clear_irq,
+	.start_conv	= exynos_adc_v1_start_conv,
+};
+
+static void exynos_adc_v2_init_hw(struct exynos_adc *info)
 {
 	u32 con1, con2;
 
-	if (info->version == ADC_V2) {
-		con1 = ADC_V2_CON1_SOFT_RESET;
-		writel(con1, ADC_V2_CON1(info->regs));
+	writel(1, info->enable_reg);
 
-		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));
+	con1 = ADC_V2_CON1_SOFT_RESET;
+	writel(con1, ADC_V2_CON1(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;
+	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 12-bit ADC resolution */
-		con1 |= ADC_V1_CON_RES;
-		writel(con1, ADC_V1_CON(info->regs));
-	}
+	/* Enable interrupts */
+	writel(1, ADC_V2_INT_EN(info->regs));
+}
+
+static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
+{
+	u32 con;
+
+	writel(0, info->enable_reg);
+
+	con = readl(ADC_V2_CON1(info->regs));
+	con &= ~ADC_CON_EN_START;
+	writel(con, ADC_V2_CON1(info->regs));
+}
+
+static void exynos_adc_v2_clear_irq(struct exynos_adc *info)
+{
+	writel(1, ADC_V2_INT_ST(info->regs));
+}
+
+static void exynos_adc_v2_start_conv(struct exynos_adc *info,
+				     unsigned long addr)
+{
+	u32 con1, con2;
+
+	con2 = readl(ADC_V2_CON2(info->regs));
+	con2 &= ~ADC_V2_CON2_ACH_MASK;
+	con2 |= ADC_V2_CON2_ACH_SEL(addr);
+	writel(con2, ADC_V2_CON2(info->regs));
+
+	con1 = readl(ADC_V2_CON1(info->regs));
+	writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs));
+}
+
+static const struct exynos_adc_data const exynos_adc_v2_data = {
+	.num_channels	= MAX_ADC_V2_CHANNELS,
+
+	.init_hw	= exynos_adc_v2_init_hw,
+	.exit_hw	= exynos_adc_v2_exit_hw,
+	.clear_irq	= exynos_adc_v2_clear_irq,
+	.start_conv	= exynos_adc_v2_start_conv,
+};
+
+static const struct of_device_id exynos_adc_match[] = {
+	{
+		.compatible = "samsung,exynos-adc-v1",
+		.data = &exynos_adc_v1_data,
+	}, {
+		.compatible = "samsung,exynos-adc-v2",
+		.data = &exynos_adc_v2_data,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos_adc_match);
+
+static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+
+	match = of_match_node(exynos_adc_match, pdev->dev.of_node);
+	return (struct exynos_adc_data *)match->data;
 }
 
 static int exynos_read_raw(struct iio_dev *indio_dev,
@@ -144,7 +236,6 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
 {
 	struct exynos_adc *info = iio_priv(indio_dev);
 	unsigned long timeout;
-	u32 con1, con2;
 	int ret;
 
 	if (mask != IIO_CHAN_INFO_RAW)
@@ -154,28 +245,15 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
 	reinit_completion(&info->completion);
 
 	/* 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_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_CON_EN_START,
-				ADC_V1_CON(info->regs));
-	}
+	if (info->data->start_conv)
+		info->data->start_conv(info, chan->address);
 
 	timeout = wait_for_completion_timeout
 			(&info->completion, EXYNOS_ADC_TIMEOUT);
 	if (timeout == 0) {
 		dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
-		exynos_adc_hw_init(info);
+		if (info->data->init_hw)
+			info->data->init_hw(info);
 		ret = -ETIMEDOUT;
 	} else {
 		*val = info->value;
@@ -193,13 +271,11 @@ static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
 	struct exynos_adc *info = (struct exynos_adc *)dev_id;
 
 	/* Read value */
-	info->value = readl(ADC_V1_DATX(info->regs)) &
-						ADC_DATX_MASK;
+	info->value = readl(ADC_V1_DATX(info->regs)) & ADC_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));
+	if (info->data->clear_irq)
+		info->data->clear_irq(info);
 
 	complete(&info->completion);
 
@@ -277,6 +353,12 @@ static int exynos_adc_probe(struct platform_device *pdev)
 
 	info = iio_priv(indio_dev);
 
+	info->data = exynos_adc_get_data(pdev);
+	if (!info->data) {
+		dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
+		return -EINVAL;
+	}
+
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	info->regs = devm_ioremap_resource(&pdev->dev, mem);
 	if (IS_ERR(info->regs))
@@ -319,10 +401,6 @@ static int exynos_adc_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_disable_reg;
 
-	writel(1, info->enable_reg);
-
-	info->version = exynos_adc_get_version(pdev);
-
 	platform_set_drvdata(pdev, indio_dev);
 
 	indio_dev->name = dev_name(&pdev->dev);
@@ -331,11 +409,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
 	indio_dev->info = &exynos_adc_iio_info;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->channels = exynos_adc_iio_channels;
-
-	if (info->version == ADC_V1)
-		indio_dev->num_channels = MAX_ADC_V1_CHANNELS;
-	else
-		indio_dev->num_channels = MAX_ADC_V2_CHANNELS;
+	indio_dev->num_channels = info->data->num_channels;
 
 	ret = request_irq(info->irq, exynos_adc_isr,
 					0, dev_name(&pdev->dev), info);
@@ -349,7 +423,8 @@ static int exynos_adc_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_irq;
 
-	exynos_adc_hw_init(info);
+	if (info->data->init_hw)
+		info->data->init_hw(info);
 
 	ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
 	if (ret < 0) {
@@ -366,7 +441,8 @@ err_of_populate:
 err_irq:
 	free_irq(info->irq, info);
 err_disable_clk:
-	writel(0, info->enable_reg);
+	if (info->data->exit_hw)
+		info->data->exit_hw(info);
 	clk_disable_unprepare(info->clk);
 err_disable_reg:
 	regulator_disable(info->vdd);
@@ -382,7 +458,8 @@ static int exynos_adc_remove(struct platform_device *pdev)
 				exynos_adc_remove_devices);
 	iio_device_unregister(indio_dev);
 	free_irq(info->irq, info);
-	writel(0, info->enable_reg);
+	if (info->data->exit_hw)
+		info->data->exit_hw(info);
 	clk_disable_unprepare(info->clk);
 	regulator_disable(info->vdd);
 
@@ -394,19 +471,10 @@ static int exynos_adc_suspend(struct device *dev)
 {
 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
 	struct exynos_adc *info = iio_priv(indio_dev);
-	u32 con;
 
-	if (info->version == ADC_V2) {
-		con = readl(ADC_V2_CON1(info->regs));
-		con &= ~ADC_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));
-	}
+	if (info->data->exit_hw)
+		info->data->exit_hw(info);
 
-	writel(0, info->enable_reg);
 	clk_disable_unprepare(info->clk);
 	regulator_disable(info->vdd);
 
@@ -427,8 +495,8 @@ static int exynos_adc_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	writel(1, info->enable_reg);
-	exynos_adc_hw_init(info);
+	if (info->data->init_hw)
+		info->data->init_hw(info);
 
 	return 0;
 }
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCHv8 2/4] iio: adc: exynos_adc: Control special clock of ADC to support Exynos3250 ADC
  2014-07-22  2:04 [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
  2014-07-22  2:04 ` [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability Chanwoo Choi
@ 2014-07-22  2:04 ` Chanwoo Choi
  2014-07-23 20:57   ` Jonathan Cameron
  2014-07-22  2:04 ` [PATCHv8 3/4] iio: devicetree: Add DT binding documentation for " Chanwoo Choi
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Chanwoo Choi @ 2014-07-22  2:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patch control special clock for ADC in Exynos series's FSYS block.
If special clock of ADC is registerd on clock list of common clk framework,
Exynos ADC drvier have to control this clock.

Exynos3250/Exynos4/Exynos5 has 'adc' clock as following:
- 'adc' clock: bus clock for ADC

Exynos3250 has additional 'sclk_adc' clock as following:
- 'sclk_adc' clock: special clock for ADC which provide clock to internal ADC

Exynos 4210/4212/4412 and Exynos5250/5420 has not included 'sclk_adc' clock
in FSYS_BLK. But, Exynos3250 based on Cortex-A7 has only included 'sclk_adc'
clock in FSYS_BLK.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/iio/adc/exynos_adc.c | 111 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 103 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index dde4ca8..87e0895 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -24,6 +24,7 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/io.h>
@@ -70,8 +71,9 @@
 #define ADC_V2_CON2_ACH_SEL(x)	(((x) & 0xF) << 0)
 #define ADC_V2_CON2_ACH_MASK	0xF
 
-#define MAX_ADC_V2_CHANNELS	10
-#define MAX_ADC_V1_CHANNELS	8
+#define MAX_ADC_V2_CHANNELS		10
+#define MAX_ADC_V1_CHANNELS		8
+#define MAX_EXYNOS3250_ADC_CHANNELS	2
 
 /* Bit definitions common for ADC_V1 and ADC_V2 */
 #define ADC_CON_EN_START	(1u << 0)
@@ -81,9 +83,11 @@
 
 struct exynos_adc {
 	struct exynos_adc_data	*data;
+	struct device		*dev;
 	void __iomem		*regs;
 	void __iomem		*enable_reg;
 	struct clk		*clk;
+	struct clk		*sclk;
 	unsigned int		irq;
 	struct regulator	*vdd;
 
@@ -95,6 +99,7 @@ struct exynos_adc {
 
 struct exynos_adc_data {
 	int num_channels;
+	bool needs_sclk;
 
 	void (*init_hw)(struct exynos_adc *info);
 	void (*exit_hw)(struct exynos_adc *info);
@@ -102,6 +107,66 @@ struct exynos_adc_data {
 	void (*start_conv)(struct exynos_adc *info, unsigned long addr);
 };
 
+static void exynos_adc_unprepare_clk(struct exynos_adc *info)
+{
+	if (info->data->needs_sclk)
+		clk_unprepare(info->sclk);
+	clk_unprepare(info->clk);
+}
+
+static int exynos_adc_prepare_clk(struct exynos_adc *info)
+{
+	int ret;
+
+	ret = clk_prepare(info->clk);
+	if (ret) {
+		dev_err(info->dev, "failed preparing adc clock: %d\n", ret);
+		return ret;
+	}
+
+	if (info->data->needs_sclk) {
+		ret = clk_prepare(info->sclk);
+		if (ret) {
+			clk_unprepare(info->clk);
+			dev_err(info->dev,
+				"failed preparing sclk_adc clock: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void exynos_adc_disable_clk(struct exynos_adc *info)
+{
+	if (info->data->needs_sclk)
+		clk_disable(info->sclk);
+	clk_disable(info->clk);
+}
+
+static int exynos_adc_enable_clk(struct exynos_adc *info)
+{
+	int ret;
+
+	ret = clk_enable(info->clk);
+	if (ret) {
+		dev_err(info->dev, "failed enabling adc clock: %d\n", ret);
+		return ret;
+	}
+
+	if (info->data->needs_sclk) {
+		ret = clk_enable(info->sclk);
+		if (ret) {
+			clk_disable(info->clk);
+			dev_err(info->dev,
+				"failed enabling sclk_adc clock: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static void exynos_adc_v1_init_hw(struct exynos_adc *info)
 {
 	u32 con1;
@@ -208,6 +273,16 @@ static const struct exynos_adc_data const exynos_adc_v2_data = {
 	.start_conv	= exynos_adc_v2_start_conv,
 };
 
+static const struct exynos_adc_data const exynos3250_adc_data = {
+	.num_channels	= MAX_EXYNOS3250_ADC_CHANNELS,
+	.needs_sclk	= true,
+
+	.init_hw	= exynos_adc_v2_init_hw,
+	.exit_hw	= exynos_adc_v2_exit_hw,
+	.clear_irq	= exynos_adc_v2_clear_irq,
+	.start_conv	= exynos_adc_v2_start_conv,
+};
+
 static const struct of_device_id exynos_adc_match[] = {
 	{
 		.compatible = "samsung,exynos-adc-v1",
@@ -215,6 +290,9 @@ static const struct of_device_id exynos_adc_match[] = {
 	}, {
 		.compatible = "samsung,exynos-adc-v2",
 		.data = &exynos_adc_v2_data,
+	}, {
+		.compatible = "samsung,exynos3250-adc",
+		.data = &exynos3250_adc_data,
 	},
 	{},
 };
@@ -376,6 +454,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
 	}
 
 	info->irq = irq;
+	info->dev = &pdev->dev;
 
 	init_completion(&info->completion);
 
@@ -386,6 +465,16 @@ static int exynos_adc_probe(struct platform_device *pdev)
 		return PTR_ERR(info->clk);
 	}
 
+	if (info->data->needs_sclk) {
+		info->sclk = devm_clk_get(&pdev->dev, "sclk");
+		if (IS_ERR(info->sclk)) {
+			dev_err(&pdev->dev,
+				"failed getting sclk clock, err = %ld\n",
+				PTR_ERR(info->sclk));
+			return PTR_ERR(info->sclk);
+		}
+	}
+
 	info->vdd = devm_regulator_get(&pdev->dev, "vdd");
 	if (IS_ERR(info->vdd)) {
 		dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
@@ -397,10 +486,14 @@ static int exynos_adc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(info->clk);
+	ret = exynos_adc_prepare_clk(info);
 	if (ret)
 		goto err_disable_reg;
 
+	ret = exynos_adc_enable_clk(info);
+	if (ret)
+		goto err_unprepare_clk;
+
 	platform_set_drvdata(pdev, indio_dev);
 
 	indio_dev->name = dev_name(&pdev->dev);
@@ -443,7 +536,9 @@ err_irq:
 err_disable_clk:
 	if (info->data->exit_hw)
 		info->data->exit_hw(info);
-	clk_disable_unprepare(info->clk);
+	exynos_adc_disable_clk(info);
+err_unprepare_clk:
+	exynos_adc_unprepare_clk(info);
 err_disable_reg:
 	regulator_disable(info->vdd);
 	return ret;
@@ -460,7 +555,8 @@ static int exynos_adc_remove(struct platform_device *pdev)
 	free_irq(info->irq, info);
 	if (info->data->exit_hw)
 		info->data->exit_hw(info);
-	clk_disable_unprepare(info->clk);
+	exynos_adc_disable_clk(info);
+	exynos_adc_unprepare_clk(info);
 	regulator_disable(info->vdd);
 
 	return 0;
@@ -474,8 +570,7 @@ static int exynos_adc_suspend(struct device *dev)
 
 	if (info->data->exit_hw)
 		info->data->exit_hw(info);
-
-	clk_disable_unprepare(info->clk);
+	exynos_adc_disable_clk(info);
 	regulator_disable(info->vdd);
 
 	return 0;
@@ -491,7 +586,7 @@ static int exynos_adc_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(info->clk);
+	ret = exynos_adc_enable_clk(info);
 	if (ret)
 		return ret;
 
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCHv8 3/4] iio: devicetree: Add DT binding documentation for Exynos3250 ADC
  2014-07-22  2:04 [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
  2014-07-22  2:04 ` [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability Chanwoo Choi
  2014-07-22  2:04 ` [PATCHv8 2/4] iio: adc: exynos_adc: Control special clock of ADC to support Exynos3250 ADC Chanwoo Choi
@ 2014-07-22  2:04 ` Chanwoo Choi
  2014-07-23 20:58   ` Jonathan Cameron
  2014-07-22  2:04 ` [PATCHv8 4/4] ARM: dts: Fix wrong compatible string " Chanwoo Choi
  2014-07-23  1:41 ` [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
  4 siblings, 1 reply; 10+ messages in thread
From: Chanwoo Choi @ 2014-07-22  2:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patch add DT binding documentation for Exynos3250 ADC IP. Exynos3250 has
special clock ('sclk_adc') for ADC which provide clock to internal ADC.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
 .../devicetree/bindings/arm/samsung/exynos-adc.txt | 25 ++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
index 832fe8c..adc61b0 100644
--- a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
+++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
@@ -14,14 +14,21 @@ Required properties:
 				for exynos4412/5250 controllers.
 			Must be "samsung,exynos-adc-v2" for
 				future controllers.
+			Must be "samsung,exynos3250-adc" for
+				controllers compatible with ADC of Exynos3250.
 - reg:			Contains ADC register address range (base address and
 			length) and the address of the phy enable register.
 - interrupts: 		Contains the interrupt information for the timer. The
 			format is being dependent on which interrupt controller
 			the Samsung device uses.
 - #io-channel-cells = <1>; As ADC has multiple outputs
-- clocks		From common clock binding: handle to adc clock.
-- clock-names		From common clock binding: Shall be "adc".
+- clocks		From common clock bindings: handles to clocks specified
+			in "clock-names" property, in the same order.
+- clock-names		From common clock bindings: list of clock input names
+			used by ADC block:
+			- "adc" : ADC bus clock
+			- "sclk" : ADC special clock (only for Exynos3250 and
+				   compatible ADC block)
 - vdd-supply		VDD input supply.
 
 Note: child nodes can be added for auto probing from device tree.
@@ -41,6 +48,20 @@ adc: adc at 12D10000 {
 	vdd-supply = <&buck5_reg>;
 };
 
+Example: adding device info in dtsi file for Exynos3250 with additional sclk
+
+adc: adc at 126C0000 {
+	compatible = "samsung,exynos3250-adc", "samsung,exynos-adc-v2;
+	reg = <0x126C0000 0x100>, <0x10020718 0x4>;
+	interrupts = <0 137 0>;
+	#io-channel-cells = <1>;
+	io-channel-ranges;
+
+	clocks = <&cmu CLK_TSADC>, <&cmu CLK_SCLK_TSADC>;
+	clock-names = "adc", "sclk";
+
+	vdd-supply = <&buck5_reg>;
+};
 
 Example: Adding child nodes in dts file
 
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCHv8 4/4] ARM: dts: Fix wrong compatible string for Exynos3250 ADC
  2014-07-22  2:04 [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
                   ` (2 preceding siblings ...)
  2014-07-22  2:04 ` [PATCHv8 3/4] iio: devicetree: Add DT binding documentation for " Chanwoo Choi
@ 2014-07-22  2:04 ` Chanwoo Choi
  2014-07-23 20:59   ` Jonathan Cameron
  2014-07-23  1:41 ` [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
  4 siblings, 1 reply; 10+ messages in thread
From: Chanwoo Choi @ 2014-07-22  2:04 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset fix wrong compatible string for Exynos3250 ADC. Exynos3250 SoC
need to control only special clock for ADC. Exynos SoC except for Exynos3250
has not included special clock for ADC. The exynos ADC driver can control
special clock if compatible string is 'exynos3250-adc-v2'.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Acked-by: Kukjin Kim <kgene.kim@samsung.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
 arch/arm/boot/dts/exynos3250.dtsi | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi
index 15c9c87..767189a 100644
--- a/arch/arm/boot/dts/exynos3250.dtsi
+++ b/arch/arm/boot/dts/exynos3250.dtsi
@@ -407,10 +407,11 @@
 		};
 
 		adc: adc at 126C0000 {
-			compatible = "samsung,exynos-adc-v3";
+			compatible = "samsung,exynos3250-adc",
+				     "samsung,exynos-adc-v2";
 			reg = <0x126C0000 0x100>, <0x10020718 0x4>;
 			interrupts = <0 137 0>;
-			clock-names = "adc", "sclk_tsadc";
+			clock-names = "adc", "sclk";
 			clocks = <&cmu CLK_TSADC>, <&cmu CLK_SCLK_TSADC>;
 			#io-channel-cells = <1>;
 			io-channel-ranges;
-- 
1.8.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean
  2014-07-22  2:04 [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
                   ` (3 preceding siblings ...)
  2014-07-22  2:04 ` [PATCHv8 4/4] ARM: dts: Fix wrong compatible string " Chanwoo Choi
@ 2014-07-23  1:41 ` Chanwoo Choi
  4 siblings, 0 replies; 10+ messages in thread
From: Chanwoo Choi @ 2014-07-23  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

Dear Jonathan,

Could you please pick this patchset for 3.17?

Best Regards,
Chanwoo Choi

On 07/22/2014 11:04 AM, Chanwoo Choi wrote:
> This patchset support Exynos3250 ADC (Analog Digital Converter) because
> Exynos3250 has additional special clock for ADC IP.
> 
> Changes from v7:
> - Add acked message by Arnd Bergmann
> - Use two compatible string for Exynos3250 ADC as following:
>   : compatible = "samsung,exynos3250-adc", "samsung,exynos-adc-v2;
> 
> Changes from v6:
> - Use "exynos3250-adc" compatible string instead of "exynos3250-adc-v2"
> - Use "sclk" clock name instead of "sclk_adc"
> - Remove un-necessary macro for exyno-adc-data-v2 structure.
> - Remove '(void *)' cast and mark the exynos-adc-data structure as 'const'
> - Change the number of ADC channels (Exynos3250 has only two channels for ADC)
> 
> Changes from v5:
> - Add acked message by Kukjin Kim
> - Add reviewed messgae by Tomasz Figa
> - Fix typo (for for -> for)
> 
> Changes from v4:
> - Use 'exynos_adc_data' structure instead of 'exynos_adc_ops' structure
>   and remove enum variable of ADC version
> - Fix wrong name of special clock (sclk_tsadc -> sclk_adc)
> - Add reviewed message by Naveen Krishna Chatradhi
> - Add functions for ADC clock control
> 
> Changes from v3:
> - Add new 'exynos_adc_ops' structure to improve readability according to
>  Tomasz Figa comment[1]
>  [1] https://lkml.org/lkml/2014/4/16/238
> - Add new 'exynos3250-adc-v2' compatible string to support Exynos3250 ADC
> - Fix wrong compaitlbe string of ADC in Exynos3250 dtsi file
> 
> Changes from v2:
> - Check return value of clock function to deal with error exception
> - Fix minor coding style to improve readability
> 
> Changes from v1:
> - Add new "samsung,exynos-adc-v3" compatible to support Exynos3250 ADC
> - Add a patch about DT binding documentation
> 
> Chanwoo Choi (4):
>   iio: adc: exynos_adc: Add exynos_adc_data structure to improve
>     readability
>   iio: adc: exynos_adc: Control special clock of ADC to support
>     Exynos3250 ADC
>   iio: devicetree: Add DT binding documentation for Exynos3250 ADC
>   ARM: dts: Fix wrong compatible string for Exynos3250 ADC
> 
>  .../devicetree/bindings/arm/samsung/exynos-adc.txt |  25 +-
>  arch/arm/boot/dts/exynos3250.dtsi                  |   5 +-
>  drivers/iio/adc/exynos_adc.c                       | 335 +++++++++++++++------
>  3 files changed, 275 insertions(+), 90 deletions(-)
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability
  2014-07-22  2:04 ` [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability Chanwoo Choi
@ 2014-07-23 20:55   ` Jonathan Cameron
  0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2014-07-23 20:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/07/14 03:04, Chanwoo Choi wrote:
> This patchset add 'exynos_adc_data' structure which includes some functions
> to control ADC operation and specific data according to ADC version (v1 or v2).
>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Reviewed-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
> Reviewed-by: Tomasz Figa <t.figa@samsung.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
Applied to the togreg branch of iio.git - initially pushed out as testing
for the autobuilders to play with it.

2 minor tweaks where you had duplicate consts that gcc moaned about during my
build tests.

Jonathan
> ---
>   drivers/iio/adc/exynos_adc.c | 226 ++++++++++++++++++++++++++++---------------
>   1 file changed, 147 insertions(+), 79 deletions(-)
>
> diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
> index 010578f..dde4ca8 100644
> --- a/drivers/iio/adc/exynos_adc.c
> +++ b/drivers/iio/adc/exynos_adc.c
> @@ -39,11 +39,6 @@
>   #include <linux/iio/machine.h>
>   #include <linux/iio/driver.h>
>
> -enum adc_version {
> -	ADC_V1,
> -	ADC_V2
> -};
> -
>   /* EXYNOS4412/5250 ADC_V1 registers definitions */
>   #define ADC_V1_CON(x)		((x) + 0x00)
>   #define ADC_V1_DLY(x)		((x) + 0x08)
> @@ -85,6 +80,7 @@ enum adc_version {
>   #define EXYNOS_ADC_TIMEOUT	(msecs_to_jiffies(100))
>
>   struct exynos_adc {
> +	struct exynos_adc_data	*data;
>   	void __iomem		*regs;
>   	void __iomem		*enable_reg;
>   	struct clk		*clk;
> @@ -97,43 +93,139 @@ struct exynos_adc {
>   	unsigned int            version;
>   };
>
> -static const struct of_device_id exynos_adc_match[] = {
> -	{ .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 },
> -	{ .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 },
> -	{},
> +struct exynos_adc_data {
> +	int num_channels;
> +
> +	void (*init_hw)(struct exynos_adc *info);
> +	void (*exit_hw)(struct exynos_adc *info);
> +	void (*clear_irq)(struct exynos_adc *info);
> +	void (*start_conv)(struct exynos_adc *info, unsigned long addr);
>   };
> -MODULE_DEVICE_TABLE(of, exynos_adc_match);
>
> -static inline unsigned int exynos_adc_get_version(struct platform_device *pdev)
> +static void exynos_adc_v1_init_hw(struct exynos_adc *info)
>   {
> -	const struct of_device_id *match;
> +	u32 con1;
>
> -	match = of_match_node(exynos_adc_match, pdev->dev.of_node);
> -	return (unsigned int)match->data;
> +	writel(1, info->enable_reg);
> +
> +	/* 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 void exynos_adc_v1_exit_hw(struct exynos_adc *info)
> +{
> +	u32 con;
> +
> +	writel(0, info->enable_reg);
> +
> +	con = readl(ADC_V1_CON(info->regs));
> +	con |= ADC_V1_CON_STANDBY;
> +	writel(con, ADC_V1_CON(info->regs));
> +}
> +
> +static void exynos_adc_v1_clear_irq(struct exynos_adc *info)
> +{
> +	writel(1, ADC_V1_INTCLR(info->regs));
>   }
>
> -static void exynos_adc_hw_init(struct exynos_adc *info)
> +static void exynos_adc_v1_start_conv(struct exynos_adc *info,
> +				     unsigned long addr)
> +{
> +	u32 con1;
> +
> +	writel(addr, ADC_V1_MUX(info->regs));
> +
> +	con1 = readl(ADC_V1_CON(info->regs));
> +	writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
> +}
> +
> +static const struct exynos_adc_data const exynos_adc_v1_data = {
duplicate const
> +	.num_channels	= MAX_ADC_V1_CHANNELS,
> +
> +	.init_hw	= exynos_adc_v1_init_hw,
> +	.exit_hw	= exynos_adc_v1_exit_hw,
> +	.clear_irq	= exynos_adc_v1_clear_irq,
> +	.start_conv	= exynos_adc_v1_start_conv,
> +};
> +
> +static void exynos_adc_v2_init_hw(struct exynos_adc *info)
>   {
>   	u32 con1, con2;
>
> -	if (info->version == ADC_V2) {
> -		con1 = ADC_V2_CON1_SOFT_RESET;
> -		writel(con1, ADC_V2_CON1(info->regs));
> +	writel(1, info->enable_reg);
>
> -		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));
> +	con1 = ADC_V2_CON1_SOFT_RESET;
> +	writel(con1, ADC_V2_CON1(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;
> +	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 12-bit ADC resolution */
> -		con1 |= ADC_V1_CON_RES;
> -		writel(con1, ADC_V1_CON(info->regs));
> -	}
> +	/* Enable interrupts */
> +	writel(1, ADC_V2_INT_EN(info->regs));
> +}
> +
> +static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
> +{
> +	u32 con;
> +
> +	writel(0, info->enable_reg);
> +
> +	con = readl(ADC_V2_CON1(info->regs));
> +	con &= ~ADC_CON_EN_START;
> +	writel(con, ADC_V2_CON1(info->regs));
> +}
> +
> +static void exynos_adc_v2_clear_irq(struct exynos_adc *info)
> +{
> +	writel(1, ADC_V2_INT_ST(info->regs));
> +}
> +
> +static void exynos_adc_v2_start_conv(struct exynos_adc *info,
> +				     unsigned long addr)
> +{
> +	u32 con1, con2;
> +
> +	con2 = readl(ADC_V2_CON2(info->regs));
> +	con2 &= ~ADC_V2_CON2_ACH_MASK;
> +	con2 |= ADC_V2_CON2_ACH_SEL(addr);
> +	writel(con2, ADC_V2_CON2(info->regs));
> +
> +	con1 = readl(ADC_V2_CON1(info->regs));
> +	writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs));
> +}
> +
> +static const struct exynos_adc_data const exynos_adc_v2_data = {
duplicate const
> +	.num_channels	= MAX_ADC_V2_CHANNELS,
> +
> +	.init_hw	= exynos_adc_v2_init_hw,
> +	.exit_hw	= exynos_adc_v2_exit_hw,
> +	.clear_irq	= exynos_adc_v2_clear_irq,
> +	.start_conv	= exynos_adc_v2_start_conv,
> +};
> +
> +static const struct of_device_id exynos_adc_match[] = {
> +	{
> +		.compatible = "samsung,exynos-adc-v1",
> +		.data = &exynos_adc_v1_data,
> +	}, {
> +		.compatible = "samsung,exynos-adc-v2",
> +		.data = &exynos_adc_v2_data,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, exynos_adc_match);
> +
> +static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +
> +	match = of_match_node(exynos_adc_match, pdev->dev.of_node);
> +	return (struct exynos_adc_data *)match->data;
>   }
>
>   static int exynos_read_raw(struct iio_dev *indio_dev,
> @@ -144,7 +236,6 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
>   {
>   	struct exynos_adc *info = iio_priv(indio_dev);
>   	unsigned long timeout;
> -	u32 con1, con2;
>   	int ret;
>
>   	if (mask != IIO_CHAN_INFO_RAW)
> @@ -154,28 +245,15 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
>   	reinit_completion(&info->completion);
>
>   	/* 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_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_CON_EN_START,
> -				ADC_V1_CON(info->regs));
> -	}
> +	if (info->data->start_conv)
> +		info->data->start_conv(info, chan->address);
>
>   	timeout = wait_for_completion_timeout
>   			(&info->completion, EXYNOS_ADC_TIMEOUT);
>   	if (timeout == 0) {
>   		dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
> -		exynos_adc_hw_init(info);
> +		if (info->data->init_hw)
> +			info->data->init_hw(info);
>   		ret = -ETIMEDOUT;
>   	} else {
>   		*val = info->value;
> @@ -193,13 +271,11 @@ static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
>   	struct exynos_adc *info = (struct exynos_adc *)dev_id;
>
>   	/* Read value */
> -	info->value = readl(ADC_V1_DATX(info->regs)) &
> -						ADC_DATX_MASK;
> +	info->value = readl(ADC_V1_DATX(info->regs)) & ADC_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));
> +	if (info->data->clear_irq)
> +		info->data->clear_irq(info);
>
>   	complete(&info->completion);
>
> @@ -277,6 +353,12 @@ static int exynos_adc_probe(struct platform_device *pdev)
>
>   	info = iio_priv(indio_dev);
>
> +	info->data = exynos_adc_get_data(pdev);
> +	if (!info->data) {
> +		dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
> +		return -EINVAL;
> +	}
> +
>   	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>   	info->regs = devm_ioremap_resource(&pdev->dev, mem);
>   	if (IS_ERR(info->regs))
> @@ -319,10 +401,6 @@ static int exynos_adc_probe(struct platform_device *pdev)
>   	if (ret)
>   		goto err_disable_reg;
>
> -	writel(1, info->enable_reg);
> -
> -	info->version = exynos_adc_get_version(pdev);
> -
>   	platform_set_drvdata(pdev, indio_dev);
>
>   	indio_dev->name = dev_name(&pdev->dev);
> @@ -331,11 +409,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
>   	indio_dev->info = &exynos_adc_iio_info;
>   	indio_dev->modes = INDIO_DIRECT_MODE;
>   	indio_dev->channels = exynos_adc_iio_channels;
> -
> -	if (info->version == ADC_V1)
> -		indio_dev->num_channels = MAX_ADC_V1_CHANNELS;
> -	else
> -		indio_dev->num_channels = MAX_ADC_V2_CHANNELS;
> +	indio_dev->num_channels = info->data->num_channels;
>
>   	ret = request_irq(info->irq, exynos_adc_isr,
>   					0, dev_name(&pdev->dev), info);
> @@ -349,7 +423,8 @@ static int exynos_adc_probe(struct platform_device *pdev)
>   	if (ret)
>   		goto err_irq;
>
> -	exynos_adc_hw_init(info);
> +	if (info->data->init_hw)
> +		info->data->init_hw(info);
>
>   	ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
>   	if (ret < 0) {
> @@ -366,7 +441,8 @@ err_of_populate:
>   err_irq:
>   	free_irq(info->irq, info);
>   err_disable_clk:
> -	writel(0, info->enable_reg);
> +	if (info->data->exit_hw)
> +		info->data->exit_hw(info);
>   	clk_disable_unprepare(info->clk);
>   err_disable_reg:
>   	regulator_disable(info->vdd);
> @@ -382,7 +458,8 @@ static int exynos_adc_remove(struct platform_device *pdev)
>   				exynos_adc_remove_devices);
>   	iio_device_unregister(indio_dev);
>   	free_irq(info->irq, info);
> -	writel(0, info->enable_reg);
> +	if (info->data->exit_hw)
> +		info->data->exit_hw(info);
>   	clk_disable_unprepare(info->clk);
>   	regulator_disable(info->vdd);
>
> @@ -394,19 +471,10 @@ static int exynos_adc_suspend(struct device *dev)
>   {
>   	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>   	struct exynos_adc *info = iio_priv(indio_dev);
> -	u32 con;
>
> -	if (info->version == ADC_V2) {
> -		con = readl(ADC_V2_CON1(info->regs));
> -		con &= ~ADC_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));
> -	}
> +	if (info->data->exit_hw)
> +		info->data->exit_hw(info);
>
> -	writel(0, info->enable_reg);
>   	clk_disable_unprepare(info->clk);
>   	regulator_disable(info->vdd);
>
> @@ -427,8 +495,8 @@ static int exynos_adc_resume(struct device *dev)
>   	if (ret)
>   		return ret;
>
> -	writel(1, info->enable_reg);
> -	exynos_adc_hw_init(info);
> +	if (info->data->init_hw)
> +		info->data->init_hw(info);
>
>   	return 0;
>   }
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCHv8 2/4] iio: adc: exynos_adc: Control special clock of ADC to support Exynos3250 ADC
  2014-07-22  2:04 ` [PATCHv8 2/4] iio: adc: exynos_adc: Control special clock of ADC to support Exynos3250 ADC Chanwoo Choi
@ 2014-07-23 20:57   ` Jonathan Cameron
  0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2014-07-23 20:57 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/07/14 03:04, Chanwoo Choi wrote:
> This patch control special clock for ADC in Exynos series's FSYS block.
> If special clock of ADC is registerd on clock list of common clk framework,
> Exynos ADC drvier have to control this clock.
>
> Exynos3250/Exynos4/Exynos5 has 'adc' clock as following:
> - 'adc' clock: bus clock for ADC
>
> Exynos3250 has additional 'sclk_adc' clock as following:
> - 'sclk_adc' clock: special clock for ADC which provide clock to internal ADC
>
> Exynos 4210/4212/4412 and Exynos5250/5420 has not included 'sclk_adc' clock
> in FSYS_BLK. But, Exynos3250 based on Cortex-A7 has only included 'sclk_adc'
> clock in FSYS_BLK.
>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Reviewed-by: Tomasz Figa <t.figa@samsung.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
Another minor tweak but otherwise applied to the togreg branch of iio.git - to
be initially pushed out as testing (this took a while as my local tree was eaten
by a file corruption this evening...)
> ---
>   drivers/iio/adc/exynos_adc.c | 111 +++++++++++++++++++++++++++++++++++++++----
>   1 file changed, 103 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
> index dde4ca8..87e0895 100644
> --- a/drivers/iio/adc/exynos_adc.c
> +++ b/drivers/iio/adc/exynos_adc.c
> @@ -24,6 +24,7 @@
>   #include <linux/platform_device.h>
>   #include <linux/interrupt.h>
>   #include <linux/delay.h>
> +#include <linux/errno.h>
>   #include <linux/kernel.h>
>   #include <linux/slab.h>
>   #include <linux/io.h>
> @@ -70,8 +71,9 @@
>   #define ADC_V2_CON2_ACH_SEL(x)	(((x) & 0xF) << 0)
>   #define ADC_V2_CON2_ACH_MASK	0xF
>
> -#define MAX_ADC_V2_CHANNELS	10
> -#define MAX_ADC_V1_CHANNELS	8
> +#define MAX_ADC_V2_CHANNELS		10
> +#define MAX_ADC_V1_CHANNELS		8
> +#define MAX_EXYNOS3250_ADC_CHANNELS	2
>
>   /* Bit definitions common for ADC_V1 and ADC_V2 */
>   #define ADC_CON_EN_START	(1u << 0)
> @@ -81,9 +83,11 @@
>
>   struct exynos_adc {
>   	struct exynos_adc_data	*data;
> +	struct device		*dev;
>   	void __iomem		*regs;
>   	void __iomem		*enable_reg;
>   	struct clk		*clk;
> +	struct clk		*sclk;
>   	unsigned int		irq;
>   	struct regulator	*vdd;
>
> @@ -95,6 +99,7 @@ struct exynos_adc {
>
>   struct exynos_adc_data {
>   	int num_channels;
> +	bool needs_sclk;
>
>   	void (*init_hw)(struct exynos_adc *info);
>   	void (*exit_hw)(struct exynos_adc *info);
> @@ -102,6 +107,66 @@ struct exynos_adc_data {
>   	void (*start_conv)(struct exynos_adc *info, unsigned long addr);
>   };
>
> +static void exynos_adc_unprepare_clk(struct exynos_adc *info)
> +{
> +	if (info->data->needs_sclk)
> +		clk_unprepare(info->sclk);
> +	clk_unprepare(info->clk);
> +}
> +
> +static int exynos_adc_prepare_clk(struct exynos_adc *info)
> +{
> +	int ret;
> +
> +	ret = clk_prepare(info->clk);
> +	if (ret) {
> +		dev_err(info->dev, "failed preparing adc clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (info->data->needs_sclk) {
> +		ret = clk_prepare(info->sclk);
> +		if (ret) {
> +			clk_unprepare(info->clk);
> +			dev_err(info->dev,
> +				"failed preparing sclk_adc clock: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void exynos_adc_disable_clk(struct exynos_adc *info)
> +{
> +	if (info->data->needs_sclk)
> +		clk_disable(info->sclk);
> +	clk_disable(info->clk);
> +}
> +
> +static int exynos_adc_enable_clk(struct exynos_adc *info)
> +{
> +	int ret;
> +
> +	ret = clk_enable(info->clk);
> +	if (ret) {
> +		dev_err(info->dev, "failed enabling adc clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (info->data->needs_sclk) {
> +		ret = clk_enable(info->sclk);
> +		if (ret) {
> +			clk_disable(info->clk);
> +			dev_err(info->dev,
> +				"failed enabling sclk_adc clock: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>   static void exynos_adc_v1_init_hw(struct exynos_adc *info)
>   {
>   	u32 con1;
> @@ -208,6 +273,16 @@ static const struct exynos_adc_data const exynos_adc_v2_data = {
>   	.start_conv	= exynos_adc_v2_start_conv,
>   };
>
> +static const struct exynos_adc_data const exynos3250_adc_data = {
and another duplicate const.
> +	.num_channels	= MAX_EXYNOS3250_ADC_CHANNELS,
> +	.needs_sclk	= true,
> +
> +	.init_hw	= exynos_adc_v2_init_hw,
> +	.exit_hw	= exynos_adc_v2_exit_hw,
> +	.clear_irq	= exynos_adc_v2_clear_irq,
> +	.start_conv	= exynos_adc_v2_start_conv,
> +};
> +
>   static const struct of_device_id exynos_adc_match[] = {
>   	{
>   		.compatible = "samsung,exynos-adc-v1",
> @@ -215,6 +290,9 @@ static const struct of_device_id exynos_adc_match[] = {
>   	}, {
>   		.compatible = "samsung,exynos-adc-v2",
>   		.data = &exynos_adc_v2_data,
> +	}, {
> +		.compatible = "samsung,exynos3250-adc",
> +		.data = &exynos3250_adc_data,
>   	},
>   	{},
>   };
> @@ -376,6 +454,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
>   	}
>
>   	info->irq = irq;
> +	info->dev = &pdev->dev;
>
>   	init_completion(&info->completion);
>
> @@ -386,6 +465,16 @@ static int exynos_adc_probe(struct platform_device *pdev)
>   		return PTR_ERR(info->clk);
>   	}
>
> +	if (info->data->needs_sclk) {
> +		info->sclk = devm_clk_get(&pdev->dev, "sclk");
> +		if (IS_ERR(info->sclk)) {
> +			dev_err(&pdev->dev,
> +				"failed getting sclk clock, err = %ld\n",
> +				PTR_ERR(info->sclk));
> +			return PTR_ERR(info->sclk);
> +		}
> +	}
> +
>   	info->vdd = devm_regulator_get(&pdev->dev, "vdd");
>   	if (IS_ERR(info->vdd)) {
>   		dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
> @@ -397,10 +486,14 @@ static int exynos_adc_probe(struct platform_device *pdev)
>   	if (ret)
>   		return ret;
>
> -	ret = clk_prepare_enable(info->clk);
> +	ret = exynos_adc_prepare_clk(info);
>   	if (ret)
>   		goto err_disable_reg;
>
> +	ret = exynos_adc_enable_clk(info);
> +	if (ret)
> +		goto err_unprepare_clk;
> +
>   	platform_set_drvdata(pdev, indio_dev);
>
>   	indio_dev->name = dev_name(&pdev->dev);
> @@ -443,7 +536,9 @@ err_irq:
>   err_disable_clk:
>   	if (info->data->exit_hw)
>   		info->data->exit_hw(info);
> -	clk_disable_unprepare(info->clk);
> +	exynos_adc_disable_clk(info);
> +err_unprepare_clk:
> +	exynos_adc_unprepare_clk(info);
>   err_disable_reg:
>   	regulator_disable(info->vdd);
>   	return ret;
> @@ -460,7 +555,8 @@ static int exynos_adc_remove(struct platform_device *pdev)
>   	free_irq(info->irq, info);
>   	if (info->data->exit_hw)
>   		info->data->exit_hw(info);
> -	clk_disable_unprepare(info->clk);
> +	exynos_adc_disable_clk(info);
> +	exynos_adc_unprepare_clk(info);
>   	regulator_disable(info->vdd);
>
>   	return 0;
> @@ -474,8 +570,7 @@ static int exynos_adc_suspend(struct device *dev)
>
>   	if (info->data->exit_hw)
>   		info->data->exit_hw(info);
> -
> -	clk_disable_unprepare(info->clk);
> +	exynos_adc_disable_clk(info);
>   	regulator_disable(info->vdd);
>
>   	return 0;
> @@ -491,7 +586,7 @@ static int exynos_adc_resume(struct device *dev)
>   	if (ret)
>   		return ret;
>
> -	ret = clk_prepare_enable(info->clk);
> +	ret = exynos_adc_enable_clk(info);
>   	if (ret)
>   		return ret;
>
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCHv8 3/4] iio: devicetree: Add DT binding documentation for Exynos3250 ADC
  2014-07-22  2:04 ` [PATCHv8 3/4] iio: devicetree: Add DT binding documentation for " Chanwoo Choi
@ 2014-07-23 20:58   ` Jonathan Cameron
  0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2014-07-23 20:58 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/07/14 03:04, Chanwoo Choi wrote:
> This patch add DT binding documentation for Exynos3250 ADC IP. Exynos3250 has
> special clock ('sclk_adc') for ADC which provide clock to internal ADC.
>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Reviewed-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
> Reviewed-by: Tomasz Figa <t.figa@samsung.com>
> Acked-by: Kukjin Kim <kgene.kim@samsung.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
Applied to the togreg branch of iio.git - pushed out as testing for now.

> ---
>   .../devicetree/bindings/arm/samsung/exynos-adc.txt | 25 ++++++++++++++++++++--
>   1 file changed, 23 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
> index 832fe8c..adc61b0 100644
> --- a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
> +++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
> @@ -14,14 +14,21 @@ Required properties:
>   				for exynos4412/5250 controllers.
>   			Must be "samsung,exynos-adc-v2" for
>   				future controllers.
> +			Must be "samsung,exynos3250-adc" for
> +				controllers compatible with ADC of Exynos3250.
>   - reg:			Contains ADC register address range (base address and
>   			length) and the address of the phy enable register.
>   - interrupts: 		Contains the interrupt information for the timer. The
>   			format is being dependent on which interrupt controller
>   			the Samsung device uses.
>   - #io-channel-cells = <1>; As ADC has multiple outputs
> -- clocks		From common clock binding: handle to adc clock.
> -- clock-names		From common clock binding: Shall be "adc".
> +- clocks		From common clock bindings: handles to clocks specified
> +			in "clock-names" property, in the same order.
> +- clock-names		From common clock bindings: list of clock input names
> +			used by ADC block:
> +			- "adc" : ADC bus clock
> +			- "sclk" : ADC special clock (only for Exynos3250 and
> +				   compatible ADC block)
>   - vdd-supply		VDD input supply.
>
>   Note: child nodes can be added for auto probing from device tree.
> @@ -41,6 +48,20 @@ adc: adc at 12D10000 {
>   	vdd-supply = <&buck5_reg>;
>   };
>
> +Example: adding device info in dtsi file for Exynos3250 with additional sclk
> +
> +adc: adc at 126C0000 {
> +	compatible = "samsung,exynos3250-adc", "samsung,exynos-adc-v2;
> +	reg = <0x126C0000 0x100>, <0x10020718 0x4>;
> +	interrupts = <0 137 0>;
> +	#io-channel-cells = <1>;
> +	io-channel-ranges;
> +
> +	clocks = <&cmu CLK_TSADC>, <&cmu CLK_SCLK_TSADC>;
> +	clock-names = "adc", "sclk";
> +
> +	vdd-supply = <&buck5_reg>;
> +};
>
>   Example: Adding child nodes in dts file
>
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCHv8 4/4] ARM: dts: Fix wrong compatible string for Exynos3250 ADC
  2014-07-22  2:04 ` [PATCHv8 4/4] ARM: dts: Fix wrong compatible string " Chanwoo Choi
@ 2014-07-23 20:59   ` Jonathan Cameron
  0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Cameron @ 2014-07-23 20:59 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/07/14 03:04, Chanwoo Choi wrote:
> This patchset fix wrong compatible string for Exynos3250 ADC. Exynos3250 SoC
> need to control only special clock for ADC. Exynos SoC except for Exynos3250
> has not included special clock for ADC. The exynos ADC driver can control
> special clock if compatible string is 'exynos3250-adc-v2'.
>
> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Reviewed-by: Tomasz Figa <t.figa@samsung.com>
> Acked-by: Kukjin Kim <kgene.kim@samsung.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
Applied to the togreg branch of iio.git - initially pushed out as testing.

Thanks

Jonathan
> ---
>   arch/arm/boot/dts/exynos3250.dtsi | 5 +++--
>   1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi
> index 15c9c87..767189a 100644
> --- a/arch/arm/boot/dts/exynos3250.dtsi
> +++ b/arch/arm/boot/dts/exynos3250.dtsi
> @@ -407,10 +407,11 @@
>   		};
>
>   		adc: adc at 126C0000 {
> -			compatible = "samsung,exynos-adc-v3";
> +			compatible = "samsung,exynos3250-adc",
> +				     "samsung,exynos-adc-v2";
>   			reg = <0x126C0000 0x100>, <0x10020718 0x4>;
>   			interrupts = <0 137 0>;
> -			clock-names = "adc", "sclk_tsadc";
> +			clock-names = "adc", "sclk";
>   			clocks = <&cmu CLK_TSADC>, <&cmu CLK_SCLK_TSADC>;
>   			#io-channel-cells = <1>;
>   			io-channel-ranges;
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2014-07-23 20:59 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-22  2:04 [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi
2014-07-22  2:04 ` [PATCHv8 1/4] iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability Chanwoo Choi
2014-07-23 20:55   ` Jonathan Cameron
2014-07-22  2:04 ` [PATCHv8 2/4] iio: adc: exynos_adc: Control special clock of ADC to support Exynos3250 ADC Chanwoo Choi
2014-07-23 20:57   ` Jonathan Cameron
2014-07-22  2:04 ` [PATCHv8 3/4] iio: devicetree: Add DT binding documentation for " Chanwoo Choi
2014-07-23 20:58   ` Jonathan Cameron
2014-07-22  2:04 ` [PATCHv8 4/4] ARM: dts: Fix wrong compatible string " Chanwoo Choi
2014-07-23 20:59   ` Jonathan Cameron
2014-07-23  1:41 ` [PATCHv8 0/4] iio: adc: exynos_adc: Support Exynos3250 ADC and code clean Chanwoo Choi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).