All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] AT91: Add a driver for the ADC
@ 2011-10-19 16:18 ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the branch master of Jonathan Cameron present at :
https://github.com/jic23/linux-iio/

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>


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

* [PATCH] AT91: Add a driver for the ADC
@ 2011-10-19 16:18 ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the branch master of Jonathan Cameron present at :
https://github.com/jic23/linux-iio/

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-10-19 16:18 ` Maxime Ripard
@ 2011-10-19 16:18   ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/include/mach/board.h |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..adbc431 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,12 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	unsigned int adc_clock;
+	u8 channels;
+	u8 startup_time;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-10-19 16:18   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/include/mach/board.h |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..adbc431 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,12 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	unsigned int adc_clock;
+	u8 channels;
+	u8 startup_time;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-19 16:18 ` Maxime Ripard
@ 2011-10-19 16:18   ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/staging/iio/adc/Kconfig   |    6 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/at91adc.c |  295 +++++++++++++++++++++++++++++++++++++
 3 files changed, 302 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/at91adc.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 070efd3..98e8005 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -206,6 +206,12 @@ config AD7280
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7280a
 
+config AT91ADC
+	tristate "Atmel Touchscreen ADC Controller"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel TSADCC.
+
 config MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 8adf096..03e517a 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o
 obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
+obj-$(CONFIG_AT91ADC) += at91adc.o
diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
new file mode 100644
index 0000000..2737261
--- /dev/null
+++ b/drivers/staging/iio/adc/at91adc.c
@@ -0,0 +1,295 @@
+/*
+ * Driver for the TouchScreen ADC Controller present in the Atmel AT91
+ * evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "../iio.h"
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
+#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	struct iio_chan_spec *channels;
+	int nb_chan;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 lcdr;
+	void __iomem *reg_base;
+
+};
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct at91adc_state *st = private;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < st->nb_chan; chan++) {
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->lcdr = at91adc_reg_read(st->reg_base,
+						    AT91_ADC_CHR(chan));
+		}
+	}
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct at91adc_state *st)
+{
+	int ret = 0, i;
+	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
+			       GFP_KERNEL);
+	if (st->channels == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < st->nb_chan; i++) {
+		struct iio_chan_spec *chan = st->channels + i;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = i;
+		++ret;
+	}
+
+	return ret;
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
+
+		wait_event_interruptible(st->wq_data_avail, st->done);
+		*val = st->lcdr;
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->lcdr = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	dev_dbg(&pdev->dev, "AT91ADC probed\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_allocate_device(sizeof(*st));
+	if (idev == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = platform_get_device_id(pdev)->name;
+	idev->modes = INDIO_DIRECT_MODE;
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	st->nb_chan = pdata->channels;
+	ret = at91adc_channel_init(st);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	idev->channels = st->channels;
+	idev->num_channels = st->nb_chan;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	return 0;
+
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_free_device(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	free_irq(st->irq, st);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_unregister(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+		   },
+};
+
+static int __init at91adc_init(void)
+{
+	return platform_driver_register(&at91adc_driver);
+}
+
+static void __exit at91adc_exit(void)
+{
+	platform_driver_unregister(&at91adc_driver);
+}
+
+module_init(at91adc_init);
+module_exit(at91adc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-19 16:18   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/staging/iio/adc/Kconfig   |    6 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/at91adc.c |  295 +++++++++++++++++++++++++++++++++++++
 3 files changed, 302 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/at91adc.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 070efd3..98e8005 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -206,6 +206,12 @@ config AD7280
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7280a
 
+config AT91ADC
+	tristate "Atmel Touchscreen ADC Controller"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel TSADCC.
+
 config MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 8adf096..03e517a 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o
 obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
+obj-$(CONFIG_AT91ADC) += at91adc.o
diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
new file mode 100644
index 0000000..2737261
--- /dev/null
+++ b/drivers/staging/iio/adc/at91adc.c
@@ -0,0 +1,295 @@
+/*
+ * Driver for the TouchScreen ADC Controller present in the Atmel AT91
+ * evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "../iio.h"
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
+#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	struct iio_chan_spec *channels;
+	int nb_chan;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 lcdr;
+	void __iomem *reg_base;
+
+};
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct at91adc_state *st = private;
+	struct iio_dev *idev = iio_priv_to_dev(st);
+	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < st->nb_chan; chan++) {
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->lcdr = at91adc_reg_read(st->reg_base,
+						    AT91_ADC_CHR(chan));
+		}
+	}
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct at91adc_state *st)
+{
+	int ret = 0, i;
+	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
+			       GFP_KERNEL);
+	if (st->channels == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < st->nb_chan; i++) {
+		struct iio_chan_spec *chan = st->channels + i;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = i;
+		++ret;
+	}
+
+	return ret;
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
+
+		wait_event_interruptible(st->wq_data_avail, st->done);
+		*val = st->lcdr;
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->lcdr = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	dev_dbg(&pdev->dev, "AT91ADC probed\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_allocate_device(sizeof(*st));
+	if (idev == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = platform_get_device_id(pdev)->name;
+	idev->modes = INDIO_DIRECT_MODE;
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	st->nb_chan = pdata->channels;
+	ret = at91adc_channel_init(st);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	idev->channels = st->channels;
+	idev->num_channels = st->nb_chan;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	return 0;
+
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_free_device(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	free_irq(st->irq, st);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_unregister(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+		   },
+};
+
+static int __init at91adc_init(void)
+{
+	return platform_driver_register(&at91adc_driver);
+}
+
+static void __exit at91adc_exit(void)
+{
+	platform_driver_unregister(&at91adc_driver);
+}
+
+module_init(at91adc_init);
+module_exit(at91adc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-10-19 16:18 ` Maxime Ripard
@ 2011-10-19 16:18   ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   44 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   12 ++++++++
 2 files changed, 56 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..4f5e0f9 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,50 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	at91_set_A_periph(AT91_PIN_PC0, 0);
+	at91_set_A_periph(AT91_PIN_PC1, 0);
+	at91_set_A_periph(AT91_PIN_PC2, 0);
+	at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..fd373c3 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels = 4,
+	.adc_clock = 5000000,
+	.startup_time = 10,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +399,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-10-19 16:18   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   44 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   12 ++++++++
 2 files changed, 56 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..4f5e0f9 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,50 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	at91_set_A_periph(AT91_PIN_PC0, 0);
+	at91_set_A_periph(AT91_PIN_PC1, 0);
+	at91_set_A_periph(AT91_PIN_PC2, 0);
+	at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..fd373c3 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels = 4,
+	.adc_clock = 5000000,
+	.startup_time = 10,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +399,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-19 16:18   ` Maxime Ripard
@ 2011-10-19 16:42     ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-10-19 16:42 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

Hi Maxime,

Afraid I'm out of time for today, but just thought I'd ask one quick question...

Why isn't this touchscreen ADC in input?

(needs to be firmly stated as early as possible!)


> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/staging/iio/adc/Kconfig   |    6 +
>  drivers/staging/iio/adc/Makefile  |    1 +
>  drivers/staging/iio/adc/at91adc.c |  295 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 302 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/at91adc.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 070efd3..98e8005 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -206,6 +206,12 @@ config AD7280
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7280a
>  
> +config AT91ADC
> +	tristate "Atmel Touchscreen ADC Controller"
> +	depends on SYSFS && ARCH_AT91
> +	help
> +	  Say yes here to build support for Atmel TSADCC.
> +
>  config MAX1363
>  	tristate "Maxim max1363 ADC driver"
>  	depends on I2C
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 8adf096..03e517a 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o
>  obj-$(CONFIG_ADT7310) += adt7310.o
>  obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
> +obj-$(CONFIG_AT91ADC) += at91adc.o
> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
> new file mode 100644
> index 0000000..2737261
> --- /dev/null
> +++ b/drivers/staging/iio/adc/at91adc.c
> @@ -0,0 +1,295 @@
> +/*
> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
> + * evaluation boards.
> + *
> + * Copyright 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "../iio.h"
> +
> +#include <mach/at91_adc.h>
> +#include <mach/board.h>
> +
> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
> +	struct iio_chan_spec *channels;
> +	int nb_chan;
> +	int irq;
> +	wait_queue_head_t wq_data_avail;
> +	u16 lcdr;
> +	void __iomem *reg_base;
> +
> +};
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct at91adc_state *st = private;
> +	struct iio_dev *idev = iio_priv_to_dev(st);
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < st->nb_chan; chan++) {
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->lcdr = at91adc_reg_read(st->reg_base,
> +						    AT91_ADC_CHR(chan));
> +		}
> +	}
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct at91adc_state *st)
> +{
> +	int ret = 0, i;
> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
> +			       GFP_KERNEL);
> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < st->nb_chan; i++) {
> +		struct iio_chan_spec *chan = st->channels + i;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = i;
> +		++ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible(st->wq_data_avail, st->done);
> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(*st));
> +	if (idev == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = platform_get_device_id(pdev)->name;
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
> +
> +	if (!pdata->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;
> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
> +
> +	/* Setup the ADC channels available on the board */
> +	st->nb_chan = pdata->channels;
> +	ret = at91adc_channel_init(st);
> +	if (ret < 0) {
> +		goto error_free_clk;
> +	}
> +
> +	idev->channels = st->channels;
> +	idev->num_channels = st->nb_chan;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		goto error_free_clk;
> +	}
> +
> +	return 0;
> +
> +error_free_clk:
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, st);
> +error_unmap_reg:
> +	iounmap(st->reg_base);
> +error_release_mem:
> +	release_mem_region(res->start, resource_size(res));
> +error_free_device:
> +	iio_free_device(idev);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	free_irq(st->irq, st);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_unregister(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +		   },
> +};
> +
> +static int __init at91adc_init(void)
> +{
> +	return platform_driver_register(&at91adc_driver);
> +}
> +
> +static void __exit at91adc_exit(void)
> +{
> +	platform_driver_unregister(&at91adc_driver);
> +}
> +
> +module_init(at91adc_init);
> +module_exit(at91adc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-19 16:42     ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-10-19 16:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

Afraid I'm out of time for today, but just thought I'd ask one quick question...

Why isn't this touchscreen ADC in input?

(needs to be firmly stated as early as possible!)


> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/staging/iio/adc/Kconfig   |    6 +
>  drivers/staging/iio/adc/Makefile  |    1 +
>  drivers/staging/iio/adc/at91adc.c |  295 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 302 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/at91adc.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 070efd3..98e8005 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -206,6 +206,12 @@ config AD7280
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7280a
>  
> +config AT91ADC
> +	tristate "Atmel Touchscreen ADC Controller"
> +	depends on SYSFS && ARCH_AT91
> +	help
> +	  Say yes here to build support for Atmel TSADCC.
> +
>  config MAX1363
>  	tristate "Maxim max1363 ADC driver"
>  	depends on I2C
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 8adf096..03e517a 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o
>  obj-$(CONFIG_ADT7310) += adt7310.o
>  obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
> +obj-$(CONFIG_AT91ADC) += at91adc.o
> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
> new file mode 100644
> index 0000000..2737261
> --- /dev/null
> +++ b/drivers/staging/iio/adc/at91adc.c
> @@ -0,0 +1,295 @@
> +/*
> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
> + * evaluation boards.
> + *
> + * Copyright 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "../iio.h"
> +
> +#include <mach/at91_adc.h>
> +#include <mach/board.h>
> +
> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
> +	struct iio_chan_spec *channels;
> +	int nb_chan;
> +	int irq;
> +	wait_queue_head_t wq_data_avail;
> +	u16 lcdr;
> +	void __iomem *reg_base;
> +
> +};
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct at91adc_state *st = private;
> +	struct iio_dev *idev = iio_priv_to_dev(st);
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < st->nb_chan; chan++) {
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->lcdr = at91adc_reg_read(st->reg_base,
> +						    AT91_ADC_CHR(chan));
> +		}
> +	}
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct at91adc_state *st)
> +{
> +	int ret = 0, i;
> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
> +			       GFP_KERNEL);
> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < st->nb_chan; i++) {
> +		struct iio_chan_spec *chan = st->channels + i;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = i;
> +		++ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible(st->wq_data_avail, st->done);
> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(*st));
> +	if (idev == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = platform_get_device_id(pdev)->name;
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
> +
> +	if (!pdata->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;
> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
> +
> +	/* Setup the ADC channels available on the board */
> +	st->nb_chan = pdata->channels;
> +	ret = at91adc_channel_init(st);
> +	if (ret < 0) {
> +		goto error_free_clk;
> +	}
> +
> +	idev->channels = st->channels;
> +	idev->num_channels = st->nb_chan;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		goto error_free_clk;
> +	}
> +
> +	return 0;
> +
> +error_free_clk:
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, st);
> +error_unmap_reg:
> +	iounmap(st->reg_base);
> +error_release_mem:
> +	release_mem_region(res->start, resource_size(res));
> +error_free_device:
> +	iio_free_device(idev);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	free_irq(st->irq, st);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_unregister(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +		   },
> +};
> +
> +static int __init at91adc_init(void)
> +{
> +	return platform_driver_register(&at91adc_driver);
> +}
> +
> +static void __exit at91adc_exit(void)
> +{
> +	platform_driver_unregister(&at91adc_driver);
> +}
> +
> +module_init(at91adc_init);
> +module_exit(at91adc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-19 16:42     ` Jonathan Cameron
@ 2011-10-19 18:23       ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 18:23 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

Hi Jonathan,

On 19/10/2011 18:42, Jonathan Cameron wrote:
> Afraid I'm out of time for today, but just thought I'd ask one quick question...
> 
> Why isn't this touchscreen ADC in input?

Because what I say in the Kconfig is mostly wrong... Sorry about that.

Actually, like I was saying, this ADC is present in a lot of AT91 SoC,
and while it is always an ADC, sometimes (like on the G45), it can be
used as a touchscreen controller, or even as both, with channels for the
touchscreen and channels as ADC. On the G20 however, it is just an ADC.

For now, the plan is only to support the ADC mode, so I put it in adc.

Maxime

>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  drivers/staging/iio/adc/Kconfig   |    6 +
>>  drivers/staging/iio/adc/Makefile  |    1 +
>>  drivers/staging/iio/adc/at91adc.c |  295 +++++++++++++++++++++++++++++++++++++
>>  3 files changed, 302 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/staging/iio/adc/at91adc.c
>>
>> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
>> index 070efd3..98e8005 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -206,6 +206,12 @@ config AD7280
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called ad7280a
>>  
>> +config AT91ADC
>> +	tristate "Atmel Touchscreen ADC Controller"
>> +	depends on SYSFS && ARCH_AT91
>> +	help
>> +	  Say yes here to build support for Atmel TSADCC.
>> +
>>  config MAX1363
>>  	tristate "Maxim max1363 ADC driver"
>>  	depends on I2C
>> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
>> index 8adf096..03e517a 100644
>> --- a/drivers/staging/iio/adc/Makefile
>> +++ b/drivers/staging/iio/adc/Makefile
>> @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o
>>  obj-$(CONFIG_ADT7310) += adt7310.o
>>  obj-$(CONFIG_ADT7410) += adt7410.o
>>  obj-$(CONFIG_AD7280) += ad7280a.o
>> +obj-$(CONFIG_AT91ADC) += at91adc.o
>> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..2737261
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/at91adc.c
>> @@ -0,0 +1,295 @@
>> +/*
>> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
>> + * evaluation boards.
>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include "../iio.h"
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
>> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
>> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	struct iio_chan_spec *channels;
>> +	int nb_chan;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 lcdr;
>> +	void __iomem *reg_base;
>> +
>> +};
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct at91adc_state *st = private;
>> +	struct iio_dev *idev = iio_priv_to_dev(st);
>> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < st->nb_chan; chan++) {
>> +		if (status & AT91_ADC_EOC(chan)) {
>> +			st->done = true;
>> +			st->lcdr = at91adc_reg_read(st->reg_base,
>> +						    AT91_ADC_CHR(chan));
>> +		}
>> +	}
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct at91adc_state *st)
>> +{
>> +	int ret = 0, i;
>> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
>> +			       GFP_KERNEL);
>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < st->nb_chan; i++) {
>> +		struct iio_chan_spec *chan = st->channels + i;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = i;
>> +		++ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible(st->wq_data_avail, st->done);
>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(*st));
>> +	if (idev == NULL) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = platform_get_device_id(pdev)->name;
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	st->nb_chan = pdata->channels;
>> +	ret = at91adc_channel_init(st);
>> +	if (ret < 0) {
>> +		goto error_free_clk;
>> +	}
>> +
>> +	idev->channels = st->channels;
>> +	idev->num_channels = st->nb_chan;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0) {
>> +		goto error_free_clk;
>> +	}
>> +
>> +	return 0;
>> +
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_free_device(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	free_irq(st->irq, st);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_unregister(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +		   },
>> +};
>> +
>> +static int __init at91adc_init(void)
>> +{
>> +	return platform_driver_register(&at91adc_driver);
>> +}
>> +
>> +static void __exit at91adc_exit(void)
>> +{
>> +	platform_driver_unregister(&at91adc_driver);
>> +}
>> +
>> +module_init(at91adc_init);
>> +module_exit(at91adc_exit);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-19 18:23       ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-19 18:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jonathan,

On 19/10/2011 18:42, Jonathan Cameron wrote:
> Afraid I'm out of time for today, but just thought I'd ask one quick question...
> 
> Why isn't this touchscreen ADC in input?

Because what I say in the Kconfig is mostly wrong... Sorry about that.

Actually, like I was saying, this ADC is present in a lot of AT91 SoC,
and while it is always an ADC, sometimes (like on the G45), it can be
used as a touchscreen controller, or even as both, with channels for the
touchscreen and channels as ADC. On the G20 however, it is just an ADC.

For now, the plan is only to support the ADC mode, so I put it in adc.

Maxime

>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  drivers/staging/iio/adc/Kconfig   |    6 +
>>  drivers/staging/iio/adc/Makefile  |    1 +
>>  drivers/staging/iio/adc/at91adc.c |  295 +++++++++++++++++++++++++++++++++++++
>>  3 files changed, 302 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/staging/iio/adc/at91adc.c
>>
>> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
>> index 070efd3..98e8005 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -206,6 +206,12 @@ config AD7280
>>  	  To compile this driver as a module, choose M here: the
>>  	  module will be called ad7280a
>>  
>> +config AT91ADC
>> +	tristate "Atmel Touchscreen ADC Controller"
>> +	depends on SYSFS && ARCH_AT91
>> +	help
>> +	  Say yes here to build support for Atmel TSADCC.
>> +
>>  config MAX1363
>>  	tristate "Maxim max1363 ADC driver"
>>  	depends on I2C
>> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
>> index 8adf096..03e517a 100644
>> --- a/drivers/staging/iio/adc/Makefile
>> +++ b/drivers/staging/iio/adc/Makefile
>> @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o
>>  obj-$(CONFIG_ADT7310) += adt7310.o
>>  obj-$(CONFIG_ADT7410) += adt7410.o
>>  obj-$(CONFIG_AD7280) += ad7280a.o
>> +obj-$(CONFIG_AT91ADC) += at91adc.o
>> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..2737261
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/at91adc.c
>> @@ -0,0 +1,295 @@
>> +/*
>> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
>> + * evaluation boards.
>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include "../iio.h"
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
>> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
>> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	struct iio_chan_spec *channels;
>> +	int nb_chan;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 lcdr;
>> +	void __iomem *reg_base;
>> +
>> +};
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct at91adc_state *st = private;
>> +	struct iio_dev *idev = iio_priv_to_dev(st);
>> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < st->nb_chan; chan++) {
>> +		if (status & AT91_ADC_EOC(chan)) {
>> +			st->done = true;
>> +			st->lcdr = at91adc_reg_read(st->reg_base,
>> +						    AT91_ADC_CHR(chan));
>> +		}
>> +	}
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct at91adc_state *st)
>> +{
>> +	int ret = 0, i;
>> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
>> +			       GFP_KERNEL);
>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < st->nb_chan; i++) {
>> +		struct iio_chan_spec *chan = st->channels + i;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = i;
>> +		++ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible(st->wq_data_avail, st->done);
>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(*st));
>> +	if (idev == NULL) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = platform_get_device_id(pdev)->name;
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	st->nb_chan = pdata->channels;
>> +	ret = at91adc_channel_init(st);
>> +	if (ret < 0) {
>> +		goto error_free_clk;
>> +	}
>> +
>> +	idev->channels = st->channels;
>> +	idev->num_channels = st->nb_chan;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0) {
>> +		goto error_free_clk;
>> +	}
>> +
>> +	return 0;
>> +
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_free_device(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	free_irq(st->irq, st);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_unregister(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +		   },
>> +};
>> +
>> +static int __init at91adc_init(void)
>> +{
>> +	return platform_driver_register(&at91adc_driver);
>> +}
>> +
>> +static void __exit at91adc_exit(void)
>> +{
>> +	platform_driver_unregister(&at91adc_driver);
>> +}
>> +
>> +module_init(at91adc_init);
>> +module_exit(at91adc_exit);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-10-19 16:18   ` Maxime Ripard
@ 2011-10-20  6:28     ` Alexander Stein
  -1 siblings, 0 replies; 131+ messages in thread
From: Alexander Stein @ 2011-10-20  6:28 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre

On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote:
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c
> b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> [...]
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91adc",
> +	.id		= -1,
> +	.dev		= {
> +				.platform_data		= &adc_data,
> +	},
> +	.resource	= adc_resources,
> +	.num_resources	= ARRAY_SIZE(adc_resources),
> +};
> +
> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	at91_set_A_periph(AT91_PIN_PC0, 0);
> +	at91_set_A_periph(AT91_PIN_PC1, 0);
> +	at91_set_A_periph(AT91_PIN_PC2, 0);
> +	at91_set_A_periph(AT91_PIN_PC3, 0);
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +
> +

This assumes that all 4 channels are used every time.  How about only using an 
AD channel on PC2 or all but the one on PC1?

Regards,
Alexander

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-10-20  6:28     ` Alexander Stein
  0 siblings, 0 replies; 131+ messages in thread
From: Alexander Stein @ 2011-10-20  6:28 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote:
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c
> b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> [...]
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91adc",
> +	.id		= -1,
> +	.dev		= {
> +				.platform_data		= &adc_data,
> +	},
> +	.resource	= adc_resources,
> +	.num_resources	= ARRAY_SIZE(adc_resources),
> +};
> +
> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	at91_set_A_periph(AT91_PIN_PC0, 0);
> +	at91_set_A_periph(AT91_PIN_PC1, 0);
> +	at91_set_A_periph(AT91_PIN_PC2, 0);
> +	at91_set_A_periph(AT91_PIN_PC3, 0);
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +
> +

This assumes that all 4 channels are used every time.  How about only using an 
AD channel on PC2 or all but the one on PC1?

Regards,
Alexander

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-19 18:23       ` Maxime Ripard
@ 2011-10-20  7:05         ` Thomas Petazzoni
  -1 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-10-20  7:05 UTC (permalink / raw)
  To: Maxime Ripard, Jonathan Cameron
  Cc: linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel

Hello,

Le Wed, 19 Oct 2011 20:23:50 +0200,
Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit :

> On 19/10/2011 18:42, Jonathan Cameron wrote:
> > Afraid I'm out of time for today, but just thought I'd ask one quick qu=
estion...
> >=20
> > Why isn't this touchscreen ADC in input?
>=20
> Because what I say in the Kconfig is mostly wrong... Sorry about that.
>=20
> Actually, like I was saying, this ADC is present in a lot of AT91 SoC,
> and while it is always an ADC, sometimes (like on the G45), it can be
> used as a touchscreen controller, or even as both, with channels for the
> touchscreen and channels as ADC. On the G20 however, it is just an ADC.
>=20
> For now, the plan is only to support the ADC mode, so I put it in adc.

Just to expand a bit on this: there is already in the kernel an input
driver for the AT91 touchscreen which uses some ADC channels (that have
additional touchscreen-related features: on the G45, you have 8 ADCs
channels, 4 can optionally be used for touchscreen, the 4 other are
just raw ADCs).

The ultimate goal is of course to make it possible to use both the IIO
AT91 ADC driver and the AT91 touchscreen driver work simultaneously
(with of course non-conflicting ADC channels usage). However, as shown
by recent discussions on this list, it is not yet entirely clearly how
this should be done:

 * Should we have some low-level ADC driver in arch/arm/mach-at91/ that
   allows to request/release/access the ADC channels, this driver
   providing an internal kernel API used by the IIO ADC driver and the
   touchscreen driver ?

           sysfs access                  /dev/input/...
           to ADC channels               access to touchscreen
                ||                              ||
                \/                              \/
        +------------------+            +------------------+
        |     AT91 IIO     |            | AT91 touchscreen |
        +------------------+            +------------------+
                 |                               |
                 \_______________________________/
                                 |
                        +---------------------+
                        | Some low-level ADC  |
                        | code to request,    |
                        | release and access  |
                        | ADC channels        |
                        +---------------------+

 * Should IIO provide some internal kernel API (as you suggested some
   time ago) so that kernel drivers can use ADC channels ?


            +-------------------+
            | AT91 touchscreen  | -> /dev/input/... access to touchscreen
            +-------------------+
                     ||
                     \/
            +-------------------+
            |   AT91 IIO        | -> sysfs access to ADC channels
            +-------------------+

Regards,

Thomas
--=20
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-20  7:05         ` Thomas Petazzoni
  0 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-10-20  7:05 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

Le Wed, 19 Oct 2011 20:23:50 +0200,
Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit :

> On 19/10/2011 18:42, Jonathan Cameron wrote:
> > Afraid I'm out of time for today, but just thought I'd ask one quick question...
> > 
> > Why isn't this touchscreen ADC in input?
> 
> Because what I say in the Kconfig is mostly wrong... Sorry about that.
> 
> Actually, like I was saying, this ADC is present in a lot of AT91 SoC,
> and while it is always an ADC, sometimes (like on the G45), it can be
> used as a touchscreen controller, or even as both, with channels for the
> touchscreen and channels as ADC. On the G20 however, it is just an ADC.
> 
> For now, the plan is only to support the ADC mode, so I put it in adc.

Just to expand a bit on this: there is already in the kernel an input
driver for the AT91 touchscreen which uses some ADC channels (that have
additional touchscreen-related features: on the G45, you have 8 ADCs
channels, 4 can optionally be used for touchscreen, the 4 other are
just raw ADCs).

The ultimate goal is of course to make it possible to use both the IIO
AT91 ADC driver and the AT91 touchscreen driver work simultaneously
(with of course non-conflicting ADC channels usage). However, as shown
by recent discussions on this list, it is not yet entirely clearly how
this should be done:

 * Should we have some low-level ADC driver in arch/arm/mach-at91/ that
   allows to request/release/access the ADC channels, this driver
   providing an internal kernel API used by the IIO ADC driver and the
   touchscreen driver ?

           sysfs access                  /dev/input/...
           to ADC channels               access to touchscreen
                ||                              ||
                \/                              \/
        +------------------+            +------------------+
        |     AT91 IIO     |            | AT91 touchscreen |
        +------------------+            +------------------+
                 |                               |
                 \_______________________________/
                                 |
                        +---------------------+
                        | Some low-level ADC  |
                        | code to request,    |
                        | release and access  |
                        | ADC channels        |
                        +---------------------+

 * Should IIO provide some internal kernel API (as you suggested some
   time ago) so that kernel drivers can use ADC channels ?


            +-------------------+
            | AT91 touchscreen  | -> /dev/input/... access to touchscreen
            +-------------------+
                     ||
                     \/
            +-------------------+
            |   AT91 IIO        | -> sysfs access to ADC channels
            +-------------------+

Regards,

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-19 16:18   ` Maxime Ripard
@ 2011-10-20  7:09     ` Lars-Peter Clausen
  -1 siblings, 0 replies; 131+ messages in thread
From: Lars-Peter Clausen @ 2011-10-20  7:09 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

On 10/19/2011 06:18 PM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  [...]
> +
> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))

Shouldn't this rather be {readl,writel}_relaxed?

> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
> +	struct iio_chan_spec *channels;
> +	int nb_chan;
> +	int irq;
> +	wait_queue_head_t wq_data_avail;
> +	u16 lcdr;
> +	void __iomem *reg_base;
> +
> +};
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct at91adc_state *st = private;
> +	struct iio_dev *idev = iio_priv_to_dev(st);
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < st->nb_chan; chan++) {
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->lcdr = at91adc_reg_read(st->reg_base,
> +						    AT91_ADC_CHR(chan));
> +		}
> +	}
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct at91adc_state *st)
> +{
> +	int ret = 0, i;
> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
> +			       GFP_KERNEL);

kcalloc

> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < st->nb_chan; i++) {
> +		struct iio_chan_spec *chan = st->channels + i;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = i;


It probably makes sense to initialize scan_type as well.

> +		++ret;
> +	}
> +
> +	return ret;

Why do you need ret? Shouldn't this be just st->nb_chan?

> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible(st->wq_data_avail, st->done);

Its probably a good idea to add a timeout in case there is a problem with
the hardware.

> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(*st));
> +	if (idev == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = platform_get_device_id(pdev)->name;

You don't use device ids. This should cause a NULL pointer deref? Maybe use
dev_name(&pdev->dev) instead.

> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +[...]
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	free_irq(st->irq, st);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_unregister(idev);
> +

The iio API changed a bit recently and uses a two stage unregistration now.
You should call iio_device_unregister before freeing resources and
iio_device_free afterwards.

> +	return 0;
> +}
> +
> [...]

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-20  7:09     ` Lars-Peter Clausen
  0 siblings, 0 replies; 131+ messages in thread
From: Lars-Peter Clausen @ 2011-10-20  7:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/19/2011 06:18 PM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  [...]
> +
> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))

Shouldn't this rather be {readl,writel}_relaxed?

> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
> +	struct iio_chan_spec *channels;
> +	int nb_chan;
> +	int irq;
> +	wait_queue_head_t wq_data_avail;
> +	u16 lcdr;
> +	void __iomem *reg_base;
> +
> +};
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct at91adc_state *st = private;
> +	struct iio_dev *idev = iio_priv_to_dev(st);
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < st->nb_chan; chan++) {
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->lcdr = at91adc_reg_read(st->reg_base,
> +						    AT91_ADC_CHR(chan));
> +		}
> +	}
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct at91adc_state *st)
> +{
> +	int ret = 0, i;
> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
> +			       GFP_KERNEL);

kcalloc

> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < st->nb_chan; i++) {
> +		struct iio_chan_spec *chan = st->channels + i;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = i;


It probably makes sense to initialize scan_type as well.

> +		++ret;
> +	}
> +
> +	return ret;

Why do you need ret? Shouldn't this be just st->nb_chan?

> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible(st->wq_data_avail, st->done);

Its probably a good idea to add a timeout in case there is a problem with
the hardware.

> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(*st));
> +	if (idev == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = platform_get_device_id(pdev)->name;

You don't use device ids. This should cause a NULL pointer deref? Maybe use
dev_name(&pdev->dev) instead.

> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +[...]
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	free_irq(st->irq, st);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_unregister(idev);
> +

The iio API changed a bit recently and uses a two stage unregistration now.
You should call iio_device_unregister before freeing resources and
iio_device_free afterwards.

> +	return 0;
> +}
> +
> [...]

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

* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-10-19 16:18   ` Maxime Ripard
@ 2011-10-20  7:14     ` Thomas Petazzoni
  -1 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-10-20  7:14 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre

Le Wed, 19 Oct 2011 18:18:54 +0200,
Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit :

> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	at91_set_A_periph(AT91_PIN_PC0, 0);
> +	at91_set_A_periph(AT91_PIN_PC1, 0);
> +	at91_set_A_periph(AT91_PIN_PC2, 0);
> +	at91_set_A_periph(AT91_PIN_PC3, 0);

As suggested, the decision of which ADC channels are used and therefore
which pins should be muxed to function A is a board-specific decision
and should not be enforced by SoC code.

> +static struct at91_adc_data ek_adc_data =3D {
> +	.channels =3D 4,
> +	.adc_clock =3D 5000000,
> +	.startup_time =3D 10,
> +};

And on the opposite, those informations are specific to a SoC, not to a
board. So to avoid duplication in all board files, they should be moved
to the corresponding SoC file.

Maybe something like:

#define AT91_ADC_MAX_CHANNELS 8

struct at91_adc_data {
    unsigned int adc_clock;
    u8 channels_used[AT91_ADC_MAX_CHANNELS];
    u8 startup_time;
    u8 channels;
};

at91_add_device_adc() {
	if (data->channels_used[0])
		at91_set_A_periph(AT91_PIN_PC0, 0);
	if (data->channels_used[1])
		at91_set_A_periph(AT91_PIN_PC1, 0);
	if (data->channels_used[2])
		at91_set_A_periph(AT91_PIN_PC2, 0);
	if (data->channels_used[3])
		at91_set_A_periph(AT91_PIN_PC3, 0);
	data->startup_time =3D ...
	data->channels =3D ...
	data->adc_clock =3D ...
}

and the board file would do:

struct at91_adc_data ek_adc_data =3D {
	.channels_used =3D { 1, 0, 1, 1 },;
};

Or maybe others have different suggestions ?

Regards,

Thomas
--=20
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-10-20  7:14     ` Thomas Petazzoni
  0 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-10-20  7:14 UTC (permalink / raw)
  To: linux-arm-kernel

Le Wed, 19 Oct 2011 18:18:54 +0200,
Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit :

> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	at91_set_A_periph(AT91_PIN_PC0, 0);
> +	at91_set_A_periph(AT91_PIN_PC1, 0);
> +	at91_set_A_periph(AT91_PIN_PC2, 0);
> +	at91_set_A_periph(AT91_PIN_PC3, 0);

As suggested, the decision of which ADC channels are used and therefore
which pins should be muxed to function A is a board-specific decision
and should not be enforced by SoC code.

> +static struct at91_adc_data ek_adc_data = {
> +	.channels = 4,
> +	.adc_clock = 5000000,
> +	.startup_time = 10,
> +};

And on the opposite, those informations are specific to a SoC, not to a
board. So to avoid duplication in all board files, they should be moved
to the corresponding SoC file.

Maybe something like:

#define AT91_ADC_MAX_CHANNELS 8

struct at91_adc_data {
    unsigned int adc_clock;
    u8 channels_used[AT91_ADC_MAX_CHANNELS];
    u8 startup_time;
    u8 channels;
};

at91_add_device_adc() {
	if (data->channels_used[0])
		at91_set_A_periph(AT91_PIN_PC0, 0);
	if (data->channels_used[1])
		at91_set_A_periph(AT91_PIN_PC1, 0);
	if (data->channels_used[2])
		at91_set_A_periph(AT91_PIN_PC2, 0);
	if (data->channels_used[3])
		at91_set_A_periph(AT91_PIN_PC3, 0);
	data->startup_time = ...
	data->channels = ...
	data->adc_clock = ...
}

and the board file would do:

struct at91_adc_data ek_adc_data = {
	.channels_used = { 1, 0, 1, 1 },;
};

Or maybe others have different suggestions ?

Regards,

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-20  7:05         ` Thomas Petazzoni
@ 2011-10-20  8:33           ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-10-20  8:33 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre,
	linux-arm-kernel, Mark Brown, Linus Walleij

Bringing in Mark and Linus as others who have been active in talking ab=
out
similar issues.
> Hello,
>=20
> Le Wed, 19 Oct 2011 20:23:50 +0200,
> Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit :
>=20
>> On 19/10/2011 18:42, Jonathan Cameron wrote:
>>> Afraid I'm out of time for today, but just thought I'd ask one quic=
k question...
>>>
>>> Why isn't this touchscreen ADC in input?
>>
>> Because what I say in the Kconfig is mostly wrong... Sorry about tha=
t.
>>
>> Actually, like I was saying, this ADC is present in a lot of AT91 So=
C,
>> and while it is always an ADC, sometimes (like on the G45), it can b=
e
>> used as a touchscreen controller, or even as both, with channels for=
 the
>> touchscreen and channels as ADC. On the G20 however, it is just an A=
DC.
>>
>> For now, the plan is only to support the ADC mode, so I put it in ad=
c.
>=20
> Just to expand a bit on this: there is already in the kernel an input
> driver for the AT91 touchscreen which uses some ADC channels (that ha=
ve
> additional touchscreen-related features: on the G45, you have 8 ADCs
> channels, 4 can optionally be used for touchscreen, the 4 other are
> just raw ADCs).
>=20
> The ultimate goal is of course to make it possible to use both the II=
O
> AT91 ADC driver and the AT91 touchscreen driver work simultaneously
> (with of course non-conflicting ADC channels usage). However, as show=
n
> by recent discussions on this list, it is not yet entirely clearly ho=
w
> this should be done:
>=20
>  * Should we have some low-level ADC driver in arch/arm/mach-at91/ th=
at
>    allows to request/release/access the ADC channels, this driver
>    providing an internal kernel API used by the IIO ADC driver and th=
e
>    touchscreen driver ?
That's definitely not going to go down well against moves to move every=
thing
that looks like a driver out of the arch directories. An equivalent som=
ewhere
else might work though.
>=20
>            sysfs access                  /dev/input/...
>            to ADC channels               access to touchscreen
>                 ||                              ||
>                 \/                              \/
>         +------------------+            +------------------+
>         |     AT91 IIO     |            | AT91 touchscreen |
>         +------------------+            +------------------+
>                  |                               |
>                  \_______________________________/
>                                  |
>                         +---------------------+
>                         | Some low-level ADC  |
>                         | code to request,    |
>                         | release and access  |
>                         | ADC channels        |
>                         +---------------------+
>=20
>  * Should IIO provide some internal kernel API (as you suggested some
>    time ago) so that kernel drivers can use ADC channels ?
>=20
>=20
>             +-------------------+
>             | AT91 touchscreen  | -> /dev/input/... access to touchsc=
reen
>             +-------------------+
>                      ||
>                      \/
>             +-------------------+
>             |   AT91 IIO        | -> sysfs access to ADC channels
>             +-------------------+
>=20
> Regards,
>=20
> Thomas
Agreed.  This is currently up in the air. The current state of those II=
O
hooks is that they do pull only.  Push based capture is tricky as for I=
IO
we want to control the triggering of the push at a far finer scale than
makes sense for input. I'm not yet clear how these two will play togeth=
er.

If we sit something underneath the IIO and input drivers (or use the lo=
wer
portions of IIO to do this) then the intent would be to have a fully ge=
neric
input touchscreen driver on top.  I'm not sure that's possible. For
example are the switches on the G45's adc (from datasheet) something th=
at
all/many similar touch screen controllers have?
I suppose we could support these in the chan_spec but only if it helps
us to write a generic touch screen driver on top.  I have no real
feeling for whether this would be possible...
(all my boards are headless ;)

Jonathan

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-20  8:33           ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-10-20  8:33 UTC (permalink / raw)
  To: linux-arm-kernel

Bringing in Mark and Linus as others who have been active in talking about
similar issues.
> Hello,
> 
> Le Wed, 19 Oct 2011 20:23:50 +0200,
> Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit :
> 
>> On 19/10/2011 18:42, Jonathan Cameron wrote:
>>> Afraid I'm out of time for today, but just thought I'd ask one quick question...
>>>
>>> Why isn't this touchscreen ADC in input?
>>
>> Because what I say in the Kconfig is mostly wrong... Sorry about that.
>>
>> Actually, like I was saying, this ADC is present in a lot of AT91 SoC,
>> and while it is always an ADC, sometimes (like on the G45), it can be
>> used as a touchscreen controller, or even as both, with channels for the
>> touchscreen and channels as ADC. On the G20 however, it is just an ADC.
>>
>> For now, the plan is only to support the ADC mode, so I put it in adc.
> 
> Just to expand a bit on this: there is already in the kernel an input
> driver for the AT91 touchscreen which uses some ADC channels (that have
> additional touchscreen-related features: on the G45, you have 8 ADCs
> channels, 4 can optionally be used for touchscreen, the 4 other are
> just raw ADCs).
> 
> The ultimate goal is of course to make it possible to use both the IIO
> AT91 ADC driver and the AT91 touchscreen driver work simultaneously
> (with of course non-conflicting ADC channels usage). However, as shown
> by recent discussions on this list, it is not yet entirely clearly how
> this should be done:
> 
>  * Should we have some low-level ADC driver in arch/arm/mach-at91/ that
>    allows to request/release/access the ADC channels, this driver
>    providing an internal kernel API used by the IIO ADC driver and the
>    touchscreen driver ?
That's definitely not going to go down well against moves to move everything
that looks like a driver out of the arch directories. An equivalent somewhere
else might work though.
> 
>            sysfs access                  /dev/input/...
>            to ADC channels               access to touchscreen
>                 ||                              ||
>                 \/                              \/
>         +------------------+            +------------------+
>         |     AT91 IIO     |            | AT91 touchscreen |
>         +------------------+            +------------------+
>                  |                               |
>                  \_______________________________/
>                                  |
>                         +---------------------+
>                         | Some low-level ADC  |
>                         | code to request,    |
>                         | release and access  |
>                         | ADC channels        |
>                         +---------------------+
> 
>  * Should IIO provide some internal kernel API (as you suggested some
>    time ago) so that kernel drivers can use ADC channels ?
> 
> 
>             +-------------------+
>             | AT91 touchscreen  | -> /dev/input/... access to touchscreen
>             +-------------------+
>                      ||
>                      \/
>             +-------------------+
>             |   AT91 IIO        | -> sysfs access to ADC channels
>             +-------------------+
> 
> Regards,
> 
> Thomas
Agreed.  This is currently up in the air. The current state of those IIO
hooks is that they do pull only.  Push based capture is tricky as for IIO
we want to control the triggering of the push at a far finer scale than
makes sense for input. I'm not yet clear how these two will play together.

If we sit something underneath the IIO and input drivers (or use the lower
portions of IIO to do this) then the intent would be to have a fully generic
input touchscreen driver on top.  I'm not sure that's possible. For
example are the switches on the G45's adc (from datasheet) something that
all/many similar touch screen controllers have?
I suppose we could support these in the chan_spec but only if it helps
us to write a generic touch screen driver on top.  I have no real
feeling for whether this would be possible...
(all my boards are headless ;)

Jonathan

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-20  8:33           ` Jonathan Cameron
@ 2011-10-20  8:49             ` Thomas Petazzoni
  -1 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-10-20  8:49 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre,
	linux-arm-kernel, Mark Brown, Linus Walleij

Le Thu, 20 Oct 2011 09:33:29 +0100,
Jonathan Cameron <jic23@cam.ac.uk> a =C3=A9crit :

> >  * Should we have some low-level ADC driver in arch/arm/mach-at91/ that
> >    allows to request/release/access the ADC channels, this driver
> >    providing an internal kernel API used by the IIO ADC driver and the
> >    touchscreen driver ?
> That's definitely not going to go down well against moves to move everyth=
ing
> that looks like a driver out of the arch directories. An equivalent somew=
here
> else might work though.

Yes, of course if this needs to be implemented, it should be in some
other location than arch/arm, but not sure where (which is basically
the discussion that was started some time ago).

> Agreed.  This is currently up in the air. The current state of those IIO
> hooks is that they do pull only.  Push based capture is tricky as for IIO
> we want to control the triggering of the push at a far finer scale than
> makes sense for input. I'm not yet clear how these two will play together.

My IIO knowledge is still too limited to understand what's the problem
with exposing an API for push-based capture.

> If we sit something underneath the IIO and input drivers (or use the lower
> portions of IIO to do this) then the intent would be to have a fully gene=
ric
> input touchscreen driver on top.  I'm not sure that's possible.

I am not sure it's possible to make the touchscreen driver generic. On
the G45, the ADC IP has some generic ADC registers, but also some
touchscreen-specific registers. So the touchscreen driver probably
cannot be completely generic and some cooperation between the AT91 ADC
driver and the AT91 touchscreen driver might be needed. I'd have to
look into more details on how the AT91 touchscreen thing works to
provide some more details here.

> For example are the switches on the G45's adc (from datasheet) something =
that
> all/many similar touch screen controllers have?

I have no idea.

However, I have also seen a platform (SuperH 2A) where ADC channels are
used for keypad handling. 4 ADCs channels, each connected to 4 keys,
each having a different resistor. Depending on the voltage measured at
the ADC, you're capable of knowing which key of the 4x4 keypad is
pressed. This is a different situation where the ADC values need to be
used by another in-kernel driver.

Regards,

Thomas
--=20
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-20  8:49             ` Thomas Petazzoni
  0 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-10-20  8:49 UTC (permalink / raw)
  To: linux-arm-kernel

Le Thu, 20 Oct 2011 09:33:29 +0100,
Jonathan Cameron <jic23@cam.ac.uk> a ?crit :

> >  * Should we have some low-level ADC driver in arch/arm/mach-at91/ that
> >    allows to request/release/access the ADC channels, this driver
> >    providing an internal kernel API used by the IIO ADC driver and the
> >    touchscreen driver ?
> That's definitely not going to go down well against moves to move everything
> that looks like a driver out of the arch directories. An equivalent somewhere
> else might work though.

Yes, of course if this needs to be implemented, it should be in some
other location than arch/arm, but not sure where (which is basically
the discussion that was started some time ago).

> Agreed.  This is currently up in the air. The current state of those IIO
> hooks is that they do pull only.  Push based capture is tricky as for IIO
> we want to control the triggering of the push at a far finer scale than
> makes sense for input. I'm not yet clear how these two will play together.

My IIO knowledge is still too limited to understand what's the problem
with exposing an API for push-based capture.

> If we sit something underneath the IIO and input drivers (or use the lower
> portions of IIO to do this) then the intent would be to have a fully generic
> input touchscreen driver on top.  I'm not sure that's possible.

I am not sure it's possible to make the touchscreen driver generic. On
the G45, the ADC IP has some generic ADC registers, but also some
touchscreen-specific registers. So the touchscreen driver probably
cannot be completely generic and some cooperation between the AT91 ADC
driver and the AT91 touchscreen driver might be needed. I'd have to
look into more details on how the AT91 touchscreen thing works to
provide some more details here.

> For example are the switches on the G45's adc (from datasheet) something that
> all/many similar touch screen controllers have?

I have no idea.

However, I have also seen a platform (SuperH 2A) where ADC channels are
used for keypad handling. 4 ADCs channels, each connected to 4 keys,
each having a different resistor. Depending on the voltage measured at
the ADC, you're capable of knowing which key of the 4x4 keypad is
pressed. This is a different situation where the ADC values need to be
used by another in-kernel driver.

Regards,

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-20  8:49             ` Thomas Petazzoni
@ 2011-10-20  9:19               ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-10-20  9:19 UTC (permalink / raw)
  To: Thomas Petazzoni
  Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre,
	linux-arm-kernel, Mark Brown, Linus Walleij

On 10/20/11 09:49, Thomas Petazzoni wrote:
> Le Thu, 20 Oct 2011 09:33:29 +0100,
> Jonathan Cameron <jic23@cam.ac.uk> a =C3=A9crit :
>=20
>>>  * Should we have some low-level ADC driver in arch/arm/mach-at91/ =
that
>>>    allows to request/release/access the ADC channels, this driver
>>>    providing an internal kernel API used by the IIO ADC driver and =
the
>>>    touchscreen driver ?
>> That's definitely not going to go down well against moves to move ev=
erything
>> that looks like a driver out of the arch directories. An equivalent =
somewhere
>> else might work though.
>=20
> Yes, of course if this needs to be implemented, it should be in some
> other location than arch/arm, but not sure where (which is basically
> the discussion that was started some time ago).
>=20
>> Agreed.  This is currently up in the air. The current state of those=
 IIO
>> hooks is that they do pull only.  Push based capture is tricky as fo=
r IIO
>> we want to control the triggering of the push at a far finer scale t=
han
>> makes sense for input. I'm not yet clear how these two will play tog=
ether.
>=20
> My IIO knowledge is still too limited to understand what's the proble=
m
> with exposing an API for push-based capture.
I'll give a quick summary:

Push in IIO is done from a trigger (imagine an oscilloscope).  Those tr=
iggers
may be supplied by a dataready signal or from somewhere entirely differ=
ent.
Also multiple devices can be triggered by the same signal.  Thus the da=
taready
from one chip can trigger a whole load of others.

Right now filtering functions prevent some devices from using anything =
other than
their own trigger.  Setting defaults is also possible. If anything is t=
hen registered
to receive the data the trigger cannot be changed anyway so that will w=
ork here.

The other bit that makes it complex is the data flow.
This is done in the form of scans of a set of channels. The description=
 of these
are more or less available to the core.  Note the scan you get may well=
 have
more channels in it than you explicitly requested.

These scans are then pushed into one of a set of different buffers.  No=
te
All of the scan is currently pushed.  That makes sense if you only have=
 one
user of the data.  Right now the pushing is done by the individual driv=
ers
but this could pass through the core.

So things that stand in the way of more general use:

1) Userspace controlled triggers (apply to whole ADC) - can lock these =
down if
say a touchscreen driver is using the device.
2) Single output stream of data.
3) Note there is deliberately no metadata in the stream. It is all out =
of band.

Push to one driver is easy.  Anything else needs a demux that is aware =
of the
stream contents.  This bit doesn't exist and looks non trivial.  The re=
al
trick is going to be keeping it light and fast (or entirely out of the
way when not needed.)

At some point I'll have a play with rerouting the data so such a demux
can sit in the current flow (can use it to kill off unwanted channels).

>=20
>> If we sit something underneath the IIO and input drivers (or use the=
 lower
>> portions of IIO to do this) then the intent would be to have a fully=
 generic
>> input touchscreen driver on top.  I'm not sure that's possible.
>=20
> I am not sure it's possible to make the touchscreen driver generic. O=
n
> the G45, the ADC IP has some generic ADC registers, but also some
> touchscreen-specific registers. So the touchscreen driver probably
> cannot be completely generic and some cooperation between the AT91 AD=
C
> driver and the AT91 touchscreen driver might be needed. I'd have to
> look into more details on how the AT91 touchscreen thing works to
> provide some more details here.
>=20
>> For example are the switches on the G45's adc (from datasheet) somet=
hing that
>> all/many similar touch screen controllers have?
>=20
> I have no idea.
>=20
> However, I have also seen a platform (SuperH 2A) where ADC channels a=
re
> used for keypad handling. 4 ADCs channels, each connected to 4 keys,
> each having a different resistor. Depending on the voltage measured a=
t
> the ADC, you're capable of knowing which key of the 4x4 keypad is
> pressed. This is a different situation where the ADC values need to b=
e
> used by another in-kernel driver.
That one is much easier to handle and indeed not that uncommon a hack.
It really is just using the ADCs to get a value.  Hence with suitable
descriptive map it's easy to write a generic driver for it.
Even easier if it's simply polled ;)
>=20
> Regards,
>=20
> Thomas


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-20  9:19               ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-10-20  9:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/20/11 09:49, Thomas Petazzoni wrote:
> Le Thu, 20 Oct 2011 09:33:29 +0100,
> Jonathan Cameron <jic23@cam.ac.uk> a ?crit :
> 
>>>  * Should we have some low-level ADC driver in arch/arm/mach-at91/ that
>>>    allows to request/release/access the ADC channels, this driver
>>>    providing an internal kernel API used by the IIO ADC driver and the
>>>    touchscreen driver ?
>> That's definitely not going to go down well against moves to move everything
>> that looks like a driver out of the arch directories. An equivalent somewhere
>> else might work though.
> 
> Yes, of course if this needs to be implemented, it should be in some
> other location than arch/arm, but not sure where (which is basically
> the discussion that was started some time ago).
> 
>> Agreed.  This is currently up in the air. The current state of those IIO
>> hooks is that they do pull only.  Push based capture is tricky as for IIO
>> we want to control the triggering of the push at a far finer scale than
>> makes sense for input. I'm not yet clear how these two will play together.
> 
> My IIO knowledge is still too limited to understand what's the problem
> with exposing an API for push-based capture.
I'll give a quick summary:

Push in IIO is done from a trigger (imagine an oscilloscope).  Those triggers
may be supplied by a dataready signal or from somewhere entirely different.
Also multiple devices can be triggered by the same signal.  Thus the dataready
from one chip can trigger a whole load of others.

Right now filtering functions prevent some devices from using anything other than
their own trigger.  Setting defaults is also possible. If anything is then registered
to receive the data the trigger cannot be changed anyway so that will work here.

The other bit that makes it complex is the data flow.
This is done in the form of scans of a set of channels. The description of these
are more or less available to the core.  Note the scan you get may well have
more channels in it than you explicitly requested.

These scans are then pushed into one of a set of different buffers.  Note
All of the scan is currently pushed.  That makes sense if you only have one
user of the data.  Right now the pushing is done by the individual drivers
but this could pass through the core.

So things that stand in the way of more general use:

1) Userspace controlled triggers (apply to whole ADC) - can lock these down if
say a touchscreen driver is using the device.
2) Single output stream of data.
3) Note there is deliberately no metadata in the stream. It is all out of band.

Push to one driver is easy.  Anything else needs a demux that is aware of the
stream contents.  This bit doesn't exist and looks non trivial.  The real
trick is going to be keeping it light and fast (or entirely out of the
way when not needed.)

At some point I'll have a play with rerouting the data so such a demux
can sit in the current flow (can use it to kill off unwanted channels).

> 
>> If we sit something underneath the IIO and input drivers (or use the lower
>> portions of IIO to do this) then the intent would be to have a fully generic
>> input touchscreen driver on top.  I'm not sure that's possible.
> 
> I am not sure it's possible to make the touchscreen driver generic. On
> the G45, the ADC IP has some generic ADC registers, but also some
> touchscreen-specific registers. So the touchscreen driver probably
> cannot be completely generic and some cooperation between the AT91 ADC
> driver and the AT91 touchscreen driver might be needed. I'd have to
> look into more details on how the AT91 touchscreen thing works to
> provide some more details here.
> 
>> For example are the switches on the G45's adc (from datasheet) something that
>> all/many similar touch screen controllers have?
> 
> I have no idea.
> 
> However, I have also seen a platform (SuperH 2A) where ADC channels are
> used for keypad handling. 4 ADCs channels, each connected to 4 keys,
> each having a different resistor. Depending on the voltage measured at
> the ADC, you're capable of knowing which key of the 4x4 keypad is
> pressed. This is a different situation where the ADC values need to be
> used by another in-kernel driver.
That one is much easier to handle and indeed not that uncommon a hack.
It really is just using the ADCs to get a value.  Hence with suitable
descriptive map it's easy to write a generic driver for it.
Even easier if it's simply polled ;)
> 
> Regards,
> 
> Thomas

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-20  8:33           ` Jonathan Cameron
@ 2011-10-20  9:52             ` Mark Brown
  -1 siblings, 0 replies; 131+ messages in thread
From: Mark Brown @ 2011-10-20  9:52 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Thomas Petazzoni, linux-iio, Patrice Vilchez, Linus Walleij,
	Nicolas Ferre, Maxime Ripard, linux-arm-kernel

On Thu, Oct 20, 2011 at 09:33:29AM +0100, Jonathan Cameron wrote:

> Agreed.  This is currently up in the air. The current state of those IIO
> hooks is that they do pull only.  Push based capture is tricky as for IIO
> we want to control the triggering of the push at a far finer scale than
> makes sense for input. I'm not yet clear how these two will play together.

What's the issue here?  The touchscreen input is just a trigger

> If we sit something underneath the IIO and input drivers (or use the lower
> portions of IIO to do this) then the intent would be to have a fully generic
> input touchscreen driver on top.  I'm not sure that's possible. For

I'm not sure that's going to be tractable, while the ADC usage may be the
same I'd expect to see a bunch of other logic around them for pen down
detection.  I'd guess it is going to be more tractable to have a library
that most ADC touchscreens can share which handles the actual data
transfer bit of things but has extra glue logic around it to make the
actual device.

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-20  9:52             ` Mark Brown
  0 siblings, 0 replies; 131+ messages in thread
From: Mark Brown @ 2011-10-20  9:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Oct 20, 2011 at 09:33:29AM +0100, Jonathan Cameron wrote:

> Agreed.  This is currently up in the air. The current state of those IIO
> hooks is that they do pull only.  Push based capture is tricky as for IIO
> we want to control the triggering of the push at a far finer scale than
> makes sense for input. I'm not yet clear how these two will play together.

What's the issue here?  The touchscreen input is just a trigger

> If we sit something underneath the IIO and input drivers (or use the lower
> portions of IIO to do this) then the intent would be to have a fully generic
> input touchscreen driver on top.  I'm not sure that's possible. For

I'm not sure that's going to be tractable, while the ADC usage may be the
same I'd expect to see a bunch of other logic around them for pen down
detection.  I'd guess it is going to be more tractable to have a library
that most ADC touchscreens can share which handles the actual data
transfer bit of things but has extra glue logic around it to make the
actual device.

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

* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-10-20  6:28     ` Alexander Stein
@ 2011-10-21 17:47       ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-21 17:47 UTC (permalink / raw)
  To: Alexander Stein
  Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre

Hi,

On 20/10/2011 08:28, Alexander Stein wrote:
> On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote:
>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c
>> b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644
>> --- a/arch/arm/mach-at91/at91sam9260_devices.c
>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
>> [...]
>> +static struct platform_device at91_adc_device = {
>> +	.name		= "at91adc",
>> +	.id		= -1,
>> +	.dev		= {
>> +				.platform_data		= &adc_data,
>> +	},
>> +	.resource	= adc_resources,
>> +	.num_resources	= ARRAY_SIZE(adc_resources),
>> +};
>> +
>> +void __init at91_add_device_adc(struct at91_adc_data *data)
>> +{
>> +	if (!data)
>> +		return;
>> +
>> +	at91_set_A_periph(AT91_PIN_PC0, 0);
>> +	at91_set_A_periph(AT91_PIN_PC1, 0);
>> +	at91_set_A_periph(AT91_PIN_PC2, 0);
>> +	at91_set_A_periph(AT91_PIN_PC3, 0);
>> +
>> +	adc_data = *data;
>> +	platform_device_register(&at91_adc_device);
>> +}
>> +
>> +
> 
> This assumes that all 4 channels are used every time.  How about only using an 
> AD channel on PC2 or all but the one on PC1?

You're right. I've implemented Thomas suggestion to define the channels
in use in a board specific manner. I need to test my changes and will
submit a new version by monday.

Thanks,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-10-21 17:47       ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-21 17:47 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 20/10/2011 08:28, Alexander Stein wrote:
> On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote:
>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c
>> b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644
>> --- a/arch/arm/mach-at91/at91sam9260_devices.c
>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
>> [...]
>> +static struct platform_device at91_adc_device = {
>> +	.name		= "at91adc",
>> +	.id		= -1,
>> +	.dev		= {
>> +				.platform_data		= &adc_data,
>> +	},
>> +	.resource	= adc_resources,
>> +	.num_resources	= ARRAY_SIZE(adc_resources),
>> +};
>> +
>> +void __init at91_add_device_adc(struct at91_adc_data *data)
>> +{
>> +	if (!data)
>> +		return;
>> +
>> +	at91_set_A_periph(AT91_PIN_PC0, 0);
>> +	at91_set_A_periph(AT91_PIN_PC1, 0);
>> +	at91_set_A_periph(AT91_PIN_PC2, 0);
>> +	at91_set_A_periph(AT91_PIN_PC3, 0);
>> +
>> +	adc_data = *data;
>> +	platform_device_register(&at91_adc_device);
>> +}
>> +
>> +
> 
> This assumes that all 4 channels are used every time.  How about only using an 
> AD channel on PC2 or all but the one on PC1?

You're right. I've implemented Thomas suggestion to define the channels
in use in a board specific manner. I need to test my changes and will
submit a new version by monday.

Thanks,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-20  7:09     ` Lars-Peter Clausen
@ 2011-10-21 17:54       ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-21 17:54 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

Hi,

On 20/10/2011 09:09, Lars-Peter Clausen wrote:
> On 10/19/2011 06:18 PM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  [...]
>> +
>> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
>> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
> 
> Shouldn't this rather be {readl,writel}_relaxed?

Yep, corrected.

>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	struct iio_chan_spec *channels;
>> +	int nb_chan;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 lcdr;
>> +	void __iomem *reg_base;
>> +
>> +};
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct at91adc_state *st = private;
>> +	struct iio_dev *idev = iio_priv_to_dev(st);
>> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < st->nb_chan; chan++) {
>> +		if (status & AT91_ADC_EOC(chan)) {
>> +			st->done = true;
>> +			st->lcdr = at91adc_reg_read(st->reg_base,
>> +						    AT91_ADC_CHR(chan));
>> +		}
>> +	}
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct at91adc_state *st)
>> +{
>> +	int ret = 0, i;
>> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
>> +			       GFP_KERNEL);
> 
> kcalloc

Corrected

>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < st->nb_chan; i++) {
>> +		struct iio_chan_spec *chan = st->channels + i;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = i;
> 
> 
> It probably makes sense to initialize scan_type as well.

Ah, didn't see that one, but yeah, you're right.

>> +		++ret;
>> +	}
>> +
>> +	return ret;
> 
> Why do you need ret? Shouldn't this be just st->nb_chan?

The initial plan was to make sure all channels were initialised, but
obviously, my code isn't making such use of ret. You're right.

>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible(st->wq_data_avail, st->done);
> 
> Its probably a good idea to add a timeout in case there is a problem with
> the hardware.

Indeed.

>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(*st));
>> +	if (idev == NULL) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = platform_get_device_id(pdev)->name;
> 
> You don't use device ids. This should cause a NULL pointer deref? Maybe use
> dev_name(&pdev->dev) instead.

Ok.

>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +[...]
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	free_irq(st->irq, st);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_unregister(idev);
>> +
> 
> The iio API changed a bit recently and uses a two stage unregistration now.
> You should call iio_device_unregister before freeing resources and
> iio_device_free afterwards.

Hmmm, I don't see the iio_device_free in Jonathan's git. Is it
iio_free_device, or should I use another git repo/branch as base ?

Thanks,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-21 17:54       ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-21 17:54 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 20/10/2011 09:09, Lars-Peter Clausen wrote:
> On 10/19/2011 06:18 PM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  [...]
>> +
>> +#define at91adc_reg_read(base, reg)        __raw_readl((base) + (reg))
>> +#define at91adc_reg_write(base, reg, val)  __raw_writel((val), (base) + (reg))
> 
> Shouldn't this rather be {readl,writel}_relaxed?

Yep, corrected.

>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	struct iio_chan_spec *channels;
>> +	int nb_chan;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 lcdr;
>> +	void __iomem *reg_base;
>> +
>> +};
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct at91adc_state *st = private;
>> +	struct iio_dev *idev = iio_priv_to_dev(st);
>> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < st->nb_chan; chan++) {
>> +		if (status & AT91_ADC_EOC(chan)) {
>> +			st->done = true;
>> +			st->lcdr = at91adc_reg_read(st->reg_base,
>> +						    AT91_ADC_CHR(chan));
>> +		}
>> +	}
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct at91adc_state *st)
>> +{
>> +	int ret = 0, i;
>> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
>> +			       GFP_KERNEL);
> 
> kcalloc

Corrected

>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < st->nb_chan; i++) {
>> +		struct iio_chan_spec *chan = st->channels + i;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = i;
> 
> 
> It probably makes sense to initialize scan_type as well.

Ah, didn't see that one, but yeah, you're right.

>> +		++ret;
>> +	}
>> +
>> +	return ret;
> 
> Why do you need ret? Shouldn't this be just st->nb_chan?

The initial plan was to make sure all channels were initialised, but
obviously, my code isn't making such use of ret. You're right.

>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible(st->wq_data_avail, st->done);
> 
> Its probably a good idea to add a timeout in case there is a problem with
> the hardware.

Indeed.

>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(*st));
>> +	if (idev == NULL) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = platform_get_device_id(pdev)->name;
> 
> You don't use device ids. This should cause a NULL pointer deref? Maybe use
> dev_name(&pdev->dev) instead.

Ok.

>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +[...]
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	free_irq(st->irq, st);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_unregister(idev);
>> +
> 
> The iio API changed a bit recently and uses a two stage unregistration now.
> You should call iio_device_unregister before freeing resources and
> iio_device_free afterwards.

Hmmm, I don't see the iio_device_free in Jonathan's git. Is it
iio_free_device, or should I use another git repo/branch as base ?

Thanks,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-21 17:54       ` Maxime Ripard
@ 2011-10-21 17:55         ` Lars-Peter Clausen
  -1 siblings, 0 replies; 131+ messages in thread
From: Lars-Peter Clausen @ 2011-10-21 17:55 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

On 10/21/2011 07:54 PM, Maxime Ripard wrote:
> [...]
>>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +
>>> +	free_irq(st->irq, st);
>>> +	iounmap(st->reg_base);
>>> +	release_mem_region(res->start, resource_size(res));
>>> +	iio_device_unregister(idev);
>>> +
>>
>> The iio API changed a bit recently and uses a two stage unregistration now.
>> You should call iio_device_unregister before freeing resources and
>> iio_device_free afterwards.
> 
> Hmmm, I don't see the iio_device_free in Jonathan's git. Is it
> iio_free_device, or should I use another git repo/branch as base ?
> 
> Thanks,
> 

Yes, iio_free_device, sorry.

- Lars

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-21 17:55         ` Lars-Peter Clausen
  0 siblings, 0 replies; 131+ messages in thread
From: Lars-Peter Clausen @ 2011-10-21 17:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/21/2011 07:54 PM, Maxime Ripard wrote:
> [...]
>>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +
>>> +	free_irq(st->irq, st);
>>> +	iounmap(st->reg_base);
>>> +	release_mem_region(res->start, resource_size(res));
>>> +	iio_device_unregister(idev);
>>> +
>>
>> The iio API changed a bit recently and uses a two stage unregistration now.
>> You should call iio_device_unregister before freeing resources and
>> iio_device_free afterwards.
> 
> Hmmm, I don't see the iio_device_free in Jonathan's git. Is it
> iio_free_device, or should I use another git repo/branch as base ?
> 
> Thanks,
> 

Yes, iio_free_device, sorry.

- Lars

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-19 16:18   ` Maxime Ripard
@ 2011-10-23  9:08     ` Jean-Christophe PLAGNIOL-VILLARD
  -1 siblings, 0 replies; 131+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-10-23  9:08 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre

On 18:18 Wed 19 Oct     , Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
please keep me in CC
> +
> +static int at91adc_channel_init(struct at91adc_state *st)
> +{
> +	int ret = 0, i;
> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
> +			       GFP_KERNEL);
> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < st->nb_chan; i++) {
> +		struct iio_chan_spec *chan = st->channels + i;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = i;
> +		++ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible(st->wq_data_avail, st->done);
> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
	do not refence it copy need for the DT
> +
> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(*st));
> +	if (idev == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = platform_get_device_id(pdev)->name;
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
where is the platform data struct?

Best Regards,
J.

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-23  9:08     ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 0 replies; 131+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-10-23  9:08 UTC (permalink / raw)
  To: linux-arm-kernel

On 18:18 Wed 19 Oct     , Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
please keep me in CC
> +
> +static int at91adc_channel_init(struct at91adc_state *st)
> +{
> +	int ret = 0, i;
> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
> +			       GFP_KERNEL);
> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < st->nb_chan; i++) {
> +		struct iio_chan_spec *chan = st->channels + i;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = i;
> +		++ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible(st->wq_data_avail, st->done);
> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
	do not refence it copy need for the DT
> +
> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(*st));
> +	if (idev == NULL) {
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = platform_get_device_id(pdev)->name;
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
where is the platform data struct?

Best Regards,
J.

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-10-23  9:08     ` Jean-Christophe PLAGNIOL-VILLARD
@ 2011-10-24  8:21       ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-24  8:21 UTC (permalink / raw)
  To: Jean-Christophe PLAGNIOL-VILLARD
  Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre

Hi,

On 23/10/2011 11:08, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 18:18 Wed 19 Oct     , Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> please keep me in CC
ACK.

>> +
>> +static int at91adc_channel_init(struct at91adc_state *st)
>> +{
>> +	int ret = 0, i;
>> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
>> +			       GFP_KERNEL);
>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < st->nb_chan; i++) {
>> +		struct iio_chan_spec *chan = st->channels + i;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = i;
>> +		++ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible(st->wq_data_avail, st->done);
>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> 	do not refence it copy need for the DT

Ok

>> +
>> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(*st));
>> +	if (idev == NULL) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = platform_get_device_id(pdev)->name;
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
> where is the platform data struct?

In the first patch.

Regards,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-10-24  8:21       ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-10-24  8:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 23/10/2011 11:08, Jean-Christophe PLAGNIOL-VILLARD wrote:
> On 18:18 Wed 19 Oct     , Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> please keep me in CC
ACK.

>> +
>> +static int at91adc_channel_init(struct at91adc_state *st)
>> +{
>> +	int ret = 0, i;
>> +	st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan,
>> +			       GFP_KERNEL);
>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
>> +	for (i = 0; i < st->nb_chan; i++) {
>> +		struct iio_chan_spec *chan = st->channels + i;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = i;
>> +		++ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible(st->wq_data_avail, st->done);
>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> 	do not refence it copy need for the DT

Ok

>> +
>> +	dev_dbg(&pdev->dev, "AT91ADC probed\n");
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(*st));
>> +	if (idev == NULL) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = platform_get_device_id(pdev)->name;
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
> where is the platform data struct?

In the first patch.

Regards,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCHv2] AT91: Add a driver for the ADC
  2011-10-19 16:18 ` Maxime Ripard
@ 2011-11-03 10:11   ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the branch master of Jonathan Cameron present at :
https://github.com/jic23/linux-iio/, commit 85d8ff8

Improvements since v1:
  - Rebase on top of commit 85d8ff8
  - Initialise scan_types
  - Added scale informations in the driver
  - Allow to use only a subset of adc channels available on the SoC
  - Various fix according to reviews

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>


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

* [PATCHv2] AT91: Add a driver for the ADC
@ 2011-11-03 10:11   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the branch master of Jonathan Cameron present at :
https://github.com/jic23/linux-iio/, commit 85d8ff8

Improvements since v1:
  - Rebase on top of commit 85d8ff8
  - Initialise scan_types
  - Added scale informations in the driver
  - Allow to use only a subset of adc channels available on the SoC
  - Various fix according to reviews

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 10:11   ` Maxime Ripard
@ 2011-11-03 10:11     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..b837da8 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+#define AT91_ADC_MAX_CHANNELS   16
+
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used array).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board */
+	u8 channels_used[AT91_ADC_MAX_CHANNELS];
+	/* Number of channels in use */
+	u8 num_channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-03 10:11     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..b837da8 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+#define AT91_ADC_MAX_CHANNELS   16
+
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used array).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board */
+	u8 channels_used[AT91_ADC_MAX_CHANNELS];
+	/* Number of channels in use */
+	u8 num_channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-03 10:11   ` Maxime Ripard
@ 2011-11-03 10:11     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/staging/iio/adc/Kconfig   |    6 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/at91adc.c |  310 +++++++++++++++++++++++++++++++++++++
 3 files changed, 317 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/at91adc.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index d9decea..c384894 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -169,6 +169,12 @@ config AD7280
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7280a
 
+config AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index ceee7f3..d250d06 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o
 obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
+obj-$(CONFIG_AT91ADC) += at91adc.o
diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
new file mode 100644
index 0000000..acf656d
--- /dev/null
+++ b/drivers/staging/iio/adc/at91adc.c
@@ -0,0 +1,310 @@
+/*
+ * Driver for the TouchScreen ADC Controller present in the Atmel AT91
+ * evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "../iio.h"
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+#define at91adc_reg_read(base, reg)        readl_relaxed((base) + (reg))
+#define at91adc_reg_write(base, reg, val)  writel_relaxed((val), (base) + (reg))
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	struct iio_chan_spec *channels;
+	int nb_chan;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 lcdr;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct at91adc_state *st = private;
+	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < st->nb_chan; chan++) {
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->lcdr = at91adc_reg_read(st->reg_base,
+						    AT91_ADC_CHR(chan));
+		}
+	}
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct at91adc_state *st,
+				struct at91_adc_data *pdata)
+{
+	int i, idx = 0;
+	st->channels = kcalloc(pdata->num_channels_used,
+			       sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (st->channels == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < st->nb_chan; i++) {
+		if(pdata->channels_used[i]) {
+			struct iio_chan_spec *chan = st->channels + idx;
+			chan->type = IIO_VOLTAGE;
+			chan->indexed = 1;
+			chan->channel = i;
+			chan->scan_type.sign = 's';
+			chan->scan_type.realbits = 10;
+			chan->scan_type.storagebits = 32;
+			chan->scan_type.shift = 0;
+			chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+			++idx;
+		}
+	}
+
+	return st->nb_chan;
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
+
+		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
+						 msecs_to_jiffies(10 * 1000));
+		*val = st->lcdr;
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->lcdr = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_allocate_device(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->modes = INDIO_DIRECT_MODE;
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	st->nb_chan = pdata->num_channels;
+	ret = at91adc_channel_init(st, pdata);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	idev->channels = st->channels;
+	idev->num_channels = pdata->num_channels_used;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	return 0;
+
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_free_device(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	free_irq(st->irq, st);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_free_device(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+		   },
+};
+
+static int __init at91adc_init(void)
+{
+	return platform_driver_register(&at91adc_driver);
+}
+
+static void __exit at91adc_exit(void)
+{
+	platform_driver_unregister(&at91adc_driver);
+}
+
+module_init(at91adc_init);
+module_exit(at91adc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-03 10:11     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/staging/iio/adc/Kconfig   |    6 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/at91adc.c |  310 +++++++++++++++++++++++++++++++++++++
 3 files changed, 317 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/at91adc.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index d9decea..c384894 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -169,6 +169,12 @@ config AD7280
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7280a
 
+config AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index ceee7f3..d250d06 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o
 obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
+obj-$(CONFIG_AT91ADC) += at91adc.o
diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
new file mode 100644
index 0000000..acf656d
--- /dev/null
+++ b/drivers/staging/iio/adc/at91adc.c
@@ -0,0 +1,310 @@
+/*
+ * Driver for the TouchScreen ADC Controller present in the Atmel AT91
+ * evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "../iio.h"
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+#define at91adc_reg_read(base, reg)        readl_relaxed((base) + (reg))
+#define at91adc_reg_write(base, reg, val)  writel_relaxed((val), (base) + (reg))
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	struct iio_chan_spec *channels;
+	int nb_chan;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 lcdr;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct at91adc_state *st = private;
+	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < st->nb_chan; chan++) {
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->lcdr = at91adc_reg_read(st->reg_base,
+						    AT91_ADC_CHR(chan));
+		}
+	}
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct at91adc_state *st,
+				struct at91_adc_data *pdata)
+{
+	int i, idx = 0;
+	st->channels = kcalloc(pdata->num_channels_used,
+			       sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (st->channels == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < st->nb_chan; i++) {
+		if(pdata->channels_used[i]) {
+			struct iio_chan_spec *chan = st->channels + idx;
+			chan->type = IIO_VOLTAGE;
+			chan->indexed = 1;
+			chan->channel = i;
+			chan->scan_type.sign = 's';
+			chan->scan_type.realbits = 10;
+			chan->scan_type.storagebits = 32;
+			chan->scan_type.shift = 0;
+			chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+			++idx;
+		}
+	}
+
+	return st->nb_chan;
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
+
+		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
+						 msecs_to_jiffies(10 * 1000));
+		*val = st->lcdr;
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->lcdr = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_allocate_device(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		dev_err(&pdev->dev, "Failed to allocate memory.\n");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->modes = INDIO_DIRECT_MODE;
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	st->nb_chan = pdata->num_channels;
+	ret = at91adc_channel_init(st, pdata);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	idev->channels = st->channels;
+	idev->num_channels = pdata->num_channels_used;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		goto error_free_clk;
+	}
+
+	return 0;
+
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_free_device(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	free_irq(st->irq, st);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_free_device(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+		   },
+};
+
+static int __init at91adc_init(void)
+{
+	return platform_driver_register(&at91adc_driver);
+}
+
+static void __exit at91adc_exit(void)
+{
+	platform_driver_unregister(&at91adc_driver);
+}
+
+module_init(at91adc_init);
+module_exit(at91adc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-03 10:11   ` Maxime Ripard
@ 2011-11-03 10:11     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
 2 files changed, 64 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..bfc50ac 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if(data->channels_used[0])
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if(data->channels_used[1])
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if(data->channels_used[2])
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if(data->channels_used[3])
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..6f2542fb 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = { 1, 1, 1, 1} ,
+	.num_channels_used = 4,
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +399,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-11-03 10:11     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
 2 files changed, 64 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..bfc50ac 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if(data->channels_used[0])
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if(data->channels_used[1])
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if(data->channels_used[2])
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if(data->channels_used[3])
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..6f2542fb 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = { 1, 1, 1, 1} ,
+	.num_channels_used = 4,
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +399,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 10:11     ` Maxime Ripard
@ 2011-11-03 11:27       ` Linus Walleij
  -1 siblings, 0 replies; 131+ messages in thread
From: Linus Walleij @ 2011-11-03 11:27 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>:

> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> =A0arch/arm/mach-at91/include/mach/board.h | =A0 22 +++++++++++++++++=
+++++
> =A01 files changed, 22 insertions(+), 0 deletions(-)

We're not supposed to have platform data dependent to stuff in
staging under arch/arm or anyplace else in the main kernel tree.

Please move this to
drivers/staging/iio/adc/at91adc-board.h
or so.

As for calling the at91_add_device_adc() function (which I guess
you want to do at some point) the pattern I followed for other
drivers is to declare a dummy function in arch/arm/mach-*
with __weak and let the staging driver override that. This way
the staging driver can go away without any compilation trouble
happening.

Yours,
Linus Walleij

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-03 11:27       ` Linus Walleij
  0 siblings, 0 replies; 131+ messages in thread
From: Linus Walleij @ 2011-11-03 11:27 UTC (permalink / raw)
  To: linux-arm-kernel

2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>:

> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> ?arch/arm/mach-at91/include/mach/board.h | ? 22 ++++++++++++++++++++++
> ?1 files changed, 22 insertions(+), 0 deletions(-)

We're not supposed to have platform data dependent to stuff in
staging under arch/arm or anyplace else in the main kernel tree.

Please move this to
drivers/staging/iio/adc/at91adc-board.h
or so.

As for calling the at91_add_device_adc() function (which I guess
you want to do at some point) the pattern I followed for other
drivers is to declare a dummy function in arch/arm/mach-*
with __weak and let the staging driver override that. This way
the staging driver can go away without any compilation trouble
happening.

Yours,
Linus Walleij

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 11:27       ` Linus Walleij
@ 2011-11-03 16:27         ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 16:27 UTC (permalink / raw)
  To: Linus Walleij; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

Hi Linus,

On 03/11/2011 12:27, Linus Walleij wrote:
> 2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>:
> 
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> We're not supposed to have platform data dependent to stuff in
> staging under arch/arm or anyplace else in the main kernel tree.
> 
> Please move this to
> drivers/staging/iio/adc/at91adc-board.h
> or so.

Won't moving this part to staging prevent from using this structure in
board files ? If so, how will I be able to declare a new board that is
using this ADC (or add the support for the ADC to a new one) ?

> As for calling the at91_add_device_adc() function (which I guess
> you want to do at some point) the pattern I followed for other
> drivers is to declare a dummy function in arch/arm/mach-*
> with __weak and let the staging driver override that. This way
> the staging driver can go away without any compilation trouble
> happening.

I don't really see why my changes will break the compilation if the
driver is no longer present in staging. At worst, the structure will be
filled but used by no one, right ?

Regards,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-03 16:27         ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-03 16:27 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Linus,

On 03/11/2011 12:27, Linus Walleij wrote:
> 2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>:
> 
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> We're not supposed to have platform data dependent to stuff in
> staging under arch/arm or anyplace else in the main kernel tree.
> 
> Please move this to
> drivers/staging/iio/adc/at91adc-board.h
> or so.

Won't moving this part to staging prevent from using this structure in
board files ? If so, how will I be able to declare a new board that is
using this ADC (or add the support for the ADC to a new one) ?

> As for calling the at91_add_device_adc() function (which I guess
> you want to do at some point) the pattern I followed for other
> drivers is to declare a dummy function in arch/arm/mach-*
> with __weak and let the staging driver override that. This way
> the staging driver can go away without any compilation trouble
> happening.

I don't really see why my changes will break the compilation if the
driver is no longer present in staging. At worst, the structure will be
filled but used by no one, right ?

Regards,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 16:27         ` Maxime Ripard
  (?)
@ 2011-11-03 16:38         ` Linus Walleij
  2011-11-03 18:05             ` Jean-Christophe PLAGNIOL-VILLARD
  -1 siblings, 1 reply; 131+ messages in thread
From: Linus Walleij @ 2011-11-03 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Nov 3, 2011 at 5:27 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> [Me]
>> We're not supposed to have platform data dependent to stuff in
>> staging under arch/arm or anyplace else in the main kernel tree.
>>
>> Please move this to
>> drivers/staging/iio/adc/at91adc-board.h
>> or so.
>
> Won't moving this part to staging prevent from using this structure in
> board files ? If so, how will I be able to declare a new board that is
> using this ADC (or add the support for the ADC to a new one) ?

Put this into a separate board file living under staging/iio/adc

Compare:
drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c

>> As for calling the at91_add_device_adc() function (which I guess
>> you want to do at some point) the pattern I followed for other
>> drivers is to declare a dummy function in arch/arm/mach-*
>> with __weak and let the staging driver override that. This way
>> the staging driver can go away without any compilation trouble
>> happening.
>
> I don't really see why my changes will break the compilation if the
> driver is no longer present in staging. At worst, the structure will be
> filled but used by no one, right ?

You're right.

But still, we cannot add that header file for a driver that
is in the staging tree. Header files go into the staging dir too.

Yours,
Linus Walleij

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 16:38         ` Linus Walleij
@ 2011-11-03 18:05             ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 0 replies; 131+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-03 18:05 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Maxime Ripard, linux-iio, Linus Walleij, Patrice Vilchez,
	linux-arm-kernel, Nicolas Ferre

On 17:38 Thu 03 Nov     , Linus Walleij wrote:
> On Thu, Nov 3, 2011 at 5:27 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > [Me]
> >> We're not supposed to have platform data dependent to stuff in
> >> staging under arch/arm or anyplace else in the main kernel tree.
> >>
> >> Please move this to
> >> drivers/staging/iio/adc/at91adc-board.h
> >> or so.
> >
> > Won't moving this part to staging prevent from using this structure in
> > board files ? If so, how will I be able to declare a new board that is
> > using this ADC (or add the support for the ADC to a new one) ?
> 
> Put this into a separate board file living under staging/iio/adc
> 
> Compare:
> drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c
> 
> >> As for calling the at91_add_device_adc() function (which I guess
> >> you want to do at some point) the pattern I followed for other
> >> drivers is to declare a dummy function in arch/arm/mach-*
> >> with __weak and let the staging driver override that. This way
> >> the staging driver can go away without any compilation trouble
> >> happening.
> >
> > I don't really see why my changes will break the compilation if the
> > driver is no longer present in staging. At worst, the structure will be
> > filled but used by no one, right ?
> 
> You're right.
> 
> But still, we cannot add that header file for a driver that
> is in the staging tree. Header files go into the staging dir too.
agreed

Best Regards,
J.

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-03 18:05             ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 0 replies; 131+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-03 18:05 UTC (permalink / raw)
  To: linux-arm-kernel

On 17:38 Thu 03 Nov     , Linus Walleij wrote:
> On Thu, Nov 3, 2011 at 5:27 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > [Me]
> >> We're not supposed to have platform data dependent to stuff in
> >> staging under arch/arm or anyplace else in the main kernel tree.
> >>
> >> Please move this to
> >> drivers/staging/iio/adc/at91adc-board.h
> >> or so.
> >
> > Won't moving this part to staging prevent from using this structure in
> > board files ? If so, how will I be able to declare a new board that is
> > using this ADC (or add the support for the ADC to a new one) ?
> 
> Put this into a separate board file living under staging/iio/adc
> 
> Compare:
> drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c
> 
> >> As for calling the at91_add_device_adc() function (which I guess
> >> you want to do at some point) the pattern I followed for other
> >> drivers is to declare a dummy function in arch/arm/mach-*
> >> with __weak and let the staging driver override that. This way
> >> the staging driver can go away without any compilation trouble
> >> happening.
> >
> > I don't really see why my changes will break the compilation if the
> > driver is no longer present in staging. At worst, the structure will be
> > filled but used by no one, right ?
> 
> You're right.
> 
> But still, we cannot add that header file for a driver that
> is in the staging tree. Header files go into the staging dir too.
agreed

Best Regards,
J.

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-03 10:11     ` Maxime Ripard
@ 2011-11-04 10:27       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Jean-Christophe Plagniol-Villard

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/staging/iio/adc/Kconfig   |    6 +
>  drivers/staging/iio/adc/Makefile  |    1 +
>  drivers/staging/iio/adc/at91adc.c |  310 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 317 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/at91adc.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index d9decea..c384894 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -169,6 +169,12 @@ config AD7280
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7280a
>  
> +config AT91ADC
> +	tristate "Atmel AT91 ADC"
> +	depends on SYSFS && ARCH_AT91
> +	help
> +	  Say yes here to build support for Atmel AT91 ADC.
> +
>  config MAX1363
>  	tristate "Maxim max1363 ADC driver"
>  	depends on I2C
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index ceee7f3..d250d06 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o
>  obj-$(CONFIG_ADT7310) += adt7310.o
>  obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
> +obj-$(CONFIG_AT91ADC) += at91adc.o
> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
> new file mode 100644
> index 0000000..acf656d
> --- /dev/null
> +++ b/drivers/staging/iio/adc/at91adc.c
> @@ -0,0 +1,310 @@
> +/*
> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
> + * evaluation boards.
I'd add something here to make it clear that it isn't being used for
touchscreen input in this driver.
> + *
> + * Copyright 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "../iio.h"
> +
> +#include <mach/at91_adc.h>
> +#include <mach/board.h>
> +
I'd marginally prefer to see these as inlines rather than macros.  Far
from critical though!
> +#define at91adc_reg_read(base, reg)        readl_relaxed((base) + (reg))
> +#define at91adc_reg_write(base, reg, val)  writel_relaxed((val), (base) + (reg))
> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
why hold a local pointer?  Just put these
directly into iio_dev->channels.
> +	struct iio_chan_spec *channels;
likewise, we already have iio_dev->num_channels.
> +	int nb_chan;

> +	int irq;
> +	wait_queue_head_t wq_data_avail;
cryptic element.  Nice to have everything documented, but
this one is most important as it isn't self explanatory.
> +	u16 lcdr;
> +	void __iomem *reg_base;
> +	unsigned int vref_mv;
> +};
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct at91adc_state *st = private;
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
extra unneeded brackets for the for loop.
> +	for (chan = 0; chan < st->nb_chan; chan++) {
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->lcdr = at91adc_reg_read(st->reg_base,
> +						    AT91_ADC_CHR(chan));
> +		}
> +	}
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct at91adc_state *st,
> +				struct at91_adc_data *pdata)
> +{
> +	int i, idx = 0;
> +	st->channels = kcalloc(pdata->num_channels_used,
> +			       sizeof(struct iio_chan_spec), GFP_KERNEL);
> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
Again, extra brackets.  Also, some wrong spacing for the if.
can you run checkpatch.pl over this again and make sure you
have fixed everything.
> +	for (i = 0; i < st->nb_chan; i++) {
as stated for the previous patch, this should be a bitmap rather
than array of u8's.   You can then use for_each_bit_set
to clean this code up nicely ;)
> +		if(pdata->channels_used[i]) {
> +			struct iio_chan_spec *chan = st->channels + idx;
> +			chan->type = IIO_VOLTAGE;
> +			chan->indexed = 1;
> +			chan->channel = i;
> +			chan->scan_type.sign = 's';
> +			chan->scan_type.realbits = 10;
> +			chan->scan_type.storagebits = 32;
That is pretty inefficient storage!  If we do implement buffering
on this, given mass reads don't look to be quicker, we'll just
munge it down to 16 bits.

> +			chan->scan_type.shift = 0;
don't bother initialising this value as it is zero (and that's an
obvious default).
> +			chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
> +			++idx;
> +		}
> +	}
> +
> +	return st->nb_chan;
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int scale_uv;
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
> +						 msecs_to_jiffies(10 * 1000));
> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
> +		*val = scale_uv / 1000;
> +		*val2 = (scale_uv % 1000) * 1000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(struct at91adc_state));
sizeof(*st) is a little neater?
> +	if (idev == NULL) {
I'm not sure this message adds anything or is even necessarily
correct as that function doesn't just allocate memory!
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
> +
> +	if (!pdata->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;
> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
> +
> +	/* Setup the ADC channels available on the board */
> +	st->nb_chan = pdata->num_channels;
> +	ret = at91adc_channel_init(st, pdata);
> +	if (ret < 0) {
no brackets please. Again, checkpatch would probably have shouted
about this.
> +		goto error_free_clk;
> +	}
> +
This is what I meant when I asked why you have two copies of the
pointer and count?
> +	idev->channels = st->channels;
> +	idev->num_channels = pdata->num_channels_used;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	st->vref_mv = pdata->vref;
> +
> +	ret = iio_device_register(idev);
again incorrect brackets for the code style.
> +	if (ret < 0) {
unwind the channel init?
> +		goto error_free_clk;
> +	}
> +
> +	return 0;
> +
> +error_free_clk:
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, st);
> +error_unmap_reg:
> +	iounmap(st->reg_base);
> +error_release_mem:
> +	release_mem_region(res->start, resource_size(res));
> +error_free_device:
> +	iio_free_device(idev);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	iio_device_unregister(idev);
Free channels array?
> +	free_irq(st->irq, st);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_free_device(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +		   },
> +};
> +
> +static int __init at91adc_init(void)
> +{
> +	return platform_driver_register(&at91adc_driver);
> +}
> +
> +static void __exit at91adc_exit(void)
> +{
> +	platform_driver_unregister(&at91adc_driver);
> +}
> +

Some neat boiler plate removal stuff was just merged for
platform drivers.  See module_platform_driver in include/linux/platform.h

Definitely want to use that!  I'm hoping the spi and i2c versions
turn up soon as it will save us a lot of silly cut and paste code.
> +module_init(at91adc_init);
> +module_exit(at91adc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-04 10:27       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/staging/iio/adc/Kconfig   |    6 +
>  drivers/staging/iio/adc/Makefile  |    1 +
>  drivers/staging/iio/adc/at91adc.c |  310 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 317 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/at91adc.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index d9decea..c384894 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -169,6 +169,12 @@ config AD7280
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7280a
>  
> +config AT91ADC
> +	tristate "Atmel AT91 ADC"
> +	depends on SYSFS && ARCH_AT91
> +	help
> +	  Say yes here to build support for Atmel AT91 ADC.
> +
>  config MAX1363
>  	tristate "Maxim max1363 ADC driver"
>  	depends on I2C
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index ceee7f3..d250d06 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o
>  obj-$(CONFIG_ADT7310) += adt7310.o
>  obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
> +obj-$(CONFIG_AT91ADC) += at91adc.o
> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
> new file mode 100644
> index 0000000..acf656d
> --- /dev/null
> +++ b/drivers/staging/iio/adc/at91adc.c
> @@ -0,0 +1,310 @@
> +/*
> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
> + * evaluation boards.
I'd add something here to make it clear that it isn't being used for
touchscreen input in this driver.
> + *
> + * Copyright 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "../iio.h"
> +
> +#include <mach/at91_adc.h>
> +#include <mach/board.h>
> +
I'd marginally prefer to see these as inlines rather than macros.  Far
from critical though!
> +#define at91adc_reg_read(base, reg)        readl_relaxed((base) + (reg))
> +#define at91adc_reg_write(base, reg, val)  writel_relaxed((val), (base) + (reg))
> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
why hold a local pointer?  Just put these
directly into iio_dev->channels.
> +	struct iio_chan_spec *channels;
likewise, we already have iio_dev->num_channels.
> +	int nb_chan;

> +	int irq;
> +	wait_queue_head_t wq_data_avail;
cryptic element.  Nice to have everything documented, but
this one is most important as it isn't self explanatory.
> +	u16 lcdr;
> +	void __iomem *reg_base;
> +	unsigned int vref_mv;
> +};
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct at91adc_state *st = private;
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
extra unneeded brackets for the for loop.
> +	for (chan = 0; chan < st->nb_chan; chan++) {
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->lcdr = at91adc_reg_read(st->reg_base,
> +						    AT91_ADC_CHR(chan));
> +		}
> +	}
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct at91adc_state *st,
> +				struct at91_adc_data *pdata)
> +{
> +	int i, idx = 0;
> +	st->channels = kcalloc(pdata->num_channels_used,
> +			       sizeof(struct iio_chan_spec), GFP_KERNEL);
> +	if (st->channels == NULL)
> +		return -ENOMEM;
> +
Again, extra brackets.  Also, some wrong spacing for the if.
can you run checkpatch.pl over this again and make sure you
have fixed everything.
> +	for (i = 0; i < st->nb_chan; i++) {
as stated for the previous patch, this should be a bitmap rather
than array of u8's.   You can then use for_each_bit_set
to clean this code up nicely ;)
> +		if(pdata->channels_used[i]) {
> +			struct iio_chan_spec *chan = st->channels + idx;
> +			chan->type = IIO_VOLTAGE;
> +			chan->indexed = 1;
> +			chan->channel = i;
> +			chan->scan_type.sign = 's';
> +			chan->scan_type.realbits = 10;
> +			chan->scan_type.storagebits = 32;
That is pretty inefficient storage!  If we do implement buffering
on this, given mass reads don't look to be quicker, we'll just
munge it down to 16 bits.

> +			chan->scan_type.shift = 0;
don't bother initialising this value as it is zero (and that's an
obvious default).
> +			chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
> +			++idx;
> +		}
> +	}
> +
> +	return st->nb_chan;
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int scale_uv;
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
> +						 msecs_to_jiffies(10 * 1000));
> +		*val = st->lcdr;
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->lcdr = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
> +		*val = scale_uv / 1000;
> +		*val2 = (scale_uv % 1000) * 1000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_allocate_device(sizeof(struct at91adc_state));
sizeof(*st) is a little neater?
> +	if (idev == NULL) {
I'm not sure this message adds anything or is even necessarily
correct as that function doesn't just allocate memory!
> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
> +
> +	if (!pdata->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;
> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
> +
> +	/* Setup the ADC channels available on the board */
> +	st->nb_chan = pdata->num_channels;
> +	ret = at91adc_channel_init(st, pdata);
> +	if (ret < 0) {
no brackets please. Again, checkpatch would probably have shouted
about this.
> +		goto error_free_clk;
> +	}
> +
This is what I meant when I asked why you have two copies of the
pointer and count?
> +	idev->channels = st->channels;
> +	idev->num_channels = pdata->num_channels_used;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	st->vref_mv = pdata->vref;
> +
> +	ret = iio_device_register(idev);
again incorrect brackets for the code style.
> +	if (ret < 0) {
unwind the channel init?
> +		goto error_free_clk;
> +	}
> +
> +	return 0;
> +
> +error_free_clk:
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, st);
> +error_unmap_reg:
> +	iounmap(st->reg_base);
> +error_release_mem:
> +	release_mem_region(res->start, resource_size(res));
> +error_free_device:
> +	iio_free_device(idev);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	iio_device_unregister(idev);
Free channels array?
> +	free_irq(st->irq, st);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_free_device(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +		   },
> +};
> +
> +static int __init at91adc_init(void)
> +{
> +	return platform_driver_register(&at91adc_driver);
> +}
> +
> +static void __exit at91adc_exit(void)
> +{
> +	platform_driver_unregister(&at91adc_driver);
> +}
> +

Some neat boiler plate removal stuff was just merged for
platform drivers.  See module_platform_driver in include/linux/platform.h

Definitely want to use that!  I'm hoping the spi and i2c versions
turn up soon as it will save us a lot of silly cut and paste code.
> +module_init(at91adc_init);
> +module_exit(at91adc_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 10:11     ` Maxime Ripard
@ 2011-11-04 10:27       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..b837da8 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +#define AT91_ADC_MAX_CHANNELS   16
> +
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board */
> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
> +	/* Number of channels in use */
> +	u8 num_channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-04 10:27       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..b837da8 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +#define AT91_ADC_MAX_CHANNELS   16
> +
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board */
> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
> +	/* Number of channels in use */
> +	u8 num_channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif

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

* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-03 10:11     ` Maxime Ripard
@ 2011-11-04 10:33       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:33 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

Forgot to put some general comments in the previous patch reviews.

Please run checkpatch.pl over all patches as it will clean up quite a few
issues.

Mostly looking good.  At the moment it's simple enough that we can
rapidly move this one out of staging once the core has gone.

Then we can build up more interesting stuff as support goes into
the non staging core.

Thanks,

Jonathan

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
>  2 files changed, 64 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 39f81f4..bfc50ac 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>  #endif
>  
> +/* --------------------------------------------------------------------
> + *  ADCs
> + * -------------------------------------------------------------------- */
> +
> +static struct at91_adc_data adc_data;
> +
> +static struct resource adc_resources[] = {
> +	[0] = {
> +		.start	= AT91SAM9260_BASE_ADC,
> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= AT91SAM9260_ID_ADC,
> +		.end	= AT91SAM9260_ID_ADC,
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91adc",
> +	.id		= -1,
> +	.dev		= {
> +				.platform_data		= &adc_data,
> +	},
> +	.resource	= adc_resources,
> +	.num_resources	= ARRAY_SIZE(adc_resources),
> +};
> +
> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	if(data->channels_used[0])
> +		at91_set_A_periph(AT91_PIN_PC0, 0);
> +	if(data->channels_used[1])
> +		at91_set_A_periph(AT91_PIN_PC1, 0);
> +	if(data->channels_used[2])
> +		at91_set_A_periph(AT91_PIN_PC2, 0);
> +	if(data->channels_used[3])
> +		at91_set_A_periph(AT91_PIN_PC3, 0);
formatting issues so another one for checkpatch.

Also define AT91_PIN_PC(n) then make this another
for_each_bit_set call.  That should get you down from
8 lines to about 3.
> +
> +	data->adc_clock = 5000000;
> +	data->num_channels = 4;
> +	data->startup_time = 10;
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +
> +
>  /* -------------------------------------------------------------------- */
>  /*
>   * These devices are always present and don't need any board-specific
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 817f59d..6f2542fb 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
>  static void __init ek_add_device_buttons(void) {}
>  #endif
>  
> +/*
> + * ADCs
> + */
> +
> +static struct at91_adc_data ek_adc_data = {
> +	.channels_used = { 1, 1, 1, 1} ,
> +	.num_channels_used = 4,
> +	.vref = 3300,
> +};
> +
>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
> @@ -389,6 +399,8 @@ static void __init ek_board_init(void)
>  	ek_add_device_gpio_leds();
>  	/* Push Buttons */
>  	ek_add_device_buttons();
> +	/* ADCs */
> +	at91_add_device_adc(&ek_adc_data);
>  	/* PCK0 provides MCLK to the WM8731 */
>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>  	/* SSC (for WM8731) */


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-11-04 10:33       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:33 UTC (permalink / raw)
  To: linux-arm-kernel

Forgot to put some general comments in the previous patch reviews.

Please run checkpatch.pl over all patches as it will clean up quite a few
issues.

Mostly looking good.  At the moment it's simple enough that we can
rapidly move this one out of staging once the core has gone.

Then we can build up more interesting stuff as support goes into
the non staging core.

Thanks,

Jonathan

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
>  2 files changed, 64 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 39f81f4..bfc50ac 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>  #endif
>  
> +/* --------------------------------------------------------------------
> + *  ADCs
> + * -------------------------------------------------------------------- */
> +
> +static struct at91_adc_data adc_data;
> +
> +static struct resource adc_resources[] = {
> +	[0] = {
> +		.start	= AT91SAM9260_BASE_ADC,
> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= AT91SAM9260_ID_ADC,
> +		.end	= AT91SAM9260_ID_ADC,
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91adc",
> +	.id		= -1,
> +	.dev		= {
> +				.platform_data		= &adc_data,
> +	},
> +	.resource	= adc_resources,
> +	.num_resources	= ARRAY_SIZE(adc_resources),
> +};
> +
> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	if(data->channels_used[0])
> +		at91_set_A_periph(AT91_PIN_PC0, 0);
> +	if(data->channels_used[1])
> +		at91_set_A_periph(AT91_PIN_PC1, 0);
> +	if(data->channels_used[2])
> +		at91_set_A_periph(AT91_PIN_PC2, 0);
> +	if(data->channels_used[3])
> +		at91_set_A_periph(AT91_PIN_PC3, 0);
formatting issues so another one for checkpatch.

Also define AT91_PIN_PC(n) then make this another
for_each_bit_set call.  That should get you down from
8 lines to about 3.
> +
> +	data->adc_clock = 5000000;
> +	data->num_channels = 4;
> +	data->startup_time = 10;
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +
> +
>  /* -------------------------------------------------------------------- */
>  /*
>   * These devices are always present and don't need any board-specific
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 817f59d..6f2542fb 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
>  static void __init ek_add_device_buttons(void) {}
>  #endif
>  
> +/*
> + * ADCs
> + */
> +
> +static struct at91_adc_data ek_adc_data = {
> +	.channels_used = { 1, 1, 1, 1} ,
> +	.num_channels_used = 4,
> +	.vref = 3300,
> +};
> +
>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
> @@ -389,6 +399,8 @@ static void __init ek_board_init(void)
>  	ek_add_device_gpio_leds();
>  	/* Push Buttons */
>  	ek_add_device_buttons();
> +	/* ADCs */
> +	at91_add_device_adc(&ek_adc_data);
>  	/* PCK0 provides MCLK to the WM8731 */
>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>  	/* SSC (for WM8731) */

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-03 10:11     ` Maxime Ripard
@ 2011-11-04 10:34       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:34 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..b837da8 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +#define AT91_ADC_MAX_CHANNELS   16
> +
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board */
put it in a unsigned long and use the bitmap functions.
> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
> +	/* Number of channels in use */
just use bitmap_weight with the long above and
length set to num_channels.
> +	u8 num_channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-04 10:34       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/03/2011 10:11 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>  1 files changed, 22 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..b837da8 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +#define AT91_ADC_MAX_CHANNELS   16
> +
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board */
put it in a unsigned long and use the bitmap functions.
> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
> +	/* Number of channels in use */
just use bitmap_weight with the long above and
length set to num_channels.
> +	u8 num_channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-04 10:27       ` Jonathan Cameron
@ 2011-11-04 10:36         ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:36 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez

On 11/04/2011 10:27 AM, Jonathan Cameron wrote:
> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>>  1 files changed, 22 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
>> index ed544a0..b837da8 100644
>> --- a/arch/arm/mach-at91/include/mach/board.h
>> +++ b/arch/arm/mach-at91/include/mach/board.h
>> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>>  /* FIXME: this needs a better location, but gets stuff building again */
>>  extern int at91_suspend_entering_slow_clock(void);
>>  
>> +/* ADC */
>> +#define AT91_ADC_MAX_CHANNELS   16
>> +
>> +struct at91_adc_data {
>> +	/* ADC Clock as specified by the datasheet, in Hz. */
>> +	unsigned int adc_clock;
>> +	/*
>> +	 * Global number of channels available (to specify which channels are
>> +	 * indeed used on the board, see the channels_used array).
>> +	 */
>> +	u8 num_channels;
>> +	/* Channels in use on the board */
>> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
>> +	/* Number of channels in use */
>> +	u8 num_channels_used;
>> +	/* Startup time of the ADC, in microseconds. */
>> +	u8 startup_time;
>> +	/* Reference voltage for the ADC in millivolts */
>> +	unsigned short vref;
>> +};
>> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
>> +
>>  #endif
oops. version with actual comments should also have gone out.

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-04 10:36         ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 10:36 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/04/2011 10:27 AM, Jonathan Cameron wrote:
> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  arch/arm/mach-at91/include/mach/board.h |   22 ++++++++++++++++++++++
>>  1 files changed, 22 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
>> index ed544a0..b837da8 100644
>> --- a/arch/arm/mach-at91/include/mach/board.h
>> +++ b/arch/arm/mach-at91/include/mach/board.h
>> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>>  /* FIXME: this needs a better location, but gets stuff building again */
>>  extern int at91_suspend_entering_slow_clock(void);
>>  
>> +/* ADC */
>> +#define AT91_ADC_MAX_CHANNELS   16
>> +
>> +struct at91_adc_data {
>> +	/* ADC Clock as specified by the datasheet, in Hz. */
>> +	unsigned int adc_clock;
>> +	/*
>> +	 * Global number of channels available (to specify which channels are
>> +	 * indeed used on the board, see the channels_used array).
>> +	 */
>> +	u8 num_channels;
>> +	/* Channels in use on the board */
>> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
>> +	/* Number of channels in use */
>> +	u8 num_channels_used;
>> +	/* Startup time of the ADC, in microseconds. */
>> +	u8 startup_time;
>> +	/* Reference voltage for the ADC in millivolts */
>> +	unsigned short vref;
>> +};
>> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
>> +
>>  #endif
oops. version with actual comments should also have gone out.

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-04 10:33       ` Jonathan Cameron
  (?)
@ 2011-11-04 11:25       ` Maxime Ripard
  2011-11-04 15:52         ` Linus Walleij
  2011-11-04 16:32           ` Jonathan Cameron
  -1 siblings, 2 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-04 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jonathan,

Thanks for your review, I'm working on it and will submit a new version
asap.

On 04/11/2011 11:33, Jonathan Cameron wrote:
> Please run checkpatch.pl over all patches as it will clean up quite a few
> issues.

Yep, I forgot to run it, my bad.

> Mostly looking good.  At the moment it's simple enough that we can
> rapidly move this one out of staging once the core has gone.

Ok. Rebasing on your outofstaging branch for the next version would be a
good idea ?

Or do you want the driver to still go through the staging step ?

Linus, I guess that if we put the driver directly into the main tree you
are ok with the changes made to boards files (code reviews apart) ?

Thanks,
Maxime

> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> ---
>>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>>  arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
>>  2 files changed, 64 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
>> index 39f81f4..bfc50ac 100644
>> --- a/arch/arm/mach-at91/at91sam9260_devices.c
>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
>> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>>  #endif
>>  
>> +/* --------------------------------------------------------------------
>> + *  ADCs
>> + * -------------------------------------------------------------------- */
>> +
>> +static struct at91_adc_data adc_data;
>> +
>> +static struct resource adc_resources[] = {
>> +	[0] = {
>> +		.start	= AT91SAM9260_BASE_ADC,
>> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
>> +		.flags	= IORESOURCE_MEM,
>> +	},
>> +	[1] = {
>> +		.start	= AT91SAM9260_ID_ADC,
>> +		.end	= AT91SAM9260_ID_ADC,
>> +		.flags	= IORESOURCE_IRQ,
>> +	},
>> +};
>> +
>> +static struct platform_device at91_adc_device = {
>> +	.name		= "at91adc",
>> +	.id		= -1,
>> +	.dev		= {
>> +				.platform_data		= &adc_data,
>> +	},
>> +	.resource	= adc_resources,
>> +	.num_resources	= ARRAY_SIZE(adc_resources),
>> +};
>> +
>> +void __init at91_add_device_adc(struct at91_adc_data *data)
>> +{
>> +	if (!data)
>> +		return;
>> +
>> +	if(data->channels_used[0])
>> +		at91_set_A_periph(AT91_PIN_PC0, 0);
>> +	if(data->channels_used[1])
>> +		at91_set_A_periph(AT91_PIN_PC1, 0);
>> +	if(data->channels_used[2])
>> +		at91_set_A_periph(AT91_PIN_PC2, 0);
>> +	if(data->channels_used[3])
>> +		at91_set_A_periph(AT91_PIN_PC3, 0);
> formatting issues so another one for checkpatch.
> 
> Also define AT91_PIN_PC(n) then make this another
> for_each_bit_set call.  That should get you down from
> 8 lines to about 3.
>> +
>> +	data->adc_clock = 5000000;
>> +	data->num_channels = 4;
>> +	data->startup_time = 10;
>> +
>> +	adc_data = *data;
>> +	platform_device_register(&at91_adc_device);
>> +}
>> +
>> +
>>  /* -------------------------------------------------------------------- */
>>  /*
>>   * These devices are always present and don't need any board-specific
>> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
>> index 817f59d..6f2542fb 100644
>> --- a/arch/arm/mach-at91/board-sam9g20ek.c
>> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
>> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
>>  static void __init ek_add_device_buttons(void) {}
>>  #endif
>>  
>> +/*
>> + * ADCs
>> + */
>> +
>> +static struct at91_adc_data ek_adc_data = {
>> +	.channels_used = { 1, 1, 1, 1} ,
>> +	.num_channels_used = 4,
>> +	.vref = 3300,
>> +};
>> +
>>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
>> @@ -389,6 +399,8 @@ static void __init ek_board_init(void)
>>  	ek_add_device_gpio_leds();
>>  	/* Push Buttons */
>>  	ek_add_device_buttons();
>> +	/* ADCs */
>> +	at91_add_device_adc(&ek_adc_data);
>>  	/* PCK0 provides MCLK to the WM8731 */
>>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>>  	/* SSC (for WM8731) */
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-04 10:34       ` Jonathan Cameron
  (?)
@ 2011-11-04 15:22       ` Maxime Ripard
  2011-11-04 16:28           ` Jonathan Cameron
  -1 siblings, 1 reply; 131+ messages in thread
From: Maxime Ripard @ 2011-11-04 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jonathan,

On 04/11/2011 11:34, Jonathan Cameron wrote:
> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>> +/* ADC */
>> +#define AT91_ADC_MAX_CHANNELS   16
>> +
>> +struct at91_adc_data {
>> +	/* ADC Clock as specified by the datasheet, in Hz. */
>> +	unsigned int adc_clock;
>> +	/*
>> +	 * Global number of channels available (to specify which channels are
>> +	 * indeed used on the board, see the channels_used array).
>> +	 */
>> +	u8 num_channels;
>> +	/* Channels in use on the board */
> put it in a unsigned long and use the bitmap functions.
>> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
>> +	/* Number of channels in use */
> just use bitmap_weight with the long above and
> length set to num_channels.
>> +	u8 num_channels_used;

I didn't know bitmap functions before you mentioned it, but if I read it
correctly, while I agree with your point, I will lose the ability to
define a combination of enabled and disabled channel in a board specific
manner.

In the third patch, I defined channels_used as an array of one. But
let's say that instead I want all channels except the second one. With
what's in the driver for now, I initialise it to {1, 0, 1, 1}. I don't
think it's possible to do so with bitmap functions, or am I missing
something ?

Or put the calls to bitmap_set in the board_init function ?

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-04 11:25       ` Maxime Ripard
@ 2011-11-04 15:52         ` Linus Walleij
  2011-11-04 16:32           ` Jonathan Cameron
  1 sibling, 0 replies; 131+ messages in thread
From: Linus Walleij @ 2011-11-04 15:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Nov 4, 2011 at 12:25 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:

> Ok. Rebasing on your outofstaging branch for the next version would be a
> good idea ?
>
> Or do you want the driver to still go through the staging step ?
>
> Linus, I guess that if we put the driver directly into the main tree you
> are ok with the changes made to boards files (code reviews apart) ?

Yes that's fine.

Linus Walleij

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-04 15:22       ` Maxime Ripard
@ 2011-11-04 16:28           ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 16:28 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel

On 11/04/2011 03:22 PM, Maxime Ripard wrote:
> Hi Jonathan,
> 
> On 04/11/2011 11:34, Jonathan Cameron wrote:
>> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>>> +/* ADC */
>>> +#define AT91_ADC_MAX_CHANNELS   16
>>> +
>>> +struct at91_adc_data {
>>> +	/* ADC Clock as specified by the datasheet, in Hz. */
>>> +	unsigned int adc_clock;
>>> +	/*
>>> +	 * Global number of channels available (to specify which channels are
>>> +	 * indeed used on the board, see the channels_used array).
>>> +	 */
>>> +	u8 num_channels;
>>> +	/* Channels in use on the board */
>> put it in a unsigned long and use the bitmap functions.
>>> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
>>> +	/* Number of channels in use */
>> just use bitmap_weight with the long above and
>> length set to num_channels.
>>> +	u8 num_channels_used;
> 
> I didn't know bitmap functions before you mentioned it, but if I read it
> correctly, while I agree with your point, I will lose the ability to
> define a combination of enabled and disabled channel in a board specific
> manner.
> 
> In the third patch, I defined channels_used as an array of one. But
> let's say that instead I want all channels except the second one. With
> what's in the driver for now, I initialise it to {1, 0, 1, 1}. I don't
> think it's possible to do so with bitmap functions, or am I missing
> something ?

unsigned long bitmask = BIT(0) | BIT(1) | BIT(3);

Bitmaps are just arrays of unsigned longs.  If you 'know' you have
less than 32 elements, you can just use a single unsigned long and
this assignment gets easier.

Most of the stuff you want comes from bitops.h anyway (maybe all come
to think of it?)
> 
> Or put the calls to bitmap_set in the board_init function ?
Just edit the underlying unsigned long directly.  The fact that the
bitops.h stuff is supposed to work on a bitmap as well means that
will always work.

Jonathan


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-04 16:28           ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 16:28 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/04/2011 03:22 PM, Maxime Ripard wrote:
> Hi Jonathan,
> 
> On 04/11/2011 11:34, Jonathan Cameron wrote:
>> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>>> +/* ADC */
>>> +#define AT91_ADC_MAX_CHANNELS   16
>>> +
>>> +struct at91_adc_data {
>>> +	/* ADC Clock as specified by the datasheet, in Hz. */
>>> +	unsigned int adc_clock;
>>> +	/*
>>> +	 * Global number of channels available (to specify which channels are
>>> +	 * indeed used on the board, see the channels_used array).
>>> +	 */
>>> +	u8 num_channels;
>>> +	/* Channels in use on the board */
>> put it in a unsigned long and use the bitmap functions.
>>> +	u8 channels_used[AT91_ADC_MAX_CHANNELS];
>>> +	/* Number of channels in use */
>> just use bitmap_weight with the long above and
>> length set to num_channels.
>>> +	u8 num_channels_used;
> 
> I didn't know bitmap functions before you mentioned it, but if I read it
> correctly, while I agree with your point, I will lose the ability to
> define a combination of enabled and disabled channel in a board specific
> manner.
> 
> In the third patch, I defined channels_used as an array of one. But
> let's say that instead I want all channels except the second one. With
> what's in the driver for now, I initialise it to {1, 0, 1, 1}. I don't
> think it's possible to do so with bitmap functions, or am I missing
> something ?

unsigned long bitmask = BIT(0) | BIT(1) | BIT(3);

Bitmaps are just arrays of unsigned longs.  If you 'know' you have
less than 32 elements, you can just use a single unsigned long and
this assignment gets easier.

Most of the stuff you want comes from bitops.h anyway (maybe all come
to think of it?)
> 
> Or put the calls to bitmap_set in the board_init function ?
Just edit the underlying unsigned long directly.  The fact that the
bitops.h stuff is supposed to work on a bitmap as well means that
will always work.

Jonathan

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-04 10:27       ` Jonathan Cameron
  (?)
@ 2011-11-04 16:29       ` Maxime Ripard
  2011-11-04 16:40         ` Jonathan Cameron
  -1 siblings, 1 reply; 131+ messages in thread
From: Maxime Ripard @ 2011-11-04 16:29 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/11/2011 11:27, Jonathan Cameron wrote:
> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..acf656d
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/at91adc.c
>> @@ -0,0 +1,310 @@
>> +/*
>> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91
>> + * evaluation boards.
> I'd add something here to make it clear that it isn't being used for
> touchscreen input in this driver.

Indeed, I forgot to change it here as well.

>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include "../iio.h"
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
> I'd marginally prefer to see these as inlines rather than macros.  Far
> from critical though!

Ok.

>> +#define at91adc_reg_read(base, reg)        readl_relaxed((base) + (reg))
>> +#define at91adc_reg_write(base, reg, val)  writel_relaxed((val), (base) + (reg))
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
> why hold a local pointer?  Just put these
> directly into iio_dev->channels.
>> +	struct iio_chan_spec *channels;
> likewise, we already have iio_dev->num_channels.
>> +	int nb_chan;

Indeed

>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
> cryptic element.  Nice to have everything documented, but
> this one is most important as it isn't self explanatory.
>> +	u16 lcdr;
>> +	void __iomem *reg_base;
>> +	unsigned int vref_mv;
>> +};
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct at91adc_state *st = private;
>> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
> extra unneeded brackets for the for loop.

I'm confused here. While it makes sense, I find it more readable around
large blocks of code and checkpatch doesn't complain on that one. But
coding style is a huge troll, so will change.

>> +	for (chan = 0; chan < st->nb_chan; chan++) {
>> +		if (status & AT91_ADC_EOC(chan)) {
>> +			st->done = true;
>> +			st->lcdr = at91adc_reg_read(st->reg_base,
>> +						    AT91_ADC_CHR(chan));
>> +		}
>> +	}
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct at91adc_state *st,
>> +				struct at91_adc_data *pdata)
>> +{
>> +	int i, idx = 0;
>> +	st->channels = kcalloc(pdata->num_channels_used,
>> +			       sizeof(struct iio_chan_spec), GFP_KERNEL);
>> +	if (st->channels == NULL)
>> +		return -ENOMEM;
>> +
> Again, extra brackets.  Also, some wrong spacing for the if.
> can you run checkpatch.pl over this again and make sure you
> have fixed everything.
>> +	for (i = 0; i < st->nb_chan; i++) {
> as stated for the previous patch, this should be a bitmap rather
> than array of u8's.   You can then use for_each_bit_set
> to clean this code up nicely ;)
>> +		if(pdata->channels_used[i]) {
>> +			struct iio_chan_spec *chan = st->channels + idx;
>> +			chan->type = IIO_VOLTAGE;
>> +			chan->indexed = 1;
>> +			chan->channel = i;
>> +			chan->scan_type.sign = 's';
>> +			chan->scan_type.realbits = 10;
>> +			chan->scan_type.storagebits = 32;
> That is pretty inefficient storage!  If we do implement buffering
> on this, given mass reads don't look to be quicker, we'll just
> munge it down to 16 bits.

Well, I thought that storage bits was here to represent how the hardware
stored the values. Since these values are stored on 10 bits alone in a
32 bits register, I thought that these were the right values, but we can
definitely lower the storagebits to 16 here.

> 
>> +			chan->scan_type.shift = 0;
> don't bother initialising this value as it is zero (and that's an
> obvious default).
>> +			chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>> +			++idx;
>> +		}
>> +	}
>> +
>> +	return st->nb_chan;
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int scale_uv;
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
>> +						 msecs_to_jiffies(10 * 1000));
>> +		*val = st->lcdr;
>> +
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->lcdr = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>> +		*val = scale_uv / 1000;
>> +		*val2 = (scale_uv % 1000) * 1000;
>> +		return IIO_VAL_INT_PLUS_MICRO;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_allocate_device(sizeof(struct at91adc_state));
> sizeof(*st) is a little neater?

Well, I find sizeof(struct at91adc_state) clearer, but ok.

>> +	if (idev == NULL) {
> I'm not sure this message adds anything or is even necessarily
> correct as that function doesn't just allocate memory!

Right

>> +		dev_err(&pdev->dev, "Failed to allocate memory.\n");
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->modes = INDIO_DIRECT_MODE;
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, st);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	st->nb_chan = pdata->num_channels;
>> +	ret = at91adc_channel_init(st, pdata);
>> +	if (ret < 0) {
> no brackets please. Again, checkpatch would probably have shouted
> about this.
>> +		goto error_free_clk;
>> +	}
>> +
> This is what I meant when I asked why you have two copies of the
> pointer and count?
>> +	idev->channels = st->channels;
>> +	idev->num_channels = pdata->num_channels_used;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	st->vref_mv = pdata->vref;
>> +
>> +	ret = iio_device_register(idev);
> again incorrect brackets for the code style.
>> +	if (ret < 0) {
> unwind the channel init?
Oooh, right...

>> +		goto error_free_clk;
>> +	}
>> +
>> +	return 0;
>> +
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_free_device(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	iio_device_unregister(idev);
> Free channels array?
>> +	free_irq(st->irq, st);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_free_device(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +		   },
>> +};
>> +
>> +static int __init at91adc_init(void)
>> +{
>> +	return platform_driver_register(&at91adc_driver);
>> +}
>> +
>> +static void __exit at91adc_exit(void)
>> +{
>> +	platform_driver_unregister(&at91adc_driver);
>> +}
>> +
> 
> Some neat boiler plate removal stuff was just merged for
> platform drivers.  See module_platform_driver in include/linux/platform.h
> 
> Definitely want to use that!  I'm hoping the spi and i2c versions
> turn up soon as it will save us a lot of silly cut and paste code.
Aah, nice :)

Thanks,

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-04 11:25       ` Maxime Ripard
@ 2011-11-04 16:32           ` Jonathan Cameron
  2011-11-04 16:32           ` Jonathan Cameron
  1 sibling, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 16:32 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Linus Walleij, linux-iio, Patrice Vilchez, Nicolas Ferre,
	linux-arm-kernel

On 11/04/2011 11:25 AM, Maxime Ripard wrote:
> Hi Jonathan,
> 
> Thanks for your review, I'm working on it and will submit a new version
> asap.
> 
> On 04/11/2011 11:33, Jonathan Cameron wrote:
>> Please run checkpatch.pl over all patches as it will clean up quite a few
>> issues.
> 
> Yep, I forgot to run it, my bad.
> 
>> Mostly looking good.  At the moment it's simple enough that we can
>> rapidly move this one out of staging once the core has gone.
> 
> Ok. Rebasing on your outofstaging branch for the next version would be a
> good idea ?
> 
> Or do you want the driver to still go through the staging step ?
I don't really mind.  I doubt Greg would send it on in this merge window
anyway.  Going from one to the other is trivial change of about 4
function names anyway for a simple driver like this.  It probably mostly
depends on whether you want to mess around with the more complex bits
that are going to stay in staging for sometime yet (buffering, in kernel
push interface, events).

> 
> Linus, I guess that if we put the driver directly into the main tree you
> are ok with the changes made to boards files (code reviews apart) ?
> 
> Thanks,
> Maxime
> 
>> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>>> ---
>>>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>>>  arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
>>>  2 files changed, 64 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
>>> index 39f81f4..bfc50ac 100644
>>> --- a/arch/arm/mach-at91/at91sam9260_devices.c
>>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
>>> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>>>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>>>  #endif
>>>  
>>> +/* --------------------------------------------------------------------
>>> + *  ADCs
>>> + * -------------------------------------------------------------------- */
>>> +
>>> +static struct at91_adc_data adc_data;
>>> +
>>> +static struct resource adc_resources[] = {
>>> +	[0] = {
>>> +		.start	= AT91SAM9260_BASE_ADC,
>>> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
>>> +		.flags	= IORESOURCE_MEM,
>>> +	},
>>> +	[1] = {
>>> +		.start	= AT91SAM9260_ID_ADC,
>>> +		.end	= AT91SAM9260_ID_ADC,
>>> +		.flags	= IORESOURCE_IRQ,
>>> +	},
>>> +};
>>> +
>>> +static struct platform_device at91_adc_device = {
>>> +	.name		= "at91adc",
>>> +	.id		= -1,
>>> +	.dev		= {
>>> +				.platform_data		= &adc_data,
>>> +	},
>>> +	.resource	= adc_resources,
>>> +	.num_resources	= ARRAY_SIZE(adc_resources),
>>> +};
>>> +
>>> +void __init at91_add_device_adc(struct at91_adc_data *data)
>>> +{
>>> +	if (!data)
>>> +		return;
>>> +
>>> +	if(data->channels_used[0])
>>> +		at91_set_A_periph(AT91_PIN_PC0, 0);
>>> +	if(data->channels_used[1])
>>> +		at91_set_A_periph(AT91_PIN_PC1, 0);
>>> +	if(data->channels_used[2])
>>> +		at91_set_A_periph(AT91_PIN_PC2, 0);
>>> +	if(data->channels_used[3])
>>> +		at91_set_A_periph(AT91_PIN_PC3, 0);
>> formatting issues so another one for checkpatch.
>>
>> Also define AT91_PIN_PC(n) then make this another
>> for_each_bit_set call.  That should get you down from
>> 8 lines to about 3.
>>> +
>>> +	data->adc_clock = 5000000;
>>> +	data->num_channels = 4;
>>> +	data->startup_time = 10;
>>> +
>>> +	adc_data = *data;
>>> +	platform_device_register(&at91_adc_device);
>>> +}
>>> +
>>> +
>>>  /* -------------------------------------------------------------------- */
>>>  /*
>>>   * These devices are always present and don't need any board-specific
>>> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
>>> index 817f59d..6f2542fb 100644
>>> --- a/arch/arm/mach-at91/board-sam9g20ek.c
>>> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
>>> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
>>>  static void __init ek_add_device_buttons(void) {}
>>>  #endif
>>>  
>>> +/*
>>> + * ADCs
>>> + */
>>> +
>>> +static struct at91_adc_data ek_adc_data = {
>>> +	.channels_used = { 1, 1, 1, 1} ,
>>> +	.num_channels_used = 4,
>>> +	.vref = 3300,
>>> +};
>>> +
>>>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>>>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>>>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
>>> @@ -389,6 +399,8 @@ static void __init ek_board_init(void)
>>>  	ek_add_device_gpio_leds();
>>>  	/* Push Buttons */
>>>  	ek_add_device_buttons();
>>> +	/* ADCs */
>>> +	at91_add_device_adc(&ek_adc_data);
>>>  	/* PCK0 provides MCLK to the WM8731 */
>>>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>>>  	/* SSC (for WM8731) */
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
> 


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-11-04 16:32           ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/04/2011 11:25 AM, Maxime Ripard wrote:
> Hi Jonathan,
> 
> Thanks for your review, I'm working on it and will submit a new version
> asap.
> 
> On 04/11/2011 11:33, Jonathan Cameron wrote:
>> Please run checkpatch.pl over all patches as it will clean up quite a few
>> issues.
> 
> Yep, I forgot to run it, my bad.
> 
>> Mostly looking good.  At the moment it's simple enough that we can
>> rapidly move this one out of staging once the core has gone.
> 
> Ok. Rebasing on your outofstaging branch for the next version would be a
> good idea ?
> 
> Or do you want the driver to still go through the staging step ?
I don't really mind.  I doubt Greg would send it on in this merge window
anyway.  Going from one to the other is trivial change of about 4
function names anyway for a simple driver like this.  It probably mostly
depends on whether you want to mess around with the more complex bits
that are going to stay in staging for sometime yet (buffering, in kernel
push interface, events).

> 
> Linus, I guess that if we put the driver directly into the main tree you
> are ok with the changes made to boards files (code reviews apart) ?
> 
> Thanks,
> Maxime
> 
>> On 11/03/2011 10:11 AM, Maxime Ripard wrote:
>>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>>> ---
>>>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>>>  arch/arm/mach-at91/board-sam9g20ek.c     |   12 +++++++
>>>  2 files changed, 64 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
>>> index 39f81f4..bfc50ac 100644
>>> --- a/arch/arm/mach-at91/at91sam9260_devices.c
>>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
>>> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>>>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>>>  #endif
>>>  
>>> +/* --------------------------------------------------------------------
>>> + *  ADCs
>>> + * -------------------------------------------------------------------- */
>>> +
>>> +static struct at91_adc_data adc_data;
>>> +
>>> +static struct resource adc_resources[] = {
>>> +	[0] = {
>>> +		.start	= AT91SAM9260_BASE_ADC,
>>> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
>>> +		.flags	= IORESOURCE_MEM,
>>> +	},
>>> +	[1] = {
>>> +		.start	= AT91SAM9260_ID_ADC,
>>> +		.end	= AT91SAM9260_ID_ADC,
>>> +		.flags	= IORESOURCE_IRQ,
>>> +	},
>>> +};
>>> +
>>> +static struct platform_device at91_adc_device = {
>>> +	.name		= "at91adc",
>>> +	.id		= -1,
>>> +	.dev		= {
>>> +				.platform_data		= &adc_data,
>>> +	},
>>> +	.resource	= adc_resources,
>>> +	.num_resources	= ARRAY_SIZE(adc_resources),
>>> +};
>>> +
>>> +void __init at91_add_device_adc(struct at91_adc_data *data)
>>> +{
>>> +	if (!data)
>>> +		return;
>>> +
>>> +	if(data->channels_used[0])
>>> +		at91_set_A_periph(AT91_PIN_PC0, 0);
>>> +	if(data->channels_used[1])
>>> +		at91_set_A_periph(AT91_PIN_PC1, 0);
>>> +	if(data->channels_used[2])
>>> +		at91_set_A_periph(AT91_PIN_PC2, 0);
>>> +	if(data->channels_used[3])
>>> +		at91_set_A_periph(AT91_PIN_PC3, 0);
>> formatting issues so another one for checkpatch.
>>
>> Also define AT91_PIN_PC(n) then make this another
>> for_each_bit_set call.  That should get you down from
>> 8 lines to about 3.
>>> +
>>> +	data->adc_clock = 5000000;
>>> +	data->num_channels = 4;
>>> +	data->startup_time = 10;
>>> +
>>> +	adc_data = *data;
>>> +	platform_device_register(&at91_adc_device);
>>> +}
>>> +
>>> +
>>>  /* -------------------------------------------------------------------- */
>>>  /*
>>>   * These devices are always present and don't need any board-specific
>>> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
>>> index 817f59d..6f2542fb 100644
>>> --- a/arch/arm/mach-at91/board-sam9g20ek.c
>>> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
>>> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void)
>>>  static void __init ek_add_device_buttons(void) {}
>>>  #endif
>>>  
>>> +/*
>>> + * ADCs
>>> + */
>>> +
>>> +static struct at91_adc_data ek_adc_data = {
>>> +	.channels_used = { 1, 1, 1, 1} ,
>>> +	.num_channels_used = 4,
>>> +	.vref = 3300,
>>> +};
>>> +
>>>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>>>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>>>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
>>> @@ -389,6 +399,8 @@ static void __init ek_board_init(void)
>>>  	ek_add_device_gpio_leds();
>>>  	/* Push Buttons */
>>>  	ek_add_device_buttons();
>>> +	/* ADCs */
>>> +	at91_add_device_adc(&ek_adc_data);
>>>  	/* PCK0 provides MCLK to the WM8731 */
>>>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>>>  	/* SSC (for WM8731) */
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
> 

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-04 16:29       ` Maxime Ripard
@ 2011-11-04 16:40         ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-04 16:40 UTC (permalink / raw)
  To: linux-arm-kernel

...
>>> +		if(pdata->channels_used[i]) {
>>> +			struct iio_chan_spec *chan = st->channels + idx;
>>> +			chan->type = IIO_VOLTAGE;
>>> +			chan->indexed = 1;
>>> +			chan->channel = i;
>>> +			chan->scan_type.sign = 's';
>>> +			chan->scan_type.realbits = 10;
>>> +			chan->scan_type.storagebits = 32;
>> That is pretty inefficient storage!  If we do implement buffering
>> on this, given mass reads don't look to be quicker, we'll just
>> munge it down to 16 bits.
> 
> Well, I thought that storage bits was here to represent how the hardware
> stored the values. Since these values are stored on 10 bits alone in a
> 32 bits register, I thought that these were the right values, but we can
> definitely lower the storagebits to 16 here.
> 
It's actually the storage in a buffer if we use one. scan_type doesn't
actually need defining at all if we have no buffered support (it's not
used by the core).  Clearly if buffering is used, 32 bits 'might' give
us slightly fewer copies, but at the cost of double the memory usage.
It would take some pretty impressive benchmark figures to convince that
it was worth the space.
>>> +		goto error_ret;
>>> +	}
>>> +
>>> +	idev = iio_allocate_device(sizeof(struct at91adc_state));
>> sizeof(*st) is a little neater?
> 
> Well, I find sizeof(struct at91adc_state) clearer, but ok.
I don't care so stick with it if you prefer!

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

* [PATCHv3] AT91: Add a driver for the ADC
  2011-10-19 16:18 ` Maxime Ripard
@ 2011-11-07 16:08   ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The
core" patchset from Jonathan Cameron, applied on top of 3.1

Improvements since v2:
  - Move the driver out of staging
  - Slightly modify the definition of channels to use bitmaps
  - The driver no longer leaks the channels array
  - Various minor fixes and codestyle improvements

Improvements since v1:
  - Rebase on top of commit 85d8ff8
  - Initialise scan_types
  - Added scale informations in the driver
  - Allow to use only a subset of adc channels available on the SoC
  - Various fix according to reviews

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>


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

* [PATCHv3] AT91: Add a driver for the ADC
@ 2011-11-07 16:08   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The
core" patchset from Jonathan Cameron, applied on top of 3.1

Improvements since v2:
  - Move the driver out of staging
  - Slightly modify the definition of channels to use bitmaps
  - The driver no longer leaks the channels array
  - Various minor fixes and codestyle improvements

Improvements since v1:
  - Rebase on top of commit 85d8ff8
  - Initialise scan_types
  - Added scale informations in the driver
  - Allow to use only a subset of adc channels available on the SoC
  - Various fix according to reviews

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-07 16:08   ` Maxime Ripard
@ 2011-11-07 16:08     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..afdcac0 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used array).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-07 16:08     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..afdcac0 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used array).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-07 16:08   ` Maxime Ripard
@ 2011-11-07 16:08     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard,
	Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/iio/adc/Kconfig   |    6 +
 drivers/iio/adc/Makefile  |    4 +-
 drivers/iio/adc/at91adc.c |  311 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 320 insertions(+), 1 deletions(-)
 create mode 100644 drivers/iio/adc/at91adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3d97b21..74f4d9f 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -14,6 +14,12 @@ config IIO_AD799X
 	  i2c analog to digital convertors (ADC). Provides direct access
 	  via sysfs.
 
+config IIO_AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config IIO_MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index c197334..776b56f 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
 obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
 
 iio_max1363-y := max1363_core.o
-obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
+
+obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
new file mode 100644
index 0000000..3ca2a64
--- /dev/null
+++ b/drivers/iio/adc/at91adc.c
@@ -0,0 +1,311 @@
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/iio/iio.h>
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 last_value;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static inline u32 at91adc_reg_read(void *base, u8 reg)
+{
+	return readl_relaxed(base + reg);
+}
+
+static inline void at91adc_reg_write(void *base, u8 reg, u32 val)
+{
+	writel_relaxed(val, base + reg);
+}
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct iio_dev *idev = private;
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < idev->num_channels; chan++)
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->last_value = at91adc_reg_read(st->reg_base,
+							  AT91_ADC_CHR(chan));
+		}
+
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct iio_dev *idev,
+				struct at91_adc_data *pdata)
+{
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&(pdata->channels_used),
+					   pdata->num_channels);
+	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
+			     GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;
+
+	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
+		struct iio_chan_spec *chan = chan_array + idx;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = bit;
+		chan->scan_type.sign = 's';
+		chan->scan_type.realbits = 10;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		++idx;
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91adc_channel_remove(struct iio_dev *idev)
+{
+	if (idev->channels == NULL)
+		return;
+
+	kfree(idev->channels);
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
+
+		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
+						 msecs_to_jiffies(10 * 1000));
+		*val = st->last_value;
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->last_value = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_device_allocate(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	ret = at91adc_channel_init(idev, pdata);
+	if (ret < 0)
+		goto error_free_clk;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0)
+		goto error_free_channels;
+
+	return 0;
+
+error_free_channels:
+	at91adc_channel_remove(idev);
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_device_free(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	at91adc_channel_remove(idev);
+	iio_device_unregister(idev);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_free(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+		   },
+};
+
+module_platform_driver(at91adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-07 16:08     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/iio/adc/Kconfig   |    6 +
 drivers/iio/adc/Makefile  |    4 +-
 drivers/iio/adc/at91adc.c |  311 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 320 insertions(+), 1 deletions(-)
 create mode 100644 drivers/iio/adc/at91adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3d97b21..74f4d9f 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -14,6 +14,12 @@ config IIO_AD799X
 	  i2c analog to digital convertors (ADC). Provides direct access
 	  via sysfs.
 
+config IIO_AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config IIO_MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index c197334..776b56f 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
 obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
 
 iio_max1363-y := max1363_core.o
-obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
+
+obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
new file mode 100644
index 0000000..3ca2a64
--- /dev/null
+++ b/drivers/iio/adc/at91adc.c
@@ -0,0 +1,311 @@
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/iio/iio.h>
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 last_value;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static inline u32 at91adc_reg_read(void *base, u8 reg)
+{
+	return readl_relaxed(base + reg);
+}
+
+static inline void at91adc_reg_write(void *base, u8 reg, u32 val)
+{
+	writel_relaxed(val, base + reg);
+}
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct iio_dev *idev = private;
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < idev->num_channels; chan++)
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->last_value = at91adc_reg_read(st->reg_base,
+							  AT91_ADC_CHR(chan));
+		}
+
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct iio_dev *idev,
+				struct at91_adc_data *pdata)
+{
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&(pdata->channels_used),
+					   pdata->num_channels);
+	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
+			     GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;
+
+	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
+		struct iio_chan_spec *chan = chan_array + idx;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = bit;
+		chan->scan_type.sign = 's';
+		chan->scan_type.realbits = 10;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		++idx;
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91adc_channel_remove(struct iio_dev *idev)
+{
+	if (idev->channels == NULL)
+		return;
+
+	kfree(idev->channels);
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
+
+		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
+						 msecs_to_jiffies(10 * 1000));
+		*val = st->last_value;
+
+		at91adc_reg_write(st->reg_base, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st->reg_base, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->last_value = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_device_allocate(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st->reg_base, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	ret = at91adc_channel_init(idev, pdata);
+	if (ret < 0)
+		goto error_free_clk;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0)
+		goto error_free_channels;
+
+	return 0;
+
+error_free_channels:
+	at91adc_channel_remove(idev);
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_device_free(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	at91adc_channel_remove(idev);
+	iio_device_unregister(idev);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_free(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+		   },
+};
+
+module_platform_driver(at91adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-07 16:08   ` Maxime Ripard
@ 2011-11-07 16:08     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..0859553 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if (test_bit(0, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if (test_bit(1, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if (test_bit(2, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if (test_bit(3, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..e34d41a 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +398,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-11-07 16:08     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..0859553 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if (test_bit(0, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if (test_bit(1, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if (test_bit(2, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if (test_bit(3, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..e34d41a 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +398,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-07 16:08     ` Maxime Ripard
@ 2011-11-07 16:27       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-07 16:27 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Thomas Petazzoni

Looks sensible to me though obviously this is bit is really for the
at91 lot to comment on.

On 11/07/2011 04:08 PM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
>  1 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..afdcac0 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board */
One nitpick here. You could make it more obvious that this is a mask...
> +	unsigned long channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-07 16:27       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-07 16:27 UTC (permalink / raw)
  To: linux-arm-kernel

Looks sensible to me though obviously this is bit is really for the
at91 lot to comment on.

On 11/07/2011 04:08 PM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
>  1 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..afdcac0 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board */
One nitpick here. You could make it more obvious that this is a mask...
> +	unsigned long channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-07 16:08     ` Maxime Ripard
@ 2011-11-08 13:19       ` Thomas Petazzoni
  -1 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-11-08 13:19 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre

Le Mon,  7 Nov 2011 17:08:31 +0100,
Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit :

> +/* ADC */
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels a=
re
> +	 * indeed used on the board, see the channels_used array).

channels_used is no longer an array, but a bitmap.

Regards,

Thomas
--=20
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-08 13:19       ` Thomas Petazzoni
  0 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-11-08 13:19 UTC (permalink / raw)
  To: linux-arm-kernel

Le Mon,  7 Nov 2011 17:08:31 +0100,
Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit :

> +/* ADC */
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used array).

channels_used is no longer an array, but a bitmap.

Regards,

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-07 16:08     ` Maxime Ripard
@ 2011-11-08 13:30       ` Thomas Petazzoni
  -1 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-11-08 13:30 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Patrice Vilchez,
	Jean-Christophe Plagniol-Villard, Nicolas Ferre

Le Mon,  7 Nov 2011 17:08:32 +0100,
Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit :

> +static inline u32 at91adc_reg_read(void *base, u8 reg)
> +{
> +	return readl_relaxed(base + reg);
> +}
> +
> +static inline void at91adc_reg_write(void *base, u8 reg, u32 val)
> +{
> +	writel_relaxed(val, base + reg);
> +}

Those could probably be written to take a at91adc_state structure as
argument, in order to simplify the call sites. I.e:

static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
{
	return readl_relaxed(st->reg_base + reg);
}

static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, =
u32 val)
{
	writel_relaxed(val, st->reg_base + reg);
}


> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct iio_dev *idev =3D private;
> +	struct at91adc_state *st =3D iio_priv(idev);
> +	unsigned int status =3D at91adc_reg_read(st->reg_base, AT91_ADC_SR)=
;

would become

+	unsigned int status =3D at91adc_reg_read(st, AT91_ADC_SR);

> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan =3D 0; chan < idev->num_channels; chan++)
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done =3D true;
> +			st->last_value =3D at91adc_reg_read(st->reg_base,
> +							  AT91_ADC_CHR(chan));

would become

+			st->last_value =3D at91adc_reg_read(st, AT91_ADC_CHR(chan));


> +static int at91adc_channel_init(struct iio_dev *idev,
> +				struct at91_adc_data *pdata)
> +{
> +	struct iio_chan_spec *chan_array;
> +	int bit, idx =3D 0;
> +
> +	idev->num_channels =3D bitmap_weight(&(pdata->channels_used),
> +					   pdata->num_channels);
> +	chan_array =3D kcalloc(idev->num_channels, sizeof(struct iio_chan_s=
pec),
> +			     GFP_KERNEL);
> +
> +	if (chan_array =3D=3D NULL)
> +		return -ENOMEM;
> +
> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels)=
 {
> +		struct iio_chan_spec *chan =3D chan_array + idx;
> +		chan->type =3D IIO_VOLTAGE;
> +		chan->indexed =3D 1;
> +		chan->channel =3D bit;
> +		chan->scan_type.sign =3D 's';

It's an unsigned value that you're reading from the ADC, so maybe this
should be =3D 'u'.

> +static void at91adc_channel_remove(struct iio_dev *idev)
> +{
> +	if (idev->channels =3D=3D NULL)
> +		return;
> +
> +	kfree(idev->channels);

kfree() is fine with having its argument being NULL, so the if test is
useless here. See http://lxr.free-electrons.com/source/mm/slab.c#L3863.

> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st =3D iio_priv(idev);
> +	unsigned int scale_uv;
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
> +						 msecs_to_jiffies(10 * 1000));

You forgot to handle the cases where wait_event_interruptible_timeout()
exits because of a signal or because of the timeout, and make the
assumption that the wait_event_interruptible_timeout() is also
successful.


> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret =3D request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk =3D clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret =3D PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk =3D clk_get_rate(st->clk);

You do a clk_get()/clk_enable() here but nowhere in the ->remove()
function you do a clk_disable()/clk_put().

> +	prsc =3D (mstrclk / (2 * pdata->adc_clock)) - 1;

[...]

> +	ticks =3D round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;

Both of those computations would be better with a small comment
explaining what's happening.

> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev =3D platform_get_drvdata(pdev);
> +	struct resource *res =3D platform_get_resource(pdev, IORESOURCE_MEM=
, 0);
> +	struct at91adc_state *st =3D iio_priv(idev);
> +
> +	at91adc_channel_remove(idev);
> +	iio_device_unregister(idev);
> +	free_irq(st->irq, idev);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_free(idev);

As said above, clk_disable()/clk_put() is missing.

> +static struct platform_driver at91adc_driver =3D {
> +	.probe =3D at91adc_probe,
> +	.remove =3D __devexit_p(at91adc_remove),
> +	.driver =3D {
> +		   .name =3D "at91adc",
> +		   },

Nitpick: wrongly placed parenthesis.

Regards,

Thomas
--=20
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-08 13:30       ` Thomas Petazzoni
  0 siblings, 0 replies; 131+ messages in thread
From: Thomas Petazzoni @ 2011-11-08 13:30 UTC (permalink / raw)
  To: linux-arm-kernel

Le Mon,  7 Nov 2011 17:08:32 +0100,
Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit :

> +static inline u32 at91adc_reg_read(void *base, u8 reg)
> +{
> +	return readl_relaxed(base + reg);
> +}
> +
> +static inline void at91adc_reg_write(void *base, u8 reg, u32 val)
> +{
> +	writel_relaxed(val, base + reg);
> +}

Those could probably be written to take a at91adc_state structure as
argument, in order to simplify the call sites. I.e:

static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
{
	return readl_relaxed(st->reg_base + reg);
}

static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
{
	writel_relaxed(val, st->reg_base + reg);
}


> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct iio_dev *idev = private;
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR);

would become

+	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);

> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < idev->num_channels; chan++)
> +		if (status & AT91_ADC_EOC(chan)) {
> +			st->done = true;
> +			st->last_value = at91adc_reg_read(st->reg_base,
> +							  AT91_ADC_CHR(chan));

would become

+			st->last_value = at91adc_reg_read(st, AT91_ADC_CHR(chan));


> +static int at91adc_channel_init(struct iio_dev *idev,
> +				struct at91_adc_data *pdata)
> +{
> +	struct iio_chan_spec *chan_array;
> +	int bit, idx = 0;
> +
> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
> +					   pdata->num_channels);
> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
> +			     GFP_KERNEL);
> +
> +	if (chan_array == NULL)
> +		return -ENOMEM;
> +
> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
> +		struct iio_chan_spec *chan = chan_array + idx;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = bit;
> +		chan->scan_type.sign = 's';

It's an unsigned value that you're reading from the ADC, so maybe this
should be = 'u'.

> +static void at91adc_channel_remove(struct iio_dev *idev)
> +{
> +	if (idev->channels == NULL)
> +		return;
> +
> +	kfree(idev->channels);

kfree() is fine with having its argument being NULL, so the if test is
useless here. See http://lxr.free-electrons.com/source/mm/slab.c#L3863.

> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int scale_uv;
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START);
> +
> +		wait_event_interruptible_timeout(st->wq_data_avail, st->done,
> +						 msecs_to_jiffies(10 * 1000));

You forgot to handle the cases where wait_event_interruptible_timeout()
exits because of a signal or because of the timeout, and make the
assumption that the wait_event_interruptible_timeout() is also
successful.


> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);

You do a clk_get()/clk_enable() here but nowhere in the ->remove()
function you do a clk_disable()/clk_put().

> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;

[...]

> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;

Both of those computations would be better with a small comment
explaining what's happening.

> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	at91adc_channel_remove(idev);
> +	iio_device_unregister(idev);
> +	free_irq(st->irq, idev);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_free(idev);

As said above, clk_disable()/clk_put() is missing.

> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +		   },

Nitpick: wrongly placed parenthesis.

Regards,

Thomas
-- 
Thomas Petazzoni, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCHv4] AT91: Add a driver for the ADC
  2011-10-19 16:18 ` Maxime Ripard
@ 2011-11-09 10:19   ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The
core" patchset from Jonathan Cameron, applied on top of 3.1

Improvements since v3:
  - Various fixes (Remove the clock at drivers's remove, change the registers
access functions prototypes,... )

Improvements since v2:
  - Move the driver out of staging
  - Slightly modify the definition of channels to use bitmaps
  - The driver no longer leaks the channels array
  - Various minor fixes and codestyle improvements

Improvements since v1:
  - Rebase on top of commit 85d8ff8
  - Initialise scan_types
  - Added scale informations in the driver
  - Allow to use only a subset of adc channels available on the SoC
  - Various fix according to reviews

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>


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

* [PATCHv4] AT91: Add a driver for the ADC
@ 2011-11-09 10:19   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on
the at91sam9g20ek and should work on sam9g45 as well.

For now, it only reads values when asked for by sysfs, but eventually will
support hardware triggers and more boards.

This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The
core" patchset from Jonathan Cameron, applied on top of 3.1

Improvements since v3:
  - Various fixes (Remove the clock at drivers's remove, change the registers
access functions prototypes,... )

Improvements since v2:
  - Move the driver out of staging
  - Slightly modify the definition of channels to use bitmaps
  - The driver no longer leaks the channels array
  - Various minor fixes and codestyle improvements

Improvements since v1:
  - Rebase on top of commit 85d8ff8
  - Initialise scan_types
  - Added scale informations in the driver
  - Allow to use only a subset of adc channels available on the SoC
  - Various fix according to reviews

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-09 10:19   ` Maxime Ripard
@ 2011-11-09 10:19     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-09 10:19     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-09 10:19   ` Maxime Ripard
@ 2011-11-09 10:19     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard,
	Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/iio/adc/Kconfig   |    6 +
 drivers/iio/adc/Makefile  |    4 +-
 drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 335 insertions(+), 1 deletions(-)
 create mode 100644 drivers/iio/adc/at91adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3d97b21..74f4d9f 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -14,6 +14,12 @@ config IIO_AD799X
 	  i2c analog to digital convertors (ADC). Provides direct access
 	  via sysfs.
 
+config IIO_AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config IIO_MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index c197334..776b56f 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
 obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
 
 iio_max1363-y := max1363_core.o
-obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
+
+obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
new file mode 100644
index 0000000..6aef5d6
--- /dev/null
+++ b/drivers/iio/adc/at91adc.c
@@ -0,0 +1,326 @@
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/iio/iio.h>
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 last_value;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
+{
+	return readl_relaxed(st->reg_base + reg);
+}
+
+static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
+{
+	writel_relaxed(val, st->reg_base + reg);
+}
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct iio_dev *idev = private;
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < idev->num_channels; chan++)
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->last_value = at91adc_reg_read(st,
+							  AT91_ADC_CHR(chan));
+		}
+
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct iio_dev *idev,
+				struct at91_adc_data *pdata)
+{
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&(pdata->channels_used),
+					   pdata->num_channels);
+	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
+			     GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;
+
+	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
+		struct iio_chan_spec *chan = chan_array + idx;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = bit;
+		chan->scan_type.sign = 'u';
+		chan->scan_type.realbits = 10;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		++idx;
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91adc_channel_remove(struct iio_dev *idev)
+{
+	kfree(idev->channels);
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+	short ret;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
+
+		ret = wait_event_interruptible_timeout(st->wq_data_avail,
+						       st->done,
+						       msecs_to_jiffies(1000));
+		if (ret == -ERESTARTSYS)
+			break;
+
+		*val = st->last_value;
+
+		at91adc_reg_write(st, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->last_value = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_device_allocate(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	/*
+	 * Prescaler rate computation using the formula from the Atmel's
+	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
+	 * specified by the electrical characteristics of the board.
+	 */
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	/*
+	 * Number of ticks needed to cover the startup time of the ADC as
+	 * defined in the electrical characteristics of the board, divided by 8.
+	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
+	 */
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	ret = at91adc_channel_init(idev, pdata);
+	if (ret < 0)
+		goto error_free_clk;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0)
+		goto error_free_channels;
+
+	return 0;
+
+error_free_channels:
+	at91adc_channel_remove(idev);
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_device_free(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	at91adc_channel_remove(idev);
+	clk_disable(st->clk);
+	clk_put(st->clk);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_free(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+	},
+};
+
+module_platform_driver(at91adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-09 10:19     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/iio/adc/Kconfig   |    6 +
 drivers/iio/adc/Makefile  |    4 +-
 drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 335 insertions(+), 1 deletions(-)
 create mode 100644 drivers/iio/adc/at91adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3d97b21..74f4d9f 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -14,6 +14,12 @@ config IIO_AD799X
 	  i2c analog to digital convertors (ADC). Provides direct access
 	  via sysfs.
 
+config IIO_AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config IIO_MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index c197334..776b56f 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
 obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
 
 iio_max1363-y := max1363_core.o
-obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
+
+obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
new file mode 100644
index 0000000..6aef5d6
--- /dev/null
+++ b/drivers/iio/adc/at91adc.c
@@ -0,0 +1,326 @@
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/iio/iio.h>
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 last_value;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
+{
+	return readl_relaxed(st->reg_base + reg);
+}
+
+static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
+{
+	writel_relaxed(val, st->reg_base + reg);
+}
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	int chan;
+	struct iio_dev *idev = private;
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	for (chan = 0; chan < idev->num_channels; chan++)
+		if (status & AT91_ADC_EOC(chan)) {
+			st->done = true;
+			st->last_value = at91adc_reg_read(st,
+							  AT91_ADC_CHR(chan));
+		}
+
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct iio_dev *idev,
+				struct at91_adc_data *pdata)
+{
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&(pdata->channels_used),
+					   pdata->num_channels);
+	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
+			     GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;
+
+	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
+		struct iio_chan_spec *chan = chan_array + idx;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = bit;
+		chan->scan_type.sign = 'u';
+		chan->scan_type.realbits = 10;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		++idx;
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91adc_channel_remove(struct iio_dev *idev)
+{
+	kfree(idev->channels);
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+	short ret;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
+
+		ret = wait_event_interruptible_timeout(st->wq_data_avail,
+						       st->done,
+						       msecs_to_jiffies(1000));
+		if (ret == -ERESTARTSYS)
+			break;
+
+		*val = st->last_value;
+
+		at91adc_reg_write(st, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->last_value = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_device_allocate(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	/*
+	 * Prescaler rate computation using the formula from the Atmel's
+	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
+	 * specified by the electrical characteristics of the board.
+	 */
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	/*
+	 * Number of ticks needed to cover the startup time of the ADC as
+	 * defined in the electrical characteristics of the board, divided by 8.
+	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
+	 */
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	ret = at91adc_channel_init(idev, pdata);
+	if (ret < 0)
+		goto error_free_clk;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0)
+		goto error_free_channels;
+
+	return 0;
+
+error_free_channels:
+	at91adc_channel_remove(idev);
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_device_free(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	at91adc_channel_remove(idev);
+	clk_disable(st->clk);
+	clk_put(st->clk);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_free(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),
+	.driver = {
+		   .name = "at91adc",
+	},
+};
+
+module_platform_driver(at91adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.4.1

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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-09 10:19   ` Maxime Ripard
@ 2011-11-09 10:19     ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..0859553 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if (test_bit(0, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if (test_bit(1, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if (test_bit(2, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if (test_bit(3, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..e34d41a 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +398,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-11-09 10:19     ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 39f81f4..0859553 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if (test_bit(0, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if (test_bit(1, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if (test_bit(2, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if (test_bit(3, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 817f59d..e34d41a 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +398,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
-- 
1.7.4.1

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-09 10:19     ` Maxime Ripard
@ 2011-11-10 17:35       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-10 17:35 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Jean-Christophe Plagniol-Villard, Thomas Petazzoni

On 11/09/2011 10:19 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
One little comment / query inline.  I'm happy with this either way
though.  Beware as you are dependent on a series under review though
and it's always possible little things in there might change!

> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>

> ---
>  drivers/iio/adc/Kconfig   |    6 +
>  drivers/iio/adc/Makefile  |    4 +-
>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 335 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/iio/adc/at91adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3d97b21..74f4d9f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -14,6 +14,12 @@ config IIO_AD799X
>  	  i2c analog to digital convertors (ADC). Provides direct access
>  	  via sysfs.
>  
> +config IIO_AT91ADC
> +	tristate "Atmel AT91 ADC"
> +	depends on SYSFS && ARCH_AT91
> +	help
> +	  Say yes here to build support for Atmel AT91 ADC.
> +
>  config IIO_MAX1363
>  	tristate "Maxim max1363 ADC driver"
>  	depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index c197334..776b56f 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>  
>  iio_max1363-y := max1363_core.o
> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
> \ No newline at end of file
> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
> +
> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
> new file mode 100644
> index 0000000..6aef5d6
> --- /dev/null
> +++ b/drivers/iio/adc/at91adc.c
> @@ -0,0 +1,326 @@
> +/*
> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
> + *
> + * Copyright 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + */
> +
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <mach/at91_adc.h>
> +#include <mach/board.h>
> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
> +	int irq;
> +	wait_queue_head_t wq_data_avail;
> +	u16 last_value;
> +	void __iomem *reg_base;
> +	unsigned int vref_mv;
> +};
> +
> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
> +{
> +	return readl_relaxed(st->reg_base + reg);
> +}
> +
> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
> +{
> +	writel_relaxed(val, st->reg_base + reg);
> +}
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct iio_dev *idev = private;
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < idev->num_channels; chan++)
Given we know only one bit should be set, it really feels like there
should be
a cleaner way of doing this. Could it use find_first_bit(&status,
idev->num_channels)?
It kind of bypasses the defines though so it's far from clean in that sense.
> +		if (status & AT91_ADC_EOC(chan)) {

The logic in here very much assumes that only one channel is being converted
and the interrupt indicates that one of the channels has finished.
Hence logically this st->done doesn't need to be in the loop.

> +			st->done = true;
> +			st->last_value = at91adc_reg_read(st,
> +							  AT91_ADC_CHR(chan));
> +		}
> +
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct iio_dev *idev,
> +				struct at91_adc_data *pdata)
> +{
> +	struct iio_chan_spec *chan_array;
> +	int bit, idx = 0;
> +
> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
> +					   pdata->num_channels);
> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
> +			     GFP_KERNEL);
> +
> +	if (chan_array == NULL)
> +		return -ENOMEM;
> +
> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
> +		struct iio_chan_spec *chan = chan_array + idx;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = bit;
> +		chan->scan_type.sign = 'u';
> +		chan->scan_type.realbits = 10;
> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
> +		++idx;
> +	}
> +
> +	idev->channels = chan_array;
> +	return idev->num_channels;
> +}
> +
> +static void at91adc_channel_remove(struct iio_dev *idev)
> +{
> +	kfree(idev->channels);
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int scale_uv;
> +	short ret;
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
> +
> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
> +						       st->done,
> +						       msecs_to_jiffies(1000));
> +		if (ret == -ERESTARTSYS)
> +			break;
> +
> +		*val = st->last_value;
> +
> +		at91adc_reg_write(st, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->last_value = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
> +		*val = scale_uv / 1000;
> +		*val2 = (scale_uv % 1000) * 1000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
> +	if (idev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	/*
> +	 * Prescaler rate computation using the formula from the Atmel's
> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
> +	 * specified by the electrical characteristics of the board.
> +	 */
> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
> +
> +	if (!pdata->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	/*
> +	 * Number of ticks needed to cover the startup time of the ADC as
> +	 * defined in the electrical characteristics of the board, divided by 8.
> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
> +	 */
> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;
> +	at91adc_reg_write(st, AT91_ADC_MR,
> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
> +
> +	/* Setup the ADC channels available on the board */
> +	ret = at91adc_channel_init(idev, pdata);
> +	if (ret < 0)
> +		goto error_free_clk;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	st->vref_mv = pdata->vref;
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0)
> +		goto error_free_channels;
> +
> +	return 0;
> +
> +error_free_channels:
> +	at91adc_channel_remove(idev);
> +error_free_clk:
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, st);
> +error_unmap_reg:
> +	iounmap(st->reg_base);
> +error_release_mem:
> +	release_mem_region(res->start, resource_size(res));
> +error_free_device:
> +	iio_device_free(idev);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	iio_device_unregister(idev);
> +	at91adc_channel_remove(idev);
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +	free_irq(st->irq, idev);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_free(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +	},
> +};
> +
> +module_platform_driver(at91adc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-10 17:35       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-10 17:35 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/09/2011 10:19 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
One little comment / query inline.  I'm happy with this either way
though.  Beware as you are dependent on a series under review though
and it's always possible little things in there might change!

> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>

> ---
>  drivers/iio/adc/Kconfig   |    6 +
>  drivers/iio/adc/Makefile  |    4 +-
>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 335 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/iio/adc/at91adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3d97b21..74f4d9f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -14,6 +14,12 @@ config IIO_AD799X
>  	  i2c analog to digital convertors (ADC). Provides direct access
>  	  via sysfs.
>  
> +config IIO_AT91ADC
> +	tristate "Atmel AT91 ADC"
> +	depends on SYSFS && ARCH_AT91
> +	help
> +	  Say yes here to build support for Atmel AT91 ADC.
> +
>  config IIO_MAX1363
>  	tristate "Maxim max1363 ADC driver"
>  	depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index c197334..776b56f 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>  
>  iio_max1363-y := max1363_core.o
> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
> \ No newline at end of file
> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
> +
> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
> new file mode 100644
> index 0000000..6aef5d6
> --- /dev/null
> +++ b/drivers/iio/adc/at91adc.c
> @@ -0,0 +1,326 @@
> +/*
> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
> + *
> + * Copyright 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + */
> +
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <mach/at91_adc.h>
> +#include <mach/board.h>
> +
> +struct at91adc_state {
> +	struct clk *clk;
> +	bool done;
> +	struct mutex lock;
> +	int irq;
> +	wait_queue_head_t wq_data_avail;
> +	u16 last_value;
> +	void __iomem *reg_base;
> +	unsigned int vref_mv;
> +};
> +
> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
> +{
> +	return readl_relaxed(st->reg_base + reg);
> +}
> +
> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
> +{
> +	writel_relaxed(val, st->reg_base + reg);
> +}
> +
> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
> +{
> +	int chan;
> +	struct iio_dev *idev = private;
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	for (chan = 0; chan < idev->num_channels; chan++)
Given we know only one bit should be set, it really feels like there
should be
a cleaner way of doing this. Could it use find_first_bit(&status,
idev->num_channels)?
It kind of bypasses the defines though so it's far from clean in that sense.
> +		if (status & AT91_ADC_EOC(chan)) {

The logic in here very much assumes that only one channel is being converted
and the interrupt indicates that one of the channels has finished.
Hence logically this st->done doesn't need to be in the loop.

> +			st->done = true;
> +			st->last_value = at91adc_reg_read(st,
> +							  AT91_ADC_CHR(chan));
> +		}
> +
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91adc_channel_init(struct iio_dev *idev,
> +				struct at91_adc_data *pdata)
> +{
> +	struct iio_chan_spec *chan_array;
> +	int bit, idx = 0;
> +
> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
> +					   pdata->num_channels);
> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
> +			     GFP_KERNEL);
> +
> +	if (chan_array == NULL)
> +		return -ENOMEM;
> +
> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
> +		struct iio_chan_spec *chan = chan_array + idx;
> +		chan->type = IIO_VOLTAGE;
> +		chan->indexed = 1;
> +		chan->channel = bit;
> +		chan->scan_type.sign = 'u';
> +		chan->scan_type.realbits = 10;
> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
> +		++idx;
> +	}
> +
> +	idev->channels = chan_array;
> +	return idev->num_channels;
> +}
> +
> +static void at91adc_channel_remove(struct iio_dev *idev)
> +{
> +	kfree(idev->channels);
> +}
> +
> +static int at91adc_read_raw(struct iio_dev *idev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct at91adc_state *st = iio_priv(idev);
> +	unsigned int scale_uv;
> +	short ret;
> +
> +	switch (mask) {
> +	case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91adc_reg_write(st, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
> +
> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
> +						       st->done,
> +						       msecs_to_jiffies(1000));
> +		if (ret == -ERESTARTSYS)
> +			break;
> +
> +		*val = st->last_value;
> +
> +		at91adc_reg_write(st, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91adc_reg_write(st, AT91_ADC_IDR,
> +				  AT91_ADC_EOC(chan->channel));
> +
> +		st->last_value = 0;
> +		st->done = false;
> +		mutex_unlock(&st->lock);
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
> +		*val = scale_uv / 1000;
> +		*val2 = (scale_uv % 1000) * 1000;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91adc_read_raw,
> +};
> +
> +static int __devinit at91adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No resource defined\n");
> +		ret = -ENXIO;
> +		goto error_ret;
> +	}
> +
> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
> +	if (idev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	platform_set_drvdata(pdev, idev);
> +
> +	idev->dev.parent = &pdev->dev;
> +	idev->name = dev_name(&pdev->dev);
> +	idev->info = &at91adc_info;
> +
> +	st = iio_priv(idev);
> +	st->irq = platform_get_irq(pdev, 0);
> +	if (st->irq < 0) {
> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
> +		ret = -ENODEV;
> +		goto error_free_device;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res),
> +				"AT91 adc registers")) {
> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
> +		ret = -EBUSY;
> +		goto error_free_device;
> +	}
> +
> +	st->reg_base = ioremap(res->start, resource_size(res));
> +	if (!st->reg_base) {
> +		dev_err(&pdev->dev, "Failed to map registers.\n");
> +		ret = -ENOMEM;
> +		goto error_release_mem;
> +	}
> +
> +	/*
> +	 * Disable all IRQs before setting up the handler
> +	 */
> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
> +		goto error_unmap_reg;
> +	}
> +
> +	st->clk = clk_get(&pdev->dev, "adc_clk");
> +	if (IS_ERR(st->clk)) {
> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
> +		ret = PTR_ERR(st->clk);
> +		goto error_free_irq;
> +	}
> +
> +	clk_enable(st->clk);
> +	mstrclk = clk_get_rate(st->clk);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	if (!pdata->adc_clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	/*
> +	 * Prescaler rate computation using the formula from the Atmel's
> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
> +	 * specified by the electrical characteristics of the board.
> +	 */
> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
> +
> +	if (!pdata->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_free_clk;
> +	}
> +
> +	/*
> +	 * Number of ticks needed to cover the startup time of the ADC as
> +	 * defined in the electrical characteristics of the board, divided by 8.
> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
> +	 */
> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
> +			  1000000) - 1, 8) / 8;
> +	at91adc_reg_write(st, AT91_ADC_MR,
> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
> +
> +	/* Setup the ADC channels available on the board */
> +	ret = at91adc_channel_init(idev, pdata);
> +	if (ret < 0)
> +		goto error_free_clk;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	st->vref_mv = pdata->vref;
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0)
> +		goto error_free_channels;
> +
> +	return 0;
> +
> +error_free_channels:
> +	at91adc_channel_remove(idev);
> +error_free_clk:
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, st);
> +error_unmap_reg:
> +	iounmap(st->reg_base);
> +error_release_mem:
> +	release_mem_region(res->start, resource_size(res));
> +error_free_device:
> +	iio_device_free(idev);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit at91adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91adc_state *st = iio_priv(idev);
> +
> +	iio_device_unregister(idev);
> +	at91adc_channel_remove(idev);
> +	clk_disable(st->clk);
> +	clk_put(st->clk);
> +	free_irq(st->irq, idev);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_device_free(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91adc_driver = {
> +	.probe = at91adc_probe,
> +	.remove = __devexit_p(at91adc_remove),
> +	.driver = {
> +		   .name = "at91adc",
> +	},
> +};
> +
> +module_platform_driver(at91adc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");

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

* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
  2011-11-09 10:19     ` Maxime Ripard
@ 2011-11-10 17:37       ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-10 17:37 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Thomas Petazzoni

On 11/09/2011 10:19 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
Fine as far as I am concerned but this one is definitely one for acks
from others as well.
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
>  2 files changed, 63 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 39f81f4..0859553 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>  #endif
>  
> +/* --------------------------------------------------------------------
> + *  ADCs
> + * -------------------------------------------------------------------- */
> +
> +static struct at91_adc_data adc_data;
> +
> +static struct resource adc_resources[] = {
> +	[0] = {
> +		.start	= AT91SAM9260_BASE_ADC,
> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= AT91SAM9260_ID_ADC,
> +		.end	= AT91SAM9260_ID_ADC,
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91adc",
> +	.id		= -1,
> +	.dev		= {
> +				.platform_data		= &adc_data,
> +	},
> +	.resource	= adc_resources,
> +	.num_resources	= ARRAY_SIZE(adc_resources),
> +};
> +
> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	if (test_bit(0, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC0, 0);
> +	if (test_bit(1, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC1, 0);
> +	if (test_bit(2, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC2, 0);
> +	if (test_bit(3, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC3, 0);
> +
> +	data->adc_clock = 5000000;
> +	data->num_channels = 4;
> +	data->startup_time = 10;
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +
> +
>  /* -------------------------------------------------------------------- */
>  /*
>   * These devices are always present and don't need any board-specific
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 817f59d..e34d41a 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
>  static void __init ek_add_device_buttons(void) {}
>  #endif
>  
> +/*
> + * ADCs
> + */
> +
> +static struct at91_adc_data ek_adc_data = {
> +	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
> +	.vref = 3300,
> +};
> +
>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
> @@ -389,6 +398,8 @@ static void __init ek_board_init(void)
>  	ek_add_device_gpio_leds();
>  	/* Push Buttons */
>  	ek_add_device_buttons();
> +	/* ADCs */
> +	at91_add_device_adc(&ek_adc_data);
>  	/* PCK0 provides MCLK to the WM8731 */
>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>  	/* SSC (for WM8731) */


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

* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2011-11-10 17:37       ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-10 17:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/09/2011 10:19 AM, Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
Fine as far as I am concerned but this one is definitely one for acks
from others as well.
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
>  2 files changed, 63 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 39f81f4..0859553 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>  #endif
>  
> +/* --------------------------------------------------------------------
> + *  ADCs
> + * -------------------------------------------------------------------- */
> +
> +static struct at91_adc_data adc_data;
> +
> +static struct resource adc_resources[] = {
> +	[0] = {
> +		.start	= AT91SAM9260_BASE_ADC,
> +		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= AT91SAM9260_ID_ADC,
> +		.end	= AT91SAM9260_ID_ADC,
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91adc",
> +	.id		= -1,
> +	.dev		= {
> +				.platform_data		= &adc_data,
> +	},
> +	.resource	= adc_resources,
> +	.num_resources	= ARRAY_SIZE(adc_resources),
> +};
> +
> +void __init at91_add_device_adc(struct at91_adc_data *data)
> +{
> +	if (!data)
> +		return;
> +
> +	if (test_bit(0, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC0, 0);
> +	if (test_bit(1, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC1, 0);
> +	if (test_bit(2, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC2, 0);
> +	if (test_bit(3, &(data->channels_used)))
> +		at91_set_A_periph(AT91_PIN_PC3, 0);
> +
> +	data->adc_clock = 5000000;
> +	data->num_channels = 4;
> +	data->startup_time = 10;
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +
> +
>  /* -------------------------------------------------------------------- */
>  /*
>   * These devices are always present and don't need any board-specific
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 817f59d..e34d41a 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
>  static void __init ek_add_device_buttons(void) {}
>  #endif
>  
> +/*
> + * ADCs
> + */
> +
> +static struct at91_adc_data ek_adc_data = {
> +	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
> +	.vref = 3300,
> +};
> +
>  #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
>  static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
>  	REGULATOR_SUPPLY("AVDD", "0-001b"),
> @@ -389,6 +398,8 @@ static void __init ek_board_init(void)
>  	ek_add_device_gpio_leds();
>  	/* Push Buttons */
>  	ek_add_device_buttons();
> +	/* ADCs */
> +	at91_add_device_adc(&ek_adc_data);
>  	/* PCK0 provides MCLK to the WM8731 */
>  	at91_set_B_periph(AT91_PIN_PC1, 0);
>  	/* SSC (for WM8731) */

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-10 17:35       ` Jonathan Cameron
@ 2011-11-11 12:34         ` Jonathan Cameron
  -1 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-11 12:34 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Jean-Christophe Plagniol-Villard, Thomas Petazzoni

On 11/10/2011 05:35 PM, Jonathan Cameron wrote:
> On 11/09/2011 10:19 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>
> One little comment / query inline.  I'm happy with this either way
> though.  Beware as you are dependent on a series under review though
> and it's always possible little things in there might change!
> 
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> 
>> ---
>>  drivers/iio/adc/Kconfig   |    6 +
>>  drivers/iio/adc/Makefile  |    4 +-
>>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 335 insertions(+), 1 deletions(-)
>>  create mode 100644 drivers/iio/adc/at91adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3d97b21..74f4d9f 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -14,6 +14,12 @@ config IIO_AD799X
>>  	  i2c analog to digital convertors (ADC). Provides direct access
>>  	  via sysfs.
>>  
>> +config IIO_AT91ADC
>> +	tristate "Atmel AT91 ADC"
>> +	depends on SYSFS && ARCH_AT91
>> +	help
>> +	  Say yes here to build support for Atmel AT91 ADC.
>> +
>>  config IIO_MAX1363
>>  	tristate "Maxim max1363 ADC driver"
>>  	depends on I2C
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index c197334..776b56f 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>>  
>>  iio_max1363-y := max1363_core.o
>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> \ No newline at end of file
>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> +
>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..6aef5d6
>> --- /dev/null
>> +++ b/drivers/iio/adc/at91adc.c
>> @@ -0,0 +1,326 @@
>> +/*
>> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 last_value;
>> +	void __iomem *reg_base;
>> +	unsigned int vref_mv;
>> +};
>> +
>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
>> +{
>> +	return readl_relaxed(st->reg_base + reg);
>> +}
>> +
>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
>> +{
>> +	writel_relaxed(val, st->reg_base + reg);
>> +}
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct iio_dev *idev = private;
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < idev->num_channels; chan++)
> Given we know only one bit should be set, it really feels like there
> should be
> a cleaner way of doing this. Could it use find_first_bit(&status,
> idev->num_channels)?
> It kind of bypasses the defines though so it's far from clean in that sense.
>> +		if (status & AT91_ADC_EOC(chan)) {
> 
> The logic in here very much assumes that only one channel is being converted
> and the interrupt indicates that one of the channels has finished.
> Hence logically this st->done doesn't need to be in the loop.
> 
Having had a look at the datasheet, could you just check against the
whole mask and use the lastdata converted register?  Should under
current conditions always give you the right thing I think?
>> +			st->done = true;
>> +			st->last_value = at91adc_reg_read(st,
>> +							  AT91_ADC_CHR(chan));
>> +		}
>> +
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct iio_dev *idev,
>> +				struct at91_adc_data *pdata)
>> +{
>> +	struct iio_chan_spec *chan_array;
>> +	int bit, idx = 0;
>> +
>> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
>> +					   pdata->num_channels);
>> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
>> +			     GFP_KERNEL);
>> +
>> +	if (chan_array == NULL)
>> +		return -ENOMEM;
>> +
>> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
>> +		struct iio_chan_spec *chan = chan_array + idx;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = bit;
>> +		chan->scan_type.sign = 'u';
>> +		chan->scan_type.realbits = 10;
>> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>> +		++idx;
>> +	}
>> +
>> +	idev->channels = chan_array;
>> +	return idev->num_channels;
>> +}
>> +
>> +static void at91adc_channel_remove(struct iio_dev *idev)
>> +{
>> +	kfree(idev->channels);
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int scale_uv;
>> +	short ret;
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
>> +						       st->done,
>> +						       msecs_to_jiffies(1000));
>> +		if (ret == -ERESTARTSYS)
>> +			break;
>> +
>> +		*val = st->last_value;
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->last_value = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>> +		*val = scale_uv / 1000;
>> +		*val2 = (scale_uv % 1000) * 1000;
>> +		return IIO_VAL_INT_PLUS_MICRO;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
>> +	if (idev == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Prescaler rate computation using the formula from the Atmel's
>> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
>> +	 * specified by the electrical characteristics of the board.
>> +	 */
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Number of ticks needed to cover the startup time of the ADC as
>> +	 * defined in the electrical characteristics of the board, divided by 8.
>> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
>> +	 */
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	ret = at91adc_channel_init(idev, pdata);
>> +	if (ret < 0)
>> +		goto error_free_clk;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	st->vref_mv = pdata->vref;
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0)
>> +		goto error_free_channels;
>> +
>> +	return 0;
>> +
>> +error_free_channels:
>> +	at91adc_channel_remove(idev);
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_device_free(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	iio_device_unregister(idev);
>> +	at91adc_channel_remove(idev);
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +	free_irq(st->irq, idev);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_free(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +	},
>> +};
>> +
>> +module_platform_driver(at91adc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-11 12:34         ` Jonathan Cameron
  0 siblings, 0 replies; 131+ messages in thread
From: Jonathan Cameron @ 2011-11-11 12:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/10/2011 05:35 PM, Jonathan Cameron wrote:
> On 11/09/2011 10:19 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>
> One little comment / query inline.  I'm happy with this either way
> though.  Beware as you are dependent on a series under review though
> and it's always possible little things in there might change!
> 
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> 
>> ---
>>  drivers/iio/adc/Kconfig   |    6 +
>>  drivers/iio/adc/Makefile  |    4 +-
>>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 335 insertions(+), 1 deletions(-)
>>  create mode 100644 drivers/iio/adc/at91adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3d97b21..74f4d9f 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -14,6 +14,12 @@ config IIO_AD799X
>>  	  i2c analog to digital convertors (ADC). Provides direct access
>>  	  via sysfs.
>>  
>> +config IIO_AT91ADC
>> +	tristate "Atmel AT91 ADC"
>> +	depends on SYSFS && ARCH_AT91
>> +	help
>> +	  Say yes here to build support for Atmel AT91 ADC.
>> +
>>  config IIO_MAX1363
>>  	tristate "Maxim max1363 ADC driver"
>>  	depends on I2C
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index c197334..776b56f 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>>  
>>  iio_max1363-y := max1363_core.o
>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> \ No newline at end of file
>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> +
>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..6aef5d6
>> --- /dev/null
>> +++ b/drivers/iio/adc/at91adc.c
>> @@ -0,0 +1,326 @@
>> +/*
>> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 last_value;
>> +	void __iomem *reg_base;
>> +	unsigned int vref_mv;
>> +};
>> +
>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
>> +{
>> +	return readl_relaxed(st->reg_base + reg);
>> +}
>> +
>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
>> +{
>> +	writel_relaxed(val, st->reg_base + reg);
>> +}
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct iio_dev *idev = private;
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < idev->num_channels; chan++)
> Given we know only one bit should be set, it really feels like there
> should be
> a cleaner way of doing this. Could it use find_first_bit(&status,
> idev->num_channels)?
> It kind of bypasses the defines though so it's far from clean in that sense.
>> +		if (status & AT91_ADC_EOC(chan)) {
> 
> The logic in here very much assumes that only one channel is being converted
> and the interrupt indicates that one of the channels has finished.
> Hence logically this st->done doesn't need to be in the loop.
> 
Having had a look at the datasheet, could you just check against the
whole mask and use the lastdata converted register?  Should under
current conditions always give you the right thing I think?
>> +			st->done = true;
>> +			st->last_value = at91adc_reg_read(st,
>> +							  AT91_ADC_CHR(chan));
>> +		}
>> +
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct iio_dev *idev,
>> +				struct at91_adc_data *pdata)
>> +{
>> +	struct iio_chan_spec *chan_array;
>> +	int bit, idx = 0;
>> +
>> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
>> +					   pdata->num_channels);
>> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
>> +			     GFP_KERNEL);
>> +
>> +	if (chan_array == NULL)
>> +		return -ENOMEM;
>> +
>> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
>> +		struct iio_chan_spec *chan = chan_array + idx;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = bit;
>> +		chan->scan_type.sign = 'u';
>> +		chan->scan_type.realbits = 10;
>> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>> +		++idx;
>> +	}
>> +
>> +	idev->channels = chan_array;
>> +	return idev->num_channels;
>> +}
>> +
>> +static void at91adc_channel_remove(struct iio_dev *idev)
>> +{
>> +	kfree(idev->channels);
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int scale_uv;
>> +	short ret;
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
>> +						       st->done,
>> +						       msecs_to_jiffies(1000));
>> +		if (ret == -ERESTARTSYS)
>> +			break;
>> +
>> +		*val = st->last_value;
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->last_value = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>> +		*val = scale_uv / 1000;
>> +		*val2 = (scale_uv % 1000) * 1000;
>> +		return IIO_VAL_INT_PLUS_MICRO;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
>> +	if (idev == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Prescaler rate computation using the formula from the Atmel's
>> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
>> +	 * specified by the electrical characteristics of the board.
>> +	 */
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Number of ticks needed to cover the startup time of the ADC as
>> +	 * defined in the electrical characteristics of the board, divided by 8.
>> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
>> +	 */
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	ret = at91adc_channel_init(idev, pdata);
>> +	if (ret < 0)
>> +		goto error_free_clk;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	st->vref_mv = pdata->vref;
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0)
>> +		goto error_free_channels;
>> +
>> +	return 0;
>> +
>> +error_free_channels:
>> +	at91adc_channel_remove(idev);
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_device_free(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	iio_device_unregister(idev);
>> +	at91adc_channel_remove(idev);
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +	free_irq(st->irq, idev);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_free(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +	},
>> +};
>> +
>> +module_platform_driver(at91adc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-10 17:35       ` Jonathan Cameron
@ 2011-11-14  9:06         ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14  9:06 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Jean-Christophe Plagniol-Villard, Thomas Petazzoni

On 10/11/2011 18:35, Jonathan Cameron wrote:
> On 11/09/2011 10:19 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>
> One little comment / query inline.  I'm happy with this either way
> though.  Beware as you are dependent on a series under review though
> and it's always possible little things in there might change!

Yes, I know.
Maybe it would be better if you took this patchset for that reason. I
guess you have more control over the move out of staging than AT91
maintaners have.

>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> 
>> ---
>>  drivers/iio/adc/Kconfig   |    6 +
>>  drivers/iio/adc/Makefile  |    4 +-
>>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 335 insertions(+), 1 deletions(-)
>>  create mode 100644 drivers/iio/adc/at91adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3d97b21..74f4d9f 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -14,6 +14,12 @@ config IIO_AD799X
>>  	  i2c analog to digital convertors (ADC). Provides direct access
>>  	  via sysfs.
>>  
>> +config IIO_AT91ADC
>> +	tristate "Atmel AT91 ADC"
>> +	depends on SYSFS && ARCH_AT91
>> +	help
>> +	  Say yes here to build support for Atmel AT91 ADC.
>> +
>>  config IIO_MAX1363
>>  	tristate "Maxim max1363 ADC driver"
>>  	depends on I2C
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index c197334..776b56f 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>>  
>>  iio_max1363-y := max1363_core.o
>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> \ No newline at end of file
>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> +
>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..6aef5d6
>> --- /dev/null
>> +++ b/drivers/iio/adc/at91adc.c
>> @@ -0,0 +1,326 @@
>> +/*
>> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 last_value;
>> +	void __iomem *reg_base;
>> +	unsigned int vref_mv;
>> +};
>> +
>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
>> +{
>> +	return readl_relaxed(st->reg_base + reg);
>> +}
>> +
>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
>> +{
>> +	writel_relaxed(val, st->reg_base + reg);
>> +}
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct iio_dev *idev = private;
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < idev->num_channels; chan++)
> Given we know only one bit should be set, it really feels like there
> should be
> a cleaner way of doing this. Could it use find_first_bit(&status,
> idev->num_channels)?
> It kind of bypasses the defines though so it's far from clean in that sense.
>> +		if (status & AT91_ADC_EOC(chan)) {
> 
> The logic in here very much assumes that only one channel is being converted
> and the interrupt indicates that one of the channels has finished.
> Hence logically this st->done doesn't need to be in the loop.
> 
>> +			st->done = true;
>> +			st->last_value = at91adc_reg_read(st,
>> +							  AT91_ADC_CHR(chan));
>> +		}
>> +
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct iio_dev *idev,
>> +				struct at91_adc_data *pdata)
>> +{
>> +	struct iio_chan_spec *chan_array;
>> +	int bit, idx = 0;
>> +
>> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
>> +					   pdata->num_channels);
>> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
>> +			     GFP_KERNEL);
>> +
>> +	if (chan_array == NULL)
>> +		return -ENOMEM;
>> +
>> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
>> +		struct iio_chan_spec *chan = chan_array + idx;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = bit;
>> +		chan->scan_type.sign = 'u';
>> +		chan->scan_type.realbits = 10;
>> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>> +		++idx;
>> +	}
>> +
>> +	idev->channels = chan_array;
>> +	return idev->num_channels;
>> +}
>> +
>> +static void at91adc_channel_remove(struct iio_dev *idev)
>> +{
>> +	kfree(idev->channels);
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int scale_uv;
>> +	short ret;
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
>> +						       st->done,
>> +						       msecs_to_jiffies(1000));
>> +		if (ret == -ERESTARTSYS)
>> +			break;
>> +
>> +		*val = st->last_value;
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->last_value = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>> +		*val = scale_uv / 1000;
>> +		*val2 = (scale_uv % 1000) * 1000;
>> +		return IIO_VAL_INT_PLUS_MICRO;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
>> +	if (idev == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Prescaler rate computation using the formula from the Atmel's
>> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
>> +	 * specified by the electrical characteristics of the board.
>> +	 */
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Number of ticks needed to cover the startup time of the ADC as
>> +	 * defined in the electrical characteristics of the board, divided by 8.
>> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
>> +	 */
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	ret = at91adc_channel_init(idev, pdata);
>> +	if (ret < 0)
>> +		goto error_free_clk;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	st->vref_mv = pdata->vref;
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0)
>> +		goto error_free_channels;
>> +
>> +	return 0;
>> +
>> +error_free_channels:
>> +	at91adc_channel_remove(idev);
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_device_free(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	iio_device_unregister(idev);
>> +	at91adc_channel_remove(idev);
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +	free_irq(st->irq, idev);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_free(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +	},
>> +};
>> +
>> +module_platform_driver(at91adc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-14  9:06         ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14  9:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 10/11/2011 18:35, Jonathan Cameron wrote:
> On 11/09/2011 10:19 AM, Maxime Ripard wrote:
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>
> One little comment / query inline.  I'm happy with this either way
> though.  Beware as you are dependent on a series under review though
> and it's always possible little things in there might change!

Yes, I know.
Maybe it would be better if you took this patchset for that reason. I
guess you have more control over the move out of staging than AT91
maintaners have.

>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> 
>> ---
>>  drivers/iio/adc/Kconfig   |    6 +
>>  drivers/iio/adc/Makefile  |    4 +-
>>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 335 insertions(+), 1 deletions(-)
>>  create mode 100644 drivers/iio/adc/at91adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3d97b21..74f4d9f 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -14,6 +14,12 @@ config IIO_AD799X
>>  	  i2c analog to digital convertors (ADC). Provides direct access
>>  	  via sysfs.
>>  
>> +config IIO_AT91ADC
>> +	tristate "Atmel AT91 ADC"
>> +	depends on SYSFS && ARCH_AT91
>> +	help
>> +	  Say yes here to build support for Atmel AT91 ADC.
>> +
>>  config IIO_MAX1363
>>  	tristate "Maxim max1363 ADC driver"
>>  	depends on I2C
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index c197334..776b56f 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>>  
>>  iio_max1363-y := max1363_core.o
>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> \ No newline at end of file
>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>> +
>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
>> new file mode 100644
>> index 0000000..6aef5d6
>> --- /dev/null
>> +++ b/drivers/iio/adc/at91adc.c
>> @@ -0,0 +1,326 @@
>> +/*
>> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
>> + *
>> + * Copyright 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + */
>> +
>> +#include <linux/bitmap.h>
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +#include <mach/at91_adc.h>
>> +#include <mach/board.h>
>> +
>> +struct at91adc_state {
>> +	struct clk *clk;
>> +	bool done;
>> +	struct mutex lock;
>> +	int irq;
>> +	wait_queue_head_t wq_data_avail;
>> +	u16 last_value;
>> +	void __iomem *reg_base;
>> +	unsigned int vref_mv;
>> +};
>> +
>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
>> +{
>> +	return readl_relaxed(st->reg_base + reg);
>> +}
>> +
>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
>> +{
>> +	writel_relaxed(val, st->reg_base + reg);
>> +}
>> +
>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>> +{
>> +	int chan;
>> +	struct iio_dev *idev = private;
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
>> +
>> +	if (!(status & AT91_ADC_DRDY))
>> +		return IRQ_HANDLED;
>> +
>> +	for (chan = 0; chan < idev->num_channels; chan++)
> Given we know only one bit should be set, it really feels like there
> should be
> a cleaner way of doing this. Could it use find_first_bit(&status,
> idev->num_channels)?
> It kind of bypasses the defines though so it's far from clean in that sense.
>> +		if (status & AT91_ADC_EOC(chan)) {
> 
> The logic in here very much assumes that only one channel is being converted
> and the interrupt indicates that one of the channels has finished.
> Hence logically this st->done doesn't need to be in the loop.
> 
>> +			st->done = true;
>> +			st->last_value = at91adc_reg_read(st,
>> +							  AT91_ADC_CHR(chan));
>> +		}
>> +
>> +	wake_up_interruptible(&st->wq_data_avail);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int at91adc_channel_init(struct iio_dev *idev,
>> +				struct at91_adc_data *pdata)
>> +{
>> +	struct iio_chan_spec *chan_array;
>> +	int bit, idx = 0;
>> +
>> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
>> +					   pdata->num_channels);
>> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
>> +			     GFP_KERNEL);
>> +
>> +	if (chan_array == NULL)
>> +		return -ENOMEM;
>> +
>> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
>> +		struct iio_chan_spec *chan = chan_array + idx;
>> +		chan->type = IIO_VOLTAGE;
>> +		chan->indexed = 1;
>> +		chan->channel = bit;
>> +		chan->scan_type.sign = 'u';
>> +		chan->scan_type.realbits = 10;
>> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>> +		++idx;
>> +	}
>> +
>> +	idev->channels = chan_array;
>> +	return idev->num_channels;
>> +}
>> +
>> +static void at91adc_channel_remove(struct iio_dev *idev)
>> +{
>> +	kfree(idev->channels);
>> +}
>> +
>> +static int at91adc_read_raw(struct iio_dev *idev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int *val, int *val2, long mask)
>> +{
>> +	struct at91adc_state *st = iio_priv(idev);
>> +	unsigned int scale_uv;
>> +	short ret;
>> +
>> +	switch (mask) {
>> +	case 0:
>> +		mutex_lock(&st->lock);
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHER,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IER,
>> +				  AT91_ADC_EOC(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
>> +
>> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
>> +						       st->done,
>> +						       msecs_to_jiffies(1000));
>> +		if (ret == -ERESTARTSYS)
>> +			break;
>> +
>> +		*val = st->last_value;
>> +
>> +		at91adc_reg_write(st, AT91_ADC_CHDR,
>> +				  AT91_ADC_CH(chan->channel));
>> +		at91adc_reg_write(st, AT91_ADC_IDR,
>> +				  AT91_ADC_EOC(chan->channel));
>> +
>> +		st->last_value = 0;
>> +		st->done = false;
>> +		mutex_unlock(&st->lock);
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SCALE:
>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>> +		*val = scale_uv / 1000;
>> +		*val2 = (scale_uv % 1000) * 1000;
>> +		return IIO_VAL_INT_PLUS_MICRO;
>> +	default:
>> +		break;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info at91adc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &at91adc_read_raw,
>> +};
>> +
>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>> +{
>> +	unsigned int prsc, mstrclk, ticks;
>> +	int ret;
>> +	struct iio_dev *idev;
>> +	struct at91adc_state *st;
>> +	struct resource *res;
>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No resource defined\n");
>> +		ret = -ENXIO;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
>> +	if (idev == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	platform_set_drvdata(pdev, idev);
>> +
>> +	idev->dev.parent = &pdev->dev;
>> +	idev->name = dev_name(&pdev->dev);
>> +	idev->info = &at91adc_info;
>> +
>> +	st = iio_priv(idev);
>> +	st->irq = platform_get_irq(pdev, 0);
>> +	if (st->irq < 0) {
>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>> +		ret = -ENODEV;
>> +		goto error_free_device;
>> +	}
>> +
>> +	if (!request_mem_region(res->start, resource_size(res),
>> +				"AT91 adc registers")) {
>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>> +		ret = -EBUSY;
>> +		goto error_free_device;
>> +	}
>> +
>> +	st->reg_base = ioremap(res->start, resource_size(res));
>> +	if (!st->reg_base) {
>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>> +		ret = -ENOMEM;
>> +		goto error_release_mem;
>> +	}
>> +
>> +	/*
>> +	 * Disable all IRQs before setting up the handler
>> +	 */
>> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
>> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
>> +	ret = request_irq(st->irq,
>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>> +		goto error_unmap_reg;
>> +	}
>> +
>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>> +	if (IS_ERR(st->clk)) {
>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>> +		ret = PTR_ERR(st->clk);
>> +		goto error_free_irq;
>> +	}
>> +
>> +	clk_enable(st->clk);
>> +	mstrclk = clk_get_rate(st->clk);
>> +
>> +	if (!pdata) {
>> +		dev_err(&pdev->dev, "No platform data available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	if (!pdata->adc_clock) {
>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Prescaler rate computation using the formula from the Atmel's
>> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
>> +	 * specified by the electrical characteristics of the board.
>> +	 */
>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>> +
>> +	if (!pdata->startup_time) {
>> +		dev_err(&pdev->dev, "No startup time available.\n");
>> +		ret = -EINVAL;
>> +		goto error_free_clk;
>> +	}
>> +
>> +	/*
>> +	 * Number of ticks needed to cover the startup time of the ADC as
>> +	 * defined in the electrical characteristics of the board, divided by 8.
>> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
>> +	 */
>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>> +			  1000000) - 1, 8) / 8;
>> +	at91adc_reg_write(st, AT91_ADC_MR,
>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>> +
>> +	/* Setup the ADC channels available on the board */
>> +	ret = at91adc_channel_init(idev, pdata);
>> +	if (ret < 0)
>> +		goto error_free_clk;
>> +
>> +	init_waitqueue_head(&st->wq_data_avail);
>> +	mutex_init(&st->lock);
>> +
>> +	st->vref_mv = pdata->vref;
>> +
>> +	ret = iio_device_register(idev);
>> +	if (ret < 0)
>> +		goto error_free_channels;
>> +
>> +	return 0;
>> +
>> +error_free_channels:
>> +	at91adc_channel_remove(idev);
>> +error_free_clk:
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +error_free_irq:
>> +	free_irq(st->irq, st);
>> +error_unmap_reg:
>> +	iounmap(st->reg_base);
>> +error_release_mem:
>> +	release_mem_region(res->start, resource_size(res));
>> +error_free_device:
>> +	iio_device_free(idev);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	struct at91adc_state *st = iio_priv(idev);
>> +
>> +	iio_device_unregister(idev);
>> +	at91adc_channel_remove(idev);
>> +	clk_disable(st->clk);
>> +	clk_put(st->clk);
>> +	free_irq(st->irq, idev);
>> +	iounmap(st->reg_base);
>> +	release_mem_region(res->start, resource_size(res));
>> +	iio_device_free(idev);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver at91adc_driver = {
>> +	.probe = at91adc_probe,
>> +	.remove = __devexit_p(at91adc_remove),
>> +	.driver = {
>> +		   .name = "at91adc",
>> +	},
>> +};
>> +
>> +module_platform_driver(at91adc_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
  2011-11-11 12:34         ` Jonathan Cameron
@ 2011-11-14  9:59           ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14  9:59 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez,
	Jean-Christophe Plagniol-Villard, Thomas Petazzoni

On 11/11/2011 13:34, Jonathan Cameron wrote:
> On 11/10/2011 05:35 PM, Jonathan Cameron wrote:
>> On 11/09/2011 10:19 AM, Maxime Ripard wrote:
>>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
>>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>>
>> One little comment / query inline.  I'm happy with this either way
>> though.  Beware as you are dependent on a series under review though
>> and it's always possible little things in there might change!
>>
>>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
>>
>>> ---
>>>  drivers/iio/adc/Kconfig   |    6 +
>>>  drivers/iio/adc/Makefile  |    4 +-
>>>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 335 insertions(+), 1 deletions(-)
>>>  create mode 100644 drivers/iio/adc/at91adc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 3d97b21..74f4d9f 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -14,6 +14,12 @@ config IIO_AD799X
>>>  	  i2c analog to digital convertors (ADC). Provides direct access
>>>  	  via sysfs.
>>>  
>>> +config IIO_AT91ADC
>>> +	tristate "Atmel AT91 ADC"
>>> +	depends on SYSFS && ARCH_AT91
>>> +	help
>>> +	  Say yes here to build support for Atmel AT91 ADC.
>>> +
>>>  config IIO_MAX1363
>>>  	tristate "Maxim max1363 ADC driver"
>>>  	depends on I2C
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index c197334..776b56f 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>>>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>>>  
>>>  iio_max1363-y := max1363_core.o
>>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>>> \ No newline at end of file
>>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>>> +
>>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
>>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
>>> new file mode 100644
>>> index 0000000..6aef5d6
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/at91adc.c
>>> @@ -0,0 +1,326 @@
>>> +/*
>>> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
>>> + *
>>> + * Copyright 2011 Free Electrons
>>> + *
>>> + * Licensed under the GPLv2 or later.
>>> + */
>>> +
>>> +#include <linux/bitmap.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/err.h>
>>> +#include <linux/io.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/wait.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +
>>> +#include <mach/at91_adc.h>
>>> +#include <mach/board.h>
>>> +
>>> +struct at91adc_state {
>>> +	struct clk *clk;
>>> +	bool done;
>>> +	struct mutex lock;
>>> +	int irq;
>>> +	wait_queue_head_t wq_data_avail;
>>> +	u16 last_value;
>>> +	void __iomem *reg_base;
>>> +	unsigned int vref_mv;
>>> +};
>>> +
>>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
>>> +{
>>> +	return readl_relaxed(st->reg_base + reg);
>>> +}
>>> +
>>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
>>> +{
>>> +	writel_relaxed(val, st->reg_base + reg);
>>> +}
>>> +
>>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>>> +{
>>> +	int chan;
>>> +	struct iio_dev *idev = private;
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
>>> +
>>> +	if (!(status & AT91_ADC_DRDY))
>>> +		return IRQ_HANDLED;
>>> +
>>> +	for (chan = 0; chan < idev->num_channels; chan++)
>> Given we know only one bit should be set, it really feels like there
>> should be
>> a cleaner way of doing this. Could it use find_first_bit(&status,
>> idev->num_channels)?
>> It kind of bypasses the defines though so it's far from clean in that sense.
>>> +		if (status & AT91_ADC_EOC(chan)) {
>>
>> The logic in here very much assumes that only one channel is being converted
>> and the interrupt indicates that one of the channels has finished.
>> Hence logically this st->done doesn't need to be in the loop.
>>
> Having had a look at the datasheet, could you just check against the
> whole mask and use the lastdata converted register?  Should under
> current conditions always give you the right thing I think?

You're right, It will be more consistent and avoid to loop over each
channels. I send a new version of the patchset.

>>> +			st->done = true;
>>> +			st->last_value = at91adc_reg_read(st,
>>> +							  AT91_ADC_CHR(chan));
>>> +		}
>>> +
>>> +	wake_up_interruptible(&st->wq_data_avail);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int at91adc_channel_init(struct iio_dev *idev,
>>> +				struct at91_adc_data *pdata)
>>> +{
>>> +	struct iio_chan_spec *chan_array;
>>> +	int bit, idx = 0;
>>> +
>>> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
>>> +					   pdata->num_channels);
>>> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
>>> +			     GFP_KERNEL);
>>> +
>>> +	if (chan_array == NULL)
>>> +		return -ENOMEM;
>>> +
>>> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
>>> +		struct iio_chan_spec *chan = chan_array + idx;
>>> +		chan->type = IIO_VOLTAGE;
>>> +		chan->indexed = 1;
>>> +		chan->channel = bit;
>>> +		chan->scan_type.sign = 'u';
>>> +		chan->scan_type.realbits = 10;
>>> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>>> +		++idx;
>>> +	}
>>> +
>>> +	idev->channels = chan_array;
>>> +	return idev->num_channels;
>>> +}
>>> +
>>> +static void at91adc_channel_remove(struct iio_dev *idev)
>>> +{
>>> +	kfree(idev->channels);
>>> +}
>>> +
>>> +static int at91adc_read_raw(struct iio_dev *idev,
>>> +			    struct iio_chan_spec const *chan,
>>> +			    int *val, int *val2, long mask)
>>> +{
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +	unsigned int scale_uv;
>>> +	short ret;
>>> +
>>> +	switch (mask) {
>>> +	case 0:
>>> +		mutex_lock(&st->lock);
>>> +
>>> +		at91adc_reg_write(st, AT91_ADC_CHER,
>>> +				  AT91_ADC_CH(chan->channel));
>>> +		at91adc_reg_write(st, AT91_ADC_IER,
>>> +				  AT91_ADC_EOC(chan->channel));
>>> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
>>> +
>>> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
>>> +						       st->done,
>>> +						       msecs_to_jiffies(1000));
>>> +		if (ret == -ERESTARTSYS)
>>> +			break;
>>> +
>>> +		*val = st->last_value;
>>> +
>>> +		at91adc_reg_write(st, AT91_ADC_CHDR,
>>> +				  AT91_ADC_CH(chan->channel));
>>> +		at91adc_reg_write(st, AT91_ADC_IDR,
>>> +				  AT91_ADC_EOC(chan->channel));
>>> +
>>> +		st->last_value = 0;
>>> +		st->done = false;
>>> +		mutex_unlock(&st->lock);
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_SCALE:
>>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>>> +		*val = scale_uv / 1000;
>>> +		*val2 = (scale_uv % 1000) * 1000;
>>> +		return IIO_VAL_INT_PLUS_MICRO;
>>> +	default:
>>> +		break;
>>> +	}
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info at91adc_info = {
>>> +	.driver_module = THIS_MODULE,
>>> +	.read_raw = &at91adc_read_raw,
>>> +};
>>> +
>>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>>> +{
>>> +	unsigned int prsc, mstrclk, ticks;
>>> +	int ret;
>>> +	struct iio_dev *idev;
>>> +	struct at91adc_state *st;
>>> +	struct resource *res;
>>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>>> +
>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +	if (!res) {
>>> +		dev_err(&pdev->dev, "No resource defined\n");
>>> +		ret = -ENXIO;
>>> +		goto error_ret;
>>> +	}
>>> +
>>> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
>>> +	if (idev == NULL) {
>>> +		ret = -ENOMEM;
>>> +		goto error_ret;
>>> +	}
>>> +	platform_set_drvdata(pdev, idev);
>>> +
>>> +	idev->dev.parent = &pdev->dev;
>>> +	idev->name = dev_name(&pdev->dev);
>>> +	idev->info = &at91adc_info;
>>> +
>>> +	st = iio_priv(idev);
>>> +	st->irq = platform_get_irq(pdev, 0);
>>> +	if (st->irq < 0) {
>>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>>> +		ret = -ENODEV;
>>> +		goto error_free_device;
>>> +	}
>>> +
>>> +	if (!request_mem_region(res->start, resource_size(res),
>>> +				"AT91 adc registers")) {
>>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>>> +		ret = -EBUSY;
>>> +		goto error_free_device;
>>> +	}
>>> +
>>> +	st->reg_base = ioremap(res->start, resource_size(res));
>>> +	if (!st->reg_base) {
>>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>>> +		ret = -ENOMEM;
>>> +		goto error_release_mem;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Disable all IRQs before setting up the handler
>>> +	 */
>>> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
>>> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
>>> +	ret = request_irq(st->irq,
>>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>>> +		goto error_unmap_reg;
>>> +	}
>>> +
>>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>>> +	if (IS_ERR(st->clk)) {
>>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>>> +		ret = PTR_ERR(st->clk);
>>> +		goto error_free_irq;
>>> +	}
>>> +
>>> +	clk_enable(st->clk);
>>> +	mstrclk = clk_get_rate(st->clk);
>>> +
>>> +	if (!pdata) {
>>> +		dev_err(&pdev->dev, "No platform data available.\n");
>>> +		ret = -EINVAL;
>>> +		goto error_free_clk;
>>> +	}
>>> +
>>> +	if (!pdata->adc_clock) {
>>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>>> +		ret = -EINVAL;
>>> +		goto error_free_clk;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Prescaler rate computation using the formula from the Atmel's
>>> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
>>> +	 * specified by the electrical characteristics of the board.
>>> +	 */
>>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>>> +
>>> +	if (!pdata->startup_time) {
>>> +		dev_err(&pdev->dev, "No startup time available.\n");
>>> +		ret = -EINVAL;
>>> +		goto error_free_clk;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Number of ticks needed to cover the startup time of the ADC as
>>> +	 * defined in the electrical characteristics of the board, divided by 8.
>>> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
>>> +	 */
>>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>>> +			  1000000) - 1, 8) / 8;
>>> +	at91adc_reg_write(st, AT91_ADC_MR,
>>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>>> +
>>> +	/* Setup the ADC channels available on the board */
>>> +	ret = at91adc_channel_init(idev, pdata);
>>> +	if (ret < 0)
>>> +		goto error_free_clk;
>>> +
>>> +	init_waitqueue_head(&st->wq_data_avail);
>>> +	mutex_init(&st->lock);
>>> +
>>> +	st->vref_mv = pdata->vref;
>>> +
>>> +	ret = iio_device_register(idev);
>>> +	if (ret < 0)
>>> +		goto error_free_channels;
>>> +
>>> +	return 0;
>>> +
>>> +error_free_channels:
>>> +	at91adc_channel_remove(idev);
>>> +error_free_clk:
>>> +	clk_disable(st->clk);
>>> +	clk_put(st->clk);
>>> +error_free_irq:
>>> +	free_irq(st->irq, st);
>>> +error_unmap_reg:
>>> +	iounmap(st->reg_base);
>>> +error_release_mem:
>>> +	release_mem_region(res->start, resource_size(res));
>>> +error_free_device:
>>> +	iio_device_free(idev);
>>> +error_ret:
>>> +	return ret;
>>> +}
>>> +
>>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +
>>> +	iio_device_unregister(idev);
>>> +	at91adc_channel_remove(idev);
>>> +	clk_disable(st->clk);
>>> +	clk_put(st->clk);
>>> +	free_irq(st->irq, idev);
>>> +	iounmap(st->reg_base);
>>> +	release_mem_region(res->start, resource_size(res));
>>> +	iio_device_free(idev);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct platform_driver at91adc_driver = {
>>> +	.probe = at91adc_probe,
>>> +	.remove = __devexit_p(at91adc_remove),
>>> +	.driver = {
>>> +		   .name = "at91adc",
>>> +	},
>>> +};
>>> +
>>> +module_platform_driver(at91adc_driver);
>>> +
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2011-11-14  9:59           ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14  9:59 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/11/2011 13:34, Jonathan Cameron wrote:
> On 11/10/2011 05:35 PM, Jonathan Cameron wrote:
>> On 11/09/2011 10:19 AM, Maxime Ripard wrote:
>>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
>>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>>>
>> One little comment / query inline.  I'm happy with this either way
>> though.  Beware as you are dependent on a series under review though
>> and it's always possible little things in there might change!
>>
>>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
>>
>>> ---
>>>  drivers/iio/adc/Kconfig   |    6 +
>>>  drivers/iio/adc/Makefile  |    4 +-
>>>  drivers/iio/adc/at91adc.c |  326 +++++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 335 insertions(+), 1 deletions(-)
>>>  create mode 100644 drivers/iio/adc/at91adc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 3d97b21..74f4d9f 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -14,6 +14,12 @@ config IIO_AD799X
>>>  	  i2c analog to digital convertors (ADC). Provides direct access
>>>  	  via sysfs.
>>>  
>>> +config IIO_AT91ADC
>>> +	tristate "Atmel AT91 ADC"
>>> +	depends on SYSFS && ARCH_AT91
>>> +	help
>>> +	  Say yes here to build support for Atmel AT91 ADC.
>>> +
>>>  config IIO_MAX1363
>>>  	tristate "Maxim max1363 ADC driver"
>>>  	depends on I2C
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index c197334..776b56f 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
>>>  obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
>>>  
>>>  iio_max1363-y := max1363_core.o
>>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>>> \ No newline at end of file
>>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
>>> +
>>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
>>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c
>>> new file mode 100644
>>> index 0000000..6aef5d6
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/at91adc.c
>>> @@ -0,0 +1,326 @@
>>> +/*
>>> + * Driver for the ADC present in the Atmel AT91 evaluation boards.
>>> + *
>>> + * Copyright 2011 Free Electrons
>>> + *
>>> + * Licensed under the GPLv2 or later.
>>> + */
>>> +
>>> +#include <linux/bitmap.h>
>>> +#include <linux/bitops.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/err.h>
>>> +#include <linux/io.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/wait.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +
>>> +#include <mach/at91_adc.h>
>>> +#include <mach/board.h>
>>> +
>>> +struct at91adc_state {
>>> +	struct clk *clk;
>>> +	bool done;
>>> +	struct mutex lock;
>>> +	int irq;
>>> +	wait_queue_head_t wq_data_avail;
>>> +	u16 last_value;
>>> +	void __iomem *reg_base;
>>> +	unsigned int vref_mv;
>>> +};
>>> +
>>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
>>> +{
>>> +	return readl_relaxed(st->reg_base + reg);
>>> +}
>>> +
>>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val)
>>> +{
>>> +	writel_relaxed(val, st->reg_base + reg);
>>> +}
>>> +
>>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
>>> +{
>>> +	int chan;
>>> +	struct iio_dev *idev = private;
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
>>> +
>>> +	if (!(status & AT91_ADC_DRDY))
>>> +		return IRQ_HANDLED;
>>> +
>>> +	for (chan = 0; chan < idev->num_channels; chan++)
>> Given we know only one bit should be set, it really feels like there
>> should be
>> a cleaner way of doing this. Could it use find_first_bit(&status,
>> idev->num_channels)?
>> It kind of bypasses the defines though so it's far from clean in that sense.
>>> +		if (status & AT91_ADC_EOC(chan)) {
>>
>> The logic in here very much assumes that only one channel is being converted
>> and the interrupt indicates that one of the channels has finished.
>> Hence logically this st->done doesn't need to be in the loop.
>>
> Having had a look at the datasheet, could you just check against the
> whole mask and use the lastdata converted register?  Should under
> current conditions always give you the right thing I think?

You're right, It will be more consistent and avoid to loop over each
channels. I send a new version of the patchset.

>>> +			st->done = true;
>>> +			st->last_value = at91adc_reg_read(st,
>>> +							  AT91_ADC_CHR(chan));
>>> +		}
>>> +
>>> +	wake_up_interruptible(&st->wq_data_avail);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int at91adc_channel_init(struct iio_dev *idev,
>>> +				struct at91_adc_data *pdata)
>>> +{
>>> +	struct iio_chan_spec *chan_array;
>>> +	int bit, idx = 0;
>>> +
>>> +	idev->num_channels = bitmap_weight(&(pdata->channels_used),
>>> +					   pdata->num_channels);
>>> +	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
>>> +			     GFP_KERNEL);
>>> +
>>> +	if (chan_array == NULL)
>>> +		return -ENOMEM;
>>> +
>>> +	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
>>> +		struct iio_chan_spec *chan = chan_array + idx;
>>> +		chan->type = IIO_VOLTAGE;
>>> +		chan->indexed = 1;
>>> +		chan->channel = bit;
>>> +		chan->scan_type.sign = 'u';
>>> +		chan->scan_type.realbits = 10;
>>> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
>>> +		++idx;
>>> +	}
>>> +
>>> +	idev->channels = chan_array;
>>> +	return idev->num_channels;
>>> +}
>>> +
>>> +static void at91adc_channel_remove(struct iio_dev *idev)
>>> +{
>>> +	kfree(idev->channels);
>>> +}
>>> +
>>> +static int at91adc_read_raw(struct iio_dev *idev,
>>> +			    struct iio_chan_spec const *chan,
>>> +			    int *val, int *val2, long mask)
>>> +{
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +	unsigned int scale_uv;
>>> +	short ret;
>>> +
>>> +	switch (mask) {
>>> +	case 0:
>>> +		mutex_lock(&st->lock);
>>> +
>>> +		at91adc_reg_write(st, AT91_ADC_CHER,
>>> +				  AT91_ADC_CH(chan->channel));
>>> +		at91adc_reg_write(st, AT91_ADC_IER,
>>> +				  AT91_ADC_EOC(chan->channel));
>>> +		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
>>> +
>>> +		ret = wait_event_interruptible_timeout(st->wq_data_avail,
>>> +						       st->done,
>>> +						       msecs_to_jiffies(1000));
>>> +		if (ret == -ERESTARTSYS)
>>> +			break;
>>> +
>>> +		*val = st->last_value;
>>> +
>>> +		at91adc_reg_write(st, AT91_ADC_CHDR,
>>> +				  AT91_ADC_CH(chan->channel));
>>> +		at91adc_reg_write(st, AT91_ADC_IDR,
>>> +				  AT91_ADC_EOC(chan->channel));
>>> +
>>> +		st->last_value = 0;
>>> +		st->done = false;
>>> +		mutex_unlock(&st->lock);
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_SCALE:
>>> +		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
>>> +		*val = scale_uv / 1000;
>>> +		*val2 = (scale_uv % 1000) * 1000;
>>> +		return IIO_VAL_INT_PLUS_MICRO;
>>> +	default:
>>> +		break;
>>> +	}
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info at91adc_info = {
>>> +	.driver_module = THIS_MODULE,
>>> +	.read_raw = &at91adc_read_raw,
>>> +};
>>> +
>>> +static int __devinit at91adc_probe(struct platform_device *pdev)
>>> +{
>>> +	unsigned int prsc, mstrclk, ticks;
>>> +	int ret;
>>> +	struct iio_dev *idev;
>>> +	struct at91adc_state *st;
>>> +	struct resource *res;
>>> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
>>> +
>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +	if (!res) {
>>> +		dev_err(&pdev->dev, "No resource defined\n");
>>> +		ret = -ENXIO;
>>> +		goto error_ret;
>>> +	}
>>> +
>>> +	idev = iio_device_allocate(sizeof(struct at91adc_state));
>>> +	if (idev == NULL) {
>>> +		ret = -ENOMEM;
>>> +		goto error_ret;
>>> +	}
>>> +	platform_set_drvdata(pdev, idev);
>>> +
>>> +	idev->dev.parent = &pdev->dev;
>>> +	idev->name = dev_name(&pdev->dev);
>>> +	idev->info = &at91adc_info;
>>> +
>>> +	st = iio_priv(idev);
>>> +	st->irq = platform_get_irq(pdev, 0);
>>> +	if (st->irq < 0) {
>>> +		dev_err(&pdev->dev, "No IRQ ID is designated\n");
>>> +		ret = -ENODEV;
>>> +		goto error_free_device;
>>> +	}
>>> +
>>> +	if (!request_mem_region(res->start, resource_size(res),
>>> +				"AT91 adc registers")) {
>>> +		dev_err(&pdev->dev, "Resources are unavailable.\n");
>>> +		ret = -EBUSY;
>>> +		goto error_free_device;
>>> +	}
>>> +
>>> +	st->reg_base = ioremap(res->start, resource_size(res));
>>> +	if (!st->reg_base) {
>>> +		dev_err(&pdev->dev, "Failed to map registers.\n");
>>> +		ret = -ENOMEM;
>>> +		goto error_release_mem;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Disable all IRQs before setting up the handler
>>> +	 */
>>> +	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
>>> +	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
>>> +	ret = request_irq(st->irq,
>>> +			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
>>> +		goto error_unmap_reg;
>>> +	}
>>> +
>>> +	st->clk = clk_get(&pdev->dev, "adc_clk");
>>> +	if (IS_ERR(st->clk)) {
>>> +		dev_err(&pdev->dev, "Failed to get the clock.\n");
>>> +		ret = PTR_ERR(st->clk);
>>> +		goto error_free_irq;
>>> +	}
>>> +
>>> +	clk_enable(st->clk);
>>> +	mstrclk = clk_get_rate(st->clk);
>>> +
>>> +	if (!pdata) {
>>> +		dev_err(&pdev->dev, "No platform data available.\n");
>>> +		ret = -EINVAL;
>>> +		goto error_free_clk;
>>> +	}
>>> +
>>> +	if (!pdata->adc_clock) {
>>> +		dev_err(&pdev->dev, "No ADCClock available.\n");
>>> +		ret = -EINVAL;
>>> +		goto error_free_clk;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Prescaler rate computation using the formula from the Atmel's
>>> +	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
>>> +	 * specified by the electrical characteristics of the board.
>>> +	 */
>>> +	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
>>> +
>>> +	if (!pdata->startup_time) {
>>> +		dev_err(&pdev->dev, "No startup time available.\n");
>>> +		ret = -EINVAL;
>>> +		goto error_free_clk;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Number of ticks needed to cover the startup time of the ADC as
>>> +	 * defined in the electrical characteristics of the board, divided by 8.
>>> +	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
>>> +	 */
>>> +	ticks = round_up((pdata->startup_time * pdata->adc_clock /
>>> +			  1000000) - 1, 8) / 8;
>>> +	at91adc_reg_write(st, AT91_ADC_MR,
>>> +			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
>>> +			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
>>> +
>>> +	/* Setup the ADC channels available on the board */
>>> +	ret = at91adc_channel_init(idev, pdata);
>>> +	if (ret < 0)
>>> +		goto error_free_clk;
>>> +
>>> +	init_waitqueue_head(&st->wq_data_avail);
>>> +	mutex_init(&st->lock);
>>> +
>>> +	st->vref_mv = pdata->vref;
>>> +
>>> +	ret = iio_device_register(idev);
>>> +	if (ret < 0)
>>> +		goto error_free_channels;
>>> +
>>> +	return 0;
>>> +
>>> +error_free_channels:
>>> +	at91adc_channel_remove(idev);
>>> +error_free_clk:
>>> +	clk_disable(st->clk);
>>> +	clk_put(st->clk);
>>> +error_free_irq:
>>> +	free_irq(st->irq, st);
>>> +error_unmap_reg:
>>> +	iounmap(st->reg_base);
>>> +error_release_mem:
>>> +	release_mem_region(res->start, resource_size(res));
>>> +error_free_device:
>>> +	iio_device_free(idev);
>>> +error_ret:
>>> +	return ret;
>>> +}
>>> +
>>> +static int __devexit at91adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct iio_dev *idev = platform_get_drvdata(pdev);
>>> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +	struct at91adc_state *st = iio_priv(idev);
>>> +
>>> +	iio_device_unregister(idev);
>>> +	at91adc_channel_remove(idev);
>>> +	clk_disable(st->clk);
>>> +	clk_put(st->clk);
>>> +	free_irq(st->irq, idev);
>>> +	iounmap(st->reg_base);
>>> +	release_mem_region(res->start, resource_size(res));
>>> +	iio_device_free(idev);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct platform_driver at91adc_driver = {
>>> +	.probe = at91adc_probe,
>>> +	.remove = __devexit_p(at91adc_remove),
>>> +	.driver = {
>>> +		   .name = "at91adc",
>>> +	},
>>> +};
>>> +
>>> +module_platform_driver(at91adc_driver);
>>> +
>>> +MODULE_LICENSE("GPL");
>>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
>>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2012-01-16 21:36 [PATCH RESEND v13] AT91: Add a driver for the ADC Maxime Ripard
@ 2012-01-16 21:36   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2012-01-16 21:36 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..c27b502
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+/**
+ * struct at91_adc_data - platform data for ADC driver
+ * @channels_use:	channels in use on the board as a bitmask
+ * @vref:		Reference voltage for the ADC in millivolts
+ */
+struct at91_adc_data {
+	unsigned long	channels_used;
+	u16		vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.5.4


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2012-01-16 21:36   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2012-01-16 21:36 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..c27b502
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+/**
+ * struct at91_adc_data - platform data for ADC driver
+ * @channels_use:	channels in use on the board as a bitmask
+ * @vref:		Reference voltage for the ADC in millivolts
+ */
+struct at91_adc_data {
+	unsigned long	channels_used;
+	u16		vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.5.4

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-12-14 10:27     ` Alexander Stein
@ 2011-12-15 19:37       ` Maxime Ripard
  -1 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-12-15 19:37 UTC (permalink / raw)
  To: Alexander Stein
  Cc: linux-arm-kernel, linux-iio, Thomas Petazzoni, Patrice Vilchez,
	Nicolas Ferre

On 14/12/2011 11:27, Alexander Stein wrote:
> On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote:
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
>> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
>>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>> ---
>>  include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
>>  1 files changed, 23 insertions(+), 0 deletions(-)
>>  create mode 100644 include/linux/platform_data/at91_adc.h
>>
>> diff --git a/include/linux/platform_data/at91_adc.h
>> b/include/linux/platform_data/at91_adc.h new file mode 100644
>> index 0000000..bad80b7
>> --- /dev/null
>> +++ b/include/linux/platform_data/at91_adc.h
>> @@ -0,0 +1,23 @@
>> +/*
>> + * Copyright (C) 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + *
>> + */
>> +
>> +#ifndef _AT91_ADC_H_
>> +#define _AT91_ADC_H_
>> +
>> +/**
>> + * struct at91_adc_data - platform data for ADC driver
>> + * @channels_use:	channels in use on the board as a bitmask
>> + * @vref:		Reference voltage for the ADC in millvolts
> 
> Nitpick, this should write millivolts, no?

Yes, of course :)

> 
>> + */
>> +struct at91_adc_data {
>> +	unsigned long	channels_used;
>> +	u16		vref;
>> +};
>> +
>> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
>> +
>> +#endif
> 
> Regards,
> Alexander


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-12-15 19:37       ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-12-15 19:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/12/2011 11:27, Alexander Stein wrote:
> On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote:
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
>> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
>> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
>>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>> ---
>>  include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
>>  1 files changed, 23 insertions(+), 0 deletions(-)
>>  create mode 100644 include/linux/platform_data/at91_adc.h
>>
>> diff --git a/include/linux/platform_data/at91_adc.h
>> b/include/linux/platform_data/at91_adc.h new file mode 100644
>> index 0000000..bad80b7
>> --- /dev/null
>> +++ b/include/linux/platform_data/at91_adc.h
>> @@ -0,0 +1,23 @@
>> +/*
>> + * Copyright (C) 2011 Free Electrons
>> + *
>> + * Licensed under the GPLv2 or later.
>> + *
>> + */
>> +
>> +#ifndef _AT91_ADC_H_
>> +#define _AT91_ADC_H_
>> +
>> +/**
>> + * struct at91_adc_data - platform data for ADC driver
>> + * @channels_use:	channels in use on the board as a bitmask
>> + * @vref:		Reference voltage for the ADC in millvolts
> 
> Nitpick, this should write millivolts, no?

Yes, of course :)

> 
>> + */
>> +struct at91_adc_data {
>> +	unsigned long	channels_used;
>> +	u16		vref;
>> +};
>> +
>> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
>> +
>> +#endif
> 
> Regards,
> Alexander


-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-12-14 10:01 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
@ 2011-12-14 10:27     ` Alexander Stein
  0 siblings, 0 replies; 131+ messages in thread
From: Alexander Stein @ 2011-12-14 10:27 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Maxime Ripard, linux-iio, Thomas Petazzoni, Patrice Vilchez,
	Nicolas Ferre

On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote:
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> 
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
>  include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
>  1 files changed, 23 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/platform_data/at91_adc.h
> 
> diff --git a/include/linux/platform_data/at91_adc.h
> b/include/linux/platform_data/at91_adc.h new file mode 100644
> index 0000000..bad80b7
> --- /dev/null
> +++ b/include/linux/platform_data/at91_adc.h
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright (C) 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + *
> + */
> +
> +#ifndef _AT91_ADC_H_
> +#define _AT91_ADC_H_
> +
> +/**
> + * struct at91_adc_data - platform data for ADC driver
> + * @channels_use:	channels in use on the board as a bitmask
> + * @vref:		Reference voltage for the ADC in millvolts

Nitpick, this should write millivolts, no?

> + */
> +struct at91_adc_data {
> +	unsigned long	channels_used;
> +	u16		vref;
> +};
> +
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
> +#endif

Regards,
Alexander

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-12-14 10:27     ` Alexander Stein
  0 siblings, 0 replies; 131+ messages in thread
From: Alexander Stein @ 2011-12-14 10:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote:
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> 
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> ---
>  include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
>  1 files changed, 23 insertions(+), 0 deletions(-)
>  create mode 100644 include/linux/platform_data/at91_adc.h
> 
> diff --git a/include/linux/platform_data/at91_adc.h
> b/include/linux/platform_data/at91_adc.h new file mode 100644
> index 0000000..bad80b7
> --- /dev/null
> +++ b/include/linux/platform_data/at91_adc.h
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright (C) 2011 Free Electrons
> + *
> + * Licensed under the GPLv2 or later.
> + *
> + */
> +
> +#ifndef _AT91_ADC_H_
> +#define _AT91_ADC_H_
> +
> +/**
> + * struct at91_adc_data - platform data for ADC driver
> + * @channels_use:	channels in use on the board as a bitmask
> + * @vref:		Reference voltage for the ADC in millvolts

Nitpick, this should write millivolts, no?

> + */
> +struct at91_adc_data {
> +	unsigned long	channels_used;
> +	u16		vref;
> +};
> +
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
> +#endif

Regards,
Alexander

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-12-14 10:01 [PATCH v13] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-12-14 10:01 ` Maxime Ripard
  2011-12-14 10:27     ` Alexander Stein
  0 siblings, 1 reply; 131+ messages in thread
From: Maxime Ripard @ 2011-12-14 10:01 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 include/linux/platform_data/at91_adc.h |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..bad80b7
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+/**
+ * struct at91_adc_data - platform data for ADC driver
+ * @channels_use:	channels in use on the board as a bitmask
+ * @vref:		Reference voltage for the ADC in millvolts
+ */
+struct at91_adc_data {
+	unsigned long	channels_used;
+	u16		vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.4.1

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-12-02 13:17 [PATCH v12] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-12-02 13:17   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-12-02 13:17 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 include/linux/platform_data/at91_adc.h |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..1212c78
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-12-02 13:17   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-12-02 13:17 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 include/linux/platform_data/at91_adc.h |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..1212c78
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.4.1

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-30  9:15   ` Maxime Ripard
@ 2011-11-30  9:45     ` Nicolas Ferre
  -1 siblings, 0 replies; 131+ messages in thread
From: Nicolas Ferre @ 2011-11-30  9:45 UTC (permalink / raw)
  To: Maxime Ripard, Jonathan Cameron
  Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Thomas Petazzoni

Hi Maxime,

On 11/30/2011 10:15 AM, Maxime Ripard :
> Cc: Patrice Vilchez<patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni<thomas.petazzoni@free-electrons.com>

"CC:" lines should not be located here: place them just *after*
"Signed-off-by:" and "Acked-by:" lines.

> Signed-off-by: Maxime Ripard<maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron<jic23@cam.ac.uk>
> Signed-off-by: Nicolas Ferre<nicolas.ferre@atmel.com>
> Acked-by: Jean-Christophe PLAGNIOL-VILLARD<plagnioj@jcrosoft.com>
> ---
>   include/linux/platform_data/at91_adc.h |   36 ++++++++++++++++++++++++++++++++
>   1 files changed, 36 insertions(+), 0 deletions(-)
>   create mode 100644 include/linux/platform_data/at91_adc.h
>
> diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
> new file mode 100644
> index 0000000..1e1813d
> --- /dev/null
> +++ b/include/linux/platform_data/at91_adc.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2011 Free Electrons
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

Can be shorten in:
"Licensed under GPLv2."
"Licensed under the GPL v2."
"License terms: GNU General Public License (GPL) version 2."

> + *
> + */
> +
> +#ifndef _AT91_ADC_H_
> +#define _AT91_ADC_H_
> +
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used bitmask).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board as a bitmask */
> +	unsigned long channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
> +#endif

I think that now it is located at this generic directory, it can easily 
go mainline through IIO subsystem (patches 1 and 2/3): Jonathan, do you 
agree with this?

Best regards,
-- 
Nicolas Ferre

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-30  9:45     ` Nicolas Ferre
  0 siblings, 0 replies; 131+ messages in thread
From: Nicolas Ferre @ 2011-11-30  9:45 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

On 11/30/2011 10:15 AM, Maxime Ripard :
> Cc: Patrice Vilchez<patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni<thomas.petazzoni@free-electrons.com>

"CC:" lines should not be located here: place them just *after*
"Signed-off-by:" and "Acked-by:" lines.

> Signed-off-by: Maxime Ripard<maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron<jic23@cam.ac.uk>
> Signed-off-by: Nicolas Ferre<nicolas.ferre@atmel.com>
> Acked-by: Jean-Christophe PLAGNIOL-VILLARD<plagnioj@jcrosoft.com>
> ---
>   include/linux/platform_data/at91_adc.h |   36 ++++++++++++++++++++++++++++++++
>   1 files changed, 36 insertions(+), 0 deletions(-)
>   create mode 100644 include/linux/platform_data/at91_adc.h
>
> diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
> new file mode 100644
> index 0000000..1e1813d
> --- /dev/null
> +++ b/include/linux/platform_data/at91_adc.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2011 Free Electrons
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

Can be shorten in:
"Licensed under GPLv2."
"Licensed under the GPL v2."
"License terms: GNU General Public License (GPL) version 2."

> + *
> + */
> +
> +#ifndef _AT91_ADC_H_
> +#define _AT91_ADC_H_
> +
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used bitmask).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board as a bitmask */
> +	unsigned long channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
> +#endif

I think that now it is located at this generic directory, it can easily 
go mainline through IIO subsystem (patches 1 and 2/3): Jonathan, do you 
agree with this?

Best regards,
-- 
Nicolas Ferre

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-30  9:14 [PATCH v11] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-11-30  9:15   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-30  9:15 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Patrice Vilchez, Thomas Petazzoni, Nicolas Ferre

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 include/linux/platform_data/at91_adc.h |   36 ++++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..1e1813d
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-30  9:15   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-30  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 include/linux/platform_data/at91_adc.h |   36 ++++++++++++++++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/platform_data/at91_adc.h

diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
new file mode 100644
index 0000000..1e1813d
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 Free Electrons
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _AT91_ADC_H_
+#define _AT91_ADC_H_
+
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
+#endif
-- 
1.7.4.1

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-24 11:27   ` Maxime Ripard
@ 2011-11-24 14:29     ` Jean-Christophe PLAGNIOL-VILLARD
  -1 siblings, 0 replies; 131+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-24 14:29 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Thomas Petazzoni, Patrice Vilchez,
	Nicolas Ferre

On 12:27 Thu 24 Nov     , Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Best Regards,
J.

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-24 14:29     ` Jean-Christophe PLAGNIOL-VILLARD
  0 siblings, 0 replies; 131+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-24 14:29 UTC (permalink / raw)
  To: linux-arm-kernel

On 12:27 Thu 24 Nov     , Maxime Ripard wrote:
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>

Best Regards,
J.

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-24 11:27 [PATCH v9] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-11-24 11:27   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-24 11:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-24 11:27   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-24 11:27 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-18 10:12 [PATCH v8] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-11-18 10:12   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-18 10:12   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-18 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-15 10:54 [PATCH v7] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-11-15 10:54   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-15 10:54 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-15 10:54   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-15 10:54 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-14 17:30 [PATCH v6] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-11-14 17:30   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14 17:30 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-14 17:30   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14 17:30 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-14 10:06   ` Maxime Ripard
@ 2011-11-14 11:29     ` Nicolas Ferre
  -1 siblings, 0 replies; 131+ messages in thread
From: Nicolas Ferre @ 2011-11-14 11:29 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Thomas Petazzoni

On 11/14/2011 11:06 AM, Maxime Ripard :
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>

> ---
>  arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
>  1 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..4f27797 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used bitmask).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board as a bitmask */
> +	unsigned long channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif


-- 
Nicolas Ferre

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-14 11:29     ` Nicolas Ferre
  0 siblings, 0 replies; 131+ messages in thread
From: Nicolas Ferre @ 2011-11-14 11:29 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/14/2011 11:06 AM, Maxime Ripard :
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>

> ---
>  arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
>  1 files changed, 18 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index ed544a0..4f27797 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
>  /* FIXME: this needs a better location, but gets stuff building again */
>  extern int at91_suspend_entering_slow_clock(void);
>  
> +/* ADC */
> +struct at91_adc_data {
> +	/* ADC Clock as specified by the datasheet, in Hz. */
> +	unsigned int adc_clock;
> +	/*
> +	 * Global number of channels available (to specify which channels are
> +	 * indeed used on the board, see the channels_used bitmask).
> +	 */
> +	u8 num_channels;
> +	/* Channels in use on the board as a bitmask */
> +	unsigned long channels_used;
> +	/* Startup time of the ADC, in microseconds. */
> +	u8 startup_time;
> +	/* Reference voltage for the ADC in millivolts */
> +	unsigned short vref;
> +};
> +extern void __init at91_add_device_adc(struct at91_adc_data *data);
> +
>  #endif


-- 
Nicolas Ferre

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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
  2011-11-14 10:06 [PATCHv5] AT91: Add a driver for the ADC Maxime Ripard
@ 2011-11-14 10:06   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14 10:06 UTC (permalink / raw)
  To: linux-arm-kernel, linux-iio
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1


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

* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs
@ 2011-11-14 10:06   ` Maxime Ripard
  0 siblings, 0 replies; 131+ messages in thread
From: Maxime Ripard @ 2011-11-14 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 arch/arm/mach-at91/include/mach/board.h |   18 ++++++++++++++++++
 1 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index ed544a0..4f27797 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
 /* FIXME: this needs a better location, but gets stuff building again */
 extern int at91_suspend_entering_slow_clock(void);
 
+/* ADC */
+struct at91_adc_data {
+	/* ADC Clock as specified by the datasheet, in Hz. */
+	unsigned int adc_clock;
+	/*
+	 * Global number of channels available (to specify which channels are
+	 * indeed used on the board, see the channels_used bitmask).
+	 */
+	u8 num_channels;
+	/* Channels in use on the board as a bitmask */
+	unsigned long channels_used;
+	/* Startup time of the ADC, in microseconds. */
+	u8 startup_time;
+	/* Reference voltage for the ADC in millivolts */
+	unsigned short vref;
+};
+extern void __init at91_add_device_adc(struct at91_adc_data *data);
+
 #endif
-- 
1.7.4.1

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

end of thread, other threads:[~2012-01-16 21:36 UTC | newest]

Thread overview: 131+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-19 16:18 [PATCH] AT91: Add a driver for the ADC Maxime Ripard
2011-10-19 16:18 ` Maxime Ripard
2011-10-19 16:18 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-10-19 16:18   ` Maxime Ripard
2011-10-19 16:18 ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard
2011-10-19 16:18   ` Maxime Ripard
2011-10-19 16:42   ` Jonathan Cameron
2011-10-19 16:42     ` Jonathan Cameron
2011-10-19 18:23     ` Maxime Ripard
2011-10-19 18:23       ` Maxime Ripard
2011-10-20  7:05       ` Thomas Petazzoni
2011-10-20  7:05         ` Thomas Petazzoni
2011-10-20  8:33         ` Jonathan Cameron
2011-10-20  8:33           ` Jonathan Cameron
2011-10-20  8:49           ` Thomas Petazzoni
2011-10-20  8:49             ` Thomas Petazzoni
2011-10-20  9:19             ` Jonathan Cameron
2011-10-20  9:19               ` Jonathan Cameron
2011-10-20  9:52           ` Mark Brown
2011-10-20  9:52             ` Mark Brown
2011-10-20  7:09   ` Lars-Peter Clausen
2011-10-20  7:09     ` Lars-Peter Clausen
2011-10-21 17:54     ` Maxime Ripard
2011-10-21 17:54       ` Maxime Ripard
2011-10-21 17:55       ` Lars-Peter Clausen
2011-10-21 17:55         ` Lars-Peter Clausen
2011-10-23  9:08   ` Jean-Christophe PLAGNIOL-VILLARD
2011-10-23  9:08     ` Jean-Christophe PLAGNIOL-VILLARD
2011-10-24  8:21     ` Maxime Ripard
2011-10-24  8:21       ` Maxime Ripard
2011-10-19 16:18 ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard
2011-10-19 16:18   ` Maxime Ripard
2011-10-20  6:28   ` Alexander Stein
2011-10-20  6:28     ` Alexander Stein
2011-10-21 17:47     ` Maxime Ripard
2011-10-21 17:47       ` Maxime Ripard
2011-10-20  7:14   ` Thomas Petazzoni
2011-10-20  7:14     ` Thomas Petazzoni
2011-11-03 10:11 ` [PATCHv2] AT91: Add a driver for the ADC Maxime Ripard
2011-11-03 10:11   ` Maxime Ripard
2011-11-03 10:11   ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-03 10:11     ` Maxime Ripard
2011-11-03 11:27     ` Linus Walleij
2011-11-03 11:27       ` Linus Walleij
2011-11-03 16:27       ` Maxime Ripard
2011-11-03 16:27         ` Maxime Ripard
2011-11-03 16:38         ` Linus Walleij
2011-11-03 18:05           ` Jean-Christophe PLAGNIOL-VILLARD
2011-11-03 18:05             ` Jean-Christophe PLAGNIOL-VILLARD
2011-11-04 10:27     ` Jonathan Cameron
2011-11-04 10:27       ` Jonathan Cameron
2011-11-04 10:36       ` Jonathan Cameron
2011-11-04 10:36         ` Jonathan Cameron
2011-11-04 10:34     ` Jonathan Cameron
2011-11-04 10:34       ` Jonathan Cameron
2011-11-04 15:22       ` Maxime Ripard
2011-11-04 16:28         ` Jonathan Cameron
2011-11-04 16:28           ` Jonathan Cameron
2011-11-03 10:11   ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard
2011-11-03 10:11     ` Maxime Ripard
2011-11-04 10:27     ` Jonathan Cameron
2011-11-04 10:27       ` Jonathan Cameron
2011-11-04 16:29       ` Maxime Ripard
2011-11-04 16:40         ` Jonathan Cameron
2011-11-03 10:11   ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard
2011-11-03 10:11     ` Maxime Ripard
2011-11-04 10:33     ` Jonathan Cameron
2011-11-04 10:33       ` Jonathan Cameron
2011-11-04 11:25       ` Maxime Ripard
2011-11-04 15:52         ` Linus Walleij
2011-11-04 16:32         ` Jonathan Cameron
2011-11-04 16:32           ` Jonathan Cameron
2011-11-07 16:08 ` [PATCHv3] AT91: Add a driver for the ADC Maxime Ripard
2011-11-07 16:08   ` Maxime Ripard
2011-11-07 16:08   ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-07 16:08     ` Maxime Ripard
2011-11-07 16:27     ` Jonathan Cameron
2011-11-07 16:27       ` Jonathan Cameron
2011-11-08 13:19     ` Thomas Petazzoni
2011-11-08 13:19       ` Thomas Petazzoni
2011-11-07 16:08   ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard
2011-11-07 16:08     ` Maxime Ripard
2011-11-08 13:30     ` Thomas Petazzoni
2011-11-08 13:30       ` Thomas Petazzoni
2011-11-07 16:08   ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard
2011-11-07 16:08     ` Maxime Ripard
2011-11-09 10:19 ` [PATCHv4] AT91: Add a driver for the ADC Maxime Ripard
2011-11-09 10:19   ` Maxime Ripard
2011-11-09 10:19   ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-09 10:19     ` Maxime Ripard
2011-11-09 10:19   ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard
2011-11-09 10:19     ` Maxime Ripard
2011-11-10 17:35     ` Jonathan Cameron
2011-11-10 17:35       ` Jonathan Cameron
2011-11-11 12:34       ` Jonathan Cameron
2011-11-11 12:34         ` Jonathan Cameron
2011-11-14  9:59         ` Maxime Ripard
2011-11-14  9:59           ` Maxime Ripard
2011-11-14  9:06       ` Maxime Ripard
2011-11-14  9:06         ` Maxime Ripard
2011-11-09 10:19   ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard
2011-11-09 10:19     ` Maxime Ripard
2011-11-10 17:37     ` Jonathan Cameron
2011-11-10 17:37       ` Jonathan Cameron
2011-11-14 10:06 [PATCHv5] AT91: Add a driver for the ADC Maxime Ripard
2011-11-14 10:06 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-14 10:06   ` Maxime Ripard
2011-11-14 11:29   ` Nicolas Ferre
2011-11-14 11:29     ` Nicolas Ferre
2011-11-14 17:30 [PATCH v6] AT91: Add a driver for the ADC Maxime Ripard
2011-11-14 17:30 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-14 17:30   ` Maxime Ripard
2011-11-15 10:54 [PATCH v7] AT91: Add a driver for the ADC Maxime Ripard
2011-11-15 10:54 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-15 10:54   ` Maxime Ripard
2011-11-18 10:12 [PATCH v8] AT91: Add a driver for the ADC Maxime Ripard
2011-11-18 10:12 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-18 10:12   ` Maxime Ripard
2011-11-24 11:27 [PATCH v9] AT91: Add a driver for the ADC Maxime Ripard
2011-11-24 11:27 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-24 11:27   ` Maxime Ripard
2011-11-24 14:29   ` Jean-Christophe PLAGNIOL-VILLARD
2011-11-24 14:29     ` Jean-Christophe PLAGNIOL-VILLARD
2011-11-30  9:14 [PATCH v11] AT91: Add a driver for the ADC Maxime Ripard
2011-11-30  9:15 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-11-30  9:15   ` Maxime Ripard
2011-11-30  9:45   ` Nicolas Ferre
2011-11-30  9:45     ` Nicolas Ferre
2011-12-02 13:17 [PATCH v12] AT91: Add a driver for the ADC Maxime Ripard
2011-12-02 13:17 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-12-02 13:17   ` Maxime Ripard
2011-12-14 10:01 [PATCH v13] AT91: Add a driver for the ADC Maxime Ripard
2011-12-14 10:01 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2011-12-14 10:27   ` Alexander Stein
2011-12-14 10:27     ` Alexander Stein
2011-12-15 19:37     ` Maxime Ripard
2011-12-15 19:37       ` Maxime Ripard
2012-01-16 21:36 [PATCH RESEND v13] AT91: Add a driver for the ADC Maxime Ripard
2012-01-16 21:36 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard
2012-01-16 21:36   ` Maxime Ripard

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.