Skip to contentSkip to conversation

Add ADC driver for G20 and G45 evaluation boards

PATCH Superseded Maxime Ripard 2 participants 12 messages Apr 18, 2012 – Apr 19, 2012

Subsystems (7)

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 Add ADC driver for G20 and G45 evaluation boards

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

#1 lore raw

PATCH 1/6 ARM: AT91: Add platform data for the AT91 ADCs

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.

Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
---
 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
include/linux/platform_data/at91_adc.h new file +22 -0
new file mode 100644
index 0000000..8b51ee6
@@ -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
Signature · 1 line
1.7.5.4
#2 lore raw

PATCH 2/6 ARM: AT91: IIO: Add AT91 ADC driver.

In reply to Maxime Ripard

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.

Cc Nicolas Ferre <[email protected]>
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Jonathan Cameron <[email protected]>
---
 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
drivers/staging/iio/adc/Kconfig +6 -0
index 592eabd..1494838 100644
@@ -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
drivers/staging/iio/adc/Makefile +1 -0
index f83ab95..8cb6d1c 100644
@@ -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
drivers/staging/iio/adc/at91_adc.c new file +396 -0

396 lines not on this page · raw message

Signature · 1 line
1.7.5.4
maintainer
#3 lore raw

PATCH 2/6 ARM: AT91: IIO: Add AT91 ADC driver.

Quoted 9 lines · Maxime Ripard

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..

Quoted 1 line
Quoted 35 lines
Cc Nicolas Ferre <[email protected]>
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Jonathan Cameron <[email protected]>
---
 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
drivers/staging/iio/adc/Kconfig +6 -0
index 592eabd..1494838 100644
@@ -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
drivers/staging/iio/adc/Makefile +0 -0
index f83ab95..8cb6d1c 100644

drivers/staging/iio/adc/Makefile obj-$(CONFIG_ADT7310) += adt7310.o · line 41

Quoted 4 lines
drivers/staging/iio/adc/Makefile +1 -0
@@ -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 ;)

Quoted 127 lines
drivers/staging/iio/adc/at91_adc.c new file +121 -0
new file mode 100644
index 0000000..6d14277
@@ -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;

drivers/staging/iio/adc/at91_adc.c line 131

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c new file +10 -0
+
+	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.

Quoted 11 lines
drivers/staging/iio/adc/at91_adc.c new file +11 -0
+		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);
+}

drivers/staging/iio/adc/at91_adc.c line 152

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c new file +10 -0
+
+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:

Quoted 244 lines
drivers/staging/iio/adc/at91_adc.c new file +244 -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 <[email protected]>");
#4 lore raw

PATCH 3/6 ARM: AT91: Add the ADC to the sam9g20ek board

In reply to Maxime Ripard

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

Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9260_devices.c +53 -0
index 5652dde..758c629 100644
@@ -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
arch/arm/mach-at91/board-sam9g20ek.c +13 -0
index 8923ec9..2ce4e26 100644
@@ -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) */
drivers/staging/iio/adc/at91_adc.c +0 -1
index 6d14277..9af036a 100644
@@ -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,
 };
Signature · 1 line
1.7.5.4
maintainer
#5 lore raw

PATCH 3/6 ARM: AT91: Add the ADC to the sam9g20ek board

Quoted 2 lines · Maxime Ripard

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.

Quoted 2 lines
Quoted 132 lines
Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9260_devices.c +53 -0
index 5652dde..758c629 100644
@@ -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
arch/arm/mach-at91/board-sam9g20ek.c +13 -0
index 8923ec9..2ce4e26 100644
@@ -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) */
drivers/staging/iio/adc/at91_adc.c +0 -1
index 6d14277..9af036a 100644
@@ -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,
 };
#6 lore raw

PATCH 4/6 IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board

In reply to Maxime Ripard

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.

Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Nicolas Ferre <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9g45.c +2 -0
index d222f83..c6c35bc 100644
@@ -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[] = {
arch/arm/mach-at91/at91sam9g45_devices.c +61 -0

73 lines not on this page · raw message

arch/arm/mach-at91/board-sam9m10g45ek.c +11 -0
index c88e908..fb122ba 100644
@@ -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 */
drivers/staging/iio/adc/at91_adc.c +11 -0
index 9af036a..b3517bb 100644
@@ -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;
 }
 
Signature · 1 line
1.7.5.4
maintainer
#7 lore raw

PATCH 4/6 IIO: AT91: ADC: Add support for the AT91SAM9M10G45-EK board

Quoted 3 lines · Maxime Ripard

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!

Quoted 171 lines
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Nicolas Ferre <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9g45.c +2 -0
index d222f83..c6c35bc 100644
@@ -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[] = {
arch/arm/mach-at91/at91sam9g45_devices.c +61 -0
index 6b008ae..05bc673 100644
@@ -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
  * -------------------------------------------------------------------- */
 
arch/arm/mach-at91/board-sam9m10g45ek.c +11 -0
index c88e908..fb122ba 100644
@@ -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 */
drivers/staging/iio/adc/at91_adc.c +11 -0
index 9af036a..b3517bb 100644
@@ -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;
 }
 
#8 lore raw

PATCH 5/6 IIO: AT91: Add support for hardware triggers for the ADC

In reply to Maxime Ripard

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

Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Nicolas Ferre <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9260_devices.c +3 -0
index 758c629..a870753 100644
@@ -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);
 }
arch/arm/mach-at91/at91sam9g45_devices.c +3 -0
index 05bc673..4ee18af 100644
@@ -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);
 }
arch/arm/mach-at91/board-sam9g20ek.c +1 -0
index 2ce4e26..3bb6e4d 100644
@@ -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,
 };
 
arch/arm/mach-at91/board-sam9m10g45ek.c +1 -0
index fb122ba..192a050 100644
@@ -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,
 };
 
drivers/staging/iio/adc/Kconfig +3 -0
index 1494838..9ee7ed9 100644
@@ -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.
 
drivers/staging/iio/adc/at91_adc.c +382 -7

494 lines not on this page · raw message

include/linux/platform_data/at91_adc.h +2 -0
index 8b51ee6..8750d80 100644
@@ -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;
 };
 
Signature · 1 line
1.7.5.4
maintainer
#9 lore raw

PATCH 5/6 IIO: AT91: Add support for hardware triggers for the ADC

Quoted 3 lines · Maxime Ripard

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

Quoted 222 lines
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Nicolas Ferre <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9260_devices.c +3 -0
index 758c629..a870753 100644
@@ -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);
 }
arch/arm/mach-at91/at91sam9g45_devices.c +3 -0
index 05bc673..4ee18af 100644
@@ -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);
 }
arch/arm/mach-at91/board-sam9g20ek.c +1 -0
index 2ce4e26..3bb6e4d 100644
@@ -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,
 };
 
arch/arm/mach-at91/board-sam9m10g45ek.c +1 -0
index fb122ba..192a050 100644
@@ -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,
 };
 
drivers/staging/iio/adc/Kconfig +3 -0
index 1494838..9ee7ed9 100644
@@ -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.
 
drivers/staging/iio/adc/at91_adc.c +87 -0
index b3517bb..7c93cde 100644
@@ -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);

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 191

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c +8 -0
 }
 
+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.

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 200

Quoted 9 lines
drivers/staging/iio/adc/at91_adc.c +9 -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);

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!

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 209

Quoted 9 lines
drivers/staging/iio/adc/at91_adc.c +9 -0
+			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!)

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 213

Quoted 4 lines
drivers/staging/iio/adc/at91_adc.c +4 -0
+		*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.

Quoted 102 lines
drivers/staging/iio/adc/at91_adc.c +56 -6
+
+	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;
+}

drivers/staging/iio/adc/at91_adc.c static void at91_adc_channel_remove(struct iio_dev *idev) · line 330

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c +10 -0
+
+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 ;)

Quoted 118 lines
drivers/staging/iio/adc/at91_adc.c +118 -0
+	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;
+

drivers/staging/iio/adc/at91_adc.c static void at91_adc_channel_remove(struct iio_dev *idev) · line 458

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c +10 -0
+	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... ;)

Quoted 101 lines
drivers/staging/iio/adc/at91_adc.c +71 -1
+	.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);
include/linux/platform_data/at91_adc.h +0 -0
index 8b51ee6..8750d80 100644

include/linux/platform_data/at91_adc.h line 13

Quoted 4 lines
include/linux/platform_data/at91_adc.h +1 -0
@@ -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?

Quoted 8 lines
include/linux/platform_data/at91_adc.h +1 -0
  * @vref:		Reference voltage for the ADC in millivolts
  */
 struct at91_adc_data {
 	unsigned long	channels_used;
+	bool		use_external;
 	u16		vref;
 };
 
#10 lore raw

PATCH 5/6 IIO: AT91: Add support for hardware triggers for the ADC

Le 18/04/2012 20:46, Jonathan Cameron a ?crit :

Quoted 7 lines · Maxime Ripard
Quoted 3 lines

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

Couple of queries inline.

Quoted 8 lines

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.

Quoted 243 lines

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

Quoted 222 lines
Cc Jean-Christophe PLAGNIOL-VILLARD <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Nicolas Ferre <[email protected]>
---
 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(-)
arch/arm/mach-at91/at91sam9260_devices.c +3 -0
index 758c629..a870753 100644
@@ -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);
 }
arch/arm/mach-at91/at91sam9g45_devices.c +3 -0
index 05bc673..4ee18af 100644
@@ -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);
 }
arch/arm/mach-at91/board-sam9g20ek.c +1 -0
index 2ce4e26..3bb6e4d 100644
@@ -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,
 };
 
arch/arm/mach-at91/board-sam9m10g45ek.c +1 -0
index fb122ba..192a050 100644
@@ -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,
 };
 
drivers/staging/iio/adc/Kconfig +3 -0
index 1494838..9ee7ed9 100644
@@ -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.
 
drivers/staging/iio/adc/at91_adc.c +87 -0
index b3517bb..7c93cde 100644
@@ -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);

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 191

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c +8 -0
 }
 
+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.

Quoted 13 lines

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 200

Quoted 9 lines
drivers/staging/iio/adc/at91_adc.c +9 -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);

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.

Quoted 7 lines

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 :)

Quoted 10 lines

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 209

Quoted 9 lines
drivers/staging/iio/adc/at91_adc.c +9 -0
+			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 :)

Quoted 8 lines

drivers/staging/iio/adc/at91_adc.c static inline void at91_adc_reg_write(struct at91_adc_state *st, · line 213

Quoted 4 lines
drivers/staging/iio/adc/at91_adc.c +4 -0
+		*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 ?

Quoted 106 lines
Quoted 106 lines
drivers/staging/iio/adc/at91_adc.c +60 -6
+
+	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;
Quoted 8 lines

drivers/staging/iio/adc/at91_adc.c static void at91_adc_channel_remove(struct iio_dev *idev) · line 330

Quoted 6 lines
drivers/staging/iio/adc/at91_adc.c +6 -0
+	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....

Quoted 231 lines
Quoted 118 lines
drivers/staging/iio/adc/at91_adc.c +118 -0
+	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;
+

drivers/staging/iio/adc/at91_adc.c static void at91_adc_channel_remove(struct iio_dev *idev) · line 458

Quoted 10 lines
drivers/staging/iio/adc/at91_adc.c +10 -0
+	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... ;)

Quoted 99 lines
drivers/staging/iio/adc/at91_adc.c +71 -1
+	.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);
include/linux/platform_data/at91_adc.h +0 -1
index 8b51ee6..8750d80 100644
--- a/include/linux/platform_data/at91_adc.h
Quoted 8 lines

include/linux/platform_data/at91_adc.h line 13

Quoted 6 lines

+++ b/include/linux/platform_data/at91_adc.h

include/linux/platform_data/at91_adc.h +1 -0
@@ -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.

Quoted 9 lines
Quoted 8 lines
include/linux/platform_data/at91_adc.h +1 -0
  * @vref:		Reference voltage for the ADC in millivolts
  */
 struct at91_adc_data {
 	unsigned long	channels_used;
+	bool		use_external;
 	u16		vref;
 };
 
Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
#11 lore raw

PATCH 6/6 IIO: AT91: Add DT support to at91_adc driver

In reply to Maxime Ripard
---
 .../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
Documentation/devicetree/bindings/arm/atmel-adc.txt new file +21 -0
new file mode 100644
index 0000000..ac62847
@@ -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>;
+	};
drivers/staging/iio/adc/at91_adc.c +66 -22

185 lines not on this page · raw message

Signature · 1 line
1.7.5.4