Skip to contentSkip to conversation

AT91: Add a driver for the ADC

PATCH Superseded Maxime Ripard 3 participants 11 messages Nov 14, 2011

Subsystems (4)

v5 of 13 Superseded Latest is v13 →

lore
Share & tools
lore
#0 lore raw

PATCH v5 1/1 AT91: Add a driver for the ADC

Hi all,

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

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

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

Improvements for v5:
- Don't loop through the channels in the irq handler anymore but use the last
converted data register instead.

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

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

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

Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>
#1 lore raw

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

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

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

On 11/14/2011 11:06 AM, Maxime Ripard :

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

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

In reply to Maxime Ripard
Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Jean-Christophe Plagniol-Villard <[email protected]>
Cc Thomas Petazzoni <[email protected]>
---
 drivers/iio/adc/Kconfig   |    6 +
 drivers/iio/adc/Makefile  |    4 +-
 drivers/iio/adc/at91adc.c |  323 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 332 insertions(+), 1 deletions(-)
 create mode 100644 drivers/iio/adc/at91adc.c
drivers/iio/adc/Kconfig +6 -0
index 3d97b21..74f4d9f 100644
@@ -14,6 +14,12 @@ config IIO_AD799X
 	  i2c analog to digital convertors (ADC). Provides direct access
 	  via sysfs.
 
+config IIO_AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config IIO_MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
drivers/iio/adc/Makefile +3 -1
index c197334..776b56f 100644
@@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
 obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
 
 iio_max1363-y := max1363_core.o
-obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
+
+obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
drivers/iio/adc/at91adc.c new file +323 -0

323 lines not on this page · raw message

Signature · 1 line
1.7.4.1
#4 lore raw

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

On 11/14/2011 11:06 AM, Maxime Ripard :

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

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

In reply to Maxime Ripard

Hi,

a few nitpicks

Quoted 114 lines
Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Jean-Christophe Plagniol-Villard <[email protected]>
Cc Thomas Petazzoni <[email protected]>
---
 drivers/iio/adc/Kconfig   |    6 +
 drivers/iio/adc/Makefile  |    4 +-
 drivers/iio/adc/at91adc.c |  323
+++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 332
insertions(+), 1 deletions(-)
 create mode 100644 drivers/iio/adc/at91adc.c
drivers/iio/adc/Kconfig +6 -0
index 3d97b21..74f4d9f 100644
@@ -14,6 +14,12 @@ config IIO_AD799X
 	  i2c analog to digital convertors (ADC). Provides direct access
 	  via sysfs.
 
+config IIO_AT91ADC
+	tristate "Atmel AT91 ADC"
+	depends on SYSFS && ARCH_AT91
+	help
+	  Say yes here to build support for Atmel AT91 ADC.
+
 config IIO_MAX1363
 	tristate "Maxim max1363 ADC driver"
 	depends on I2C
drivers/iio/adc/Makefile +3 -1
index c197334..776b56f 100644
@@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o
 obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
 
 iio_max1363-y := max1363_core.o
-obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
+
+obj-$(CONFIG_IIO_AT91ADC) += at91adc.o
drivers/iio/adc/at91adc.c new file +43 -0
new file mode 100644
index 0000000..eee591d
@@ -0,0 +1,323 @@
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/iio/iio.h>
+
+#include <mach/at91_adc.h>
+#include <mach/board.h>
+
+struct at91adc_state {
+	struct clk *clk;
+	bool done;
+	struct mutex lock;
+	int irq;
+	wait_queue_head_t wq_data_avail;
+	u16 last_value;
+	void __iomem *reg_base;
+	unsigned int vref_mv;
+};
+
+static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg)
+{
+	return readl_relaxed(st->reg_base + reg);
+}
+
+static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32

val) +{

drivers/iio/adc/at91adc.c new file +19 -0
+	writel_relaxed(val, st->reg_base + reg);
+}
+
+static irqreturn_t at91adc_eoc_trigger(int irq, void *private)
+{
+	struct iio_dev *idev = private;
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int status = at91adc_reg_read(st, AT91_ADC_SR);
+
+	if (!(status & AT91_ADC_DRDY))
+		return IRQ_HANDLED;
+
+	if (status & ((2 << idev->num_channels) - 1)) {
+		st->done = true;
+		st->last_value = at91adc_reg_read(st, AT91_ADC_LCDR);
+	}
+
+	wake_up_interruptible(&st->wq_data_avail);
+

drivers/iio/adc/at91adc.c line 73

Quoted 10 lines
drivers/iio/adc/at91adc.c new file +10 -0
+	return IRQ_HANDLED;
+}
+
+static int at91adc_channel_init(struct iio_dev *idev,
+				struct at91_adc_data *pdata)
+{
+	struct iio_chan_spec *chan_array;
+	int bit, idx = 0;
+
+	idev->num_channels = bitmap_weight(&(pdata->channels_used),

Do you need the parenthesis here ?

Quoted 6 lines
drivers/iio/adc/at91adc.c new file +6 -0
+					   pdata->num_channels);
+	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
+			     GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;

drivers/iio/adc/at91adc.c line 89

Quoted 10 lines
drivers/iio/adc/at91adc.c new file +10 -0
+
+	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
+		struct iio_chan_spec *chan = chan_array + idx;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = bit;
+		chan->scan_type.sign = 'u';
+		chan->scan_type.realbits = 10;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		++idx;

idx++ ... why the preincrement?

Besides, can't you use "bit" as the index instead?

Quoted 74 lines
drivers/iio/adc/at91adc.c new file +74 -0
+	}
+
+	idev->channels = chan_array;
+	return idev->num_channels;
+}
+
+static void at91adc_channel_remove(struct iio_dev *idev)
+{
+	kfree(idev->channels);
+}
+
+static int at91adc_read_raw(struct iio_dev *idev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct at91adc_state *st = iio_priv(idev);
+	unsigned int scale_uv;
+	short ret;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&st->lock);
+
+		at91adc_reg_write(st, AT91_ADC_CHER,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_IER,
+				  AT91_ADC_EOC(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START);
+
+		ret = wait_event_interruptible_timeout(st->wq_data_avail,
+						       st->done,
+						       msecs_to_jiffies(1000));
+		if (ret == -ERESTARTSYS)
+			break;
+
+		*val = st->last_value;
+
+		at91adc_reg_write(st, AT91_ADC_CHDR,
+				  AT91_ADC_CH(chan->channel));
+		at91adc_reg_write(st, AT91_ADC_IDR,
+				  AT91_ADC_EOC(chan->channel));
+
+		st->last_value = 0;
+		st->done = false;
+		mutex_unlock(&st->lock);
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
+		*val = scale_uv / 1000;
+		*val2 = (scale_uv % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static const struct iio_info at91adc_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &at91adc_read_raw,
+};
+
+static int __devinit at91adc_probe(struct platform_device *pdev)
+{
+	unsigned int prsc, mstrclk, ticks;
+	int ret;
+	struct iio_dev *idev;
+	struct at91adc_state *st;
+	struct resource *res;
+	struct at91_adc_data *pdata = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {

drivers/iio/adc/at91adc.c line 173

Quoted 10 lines
drivers/iio/adc/at91adc.c new file +10 -0
+		dev_err(&pdev->dev, "No resource defined\n");
+		ret = -ENXIO;
+		goto error_ret;
+	}
+
+	idev = iio_device_allocate(sizeof(struct at91adc_state));
+	if (idev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}

newline

Quoted 140 lines
drivers/iio/adc/at91adc.c new file +140 -0
+	platform_set_drvdata(pdev, idev);
+
+	idev->dev.parent = &pdev->dev;
+	idev->name = dev_name(&pdev->dev);
+	idev->info = &at91adc_info;
+
+	st = iio_priv(idev);
+	st->irq = platform_get_irq(pdev, 0);
+	if (st->irq < 0) {
+		dev_err(&pdev->dev, "No IRQ ID is designated\n");
+		ret = -ENODEV;
+		goto error_free_device;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res),
+				"AT91 adc registers")) {
+		dev_err(&pdev->dev, "Resources are unavailable.\n");
+		ret = -EBUSY;
+		goto error_free_device;
+	}
+
+	st->reg_base = ioremap(res->start, resource_size(res));
+	if (!st->reg_base) {
+		dev_err(&pdev->dev, "Failed to map registers.\n");
+		ret = -ENOMEM;
+		goto error_release_mem;
+	}
+
+	/*
+	 * Disable all IRQs before setting up the handler
+	 */
+	at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST);
+	at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF);
+	ret = request_irq(st->irq,
+			  at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+		goto error_unmap_reg;
+	}
+
+	st->clk = clk_get(&pdev->dev, "adc_clk");
+	if (IS_ERR(st->clk)) {
+		dev_err(&pdev->dev, "Failed to get the clock.\n");
+		ret = PTR_ERR(st->clk);
+		goto error_free_irq;
+	}
+
+	clk_enable(st->clk);
+	mstrclk = clk_get_rate(st->clk);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	if (!pdata->adc_clock) {
+		dev_err(&pdev->dev, "No ADCClock available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	/*
+	 * Prescaler rate computation using the formula from the Atmel's
+	 * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
+	 * specified by the electrical characteristics of the board.
+	 */
+	prsc = (mstrclk / (2 * pdata->adc_clock)) - 1;
+
+	if (!pdata->startup_time) {
+		dev_err(&pdev->dev, "No startup time available.\n");
+		ret = -EINVAL;
+		goto error_free_clk;
+	}
+
+	/*
+	 * Number of ticks needed to cover the startup time of the ADC as
+	 * defined in the electrical characteristics of the board, divided by 8.
+	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
+	 */
+	ticks = round_up((pdata->startup_time * pdata->adc_clock /
+			  1000000) - 1, 8) / 8;
+	at91adc_reg_write(st, AT91_ADC_MR,
+			  (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) |
+			  (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP));
+
+	/* Setup the ADC channels available on the board */
+	ret = at91adc_channel_init(idev, pdata);
+	if (ret < 0)
+		goto error_free_clk;
+
+	init_waitqueue_head(&st->wq_data_avail);
+	mutex_init(&st->lock);
+
+	st->vref_mv = pdata->vref;
+
+	ret = iio_device_register(idev);
+	if (ret < 0)
+		goto error_free_channels;
+
+	return 0;
+
+error_free_channels:
+	at91adc_channel_remove(idev);
+error_free_clk:
+	clk_disable(st->clk);
+	clk_put(st->clk);
+error_free_irq:
+	free_irq(st->irq, st);
+error_unmap_reg:
+	iounmap(st->reg_base);
+error_release_mem:
+	release_mem_region(res->start, resource_size(res));
+error_free_device:
+	iio_device_free(idev);
+error_ret:
+	return ret;
+}
+
+static int __devexit at91adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *idev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct at91adc_state *st = iio_priv(idev);
+
+	iio_device_unregister(idev);
+	at91adc_channel_remove(idev);
+	clk_disable(st->clk);
+	clk_put(st->clk);
+	free_irq(st->irq, idev);
+	iounmap(st->reg_base);
+	release_mem_region(res->start, resource_size(res));
+	iio_device_free(idev);
+
+	return 0;
+}
+
+static struct platform_driver at91adc_driver = {
+	.probe = at91adc_probe,
+	.remove = __devexit_p(at91adc_remove),

drivers/iio/adc/at91adc.c line 323

Quoted 10 lines
drivers/iio/adc/at91adc.c new file +10 -0
+	.driver = {
+		   .name = "at91adc",
+	},
+};
+
+module_platform_driver(at91adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <[email protected]>");

Looks good otherwise, please add

M

#6 lore raw

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

Hi

Quoted 3 lines · Marek Vasut

drivers/iio/adc/at91adc.c line 73

Quoted 1 line
drivers/iio/adc/at91adc.c new file +1 -0
+	idev->num_channels = bitmap_weight(&(pdata->channels_used),

Do you need the parenthesis here ?

No. I just find it clearer this way, but it's all about cosmetic.

Quoted 19 lines
Quoted 6 lines
drivers/iio/adc/at91adc.c new file +6 -0
+					   pdata->num_channels);
+	chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec),
+			     GFP_KERNEL);
+
+	if (chan_array == NULL)
+		return -ENOMEM;

drivers/iio/adc/at91adc.c line 89

Quoted 10 lines
drivers/iio/adc/at91adc.c new file +10 -0
+
+	for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) {
+		struct iio_chan_spec *chan = chan_array + idx;
+		chan->type = IIO_VOLTAGE;
+		chan->indexed = 1;
+		chan->channel = bit;
+		chan->scan_type.sign = 'u';
+		chan->scan_type.realbits = 10;
+		chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT;
+		++idx;

idx++ ... why the preincrement?

Quoted 1 line

Besides, can't you use "bit" as the index instead?

Nope. bit is the index in the pdata->channel_used array, while idx is the index in the chan_array array. And since these two don't have the same length, I need these two variables.

Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
#7 lore raw

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

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

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

On 11/14/2011 11:06 AM, Maxime Ripard :

Quoted 5 lines
Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>

(even if I have a tiny comment: see below)

Quoted 10 lines
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)
arch/arm/mach-at91/at91sam9260_devices.c +0 -0
index 39f81f4..0859553 100644

arch/arm/mach-at91/at91sam9260_devices.c void __init at91_add_device_cf(struct at91_cf_data *data) · line 1317

Quoted 6 lines
arch/arm/mach-at91/at91sam9260_devices.c +3 -0
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */

What about adding all this only if the IIO_AT91ADC is selected? Otherwise, you configure pins without actually using them...

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

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

Hi Nicolas,

Quoted 25 lines · Nicolas Ferre

On 11/14/2011 11:06 AM, Maxime Ripard :

Quoted 5 lines
Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>

(even if I have a tiny comment: see below)

Quoted 13 lines
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)
arch/arm/mach-at91/at91sam9260_devices.c +0 -0
index 39f81f4..0859553 100644
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
Quoted 6 lines

arch/arm/mach-at91/at91sam9260_devices.c void __init at91_add_device_cf(struct at91_cf_data *data) · line 1317

Quoted 3 lines
arch/arm/mach-at91/at91sam9260_devices.c +3 -0
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */

What about adding all this only if the IIO_AT91ADC is selected? Otherwise, you configure pins without actually using them...

I'm not sure on what I should do here. Should I only wrap the pin configuration part in the at91_add_device_adc function in an ifdef, or wrap all there is in this patch (that is structure definitions, at91_add_device_adc declaration and call, etc) by the ifdef ?

Quoted 84 lines
Quoted 82 lines
arch/arm/mach-at91/at91sam9260_devices.c +49 -0
+
+static struct at91_adc_data adc_data;
+
+static struct resource adc_resources[] = {
+	[0] = {
+		.start	= AT91SAM9260_BASE_ADC,
+		.end	= AT91SAM9260_BASE_ADC + SZ_16K - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= AT91SAM9260_ID_ADC,
+		.end	= AT91SAM9260_ID_ADC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device at91_adc_device = {
+	.name		= "at91adc",
+	.id		= -1,
+	.dev		= {
+				.platform_data		= &adc_data,
+	},
+	.resource	= adc_resources,
+	.num_resources	= ARRAY_SIZE(adc_resources),
+};
+
+void __init at91_add_device_adc(struct at91_adc_data *data)
+{
+	if (!data)
+		return;
+
+	if (test_bit(0, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC0, 0);
+	if (test_bit(1, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC1, 0);
+	if (test_bit(2, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC2, 0);
+	if (test_bit(3, &(data->channels_used)))
+		at91_set_A_periph(AT91_PIN_PC3, 0);
+
+	data->adc_clock = 5000000;
+	data->num_channels = 4;
+	data->startup_time = 10;
+
+	adc_data = *data;
+	platform_device_register(&at91_adc_device);
+}
+
+
 /* -------------------------------------------------------------------- */
 /*
  * These devices are always present and don't need any board-specific
arch/arm/mach-at91/board-sam9g20ek.c +11 -0
index 817f59d..e34d41a 100644
@@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void)
 static void __init ek_add_device_buttons(void) {}
 #endif
 
+/*
+ * ADCs
+ */
+
+static struct at91_adc_data ek_adc_data = {
+	.channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3),
+	.vref = 3300,
+};
+
 #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE)
 static struct regulator_consumer_supply ek_audio_consumer_supplies[] = {
 	REGULATOR_SUPPLY("AVDD", "0-001b"),
@@ -389,6 +398,8 @@ static void __init ek_board_init(void)
 	ek_add_device_gpio_leds();
 	/* Push Buttons */
 	ek_add_device_buttons();
+	/* ADCs */
+	at91_add_device_adc(&ek_adc_data);
 	/* PCK0 provides MCLK to the WM8731 */
 	at91_set_B_periph(AT91_PIN_PC1, 0);
 	/* SSC (for WM8731) */
Signature · 4 lines
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
#10 lore raw

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

On 11/14/2011 04:17 PM, Maxime Ripard :

Quoted 35 lines

Hi Nicolas,

Quoted 25 lines · Nicolas Ferre

On 11/14/2011 11:06 AM, Maxime Ripard :

Quoted 5 lines
Cc Nicolas Ferre <[email protected]>
Cc Patrice Vilchez <[email protected]>
Cc Thomas Petazzoni <[email protected]>

(even if I have a tiny comment: see below)

Quoted 13 lines
---
 arch/arm/mach-at91/at91sam9260_devices.c |   52 ++++++++++++++++++++++++++++++
 arch/arm/mach-at91/board-sam9g20ek.c     |   11 ++++++
 2 files changed, 63 insertions(+), 0 deletions(-)
arch/arm/mach-at91/at91sam9260_devices.c +0 -0
index 39f81f4..0859553 100644
@@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data)
 void __init at91_add_device_cf(struct at91_cf_data * data) {}
 #endif
 
Quoted 6 lines

arch/arm/mach-at91/at91sam9260_devices.c void __init at91_add_device_cf(struct at91_cf_data *data) · line 1317

Quoted 3 lines
arch/arm/mach-at91/at91sam9260_devices.c +3 -0
+/* --------------------------------------------------------------------
+ *  ADCs
+ * -------------------------------------------------------------------- */

What about adding all this only if the IIO_AT91ADC is selected? Otherwise, you configure pins without actually using them...

Quoted 4 lines

I'm not sure on what I should do here. Should I only wrap the pin configuration part in the at91_add_device_adc function in an ifdef, or wrap all there is in this patch (that is structure definitions, at91_add_device_adc declaration and call, etc) by the ifdef ?

I mean all adc material surrounded by ifdefs with empty function call if not selected. I was only talking about the pin configuration because the additional structure size is not so much a big deal.

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

arch/arm/mach-at91/board-sam9g20ek.c static void __init ek_board_init(void) · line 405

Quoted 8 lines
arch/arm/mach-at91/board-sam9g20ek.c +2 -0
 	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) */

Bye,

Signature · 1 line
Nicolas Ferre