All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2] Add ADC driver for G20 and G45 evaluation boards
@ 2012-04-18 13:33 ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel

Hi all,

Here is the improved version of what I've been posting some time ago.
Some things changed since last version:
  - Support for Hardware triggers for the G20 has been added
  - Support for the AT91SAM9M10G45 board has been added too, both for
    software and hardware triggers
  - The driver is now in drivers/staging/iio to include all these features.

The first three patches should not have changed much since last post, so it
should be pretty ok.

Changes since v1:
  - Added DT bindings
  - Removed the clock_name field and used adc_clk instead. Used CLKDEV_CON_ID
    for the G45.
  - Misc fixes:
    * Added clk_prepare
    * Replaced kcalloc by devm_kzalloc for the trigger allocation

Thanks,
Maxime


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

* [PATCHv2] Add ADC driver for G20 and G45 evaluation boards
@ 2012-04-18 13:33 ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

Here is the improved version of what I've been posting some time ago.
Some things changed since last version:
  - Support for Hardware triggers for the G20 has been added
  - Support for the AT91SAM9M10G45 board has been added too, both for
    software and hardware triggers
  - The driver is now in drivers/staging/iio to include all these features.

The first three patches should not have changed much since last post, so it
should be pretty ok.

Changes since v1:
  - Added DT bindings
  - Removed the clock_name field and used adc_clk instead. Used CLKDEV_CON_ID
    for the G45.
  - Misc fixes:
    * Added clk_prepare
    * Replaced kcalloc by devm_kzalloc for the trigger allocation

Thanks,
Maxime

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

* [PATCH 1/6] ARM: AT91: Add platform data for the AT91 ADCs
  2012-04-18 13:33 ` Maxime Ripard
@ 2012-04-18 13:33   ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni,
	Jean-Christophe PLAGNIOL-VILLARD

The AT91 SoCs often embeds an ADC. This patch adds the needed
platform data to specify the informations required by the driver
to work properly.

For now, we only need the reference voltage and which channels
are available on the board.

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

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 include/linux/platform_data/at91_adc.h |   22 ++++++++++++++++++++++
 1 files changed, 22 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..8b51ee6
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,22 @@
+/*
+ * 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_used:	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] 23+ messages in thread

* [PATCH 1/6] ARM: AT91: Add platform data for the AT91 ADCs
@ 2012-04-18 13:33   ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

The AT91 SoCs often embeds an ADC. This patch adds the needed
platform data to specify the informations required by the driver
to work properly.

For now, we only need the reference voltage and which channels
are available on the board.

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

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 include/linux/platform_data/at91_adc.h |   22 ++++++++++++++++++++++
 1 files changed, 22 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..8b51ee6
--- /dev/null
+++ b/include/linux/platform_data/at91_adc.h
@@ -0,0 +1,22 @@
+/*
+ * 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_used:	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] 23+ messages in thread

* [PATCH 2/6] ARM: AT91: IIO: Add AT91 ADC driver.
  2012-04-18 13:33 ` Maxime Ripard
@ 2012-04-18 13:33   ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel
  Cc: Nicolas Ferre, Jean-Christophe PLAGNIOL-VILLARD, Patrice Vilchez,
	Thomas Petazzoni, Jonathan Cameron

Add the IIO driver for the AT91 ADCs. It only supports and has
been tested on the SAM9G20 evaluation boards, but support for
other boards will come eventually.

This ADC is a multi-channel ADC with support for both hardware
and software triggers.

This first version only supports software triggers.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/staging/iio/adc/Kconfig    |    6 +
 drivers/staging/iio/adc/Makefile   |    1 +
 drivers/staging/iio/adc/at91_adc.c |  396 ++++++++++++++++++++++++++++++++++++
 3 files changed, 403 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/at91_adc.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 592eabd..1494838 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 AT91_ADC
+	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 f83ab95..8cb6d1c 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
+obj-$(CONFIG_AT91_ADC) += at91_adc.o
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
new file mode 100644
index 0000000..6d14277
--- /dev/null
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -0,0 +1,396 @@
+/*
+ * 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/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "../iio.h"
+#include <linux/platform_data/at91_adc.h>
+
+#include <mach/at91_adc.h>
+#include <mach/cpu.h>
+
+/**
+ * struct at91_adc_desc - description of the ADC on the board
+ * @clock:		ADC clock as specified by the datasheet, in Hz.
+ * @num_channels:	global number of channels available on the board (to
+			specify which channels are indeed in use on the
+			board, see the channels_used bitmask in the platform
+			data)
+ * @startup_time:	startup time of the ADC in microseconds
+ */
+struct at91_adc_desc {
+	u32			clock;
+	u8			num_channels;
+	u8			startup_time;
+};
+
+struct at91_adc_state {
+	unsigned long		channels_mask;
+	struct clk		*clk;
+	bool			done;
+	struct at91_adc_desc	*desc;
+	int			irq;
+	u16			last_value;
+	struct mutex		lock;
+	void __iomem		*reg_base;
+	u32			vref_mv;
+	wait_queue_head_t	wq_data_avail;
+};
+
+static struct at91_adc_desc at91_adc_desc_sam9g20 = {
+	.clock = 5000000,
+	.clock_name = "adc_clk",
+	.num_channels = 4,
+	.startup_time = 10,
+};
+
+static int at91_adc_select_soc(struct at91_adc_state *st)
+{
+	if (cpu_is_at91sam9g20()) {
+		st->desc = &at91_adc_desc_sam9g20;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static inline u32 at91_adc_reg_read(struct at91_adc_state *st,
+				    u8 reg)
+{
+	return readl_relaxed(st->reg_base + reg);
+}
+
+static inline void at91_adc_reg_write(struct at91_adc_state *st,
+				      u8 reg,
+				      u32 val)
+{
+	writel_relaxed(val, st->reg_base + reg);
+}
+
+static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct at91_adc_state *st = iio_priv(idev);
+	unsigned int status = at91_adc_reg_read(st, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	if (status & st->channels_mask) {
+		st->done = true;
+		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
+	}
+
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91_adc_channel_init(struct iio_dev *idev,
+				 struct at91_adc_data *pdata)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&pdata->channels_used,
+					   st->desc->num_channels);
+
+	chan_array = devm_kzalloc(&idev->dev,
+				  idev->num_channels * sizeof(struct iio_chan_spec),
+				  GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;
+
+	for_each_set_bit(bit, &pdata->channels_used, st->desc->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->scan_type.storagebits = 16;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		idx++;
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91_adc_channel_remove(struct iio_dev *idev)
+{
+	devm_kfree(&idev->dev, (void*)idev->channels);
+}
+
+static int at91_adc_read_raw(struct iio_dev *idev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	int ret;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91_adc_reg_write(st, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91_adc_reg_write(st, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91_adc_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 == 0)
+			return -ETIMEDOUT;
+		else if (ret < 0)
+			return ret;
+
+		*val = st->last_value;
+
+		at91_adc_reg_write(st, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91_adc_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:
+		*val = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val2 = 0;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91_adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91_adc_read_raw,
+};
+
+static int __devinit at91_adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91_adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
+	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 at91_adc_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->modes = INDIO_DIRECT_MODE;
+	idev->info = &at91_adc_info;
+
+	st = iio_priv(idev);
+	ret = at91_adc_select_soc(st);
+	if (ret) {
+		dev_err(&pdev->dev, "SoC unknown\n");
+		goto error_free_device;
+	}
+
+	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
+	 */
+	at91_adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91_adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91_adc_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;
+	}
+
+	ret = clk_prepare(st->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not prepare the clock.\n");
+		goto error_free_clk;
+	}
+
+	ret = clk_enable(st->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not enable the clock.\n");
+		goto error_unprepare_clk;
+	}
+
+	if (!st->desc->clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_disable_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.
+	 */
+	mstrclk = clk_get_rate(st->clk);
+	prsc = (mstrclk / (2 * st->desc->clock)) - 1;
+
+	if (!st->desc->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_disable_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((st->desc->startup_time * st->desc->clock /
+			  1000000) - 1, 8) / 8;
+	at91_adc_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 = at91_adc_channel_init(idev, pdata);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
+		goto error_disable_clk;
+	}
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+	st->channels_mask = pdata->channels_used;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't register the device.\n");
+		goto error_free_channels;
+	}
+
+	return 0;
+
+error_free_channels:
+	at91_adc_channel_remove(idev);
+error_disable_clk:
+	clk_disable(st->clk);
+error_unprepare_clk:
+	clk_unprepare(st->clk);
+error_free_clk:
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, idev);
+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 at91_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91_adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	at91_adc_channel_remove(idev);
+	clk_disable(st->clk);
+	clk_unprepare(st->clk);
+	clk_put(st->clk);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_free_device(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91_adc_driver = {
+	.probe = at91_adc_probe,
+	.remove = __devexit_p(at91_adc_remove),
+	.driver = {
+		   .name = "at91_adc",
+	},
+};
+
+module_platform_driver(at91_adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.5.4

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

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

Add the IIO driver for the AT91 ADCs. It only supports and has
been tested on the SAM9G20 evaluation boards, but support for
other boards will come eventually.

This ADC is a multi-channel ADC with support for both hardware
and software triggers.

This first version only supports software triggers.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/staging/iio/adc/Kconfig    |    6 +
 drivers/staging/iio/adc/Makefile   |    1 +
 drivers/staging/iio/adc/at91_adc.c |  396 ++++++++++++++++++++++++++++++++++++
 3 files changed, 403 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/at91_adc.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 592eabd..1494838 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 AT91_ADC
+	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 f83ab95..8cb6d1c 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7410) += adt7410.o
 obj-$(CONFIG_AD7280) += ad7280a.o
 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
+obj-$(CONFIG_AT91_ADC) += at91_adc.o
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
new file mode 100644
index 0000000..6d14277
--- /dev/null
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -0,0 +1,396 @@
+/*
+ * 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/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "../iio.h"
+#include <linux/platform_data/at91_adc.h>
+
+#include <mach/at91_adc.h>
+#include <mach/cpu.h>
+
+/**
+ * struct at91_adc_desc - description of the ADC on the board
+ * @clock:		ADC clock as specified by the datasheet, in Hz.
+ * @num_channels:	global number of channels available on the board (to
+			specify which channels are indeed in use on the
+			board, see the channels_used bitmask in the platform
+			data)
+ * @startup_time:	startup time of the ADC in microseconds
+ */
+struct at91_adc_desc {
+	u32			clock;
+	u8			num_channels;
+	u8			startup_time;
+};
+
+struct at91_adc_state {
+	unsigned long		channels_mask;
+	struct clk		*clk;
+	bool			done;
+	struct at91_adc_desc	*desc;
+	int			irq;
+	u16			last_value;
+	struct mutex		lock;
+	void __iomem		*reg_base;
+	u32			vref_mv;
+	wait_queue_head_t	wq_data_avail;
+};
+
+static struct at91_adc_desc at91_adc_desc_sam9g20 = {
+	.clock = 5000000,
+	.clock_name = "adc_clk",
+	.num_channels = 4,
+	.startup_time = 10,
+};
+
+static int at91_adc_select_soc(struct at91_adc_state *st)
+{
+	if (cpu_is_at91sam9g20()) {
+		st->desc = &at91_adc_desc_sam9g20;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static inline u32 at91_adc_reg_read(struct at91_adc_state *st,
+				    u8 reg)
+{
+	return readl_relaxed(st->reg_base + reg);
+}
+
+static inline void at91_adc_reg_write(struct at91_adc_state *st,
+				      u8 reg,
+				      u32 val)
+{
+	writel_relaxed(val, st->reg_base + reg);
+}
+
+static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct at91_adc_state *st = iio_priv(idev);
+	unsigned int status = at91_adc_reg_read(st, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	if (status & st->channels_mask) {
+		st->done = true;
+		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
+	}
+
+	wake_up_interruptible(&st->wq_data_avail);
+
+	return IRQ_HANDLED;
+}
+
+static int at91_adc_channel_init(struct iio_dev *idev,
+				 struct at91_adc_data *pdata)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&pdata->channels_used,
+					   st->desc->num_channels);
+
+	chan_array = devm_kzalloc(&idev->dev,
+				  idev->num_channels * sizeof(struct iio_chan_spec),
+				  GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;
+
+	for_each_set_bit(bit, &pdata->channels_used, st->desc->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->scan_type.storagebits = 16;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		idx++;
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91_adc_channel_remove(struct iio_dev *idev)
+{
+	devm_kfree(&idev->dev, (void*)idev->channels);
+}
+
+static int at91_adc_read_raw(struct iio_dev *idev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	int ret;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91_adc_reg_write(st, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91_adc_reg_write(st, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91_adc_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 == 0)
+			return -ETIMEDOUT;
+		else if (ret < 0)
+			return ret;
+
+		*val = st->last_value;
+
+		at91_adc_reg_write(st, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91_adc_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:
+		*val = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val2 = 0;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91_adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91_adc_read_raw,
+};
+
+static int __devinit at91_adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91_adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
+	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 at91_adc_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->modes = INDIO_DIRECT_MODE;
+	idev->info = &at91_adc_info;
+
+	st = iio_priv(idev);
+	ret = at91_adc_select_soc(st);
+	if (ret) {
+		dev_err(&pdev->dev, "SoC unknown\n");
+		goto error_free_device;
+	}
+
+	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
+	 */
+	at91_adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91_adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91_adc_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;
+	}
+
+	ret = clk_prepare(st->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not prepare the clock.\n");
+		goto error_free_clk;
+	}
+
+	ret = clk_enable(st->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not enable the clock.\n");
+		goto error_unprepare_clk;
+	}
+
+	if (!st->desc->clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_disable_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.
+	 */
+	mstrclk = clk_get_rate(st->clk);
+	prsc = (mstrclk / (2 * st->desc->clock)) - 1;
+
+	if (!st->desc->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_disable_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((st->desc->startup_time * st->desc->clock /
+			  1000000) - 1, 8) / 8;
+	at91_adc_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 = at91_adc_channel_init(idev, pdata);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
+		goto error_disable_clk;
+	}
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+	st->channels_mask = pdata->channels_used;
+
+	ret = iio_device_register(idev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't register the device.\n");
+		goto error_free_channels;
+	}
+
+	return 0;
+
+error_free_channels:
+	at91_adc_channel_remove(idev);
+error_disable_clk:
+	clk_disable(st->clk);
+error_unprepare_clk:
+	clk_unprepare(st->clk);
+error_free_clk:
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, idev);
+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 at91_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91_adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	at91_adc_channel_remove(idev);
+	clk_disable(st->clk);
+	clk_unprepare(st->clk);
+	clk_put(st->clk);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_free_device(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91_adc_driver = {
+	.probe = at91_adc_probe,
+	.remove = __devexit_p(at91_adc_remove),
+	.driver = {
+		   .name = "at91_adc",
+	},
+};
+
+module_platform_driver(at91_adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
-- 
1.7.5.4

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

* [PATCH 3/6] ARM: AT91: Add the ADC to the sam9g20ek board
  2012-04-18 13:33 ` Maxime Ripard
@ 2012-04-18 13:33   ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel
  Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni,
	Jean-Christophe PLAGNIOL-VILLARD

This patch adds platform data for the AT91 ADC driver support for
the AT91SAM9G20-EK board.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   53 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   13 +++++++
 drivers/staging/iio/adc/at91_adc.c       |    1 -
 3 files changed, 66 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 5652dde..758c629 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -17,6 +17,8 @@
 #include <linux/platform_device.h>
 #include <linux/i2c-gpio.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <mach/board.h>
 #include <mach/cpu.h>
 #include <mach/at91sam9260.h>
@@ -1369,6 +1371,57 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
+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		= "at91_adc",
+	.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);
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+#else
+void __init at91_add_device_adc(struct at91_adc_data *data) {}
+#endif
+
 /* -------------------------------------------------------------------- */
 /*
  * 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 8923ec9..2ce4e26 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -32,6 +32,8 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/consumer.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <mach/hardware.h>
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -318,6 +320,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"),
@@ -393,6 +404,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) */
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index 6d14277..9af036a 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -56,7 +56,6 @@ struct at91_adc_state {
 
 static struct at91_adc_desc at91_adc_desc_sam9g20 = {
 	.clock = 5000000,
-	.clock_name = "adc_clk",
 	.num_channels = 4,
 	.startup_time = 10,
 };
-- 
1.7.5.4


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

* [PATCH 3/6] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2012-04-18 13:33   ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds platform data for the AT91 ADC driver support for
the AT91SAM9G20-EK board.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |   53 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   13 +++++++
 drivers/staging/iio/adc/at91_adc.c       |    1 -
 3 files changed, 66 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 5652dde..758c629 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -17,6 +17,8 @@
 #include <linux/platform_device.h>
 #include <linux/i2c-gpio.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <mach/board.h>
 #include <mach/cpu.h>
 #include <mach/at91sam9260.h>
@@ -1369,6 +1371,57 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
+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		= "at91_adc",
+	.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);
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+#else
+void __init at91_add_device_adc(struct at91_adc_data *data) {}
+#endif
+
 /* -------------------------------------------------------------------- */
 /*
  * 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 8923ec9..2ce4e26 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -32,6 +32,8 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/consumer.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <mach/hardware.h>
 #include <asm/setup.h>
 #include <asm/mach-types.h>
@@ -318,6 +320,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"),
@@ -393,6 +404,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) */
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index 6d14277..9af036a 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -56,7 +56,6 @@ struct at91_adc_state {
 
 static struct at91_adc_desc at91_adc_desc_sam9g20 = {
 	.clock = 5000000,
-	.clock_name = "adc_clk",
 	.num_channels = 4,
 	.startup_time = 10,
 };
-- 
1.7.5.4

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

* [PATCH 4/6] IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board
  2012-04-18 13:33 ` Maxime Ripard
@ 2012-04-18 13:33   ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel
  Cc: Jean-Christophe PLAGNIOL-VILLARD, Patrice Vilchez,
	Thomas Petazzoni, Nicolas Ferre

This patch adds the necessary logic and the required platform_data to
support the ADC present in the AT91SAM9M10G45-EK board from Atmel. It
only supports software triggers for now.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam9g45.c         |    2 +
 arch/arm/mach-at91/at91sam9g45_devices.c |   61 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9m10g45ek.c  |   11 +++++
 drivers/staging/iio/adc/at91_adc.c       |   11 +++++
 4 files changed, 85 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index d222f83..c6c35bc 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -242,6 +242,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_ID("pioC", &pioC_clk),
 	CLKDEV_CON_ID("pioD", &pioDE_clk),
 	CLKDEV_CON_ID("pioE", &pioDE_clk),
+	/* Fake adc clock */
+	CLKDEV_CON_ID("adc_clk", &tsc_clk),
 };
 
 static struct clk_lookup usart_clocks_lookups[] = {
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 6b008ae..05bc673 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -19,6 +19,8 @@
 #include <linux/i2c-gpio.h>
 #include <linux/atmel-mci.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <linux/fb.h>
 #include <video/atmel_lcdc.h>
 
@@ -1207,6 +1209,65 @@ void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data) {}
 
 
 /* --------------------------------------------------------------------
+ *  ADC
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9G45_BASE_TSC,
+		.end	= AT91SAM9G45_BASE_TSC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9G45_ID_TSC,
+		.end	= AT91SAM9G45_ID_TSC,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91_adc",
+	.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_gpio_input(AT91_PIN_PD20, 0);
+	if (test_bit(1, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD21, 0);
+	if (test_bit(2, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD22, 0);
+	if (test_bit(3, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD23, 0);
+	if (test_bit(4, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD24, 0);
+	if (test_bit(5, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD25, 0);
+	if (test_bit(6, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD26, 0);
+	if (test_bit(7, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD27, 0);
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+#else
+void __init at91_add_device_adc(struct at91_adc_data *data) {}
+#endif
+
+/* --------------------------------------------------------------------
  *  RTT
  * -------------------------------------------------------------------- */
 
diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
index c88e908..fb122ba 100644
--- a/arch/arm/mach-at91/board-sam9m10g45ek.c
+++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
@@ -27,6 +27,8 @@
 #include <linux/atmel-mci.h>
 #include <linux/delay.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <mach/hardware.h>
 #include <video/atmel_lcdc.h>
 #include <media/soc_camera.h>
@@ -315,6 +317,13 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
 	.ts_sample_hold_time	= 0x0a,
 };
 
+/*
+ * ADCs
+ */
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
+	.vref = 3300,
+};
 
 /*
  * GPIO Buttons
@@ -480,6 +489,8 @@ static void __init ek_board_init(void)
 	at91_add_device_lcdc(&ek_lcdc_data);
 	/* Touch Screen */
 	at91_add_device_tsadcc(&ek_tsadcc_data);
+	/* ADC */
+	at91_add_device_adc(&ek_adc_data);
 	/* Push Buttons */
 	ek_add_device_buttons();
 	/* AC97 */
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index 9af036a..b3517bb 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -54,6 +54,12 @@ struct at91_adc_state {
 	wait_queue_head_t	wq_data_avail;
 };
 
+static struct at91_adc_desc at91_adc_desc_sam9g45 = {
+	.clock = 13200000,
+	.num_channels = 8,
+	.startup_time = 40,
+};
+
 static struct at91_adc_desc at91_adc_desc_sam9g20 = {
 	.clock = 5000000,
 	.num_channels = 4,
@@ -67,6 +73,11 @@ static int at91_adc_select_soc(struct at91_adc_state *st)
 		return 0;
 	}
 
+	if (cpu_is_at91sam9g45()) {
+		st->desc = &at91_adc_desc_sam9g45;
+		return 0;
+	}
+
 	return -ENODEV;
 }
 
-- 
1.7.5.4


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

* [PATCH 4/6] IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board
@ 2012-04-18 13:33   ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds the necessary logic and the required platform_data to
support the ADC present in the AT91SAM9M10G45-EK board from Atmel. It
only supports software triggers for now.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam9g45.c         |    2 +
 arch/arm/mach-at91/at91sam9g45_devices.c |   61 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9m10g45ek.c  |   11 +++++
 drivers/staging/iio/adc/at91_adc.c       |   11 +++++
 4 files changed, 85 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index d222f83..c6c35bc 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -242,6 +242,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
 	CLKDEV_CON_ID("pioC", &pioC_clk),
 	CLKDEV_CON_ID("pioD", &pioDE_clk),
 	CLKDEV_CON_ID("pioE", &pioDE_clk),
+	/* Fake adc clock */
+	CLKDEV_CON_ID("adc_clk", &tsc_clk),
 };
 
 static struct clk_lookup usart_clocks_lookups[] = {
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 6b008ae..05bc673 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -19,6 +19,8 @@
 #include <linux/i2c-gpio.h>
 #include <linux/atmel-mci.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <linux/fb.h>
 #include <video/atmel_lcdc.h>
 
@@ -1207,6 +1209,65 @@ void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data) {}
 
 
 /* --------------------------------------------------------------------
+ *  ADC
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9G45_BASE_TSC,
+		.end	= AT91SAM9G45_BASE_TSC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9G45_ID_TSC,
+		.end	= AT91SAM9G45_ID_TSC,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91_adc",
+	.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_gpio_input(AT91_PIN_PD20, 0);
+	if (test_bit(1, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD21, 0);
+	if (test_bit(2, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD22, 0);
+	if (test_bit(3, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD23, 0);
+	if (test_bit(4, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD24, 0);
+	if (test_bit(5, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD25, 0);
+	if (test_bit(6, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD26, 0);
+	if (test_bit(7, &data->channels_used))
+		at91_set_gpio_input(AT91_PIN_PD27, 0);
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+#else
+void __init at91_add_device_adc(struct at91_adc_data *data) {}
+#endif
+
+/* --------------------------------------------------------------------
  *  RTT
  * -------------------------------------------------------------------- */
 
diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
index c88e908..fb122ba 100644
--- a/arch/arm/mach-at91/board-sam9m10g45ek.c
+++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
@@ -27,6 +27,8 @@
 #include <linux/atmel-mci.h>
 #include <linux/delay.h>
 
+#include <linux/platform_data/at91_adc.h>
+
 #include <mach/hardware.h>
 #include <video/atmel_lcdc.h>
 #include <media/soc_camera.h>
@@ -315,6 +317,13 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
 	.ts_sample_hold_time	= 0x0a,
 };
 
+/*
+ * ADCs
+ */
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
+	.vref = 3300,
+};
 
 /*
  * GPIO Buttons
@@ -480,6 +489,8 @@ static void __init ek_board_init(void)
 	at91_add_device_lcdc(&ek_lcdc_data);
 	/* Touch Screen */
 	at91_add_device_tsadcc(&ek_tsadcc_data);
+	/* ADC */
+	at91_add_device_adc(&ek_adc_data);
 	/* Push Buttons */
 	ek_add_device_buttons();
 	/* AC97 */
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index 9af036a..b3517bb 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -54,6 +54,12 @@ struct at91_adc_state {
 	wait_queue_head_t	wq_data_avail;
 };
 
+static struct at91_adc_desc at91_adc_desc_sam9g45 = {
+	.clock = 13200000,
+	.num_channels = 8,
+	.startup_time = 40,
+};
+
 static struct at91_adc_desc at91_adc_desc_sam9g20 = {
 	.clock = 5000000,
 	.num_channels = 4,
@@ -67,6 +73,11 @@ static int at91_adc_select_soc(struct at91_adc_state *st)
 		return 0;
 	}
 
+	if (cpu_is_at91sam9g45()) {
+		st->desc = &at91_adc_desc_sam9g45;
+		return 0;
+	}
+
 	return -ENODEV;
 }
 
-- 
1.7.5.4

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

* [PATCH 5/6] IIO: AT91: Add support for hardware triggers for the ADC
  2012-04-18 13:33 ` Maxime Ripard
@ 2012-04-18 13:33   ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel
  Cc: Jean-Christophe PLAGNIOL-VILLARD, Patrice Vilchez,
	Thomas Petazzoni, Nicolas Ferre

This patch adds support for the hardware triggers for both the
AT91SAM9G20-EK and AT91SAM9M10G45-EK evaluation board from Atmel.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |    3 +
 arch/arm/mach-at91/at91sam9g45_devices.c |    3 +
 arch/arm/mach-at91/board-sam9g20ek.c     |    1 +
 arch/arm/mach-at91/board-sam9m10g45ek.c  |    1 +
 drivers/staging/iio/adc/Kconfig          |    3 +
 drivers/staging/iio/adc/at91_adc.c       |  389 +++++++++++++++++++++++++++++-
 include/linux/platform_data/at91_adc.h   |    2 +
 7 files changed, 395 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 758c629..a870753 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1415,6 +1415,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
 	if (test_bit(3, &data->channels_used))
 		at91_set_A_periph(AT91_PIN_PC3, 0);
 
+	if (data->use_external)
+		at91_set_A_periph(AT91_PIN_PA22, 0);
+
 	adc_data = *data;
 	platform_device_register(&at91_adc_device);
 }
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 05bc673..4ee18af 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -1260,6 +1260,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
 	if (test_bit(7, &data->channels_used))
 		at91_set_gpio_input(AT91_PIN_PD27, 0);
 
+	if (data->use_external)
+		at91_set_A_periph(AT91_PIN_PD28, 0);
+
 	adc_data = *data;
 	platform_device_register(&at91_adc_device);
 }
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 2ce4e26..3bb6e4d 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -326,6 +326,7 @@ static void __init ek_add_device_buttons(void) {}
 
 static struct at91_adc_data ek_adc_data = {
 	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.use_external = true,
 	.vref = 3300,
 };
 
diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
index fb122ba..192a050 100644
--- a/arch/arm/mach-at91/board-sam9m10g45ek.c
+++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
@@ -322,6 +322,7 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
  */
 static struct at91_adc_data ek_adc_data = {
 	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
+	.use_external = true,
 	.vref = 3300,
 };
 
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 1494838..9ee7ed9 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -172,6 +172,9 @@ config AD7280
 config AT91_ADC
 	tristate "Atmel AT91 ADC"
 	depends on SYSFS && ARCH_AT91
+	select IIO_BUFFER
+	select IIO_SW_RING
+	select IIO_TRIGGER
 	help
 	  Say yes here to build support for Atmel AT91 ADC.
 
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index b3517bb..7c93cde 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -23,9 +23,33 @@
 #include "../iio.h"
 #include <linux/platform_data/at91_adc.h>
 
+#include "../buffer.h"
+#include "../ring_sw.h"
+#include "../trigger.h"
+#include "../trigger_consumer.h"
+
 #include <mach/at91_adc.h>
 #include <mach/cpu.h>
 
+#define AT91_TSADCC_TRGR		(0x08)
+#define AT91_TSADCC_TRGMOD_EXT_RISING	(1 << 0)
+#define AT91_TSADCC_TRGMOD_EXT_FALLING	(1 << 1)
+#define AT91_TSADCC_TRGMOD_EXT_ANY	(AT91_TSADCC_TRGMOD_EXT_RISING | AT91_TSADCC_TRGMOD_EXT_FALLING)
+#define AT91_TSADCC_TRGMOD_CONTINUOUS	(3 << 1)
+
+/**
+ * struct at91_adc_trigger - description of triggers
+ * @name:		name of the trigger advertised to the user
+ * @value:		value to set in the ADC's mode register to enable
+			the trigger
+ * @is_external:	is the trigger relies on an external pin ?
+ */
+struct at91_adc_trigger {
+	char	*name;
+	u8	value;
+	bool	is_external;
+};
+
 /**
  * struct at91_adc_desc - description of the ADC on the board
  * @clock:		ADC clock as specified by the datasheet, in Hz.
@@ -34,36 +58,99 @@
 			board, see the channels_used bitmask in the platform
 			data)
  * @startup_time:	startup time of the ADC in microseconds
+ * @triggers:		array of the triggers available on the board
+ * @trigger_register:	index of the register managing the hardware triggers
  */
 struct at91_adc_desc {
 	u32			clock;
 	u8			num_channels;
 	u8			startup_time;
+	struct at91_adc_trigger	*triggers;
+	u8			trigger_register;
 };
 
 struct at91_adc_state {
+	u16			*buffer;
 	unsigned long		channels_mask;
 	struct clk		*clk;
 	bool			done;
 	struct at91_adc_desc	*desc;
 	int			irq;
+	bool			irq_enabled;
 	u16			last_value;
 	struct mutex		lock;
 	void __iomem		*reg_base;
+	struct iio_trigger	**trig;
 	u32			vref_mv;
 	wait_queue_head_t	wq_data_avail;
 };
 
+static struct at91_adc_trigger at91_adc_trigger_sam9g45[] = {
+	[0] = {
+		.name = "external-rising",
+		.value = AT91_TSADCC_TRGMOD_EXT_RISING,
+		.is_external = true,
+	},
+	[1] = {
+		.name = "external-falling",
+		.value = AT91_TSADCC_TRGMOD_EXT_FALLING,
+		.is_external = true,
+	},
+	[2] = {
+		.name = "external-any",
+		.value = AT91_TSADCC_TRGMOD_EXT_ANY,
+		.is_external = true,
+	},
+	[3] = {
+		.name = "continuous",
+		.value = AT91_TSADCC_TRGMOD_CONTINUOUS,
+		.is_external = false,
+	},
+	[4] = {
+		.name = NULL,
+	},
+};
+
 static struct at91_adc_desc at91_adc_desc_sam9g45 = {
 	.clock = 13200000,
 	.num_channels = 8,
 	.startup_time = 40,
+	.triggers = at91_adc_trigger_sam9g45,
+	.trigger_register = AT91_TSADCC_TRGR,
+};
+
+static struct at91_adc_trigger at91_adc_trigger_sam9g20[] = {
+	[0] = {
+		.name = "timer-counter-0",
+		.value = AT91_ADC_TRGSEL_TC0 | AT91_ADC_TRGEN,
+		.is_external = false,
+	},
+	[1] = {
+		.name = "timer-counter-1",
+		.value = AT91_ADC_TRGSEL_TC1 | AT91_ADC_TRGEN,
+		.is_external = false,
+	},
+	[2] = {
+		.name = "timer-counter-2",
+		.value = AT91_ADC_TRGSEL_TC2 | AT91_ADC_TRGEN,
+		.is_external = false,
+	},
+	[3] = {
+		.name = "external",
+		.value = AT91_ADC_TRGSEL_EXTERNAL | AT91_ADC_TRGEN,
+		.is_external = true,
+	},
+	[4] = {
+		.name = NULL,
+	},
 };
 
 static struct at91_adc_desc at91_adc_desc_sam9g20 = {
 	.clock = 5000000,
 	.num_channels = 4,
 	.startup_time = 10,
+	.triggers = at91_adc_trigger_sam9g20,
+	.trigger_register = AT91_ADC_MR,
 };
 
 static int at91_adc_select_soc(struct at91_adc_state *st)
@@ -94,6 +181,48 @@ static inline void at91_adc_reg_write(struct at91_adc_state *st,
 	writel_relaxed(val, st->reg_base + reg);
 }
 
+static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *idev = pf->indio_dev;
+	struct at91_adc_state *st = iio_priv(idev);
+	struct iio_buffer *buffer = idev->buffer;
+	int len = 0;
+
+	if (!bitmap_empty(idev->active_scan_mask, idev->masklength)) {
+		int i, j;
+		for (i = 0, j = 0;
+		     i < bitmap_weight(idev->active_scan_mask,
+				       idev->masklength);
+		     i++) {
+			j = find_next_bit(buffer->scan_mask,
+					  idev->masklength,
+					  j);
+			st->buffer[i] = at91_adc_reg_read(st, AT91_ADC_CHR(j));
+			j++;
+			len += 2;
+		}
+	}
+
+	if (buffer->scan_timestamp) {
+		s64 *timestamp = (s64 *)((u8 *)st->buffer + ALIGN(len,
+								  sizeof(s64)));
+		*timestamp = pf->timestamp;
+	}
+
+	buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
+
+	iio_trigger_notify_done(idev->trig);
+	st->irq_enabled = true;
+
+	/* Needed to ACK the DRDY interruption */
+	at91_adc_reg_read(st, AT91_ADC_LCDR);
+
+	enable_irq(st->irq);
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 {
 	struct iio_dev *idev = private;
@@ -103,13 +232,16 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 	if (!(status & AT91_ADC_DRDY))
 		return IRQ_HANDLED;
 
-	if (status & st->channels_mask) {
-		st->done = true;
+	if (iio_buffer_enabled(idev)) {
+		disable_irq_nosync(irq);
+		st->irq_enabled = false;
+		iio_trigger_poll(idev->trig, iio_get_time_ns());
+	} else {
 		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
+		st->done = true;
+		wake_up_interruptible(&st->wq_data_avail);
 	}
 
-	wake_up_interruptible(&st->wq_data_avail);
-
 	return IRQ_HANDLED;
 }
 
@@ -121,10 +253,10 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 	int bit, idx = 0;
 
 	idev->num_channels = bitmap_weight(&pdata->channels_used,
-					   st->desc->num_channels);
+					   st->desc->num_channels) + 1;
 
 	chan_array = devm_kzalloc(&idev->dev,
-				  idev->num_channels * sizeof(struct iio_chan_spec),
+				  (idev->num_channels + 1) * sizeof(struct iio_chan_spec),
 				  GFP_KERNEL);
 
 	if (chan_array == NULL)
@@ -135,6 +267,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 		chan->type = IIO_VOLTAGE;
 		chan->indexed = 1;
 		chan->channel = bit;
+		chan->scan_index = idx;
 		chan->scan_type.sign = 'u';
 		chan->scan_type.realbits = 10;
 		chan->scan_type.storagebits = 16;
@@ -142,6 +275,13 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 		idx++;
 	}
 
+	(chan_array + idx)->type = IIO_TIMESTAMP;
+	(chan_array + idx)->channel = -1;
+	(chan_array + idx)->scan_index = idx;
+	(chan_array + idx)->scan_type.sign = 's';
+	(chan_array + idx)->scan_type.realbits = 64;
+	(chan_array + idx)->scan_type.storagebits = 64;
+
 	idev->channels = chan_array;
 	return idev->num_channels;
 }
@@ -151,6 +291,223 @@ static void at91_adc_channel_remove(struct iio_dev *idev)
 	devm_kfree(&idev->dev, (void*)idev->channels);
 }
 
+static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
+					     struct at91_adc_trigger *triggers,
+					     const char *trigger_name)
+{
+	u8 value = 0;
+	int i;
+
+	for (i = 0; (triggers + i) != NULL; i++) {
+		char *name = kasprintf(GFP_KERNEL,
+				"%s-dev%d-%s",
+				idev->name,
+				idev->id,
+				triggers[i].name);
+		if (name == NULL)
+			return -ENOMEM;
+
+		if (strcmp(trigger_name, name) == 0) {
+			value = triggers[i].value;
+			kfree(name);
+			break;
+		}
+
+		kfree(name);
+	}
+
+	return value;
+}
+
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *idev = trig->private_data;
+	struct at91_adc_state *st = iio_priv(idev);
+	struct iio_buffer *buffer = idev->buffer;
+	u32 status = at91_adc_reg_read(st, st->desc->trigger_register);
+	u8 value;
+	u8 bit;
+
+	value = at91_adc_get_trigger_value_by_name(idev,
+						   st->desc->triggers,
+						   idev->trig->name);
+	if (value == 0)
+		return -EINVAL;
+
+	if (state) {
+		size_t datasize = buffer->access->get_bytes_per_datum(buffer);
+
+		st->buffer = kmalloc(datasize, GFP_KERNEL);
+		if (st->buffer == NULL)
+			return -ENOMEM;
+
+		at91_adc_reg_write(st,
+				   st->desc->trigger_register,
+				   status | value);
+
+		for_each_set_bit(bit, buffer->scan_mask,
+				 st->desc->num_channels) {
+			struct iio_chan_spec const *chan = idev->channels + bit;
+			at91_adc_reg_write(st, AT91_ADC_CHER,
+					   AT91_ADC_CH(chan->channel));
+		}
+
+		at91_adc_reg_write(st, AT91_ADC_IER, AT91_ADC_DRDY);
+
+	} else {
+		at91_adc_reg_write(st, AT91_ADC_IDR, AT91_ADC_DRDY);
+
+		at91_adc_reg_write(st, st->desc->trigger_register,
+				   status & ~value);
+
+		for_each_set_bit(bit, buffer->scan_mask,
+				 st->desc->num_channels) {
+			struct iio_chan_spec const *chan = idev->channels + bit;
+			at91_adc_reg_write(st, AT91_ADC_CHDR,
+					   AT91_ADC_CH(chan->channel));
+		}
+		kfree(st->buffer);
+	}
+
+	return 0;
+}
+
+static const struct iio_trigger_ops at91_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &at91_adc_configure_trigger,
+};
+
+static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
+						     struct at91_adc_trigger *trigger)
+{
+	struct iio_trigger *trig = iio_allocate_trigger("%s-dev%d-%s",
+							idev->name,
+							idev->id,
+							trigger->name);
+	int ret;
+
+	if (trig == NULL)
+		return NULL;
+
+	trig->dev.parent = idev->dev.parent;
+	trig->private_data = idev;
+	trig->ops = &at91_adc_trigger_ops;
+
+	ret = iio_trigger_register(trig);
+	if (ret < 0)
+		return NULL;
+
+	return trig;
+}
+
+static int at91_adc_trigger_init(struct iio_dev *idev,
+				 struct at91_adc_data *pdata)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	int i, ret;
+
+	st->trig = kcalloc(sizeof(st->desc->triggers),
+			   sizeof(st->trig),
+			   GFP_KERNEL);
+
+	if (st->trig == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
+		if (st->desc->triggers[i].is_external && !(pdata->use_external))
+			continue;
+
+		st->trig[i] = at91_adc_allocate_trigger(idev,
+							st->desc->triggers + i);
+		if (st->trig[i] == NULL) {
+			dev_err(&idev->dev,
+				"Could not allocate trigger %d\n", i);
+			ret = -ENOMEM;
+			goto error_trigger;
+		}
+	}
+
+	return 0;
+
+error_trigger:
+	for (i--; i >= 0; i--) {
+		iio_trigger_unregister(st->trig[i]);
+		iio_free_trigger(st->trig[i]);
+	}
+	kfree(st->trig);
+error_ret:
+	return ret;
+}
+
+static void at91_adc_trigger_remove(struct iio_dev *idev)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	int i;
+
+	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
+		iio_trigger_unregister(st->trig[i]);
+		iio_free_trigger(st->trig[i]);
+	}
+
+	kfree(st->trig);
+}
+
+static const struct iio_buffer_setup_ops at91_adc_buffer_ops = {
+	.preenable = &iio_sw_buffer_preenable,
+	.postenable = &iio_triggered_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+};
+
+static int at91_adc_buffer_init(struct iio_dev *idev)
+{
+	int ret;
+
+	idev->buffer = iio_sw_rb_allocate(idev);
+	if (!idev->buffer) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	idev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
+					    &at91_adc_trigger_handler,
+					    IRQF_ONESHOT,
+					    idev,
+					    "%s-consumer%d",
+					    idev->name,
+					    idev->id);
+	if (idev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_pollfunc;
+	}
+
+	idev->setup_ops = &at91_adc_buffer_ops;
+	idev->modes |= INDIO_BUFFER_TRIGGERED;
+
+	ret = iio_buffer_register(idev,
+				  idev->channels,
+				  idev->num_channels);
+	if (ret)
+		goto error_register;
+
+	return 0;
+
+error_register:
+	iio_dealloc_pollfunc(idev->pollfunc);
+error_pollfunc:
+	iio_sw_rb_free(idev->buffer);
+error_ret:
+	return ret;
+}
+
+static void at91_adc_buffer_remove(struct iio_dev *idev)
+{
+	iio_buffer_unregister(idev);
+	iio_dealloc_pollfunc(idev->pollfunc);
+	iio_sw_rb_free(idev->buffer);
+}
+
 static int at91_adc_read_raw(struct iio_dev *idev,
 			     struct iio_chan_spec const *chan,
 			     int *val, int *val2, long mask)
@@ -344,14 +701,30 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	st->vref_mv = pdata->vref;
 	st->channels_mask = pdata->channels_used;
 
+	ret = at91_adc_buffer_init(idev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+		goto error_free_channels;
+	}
+
+	ret = at91_adc_trigger_init(idev, pdata);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+		goto error_unregister_buffer;
+	}
+
 	ret = iio_device_register(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't register the device.\n");
-		goto error_free_channels;
+		goto error_remove_triggers;
 	}
 
 	return 0;
 
+error_remove_triggers:
+	at91_adc_trigger_remove(idev);
+error_unregister_buffer:
+	at91_adc_buffer_remove(idev);
 error_free_channels:
 	at91_adc_channel_remove(idev);
 error_disable_clk:
@@ -379,6 +752,8 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
 	struct at91_adc_state *st = iio_priv(idev);
 
 	iio_device_unregister(idev);
+	at91_adc_trigger_remove(idev);
+	at91_adc_buffer_remove(idev);
 	at91_adc_channel_remove(idev);
 	clk_disable(st->clk);
 	clk_unprepare(st->clk);
diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
index 8b51ee6..8750d80 100644
--- a/include/linux/platform_data/at91_adc.h
+++ b/include/linux/platform_data/at91_adc.h
@@ -10,10 +10,12 @@
 /**
  * struct at91_adc_data - platform data for ADC driver
  * @channels_used:	channels in use on the board as a bitmask
+ * @use_external:	does the board has external triggers availables
  * @vref:		Reference voltage for the ADC in millivolts
  */
 struct at91_adc_data {
 	unsigned long	channels_used;
+	bool		use_external;
 	u16		vref;
 };
 
-- 
1.7.5.4


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

* [PATCH 5/6] IIO: AT91: Add support for hardware triggers for the ADC
@ 2012-04-18 13:33   ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the hardware triggers for both the
AT91SAM9G20-EK and AT91SAM9M10G45-EK evaluation board from Atmel.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
---
 arch/arm/mach-at91/at91sam9260_devices.c |    3 +
 arch/arm/mach-at91/at91sam9g45_devices.c |    3 +
 arch/arm/mach-at91/board-sam9g20ek.c     |    1 +
 arch/arm/mach-at91/board-sam9m10g45ek.c  |    1 +
 drivers/staging/iio/adc/Kconfig          |    3 +
 drivers/staging/iio/adc/at91_adc.c       |  389 +++++++++++++++++++++++++++++-
 include/linux/platform_data/at91_adc.h   |    2 +
 7 files changed, 395 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 758c629..a870753 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1415,6 +1415,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
 	if (test_bit(3, &data->channels_used))
 		at91_set_A_periph(AT91_PIN_PC3, 0);
 
+	if (data->use_external)
+		at91_set_A_periph(AT91_PIN_PA22, 0);
+
 	adc_data = *data;
 	platform_device_register(&at91_adc_device);
 }
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 05bc673..4ee18af 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -1260,6 +1260,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
 	if (test_bit(7, &data->channels_used))
 		at91_set_gpio_input(AT91_PIN_PD27, 0);
 
+	if (data->use_external)
+		at91_set_A_periph(AT91_PIN_PD28, 0);
+
 	adc_data = *data;
 	platform_device_register(&at91_adc_device);
 }
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 2ce4e26..3bb6e4d 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -326,6 +326,7 @@ static void __init ek_add_device_buttons(void) {}
 
 static struct at91_adc_data ek_adc_data = {
 	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.use_external = true,
 	.vref = 3300,
 };
 
diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
index fb122ba..192a050 100644
--- a/arch/arm/mach-at91/board-sam9m10g45ek.c
+++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
@@ -322,6 +322,7 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
  */
 static struct at91_adc_data ek_adc_data = {
 	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
+	.use_external = true,
 	.vref = 3300,
 };
 
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 1494838..9ee7ed9 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -172,6 +172,9 @@ config AD7280
 config AT91_ADC
 	tristate "Atmel AT91 ADC"
 	depends on SYSFS && ARCH_AT91
+	select IIO_BUFFER
+	select IIO_SW_RING
+	select IIO_TRIGGER
 	help
 	  Say yes here to build support for Atmel AT91 ADC.
 
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index b3517bb..7c93cde 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -23,9 +23,33 @@
 #include "../iio.h"
 #include <linux/platform_data/at91_adc.h>
 
+#include "../buffer.h"
+#include "../ring_sw.h"
+#include "../trigger.h"
+#include "../trigger_consumer.h"
+
 #include <mach/at91_adc.h>
 #include <mach/cpu.h>
 
+#define AT91_TSADCC_TRGR		(0x08)
+#define AT91_TSADCC_TRGMOD_EXT_RISING	(1 << 0)
+#define AT91_TSADCC_TRGMOD_EXT_FALLING	(1 << 1)
+#define AT91_TSADCC_TRGMOD_EXT_ANY	(AT91_TSADCC_TRGMOD_EXT_RISING | AT91_TSADCC_TRGMOD_EXT_FALLING)
+#define AT91_TSADCC_TRGMOD_CONTINUOUS	(3 << 1)
+
+/**
+ * struct at91_adc_trigger - description of triggers
+ * @name:		name of the trigger advertised to the user
+ * @value:		value to set in the ADC's mode register to enable
+			the trigger
+ * @is_external:	is the trigger relies on an external pin ?
+ */
+struct at91_adc_trigger {
+	char	*name;
+	u8	value;
+	bool	is_external;
+};
+
 /**
  * struct at91_adc_desc - description of the ADC on the board
  * @clock:		ADC clock as specified by the datasheet, in Hz.
@@ -34,36 +58,99 @@
 			board, see the channels_used bitmask in the platform
 			data)
  * @startup_time:	startup time of the ADC in microseconds
+ * @triggers:		array of the triggers available on the board
+ * @trigger_register:	index of the register managing the hardware triggers
  */
 struct at91_adc_desc {
 	u32			clock;
 	u8			num_channels;
 	u8			startup_time;
+	struct at91_adc_trigger	*triggers;
+	u8			trigger_register;
 };
 
 struct at91_adc_state {
+	u16			*buffer;
 	unsigned long		channels_mask;
 	struct clk		*clk;
 	bool			done;
 	struct at91_adc_desc	*desc;
 	int			irq;
+	bool			irq_enabled;
 	u16			last_value;
 	struct mutex		lock;
 	void __iomem		*reg_base;
+	struct iio_trigger	**trig;
 	u32			vref_mv;
 	wait_queue_head_t	wq_data_avail;
 };
 
+static struct at91_adc_trigger at91_adc_trigger_sam9g45[] = {
+	[0] = {
+		.name = "external-rising",
+		.value = AT91_TSADCC_TRGMOD_EXT_RISING,
+		.is_external = true,
+	},
+	[1] = {
+		.name = "external-falling",
+		.value = AT91_TSADCC_TRGMOD_EXT_FALLING,
+		.is_external = true,
+	},
+	[2] = {
+		.name = "external-any",
+		.value = AT91_TSADCC_TRGMOD_EXT_ANY,
+		.is_external = true,
+	},
+	[3] = {
+		.name = "continuous",
+		.value = AT91_TSADCC_TRGMOD_CONTINUOUS,
+		.is_external = false,
+	},
+	[4] = {
+		.name = NULL,
+	},
+};
+
 static struct at91_adc_desc at91_adc_desc_sam9g45 = {
 	.clock = 13200000,
 	.num_channels = 8,
 	.startup_time = 40,
+	.triggers = at91_adc_trigger_sam9g45,
+	.trigger_register = AT91_TSADCC_TRGR,
+};
+
+static struct at91_adc_trigger at91_adc_trigger_sam9g20[] = {
+	[0] = {
+		.name = "timer-counter-0",
+		.value = AT91_ADC_TRGSEL_TC0 | AT91_ADC_TRGEN,
+		.is_external = false,
+	},
+	[1] = {
+		.name = "timer-counter-1",
+		.value = AT91_ADC_TRGSEL_TC1 | AT91_ADC_TRGEN,
+		.is_external = false,
+	},
+	[2] = {
+		.name = "timer-counter-2",
+		.value = AT91_ADC_TRGSEL_TC2 | AT91_ADC_TRGEN,
+		.is_external = false,
+	},
+	[3] = {
+		.name = "external",
+		.value = AT91_ADC_TRGSEL_EXTERNAL | AT91_ADC_TRGEN,
+		.is_external = true,
+	},
+	[4] = {
+		.name = NULL,
+	},
 };
 
 static struct at91_adc_desc at91_adc_desc_sam9g20 = {
 	.clock = 5000000,
 	.num_channels = 4,
 	.startup_time = 10,
+	.triggers = at91_adc_trigger_sam9g20,
+	.trigger_register = AT91_ADC_MR,
 };
 
 static int at91_adc_select_soc(struct at91_adc_state *st)
@@ -94,6 +181,48 @@ static inline void at91_adc_reg_write(struct at91_adc_state *st,
 	writel_relaxed(val, st->reg_base + reg);
 }
 
+static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *idev = pf->indio_dev;
+	struct at91_adc_state *st = iio_priv(idev);
+	struct iio_buffer *buffer = idev->buffer;
+	int len = 0;
+
+	if (!bitmap_empty(idev->active_scan_mask, idev->masklength)) {
+		int i, j;
+		for (i = 0, j = 0;
+		     i < bitmap_weight(idev->active_scan_mask,
+				       idev->masklength);
+		     i++) {
+			j = find_next_bit(buffer->scan_mask,
+					  idev->masklength,
+					  j);
+			st->buffer[i] = at91_adc_reg_read(st, AT91_ADC_CHR(j));
+			j++;
+			len += 2;
+		}
+	}
+
+	if (buffer->scan_timestamp) {
+		s64 *timestamp = (s64 *)((u8 *)st->buffer + ALIGN(len,
+								  sizeof(s64)));
+		*timestamp = pf->timestamp;
+	}
+
+	buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
+
+	iio_trigger_notify_done(idev->trig);
+	st->irq_enabled = true;
+
+	/* Needed to ACK the DRDY interruption */
+	at91_adc_reg_read(st, AT91_ADC_LCDR);
+
+	enable_irq(st->irq);
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 {
 	struct iio_dev *idev = private;
@@ -103,13 +232,16 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 	if (!(status & AT91_ADC_DRDY))
 		return IRQ_HANDLED;
 
-	if (status & st->channels_mask) {
-		st->done = true;
+	if (iio_buffer_enabled(idev)) {
+		disable_irq_nosync(irq);
+		st->irq_enabled = false;
+		iio_trigger_poll(idev->trig, iio_get_time_ns());
+	} else {
 		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
+		st->done = true;
+		wake_up_interruptible(&st->wq_data_avail);
 	}
 
-	wake_up_interruptible(&st->wq_data_avail);
-
 	return IRQ_HANDLED;
 }
 
@@ -121,10 +253,10 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 	int bit, idx = 0;
 
 	idev->num_channels = bitmap_weight(&pdata->channels_used,
-					   st->desc->num_channels);
+					   st->desc->num_channels) + 1;
 
 	chan_array = devm_kzalloc(&idev->dev,
-				  idev->num_channels * sizeof(struct iio_chan_spec),
+				  (idev->num_channels + 1) * sizeof(struct iio_chan_spec),
 				  GFP_KERNEL);
 
 	if (chan_array == NULL)
@@ -135,6 +267,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 		chan->type = IIO_VOLTAGE;
 		chan->indexed = 1;
 		chan->channel = bit;
+		chan->scan_index = idx;
 		chan->scan_type.sign = 'u';
 		chan->scan_type.realbits = 10;
 		chan->scan_type.storagebits = 16;
@@ -142,6 +275,13 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 		idx++;
 	}
 
+	(chan_array + idx)->type = IIO_TIMESTAMP;
+	(chan_array + idx)->channel = -1;
+	(chan_array + idx)->scan_index = idx;
+	(chan_array + idx)->scan_type.sign = 's';
+	(chan_array + idx)->scan_type.realbits = 64;
+	(chan_array + idx)->scan_type.storagebits = 64;
+
 	idev->channels = chan_array;
 	return idev->num_channels;
 }
@@ -151,6 +291,223 @@ static void at91_adc_channel_remove(struct iio_dev *idev)
 	devm_kfree(&idev->dev, (void*)idev->channels);
 }
 
+static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
+					     struct at91_adc_trigger *triggers,
+					     const char *trigger_name)
+{
+	u8 value = 0;
+	int i;
+
+	for (i = 0; (triggers + i) != NULL; i++) {
+		char *name = kasprintf(GFP_KERNEL,
+				"%s-dev%d-%s",
+				idev->name,
+				idev->id,
+				triggers[i].name);
+		if (name == NULL)
+			return -ENOMEM;
+
+		if (strcmp(trigger_name, name) == 0) {
+			value = triggers[i].value;
+			kfree(name);
+			break;
+		}
+
+		kfree(name);
+	}
+
+	return value;
+}
+
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *idev = trig->private_data;
+	struct at91_adc_state *st = iio_priv(idev);
+	struct iio_buffer *buffer = idev->buffer;
+	u32 status = at91_adc_reg_read(st, st->desc->trigger_register);
+	u8 value;
+	u8 bit;
+
+	value = at91_adc_get_trigger_value_by_name(idev,
+						   st->desc->triggers,
+						   idev->trig->name);
+	if (value == 0)
+		return -EINVAL;
+
+	if (state) {
+		size_t datasize = buffer->access->get_bytes_per_datum(buffer);
+
+		st->buffer = kmalloc(datasize, GFP_KERNEL);
+		if (st->buffer == NULL)
+			return -ENOMEM;
+
+		at91_adc_reg_write(st,
+				   st->desc->trigger_register,
+				   status | value);
+
+		for_each_set_bit(bit, buffer->scan_mask,
+				 st->desc->num_channels) {
+			struct iio_chan_spec const *chan = idev->channels + bit;
+			at91_adc_reg_write(st, AT91_ADC_CHER,
+					   AT91_ADC_CH(chan->channel));
+		}
+
+		at91_adc_reg_write(st, AT91_ADC_IER, AT91_ADC_DRDY);
+
+	} else {
+		at91_adc_reg_write(st, AT91_ADC_IDR, AT91_ADC_DRDY);
+
+		at91_adc_reg_write(st, st->desc->trigger_register,
+				   status & ~value);
+
+		for_each_set_bit(bit, buffer->scan_mask,
+				 st->desc->num_channels) {
+			struct iio_chan_spec const *chan = idev->channels + bit;
+			at91_adc_reg_write(st, AT91_ADC_CHDR,
+					   AT91_ADC_CH(chan->channel));
+		}
+		kfree(st->buffer);
+	}
+
+	return 0;
+}
+
+static const struct iio_trigger_ops at91_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = &at91_adc_configure_trigger,
+};
+
+static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
+						     struct at91_adc_trigger *trigger)
+{
+	struct iio_trigger *trig = iio_allocate_trigger("%s-dev%d-%s",
+							idev->name,
+							idev->id,
+							trigger->name);
+	int ret;
+
+	if (trig == NULL)
+		return NULL;
+
+	trig->dev.parent = idev->dev.parent;
+	trig->private_data = idev;
+	trig->ops = &at91_adc_trigger_ops;
+
+	ret = iio_trigger_register(trig);
+	if (ret < 0)
+		return NULL;
+
+	return trig;
+}
+
+static int at91_adc_trigger_init(struct iio_dev *idev,
+				 struct at91_adc_data *pdata)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	int i, ret;
+
+	st->trig = kcalloc(sizeof(st->desc->triggers),
+			   sizeof(st->trig),
+			   GFP_KERNEL);
+
+	if (st->trig == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
+		if (st->desc->triggers[i].is_external && !(pdata->use_external))
+			continue;
+
+		st->trig[i] = at91_adc_allocate_trigger(idev,
+							st->desc->triggers + i);
+		if (st->trig[i] == NULL) {
+			dev_err(&idev->dev,
+				"Could not allocate trigger %d\n", i);
+			ret = -ENOMEM;
+			goto error_trigger;
+		}
+	}
+
+	return 0;
+
+error_trigger:
+	for (i--; i >= 0; i--) {
+		iio_trigger_unregister(st->trig[i]);
+		iio_free_trigger(st->trig[i]);
+	}
+	kfree(st->trig);
+error_ret:
+	return ret;
+}
+
+static void at91_adc_trigger_remove(struct iio_dev *idev)
+{
+	struct at91_adc_state *st = iio_priv(idev);
+	int i;
+
+	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
+		iio_trigger_unregister(st->trig[i]);
+		iio_free_trigger(st->trig[i]);
+	}
+
+	kfree(st->trig);
+}
+
+static const struct iio_buffer_setup_ops at91_adc_buffer_ops = {
+	.preenable = &iio_sw_buffer_preenable,
+	.postenable = &iio_triggered_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+};
+
+static int at91_adc_buffer_init(struct iio_dev *idev)
+{
+	int ret;
+
+	idev->buffer = iio_sw_rb_allocate(idev);
+	if (!idev->buffer) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	idev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
+					    &at91_adc_trigger_handler,
+					    IRQF_ONESHOT,
+					    idev,
+					    "%s-consumer%d",
+					    idev->name,
+					    idev->id);
+	if (idev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_pollfunc;
+	}
+
+	idev->setup_ops = &at91_adc_buffer_ops;
+	idev->modes |= INDIO_BUFFER_TRIGGERED;
+
+	ret = iio_buffer_register(idev,
+				  idev->channels,
+				  idev->num_channels);
+	if (ret)
+		goto error_register;
+
+	return 0;
+
+error_register:
+	iio_dealloc_pollfunc(idev->pollfunc);
+error_pollfunc:
+	iio_sw_rb_free(idev->buffer);
+error_ret:
+	return ret;
+}
+
+static void at91_adc_buffer_remove(struct iio_dev *idev)
+{
+	iio_buffer_unregister(idev);
+	iio_dealloc_pollfunc(idev->pollfunc);
+	iio_sw_rb_free(idev->buffer);
+}
+
 static int at91_adc_read_raw(struct iio_dev *idev,
 			     struct iio_chan_spec const *chan,
 			     int *val, int *val2, long mask)
@@ -344,14 +701,30 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	st->vref_mv = pdata->vref;
 	st->channels_mask = pdata->channels_used;
 
+	ret = at91_adc_buffer_init(idev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+		goto error_free_channels;
+	}
+
+	ret = at91_adc_trigger_init(idev, pdata);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+		goto error_unregister_buffer;
+	}
+
 	ret = iio_device_register(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't register the device.\n");
-		goto error_free_channels;
+		goto error_remove_triggers;
 	}
 
 	return 0;
 
+error_remove_triggers:
+	at91_adc_trigger_remove(idev);
+error_unregister_buffer:
+	at91_adc_buffer_remove(idev);
 error_free_channels:
 	at91_adc_channel_remove(idev);
 error_disable_clk:
@@ -379,6 +752,8 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
 	struct at91_adc_state *st = iio_priv(idev);
 
 	iio_device_unregister(idev);
+	at91_adc_trigger_remove(idev);
+	at91_adc_buffer_remove(idev);
 	at91_adc_channel_remove(idev);
 	clk_disable(st->clk);
 	clk_unprepare(st->clk);
diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
index 8b51ee6..8750d80 100644
--- a/include/linux/platform_data/at91_adc.h
+++ b/include/linux/platform_data/at91_adc.h
@@ -10,10 +10,12 @@
 /**
  * struct at91_adc_data - platform data for ADC driver
  * @channels_used:	channels in use on the board as a bitmask
+ * @use_external:	does the board has external triggers availables
  * @vref:		Reference voltage for the ADC in millivolts
  */
 struct at91_adc_data {
 	unsigned long	channels_used;
+	bool		use_external;
 	u16		vref;
 };
 
-- 
1.7.5.4

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

* [PATCH 6/6] IIO: AT91: Add DT support to at91_adc driver
  2012-04-18 13:33 ` Maxime Ripard
@ 2012-04-18 13:33   ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   21 +++++
 drivers/staging/iio/adc/at91_adc.c                 |   88 +++++++++++++++-----
 2 files changed, 87 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/atmel-adc.txt

diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt
new file mode 100644
index 0000000..ac62847
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt
@@ -0,0 +1,21 @@
+* AT91's Analog to Digital Converter (ADC)
+
+Required properties:
+  - compatible: Should be "atmel,at91-adc"
+  - reg: Should contain ADC registers location and length
+  - interrupts: Should contain the IRQ line for the ADC
+  - atmel,adc-use-external: Boolean to enable the muxing and the use of external
+    triggers
+  - atmel,adc-vref: Reference voltage in millivolts for the conversions
+  - atmel,adc-channels-used: Bitmask of the channels muxed and enable for this
+    device
+
+Examples:
+	adc@f804c000 {
+		compatible = "atmel,at91-adc";
+		reg = <0xf804c000 0x100>;
+		interrupts = <19 4>;
+		atmel,adc-use-external;
+		atmel,adc-channels-used = <0xff>;
+		atmel,adc-vref = <3300>;
+	};
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index 7c93cde..d655cba 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -15,6 +15,8 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -81,6 +83,7 @@ struct at91_adc_state {
 	struct mutex		lock;
 	void __iomem		*reg_base;
 	struct iio_trigger	**trig;
+	bool			use_external;
 	u32			vref_mv;
 	wait_queue_head_t	wq_data_avail;
 };
@@ -245,14 +248,13 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 	return IRQ_HANDLED;
 }
 
-static int at91_adc_channel_init(struct iio_dev *idev,
-				 struct at91_adc_data *pdata)
+static int at91_adc_channel_init(struct iio_dev *idev)
 {
 	struct at91_adc_state *st = iio_priv(idev);
 	struct iio_chan_spec *chan_array;
 	int bit, idx = 0;
 
-	idev->num_channels = bitmap_weight(&pdata->channels_used,
+	idev->num_channels = bitmap_weight(&st->channels_mask,
 					   st->desc->num_channels) + 1;
 
 	chan_array = devm_kzalloc(&idev->dev,
@@ -262,7 +264,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 	if (chan_array == NULL)
 		return -ENOMEM;
 
-	for_each_set_bit(bit, &pdata->channels_used, st->desc->num_channels) {
+	for_each_set_bit(bit, &st->channels_mask, st->desc->num_channels) {
 		struct iio_chan_spec *chan = chan_array + idx;
 		chan->type = IIO_VOLTAGE;
 		chan->indexed = 1;
@@ -400,8 +402,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
 	return trig;
 }
 
-static int at91_adc_trigger_init(struct iio_dev *idev,
-				 struct at91_adc_data *pdata)
+static int at91_adc_trigger_init(struct iio_dev *idev)
 {
 	struct at91_adc_state *st = iio_priv(idev);
 	int i, ret;
@@ -416,7 +417,7 @@ static int at91_adc_trigger_init(struct iio_dev *idev,
 	}
 
 	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
-		if (st->desc->triggers[i].is_external && !(pdata->use_external))
+		if (st->desc->triggers[i].is_external && !(st->use_external))
 			continue;
 
 		st->trig[i] = at91_adc_allocate_trigger(idev,
@@ -555,6 +556,39 @@ static int at91_adc_read_raw(struct iio_dev *idev,
 	return -EINVAL;
 }
 
+static int at91_adc_probe_dt(struct at91_adc_state *st,
+			     struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+
+	if (!node)
+		return -EINVAL;
+
+	if (of_get_property(node, "atmel,adc-use-external", NULL))
+		st->use_external = true;
+
+	of_property_read_u32(node, "atmel,adc-channels-used",
+			     (u32*)&st->channels_mask);
+	of_property_read_u32(node, "atmel,adc-vref", &st->vref_mv);
+
+	return 0;
+}
+
+static int at91_adc_probe_pdata(struct at91_adc_state *st,
+				struct platform_device *pdev)
+{
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+
+	st->use_external = pdata->use_external;
+	st->vref_mv = pdata->vref;
+	st->channels_mask = pdata->channels_used;
+
+	return 0;
+}
+
 static const struct iio_info at91_adc_info = {
 	.driver_module = THIS_MODULE,
 	.read_raw = &at91_adc_read_raw,
@@ -567,12 +601,25 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	struct iio_dev *idev;
 	struct at91_adc_state *st;
 	struct resource *res;
-	struct at91_adc_data *pdata = pdev->dev.platform_data;
 
-	if (!pdata) {
+	idev = iio_allocate_device(sizeof(struct at91_adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	st = iio_priv(idev);
+
+	if (pdev->dev.of_node) {
+		ret = at91_adc_probe_dt(st, pdev);
+	} else {
+		ret = at91_adc_probe_pdata(st, pdev);
+	}
+
+	if (ret) {
 		dev_err(&pdev->dev, "No platform data available.\n");
 		ret = -EINVAL;
-		goto error_ret;
+		goto error_free_device;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -582,12 +629,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 		goto error_ret;
 	}
 
-	idev = iio_allocate_device(sizeof(struct at91_adc_state));
-	if (idev == NULL) {
-		ret = -ENOMEM;
-		goto error_ret;
-	}
-
 	platform_set_drvdata(pdev, idev);
 
 	idev->dev.parent = &pdev->dev;
@@ -595,7 +636,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	idev->modes = INDIO_DIRECT_MODE;
 	idev->info = &at91_adc_info;
 
-	st = iio_priv(idev);
 	ret = at91_adc_select_soc(st);
 	if (ret) {
 		dev_err(&pdev->dev, "SoC unknown\n");
@@ -689,7 +729,7 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
 
 	/* Setup the ADC channels available on the board */
-	ret = at91_adc_channel_init(idev, pdata);
+	ret = at91_adc_channel_init(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
 		goto error_disable_clk;
@@ -698,16 +738,13 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	init_waitqueue_head(&st->wq_data_avail);
 	mutex_init(&st->lock);
 
-	st->vref_mv = pdata->vref;
-	st->channels_mask = pdata->channels_used;
-
 	ret = at91_adc_buffer_init(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
 		goto error_free_channels;
 	}
 
-	ret = at91_adc_trigger_init(idev, pdata);
+	ret = at91_adc_trigger_init(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
 		goto error_unregister_buffer;
@@ -766,11 +803,18 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id at91_adc_dt_ids[] = {
+	{ .compatible = "atmel,at91-adc" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
+
 static struct platform_driver at91_adc_driver = {
 	.probe = at91_adc_probe,
 	.remove = __devexit_p(at91_adc_remove),
 	.driver = {
 		   .name = "at91_adc",
+		   .of_match_table = of_match_ptr(at91_adc_dt_ids),
 	},
 };
 
-- 
1.7.5.4


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

* [PATCH 6/6] IIO: AT91: Add DT support to at91_adc driver
@ 2012-04-18 13:33   ` Maxime Ripard
  0 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-18 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 .../devicetree/bindings/arm/atmel-adc.txt          |   21 +++++
 drivers/staging/iio/adc/at91_adc.c                 |   88 +++++++++++++++-----
 2 files changed, 87 insertions(+), 22 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/atmel-adc.txt

diff --git a/Documentation/devicetree/bindings/arm/atmel-adc.txt b/Documentation/devicetree/bindings/arm/atmel-adc.txt
new file mode 100644
index 0000000..ac62847
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/atmel-adc.txt
@@ -0,0 +1,21 @@
+* AT91's Analog to Digital Converter (ADC)
+
+Required properties:
+  - compatible: Should be "atmel,at91-adc"
+  - reg: Should contain ADC registers location and length
+  - interrupts: Should contain the IRQ line for the ADC
+  - atmel,adc-use-external: Boolean to enable the muxing and the use of external
+    triggers
+  - atmel,adc-vref: Reference voltage in millivolts for the conversions
+  - atmel,adc-channels-used: Bitmask of the channels muxed and enable for this
+    device
+
+Examples:
+	adc at f804c000 {
+		compatible = "atmel,at91-adc";
+		reg = <0xf804c000 0x100>;
+		interrupts = <19 4>;
+		atmel,adc-use-external;
+		atmel,adc-channels-used = <0xff>;
+		atmel,adc-vref = <3300>;
+	};
diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
index 7c93cde..d655cba 100644
--- a/drivers/staging/iio/adc/at91_adc.c
+++ b/drivers/staging/iio/adc/at91_adc.c
@@ -15,6 +15,8 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -81,6 +83,7 @@ struct at91_adc_state {
 	struct mutex		lock;
 	void __iomem		*reg_base;
 	struct iio_trigger	**trig;
+	bool			use_external;
 	u32			vref_mv;
 	wait_queue_head_t	wq_data_avail;
 };
@@ -245,14 +248,13 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
 	return IRQ_HANDLED;
 }
 
-static int at91_adc_channel_init(struct iio_dev *idev,
-				 struct at91_adc_data *pdata)
+static int at91_adc_channel_init(struct iio_dev *idev)
 {
 	struct at91_adc_state *st = iio_priv(idev);
 	struct iio_chan_spec *chan_array;
 	int bit, idx = 0;
 
-	idev->num_channels = bitmap_weight(&pdata->channels_used,
+	idev->num_channels = bitmap_weight(&st->channels_mask,
 					   st->desc->num_channels) + 1;
 
 	chan_array = devm_kzalloc(&idev->dev,
@@ -262,7 +264,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
 	if (chan_array == NULL)
 		return -ENOMEM;
 
-	for_each_set_bit(bit, &pdata->channels_used, st->desc->num_channels) {
+	for_each_set_bit(bit, &st->channels_mask, st->desc->num_channels) {
 		struct iio_chan_spec *chan = chan_array + idx;
 		chan->type = IIO_VOLTAGE;
 		chan->indexed = 1;
@@ -400,8 +402,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
 	return trig;
 }
 
-static int at91_adc_trigger_init(struct iio_dev *idev,
-				 struct at91_adc_data *pdata)
+static int at91_adc_trigger_init(struct iio_dev *idev)
 {
 	struct at91_adc_state *st = iio_priv(idev);
 	int i, ret;
@@ -416,7 +417,7 @@ static int at91_adc_trigger_init(struct iio_dev *idev,
 	}
 
 	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
-		if (st->desc->triggers[i].is_external && !(pdata->use_external))
+		if (st->desc->triggers[i].is_external && !(st->use_external))
 			continue;
 
 		st->trig[i] = at91_adc_allocate_trigger(idev,
@@ -555,6 +556,39 @@ static int at91_adc_read_raw(struct iio_dev *idev,
 	return -EINVAL;
 }
 
+static int at91_adc_probe_dt(struct at91_adc_state *st,
+			     struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+
+	if (!node)
+		return -EINVAL;
+
+	if (of_get_property(node, "atmel,adc-use-external", NULL))
+		st->use_external = true;
+
+	of_property_read_u32(node, "atmel,adc-channels-used",
+			     (u32*)&st->channels_mask);
+	of_property_read_u32(node, "atmel,adc-vref", &st->vref_mv);
+
+	return 0;
+}
+
+static int at91_adc_probe_pdata(struct at91_adc_state *st,
+				struct platform_device *pdev)
+{
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+
+	st->use_external = pdata->use_external;
+	st->vref_mv = pdata->vref;
+	st->channels_mask = pdata->channels_used;
+
+	return 0;
+}
+
 static const struct iio_info at91_adc_info = {
 	.driver_module = THIS_MODULE,
 	.read_raw = &at91_adc_read_raw,
@@ -567,12 +601,25 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	struct iio_dev *idev;
 	struct at91_adc_state *st;
 	struct resource *res;
-	struct at91_adc_data *pdata = pdev->dev.platform_data;
 
-	if (!pdata) {
+	idev = iio_allocate_device(sizeof(struct at91_adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	st = iio_priv(idev);
+
+	if (pdev->dev.of_node) {
+		ret = at91_adc_probe_dt(st, pdev);
+	} else {
+		ret = at91_adc_probe_pdata(st, pdev);
+	}
+
+	if (ret) {
 		dev_err(&pdev->dev, "No platform data available.\n");
 		ret = -EINVAL;
-		goto error_ret;
+		goto error_free_device;
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -582,12 +629,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 		goto error_ret;
 	}
 
-	idev = iio_allocate_device(sizeof(struct at91_adc_state));
-	if (idev == NULL) {
-		ret = -ENOMEM;
-		goto error_ret;
-	}
-
 	platform_set_drvdata(pdev, idev);
 
 	idev->dev.parent = &pdev->dev;
@@ -595,7 +636,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	idev->modes = INDIO_DIRECT_MODE;
 	idev->info = &at91_adc_info;
 
-	st = iio_priv(idev);
 	ret = at91_adc_select_soc(st);
 	if (ret) {
 		dev_err(&pdev->dev, "SoC unknown\n");
@@ -689,7 +729,7 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
 
 	/* Setup the ADC channels available on the board */
-	ret = at91_adc_channel_init(idev, pdata);
+	ret = at91_adc_channel_init(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
 		goto error_disable_clk;
@@ -698,16 +738,13 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
 	init_waitqueue_head(&st->wq_data_avail);
 	mutex_init(&st->lock);
 
-	st->vref_mv = pdata->vref;
-	st->channels_mask = pdata->channels_used;
-
 	ret = at91_adc_buffer_init(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
 		goto error_free_channels;
 	}
 
-	ret = at91_adc_trigger_init(idev, pdata);
+	ret = at91_adc_trigger_init(idev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
 		goto error_unregister_buffer;
@@ -766,11 +803,18 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id at91_adc_dt_ids[] = {
+	{ .compatible = "atmel,at91-adc" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
+
 static struct platform_driver at91_adc_driver = {
 	.probe = at91_adc_probe,
 	.remove = __devexit_p(at91_adc_remove),
 	.driver = {
 		   .name = "at91_adc",
+		   .of_match_table = of_match_ptr(at91_adc_dt_ids),
 	},
 };
 
-- 
1.7.5.4

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

* Re: [PATCH 2/6] ARM: AT91: IIO: Add AT91 ADC driver.
  2012-04-18 13:33   ` Maxime Ripard
@ 2012-04-18 17:52     ` Jonathan Cameron
  -1 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 17:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-iio, linux-arm-kernel, Nicolas Ferre,
	Jean-Christophe PLAGNIOL-VILLARD, Patrice Vilchez,
	Thomas Petazzoni, Jonathan Cameron

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> Add the IIO driver for the AT91 ADCs. It only supports and has
> been tested on the SAM9G20 evaluation boards, but support for
> other boards will come eventually.
> 
> This ADC is a multi-channel ADC with support for both hardware
> and software triggers.
> 
> This first version only supports software triggers.
> 
Basically looks fine.  I think there are a few places where
devm stuff could be used but I don't really care.

Other than that, there is a series winging its way to Greg
that changes the abi.  The fixups that will be needed to work
with that are inline below.  Shouldn't cause compile issues
but will break the driver..

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

> 
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig    |    6 +
>  drivers/staging/iio/adc/Makefile   |    1 +
>  drivers/staging/iio/adc/at91_adc.c |  396 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 403 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/at91_adc.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 592eabd..1494838 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 AT91_ADC
> +	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 f83ab95..8cb6d1c 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -38,3 +38,4 @@ obj-$(CONFIG_ADT7310) += adt7310.o
>  obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
>  obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
> +obj-$(CONFIG_AT91_ADC) += at91_adc.o
Ideally pretend those files were in alphabetical order and slot in where
ever will cause
the smallest possible patch to fix the rest of the file ;)

> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> new file mode 100644
> index 0000000..6d14277
> --- /dev/null
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -0,0 +1,396 @@
> +/*
> + * 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/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "../iio.h"
> +#include <linux/platform_data/at91_adc.h>
> +
> +#include <mach/at91_adc.h>
> +#include <mach/cpu.h>
> +
> +/**
> + * struct at91_adc_desc - description of the ADC on the board
> + * @clock:		ADC clock as specified by the datasheet, in Hz.
> + * @num_channels:	global number of channels available on the board (to
> +			specify which channels are indeed in use on the
> +			board, see the channels_used bitmask in the platform
> +			data)
> + * @startup_time:	startup time of the ADC in microseconds
> + */
> +struct at91_adc_desc {
> +	u32			clock;
> +	u8			num_channels;
> +	u8			startup_time;
> +};
> +
> +struct at91_adc_state {
> +	unsigned long		channels_mask;
> +	struct clk		*clk;
> +	bool			done;
> +	struct at91_adc_desc	*desc;
> +	int			irq;
> +	u16			last_value;
> +	struct mutex		lock;
> +	void __iomem		*reg_base;
> +	u32			vref_mv;
> +	wait_queue_head_t	wq_data_avail;
> +};
> +
> +static struct at91_adc_desc at91_adc_desc_sam9g20 = {
> +	.clock = 5000000,
> +	.clock_name = "adc_clk",
> +	.num_channels = 4,
> +	.startup_time = 10,
> +};
> +
> +static int at91_adc_select_soc(struct at91_adc_state *st)
> +{
> +	if (cpu_is_at91sam9g20()) {
> +		st->desc = &at91_adc_desc_sam9g20;
> +		return 0;
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +static inline u32 at91_adc_reg_read(struct at91_adc_state *st,
> +				    u8 reg)
> +{
> +	return readl_relaxed(st->reg_base + reg);
> +}
> +
> +static inline void at91_adc_reg_write(struct at91_adc_state *st,
> +				      u8 reg,
> +				      u32 val)
> +{
> +	writel_relaxed(val, st->reg_base + reg);
> +}
> +
> +static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct at91_adc_state *st = iio_priv(idev);
> +	unsigned int status = at91_adc_reg_read(st, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	if (status & st->channels_mask) {
> +		st->done = true;
> +		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
> +	}
> +
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91_adc_channel_init(struct iio_dev *idev,
> +				 struct at91_adc_data *pdata)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	struct iio_chan_spec *chan_array;
> +	int bit, idx = 0;
> +
> +	idev->num_channels = bitmap_weight(&pdata->channels_used,
> +					   st->desc->num_channels);
> +
> +	chan_array = devm_kzalloc(&idev->dev,
> +				  idev->num_channels * sizeof(struct iio_chan_spec),
> +				  GFP_KERNEL);
> +
> +	if (chan_array == NULL)
> +		return -ENOMEM;
> +
> +	for_each_set_bit(bit, &pdata->channels_used, st->desc->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->scan_type.storagebits = 16;
> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
Unfortunately you have probably (unless Greg doesn't merge it)
crossed with a series that means you'll need to add
IIO_CHAN_INFO_RAW_SEPARATE_BIT to the chan->info_mask or
no raw reading attributes will get registered.
Sorry, we had reason to make this optional and obviously
the patch only drove it into drivers that were in tree.

> +		idx++;
> +	}
> +
> +	idev->channels = chan_array;
> +	return idev->num_channels;
> +}
> +
> +static void at91_adc_channel_remove(struct iio_dev *idev)
> +{
> +	devm_kfree(&idev->dev, (void*)idev->channels);
> +}
> +
> +static int at91_adc_read_raw(struct iio_dev *idev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val, int *val2, long mask)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	int ret;
> +
> +	switch (mask) {
> +	case 0:
The patch mentioned above that is winging it's way to Greg
also gives you IIO_CHAN_INFO_RAW which is cleaner than case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91_adc_reg_write(st, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91_adc_reg_write(st, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91_adc_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 == 0)
> +			return -ETIMEDOUT;
> +		else if (ret < 0)
> +			return ret;
> +
> +		*val = st->last_value;
> +
> +		at91_adc_reg_write(st, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91_adc_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:
> +		*val = (st->vref_mv * 1000) >> chan->scan_type.realbits;
> +		*val2 = 0;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91_adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91_adc_read_raw,
> +};
> +
> +static int __devinit at91_adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91_adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_ret;
> +	}
> +
> +	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 at91_adc_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->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91_adc_info;
> +
> +	st = iio_priv(idev);
> +	ret = at91_adc_select_soc(st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "SoC unknown\n");
> +		goto error_free_device;
> +	}
> +
> +	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
> +	 */
> +	at91_adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91_adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91_adc_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;
> +	}
> +
> +	ret = clk_prepare(st->clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not prepare the clock.\n");
> +		goto error_free_clk;
> +	}
> +
> +	ret = clk_enable(st->clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not enable the clock.\n");
> +		goto error_unprepare_clk;
> +	}
> +
> +	if (!st->desc->clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_disable_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.
> +	 */
> +	mstrclk = clk_get_rate(st->clk);
> +	prsc = (mstrclk / (2 * st->desc->clock)) - 1;
> +
> +	if (!st->desc->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_disable_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((st->desc->startup_time * st->desc->clock /
> +			  1000000) - 1, 8) / 8;
> +	at91_adc_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 = at91_adc_channel_init(idev, pdata);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
> +		goto error_disable_clk;
> +	}
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	st->vref_mv = pdata->vref;
> +	st->channels_mask = pdata->channels_used;
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't register the device.\n");
> +		goto error_free_channels;
> +	}
> +
> +	return 0;
> +
> +error_free_channels:
> +	at91_adc_channel_remove(idev);
> +error_disable_clk:
> +	clk_disable(st->clk);
> +error_unprepare_clk:
> +	clk_unprepare(st->clk);
> +error_free_clk:
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, idev);
> +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 at91_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91_adc_state *st = iio_priv(idev);
> +
> +	iio_device_unregister(idev);
> +	at91_adc_channel_remove(idev);
> +	clk_disable(st->clk);
> +	clk_unprepare(st->clk);
> +	clk_put(st->clk);
> +	free_irq(st->irq, idev);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_free_device(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91_adc_driver = {
> +	.probe = at91_adc_probe,
> +	.remove = __devexit_p(at91_adc_remove),
> +	.driver = {
> +		   .name = "at91_adc",
> +	},
> +};
> +
> +module_platform_driver(at91_adc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");

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

* [PATCH 2/6] ARM: AT91: IIO: Add AT91 ADC driver.
@ 2012-04-18 17:52     ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 17:52 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> Add the IIO driver for the AT91 ADCs. It only supports and has
> been tested on the SAM9G20 evaluation boards, but support for
> other boards will come eventually.
> 
> This ADC is a multi-channel ADC with support for both hardware
> and software triggers.
> 
> This first version only supports software triggers.
> 
Basically looks fine.  I think there are a few places where
devm stuff could be used but I don't really care.

Other than that, there is a series winging its way to Greg
that changes the abi.  The fixups that will be needed to work
with that are inline below.  Shouldn't cause compile issues
but will break the driver..

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

> 
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig    |    6 +
>  drivers/staging/iio/adc/Makefile   |    1 +
>  drivers/staging/iio/adc/at91_adc.c |  396 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 403 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/at91_adc.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 592eabd..1494838 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 AT91_ADC
> +	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 f83ab95..8cb6d1c 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -38,3 +38,4 @@ obj-$(CONFIG_ADT7310) += adt7310.o
>  obj-$(CONFIG_ADT7410) += adt7410.o
>  obj-$(CONFIG_AD7280) += ad7280a.o
>  obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
> +obj-$(CONFIG_AT91_ADC) += at91_adc.o
Ideally pretend those files were in alphabetical order and slot in where
ever will cause
the smallest possible patch to fix the rest of the file ;)

> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> new file mode 100644
> index 0000000..6d14277
> --- /dev/null
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -0,0 +1,396 @@
> +/*
> + * 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/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "../iio.h"
> +#include <linux/platform_data/at91_adc.h>
> +
> +#include <mach/at91_adc.h>
> +#include <mach/cpu.h>
> +
> +/**
> + * struct at91_adc_desc - description of the ADC on the board
> + * @clock:		ADC clock as specified by the datasheet, in Hz.
> + * @num_channels:	global number of channels available on the board (to
> +			specify which channels are indeed in use on the
> +			board, see the channels_used bitmask in the platform
> +			data)
> + * @startup_time:	startup time of the ADC in microseconds
> + */
> +struct at91_adc_desc {
> +	u32			clock;
> +	u8			num_channels;
> +	u8			startup_time;
> +};
> +
> +struct at91_adc_state {
> +	unsigned long		channels_mask;
> +	struct clk		*clk;
> +	bool			done;
> +	struct at91_adc_desc	*desc;
> +	int			irq;
> +	u16			last_value;
> +	struct mutex		lock;
> +	void __iomem		*reg_base;
> +	u32			vref_mv;
> +	wait_queue_head_t	wq_data_avail;
> +};
> +
> +static struct at91_adc_desc at91_adc_desc_sam9g20 = {
> +	.clock = 5000000,
> +	.clock_name = "adc_clk",
> +	.num_channels = 4,
> +	.startup_time = 10,
> +};
> +
> +static int at91_adc_select_soc(struct at91_adc_state *st)
> +{
> +	if (cpu_is_at91sam9g20()) {
> +		st->desc = &at91_adc_desc_sam9g20;
> +		return 0;
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +static inline u32 at91_adc_reg_read(struct at91_adc_state *st,
> +				    u8 reg)
> +{
> +	return readl_relaxed(st->reg_base + reg);
> +}
> +
> +static inline void at91_adc_reg_write(struct at91_adc_state *st,
> +				      u8 reg,
> +				      u32 val)
> +{
> +	writel_relaxed(val, st->reg_base + reg);
> +}
> +
> +static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
> +{
> +	struct iio_dev *idev = private;
> +	struct at91_adc_state *st = iio_priv(idev);
> +	unsigned int status = at91_adc_reg_read(st, AT91_ADC_SR);
> +
> +	if (!(status & AT91_ADC_DRDY))
> +		return IRQ_HANDLED;
> +
> +	if (status & st->channels_mask) {
> +		st->done = true;
> +		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
> +	}
> +
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int at91_adc_channel_init(struct iio_dev *idev,
> +				 struct at91_adc_data *pdata)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	struct iio_chan_spec *chan_array;
> +	int bit, idx = 0;
> +
> +	idev->num_channels = bitmap_weight(&pdata->channels_used,
> +					   st->desc->num_channels);
> +
> +	chan_array = devm_kzalloc(&idev->dev,
> +				  idev->num_channels * sizeof(struct iio_chan_spec),
> +				  GFP_KERNEL);
> +
> +	if (chan_array == NULL)
> +		return -ENOMEM;
> +
> +	for_each_set_bit(bit, &pdata->channels_used, st->desc->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->scan_type.storagebits = 16;
> +		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
Unfortunately you have probably (unless Greg doesn't merge it)
crossed with a series that means you'll need to add
IIO_CHAN_INFO_RAW_SEPARATE_BIT to the chan->info_mask or
no raw reading attributes will get registered.
Sorry, we had reason to make this optional and obviously
the patch only drove it into drivers that were in tree.

> +		idx++;
> +	}
> +
> +	idev->channels = chan_array;
> +	return idev->num_channels;
> +}
> +
> +static void at91_adc_channel_remove(struct iio_dev *idev)
> +{
> +	devm_kfree(&idev->dev, (void*)idev->channels);
> +}
> +
> +static int at91_adc_read_raw(struct iio_dev *idev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val, int *val2, long mask)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	int ret;
> +
> +	switch (mask) {
> +	case 0:
The patch mentioned above that is winging it's way to Greg
also gives you IIO_CHAN_INFO_RAW which is cleaner than case 0:
> +		mutex_lock(&st->lock);
> +
> +		at91_adc_reg_write(st, AT91_ADC_CHER,
> +				  AT91_ADC_CH(chan->channel));
> +		at91_adc_reg_write(st, AT91_ADC_IER,
> +				  AT91_ADC_EOC(chan->channel));
> +		at91_adc_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 == 0)
> +			return -ETIMEDOUT;
> +		else if (ret < 0)
> +			return ret;
> +
> +		*val = st->last_value;
> +
> +		at91_adc_reg_write(st, AT91_ADC_CHDR,
> +				  AT91_ADC_CH(chan->channel));
> +		at91_adc_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:
> +		*val = (st->vref_mv * 1000) >> chan->scan_type.realbits;
> +		*val2 = 0;
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		break;
> +	}
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info at91_adc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &at91_adc_read_raw,
> +};
> +
> +static int __devinit at91_adc_probe(struct platform_device *pdev)
> +{
> +	unsigned int prsc, mstrclk, ticks;
> +	int ret;
> +	struct iio_dev *idev;
> +	struct at91_adc_state *st;
> +	struct resource *res;
> +	struct at91_adc_data *pdata = pdev->dev.platform_data;
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data available.\n");
> +		ret = -EINVAL;
> +		goto error_ret;
> +	}
> +
> +	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 at91_adc_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->modes = INDIO_DIRECT_MODE;
> +	idev->info = &at91_adc_info;
> +
> +	st = iio_priv(idev);
> +	ret = at91_adc_select_soc(st);
> +	if (ret) {
> +		dev_err(&pdev->dev, "SoC unknown\n");
> +		goto error_free_device;
> +	}
> +
> +	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
> +	 */
> +	at91_adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
> +	at91_adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
> +	ret = request_irq(st->irq,
> +			  at91_adc_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;
> +	}
> +
> +	ret = clk_prepare(st->clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not prepare the clock.\n");
> +		goto error_free_clk;
> +	}
> +
> +	ret = clk_enable(st->clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Could not enable the clock.\n");
> +		goto error_unprepare_clk;
> +	}
> +
> +	if (!st->desc->clock) {
> +		dev_err(&pdev->dev, "No ADCClock available.\n");
> +		ret = -EINVAL;
> +		goto error_disable_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.
> +	 */
> +	mstrclk = clk_get_rate(st->clk);
> +	prsc = (mstrclk / (2 * st->desc->clock)) - 1;
> +
> +	if (!st->desc->startup_time) {
> +		dev_err(&pdev->dev, "No startup time available.\n");
> +		ret = -EINVAL;
> +		goto error_disable_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((st->desc->startup_time * st->desc->clock /
> +			  1000000) - 1, 8) / 8;
> +	at91_adc_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 = at91_adc_channel_init(idev, pdata);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
> +		goto error_disable_clk;
> +	}
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +	mutex_init(&st->lock);
> +
> +	st->vref_mv = pdata->vref;
> +	st->channels_mask = pdata->channels_used;
> +
> +	ret = iio_device_register(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't register the device.\n");
> +		goto error_free_channels;
> +	}
> +
> +	return 0;
> +
> +error_free_channels:
> +	at91_adc_channel_remove(idev);
> +error_disable_clk:
> +	clk_disable(st->clk);
> +error_unprepare_clk:
> +	clk_unprepare(st->clk);
> +error_free_clk:
> +	clk_put(st->clk);
> +error_free_irq:
> +	free_irq(st->irq, idev);
> +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 at91_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *idev = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	struct at91_adc_state *st = iio_priv(idev);
> +
> +	iio_device_unregister(idev);
> +	at91_adc_channel_remove(idev);
> +	clk_disable(st->clk);
> +	clk_unprepare(st->clk);
> +	clk_put(st->clk);
> +	free_irq(st->irq, idev);
> +	iounmap(st->reg_base);
> +	release_mem_region(res->start, resource_size(res));
> +	iio_free_device(idev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver at91_adc_driver = {
> +	.probe = at91_adc_probe,
> +	.remove = __devexit_p(at91_adc_remove),
> +	.driver = {
> +		   .name = "at91_adc",
> +	},
> +};
> +
> +module_platform_driver(at91_adc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");

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

* Re: [PATCH 3/6] ARM: AT91: Add the ADC to the sam9g20ek board
  2012-04-18 13:33   ` Maxime Ripard
@ 2012-04-18 17:54     ` Jonathan Cameron
  -1 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 17:54 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-iio, linux-arm-kernel, Nicolas Ferre, Patrice Vilchez,
	Thomas Petazzoni, Jean-Christophe PLAGNIOL-VILLARD

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> This patch adds platform data for the AT91 ADC driver support for
> the AT91SAM9G20-EK board.
There is a question of whether config variables from staging can be used
outside but hopefully that'll become a moot point soon anyway.

This looks fine to me.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
> 
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |   53 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9g20ek.c     |   13 +++++++
>  drivers/staging/iio/adc/at91_adc.c       |    1 -
>  3 files changed, 66 insertions(+), 1 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 5652dde..758c629 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -17,6 +17,8 @@
>  #include <linux/platform_device.h>
>  #include <linux/i2c-gpio.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <mach/board.h>
>  #include <mach/cpu.h>
>  #include <mach/at91sam9260.h>
> @@ -1369,6 +1371,57 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>  #endif
>  
> +/* --------------------------------------------------------------------
> + *  ADCs
> + * -------------------------------------------------------------------- */
> +
> +#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
> +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		= "at91_adc",
> +	.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);
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +#else
> +void __init at91_add_device_adc(struct at91_adc_data *data) {}
> +#endif
> +
>  /* -------------------------------------------------------------------- */
>  /*
>   * 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 8923ec9..2ce4e26 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -32,6 +32,8 @@
>  #include <linux/regulator/fixed.h>
>  #include <linux/regulator/consumer.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <mach/hardware.h>
>  #include <asm/setup.h>
>  #include <asm/mach-types.h>
> @@ -318,6 +320,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"),
> @@ -393,6 +404,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) */
> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> index 6d14277..9af036a 100644
> --- a/drivers/staging/iio/adc/at91_adc.c
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -56,7 +56,6 @@ struct at91_adc_state {
>  
>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>  	.clock = 5000000,
> -	.clock_name = "adc_clk",
>  	.num_channels = 4,
>  	.startup_time = 10,
>  };


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

* [PATCH 3/6] ARM: AT91: Add the ADC to the sam9g20ek board
@ 2012-04-18 17:54     ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 17:54 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> This patch adds platform data for the AT91 ADC driver support for
> the AT91SAM9G20-EK board.
There is a question of whether config variables from staging can be used
outside but hopefully that'll become a moot point soon anyway.

This looks fine to me.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
> 
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |   53 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9g20ek.c     |   13 +++++++
>  drivers/staging/iio/adc/at91_adc.c       |    1 -
>  3 files changed, 66 insertions(+), 1 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 5652dde..758c629 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -17,6 +17,8 @@
>  #include <linux/platform_device.h>
>  #include <linux/i2c-gpio.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <mach/board.h>
>  #include <mach/cpu.h>
>  #include <mach/at91sam9260.h>
> @@ -1369,6 +1371,57 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
>  void __init at91_add_device_cf(struct at91_cf_data * data) {}
>  #endif
>  
> +/* --------------------------------------------------------------------
> + *  ADCs
> + * -------------------------------------------------------------------- */
> +
> +#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
> +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		= "at91_adc",
> +	.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);
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +#else
> +void __init at91_add_device_adc(struct at91_adc_data *data) {}
> +#endif
> +
>  /* -------------------------------------------------------------------- */
>  /*
>   * 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 8923ec9..2ce4e26 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -32,6 +32,8 @@
>  #include <linux/regulator/fixed.h>
>  #include <linux/regulator/consumer.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <mach/hardware.h>
>  #include <asm/setup.h>
>  #include <asm/mach-types.h>
> @@ -318,6 +320,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"),
> @@ -393,6 +404,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) */
> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> index 6d14277..9af036a 100644
> --- a/drivers/staging/iio/adc/at91_adc.c
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -56,7 +56,6 @@ struct at91_adc_state {
>  
>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>  	.clock = 5000000,
> -	.clock_name = "adc_clk",
>  	.num_channels = 4,
>  	.startup_time = 10,
>  };

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

* Re: [PATCH 4/6] IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board
  2012-04-18 13:33   ` Maxime Ripard
@ 2012-04-18 17:57     ` Jonathan Cameron
  -1 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 17:57 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-iio, linux-arm-kernel, Jean-Christophe PLAGNIOL-VILLARD,
	Patrice Vilchez, Thomas Petazzoni, Nicolas Ferre

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> This patch adds the necessary logic and the required platform_data to
> support the ADC present in the AT91SAM9M10G45-EK board from Atmel. It
> only supports software triggers for now.
Same possible issue as with the other one...  Actually, not sure why
I acked that given I know very little about the particular platform.
Hence I'm fine with these, but not really for me to say whether they
are good!
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> ---
>  arch/arm/mach-at91/at91sam9g45.c         |    2 +
>  arch/arm/mach-at91/at91sam9g45_devices.c |   61 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9m10g45ek.c  |   11 +++++
>  drivers/staging/iio/adc/at91_adc.c       |   11 +++++
>  4 files changed, 85 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
> index d222f83..c6c35bc 100644
> --- a/arch/arm/mach-at91/at91sam9g45.c
> +++ b/arch/arm/mach-at91/at91sam9g45.c
> @@ -242,6 +242,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_ID("pioC", &pioC_clk),
>  	CLKDEV_CON_ID("pioD", &pioDE_clk),
>  	CLKDEV_CON_ID("pioE", &pioDE_clk),
> +	/* Fake adc clock */
> +	CLKDEV_CON_ID("adc_clk", &tsc_clk),
>  };
>  
>  static struct clk_lookup usart_clocks_lookups[] = {
> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
> index 6b008ae..05bc673 100644
> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
> @@ -19,6 +19,8 @@
>  #include <linux/i2c-gpio.h>
>  #include <linux/atmel-mci.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <linux/fb.h>
>  #include <video/atmel_lcdc.h>
>  
> @@ -1207,6 +1209,65 @@ void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data) {}
>  
>  
>  /* --------------------------------------------------------------------
> + *  ADC
> + * -------------------------------------------------------------------- */
> +
> +#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
> +static struct at91_adc_data adc_data;
> +
> +static struct resource adc_resources[] = {
> +	[0] = {
> +		.start	= AT91SAM9G45_BASE_TSC,
> +		.end	= AT91SAM9G45_BASE_TSC + SZ_16K - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= AT91SAM9G45_ID_TSC,
> +		.end	= AT91SAM9G45_ID_TSC,
> +		.flags	= IORESOURCE_IRQ,
> +	}
> +};
> +
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91_adc",
> +	.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_gpio_input(AT91_PIN_PD20, 0);
> +	if (test_bit(1, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD21, 0);
> +	if (test_bit(2, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD22, 0);
> +	if (test_bit(3, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD23, 0);
> +	if (test_bit(4, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD24, 0);
> +	if (test_bit(5, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD25, 0);
> +	if (test_bit(6, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD26, 0);
> +	if (test_bit(7, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD27, 0);
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +#else
> +void __init at91_add_device_adc(struct at91_adc_data *data) {}
> +#endif
> +
> +/* --------------------------------------------------------------------
>   *  RTT
>   * -------------------------------------------------------------------- */
>  
> diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
> index c88e908..fb122ba 100644
> --- a/arch/arm/mach-at91/board-sam9m10g45ek.c
> +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
> @@ -27,6 +27,8 @@
>  #include <linux/atmel-mci.h>
>  #include <linux/delay.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <mach/hardware.h>
>  #include <video/atmel_lcdc.h>
>  #include <media/soc_camera.h>
> @@ -315,6 +317,13 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
>  	.ts_sample_hold_time	= 0x0a,
>  };
>  
> +/*
> + * ADCs
> + */
> +static struct at91_adc_data ek_adc_data = {
> +	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
> +	.vref = 3300,
> +};
>  
>  /*
>   * GPIO Buttons
> @@ -480,6 +489,8 @@ static void __init ek_board_init(void)
>  	at91_add_device_lcdc(&ek_lcdc_data);
>  	/* Touch Screen */
>  	at91_add_device_tsadcc(&ek_tsadcc_data);
> +	/* ADC */
> +	at91_add_device_adc(&ek_adc_data);
>  	/* Push Buttons */
>  	ek_add_device_buttons();
>  	/* AC97 */
> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> index 9af036a..b3517bb 100644
> --- a/drivers/staging/iio/adc/at91_adc.c
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -54,6 +54,12 @@ struct at91_adc_state {
>  	wait_queue_head_t	wq_data_avail;
>  };
>  
> +static struct at91_adc_desc at91_adc_desc_sam9g45 = {
> +	.clock = 13200000,
> +	.num_channels = 8,
> +	.startup_time = 40,
> +};
> +
>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>  	.clock = 5000000,
>  	.num_channels = 4,
> @@ -67,6 +73,11 @@ static int at91_adc_select_soc(struct at91_adc_state *st)
>  		return 0;
>  	}
>  
> +	if (cpu_is_at91sam9g45()) {
> +		st->desc = &at91_adc_desc_sam9g45;
> +		return 0;
> +	}
> +
>  	return -ENODEV;
>  }
>  


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

* [PATCH 4/6] IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board
@ 2012-04-18 17:57     ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 17:57 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> This patch adds the necessary logic and the required platform_data to
> support the ADC present in the AT91SAM9M10G45-EK board from Atmel. It
> only supports software triggers for now.
Same possible issue as with the other one...  Actually, not sure why
I acked that given I know very little about the particular platform.
Hence I'm fine with these, but not really for me to say whether they
are good!
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> ---
>  arch/arm/mach-at91/at91sam9g45.c         |    2 +
>  arch/arm/mach-at91/at91sam9g45_devices.c |   61 ++++++++++++++++++++++++++++++
>  arch/arm/mach-at91/board-sam9m10g45ek.c  |   11 +++++
>  drivers/staging/iio/adc/at91_adc.c       |   11 +++++
>  4 files changed, 85 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
> index d222f83..c6c35bc 100644
> --- a/arch/arm/mach-at91/at91sam9g45.c
> +++ b/arch/arm/mach-at91/at91sam9g45.c
> @@ -242,6 +242,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
>  	CLKDEV_CON_ID("pioC", &pioC_clk),
>  	CLKDEV_CON_ID("pioD", &pioDE_clk),
>  	CLKDEV_CON_ID("pioE", &pioDE_clk),
> +	/* Fake adc clock */
> +	CLKDEV_CON_ID("adc_clk", &tsc_clk),
>  };
>  
>  static struct clk_lookup usart_clocks_lookups[] = {
> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
> index 6b008ae..05bc673 100644
> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
> @@ -19,6 +19,8 @@
>  #include <linux/i2c-gpio.h>
>  #include <linux/atmel-mci.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <linux/fb.h>
>  #include <video/atmel_lcdc.h>
>  
> @@ -1207,6 +1209,65 @@ void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data) {}
>  
>  
>  /* --------------------------------------------------------------------
> + *  ADC
> + * -------------------------------------------------------------------- */
> +
> +#if defined(CONFIG_AT91_ADC) || defined(CONFIG_AT91_ADC_MODULE)
> +static struct at91_adc_data adc_data;
> +
> +static struct resource adc_resources[] = {
> +	[0] = {
> +		.start	= AT91SAM9G45_BASE_TSC,
> +		.end	= AT91SAM9G45_BASE_TSC + SZ_16K - 1,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= AT91SAM9G45_ID_TSC,
> +		.end	= AT91SAM9G45_ID_TSC,
> +		.flags	= IORESOURCE_IRQ,
> +	}
> +};
> +
> +static struct platform_device at91_adc_device = {
> +	.name		= "at91_adc",
> +	.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_gpio_input(AT91_PIN_PD20, 0);
> +	if (test_bit(1, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD21, 0);
> +	if (test_bit(2, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD22, 0);
> +	if (test_bit(3, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD23, 0);
> +	if (test_bit(4, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD24, 0);
> +	if (test_bit(5, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD25, 0);
> +	if (test_bit(6, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD26, 0);
> +	if (test_bit(7, &data->channels_used))
> +		at91_set_gpio_input(AT91_PIN_PD27, 0);
> +
> +	adc_data = *data;
> +	platform_device_register(&at91_adc_device);
> +}
> +#else
> +void __init at91_add_device_adc(struct at91_adc_data *data) {}
> +#endif
> +
> +/* --------------------------------------------------------------------
>   *  RTT
>   * -------------------------------------------------------------------- */
>  
> diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
> index c88e908..fb122ba 100644
> --- a/arch/arm/mach-at91/board-sam9m10g45ek.c
> +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
> @@ -27,6 +27,8 @@
>  #include <linux/atmel-mci.h>
>  #include <linux/delay.h>
>  
> +#include <linux/platform_data/at91_adc.h>
> +
>  #include <mach/hardware.h>
>  #include <video/atmel_lcdc.h>
>  #include <media/soc_camera.h>
> @@ -315,6 +317,13 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
>  	.ts_sample_hold_time	= 0x0a,
>  };
>  
> +/*
> + * ADCs
> + */
> +static struct at91_adc_data ek_adc_data = {
> +	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
> +	.vref = 3300,
> +};
>  
>  /*
>   * GPIO Buttons
> @@ -480,6 +489,8 @@ static void __init ek_board_init(void)
>  	at91_add_device_lcdc(&ek_lcdc_data);
>  	/* Touch Screen */
>  	at91_add_device_tsadcc(&ek_tsadcc_data);
> +	/* ADC */
> +	at91_add_device_adc(&ek_adc_data);
>  	/* Push Buttons */
>  	ek_add_device_buttons();
>  	/* AC97 */
> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> index 9af036a..b3517bb 100644
> --- a/drivers/staging/iio/adc/at91_adc.c
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -54,6 +54,12 @@ struct at91_adc_state {
>  	wait_queue_head_t	wq_data_avail;
>  };
>  
> +static struct at91_adc_desc at91_adc_desc_sam9g45 = {
> +	.clock = 13200000,
> +	.num_channels = 8,
> +	.startup_time = 40,
> +};
> +
>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>  	.clock = 5000000,
>  	.num_channels = 4,
> @@ -67,6 +73,11 @@ static int at91_adc_select_soc(struct at91_adc_state *st)
>  		return 0;
>  	}
>  
> +	if (cpu_is_at91sam9g45()) {
> +		st->desc = &at91_adc_desc_sam9g45;
> +		return 0;
> +	}
> +
>  	return -ENODEV;
>  }
>  

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

* Re: [PATCH 5/6] IIO: AT91: Add support for hardware triggers for the ADC
  2012-04-18 13:33   ` Maxime Ripard
@ 2012-04-18 18:46     ` Jonathan Cameron
  -1 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 18:46 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-iio, linux-arm-kernel, Jean-Christophe PLAGNIOL-VILLARD,
	Patrice Vilchez, Thomas Petazzoni, Nicolas Ferre

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> This patch adds support for the hardware triggers for both the
> AT91SAM9G20-EK and AT91SAM9M10G45-EK evaluation board from Atmel.
> 

Couple of queries inline.

The 'interesting' bit is why it is currently possible for
iio_dev->active_scan_mask to differ from buffer->scan_mask?
I'm curious given this leads to a demux  operation within the
driver.

The name based matching is uggly. Maybe we should just add
an 'index' field to the iio_trigger structure?
I'm sure other users will turn up...

I've also hightlighted a few areas where a current patch
set of mine (introducing multiple buffers) will need
changes in here (about as trivial as they are in any
driver..)


Jonathan

> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |    3 +
>  arch/arm/mach-at91/at91sam9g45_devices.c |    3 +
>  arch/arm/mach-at91/board-sam9g20ek.c     |    1 +
>  arch/arm/mach-at91/board-sam9m10g45ek.c  |    1 +
>  drivers/staging/iio/adc/Kconfig          |    3 +
>  drivers/staging/iio/adc/at91_adc.c       |  389 +++++++++++++++++++++++++++++-
>  include/linux/platform_data/at91_adc.h   |    2 +
>  7 files changed, 395 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 758c629..a870753 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -1415,6 +1415,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
>  	if (test_bit(3, &data->channels_used))
>  		at91_set_A_periph(AT91_PIN_PC3, 0);
>  
> +	if (data->use_external)
> +		at91_set_A_periph(AT91_PIN_PA22, 0);
> +
>  	adc_data = *data;
>  	platform_device_register(&at91_adc_device);
>  }
> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
> index 05bc673..4ee18af 100644
> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
> @@ -1260,6 +1260,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
>  	if (test_bit(7, &data->channels_used))
>  		at91_set_gpio_input(AT91_PIN_PD27, 0);
>  
> +	if (data->use_external)
> +		at91_set_A_periph(AT91_PIN_PD28, 0);
> +
>  	adc_data = *data;
>  	platform_device_register(&at91_adc_device);
>  }
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 2ce4e26..3bb6e4d 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -326,6 +326,7 @@ static void __init ek_add_device_buttons(void) {}
>  
>  static struct at91_adc_data ek_adc_data = {
>  	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
> +	.use_external = true,
>  	.vref = 3300,
>  };
>  
> diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
> index fb122ba..192a050 100644
> --- a/arch/arm/mach-at91/board-sam9m10g45ek.c
> +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
> @@ -322,6 +322,7 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
>   */
>  static struct at91_adc_data ek_adc_data = {
>  	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
> +	.use_external = true,
>  	.vref = 3300,
>  };
>  
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 1494838..9ee7ed9 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -172,6 +172,9 @@ config AD7280
>  config AT91_ADC
>  	tristate "Atmel AT91 ADC"
>  	depends on SYSFS && ARCH_AT91
> +	select IIO_BUFFER
> +	select IIO_SW_RING
> +	select IIO_TRIGGER
>  	help
>  	  Say yes here to build support for Atmel AT91 ADC.
>  
> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> index b3517bb..7c93cde 100644
> --- a/drivers/staging/iio/adc/at91_adc.c
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -23,9 +23,33 @@
>  #include "../iio.h"
>  #include <linux/platform_data/at91_adc.h>
>  
> +#include "../buffer.h"
> +#include "../ring_sw.h"
> +#include "../trigger.h"
> +#include "../trigger_consumer.h"
> +
>  #include <mach/at91_adc.h>
>  #include <mach/cpu.h>
>  
> +#define AT91_TSADCC_TRGR		(0x08)
> +#define AT91_TSADCC_TRGMOD_EXT_RISING	(1 << 0)
> +#define AT91_TSADCC_TRGMOD_EXT_FALLING	(1 << 1)
> +#define AT91_TSADCC_TRGMOD_EXT_ANY	(AT91_TSADCC_TRGMOD_EXT_RISING | AT91_TSADCC_TRGMOD_EXT_FALLING)
> +#define AT91_TSADCC_TRGMOD_CONTINUOUS	(3 << 1)
> +
> +/**
> + * struct at91_adc_trigger - description of triggers
> + * @name:		name of the trigger advertised to the user
> + * @value:		value to set in the ADC's mode register to enable
> +			the trigger
> + * @is_external:	is the trigger relies on an external pin ?
> + */
> +struct at91_adc_trigger {
> +	char	*name;
> +	u8	value;
> +	bool	is_external;
> +};
> +
>  /**
>   * struct at91_adc_desc - description of the ADC on the board
>   * @clock:		ADC clock as specified by the datasheet, in Hz.
> @@ -34,36 +58,99 @@
>  			board, see the channels_used bitmask in the platform
>  			data)
>   * @startup_time:	startup time of the ADC in microseconds
> + * @triggers:		array of the triggers available on the board
> + * @trigger_register:	index of the register managing the hardware triggers
>   */
>  struct at91_adc_desc {
>  	u32			clock;
>  	u8			num_channels;
>  	u8			startup_time;
> +	struct at91_adc_trigger	*triggers;
> +	u8			trigger_register;
>  };
>  
>  struct at91_adc_state {
> +	u16			*buffer;
>  	unsigned long		channels_mask;
>  	struct clk		*clk;
>  	bool			done;
>  	struct at91_adc_desc	*desc;
>  	int			irq;
> +	bool			irq_enabled;
>  	u16			last_value;
>  	struct mutex		lock;
>  	void __iomem		*reg_base;
> +	struct iio_trigger	**trig;
>  	u32			vref_mv;
>  	wait_queue_head_t	wq_data_avail;
>  };
>  
> +static struct at91_adc_trigger at91_adc_trigger_sam9g45[] = {
> +	[0] = {
> +		.name = "external-rising",
> +		.value = AT91_TSADCC_TRGMOD_EXT_RISING,
> +		.is_external = true,
> +	},
> +	[1] = {
> +		.name = "external-falling",
> +		.value = AT91_TSADCC_TRGMOD_EXT_FALLING,
> +		.is_external = true,
> +	},
> +	[2] = {
> +		.name = "external-any",
> +		.value = AT91_TSADCC_TRGMOD_EXT_ANY,
> +		.is_external = true,
> +	},
> +	[3] = {
> +		.name = "continuous",
> +		.value = AT91_TSADCC_TRGMOD_CONTINUOUS,
> +		.is_external = false,
> +	},
> +	[4] = {
> +		.name = NULL,
> +	},
> +};
> +
>  static struct at91_adc_desc at91_adc_desc_sam9g45 = {
>  	.clock = 13200000,
>  	.num_channels = 8,
>  	.startup_time = 40,
> +	.triggers = at91_adc_trigger_sam9g45,
> +	.trigger_register = AT91_TSADCC_TRGR,
> +};
> +
> +static struct at91_adc_trigger at91_adc_trigger_sam9g20[] = {
> +	[0] = {
> +		.name = "timer-counter-0",
> +		.value = AT91_ADC_TRGSEL_TC0 | AT91_ADC_TRGEN,
> +		.is_external = false,
> +	},
> +	[1] = {
> +		.name = "timer-counter-1",
> +		.value = AT91_ADC_TRGSEL_TC1 | AT91_ADC_TRGEN,
> +		.is_external = false,
> +	},
> +	[2] = {
> +		.name = "timer-counter-2",
> +		.value = AT91_ADC_TRGSEL_TC2 | AT91_ADC_TRGEN,
> +		.is_external = false,
> +	},
> +	[3] = {
> +		.name = "external",
> +		.value = AT91_ADC_TRGSEL_EXTERNAL | AT91_ADC_TRGEN,
> +		.is_external = true,
> +	},
> +	[4] = {
> +		.name = NULL,
> +	},
>  };
>  
>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>  	.clock = 5000000,
>  	.num_channels = 4,
>  	.startup_time = 10,
> +	.triggers = at91_adc_trigger_sam9g20,
> +	.trigger_register = AT91_ADC_MR,
>  };
>  
>  static int at91_adc_select_soc(struct at91_adc_state *st)
> @@ -94,6 +181,48 @@ static inline void at91_adc_reg_write(struct at91_adc_state *st,
>  	writel_relaxed(val, st->reg_base + reg);
>  }
>  
> +static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct at91_adc_state *st = iio_priv(idev);
> +	struct iio_buffer *buffer = idev->buffer;
> +	int len = 0;
> +
Don't think this if adds much as the for loop will just not
do anything if no bits are set.

> +	if (!bitmap_empty(idev->active_scan_mask, idev->masklength)) {
> +		int i, j;
> +		for (i = 0, j = 0;
> +		     i < bitmap_weight(idev->active_scan_mask,
> +				       idev->masklength);
> +		     i++) {
> +			j = find_next_bit(buffer->scan_mask,
> +					  idev->masklength,
> +					  j);
This looks rather like a demux operation. How costly are the register reads?
If not terribly I'd read the lot and let the demuxer in the core handle
pulling
the correct values out...

There are only a few reasons why buffer->scan_mask !=
idev->active_scan_mask.

1) only some masks are possible (not true here?)
2) Multiple buffers are being filled.  This code is pre that being possible
and anyway if it were you'd want to be reading everythin in the mask...
3) something I haven't thought of, in which case please explain!
> +			st->buffer[i] = at91_adc_reg_read(st, AT91_ADC_CHR(j));
> +			j++;
> +			len += 2;
> +		}
> +	}
> +
> +	if (buffer->scan_timestamp) {
> +		s64 *timestamp = (s64 *)((u8 *)st->buffer + ALIGN(len,
> +								  sizeof(s64)));
I'd break the line after the + (nitpick!)
> +		*timestamp = pf->timestamp;
> +	}
> +
> +	buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
As a heads up, a patch set I have out for review stops drivers doing
this and
instead routes everything through a generic function which in turn calls a
demux on demand and pushes data on to any number of buffers.
> +
> +	iio_trigger_notify_done(idev->trig);
> +	st->irq_enabled = true;
> +
> +	/* Needed to ACK the DRDY interruption */
> +	at91_adc_reg_read(st, AT91_ADC_LCDR);
> +
> +	enable_irq(st->irq);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
>  {
>  	struct iio_dev *idev = private;
> @@ -103,13 +232,16 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
>  	if (!(status & AT91_ADC_DRDY))
>  		return IRQ_HANDLED;
>  
> -	if (status & st->channels_mask) {
> -		st->done = true;
> +	if (iio_buffer_enabled(idev)) {
> +		disable_irq_nosync(irq);
> +		st->irq_enabled = false;
> +		iio_trigger_poll(idev->trig, iio_get_time_ns());
> +	} else {
>  		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
> +		st->done = true;
> +		wake_up_interruptible(&st->wq_data_avail);
>  	}
>  
> -	wake_up_interruptible(&st->wq_data_avail);
> -
>  	return IRQ_HANDLED;
>  }
>  
> @@ -121,10 +253,10 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>  	int bit, idx = 0;
>  
>  	idev->num_channels = bitmap_weight(&pdata->channels_used,
> -					   st->desc->num_channels);
> +					   st->desc->num_channels) + 1;
>  
>  	chan_array = devm_kzalloc(&idev->dev,
> -				  idev->num_channels * sizeof(struct iio_chan_spec),
> +				  (idev->num_channels + 1) * sizeof(struct iio_chan_spec),
>  				  GFP_KERNEL);
>  
>  	if (chan_array == NULL)
> @@ -135,6 +267,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>  		chan->type = IIO_VOLTAGE;
>  		chan->indexed = 1;
>  		chan->channel = bit;
> +		chan->scan_index = idx;
>  		chan->scan_type.sign = 'u';
>  		chan->scan_type.realbits = 10;
>  		chan->scan_type.storagebits = 16;
> @@ -142,6 +275,13 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>  		idx++;
>  	}
>  
> +	(chan_array + idx)->type = IIO_TIMESTAMP;
> +	(chan_array + idx)->channel = -1;
> +	(chan_array + idx)->scan_index = idx;
> +	(chan_array + idx)->scan_type.sign = 's';
> +	(chan_array + idx)->scan_type.realbits = 64;
> +	(chan_array + idx)->scan_type.storagebits = 64;
> +
>  	idev->channels = chan_array;
>  	return idev->num_channels;
>  }
> @@ -151,6 +291,223 @@ static void at91_adc_channel_remove(struct iio_dev *idev)
>  	devm_kfree(&idev->dev, (void*)idev->channels);
>  }
>  
> +static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
> +					     struct at91_adc_trigger *triggers,
> +					     const char *trigger_name)
> +{
> +	u8 value = 0;
> +	int i;
> +
> +	for (i = 0; (triggers + i) != NULL; i++) {
> +		char *name = kasprintf(GFP_KERNEL,
> +				"%s-dev%d-%s",
> +				idev->name,
> +				idev->id,
> +				triggers[i].name);
> +		if (name == NULL)
> +			return -ENOMEM;
> +
> +		if (strcmp(trigger_name, name) == 0) {
> +			value = triggers[i].value;
> +			kfree(name);
> +			break;
> +		}
> +
> +		kfree(name);
> +	}
> +
> +	return value;
> +}
> +
> +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *idev = trig->private_data;
> +	struct at91_adc_state *st = iio_priv(idev);
> +	struct iio_buffer *buffer = idev->buffer;
> +	u32 status = at91_adc_reg_read(st, st->desc->trigger_register);
> +	u8 value;
> +	u8 bit;
> +
Hmm.. this seems rather convoluted...  Not that I can immediately think
of a better way... So I'll moan and let it go ;)
> +	value = at91_adc_get_trigger_value_by_name(idev,
> +						   st->desc->triggers,
> +						   idev->trig->name);
> +	if (value == 0)
> +		return -EINVAL;
> +
> +	if (state) {
> +		size_t datasize = buffer->access->get_bytes_per_datum(buffer);
> +
> +		st->buffer = kmalloc(datasize, GFP_KERNEL);
> +		if (st->buffer == NULL)
> +			return -ENOMEM;
> +
> +		at91_adc_reg_write(st,
> +				   st->desc->trigger_register,
> +				   status | value);
> +
> +		for_each_set_bit(bit, buffer->scan_mask,
> +				 st->desc->num_channels) {
> +			struct iio_chan_spec const *chan = idev->channels + bit;
> +			at91_adc_reg_write(st, AT91_ADC_CHER,
> +					   AT91_ADC_CH(chan->channel));
> +		}
> +
> +		at91_adc_reg_write(st, AT91_ADC_IER, AT91_ADC_DRDY);
> +
> +	} else {
> +		at91_adc_reg_write(st, AT91_ADC_IDR, AT91_ADC_DRDY);
> +
> +		at91_adc_reg_write(st, st->desc->trigger_register,
> +				   status & ~value);
> +
> +		for_each_set_bit(bit, buffer->scan_mask,
> +				 st->desc->num_channels) {
> +			struct iio_chan_spec const *chan = idev->channels + bit;
> +			at91_adc_reg_write(st, AT91_ADC_CHDR,
> +					   AT91_ADC_CH(chan->channel));
> +		}
> +		kfree(st->buffer);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_trigger_ops at91_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = &at91_adc_configure_trigger,
> +};
> +
> +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
> +						     struct at91_adc_trigger *trigger)
> +{
> +	struct iio_trigger *trig = iio_allocate_trigger("%s-dev%d-%s",
> +							idev->name,
> +							idev->id,
> +							trigger->name);
> +	int ret;
> +
> +	if (trig == NULL)
> +		return NULL;
> +
> +	trig->dev.parent = idev->dev.parent;
> +	trig->private_data = idev;
> +	trig->ops = &at91_adc_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret < 0)
> +		return NULL;
> +
> +	return trig;
> +}
> +
> +static int at91_adc_trigger_init(struct iio_dev *idev,
> +				 struct at91_adc_data *pdata)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	int i, ret;
> +
> +	st->trig = kcalloc(sizeof(st->desc->triggers),
> +			   sizeof(st->trig),
> +			   GFP_KERNEL);
> +
> +	if (st->trig == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
> +		if (st->desc->triggers[i].is_external && !(pdata->use_external))
> +			continue;
> +
> +		st->trig[i] = at91_adc_allocate_trigger(idev,
> +							st->desc->triggers + i);
> +		if (st->trig[i] == NULL) {
> +			dev_err(&idev->dev,
> +				"Could not allocate trigger %d\n", i);
> +			ret = -ENOMEM;
> +			goto error_trigger;
> +		}
> +	}
> +
> +	return 0;
> +
> +error_trigger:
> +	for (i--; i >= 0; i--) {
> +		iio_trigger_unregister(st->trig[i]);
> +		iio_free_trigger(st->trig[i]);
> +	}
> +	kfree(st->trig);
> +error_ret:
> +	return ret;
> +}
> +
> +static void at91_adc_trigger_remove(struct iio_dev *idev)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	int i;
> +
> +	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
> +		iio_trigger_unregister(st->trig[i]);
> +		iio_free_trigger(st->trig[i]);
> +	}
> +
> +	kfree(st->trig);
> +}
> +
> +static const struct iio_buffer_setup_ops at91_adc_buffer_ops = {
> +	.preenable = &iio_sw_buffer_preenable,
That's handy. Any other preenable and the series I sent out today
would require some changes.  As is, it 'should' just work and give
you multiple buffer support.
(now if you happened to have time to test that... ;)
> +	.postenable = &iio_triggered_buffer_postenable,
> +	.predisable = &iio_triggered_buffer_predisable,
> +};
> +
> +static int at91_adc_buffer_init(struct iio_dev *idev)
> +{
> +	int ret;
> +
> +	idev->buffer = iio_sw_rb_allocate(idev);
> +	if (!idev->buffer) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	idev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
> +					    &at91_adc_trigger_handler,
> +					    IRQF_ONESHOT,
> +					    idev,
> +					    "%s-consumer%d",
> +					    idev->name,
> +					    idev->id);
> +	if (idev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_pollfunc;
> +	}
> +
> +	idev->setup_ops = &at91_adc_buffer_ops;
> +	idev->modes |= INDIO_BUFFER_TRIGGERED;
> +
> +	ret = iio_buffer_register(idev,
> +				  idev->channels,
> +				  idev->num_channels);
> +	if (ret)
> +		goto error_register;
> +
> +	return 0;
> +
> +error_register:
> +	iio_dealloc_pollfunc(idev->pollfunc);
> +error_pollfunc:
> +	iio_sw_rb_free(idev->buffer);
> +error_ret:
> +	return ret;
> +}
> +
> +static void at91_adc_buffer_remove(struct iio_dev *idev)
> +{
> +	iio_buffer_unregister(idev);
> +	iio_dealloc_pollfunc(idev->pollfunc);
> +	iio_sw_rb_free(idev->buffer);
> +}
> +
>  static int at91_adc_read_raw(struct iio_dev *idev,
>  			     struct iio_chan_spec const *chan,
>  			     int *val, int *val2, long mask)
> @@ -344,14 +701,30 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
>  	st->vref_mv = pdata->vref;
>  	st->channels_mask = pdata->channels_used;
>  
> +	ret = at91_adc_buffer_init(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
> +		goto error_free_channels;
> +	}
> +
> +	ret = at91_adc_trigger_init(idev, pdata);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
> +		goto error_unregister_buffer;
> +	}
> +
>  	ret = iio_device_register(idev);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "Couldn't register the device.\n");
> -		goto error_free_channels;
> +		goto error_remove_triggers;
>  	}
>  
>  	return 0;
>  
> +error_remove_triggers:
> +	at91_adc_trigger_remove(idev);
> +error_unregister_buffer:
> +	at91_adc_buffer_remove(idev);
>  error_free_channels:
>  	at91_adc_channel_remove(idev);
>  error_disable_clk:
> @@ -379,6 +752,8 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
>  	struct at91_adc_state *st = iio_priv(idev);
>  
>  	iio_device_unregister(idev);
> +	at91_adc_trigger_remove(idev);
> +	at91_adc_buffer_remove(idev);
>  	at91_adc_channel_remove(idev);
>  	clk_disable(st->clk);
>  	clk_unprepare(st->clk);
> diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
> index 8b51ee6..8750d80 100644
> --- a/include/linux/platform_data/at91_adc.h
> +++ b/include/linux/platform_data/at91_adc.h
> @@ -10,10 +10,12 @@
>  /**
>   * struct at91_adc_data - platform data for ADC driver
>   * @channels_used:	channels in use on the board as a bitmask
> + * @use_external:	does the board has external triggers availables
That's not exactly a well named variable.  Howabout,
use_external_triggers?
>   * @vref:		Reference voltage for the ADC in millivolts
>   */
>  struct at91_adc_data {
>  	unsigned long	channels_used;
> +	bool		use_external;
>  	u16		vref;
>  };
>  


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

* [PATCH 5/6] IIO: AT91: Add support for hardware triggers for the ADC
@ 2012-04-18 18:46     ` Jonathan Cameron
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Cameron @ 2012-04-18 18:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/18/2012 02:33 PM, Maxime Ripard wrote:
> This patch adds support for the hardware triggers for both the
> AT91SAM9G20-EK and AT91SAM9M10G45-EK evaluation board from Atmel.
> 

Couple of queries inline.

The 'interesting' bit is why it is currently possible for
iio_dev->active_scan_mask to differ from buffer->scan_mask?
I'm curious given this leads to a demux  operation within the
driver.

The name based matching is uggly. Maybe we should just add
an 'index' field to the iio_trigger structure?
I'm sure other users will turn up...

I've also hightlighted a few areas where a current patch
set of mine (introducing multiple buffers) will need
changes in here (about as trivial as they are in any
driver..)


Jonathan

> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
> ---
>  arch/arm/mach-at91/at91sam9260_devices.c |    3 +
>  arch/arm/mach-at91/at91sam9g45_devices.c |    3 +
>  arch/arm/mach-at91/board-sam9g20ek.c     |    1 +
>  arch/arm/mach-at91/board-sam9m10g45ek.c  |    1 +
>  drivers/staging/iio/adc/Kconfig          |    3 +
>  drivers/staging/iio/adc/at91_adc.c       |  389 +++++++++++++++++++++++++++++-
>  include/linux/platform_data/at91_adc.h   |    2 +
>  7 files changed, 395 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
> index 758c629..a870753 100644
> --- a/arch/arm/mach-at91/at91sam9260_devices.c
> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
> @@ -1415,6 +1415,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
>  	if (test_bit(3, &data->channels_used))
>  		at91_set_A_periph(AT91_PIN_PC3, 0);
>  
> +	if (data->use_external)
> +		at91_set_A_periph(AT91_PIN_PA22, 0);
> +
>  	adc_data = *data;
>  	platform_device_register(&at91_adc_device);
>  }
> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
> index 05bc673..4ee18af 100644
> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
> @@ -1260,6 +1260,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
>  	if (test_bit(7, &data->channels_used))
>  		at91_set_gpio_input(AT91_PIN_PD27, 0);
>  
> +	if (data->use_external)
> +		at91_set_A_periph(AT91_PIN_PD28, 0);
> +
>  	adc_data = *data;
>  	platform_device_register(&at91_adc_device);
>  }
> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
> index 2ce4e26..3bb6e4d 100644
> --- a/arch/arm/mach-at91/board-sam9g20ek.c
> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
> @@ -326,6 +326,7 @@ static void __init ek_add_device_buttons(void) {}
>  
>  static struct at91_adc_data ek_adc_data = {
>  	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
> +	.use_external = true,
>  	.vref = 3300,
>  };
>  
> diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
> index fb122ba..192a050 100644
> --- a/arch/arm/mach-at91/board-sam9m10g45ek.c
> +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
> @@ -322,6 +322,7 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
>   */
>  static struct at91_adc_data ek_adc_data = {
>  	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
> +	.use_external = true,
>  	.vref = 3300,
>  };
>  
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 1494838..9ee7ed9 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -172,6 +172,9 @@ config AD7280
>  config AT91_ADC
>  	tristate "Atmel AT91 ADC"
>  	depends on SYSFS && ARCH_AT91
> +	select IIO_BUFFER
> +	select IIO_SW_RING
> +	select IIO_TRIGGER
>  	help
>  	  Say yes here to build support for Atmel AT91 ADC.
>  
> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
> index b3517bb..7c93cde 100644
> --- a/drivers/staging/iio/adc/at91_adc.c
> +++ b/drivers/staging/iio/adc/at91_adc.c
> @@ -23,9 +23,33 @@
>  #include "../iio.h"
>  #include <linux/platform_data/at91_adc.h>
>  
> +#include "../buffer.h"
> +#include "../ring_sw.h"
> +#include "../trigger.h"
> +#include "../trigger_consumer.h"
> +
>  #include <mach/at91_adc.h>
>  #include <mach/cpu.h>
>  
> +#define AT91_TSADCC_TRGR		(0x08)
> +#define AT91_TSADCC_TRGMOD_EXT_RISING	(1 << 0)
> +#define AT91_TSADCC_TRGMOD_EXT_FALLING	(1 << 1)
> +#define AT91_TSADCC_TRGMOD_EXT_ANY	(AT91_TSADCC_TRGMOD_EXT_RISING | AT91_TSADCC_TRGMOD_EXT_FALLING)
> +#define AT91_TSADCC_TRGMOD_CONTINUOUS	(3 << 1)
> +
> +/**
> + * struct at91_adc_trigger - description of triggers
> + * @name:		name of the trigger advertised to the user
> + * @value:		value to set in the ADC's mode register to enable
> +			the trigger
> + * @is_external:	is the trigger relies on an external pin ?
> + */
> +struct at91_adc_trigger {
> +	char	*name;
> +	u8	value;
> +	bool	is_external;
> +};
> +
>  /**
>   * struct at91_adc_desc - description of the ADC on the board
>   * @clock:		ADC clock as specified by the datasheet, in Hz.
> @@ -34,36 +58,99 @@
>  			board, see the channels_used bitmask in the platform
>  			data)
>   * @startup_time:	startup time of the ADC in microseconds
> + * @triggers:		array of the triggers available on the board
> + * @trigger_register:	index of the register managing the hardware triggers
>   */
>  struct at91_adc_desc {
>  	u32			clock;
>  	u8			num_channels;
>  	u8			startup_time;
> +	struct at91_adc_trigger	*triggers;
> +	u8			trigger_register;
>  };
>  
>  struct at91_adc_state {
> +	u16			*buffer;
>  	unsigned long		channels_mask;
>  	struct clk		*clk;
>  	bool			done;
>  	struct at91_adc_desc	*desc;
>  	int			irq;
> +	bool			irq_enabled;
>  	u16			last_value;
>  	struct mutex		lock;
>  	void __iomem		*reg_base;
> +	struct iio_trigger	**trig;
>  	u32			vref_mv;
>  	wait_queue_head_t	wq_data_avail;
>  };
>  
> +static struct at91_adc_trigger at91_adc_trigger_sam9g45[] = {
> +	[0] = {
> +		.name = "external-rising",
> +		.value = AT91_TSADCC_TRGMOD_EXT_RISING,
> +		.is_external = true,
> +	},
> +	[1] = {
> +		.name = "external-falling",
> +		.value = AT91_TSADCC_TRGMOD_EXT_FALLING,
> +		.is_external = true,
> +	},
> +	[2] = {
> +		.name = "external-any",
> +		.value = AT91_TSADCC_TRGMOD_EXT_ANY,
> +		.is_external = true,
> +	},
> +	[3] = {
> +		.name = "continuous",
> +		.value = AT91_TSADCC_TRGMOD_CONTINUOUS,
> +		.is_external = false,
> +	},
> +	[4] = {
> +		.name = NULL,
> +	},
> +};
> +
>  static struct at91_adc_desc at91_adc_desc_sam9g45 = {
>  	.clock = 13200000,
>  	.num_channels = 8,
>  	.startup_time = 40,
> +	.triggers = at91_adc_trigger_sam9g45,
> +	.trigger_register = AT91_TSADCC_TRGR,
> +};
> +
> +static struct at91_adc_trigger at91_adc_trigger_sam9g20[] = {
> +	[0] = {
> +		.name = "timer-counter-0",
> +		.value = AT91_ADC_TRGSEL_TC0 | AT91_ADC_TRGEN,
> +		.is_external = false,
> +	},
> +	[1] = {
> +		.name = "timer-counter-1",
> +		.value = AT91_ADC_TRGSEL_TC1 | AT91_ADC_TRGEN,
> +		.is_external = false,
> +	},
> +	[2] = {
> +		.name = "timer-counter-2",
> +		.value = AT91_ADC_TRGSEL_TC2 | AT91_ADC_TRGEN,
> +		.is_external = false,
> +	},
> +	[3] = {
> +		.name = "external",
> +		.value = AT91_ADC_TRGSEL_EXTERNAL | AT91_ADC_TRGEN,
> +		.is_external = true,
> +	},
> +	[4] = {
> +		.name = NULL,
> +	},
>  };
>  
>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>  	.clock = 5000000,
>  	.num_channels = 4,
>  	.startup_time = 10,
> +	.triggers = at91_adc_trigger_sam9g20,
> +	.trigger_register = AT91_ADC_MR,
>  };
>  
>  static int at91_adc_select_soc(struct at91_adc_state *st)
> @@ -94,6 +181,48 @@ static inline void at91_adc_reg_write(struct at91_adc_state *st,
>  	writel_relaxed(val, st->reg_base + reg);
>  }
>  
> +static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *idev = pf->indio_dev;
> +	struct at91_adc_state *st = iio_priv(idev);
> +	struct iio_buffer *buffer = idev->buffer;
> +	int len = 0;
> +
Don't think this if adds much as the for loop will just not
do anything if no bits are set.

> +	if (!bitmap_empty(idev->active_scan_mask, idev->masklength)) {
> +		int i, j;
> +		for (i = 0, j = 0;
> +		     i < bitmap_weight(idev->active_scan_mask,
> +				       idev->masklength);
> +		     i++) {
> +			j = find_next_bit(buffer->scan_mask,
> +					  idev->masklength,
> +					  j);
This looks rather like a demux operation. How costly are the register reads?
If not terribly I'd read the lot and let the demuxer in the core handle
pulling
the correct values out...

There are only a few reasons why buffer->scan_mask !=
idev->active_scan_mask.

1) only some masks are possible (not true here?)
2) Multiple buffers are being filled.  This code is pre that being possible
and anyway if it were you'd want to be reading everythin in the mask...
3) something I haven't thought of, in which case please explain!
> +			st->buffer[i] = at91_adc_reg_read(st, AT91_ADC_CHR(j));
> +			j++;
> +			len += 2;
> +		}
> +	}
> +
> +	if (buffer->scan_timestamp) {
> +		s64 *timestamp = (s64 *)((u8 *)st->buffer + ALIGN(len,
> +								  sizeof(s64)));
I'd break the line after the + (nitpick!)
> +		*timestamp = pf->timestamp;
> +	}
> +
> +	buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
As a heads up, a patch set I have out for review stops drivers doing
this and
instead routes everything through a generic function which in turn calls a
demux on demand and pushes data on to any number of buffers.
> +
> +	iio_trigger_notify_done(idev->trig);
> +	st->irq_enabled = true;
> +
> +	/* Needed to ACK the DRDY interruption */
> +	at91_adc_reg_read(st, AT91_ADC_LCDR);
> +
> +	enable_irq(st->irq);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
>  {
>  	struct iio_dev *idev = private;
> @@ -103,13 +232,16 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
>  	if (!(status & AT91_ADC_DRDY))
>  		return IRQ_HANDLED;
>  
> -	if (status & st->channels_mask) {
> -		st->done = true;
> +	if (iio_buffer_enabled(idev)) {
> +		disable_irq_nosync(irq);
> +		st->irq_enabled = false;
> +		iio_trigger_poll(idev->trig, iio_get_time_ns());
> +	} else {
>  		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
> +		st->done = true;
> +		wake_up_interruptible(&st->wq_data_avail);
>  	}
>  
> -	wake_up_interruptible(&st->wq_data_avail);
> -
>  	return IRQ_HANDLED;
>  }
>  
> @@ -121,10 +253,10 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>  	int bit, idx = 0;
>  
>  	idev->num_channels = bitmap_weight(&pdata->channels_used,
> -					   st->desc->num_channels);
> +					   st->desc->num_channels) + 1;
>  
>  	chan_array = devm_kzalloc(&idev->dev,
> -				  idev->num_channels * sizeof(struct iio_chan_spec),
> +				  (idev->num_channels + 1) * sizeof(struct iio_chan_spec),
>  				  GFP_KERNEL);
>  
>  	if (chan_array == NULL)
> @@ -135,6 +267,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>  		chan->type = IIO_VOLTAGE;
>  		chan->indexed = 1;
>  		chan->channel = bit;
> +		chan->scan_index = idx;
>  		chan->scan_type.sign = 'u';
>  		chan->scan_type.realbits = 10;
>  		chan->scan_type.storagebits = 16;
> @@ -142,6 +275,13 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>  		idx++;
>  	}
>  
> +	(chan_array + idx)->type = IIO_TIMESTAMP;
> +	(chan_array + idx)->channel = -1;
> +	(chan_array + idx)->scan_index = idx;
> +	(chan_array + idx)->scan_type.sign = 's';
> +	(chan_array + idx)->scan_type.realbits = 64;
> +	(chan_array + idx)->scan_type.storagebits = 64;
> +
>  	idev->channels = chan_array;
>  	return idev->num_channels;
>  }
> @@ -151,6 +291,223 @@ static void at91_adc_channel_remove(struct iio_dev *idev)
>  	devm_kfree(&idev->dev, (void*)idev->channels);
>  }
>  
> +static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
> +					     struct at91_adc_trigger *triggers,
> +					     const char *trigger_name)
> +{
> +	u8 value = 0;
> +	int i;
> +
> +	for (i = 0; (triggers + i) != NULL; i++) {
> +		char *name = kasprintf(GFP_KERNEL,
> +				"%s-dev%d-%s",
> +				idev->name,
> +				idev->id,
> +				triggers[i].name);
> +		if (name == NULL)
> +			return -ENOMEM;
> +
> +		if (strcmp(trigger_name, name) == 0) {
> +			value = triggers[i].value;
> +			kfree(name);
> +			break;
> +		}
> +
> +		kfree(name);
> +	}
> +
> +	return value;
> +}
> +
> +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
> +{
> +	struct iio_dev *idev = trig->private_data;
> +	struct at91_adc_state *st = iio_priv(idev);
> +	struct iio_buffer *buffer = idev->buffer;
> +	u32 status = at91_adc_reg_read(st, st->desc->trigger_register);
> +	u8 value;
> +	u8 bit;
> +
Hmm.. this seems rather convoluted...  Not that I can immediately think
of a better way... So I'll moan and let it go ;)
> +	value = at91_adc_get_trigger_value_by_name(idev,
> +						   st->desc->triggers,
> +						   idev->trig->name);
> +	if (value == 0)
> +		return -EINVAL;
> +
> +	if (state) {
> +		size_t datasize = buffer->access->get_bytes_per_datum(buffer);
> +
> +		st->buffer = kmalloc(datasize, GFP_KERNEL);
> +		if (st->buffer == NULL)
> +			return -ENOMEM;
> +
> +		at91_adc_reg_write(st,
> +				   st->desc->trigger_register,
> +				   status | value);
> +
> +		for_each_set_bit(bit, buffer->scan_mask,
> +				 st->desc->num_channels) {
> +			struct iio_chan_spec const *chan = idev->channels + bit;
> +			at91_adc_reg_write(st, AT91_ADC_CHER,
> +					   AT91_ADC_CH(chan->channel));
> +		}
> +
> +		at91_adc_reg_write(st, AT91_ADC_IER, AT91_ADC_DRDY);
> +
> +	} else {
> +		at91_adc_reg_write(st, AT91_ADC_IDR, AT91_ADC_DRDY);
> +
> +		at91_adc_reg_write(st, st->desc->trigger_register,
> +				   status & ~value);
> +
> +		for_each_set_bit(bit, buffer->scan_mask,
> +				 st->desc->num_channels) {
> +			struct iio_chan_spec const *chan = idev->channels + bit;
> +			at91_adc_reg_write(st, AT91_ADC_CHDR,
> +					   AT91_ADC_CH(chan->channel));
> +		}
> +		kfree(st->buffer);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_trigger_ops at91_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.set_trigger_state = &at91_adc_configure_trigger,
> +};
> +
> +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
> +						     struct at91_adc_trigger *trigger)
> +{
> +	struct iio_trigger *trig = iio_allocate_trigger("%s-dev%d-%s",
> +							idev->name,
> +							idev->id,
> +							trigger->name);
> +	int ret;
> +
> +	if (trig == NULL)
> +		return NULL;
> +
> +	trig->dev.parent = idev->dev.parent;
> +	trig->private_data = idev;
> +	trig->ops = &at91_adc_trigger_ops;
> +
> +	ret = iio_trigger_register(trig);
> +	if (ret < 0)
> +		return NULL;
> +
> +	return trig;
> +}
> +
> +static int at91_adc_trigger_init(struct iio_dev *idev,
> +				 struct at91_adc_data *pdata)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	int i, ret;
> +
> +	st->trig = kcalloc(sizeof(st->desc->triggers),
> +			   sizeof(st->trig),
> +			   GFP_KERNEL);
> +
> +	if (st->trig == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
> +		if (st->desc->triggers[i].is_external && !(pdata->use_external))
> +			continue;
> +
> +		st->trig[i] = at91_adc_allocate_trigger(idev,
> +							st->desc->triggers + i);
> +		if (st->trig[i] == NULL) {
> +			dev_err(&idev->dev,
> +				"Could not allocate trigger %d\n", i);
> +			ret = -ENOMEM;
> +			goto error_trigger;
> +		}
> +	}
> +
> +	return 0;
> +
> +error_trigger:
> +	for (i--; i >= 0; i--) {
> +		iio_trigger_unregister(st->trig[i]);
> +		iio_free_trigger(st->trig[i]);
> +	}
> +	kfree(st->trig);
> +error_ret:
> +	return ret;
> +}
> +
> +static void at91_adc_trigger_remove(struct iio_dev *idev)
> +{
> +	struct at91_adc_state *st = iio_priv(idev);
> +	int i;
> +
> +	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
> +		iio_trigger_unregister(st->trig[i]);
> +		iio_free_trigger(st->trig[i]);
> +	}
> +
> +	kfree(st->trig);
> +}
> +
> +static const struct iio_buffer_setup_ops at91_adc_buffer_ops = {
> +	.preenable = &iio_sw_buffer_preenable,
That's handy. Any other preenable and the series I sent out today
would require some changes.  As is, it 'should' just work and give
you multiple buffer support.
(now if you happened to have time to test that... ;)
> +	.postenable = &iio_triggered_buffer_postenable,
> +	.predisable = &iio_triggered_buffer_predisable,
> +};
> +
> +static int at91_adc_buffer_init(struct iio_dev *idev)
> +{
> +	int ret;
> +
> +	idev->buffer = iio_sw_rb_allocate(idev);
> +	if (!idev->buffer) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	idev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
> +					    &at91_adc_trigger_handler,
> +					    IRQF_ONESHOT,
> +					    idev,
> +					    "%s-consumer%d",
> +					    idev->name,
> +					    idev->id);
> +	if (idev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_pollfunc;
> +	}
> +
> +	idev->setup_ops = &at91_adc_buffer_ops;
> +	idev->modes |= INDIO_BUFFER_TRIGGERED;
> +
> +	ret = iio_buffer_register(idev,
> +				  idev->channels,
> +				  idev->num_channels);
> +	if (ret)
> +		goto error_register;
> +
> +	return 0;
> +
> +error_register:
> +	iio_dealloc_pollfunc(idev->pollfunc);
> +error_pollfunc:
> +	iio_sw_rb_free(idev->buffer);
> +error_ret:
> +	return ret;
> +}
> +
> +static void at91_adc_buffer_remove(struct iio_dev *idev)
> +{
> +	iio_buffer_unregister(idev);
> +	iio_dealloc_pollfunc(idev->pollfunc);
> +	iio_sw_rb_free(idev->buffer);
> +}
> +
>  static int at91_adc_read_raw(struct iio_dev *idev,
>  			     struct iio_chan_spec const *chan,
>  			     int *val, int *val2, long mask)
> @@ -344,14 +701,30 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
>  	st->vref_mv = pdata->vref;
>  	st->channels_mask = pdata->channels_used;
>  
> +	ret = at91_adc_buffer_init(idev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
> +		goto error_free_channels;
> +	}
> +
> +	ret = at91_adc_trigger_init(idev, pdata);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
> +		goto error_unregister_buffer;
> +	}
> +
>  	ret = iio_device_register(idev);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "Couldn't register the device.\n");
> -		goto error_free_channels;
> +		goto error_remove_triggers;
>  	}
>  
>  	return 0;
>  
> +error_remove_triggers:
> +	at91_adc_trigger_remove(idev);
> +error_unregister_buffer:
> +	at91_adc_buffer_remove(idev);
>  error_free_channels:
>  	at91_adc_channel_remove(idev);
>  error_disable_clk:
> @@ -379,6 +752,8 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
>  	struct at91_adc_state *st = iio_priv(idev);
>  
>  	iio_device_unregister(idev);
> +	at91_adc_trigger_remove(idev);
> +	at91_adc_buffer_remove(idev);
>  	at91_adc_channel_remove(idev);
>  	clk_disable(st->clk);
>  	clk_unprepare(st->clk);
> diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
> index 8b51ee6..8750d80 100644
> --- a/include/linux/platform_data/at91_adc.h
> +++ b/include/linux/platform_data/at91_adc.h
> @@ -10,10 +10,12 @@
>  /**
>   * struct at91_adc_data - platform data for ADC driver
>   * @channels_used:	channels in use on the board as a bitmask
> + * @use_external:	does the board has external triggers availables
That's not exactly a well named variable.  Howabout,
use_external_triggers?
>   * @vref:		Reference voltage for the ADC in millivolts
>   */
>  struct at91_adc_data {
>  	unsigned long	channels_used;
> +	bool		use_external;
>  	u16		vref;
>  };
>  

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

* [PATCH 5/6] IIO: AT91: Add support for hardware triggers for the ADC
  2012-04-18 18:46     ` Jonathan Cameron
  (?)
@ 2012-04-19 14:24     ` Maxime Ripard
  -1 siblings, 0 replies; 23+ messages in thread
From: Maxime Ripard @ 2012-04-19 14:24 UTC (permalink / raw)
  To: linux-arm-kernel

Le 18/04/2012 20:46, Jonathan Cameron a ?crit :
> On 04/18/2012 02:33 PM, Maxime Ripard wrote:
>> This patch adds support for the hardware triggers for both the
>> AT91SAM9G20-EK and AT91SAM9M10G45-EK evaluation board from Atmel.
>>
> 
> Couple of queries inline.
> 
> The 'interesting' bit is why it is currently possible for
> iio_dev->active_scan_mask to differ from buffer->scan_mask?
> I'm curious given this leads to a demux  operation within the
> driver.
> 
> The name based matching is uggly. Maybe we should just add
> an 'index' field to the iio_trigger structure?
> I'm sure other users will turn up...

I have to agree on that. I'll try to come up with something once the
driver will be included.

> I've also hightlighted a few areas where a current patch
> set of mine (introducing multiple buffers) will need
> changes in here (about as trivial as they are in any
> driver..)
> 
> 
> Jonathan
> 
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>>
>> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com>
>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>
>> ---
>>  arch/arm/mach-at91/at91sam9260_devices.c |    3 +
>>  arch/arm/mach-at91/at91sam9g45_devices.c |    3 +
>>  arch/arm/mach-at91/board-sam9g20ek.c     |    1 +
>>  arch/arm/mach-at91/board-sam9m10g45ek.c  |    1 +
>>  drivers/staging/iio/adc/Kconfig          |    3 +
>>  drivers/staging/iio/adc/at91_adc.c       |  389 +++++++++++++++++++++++++++++-
>>  include/linux/platform_data/at91_adc.h   |    2 +
>>  7 files changed, 395 insertions(+), 7 deletions(-)
>>
>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
>> index 758c629..a870753 100644
>> --- a/arch/arm/mach-at91/at91sam9260_devices.c
>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c
>> @@ -1415,6 +1415,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
>>  	if (test_bit(3, &data->channels_used))
>>  		at91_set_A_periph(AT91_PIN_PC3, 0);
>>  
>> +	if (data->use_external)
>> +		at91_set_A_periph(AT91_PIN_PA22, 0);
>> +
>>  	adc_data = *data;
>>  	platform_device_register(&at91_adc_device);
>>  }
>> diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
>> index 05bc673..4ee18af 100644
>> --- a/arch/arm/mach-at91/at91sam9g45_devices.c
>> +++ b/arch/arm/mach-at91/at91sam9g45_devices.c
>> @@ -1260,6 +1260,9 @@ void __init at91_add_device_adc(struct at91_adc_data *data)
>>  	if (test_bit(7, &data->channels_used))
>>  		at91_set_gpio_input(AT91_PIN_PD27, 0);
>>  
>> +	if (data->use_external)
>> +		at91_set_A_periph(AT91_PIN_PD28, 0);
>> +
>>  	adc_data = *data;
>>  	platform_device_register(&at91_adc_device);
>>  }
>> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
>> index 2ce4e26..3bb6e4d 100644
>> --- a/arch/arm/mach-at91/board-sam9g20ek.c
>> +++ b/arch/arm/mach-at91/board-sam9g20ek.c
>> @@ -326,6 +326,7 @@ static void __init ek_add_device_buttons(void) {}
>>  
>>  static struct at91_adc_data ek_adc_data = {
>>  	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
>> +	.use_external = true,
>>  	.vref = 3300,
>>  };
>>  
>> diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
>> index fb122ba..192a050 100644
>> --- a/arch/arm/mach-at91/board-sam9m10g45ek.c
>> +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
>> @@ -322,6 +322,7 @@ static struct at91_tsadcc_data ek_tsadcc_data = {
>>   */
>>  static struct at91_adc_data ek_adc_data = {
>>  	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | BIT(6) | BIT(7),
>> +	.use_external = true,
>>  	.vref = 3300,
>>  };
>>  
>> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
>> index 1494838..9ee7ed9 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -172,6 +172,9 @@ config AD7280
>>  config AT91_ADC
>>  	tristate "Atmel AT91 ADC"
>>  	depends on SYSFS && ARCH_AT91
>> +	select IIO_BUFFER
>> +	select IIO_SW_RING
>> +	select IIO_TRIGGER
>>  	help
>>  	  Say yes here to build support for Atmel AT91 ADC.
>>  
>> diff --git a/drivers/staging/iio/adc/at91_adc.c b/drivers/staging/iio/adc/at91_adc.c
>> index b3517bb..7c93cde 100644
>> --- a/drivers/staging/iio/adc/at91_adc.c
>> +++ b/drivers/staging/iio/adc/at91_adc.c
>> @@ -23,9 +23,33 @@
>>  #include "../iio.h"
>>  #include <linux/platform_data/at91_adc.h>
>>  
>> +#include "../buffer.h"
>> +#include "../ring_sw.h"
>> +#include "../trigger.h"
>> +#include "../trigger_consumer.h"
>> +
>>  #include <mach/at91_adc.h>
>>  #include <mach/cpu.h>
>>  
>> +#define AT91_TSADCC_TRGR		(0x08)
>> +#define AT91_TSADCC_TRGMOD_EXT_RISING	(1 << 0)
>> +#define AT91_TSADCC_TRGMOD_EXT_FALLING	(1 << 1)
>> +#define AT91_TSADCC_TRGMOD_EXT_ANY	(AT91_TSADCC_TRGMOD_EXT_RISING | AT91_TSADCC_TRGMOD_EXT_FALLING)
>> +#define AT91_TSADCC_TRGMOD_CONTINUOUS	(3 << 1)
>> +
>> +/**
>> + * struct at91_adc_trigger - description of triggers
>> + * @name:		name of the trigger advertised to the user
>> + * @value:		value to set in the ADC's mode register to enable
>> +			the trigger
>> + * @is_external:	is the trigger relies on an external pin ?
>> + */
>> +struct at91_adc_trigger {
>> +	char	*name;
>> +	u8	value;
>> +	bool	is_external;
>> +};
>> +
>>  /**
>>   * struct at91_adc_desc - description of the ADC on the board
>>   * @clock:		ADC clock as specified by the datasheet, in Hz.
>> @@ -34,36 +58,99 @@
>>  			board, see the channels_used bitmask in the platform
>>  			data)
>>   * @startup_time:	startup time of the ADC in microseconds
>> + * @triggers:		array of the triggers available on the board
>> + * @trigger_register:	index of the register managing the hardware triggers
>>   */
>>  struct at91_adc_desc {
>>  	u32			clock;
>>  	u8			num_channels;
>>  	u8			startup_time;
>> +	struct at91_adc_trigger	*triggers;
>> +	u8			trigger_register;
>>  };
>>  
>>  struct at91_adc_state {
>> +	u16			*buffer;
>>  	unsigned long		channels_mask;
>>  	struct clk		*clk;
>>  	bool			done;
>>  	struct at91_adc_desc	*desc;
>>  	int			irq;
>> +	bool			irq_enabled;
>>  	u16			last_value;
>>  	struct mutex		lock;
>>  	void __iomem		*reg_base;
>> +	struct iio_trigger	**trig;
>>  	u32			vref_mv;
>>  	wait_queue_head_t	wq_data_avail;
>>  };
>>  
>> +static struct at91_adc_trigger at91_adc_trigger_sam9g45[] = {
>> +	[0] = {
>> +		.name = "external-rising",
>> +		.value = AT91_TSADCC_TRGMOD_EXT_RISING,
>> +		.is_external = true,
>> +	},
>> +	[1] = {
>> +		.name = "external-falling",
>> +		.value = AT91_TSADCC_TRGMOD_EXT_FALLING,
>> +		.is_external = true,
>> +	},
>> +	[2] = {
>> +		.name = "external-any",
>> +		.value = AT91_TSADCC_TRGMOD_EXT_ANY,
>> +		.is_external = true,
>> +	},
>> +	[3] = {
>> +		.name = "continuous",
>> +		.value = AT91_TSADCC_TRGMOD_CONTINUOUS,
>> +		.is_external = false,
>> +	},
>> +	[4] = {
>> +		.name = NULL,
>> +	},
>> +};
>> +
>>  static struct at91_adc_desc at91_adc_desc_sam9g45 = {
>>  	.clock = 13200000,
>>  	.num_channels = 8,
>>  	.startup_time = 40,
>> +	.triggers = at91_adc_trigger_sam9g45,
>> +	.trigger_register = AT91_TSADCC_TRGR,
>> +};
>> +
>> +static struct at91_adc_trigger at91_adc_trigger_sam9g20[] = {
>> +	[0] = {
>> +		.name = "timer-counter-0",
>> +		.value = AT91_ADC_TRGSEL_TC0 | AT91_ADC_TRGEN,
>> +		.is_external = false,
>> +	},
>> +	[1] = {
>> +		.name = "timer-counter-1",
>> +		.value = AT91_ADC_TRGSEL_TC1 | AT91_ADC_TRGEN,
>> +		.is_external = false,
>> +	},
>> +	[2] = {
>> +		.name = "timer-counter-2",
>> +		.value = AT91_ADC_TRGSEL_TC2 | AT91_ADC_TRGEN,
>> +		.is_external = false,
>> +	},
>> +	[3] = {
>> +		.name = "external",
>> +		.value = AT91_ADC_TRGSEL_EXTERNAL | AT91_ADC_TRGEN,
>> +		.is_external = true,
>> +	},
>> +	[4] = {
>> +		.name = NULL,
>> +	},
>>  };
>>  
>>  static struct at91_adc_desc at91_adc_desc_sam9g20 = {
>>  	.clock = 5000000,
>>  	.num_channels = 4,
>>  	.startup_time = 10,
>> +	.triggers = at91_adc_trigger_sam9g20,
>> +	.trigger_register = AT91_ADC_MR,
>>  };
>>  
>>  static int at91_adc_select_soc(struct at91_adc_state *st)
>> @@ -94,6 +181,48 @@ static inline void at91_adc_reg_write(struct at91_adc_state *st,
>>  	writel_relaxed(val, st->reg_base + reg);
>>  }
>>  
>> +static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
>> +{
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *idev = pf->indio_dev;
>> +	struct at91_adc_state *st = iio_priv(idev);
>> +	struct iio_buffer *buffer = idev->buffer;
>> +	int len = 0;
>> +
> Don't think this if adds much as the for loop will just not
> do anything if no bits are set.
> 
>> +	if (!bitmap_empty(idev->active_scan_mask, idev->masklength)) {
>> +		int i, j;
>> +		for (i = 0, j = 0;
>> +		     i < bitmap_weight(idev->active_scan_mask,
>> +				       idev->masklength);
>> +		     i++) {
>> +			j = find_next_bit(buffer->scan_mask,
>> +					  idev->masklength,
>> +					  j);
> This looks rather like a demux operation. How costly are the register reads?
> If not terribly I'd read the lot and let the demuxer in the core handle
> pulling
> the correct values out...

Hmm, actually, that only maps the channels enabled by the user to
indexes in the buffer.

I guess some simplification would be:

int i, j=0;

for (i = 0; i < idev->masklength; i++) {
    if (!test_bit(i, &(idev->active_scan_mask))
        continue;
    st->buffer[j] = at91_adc_reg_read(st, AT91_ADC_CHR(i));
    j++;
}

I'n not quite sure about what you mean by demux here.

> There are only a few reasons why buffer->scan_mask !=
> idev->active_scan_mask.
> 
> 1) only some masks are possible (not true here?)
> 2) Multiple buffers are being filled.  This code is pre that being possible
> and anyway if it were you'd want to be reading everythin in the mask...
> 3) something I haven't thought of, in which case please explain!


Hmm, that must be a typo, as I don't know the difference between the
two. Could you explain? I'm interested :)

>> +			st->buffer[i] = at91_adc_reg_read(st, AT91_ADC_CHR(j));
>> +			j++;
>> +			len += 2;
>> +		}
>> +	}
>> +
>> +	if (buffer->scan_timestamp) {
>> +		s64 *timestamp = (s64 *)((u8 *)st->buffer + ALIGN(len,
>> +								  sizeof(s64)));
> I'd break the line after the + (nitpick!)

ACK :)

>> +		*timestamp = pf->timestamp;
>> +	}
>> +
>> +	buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
> As a heads up, a patch set I have out for review stops drivers doing
> this and
> instead routes everything through a generic function which in turn calls a
> demux on demand and pushes data on to any number of buffers.

Yes, I've seen that. Should I move to it now ?

>> +
>> +	iio_trigger_notify_done(idev->trig);
>> +	st->irq_enabled = true;
>> +
>> +	/* Needed to ACK the DRDY interruption */
>> +	at91_adc_reg_read(st, AT91_ADC_LCDR);
>> +
>> +	enable_irq(st->irq);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>>  static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
>>  {
>>  	struct iio_dev *idev = private;
>> @@ -103,13 +232,16 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
>>  	if (!(status & AT91_ADC_DRDY))
>>  		return IRQ_HANDLED;
>>  
>> -	if (status & st->channels_mask) {
>> -		st->done = true;
>> +	if (iio_buffer_enabled(idev)) {
>> +		disable_irq_nosync(irq);
>> +		st->irq_enabled = false;
>> +		iio_trigger_poll(idev->trig, iio_get_time_ns());
>> +	} else {
>>  		st->last_value = at91_adc_reg_read(st, AT91_ADC_LCDR);
>> +		st->done = true;
>> +		wake_up_interruptible(&st->wq_data_avail);
>>  	}
>>  
>> -	wake_up_interruptible(&st->wq_data_avail);
>> -
>>  	return IRQ_HANDLED;
>>  }
>>  
>> @@ -121,10 +253,10 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>>  	int bit, idx = 0;
>>  
>>  	idev->num_channels = bitmap_weight(&pdata->channels_used,
>> -					   st->desc->num_channels);
>> +					   st->desc->num_channels) + 1;
>>  
>>  	chan_array = devm_kzalloc(&idev->dev,
>> -				  idev->num_channels * sizeof(struct iio_chan_spec),
>> +				  (idev->num_channels + 1) * sizeof(struct iio_chan_spec),
>>  				  GFP_KERNEL);
>>  
>>  	if (chan_array == NULL)
>> @@ -135,6 +267,7 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>>  		chan->type = IIO_VOLTAGE;
>>  		chan->indexed = 1;
>>  		chan->channel = bit;
>> +		chan->scan_index = idx;
>>  		chan->scan_type.sign = 'u';
>>  		chan->scan_type.realbits = 10;
>>  		chan->scan_type.storagebits = 16;
>> @@ -142,6 +275,13 @@ static int at91_adc_channel_init(struct iio_dev *idev,
>>  		idx++;
>>  	}
>>  
>> +	(chan_array + idx)->type = IIO_TIMESTAMP;
>> +	(chan_array + idx)->channel = -1;
>> +	(chan_array + idx)->scan_index = idx;
>> +	(chan_array + idx)->scan_type.sign = 's';
>> +	(chan_array + idx)->scan_type.realbits = 64;
>> +	(chan_array + idx)->scan_type.storagebits = 64;
>> +
>>  	idev->channels = chan_array;
>>  	return idev->num_channels;
>>  }
>> @@ -151,6 +291,223 @@ static void at91_adc_channel_remove(struct iio_dev *idev)
>>  	devm_kfree(&idev->dev, (void*)idev->channels);
>>  }
>>  
>> +static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
>> +					     struct at91_adc_trigger *triggers,
>> +					     const char *trigger_name)
>> +{
>> +	u8 value = 0;
>> +	int i;
>> +
>> +	for (i = 0; (triggers + i) != NULL; i++) {
>> +		char *name = kasprintf(GFP_KERNEL,
>> +				"%s-dev%d-%s",
>> +				idev->name,
>> +				idev->id,
>> +				triggers[i].name);
>> +		if (name == NULL)
>> +			return -ENOMEM;
>> +
>> +		if (strcmp(trigger_name, name) == 0) {
>> +			value = triggers[i].value;
>> +			kfree(name);
>> +			break;
>> +		}
>> +
>> +		kfree(name);
>> +	}
>> +
>> +	return value;
>> +}
>> +
>> +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
>> +{
>> +	struct iio_dev *idev = trig->private_data;
>> +	struct at91_adc_state *st = iio_priv(idev);
>> +	struct iio_buffer *buffer = idev->buffer;
>> +	u32 status = at91_adc_reg_read(st, st->desc->trigger_register);
>> +	u8 value;
>> +	u8 bit;
>> +
> Hmm.. this seems rather convoluted...  Not that I can immediately think
> of a better way... So I'll moan and let it go ;)

Yes, couldn't find a better way to restore the initial state except by
doing that....

>> +	value = at91_adc_get_trigger_value_by_name(idev,
>> +						   st->desc->triggers,
>> +						   idev->trig->name);
>> +	if (value == 0)
>> +		return -EINVAL;
>> +
>> +	if (state) {
>> +		size_t datasize = buffer->access->get_bytes_per_datum(buffer);
>> +
>> +		st->buffer = kmalloc(datasize, GFP_KERNEL);
>> +		if (st->buffer == NULL)
>> +			return -ENOMEM;
>> +
>> +		at91_adc_reg_write(st,
>> +				   st->desc->trigger_register,
>> +				   status | value);
>> +
>> +		for_each_set_bit(bit, buffer->scan_mask,
>> +				 st->desc->num_channels) {
>> +			struct iio_chan_spec const *chan = idev->channels + bit;
>> +			at91_adc_reg_write(st, AT91_ADC_CHER,
>> +					   AT91_ADC_CH(chan->channel));
>> +		}
>> +
>> +		at91_adc_reg_write(st, AT91_ADC_IER, AT91_ADC_DRDY);
>> +
>> +	} else {
>> +		at91_adc_reg_write(st, AT91_ADC_IDR, AT91_ADC_DRDY);
>> +
>> +		at91_adc_reg_write(st, st->desc->trigger_register,
>> +				   status & ~value);
>> +
>> +		for_each_set_bit(bit, buffer->scan_mask,
>> +				 st->desc->num_channels) {
>> +			struct iio_chan_spec const *chan = idev->channels + bit;
>> +			at91_adc_reg_write(st, AT91_ADC_CHDR,
>> +					   AT91_ADC_CH(chan->channel));
>> +		}
>> +		kfree(st->buffer);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_trigger_ops at91_adc_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +	.set_trigger_state = &at91_adc_configure_trigger,
>> +};
>> +
>> +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
>> +						     struct at91_adc_trigger *trigger)
>> +{
>> +	struct iio_trigger *trig = iio_allocate_trigger("%s-dev%d-%s",
>> +							idev->name,
>> +							idev->id,
>> +							trigger->name);
>> +	int ret;
>> +
>> +	if (trig == NULL)
>> +		return NULL;
>> +
>> +	trig->dev.parent = idev->dev.parent;
>> +	trig->private_data = idev;
>> +	trig->ops = &at91_adc_trigger_ops;
>> +
>> +	ret = iio_trigger_register(trig);
>> +	if (ret < 0)
>> +		return NULL;
>> +
>> +	return trig;
>> +}
>> +
>> +static int at91_adc_trigger_init(struct iio_dev *idev,
>> +				 struct at91_adc_data *pdata)
>> +{
>> +	struct at91_adc_state *st = iio_priv(idev);
>> +	int i, ret;
>> +
>> +	st->trig = kcalloc(sizeof(st->desc->triggers),
>> +			   sizeof(st->trig),
>> +			   GFP_KERNEL);
>> +
>> +	if (st->trig == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +
>> +	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
>> +		if (st->desc->triggers[i].is_external && !(pdata->use_external))
>> +			continue;
>> +
>> +		st->trig[i] = at91_adc_allocate_trigger(idev,
>> +							st->desc->triggers + i);
>> +		if (st->trig[i] == NULL) {
>> +			dev_err(&idev->dev,
>> +				"Could not allocate trigger %d\n", i);
>> +			ret = -ENOMEM;
>> +			goto error_trigger;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +error_trigger:
>> +	for (i--; i >= 0; i--) {
>> +		iio_trigger_unregister(st->trig[i]);
>> +		iio_free_trigger(st->trig[i]);
>> +	}
>> +	kfree(st->trig);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static void at91_adc_trigger_remove(struct iio_dev *idev)
>> +{
>> +	struct at91_adc_state *st = iio_priv(idev);
>> +	int i;
>> +
>> +	for (i = 0; st->desc->triggers[i].name != NULL; i++) {
>> +		iio_trigger_unregister(st->trig[i]);
>> +		iio_free_trigger(st->trig[i]);
>> +	}
>> +
>> +	kfree(st->trig);
>> +}
>> +
>> +static const struct iio_buffer_setup_ops at91_adc_buffer_ops = {
>> +	.preenable = &iio_sw_buffer_preenable,
> That's handy. Any other preenable and the series I sent out today
> would require some changes.  As is, it 'should' just work and give
> you multiple buffer support.
> (now if you happened to have time to test that... ;)
>> +	.postenable = &iio_triggered_buffer_postenable,
>> +	.predisable = &iio_triggered_buffer_predisable,
>> +};
>> +
>> +static int at91_adc_buffer_init(struct iio_dev *idev)
>> +{
>> +	int ret;
>> +
>> +	idev->buffer = iio_sw_rb_allocate(idev);
>> +	if (!idev->buffer) {
>> +		ret = -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +
>> +	idev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
>> +					    &at91_adc_trigger_handler,
>> +					    IRQF_ONESHOT,
>> +					    idev,
>> +					    "%s-consumer%d",
>> +					    idev->name,
>> +					    idev->id);
>> +	if (idev->pollfunc == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_pollfunc;
>> +	}
>> +
>> +	idev->setup_ops = &at91_adc_buffer_ops;
>> +	idev->modes |= INDIO_BUFFER_TRIGGERED;
>> +
>> +	ret = iio_buffer_register(idev,
>> +				  idev->channels,
>> +				  idev->num_channels);
>> +	if (ret)
>> +		goto error_register;
>> +
>> +	return 0;
>> +
>> +error_register:
>> +	iio_dealloc_pollfunc(idev->pollfunc);
>> +error_pollfunc:
>> +	iio_sw_rb_free(idev->buffer);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static void at91_adc_buffer_remove(struct iio_dev *idev)
>> +{
>> +	iio_buffer_unregister(idev);
>> +	iio_dealloc_pollfunc(idev->pollfunc);
>> +	iio_sw_rb_free(idev->buffer);
>> +}
>> +
>>  static int at91_adc_read_raw(struct iio_dev *idev,
>>  			     struct iio_chan_spec const *chan,
>>  			     int *val, int *val2, long mask)
>> @@ -344,14 +701,30 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
>>  	st->vref_mv = pdata->vref;
>>  	st->channels_mask = pdata->channels_used;
>>  
>> +	ret = at91_adc_buffer_init(idev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
>> +		goto error_free_channels;
>> +	}
>> +
>> +	ret = at91_adc_trigger_init(idev, pdata);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
>> +		goto error_unregister_buffer;
>> +	}
>> +
>>  	ret = iio_device_register(idev);
>>  	if (ret < 0) {
>>  		dev_err(&pdev->dev, "Couldn't register the device.\n");
>> -		goto error_free_channels;
>> +		goto error_remove_triggers;
>>  	}
>>  
>>  	return 0;
>>  
>> +error_remove_triggers:
>> +	at91_adc_trigger_remove(idev);
>> +error_unregister_buffer:
>> +	at91_adc_buffer_remove(idev);
>>  error_free_channels:
>>  	at91_adc_channel_remove(idev);
>>  error_disable_clk:
>> @@ -379,6 +752,8 @@ static int __devexit at91_adc_remove(struct platform_device *pdev)
>>  	struct at91_adc_state *st = iio_priv(idev);
>>  
>>  	iio_device_unregister(idev);
>> +	at91_adc_trigger_remove(idev);
>> +	at91_adc_buffer_remove(idev);
>>  	at91_adc_channel_remove(idev);
>>  	clk_disable(st->clk);
>>  	clk_unprepare(st->clk);
>> diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h
>> index 8b51ee6..8750d80 100644
>> --- a/include/linux/platform_data/at91_adc.h
>> +++ b/include/linux/platform_data/at91_adc.h
>> @@ -10,10 +10,12 @@
>>  /**
>>   * struct at91_adc_data - platform data for ADC driver
>>   * @channels_used:	channels in use on the board as a bitmask
>> + * @use_external:	does the board has external triggers availables
> That's not exactly a well named variable.  Howabout,
> use_external_triggers?

Ok, I will change that.

>>   * @vref:		Reference voltage for the ADC in millivolts
>>   */
>>  struct at91_adc_data {
>>  	unsigned long	channels_used;
>> +	bool		use_external;
>>  	u16		vref;
>>  };
>>  
> 


-- 
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] 23+ messages in thread

end of thread, other threads:[~2012-04-19 14:24 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-18 13:33 [PATCHv2] Add ADC driver for G20 and G45 evaluation boards Maxime Ripard
2012-04-18 13:33 ` Maxime Ripard
2012-04-18 13:33 ` [PATCH 1/6] ARM: AT91: Add platform data for the AT91 ADCs Maxime Ripard
2012-04-18 13:33   ` Maxime Ripard
2012-04-18 13:33 ` [PATCH 2/6] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard
2012-04-18 13:33   ` Maxime Ripard
2012-04-18 17:52   ` Jonathan Cameron
2012-04-18 17:52     ` Jonathan Cameron
2012-04-18 13:33 ` [PATCH 3/6] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard
2012-04-18 13:33   ` Maxime Ripard
2012-04-18 17:54   ` Jonathan Cameron
2012-04-18 17:54     ` Jonathan Cameron
2012-04-18 13:33 ` [PATCH 4/6] IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board Maxime Ripard
2012-04-18 13:33   ` Maxime Ripard
2012-04-18 17:57   ` Jonathan Cameron
2012-04-18 17:57     ` Jonathan Cameron
2012-04-18 13:33 ` [PATCH 5/6] IIO: AT91: Add support for hardware triggers for the ADC Maxime Ripard
2012-04-18 13:33   ` Maxime Ripard
2012-04-18 18:46   ` Jonathan Cameron
2012-04-18 18:46     ` Jonathan Cameron
2012-04-19 14:24     ` Maxime Ripard
2012-04-18 13:33 ` [PATCH 6/6] IIO: AT91: Add DT support to at91_adc driver Maxime Ripard
2012-04-18 13:33   ` 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.