* [PATCH] AT91: Add a driver for the ADC @ 2011-10-19 16:18 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the branch master of Jonathan Cameron present at : https://github.com/jic23/linux-iio/ Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH] AT91: Add a driver for the ADC @ 2011-10-19 16:18 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the branch master of Jonathan Cameron present at : https://github.com/jic23/linux-iio/ Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-19 16:18 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/include/mach/board.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..adbc431 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,12 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + unsigned int adc_clock; + u8 channels; + u8 startup_time; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-10-19 16:18 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/include/mach/board.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..adbc431 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,12 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + unsigned int adc_clock; + u8 channels; + u8 startup_time; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-19 16:18 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/staging/iio/adc/Kconfig | 6 + drivers/staging/iio/adc/Makefile | 1 + drivers/staging/iio/adc/at91adc.c | 295 +++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/adc/at91adc.c diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 070efd3..98e8005 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -206,6 +206,12 @@ config AD7280 To compile this driver as a module, choose M here: the module will be called ad7280a +config AT91ADC + tristate "Atmel Touchscreen ADC Controller" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel TSADCC. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 8adf096..03e517a 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o obj-$(CONFIG_ADT7310) += adt7310.o obj-$(CONFIG_ADT7410) += adt7410.o obj-$(CONFIG_AD7280) += ad7280a.o +obj-$(CONFIG_AT91ADC) += at91adc.o diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c new file mode 100644 index 0000000..2737261 --- /dev/null +++ b/drivers/staging/iio/adc/at91adc.c @@ -0,0 +1,295 @@ +/* + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 + * evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include "../iio.h" + +#include <mach/at91_adc.h> +#include <mach/board.h> + +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + struct iio_chan_spec *channels; + int nb_chan; + int irq; + wait_queue_head_t wq_data_avail; + u16 lcdr; + void __iomem *reg_base; + +}; + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct at91adc_state *st = private; + struct iio_dev *idev = iio_priv_to_dev(st); + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < st->nb_chan; chan++) { + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->lcdr = at91adc_reg_read(st->reg_base, + AT91_ADC_CHR(chan)); + } + } + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct at91adc_state *st) +{ + int ret = 0, i; + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, + GFP_KERNEL); + if (st->channels == NULL) + return -ENOMEM; + + for (i = 0; i < st->nb_chan; i++) { + struct iio_chan_spec *chan = st->channels + i; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + ++ret; + } + + return ret; +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); + + wait_event_interruptible(st->wq_data_avail, st->done); + *val = st->lcdr; + + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->lcdr = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "AT91ADC probed\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_allocate_device(sizeof(*st)); + if (idev == NULL) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = platform_get_device_id(pdev)->name; + idev->modes = INDIO_DIRECT_MODE; + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st->reg_base, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + st->nb_chan = pdata->channels; + ret = at91adc_channel_init(st); + if (ret < 0) { + goto error_free_clk; + } + + idev->channels = st->channels; + idev->num_channels = st->nb_chan; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + ret = iio_device_register(idev); + if (ret < 0) { + goto error_free_clk; + } + + return 0; + +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_free_device(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + free_irq(st->irq, st); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_device_unregister(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +static int __init at91adc_init(void) +{ + return platform_driver_register(&at91adc_driver); +} + +static void __exit at91adc_exit(void) +{ + platform_driver_unregister(&at91adc_driver); +} + +module_init(at91adc_init); +module_exit(at91adc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-19 16:18 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/staging/iio/adc/Kconfig | 6 + drivers/staging/iio/adc/Makefile | 1 + drivers/staging/iio/adc/at91adc.c | 295 +++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/adc/at91adc.c diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 070efd3..98e8005 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -206,6 +206,12 @@ config AD7280 To compile this driver as a module, choose M here: the module will be called ad7280a +config AT91ADC + tristate "Atmel Touchscreen ADC Controller" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel TSADCC. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 8adf096..03e517a 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o obj-$(CONFIG_ADT7310) += adt7310.o obj-$(CONFIG_ADT7410) += adt7410.o obj-$(CONFIG_AD7280) += ad7280a.o +obj-$(CONFIG_AT91ADC) += at91adc.o diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c new file mode 100644 index 0000000..2737261 --- /dev/null +++ b/drivers/staging/iio/adc/at91adc.c @@ -0,0 +1,295 @@ +/* + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 + * evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include "../iio.h" + +#include <mach/at91_adc.h> +#include <mach/board.h> + +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + struct iio_chan_spec *channels; + int nb_chan; + int irq; + wait_queue_head_t wq_data_avail; + u16 lcdr; + void __iomem *reg_base; + +}; + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct at91adc_state *st = private; + struct iio_dev *idev = iio_priv_to_dev(st); + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < st->nb_chan; chan++) { + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->lcdr = at91adc_reg_read(st->reg_base, + AT91_ADC_CHR(chan)); + } + } + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct at91adc_state *st) +{ + int ret = 0, i; + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, + GFP_KERNEL); + if (st->channels == NULL) + return -ENOMEM; + + for (i = 0; i < st->nb_chan; i++) { + struct iio_chan_spec *chan = st->channels + i; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + ++ret; + } + + return ret; +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); + + wait_event_interruptible(st->wq_data_avail, st->done); + *val = st->lcdr; + + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->lcdr = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "AT91ADC probed\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_allocate_device(sizeof(*st)); + if (idev == NULL) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = platform_get_device_id(pdev)->name; + idev->modes = INDIO_DIRECT_MODE; + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st->reg_base, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + st->nb_chan = pdata->channels; + ret = at91adc_channel_init(st); + if (ret < 0) { + goto error_free_clk; + } + + idev->channels = st->channels; + idev->num_channels = st->nb_chan; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + ret = iio_device_register(idev); + if (ret < 0) { + goto error_free_clk; + } + + return 0; + +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_free_device(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + free_irq(st->irq, st); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_device_unregister(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +static int __init at91adc_init(void) +{ + return platform_driver_register(&at91adc_driver); +} + +static void __exit at91adc_exit(void) +{ + platform_driver_unregister(&at91adc_driver); +} + +module_init(at91adc_init); +module_exit(at91adc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-19 16:42 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-10-19 16:42 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez Hi Maxime, Afraid I'm out of time for today, but just thought I'd ask one quick question... Why isn't this touchscreen ADC in input? (needs to be firmly stated as early as possible!) > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > drivers/staging/iio/adc/Kconfig | 6 + > drivers/staging/iio/adc/Makefile | 1 + > drivers/staging/iio/adc/at91adc.c | 295 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 302 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/adc/at91adc.c > > diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig > index 070efd3..98e8005 100644 > --- a/drivers/staging/iio/adc/Kconfig > +++ b/drivers/staging/iio/adc/Kconfig > @@ -206,6 +206,12 @@ config AD7280 > To compile this driver as a module, choose M here: the > module will be called ad7280a > > +config AT91ADC > + tristate "Atmel Touchscreen ADC Controller" > + depends on SYSFS && ARCH_AT91 > + help > + Say yes here to build support for Atmel TSADCC. > + > config MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile > index 8adf096..03e517a 100644 > --- a/drivers/staging/iio/adc/Makefile > +++ b/drivers/staging/iio/adc/Makefile > @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o > obj-$(CONFIG_ADT7310) += adt7310.o > obj-$(CONFIG_ADT7410) += adt7410.o > obj-$(CONFIG_AD7280) += ad7280a.o > +obj-$(CONFIG_AT91ADC) += at91adc.o > diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c > new file mode 100644 > index 0000000..2737261 > --- /dev/null > +++ b/drivers/staging/iio/adc/at91adc.c > @@ -0,0 +1,295 @@ > +/* > + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 > + * evaluation boards. > + * > + * Copyright 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + */ > + > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > + > +#include "../iio.h" > + > +#include <mach/at91_adc.h> > +#include <mach/board.h> > + > +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) > +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; > + struct iio_chan_spec *channels; > + int nb_chan; > + int irq; > + wait_queue_head_t wq_data_avail; > + u16 lcdr; > + void __iomem *reg_base; > + > +}; > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct at91adc_state *st = private; > + struct iio_dev *idev = iio_priv_to_dev(st); > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < st->nb_chan; chan++) { > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->lcdr = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); > + } > + } > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct at91adc_state *st) > +{ > + int ret = 0, i; > + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, > + GFP_KERNEL); > + if (st->channels == NULL) > + return -ENOMEM; > + > + for (i = 0; i < st->nb_chan; i++) { > + struct iio_chan_spec *chan = st->channels + i; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; > + ++ret; > + } > + > + return ret; > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible(st->wq_data_avail, st->done); > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + dev_dbg(&pdev->dev, "AT91ADC probed\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(*st)); > + if (idev == NULL) { > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = platform_get_device_id(pdev)->name; > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; > + > + if (!pdata->startup_time) { > + dev_err(&pdev->dev, "No startup time available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; > + at91adc_reg_write(st->reg_base, AT91_ADC_MR, > + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | > + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); > + > + /* Setup the ADC channels available on the board */ > + st->nb_chan = pdata->channels; > + ret = at91adc_channel_init(st); > + if (ret < 0) { > + goto error_free_clk; > + } > + > + idev->channels = st->channels; > + idev->num_channels = st->nb_chan; > + > + init_waitqueue_head(&st->wq_data_avail); > + mutex_init(&st->lock); > + > + ret = iio_device_register(idev); > + if (ret < 0) { > + goto error_free_clk; > + } > + > + return 0; > + > +error_free_clk: > + clk_disable(st->clk); > + clk_put(st->clk); > +error_free_irq: > + free_irq(st->irq, st); > +error_unmap_reg: > + iounmap(st->reg_base); > +error_release_mem: > + release_mem_region(res->start, resource_size(res)); > +error_free_device: > + iio_free_device(idev); > +error_ret: > + return ret; > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + free_irq(st->irq, st); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_unregister(idev); > + > + return 0; > +} > + > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, > +}; > + > +static int __init at91adc_init(void) > +{ > + return platform_driver_register(&at91adc_driver); > +} > + > +static void __exit at91adc_exit(void) > +{ > + platform_driver_unregister(&at91adc_driver); > +} > + > +module_init(at91adc_init); > +module_exit(at91adc_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-19 16:42 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-10-19 16:42 UTC (permalink / raw) To: linux-arm-kernel Hi Maxime, Afraid I'm out of time for today, but just thought I'd ask one quick question... Why isn't this touchscreen ADC in input? (needs to be firmly stated as early as possible!) > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > drivers/staging/iio/adc/Kconfig | 6 + > drivers/staging/iio/adc/Makefile | 1 + > drivers/staging/iio/adc/at91adc.c | 295 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 302 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/adc/at91adc.c > > diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig > index 070efd3..98e8005 100644 > --- a/drivers/staging/iio/adc/Kconfig > +++ b/drivers/staging/iio/adc/Kconfig > @@ -206,6 +206,12 @@ config AD7280 > To compile this driver as a module, choose M here: the > module will be called ad7280a > > +config AT91ADC > + tristate "Atmel Touchscreen ADC Controller" > + depends on SYSFS && ARCH_AT91 > + help > + Say yes here to build support for Atmel TSADCC. > + > config MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile > index 8adf096..03e517a 100644 > --- a/drivers/staging/iio/adc/Makefile > +++ b/drivers/staging/iio/adc/Makefile > @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o > obj-$(CONFIG_ADT7310) += adt7310.o > obj-$(CONFIG_ADT7410) += adt7410.o > obj-$(CONFIG_AD7280) += ad7280a.o > +obj-$(CONFIG_AT91ADC) += at91adc.o > diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c > new file mode 100644 > index 0000000..2737261 > --- /dev/null > +++ b/drivers/staging/iio/adc/at91adc.c > @@ -0,0 +1,295 @@ > +/* > + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 > + * evaluation boards. > + * > + * Copyright 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + */ > + > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > + > +#include "../iio.h" > + > +#include <mach/at91_adc.h> > +#include <mach/board.h> > + > +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) > +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; > + struct iio_chan_spec *channels; > + int nb_chan; > + int irq; > + wait_queue_head_t wq_data_avail; > + u16 lcdr; > + void __iomem *reg_base; > + > +}; > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct at91adc_state *st = private; > + struct iio_dev *idev = iio_priv_to_dev(st); > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < st->nb_chan; chan++) { > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->lcdr = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); > + } > + } > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct at91adc_state *st) > +{ > + int ret = 0, i; > + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, > + GFP_KERNEL); > + if (st->channels == NULL) > + return -ENOMEM; > + > + for (i = 0; i < st->nb_chan; i++) { > + struct iio_chan_spec *chan = st->channels + i; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; > + ++ret; > + } > + > + return ret; > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible(st->wq_data_avail, st->done); > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + dev_dbg(&pdev->dev, "AT91ADC probed\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(*st)); > + if (idev == NULL) { > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = platform_get_device_id(pdev)->name; > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; > + > + if (!pdata->startup_time) { > + dev_err(&pdev->dev, "No startup time available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; > + at91adc_reg_write(st->reg_base, AT91_ADC_MR, > + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | > + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); > + > + /* Setup the ADC channels available on the board */ > + st->nb_chan = pdata->channels; > + ret = at91adc_channel_init(st); > + if (ret < 0) { > + goto error_free_clk; > + } > + > + idev->channels = st->channels; > + idev->num_channels = st->nb_chan; > + > + init_waitqueue_head(&st->wq_data_avail); > + mutex_init(&st->lock); > + > + ret = iio_device_register(idev); > + if (ret < 0) { > + goto error_free_clk; > + } > + > + return 0; > + > +error_free_clk: > + clk_disable(st->clk); > + clk_put(st->clk); > +error_free_irq: > + free_irq(st->irq, st); > +error_unmap_reg: > + iounmap(st->reg_base); > +error_release_mem: > + release_mem_region(res->start, resource_size(res)); > +error_free_device: > + iio_free_device(idev); > +error_ret: > + return ret; > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + free_irq(st->irq, st); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_unregister(idev); > + > + return 0; > +} > + > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, > +}; > + > +static int __init at91adc_init(void) > +{ > + return platform_driver_register(&at91adc_driver); > +} > + > +static void __exit at91adc_exit(void) > +{ > + platform_driver_unregister(&at91adc_driver); > +} > + > +module_init(at91adc_init); > +module_exit(at91adc_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-19 16:42 ` Jonathan Cameron @ 2011-10-19 18:23 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 18:23 UTC (permalink / raw) To: Jonathan Cameron Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez Hi Jonathan, On 19/10/2011 18:42, Jonathan Cameron wrote: > Afraid I'm out of time for today, but just thought I'd ask one quick question... > > Why isn't this touchscreen ADC in input? Because what I say in the Kconfig is mostly wrong... Sorry about that. Actually, like I was saying, this ADC is present in a lot of AT91 SoC, and while it is always an ADC, sometimes (like on the G45), it can be used as a touchscreen controller, or even as both, with channels for the touchscreen and channels as ADC. On the G20 however, it is just an ADC. For now, the plan is only to support the ADC mode, so I put it in adc. Maxime >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> drivers/staging/iio/adc/Kconfig | 6 + >> drivers/staging/iio/adc/Makefile | 1 + >> drivers/staging/iio/adc/at91adc.c | 295 +++++++++++++++++++++++++++++++++++++ >> 3 files changed, 302 insertions(+), 0 deletions(-) >> create mode 100644 drivers/staging/iio/adc/at91adc.c >> >> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig >> index 070efd3..98e8005 100644 >> --- a/drivers/staging/iio/adc/Kconfig >> +++ b/drivers/staging/iio/adc/Kconfig >> @@ -206,6 +206,12 @@ config AD7280 >> To compile this driver as a module, choose M here: the >> module will be called ad7280a >> >> +config AT91ADC >> + tristate "Atmel Touchscreen ADC Controller" >> + depends on SYSFS && ARCH_AT91 >> + help >> + Say yes here to build support for Atmel TSADCC. >> + >> config MAX1363 >> tristate "Maxim max1363 ADC driver" >> depends on I2C >> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile >> index 8adf096..03e517a 100644 >> --- a/drivers/staging/iio/adc/Makefile >> +++ b/drivers/staging/iio/adc/Makefile >> @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o >> obj-$(CONFIG_ADT7310) += adt7310.o >> obj-$(CONFIG_ADT7410) += adt7410.o >> obj-$(CONFIG_AD7280) += ad7280a.o >> +obj-$(CONFIG_AT91ADC) += at91adc.o >> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..2737261 >> --- /dev/null >> +++ b/drivers/staging/iio/adc/at91adc.c >> @@ -0,0 +1,295 @@ >> +/* >> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 >> + * evaluation boards. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include "../iio.h" >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + >> +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) >> +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + struct iio_chan_spec *channels; >> + int nb_chan; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 lcdr; >> + void __iomem *reg_base; >> + >> +}; >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct at91adc_state *st = private; >> + struct iio_dev *idev = iio_priv_to_dev(st); >> + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < st->nb_chan; chan++) { >> + if (status & AT91_ADC_EOC(chan)) { >> + st->done = true; >> + st->lcdr = at91adc_reg_read(st->reg_base, >> + AT91_ADC_CHR(chan)); >> + } >> + } >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct at91adc_state *st) >> +{ >> + int ret = 0, i; >> + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, >> + GFP_KERNEL); >> + if (st->channels == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < st->nb_chan; i++) { >> + struct iio_chan_spec *chan = st->channels + i; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; >> + ++ret; >> + } >> + >> + return ret; >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible(st->wq_data_avail, st->done); >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + dev_dbg(&pdev->dev, "AT91ADC probed\n"); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(*st)); >> + if (idev == NULL) { >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = platform_get_device_id(pdev)->name; >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st->reg_base, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + st->nb_chan = pdata->channels; >> + ret = at91adc_channel_init(st); >> + if (ret < 0) { >> + goto error_free_clk; >> + } >> + >> + idev->channels = st->channels; >> + idev->num_channels = st->nb_chan; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + ret = iio_device_register(idev); >> + if (ret < 0) { >> + goto error_free_clk; >> + } >> + >> + return 0; >> + >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_free_device(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + free_irq(st->irq, st); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_unregister(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +static int __init at91adc_init(void) >> +{ >> + return platform_driver_register(&at91adc_driver); >> +} >> + >> +static void __exit at91adc_exit(void) >> +{ >> + platform_driver_unregister(&at91adc_driver); >> +} >> + >> +module_init(at91adc_init); >> +module_exit(at91adc_exit); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-19 18:23 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 18:23 UTC (permalink / raw) To: linux-arm-kernel Hi Jonathan, On 19/10/2011 18:42, Jonathan Cameron wrote: > Afraid I'm out of time for today, but just thought I'd ask one quick question... > > Why isn't this touchscreen ADC in input? Because what I say in the Kconfig is mostly wrong... Sorry about that. Actually, like I was saying, this ADC is present in a lot of AT91 SoC, and while it is always an ADC, sometimes (like on the G45), it can be used as a touchscreen controller, or even as both, with channels for the touchscreen and channels as ADC. On the G20 however, it is just an ADC. For now, the plan is only to support the ADC mode, so I put it in adc. Maxime >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> drivers/staging/iio/adc/Kconfig | 6 + >> drivers/staging/iio/adc/Makefile | 1 + >> drivers/staging/iio/adc/at91adc.c | 295 +++++++++++++++++++++++++++++++++++++ >> 3 files changed, 302 insertions(+), 0 deletions(-) >> create mode 100644 drivers/staging/iio/adc/at91adc.c >> >> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig >> index 070efd3..98e8005 100644 >> --- a/drivers/staging/iio/adc/Kconfig >> +++ b/drivers/staging/iio/adc/Kconfig >> @@ -206,6 +206,12 @@ config AD7280 >> To compile this driver as a module, choose M here: the >> module will be called ad7280a >> >> +config AT91ADC >> + tristate "Atmel Touchscreen ADC Controller" >> + depends on SYSFS && ARCH_AT91 >> + help >> + Say yes here to build support for Atmel TSADCC. >> + >> config MAX1363 >> tristate "Maxim max1363 ADC driver" >> depends on I2C >> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile >> index 8adf096..03e517a 100644 >> --- a/drivers/staging/iio/adc/Makefile >> +++ b/drivers/staging/iio/adc/Makefile >> @@ -42,3 +42,4 @@ obj-$(CONFIG_ADT75) += adt75.o >> obj-$(CONFIG_ADT7310) += adt7310.o >> obj-$(CONFIG_ADT7410) += adt7410.o >> obj-$(CONFIG_AD7280) += ad7280a.o >> +obj-$(CONFIG_AT91ADC) += at91adc.o >> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..2737261 >> --- /dev/null >> +++ b/drivers/staging/iio/adc/at91adc.c >> @@ -0,0 +1,295 @@ >> +/* >> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 >> + * evaluation boards. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include "../iio.h" >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + >> +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) >> +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + struct iio_chan_spec *channels; >> + int nb_chan; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 lcdr; >> + void __iomem *reg_base; >> + >> +}; >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct at91adc_state *st = private; >> + struct iio_dev *idev = iio_priv_to_dev(st); >> + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < st->nb_chan; chan++) { >> + if (status & AT91_ADC_EOC(chan)) { >> + st->done = true; >> + st->lcdr = at91adc_reg_read(st->reg_base, >> + AT91_ADC_CHR(chan)); >> + } >> + } >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct at91adc_state *st) >> +{ >> + int ret = 0, i; >> + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, >> + GFP_KERNEL); >> + if (st->channels == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < st->nb_chan; i++) { >> + struct iio_chan_spec *chan = st->channels + i; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; >> + ++ret; >> + } >> + >> + return ret; >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible(st->wq_data_avail, st->done); >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + dev_dbg(&pdev->dev, "AT91ADC probed\n"); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(*st)); >> + if (idev == NULL) { >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = platform_get_device_id(pdev)->name; >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st->reg_base, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + st->nb_chan = pdata->channels; >> + ret = at91adc_channel_init(st); >> + if (ret < 0) { >> + goto error_free_clk; >> + } >> + >> + idev->channels = st->channels; >> + idev->num_channels = st->nb_chan; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + ret = iio_device_register(idev); >> + if (ret < 0) { >> + goto error_free_clk; >> + } >> + >> + return 0; >> + >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_free_device(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + free_irq(st->irq, st); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_unregister(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +static int __init at91adc_init(void) >> +{ >> + return platform_driver_register(&at91adc_driver); >> +} >> + >> +static void __exit at91adc_exit(void) >> +{ >> + platform_driver_unregister(&at91adc_driver); >> +} >> + >> +module_init(at91adc_init); >> +module_exit(at91adc_exit); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-19 18:23 ` Maxime Ripard @ 2011-10-20 7:05 ` Thomas Petazzoni -1 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-10-20 7:05 UTC (permalink / raw) To: Maxime Ripard, Jonathan Cameron Cc: linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel Hello, Le Wed, 19 Oct 2011 20:23:50 +0200, Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit : > On 19/10/2011 18:42, Jonathan Cameron wrote: > > Afraid I'm out of time for today, but just thought I'd ask one quick qu= estion... > >=20 > > Why isn't this touchscreen ADC in input? >=20 > Because what I say in the Kconfig is mostly wrong... Sorry about that. >=20 > Actually, like I was saying, this ADC is present in a lot of AT91 SoC, > and while it is always an ADC, sometimes (like on the G45), it can be > used as a touchscreen controller, or even as both, with channels for the > touchscreen and channels as ADC. On the G20 however, it is just an ADC. >=20 > For now, the plan is only to support the ADC mode, so I put it in adc. Just to expand a bit on this: there is already in the kernel an input driver for the AT91 touchscreen which uses some ADC channels (that have additional touchscreen-related features: on the G45, you have 8 ADCs channels, 4 can optionally be used for touchscreen, the 4 other are just raw ADCs). The ultimate goal is of course to make it possible to use both the IIO AT91 ADC driver and the AT91 touchscreen driver work simultaneously (with of course non-conflicting ADC channels usage). However, as shown by recent discussions on this list, it is not yet entirely clearly how this should be done: * Should we have some low-level ADC driver in arch/arm/mach-at91/ that allows to request/release/access the ADC channels, this driver providing an internal kernel API used by the IIO ADC driver and the touchscreen driver ? sysfs access /dev/input/... to ADC channels access to touchscreen || || \/ \/ +------------------+ +------------------+ | AT91 IIO | | AT91 touchscreen | +------------------+ +------------------+ | | \_______________________________/ | +---------------------+ | Some low-level ADC | | code to request, | | release and access | | ADC channels | +---------------------+ * Should IIO provide some internal kernel API (as you suggested some time ago) so that kernel drivers can use ADC channels ? +-------------------+ | AT91 touchscreen | -> /dev/input/... access to touchscreen +-------------------+ || \/ +-------------------+ | AT91 IIO | -> sysfs access to ADC channels +-------------------+ Regards, Thomas --=20 Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-20 7:05 ` Thomas Petazzoni 0 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-10-20 7:05 UTC (permalink / raw) To: linux-arm-kernel Hello, Le Wed, 19 Oct 2011 20:23:50 +0200, Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit : > On 19/10/2011 18:42, Jonathan Cameron wrote: > > Afraid I'm out of time for today, but just thought I'd ask one quick question... > > > > Why isn't this touchscreen ADC in input? > > Because what I say in the Kconfig is mostly wrong... Sorry about that. > > Actually, like I was saying, this ADC is present in a lot of AT91 SoC, > and while it is always an ADC, sometimes (like on the G45), it can be > used as a touchscreen controller, or even as both, with channels for the > touchscreen and channels as ADC. On the G20 however, it is just an ADC. > > For now, the plan is only to support the ADC mode, so I put it in adc. Just to expand a bit on this: there is already in the kernel an input driver for the AT91 touchscreen which uses some ADC channels (that have additional touchscreen-related features: on the G45, you have 8 ADCs channels, 4 can optionally be used for touchscreen, the 4 other are just raw ADCs). The ultimate goal is of course to make it possible to use both the IIO AT91 ADC driver and the AT91 touchscreen driver work simultaneously (with of course non-conflicting ADC channels usage). However, as shown by recent discussions on this list, it is not yet entirely clearly how this should be done: * Should we have some low-level ADC driver in arch/arm/mach-at91/ that allows to request/release/access the ADC channels, this driver providing an internal kernel API used by the IIO ADC driver and the touchscreen driver ? sysfs access /dev/input/... to ADC channels access to touchscreen || || \/ \/ +------------------+ +------------------+ | AT91 IIO | | AT91 touchscreen | +------------------+ +------------------+ | | \_______________________________/ | +---------------------+ | Some low-level ADC | | code to request, | | release and access | | ADC channels | +---------------------+ * Should IIO provide some internal kernel API (as you suggested some time ago) so that kernel drivers can use ADC channels ? +-------------------+ | AT91 touchscreen | -> /dev/input/... access to touchscreen +-------------------+ || \/ +-------------------+ | AT91 IIO | -> sysfs access to ADC channels +-------------------+ Regards, Thomas -- Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-20 7:05 ` Thomas Petazzoni @ 2011-10-20 8:33 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-10-20 8:33 UTC (permalink / raw) To: Thomas Petazzoni Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel, Mark Brown, Linus Walleij Bringing in Mark and Linus as others who have been active in talking ab= out similar issues. > Hello, >=20 > Le Wed, 19 Oct 2011 20:23:50 +0200, > Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit : >=20 >> On 19/10/2011 18:42, Jonathan Cameron wrote: >>> Afraid I'm out of time for today, but just thought I'd ask one quic= k question... >>> >>> Why isn't this touchscreen ADC in input? >> >> Because what I say in the Kconfig is mostly wrong... Sorry about tha= t. >> >> Actually, like I was saying, this ADC is present in a lot of AT91 So= C, >> and while it is always an ADC, sometimes (like on the G45), it can b= e >> used as a touchscreen controller, or even as both, with channels for= the >> touchscreen and channels as ADC. On the G20 however, it is just an A= DC. >> >> For now, the plan is only to support the ADC mode, so I put it in ad= c. >=20 > Just to expand a bit on this: there is already in the kernel an input > driver for the AT91 touchscreen which uses some ADC channels (that ha= ve > additional touchscreen-related features: on the G45, you have 8 ADCs > channels, 4 can optionally be used for touchscreen, the 4 other are > just raw ADCs). >=20 > The ultimate goal is of course to make it possible to use both the II= O > AT91 ADC driver and the AT91 touchscreen driver work simultaneously > (with of course non-conflicting ADC channels usage). However, as show= n > by recent discussions on this list, it is not yet entirely clearly ho= w > this should be done: >=20 > * Should we have some low-level ADC driver in arch/arm/mach-at91/ th= at > allows to request/release/access the ADC channels, this driver > providing an internal kernel API used by the IIO ADC driver and th= e > touchscreen driver ? That's definitely not going to go down well against moves to move every= thing that looks like a driver out of the arch directories. An equivalent som= ewhere else might work though. >=20 > sysfs access /dev/input/... > to ADC channels access to touchscreen > || || > \/ \/ > +------------------+ +------------------+ > | AT91 IIO | | AT91 touchscreen | > +------------------+ +------------------+ > | | > \_______________________________/ > | > +---------------------+ > | Some low-level ADC | > | code to request, | > | release and access | > | ADC channels | > +---------------------+ >=20 > * Should IIO provide some internal kernel API (as you suggested some > time ago) so that kernel drivers can use ADC channels ? >=20 >=20 > +-------------------+ > | AT91 touchscreen | -> /dev/input/... access to touchsc= reen > +-------------------+ > || > \/ > +-------------------+ > | AT91 IIO | -> sysfs access to ADC channels > +-------------------+ >=20 > Regards, >=20 > Thomas Agreed. This is currently up in the air. The current state of those II= O hooks is that they do pull only. Push based capture is tricky as for I= IO we want to control the triggering of the push at a far finer scale than makes sense for input. I'm not yet clear how these two will play togeth= er. If we sit something underneath the IIO and input drivers (or use the lo= wer portions of IIO to do this) then the intent would be to have a fully ge= neric input touchscreen driver on top. I'm not sure that's possible. For example are the switches on the G45's adc (from datasheet) something th= at all/many similar touch screen controllers have? I suppose we could support these in the chan_spec but only if it helps us to write a generic touch screen driver on top. I have no real feeling for whether this would be possible... (all my boards are headless ;) Jonathan ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-20 8:33 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-10-20 8:33 UTC (permalink / raw) To: linux-arm-kernel Bringing in Mark and Linus as others who have been active in talking about similar issues. > Hello, > > Le Wed, 19 Oct 2011 20:23:50 +0200, > Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit : > >> On 19/10/2011 18:42, Jonathan Cameron wrote: >>> Afraid I'm out of time for today, but just thought I'd ask one quick question... >>> >>> Why isn't this touchscreen ADC in input? >> >> Because what I say in the Kconfig is mostly wrong... Sorry about that. >> >> Actually, like I was saying, this ADC is present in a lot of AT91 SoC, >> and while it is always an ADC, sometimes (like on the G45), it can be >> used as a touchscreen controller, or even as both, with channels for the >> touchscreen and channels as ADC. On the G20 however, it is just an ADC. >> >> For now, the plan is only to support the ADC mode, so I put it in adc. > > Just to expand a bit on this: there is already in the kernel an input > driver for the AT91 touchscreen which uses some ADC channels (that have > additional touchscreen-related features: on the G45, you have 8 ADCs > channels, 4 can optionally be used for touchscreen, the 4 other are > just raw ADCs). > > The ultimate goal is of course to make it possible to use both the IIO > AT91 ADC driver and the AT91 touchscreen driver work simultaneously > (with of course non-conflicting ADC channels usage). However, as shown > by recent discussions on this list, it is not yet entirely clearly how > this should be done: > > * Should we have some low-level ADC driver in arch/arm/mach-at91/ that > allows to request/release/access the ADC channels, this driver > providing an internal kernel API used by the IIO ADC driver and the > touchscreen driver ? That's definitely not going to go down well against moves to move everything that looks like a driver out of the arch directories. An equivalent somewhere else might work though. > > sysfs access /dev/input/... > to ADC channels access to touchscreen > || || > \/ \/ > +------------------+ +------------------+ > | AT91 IIO | | AT91 touchscreen | > +------------------+ +------------------+ > | | > \_______________________________/ > | > +---------------------+ > | Some low-level ADC | > | code to request, | > | release and access | > | ADC channels | > +---------------------+ > > * Should IIO provide some internal kernel API (as you suggested some > time ago) so that kernel drivers can use ADC channels ? > > > +-------------------+ > | AT91 touchscreen | -> /dev/input/... access to touchscreen > +-------------------+ > || > \/ > +-------------------+ > | AT91 IIO | -> sysfs access to ADC channels > +-------------------+ > > Regards, > > Thomas Agreed. This is currently up in the air. The current state of those IIO hooks is that they do pull only. Push based capture is tricky as for IIO we want to control the triggering of the push at a far finer scale than makes sense for input. I'm not yet clear how these two will play together. If we sit something underneath the IIO and input drivers (or use the lower portions of IIO to do this) then the intent would be to have a fully generic input touchscreen driver on top. I'm not sure that's possible. For example are the switches on the G45's adc (from datasheet) something that all/many similar touch screen controllers have? I suppose we could support these in the chan_spec but only if it helps us to write a generic touch screen driver on top. I have no real feeling for whether this would be possible... (all my boards are headless ;) Jonathan ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-20 8:33 ` Jonathan Cameron @ 2011-10-20 8:49 ` Thomas Petazzoni -1 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-10-20 8:49 UTC (permalink / raw) To: Jonathan Cameron Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel, Mark Brown, Linus Walleij Le Thu, 20 Oct 2011 09:33:29 +0100, Jonathan Cameron <jic23@cam.ac.uk> a =C3=A9crit : > > * Should we have some low-level ADC driver in arch/arm/mach-at91/ that > > allows to request/release/access the ADC channels, this driver > > providing an internal kernel API used by the IIO ADC driver and the > > touchscreen driver ? > That's definitely not going to go down well against moves to move everyth= ing > that looks like a driver out of the arch directories. An equivalent somew= here > else might work though. Yes, of course if this needs to be implemented, it should be in some other location than arch/arm, but not sure where (which is basically the discussion that was started some time ago). > Agreed. This is currently up in the air. The current state of those IIO > hooks is that they do pull only. Push based capture is tricky as for IIO > we want to control the triggering of the push at a far finer scale than > makes sense for input. I'm not yet clear how these two will play together. My IIO knowledge is still too limited to understand what's the problem with exposing an API for push-based capture. > If we sit something underneath the IIO and input drivers (or use the lower > portions of IIO to do this) then the intent would be to have a fully gene= ric > input touchscreen driver on top. I'm not sure that's possible. I am not sure it's possible to make the touchscreen driver generic. On the G45, the ADC IP has some generic ADC registers, but also some touchscreen-specific registers. So the touchscreen driver probably cannot be completely generic and some cooperation between the AT91 ADC driver and the AT91 touchscreen driver might be needed. I'd have to look into more details on how the AT91 touchscreen thing works to provide some more details here. > For example are the switches on the G45's adc (from datasheet) something = that > all/many similar touch screen controllers have? I have no idea. However, I have also seen a platform (SuperH 2A) where ADC channels are used for keypad handling. 4 ADCs channels, each connected to 4 keys, each having a different resistor. Depending on the voltage measured at the ADC, you're capable of knowing which key of the 4x4 keypad is pressed. This is a different situation where the ADC values need to be used by another in-kernel driver. Regards, Thomas --=20 Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-20 8:49 ` Thomas Petazzoni 0 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-10-20 8:49 UTC (permalink / raw) To: linux-arm-kernel Le Thu, 20 Oct 2011 09:33:29 +0100, Jonathan Cameron <jic23@cam.ac.uk> a ?crit : > > * Should we have some low-level ADC driver in arch/arm/mach-at91/ that > > allows to request/release/access the ADC channels, this driver > > providing an internal kernel API used by the IIO ADC driver and the > > touchscreen driver ? > That's definitely not going to go down well against moves to move everything > that looks like a driver out of the arch directories. An equivalent somewhere > else might work though. Yes, of course if this needs to be implemented, it should be in some other location than arch/arm, but not sure where (which is basically the discussion that was started some time ago). > Agreed. This is currently up in the air. The current state of those IIO > hooks is that they do pull only. Push based capture is tricky as for IIO > we want to control the triggering of the push at a far finer scale than > makes sense for input. I'm not yet clear how these two will play together. My IIO knowledge is still too limited to understand what's the problem with exposing an API for push-based capture. > If we sit something underneath the IIO and input drivers (or use the lower > portions of IIO to do this) then the intent would be to have a fully generic > input touchscreen driver on top. I'm not sure that's possible. I am not sure it's possible to make the touchscreen driver generic. On the G45, the ADC IP has some generic ADC registers, but also some touchscreen-specific registers. So the touchscreen driver probably cannot be completely generic and some cooperation between the AT91 ADC driver and the AT91 touchscreen driver might be needed. I'd have to look into more details on how the AT91 touchscreen thing works to provide some more details here. > For example are the switches on the G45's adc (from datasheet) something that > all/many similar touch screen controllers have? I have no idea. However, I have also seen a platform (SuperH 2A) where ADC channels are used for keypad handling. 4 ADCs channels, each connected to 4 keys, each having a different resistor. Depending on the voltage measured at the ADC, you're capable of knowing which key of the 4x4 keypad is pressed. This is a different situation where the ADC values need to be used by another in-kernel driver. Regards, Thomas -- Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-20 8:49 ` Thomas Petazzoni @ 2011-10-20 9:19 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-10-20 9:19 UTC (permalink / raw) To: Thomas Petazzoni Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel, Mark Brown, Linus Walleij On 10/20/11 09:49, Thomas Petazzoni wrote: > Le Thu, 20 Oct 2011 09:33:29 +0100, > Jonathan Cameron <jic23@cam.ac.uk> a =C3=A9crit : >=20 >>> * Should we have some low-level ADC driver in arch/arm/mach-at91/ = that >>> allows to request/release/access the ADC channels, this driver >>> providing an internal kernel API used by the IIO ADC driver and = the >>> touchscreen driver ? >> That's definitely not going to go down well against moves to move ev= erything >> that looks like a driver out of the arch directories. An equivalent = somewhere >> else might work though. >=20 > Yes, of course if this needs to be implemented, it should be in some > other location than arch/arm, but not sure where (which is basically > the discussion that was started some time ago). >=20 >> Agreed. This is currently up in the air. The current state of those= IIO >> hooks is that they do pull only. Push based capture is tricky as fo= r IIO >> we want to control the triggering of the push at a far finer scale t= han >> makes sense for input. I'm not yet clear how these two will play tog= ether. >=20 > My IIO knowledge is still too limited to understand what's the proble= m > with exposing an API for push-based capture. I'll give a quick summary: Push in IIO is done from a trigger (imagine an oscilloscope). Those tr= iggers may be supplied by a dataready signal or from somewhere entirely differ= ent. Also multiple devices can be triggered by the same signal. Thus the da= taready from one chip can trigger a whole load of others. Right now filtering functions prevent some devices from using anything = other than their own trigger. Setting defaults is also possible. If anything is t= hen registered to receive the data the trigger cannot be changed anyway so that will w= ork here. The other bit that makes it complex is the data flow. This is done in the form of scans of a set of channels. The description= of these are more or less available to the core. Note the scan you get may well= have more channels in it than you explicitly requested. These scans are then pushed into one of a set of different buffers. No= te All of the scan is currently pushed. That makes sense if you only have= one user of the data. Right now the pushing is done by the individual driv= ers but this could pass through the core. So things that stand in the way of more general use: 1) Userspace controlled triggers (apply to whole ADC) - can lock these = down if say a touchscreen driver is using the device. 2) Single output stream of data. 3) Note there is deliberately no metadata in the stream. It is all out = of band. Push to one driver is easy. Anything else needs a demux that is aware = of the stream contents. This bit doesn't exist and looks non trivial. The re= al trick is going to be keeping it light and fast (or entirely out of the way when not needed.) At some point I'll have a play with rerouting the data so such a demux can sit in the current flow (can use it to kill off unwanted channels). >=20 >> If we sit something underneath the IIO and input drivers (or use the= lower >> portions of IIO to do this) then the intent would be to have a fully= generic >> input touchscreen driver on top. I'm not sure that's possible. >=20 > I am not sure it's possible to make the touchscreen driver generic. O= n > the G45, the ADC IP has some generic ADC registers, but also some > touchscreen-specific registers. So the touchscreen driver probably > cannot be completely generic and some cooperation between the AT91 AD= C > driver and the AT91 touchscreen driver might be needed. I'd have to > look into more details on how the AT91 touchscreen thing works to > provide some more details here. >=20 >> For example are the switches on the G45's adc (from datasheet) somet= hing that >> all/many similar touch screen controllers have? >=20 > I have no idea. >=20 > However, I have also seen a platform (SuperH 2A) where ADC channels a= re > used for keypad handling. 4 ADCs channels, each connected to 4 keys, > each having a different resistor. Depending on the voltage measured a= t > the ADC, you're capable of knowing which key of the 4x4 keypad is > pressed. This is a different situation where the ADC values need to b= e > used by another in-kernel driver. That one is much easier to handle and indeed not that uncommon a hack. It really is just using the ADCs to get a value. Hence with suitable descriptive map it's easy to write a generic driver for it. Even easier if it's simply polled ;) >=20 > Regards, >=20 > Thomas ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-20 9:19 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-10-20 9:19 UTC (permalink / raw) To: linux-arm-kernel On 10/20/11 09:49, Thomas Petazzoni wrote: > Le Thu, 20 Oct 2011 09:33:29 +0100, > Jonathan Cameron <jic23@cam.ac.uk> a ?crit : > >>> * Should we have some low-level ADC driver in arch/arm/mach-at91/ that >>> allows to request/release/access the ADC channels, this driver >>> providing an internal kernel API used by the IIO ADC driver and the >>> touchscreen driver ? >> That's definitely not going to go down well against moves to move everything >> that looks like a driver out of the arch directories. An equivalent somewhere >> else might work though. > > Yes, of course if this needs to be implemented, it should be in some > other location than arch/arm, but not sure where (which is basically > the discussion that was started some time ago). > >> Agreed. This is currently up in the air. The current state of those IIO >> hooks is that they do pull only. Push based capture is tricky as for IIO >> we want to control the triggering of the push at a far finer scale than >> makes sense for input. I'm not yet clear how these two will play together. > > My IIO knowledge is still too limited to understand what's the problem > with exposing an API for push-based capture. I'll give a quick summary: Push in IIO is done from a trigger (imagine an oscilloscope). Those triggers may be supplied by a dataready signal or from somewhere entirely different. Also multiple devices can be triggered by the same signal. Thus the dataready from one chip can trigger a whole load of others. Right now filtering functions prevent some devices from using anything other than their own trigger. Setting defaults is also possible. If anything is then registered to receive the data the trigger cannot be changed anyway so that will work here. The other bit that makes it complex is the data flow. This is done in the form of scans of a set of channels. The description of these are more or less available to the core. Note the scan you get may well have more channels in it than you explicitly requested. These scans are then pushed into one of a set of different buffers. Note All of the scan is currently pushed. That makes sense if you only have one user of the data. Right now the pushing is done by the individual drivers but this could pass through the core. So things that stand in the way of more general use: 1) Userspace controlled triggers (apply to whole ADC) - can lock these down if say a touchscreen driver is using the device. 2) Single output stream of data. 3) Note there is deliberately no metadata in the stream. It is all out of band. Push to one driver is easy. Anything else needs a demux that is aware of the stream contents. This bit doesn't exist and looks non trivial. The real trick is going to be keeping it light and fast (or entirely out of the way when not needed.) At some point I'll have a play with rerouting the data so such a demux can sit in the current flow (can use it to kill off unwanted channels). > >> If we sit something underneath the IIO and input drivers (or use the lower >> portions of IIO to do this) then the intent would be to have a fully generic >> input touchscreen driver on top. I'm not sure that's possible. > > I am not sure it's possible to make the touchscreen driver generic. On > the G45, the ADC IP has some generic ADC registers, but also some > touchscreen-specific registers. So the touchscreen driver probably > cannot be completely generic and some cooperation between the AT91 ADC > driver and the AT91 touchscreen driver might be needed. I'd have to > look into more details on how the AT91 touchscreen thing works to > provide some more details here. > >> For example are the switches on the G45's adc (from datasheet) something that >> all/many similar touch screen controllers have? > > I have no idea. > > However, I have also seen a platform (SuperH 2A) where ADC channels are > used for keypad handling. 4 ADCs channels, each connected to 4 keys, > each having a different resistor. Depending on the voltage measured at > the ADC, you're capable of knowing which key of the 4x4 keypad is > pressed. This is a different situation where the ADC values need to be > used by another in-kernel driver. That one is much easier to handle and indeed not that uncommon a hack. It really is just using the ADCs to get a value. Hence with suitable descriptive map it's easy to write a generic driver for it. Even easier if it's simply polled ;) > > Regards, > > Thomas ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-20 8:33 ` Jonathan Cameron @ 2011-10-20 9:52 ` Mark Brown -1 siblings, 0 replies; 131+ messages in thread From: Mark Brown @ 2011-10-20 9:52 UTC (permalink / raw) To: Jonathan Cameron Cc: Thomas Petazzoni, linux-iio, Patrice Vilchez, Linus Walleij, Nicolas Ferre, Maxime Ripard, linux-arm-kernel On Thu, Oct 20, 2011 at 09:33:29AM +0100, Jonathan Cameron wrote: > Agreed. This is currently up in the air. The current state of those IIO > hooks is that they do pull only. Push based capture is tricky as for IIO > we want to control the triggering of the push at a far finer scale than > makes sense for input. I'm not yet clear how these two will play together. What's the issue here? The touchscreen input is just a trigger > If we sit something underneath the IIO and input drivers (or use the lower > portions of IIO to do this) then the intent would be to have a fully generic > input touchscreen driver on top. I'm not sure that's possible. For I'm not sure that's going to be tractable, while the ADC usage may be the same I'd expect to see a bunch of other logic around them for pen down detection. I'd guess it is going to be more tractable to have a library that most ADC touchscreens can share which handles the actual data transfer bit of things but has extra glue logic around it to make the actual device. ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-20 9:52 ` Mark Brown 0 siblings, 0 replies; 131+ messages in thread From: Mark Brown @ 2011-10-20 9:52 UTC (permalink / raw) To: linux-arm-kernel On Thu, Oct 20, 2011 at 09:33:29AM +0100, Jonathan Cameron wrote: > Agreed. This is currently up in the air. The current state of those IIO > hooks is that they do pull only. Push based capture is tricky as for IIO > we want to control the triggering of the push at a far finer scale than > makes sense for input. I'm not yet clear how these two will play together. What's the issue here? The touchscreen input is just a trigger > If we sit something underneath the IIO and input drivers (or use the lower > portions of IIO to do this) then the intent would be to have a fully generic > input touchscreen driver on top. I'm not sure that's possible. For I'm not sure that's going to be tractable, while the ADC usage may be the same I'd expect to see a bunch of other logic around them for pen down detection. I'd guess it is going to be more tractable to have a library that most ADC touchscreens can share which handles the actual data transfer bit of things but has extra glue logic around it to make the actual device. ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-20 7:09 ` Lars-Peter Clausen -1 siblings, 0 replies; 131+ messages in thread From: Lars-Peter Clausen @ 2011-10-20 7:09 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez On 10/19/2011 06:18 PM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > [...] > + > +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) > +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) Shouldn't this rather be {readl,writel}_relaxed? > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; > + struct iio_chan_spec *channels; > + int nb_chan; > + int irq; > + wait_queue_head_t wq_data_avail; > + u16 lcdr; > + void __iomem *reg_base; > + > +}; > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct at91adc_state *st = private; > + struct iio_dev *idev = iio_priv_to_dev(st); > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < st->nb_chan; chan++) { > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->lcdr = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); > + } > + } > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct at91adc_state *st) > +{ > + int ret = 0, i; > + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, > + GFP_KERNEL); kcalloc > + if (st->channels == NULL) > + return -ENOMEM; > + > + for (i = 0; i < st->nb_chan; i++) { > + struct iio_chan_spec *chan = st->channels + i; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; It probably makes sense to initialize scan_type as well. > + ++ret; > + } > + > + return ret; Why do you need ret? Shouldn't this be just st->nb_chan? > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible(st->wq_data_avail, st->done); Its probably a good idea to add a timeout in case there is a problem with the hardware. > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + dev_dbg(&pdev->dev, "AT91ADC probed\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(*st)); > + if (idev == NULL) { > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = platform_get_device_id(pdev)->name; You don't use device ids. This should cause a NULL pointer deref? Maybe use dev_name(&pdev->dev) instead. > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > +[...] > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + free_irq(st->irq, st); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_unregister(idev); > + The iio API changed a bit recently and uses a two stage unregistration now. You should call iio_device_unregister before freeing resources and iio_device_free afterwards. > + return 0; > +} > + > [...] ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-20 7:09 ` Lars-Peter Clausen 0 siblings, 0 replies; 131+ messages in thread From: Lars-Peter Clausen @ 2011-10-20 7:09 UTC (permalink / raw) To: linux-arm-kernel On 10/19/2011 06:18 PM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > [...] > + > +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) > +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) Shouldn't this rather be {readl,writel}_relaxed? > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; > + struct iio_chan_spec *channels; > + int nb_chan; > + int irq; > + wait_queue_head_t wq_data_avail; > + u16 lcdr; > + void __iomem *reg_base; > + > +}; > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct at91adc_state *st = private; > + struct iio_dev *idev = iio_priv_to_dev(st); > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < st->nb_chan; chan++) { > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->lcdr = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); > + } > + } > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct at91adc_state *st) > +{ > + int ret = 0, i; > + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, > + GFP_KERNEL); kcalloc > + if (st->channels == NULL) > + return -ENOMEM; > + > + for (i = 0; i < st->nb_chan; i++) { > + struct iio_chan_spec *chan = st->channels + i; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; It probably makes sense to initialize scan_type as well. > + ++ret; > + } > + > + return ret; Why do you need ret? Shouldn't this be just st->nb_chan? > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible(st->wq_data_avail, st->done); Its probably a good idea to add a timeout in case there is a problem with the hardware. > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + dev_dbg(&pdev->dev, "AT91ADC probed\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(*st)); > + if (idev == NULL) { > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = platform_get_device_id(pdev)->name; You don't use device ids. This should cause a NULL pointer deref? Maybe use dev_name(&pdev->dev) instead. > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > +[...] > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + free_irq(st->irq, st); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_unregister(idev); > + The iio API changed a bit recently and uses a two stage unregistration now. You should call iio_device_unregister before freeing resources and iio_device_free afterwards. > + return 0; > +} > + > [...] ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-20 7:09 ` Lars-Peter Clausen @ 2011-10-21 17:54 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-21 17:54 UTC (permalink / raw) To: Lars-Peter Clausen Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez Hi, On 20/10/2011 09:09, Lars-Peter Clausen wrote: > On 10/19/2011 06:18 PM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> [...] >> + >> +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) >> +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) > > Shouldn't this rather be {readl,writel}_relaxed? Yep, corrected. >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + struct iio_chan_spec *channels; >> + int nb_chan; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 lcdr; >> + void __iomem *reg_base; >> + >> +}; >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct at91adc_state *st = private; >> + struct iio_dev *idev = iio_priv_to_dev(st); >> + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < st->nb_chan; chan++) { >> + if (status & AT91_ADC_EOC(chan)) { >> + st->done = true; >> + st->lcdr = at91adc_reg_read(st->reg_base, >> + AT91_ADC_CHR(chan)); >> + } >> + } >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct at91adc_state *st) >> +{ >> + int ret = 0, i; >> + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, >> + GFP_KERNEL); > > kcalloc Corrected >> + if (st->channels == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < st->nb_chan; i++) { >> + struct iio_chan_spec *chan = st->channels + i; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; > > > It probably makes sense to initialize scan_type as well. Ah, didn't see that one, but yeah, you're right. >> + ++ret; >> + } >> + >> + return ret; > > Why do you need ret? Shouldn't this be just st->nb_chan? The initial plan was to make sure all channels were initialised, but obviously, my code isn't making such use of ret. You're right. >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible(st->wq_data_avail, st->done); > > Its probably a good idea to add a timeout in case there is a problem with > the hardware. Indeed. >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + dev_dbg(&pdev->dev, "AT91ADC probed\n"); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(*st)); >> + if (idev == NULL) { >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = platform_get_device_id(pdev)->name; > > You don't use device ids. This should cause a NULL pointer deref? Maybe use > dev_name(&pdev->dev) instead. Ok. >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> +[...] >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + free_irq(st->irq, st); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_unregister(idev); >> + > > The iio API changed a bit recently and uses a two stage unregistration now. > You should call iio_device_unregister before freeing resources and > iio_device_free afterwards. Hmmm, I don't see the iio_device_free in Jonathan's git. Is it iio_free_device, or should I use another git repo/branch as base ? Thanks, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-21 17:54 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-21 17:54 UTC (permalink / raw) To: linux-arm-kernel Hi, On 20/10/2011 09:09, Lars-Peter Clausen wrote: > On 10/19/2011 06:18 PM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> [...] >> + >> +#define at91adc_reg_read(base, reg) __raw_readl((base) + (reg)) >> +#define at91adc_reg_write(base, reg, val) __raw_writel((val), (base) + (reg)) > > Shouldn't this rather be {readl,writel}_relaxed? Yep, corrected. >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + struct iio_chan_spec *channels; >> + int nb_chan; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 lcdr; >> + void __iomem *reg_base; >> + >> +}; >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct at91adc_state *st = private; >> + struct iio_dev *idev = iio_priv_to_dev(st); >> + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < st->nb_chan; chan++) { >> + if (status & AT91_ADC_EOC(chan)) { >> + st->done = true; >> + st->lcdr = at91adc_reg_read(st->reg_base, >> + AT91_ADC_CHR(chan)); >> + } >> + } >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct at91adc_state *st) >> +{ >> + int ret = 0, i; >> + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, >> + GFP_KERNEL); > > kcalloc Corrected >> + if (st->channels == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < st->nb_chan; i++) { >> + struct iio_chan_spec *chan = st->channels + i; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; > > > It probably makes sense to initialize scan_type as well. Ah, didn't see that one, but yeah, you're right. >> + ++ret; >> + } >> + >> + return ret; > > Why do you need ret? Shouldn't this be just st->nb_chan? The initial plan was to make sure all channels were initialised, but obviously, my code isn't making such use of ret. You're right. >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible(st->wq_data_avail, st->done); > > Its probably a good idea to add a timeout in case there is a problem with > the hardware. Indeed. >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + dev_dbg(&pdev->dev, "AT91ADC probed\n"); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(*st)); >> + if (idev == NULL) { >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = platform_get_device_id(pdev)->name; > > You don't use device ids. This should cause a NULL pointer deref? Maybe use > dev_name(&pdev->dev) instead. Ok. >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> +[...] >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + free_irq(st->irq, st); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_unregister(idev); >> + > > The iio API changed a bit recently and uses a two stage unregistration now. > You should call iio_device_unregister before freeing resources and > iio_device_free afterwards. Hmmm, I don't see the iio_device_free in Jonathan's git. Is it iio_free_device, or should I use another git repo/branch as base ? Thanks, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-21 17:54 ` Maxime Ripard @ 2011-10-21 17:55 ` Lars-Peter Clausen -1 siblings, 0 replies; 131+ messages in thread From: Lars-Peter Clausen @ 2011-10-21 17:55 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez On 10/21/2011 07:54 PM, Maxime Ripard wrote: > [...] >>> +static int __devexit at91adc_remove(struct platform_device *pdev) >>> +{ >>> + struct iio_dev *idev = platform_get_drvdata(pdev); >>> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + struct at91adc_state *st = iio_priv(idev); >>> + >>> + free_irq(st->irq, st); >>> + iounmap(st->reg_base); >>> + release_mem_region(res->start, resource_size(res)); >>> + iio_device_unregister(idev); >>> + >> >> The iio API changed a bit recently and uses a two stage unregistration now. >> You should call iio_device_unregister before freeing resources and >> iio_device_free afterwards. > > Hmmm, I don't see the iio_device_free in Jonathan's git. Is it > iio_free_device, or should I use another git repo/branch as base ? > > Thanks, > Yes, iio_free_device, sorry. - Lars ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-21 17:55 ` Lars-Peter Clausen 0 siblings, 0 replies; 131+ messages in thread From: Lars-Peter Clausen @ 2011-10-21 17:55 UTC (permalink / raw) To: linux-arm-kernel On 10/21/2011 07:54 PM, Maxime Ripard wrote: > [...] >>> +static int __devexit at91adc_remove(struct platform_device *pdev) >>> +{ >>> + struct iio_dev *idev = platform_get_drvdata(pdev); >>> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + struct at91adc_state *st = iio_priv(idev); >>> + >>> + free_irq(st->irq, st); >>> + iounmap(st->reg_base); >>> + release_mem_region(res->start, resource_size(res)); >>> + iio_device_unregister(idev); >>> + >> >> The iio API changed a bit recently and uses a two stage unregistration now. >> You should call iio_device_unregister before freeing resources and >> iio_device_free afterwards. > > Hmmm, I don't see the iio_device_free in Jonathan's git. Is it > iio_free_device, or should I use another git repo/branch as base ? > > Thanks, > Yes, iio_free_device, sorry. - Lars ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-23 9:08 ` Jean-Christophe PLAGNIOL-VILLARD -1 siblings, 0 replies; 131+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-10-23 9:08 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre On 18:18 Wed 19 Oct , Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> please keep me in CC > + > +static int at91adc_channel_init(struct at91adc_state *st) > +{ > + int ret = 0, i; > + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, > + GFP_KERNEL); > + if (st->channels == NULL) > + return -ENOMEM; > + > + for (i = 0; i < st->nb_chan; i++) { > + struct iio_chan_spec *chan = st->channels + i; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; > + ++ret; > + } > + > + return ret; > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible(st->wq_data_avail, st->done); > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; do not refence it copy need for the DT > + > + dev_dbg(&pdev->dev, "AT91ADC probed\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(*st)); > + if (idev == NULL) { > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = platform_get_device_id(pdev)->name; > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } where is the platform data struct? Best Regards, J. ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-23 9:08 ` Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 0 replies; 131+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-10-23 9:08 UTC (permalink / raw) To: linux-arm-kernel On 18:18 Wed 19 Oct , Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> please keep me in CC > + > +static int at91adc_channel_init(struct at91adc_state *st) > +{ > + int ret = 0, i; > + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, > + GFP_KERNEL); > + if (st->channels == NULL) > + return -ENOMEM; > + > + for (i = 0; i < st->nb_chan; i++) { > + struct iio_chan_spec *chan = st->channels + i; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; > + ++ret; > + } > + > + return ret; > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible(st->wq_data_avail, st->done); > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; do not refence it copy need for the DT > + > + dev_dbg(&pdev->dev, "AT91ADC probed\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(*st)); > + if (idev == NULL) { > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = platform_get_device_id(pdev)->name; > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } where is the platform data struct? Best Regards, J. ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-10-23 9:08 ` Jean-Christophe PLAGNIOL-VILLARD @ 2011-10-24 8:21 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-24 8:21 UTC (permalink / raw) To: Jean-Christophe PLAGNIOL-VILLARD Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre Hi, On 23/10/2011 11:08, Jean-Christophe PLAGNIOL-VILLARD wrote: > On 18:18 Wed 19 Oct , Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > please keep me in CC ACK. >> + >> +static int at91adc_channel_init(struct at91adc_state *st) >> +{ >> + int ret = 0, i; >> + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, >> + GFP_KERNEL); >> + if (st->channels == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < st->nb_chan; i++) { >> + struct iio_chan_spec *chan = st->channels + i; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; >> + ++ret; >> + } >> + >> + return ret; >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible(st->wq_data_avail, st->done); >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; > do not refence it copy need for the DT Ok >> + >> + dev_dbg(&pdev->dev, "AT91ADC probed\n"); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(*st)); >> + if (idev == NULL) { >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = platform_get_device_id(pdev)->name; >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } > where is the platform data struct? In the first patch. Regards, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-10-24 8:21 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-24 8:21 UTC (permalink / raw) To: linux-arm-kernel Hi, On 23/10/2011 11:08, Jean-Christophe PLAGNIOL-VILLARD wrote: > On 18:18 Wed 19 Oct , Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > please keep me in CC ACK. >> + >> +static int at91adc_channel_init(struct at91adc_state *st) >> +{ >> + int ret = 0, i; >> + st->channels = kzalloc(sizeof(struct iio_chan_spec) * st->nb_chan, >> + GFP_KERNEL); >> + if (st->channels == NULL) >> + return -ENOMEM; >> + >> + for (i = 0; i < st->nb_chan; i++) { >> + struct iio_chan_spec *chan = st->channels + i; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; >> + ++ret; >> + } >> + >> + return ret; >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible(st->wq_data_avail, st->done); >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; > do not refence it copy need for the DT Ok >> + >> + dev_dbg(&pdev->dev, "AT91ADC probed\n"); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(*st)); >> + if (idev == NULL) { >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = platform_get_device_id(pdev)->name; >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } > where is the platform data struct? In the first patch. Regards, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-19 16:18 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 44 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 12 ++++++++ 2 files changed, 56 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,50 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + at91_set_A_periph(AT91_PIN_PC0, 0); + at91_set_A_periph(AT91_PIN_PC1, 0); + at91_set_A_periph(AT91_PIN_PC2, 0); + at91_set_A_periph(AT91_PIN_PC3, 0); + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..fd373c3 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels = 4, + .adc_clock = 5000000, + .startup_time = 10, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +399,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-10-19 16:18 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-19 16:18 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 44 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 12 ++++++++ 2 files changed, 56 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,50 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + at91_set_A_periph(AT91_PIN_PC0, 0); + at91_set_A_periph(AT91_PIN_PC1, 0); + at91_set_A_periph(AT91_PIN_PC2, 0); + at91_set_A_periph(AT91_PIN_PC3, 0); + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..fd373c3 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels = 4, + .adc_clock = 5000000, + .startup_time = 10, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +399,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-20 6:28 ` Alexander Stein -1 siblings, 0 replies; 131+ messages in thread From: Alexander Stein @ 2011-10-20 6:28 UTC (permalink / raw) To: linux-arm-kernel; +Cc: Maxime Ripard, linux-iio, Patrice Vilchez, Nicolas Ferre On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote: > diff --git a/arch/arm/mach-at91/at91sam9260_devices.c > b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644 > --- a/arch/arm/mach-at91/at91sam9260_devices.c > +++ b/arch/arm/mach-at91/at91sam9260_devices.c > [...] > +static struct platform_device at91_adc_device = { > + .name = "at91adc", > + .id = -1, > + .dev = { > + .platform_data = &adc_data, > + }, > + .resource = adc_resources, > + .num_resources = ARRAY_SIZE(adc_resources), > +}; > + > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + at91_set_A_periph(AT91_PIN_PC0, 0); > + at91_set_A_periph(AT91_PIN_PC1, 0); > + at91_set_A_periph(AT91_PIN_PC2, 0); > + at91_set_A_periph(AT91_PIN_PC3, 0); > + > + adc_data = *data; > + platform_device_register(&at91_adc_device); > +} > + > + This assumes that all 4 channels are used every time. How about only using an AD channel on PC2 or all but the one on PC1? Regards, Alexander ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-10-20 6:28 ` Alexander Stein 0 siblings, 0 replies; 131+ messages in thread From: Alexander Stein @ 2011-10-20 6:28 UTC (permalink / raw) To: linux-arm-kernel On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote: > diff --git a/arch/arm/mach-at91/at91sam9260_devices.c > b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644 > --- a/arch/arm/mach-at91/at91sam9260_devices.c > +++ b/arch/arm/mach-at91/at91sam9260_devices.c > [...] > +static struct platform_device at91_adc_device = { > + .name = "at91adc", > + .id = -1, > + .dev = { > + .platform_data = &adc_data, > + }, > + .resource = adc_resources, > + .num_resources = ARRAY_SIZE(adc_resources), > +}; > + > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + at91_set_A_periph(AT91_PIN_PC0, 0); > + at91_set_A_periph(AT91_PIN_PC1, 0); > + at91_set_A_periph(AT91_PIN_PC2, 0); > + at91_set_A_periph(AT91_PIN_PC3, 0); > + > + adc_data = *data; > + platform_device_register(&at91_adc_device); > +} > + > + This assumes that all 4 channels are used every time. How about only using an AD channel on PC2 or all but the one on PC1? Regards, Alexander ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-10-20 6:28 ` Alexander Stein @ 2011-10-21 17:47 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-21 17:47 UTC (permalink / raw) To: Alexander Stein Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre Hi, On 20/10/2011 08:28, Alexander Stein wrote: > On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote: >> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c >> b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644 >> --- a/arch/arm/mach-at91/at91sam9260_devices.c >> +++ b/arch/arm/mach-at91/at91sam9260_devices.c >> [...] >> +static struct platform_device at91_adc_device = { >> + .name = "at91adc", >> + .id = -1, >> + .dev = { >> + .platform_data = &adc_data, >> + }, >> + .resource = adc_resources, >> + .num_resources = ARRAY_SIZE(adc_resources), >> +}; >> + >> +void __init at91_add_device_adc(struct at91_adc_data *data) >> +{ >> + if (!data) >> + return; >> + >> + at91_set_A_periph(AT91_PIN_PC0, 0); >> + at91_set_A_periph(AT91_PIN_PC1, 0); >> + at91_set_A_periph(AT91_PIN_PC2, 0); >> + at91_set_A_periph(AT91_PIN_PC3, 0); >> + >> + adc_data = *data; >> + platform_device_register(&at91_adc_device); >> +} >> + >> + > > This assumes that all 4 channels are used every time. How about only using an > AD channel on PC2 or all but the one on PC1? You're right. I've implemented Thomas suggestion to define the channels in use in a board specific manner. I need to test my changes and will submit a new version by monday. Thanks, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-10-21 17:47 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-10-21 17:47 UTC (permalink / raw) To: linux-arm-kernel Hi, On 20/10/2011 08:28, Alexander Stein wrote: > On Wednesday 19 October 2011 18:18:54 Maxime Ripard wrote: >> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c >> b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..4f5e0f9 100644 >> --- a/arch/arm/mach-at91/at91sam9260_devices.c >> +++ b/arch/arm/mach-at91/at91sam9260_devices.c >> [...] >> +static struct platform_device at91_adc_device = { >> + .name = "at91adc", >> + .id = -1, >> + .dev = { >> + .platform_data = &adc_data, >> + }, >> + .resource = adc_resources, >> + .num_resources = ARRAY_SIZE(adc_resources), >> +}; >> + >> +void __init at91_add_device_adc(struct at91_adc_data *data) >> +{ >> + if (!data) >> + return; >> + >> + at91_set_A_periph(AT91_PIN_PC0, 0); >> + at91_set_A_periph(AT91_PIN_PC1, 0); >> + at91_set_A_periph(AT91_PIN_PC2, 0); >> + at91_set_A_periph(AT91_PIN_PC3, 0); >> + >> + adc_data = *data; >> + platform_device_register(&at91_adc_device); >> +} >> + >> + > > This assumes that all 4 channels are used every time. How about only using an > AD channel on PC2 or all but the one on PC1? You're right. I've implemented Thomas suggestion to define the channels in use in a board specific manner. I need to test my changes and will submit a new version by monday. Thanks, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-10-19 16:18 ` Maxime Ripard @ 2011-10-20 7:14 ` Thomas Petazzoni -1 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-10-20 7:14 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre Le Wed, 19 Oct 2011 18:18:54 +0200, Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit : > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + at91_set_A_periph(AT91_PIN_PC0, 0); > + at91_set_A_periph(AT91_PIN_PC1, 0); > + at91_set_A_periph(AT91_PIN_PC2, 0); > + at91_set_A_periph(AT91_PIN_PC3, 0); As suggested, the decision of which ADC channels are used and therefore which pins should be muxed to function A is a board-specific decision and should not be enforced by SoC code. > +static struct at91_adc_data ek_adc_data =3D { > + .channels =3D 4, > + .adc_clock =3D 5000000, > + .startup_time =3D 10, > +}; And on the opposite, those informations are specific to a SoC, not to a board. So to avoid duplication in all board files, they should be moved to the corresponding SoC file. Maybe something like: #define AT91_ADC_MAX_CHANNELS 8 struct at91_adc_data { unsigned int adc_clock; u8 channels_used[AT91_ADC_MAX_CHANNELS]; u8 startup_time; u8 channels; }; at91_add_device_adc() { if (data->channels_used[0]) at91_set_A_periph(AT91_PIN_PC0, 0); if (data->channels_used[1]) at91_set_A_periph(AT91_PIN_PC1, 0); if (data->channels_used[2]) at91_set_A_periph(AT91_PIN_PC2, 0); if (data->channels_used[3]) at91_set_A_periph(AT91_PIN_PC3, 0); data->startup_time =3D ... data->channels =3D ... data->adc_clock =3D ... } and the board file would do: struct at91_adc_data ek_adc_data =3D { .channels_used =3D { 1, 0, 1, 1 },; }; Or maybe others have different suggestions ? Regards, Thomas --=20 Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-10-20 7:14 ` Thomas Petazzoni 0 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-10-20 7:14 UTC (permalink / raw) To: linux-arm-kernel Le Wed, 19 Oct 2011 18:18:54 +0200, Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit : > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + at91_set_A_periph(AT91_PIN_PC0, 0); > + at91_set_A_periph(AT91_PIN_PC1, 0); > + at91_set_A_periph(AT91_PIN_PC2, 0); > + at91_set_A_periph(AT91_PIN_PC3, 0); As suggested, the decision of which ADC channels are used and therefore which pins should be muxed to function A is a board-specific decision and should not be enforced by SoC code. > +static struct at91_adc_data ek_adc_data = { > + .channels = 4, > + .adc_clock = 5000000, > + .startup_time = 10, > +}; And on the opposite, those informations are specific to a SoC, not to a board. So to avoid duplication in all board files, they should be moved to the corresponding SoC file. Maybe something like: #define AT91_ADC_MAX_CHANNELS 8 struct at91_adc_data { unsigned int adc_clock; u8 channels_used[AT91_ADC_MAX_CHANNELS]; u8 startup_time; u8 channels; }; at91_add_device_adc() { if (data->channels_used[0]) at91_set_A_periph(AT91_PIN_PC0, 0); if (data->channels_used[1]) at91_set_A_periph(AT91_PIN_PC1, 0); if (data->channels_used[2]) at91_set_A_periph(AT91_PIN_PC2, 0); if (data->channels_used[3]) at91_set_A_periph(AT91_PIN_PC3, 0); data->startup_time = ... data->channels = ... data->adc_clock = ... } and the board file would do: struct at91_adc_data ek_adc_data = { .channels_used = { 1, 0, 1, 1 },; }; Or maybe others have different suggestions ? Regards, Thomas -- Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCHv2] AT91: Add a driver for the ADC 2011-10-19 16:18 ` Maxime Ripard @ 2011-11-03 10:11 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the branch master of Jonathan Cameron present at : https://github.com/jic23/linux-iio/, commit 85d8ff8 Improvements since v1: - Rebase on top of commit 85d8ff8 - Initialise scan_types - Added scale informations in the driver - Allow to use only a subset of adc channels available on the SoC - Various fix according to reviews Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCHv2] AT91: Add a driver for the ADC @ 2011-11-03 10:11 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the branch master of Jonathan Cameron present at : https://github.com/jic23/linux-iio/, commit 85d8ff8 Improvements since v1: - Rebase on top of commit 85d8ff8 - Initialise scan_types - Added scale informations in the driver - Allow to use only a subset of adc channels available on the SoC - Various fix according to reviews Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-03 10:11 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ 1 files changed, 22 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..b837da8 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +#define AT91_ADC_MAX_CHANNELS 16 + +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used array). + */ + u8 num_channels; + /* Channels in use on the board */ + u8 channels_used[AT91_ADC_MAX_CHANNELS]; + /* Number of channels in use */ + u8 num_channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-03 10:11 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ 1 files changed, 22 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..b837da8 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +#define AT91_ADC_MAX_CHANNELS 16 + +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used array). + */ + u8 num_channels; + /* Channels in use on the board */ + u8 channels_used[AT91_ADC_MAX_CHANNELS]; + /* Number of channels in use */ + u8 num_channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-03 11:27 ` Linus Walleij -1 siblings, 0 replies; 131+ messages in thread From: Linus Walleij @ 2011-11-03 11:27 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez 2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > =A0arch/arm/mach-at91/include/mach/board.h | =A0 22 +++++++++++++++++= +++++ > =A01 files changed, 22 insertions(+), 0 deletions(-) We're not supposed to have platform data dependent to stuff in staging under arch/arm or anyplace else in the main kernel tree. Please move this to drivers/staging/iio/adc/at91adc-board.h or so. As for calling the at91_add_device_adc() function (which I guess you want to do at some point) the pattern I followed for other drivers is to declare a dummy function in arch/arm/mach-* with __weak and let the staging driver override that. This way the staging driver can go away without any compilation trouble happening. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-03 11:27 ` Linus Walleij 0 siblings, 0 replies; 131+ messages in thread From: Linus Walleij @ 2011-11-03 11:27 UTC (permalink / raw) To: linux-arm-kernel 2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > ?arch/arm/mach-at91/include/mach/board.h | ? 22 ++++++++++++++++++++++ > ?1 files changed, 22 insertions(+), 0 deletions(-) We're not supposed to have platform data dependent to stuff in staging under arch/arm or anyplace else in the main kernel tree. Please move this to drivers/staging/iio/adc/at91adc-board.h or so. As for calling the at91_add_device_adc() function (which I guess you want to do at some point) the pattern I followed for other drivers is to declare a dummy function in arch/arm/mach-* with __weak and let the staging driver override that. This way the staging driver can go away without any compilation trouble happening. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 11:27 ` Linus Walleij @ 2011-11-03 16:27 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 16:27 UTC (permalink / raw) To: Linus Walleij; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez Hi Linus, On 03/11/2011 12:27, Linus Walleij wrote: > 2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>: > >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ >> 1 files changed, 22 insertions(+), 0 deletions(-) > > We're not supposed to have platform data dependent to stuff in > staging under arch/arm or anyplace else in the main kernel tree. > > Please move this to > drivers/staging/iio/adc/at91adc-board.h > or so. Won't moving this part to staging prevent from using this structure in board files ? If so, how will I be able to declare a new board that is using this ADC (or add the support for the ADC to a new one) ? > As for calling the at91_add_device_adc() function (which I guess > you want to do at some point) the pattern I followed for other > drivers is to declare a dummy function in arch/arm/mach-* > with __weak and let the staging driver override that. This way > the staging driver can go away without any compilation trouble > happening. I don't really see why my changes will break the compilation if the driver is no longer present in staging. At worst, the structure will be filled but used by no one, right ? Regards, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-03 16:27 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 16:27 UTC (permalink / raw) To: linux-arm-kernel Hi Linus, On 03/11/2011 12:27, Linus Walleij wrote: > 2011/11/3 Maxime Ripard <maxime.ripard@free-electrons.com>: > >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ >> 1 files changed, 22 insertions(+), 0 deletions(-) > > We're not supposed to have platform data dependent to stuff in > staging under arch/arm or anyplace else in the main kernel tree. > > Please move this to > drivers/staging/iio/adc/at91adc-board.h > or so. Won't moving this part to staging prevent from using this structure in board files ? If so, how will I be able to declare a new board that is using this ADC (or add the support for the ADC to a new one) ? > As for calling the at91_add_device_adc() function (which I guess > you want to do at some point) the pattern I followed for other > drivers is to declare a dummy function in arch/arm/mach-* > with __weak and let the staging driver override that. This way > the staging driver can go away without any compilation trouble > happening. I don't really see why my changes will break the compilation if the driver is no longer present in staging. At worst, the structure will be filled but used by no one, right ? Regards, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 16:27 ` Maxime Ripard (?) @ 2011-11-03 16:38 ` Linus Walleij 2011-11-03 18:05 ` Jean-Christophe PLAGNIOL-VILLARD -1 siblings, 1 reply; 131+ messages in thread From: Linus Walleij @ 2011-11-03 16:38 UTC (permalink / raw) To: linux-arm-kernel On Thu, Nov 3, 2011 at 5:27 PM, Maxime Ripard <maxime.ripard@free-electrons.com> wrote: > [Me] >> We're not supposed to have platform data dependent to stuff in >> staging under arch/arm or anyplace else in the main kernel tree. >> >> Please move this to >> drivers/staging/iio/adc/at91adc-board.h >> or so. > > Won't moving this part to staging prevent from using this structure in > board files ? If so, how will I be able to declare a new board that is > using this ADC (or add the support for the ADC to a new one) ? Put this into a separate board file living under staging/iio/adc Compare: drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c >> As for calling the at91_add_device_adc() function (which I guess >> you want to do at some point) the pattern I followed for other >> drivers is to declare a dummy function in arch/arm/mach-* >> with __weak and let the staging driver override that. This way >> the staging driver can go away without any compilation trouble >> happening. > > I don't really see why my changes will break the compilation if the > driver is no longer present in staging. At worst, the structure will be > filled but used by no one, right ? You're right. But still, we cannot add that header file for a driver that is in the staging tree. Header files go into the staging dir too. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 16:38 ` Linus Walleij @ 2011-11-03 18:05 ` Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 0 replies; 131+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-03 18:05 UTC (permalink / raw) To: Linus Walleij Cc: Maxime Ripard, linux-iio, Linus Walleij, Patrice Vilchez, linux-arm-kernel, Nicolas Ferre On 17:38 Thu 03 Nov , Linus Walleij wrote: > On Thu, Nov 3, 2011 at 5:27 PM, Maxime Ripard > <maxime.ripard@free-electrons.com> wrote: > > [Me] > >> We're not supposed to have platform data dependent to stuff in > >> staging under arch/arm or anyplace else in the main kernel tree. > >> > >> Please move this to > >> drivers/staging/iio/adc/at91adc-board.h > >> or so. > > > > Won't moving this part to staging prevent from using this structure in > > board files ? If so, how will I be able to declare a new board that is > > using this ADC (or add the support for the ADC to a new one) ? > > Put this into a separate board file living under staging/iio/adc > > Compare: > drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c > > >> As for calling the at91_add_device_adc() function (which I guess > >> you want to do at some point) the pattern I followed for other > >> drivers is to declare a dummy function in arch/arm/mach-* > >> with __weak and let the staging driver override that. This way > >> the staging driver can go away without any compilation trouble > >> happening. > > > > I don't really see why my changes will break the compilation if the > > driver is no longer present in staging. At worst, the structure will be > > filled but used by no one, right ? > > You're right. > > But still, we cannot add that header file for a driver that > is in the staging tree. Header files go into the staging dir too. agreed Best Regards, J. ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-03 18:05 ` Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 0 replies; 131+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-03 18:05 UTC (permalink / raw) To: linux-arm-kernel On 17:38 Thu 03 Nov , Linus Walleij wrote: > On Thu, Nov 3, 2011 at 5:27 PM, Maxime Ripard > <maxime.ripard@free-electrons.com> wrote: > > [Me] > >> We're not supposed to have platform data dependent to stuff in > >> staging under arch/arm or anyplace else in the main kernel tree. > >> > >> Please move this to > >> drivers/staging/iio/adc/at91adc-board.h > >> or so. > > > > Won't moving this part to staging prevent from using this structure in > > board files ? If so, how will I be able to declare a new board that is > > using this ADC (or add the support for the ADC to a new one) ? > > Put this into a separate board file living under staging/iio/adc > > Compare: > drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c > > >> As for calling the at91_add_device_adc() function (which I guess > >> you want to do at some point) the pattern I followed for other > >> drivers is to declare a dummy function in arch/arm/mach-* > >> with __weak and let the staging driver override that. This way > >> the staging driver can go away without any compilation trouble > >> happening. > > > > I don't really see why my changes will break the compilation if the > > driver is no longer present in staging. At worst, the structure will be > > filled but used by no one, right ? > > You're right. > > But still, we cannot add that header file for a driver that > is in the staging tree. Header files go into the staging dir too. agreed Best Regards, J. ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-04 10:27 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ > 1 files changed, 22 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..b837da8 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +#define AT91_ADC_MAX_CHANNELS 16 > + > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). > + */ > + u8 num_channels; > + /* Channels in use on the board */ > + u8 channels_used[AT91_ADC_MAX_CHANNELS]; > + /* Number of channels in use */ > + u8 num_channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-04 10:27 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw) To: linux-arm-kernel On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ > 1 files changed, 22 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..b837da8 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +#define AT91_ADC_MAX_CHANNELS 16 > + > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). > + */ > + u8 num_channels; > + /* Channels in use on the board */ > + u8 channels_used[AT91_ADC_MAX_CHANNELS]; > + /* Number of channels in use */ > + u8 num_channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-04 10:27 ` Jonathan Cameron @ 2011-11-04 10:36 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:36 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez On 11/04/2011 10:27 AM, Jonathan Cameron wrote: > On 11/03/2011 10:11 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ >> 1 files changed, 22 insertions(+), 0 deletions(-) >> >> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h >> index ed544a0..b837da8 100644 >> --- a/arch/arm/mach-at91/include/mach/board.h >> +++ b/arch/arm/mach-at91/include/mach/board.h >> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); >> /* FIXME: this needs a better location, but gets stuff building again */ >> extern int at91_suspend_entering_slow_clock(void); >> >> +/* ADC */ >> +#define AT91_ADC_MAX_CHANNELS 16 >> + >> +struct at91_adc_data { >> + /* ADC Clock as specified by the datasheet, in Hz. */ >> + unsigned int adc_clock; >> + /* >> + * Global number of channels available (to specify which channels are >> + * indeed used on the board, see the channels_used array). >> + */ >> + u8 num_channels; >> + /* Channels in use on the board */ >> + u8 channels_used[AT91_ADC_MAX_CHANNELS]; >> + /* Number of channels in use */ >> + u8 num_channels_used; >> + /* Startup time of the ADC, in microseconds. */ >> + u8 startup_time; >> + /* Reference voltage for the ADC in millivolts */ >> + unsigned short vref; >> +}; >> +extern void __init at91_add_device_adc(struct at91_adc_data *data); >> + >> #endif oops. version with actual comments should also have gone out. ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-04 10:36 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:36 UTC (permalink / raw) To: linux-arm-kernel On 11/04/2011 10:27 AM, Jonathan Cameron wrote: > On 11/03/2011 10:11 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ >> 1 files changed, 22 insertions(+), 0 deletions(-) >> >> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h >> index ed544a0..b837da8 100644 >> --- a/arch/arm/mach-at91/include/mach/board.h >> +++ b/arch/arm/mach-at91/include/mach/board.h >> @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); >> /* FIXME: this needs a better location, but gets stuff building again */ >> extern int at91_suspend_entering_slow_clock(void); >> >> +/* ADC */ >> +#define AT91_ADC_MAX_CHANNELS 16 >> + >> +struct at91_adc_data { >> + /* ADC Clock as specified by the datasheet, in Hz. */ >> + unsigned int adc_clock; >> + /* >> + * Global number of channels available (to specify which channels are >> + * indeed used on the board, see the channels_used array). >> + */ >> + u8 num_channels; >> + /* Channels in use on the board */ >> + u8 channels_used[AT91_ADC_MAX_CHANNELS]; >> + /* Number of channels in use */ >> + u8 num_channels_used; >> + /* Startup time of the ADC, in microseconds. */ >> + u8 startup_time; >> + /* Reference voltage for the ADC in millivolts */ >> + unsigned short vref; >> +}; >> +extern void __init at91_add_device_adc(struct at91_adc_data *data); >> + >> #endif oops. version with actual comments should also have gone out. ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-04 10:34 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:34 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ > 1 files changed, 22 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..b837da8 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +#define AT91_ADC_MAX_CHANNELS 16 > + > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). > + */ > + u8 num_channels; > + /* Channels in use on the board */ put it in a unsigned long and use the bitmap functions. > + u8 channels_used[AT91_ADC_MAX_CHANNELS]; > + /* Number of channels in use */ just use bitmap_weight with the long above and length set to num_channels. > + u8 num_channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-04 10:34 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:34 UTC (permalink / raw) To: linux-arm-kernel On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > arch/arm/mach-at91/include/mach/board.h | 22 ++++++++++++++++++++++ > 1 files changed, 22 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..b837da8 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,26 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +#define AT91_ADC_MAX_CHANNELS 16 > + > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). > + */ > + u8 num_channels; > + /* Channels in use on the board */ put it in a unsigned long and use the bitmap functions. > + u8 channels_used[AT91_ADC_MAX_CHANNELS]; > + /* Number of channels in use */ just use bitmap_weight with the long above and length set to num_channels. > + u8 num_channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-04 10:34 ` Jonathan Cameron (?) @ 2011-11-04 15:22 ` Maxime Ripard 2011-11-04 16:28 ` Jonathan Cameron -1 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-04 15:22 UTC (permalink / raw) To: linux-arm-kernel Hi Jonathan, On 04/11/2011 11:34, Jonathan Cameron wrote: > On 11/03/2011 10:11 AM, Maxime Ripard wrote: >> +/* ADC */ >> +#define AT91_ADC_MAX_CHANNELS 16 >> + >> +struct at91_adc_data { >> + /* ADC Clock as specified by the datasheet, in Hz. */ >> + unsigned int adc_clock; >> + /* >> + * Global number of channels available (to specify which channels are >> + * indeed used on the board, see the channels_used array). >> + */ >> + u8 num_channels; >> + /* Channels in use on the board */ > put it in a unsigned long and use the bitmap functions. >> + u8 channels_used[AT91_ADC_MAX_CHANNELS]; >> + /* Number of channels in use */ > just use bitmap_weight with the long above and > length set to num_channels. >> + u8 num_channels_used; I didn't know bitmap functions before you mentioned it, but if I read it correctly, while I agree with your point, I will lose the ability to define a combination of enabled and disabled channel in a board specific manner. In the third patch, I defined channels_used as an array of one. But let's say that instead I want all channels except the second one. With what's in the driver for now, I initialise it to {1, 0, 1, 1}. I don't think it's possible to do so with bitmap functions, or am I missing something ? Or put the calls to bitmap_set in the board_init function ? -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-04 15:22 ` Maxime Ripard @ 2011-11-04 16:28 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 16:28 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel On 11/04/2011 03:22 PM, Maxime Ripard wrote: > Hi Jonathan, > > On 04/11/2011 11:34, Jonathan Cameron wrote: >> On 11/03/2011 10:11 AM, Maxime Ripard wrote: >>> +/* ADC */ >>> +#define AT91_ADC_MAX_CHANNELS 16 >>> + >>> +struct at91_adc_data { >>> + /* ADC Clock as specified by the datasheet, in Hz. */ >>> + unsigned int adc_clock; >>> + /* >>> + * Global number of channels available (to specify which channels are >>> + * indeed used on the board, see the channels_used array). >>> + */ >>> + u8 num_channels; >>> + /* Channels in use on the board */ >> put it in a unsigned long and use the bitmap functions. >>> + u8 channels_used[AT91_ADC_MAX_CHANNELS]; >>> + /* Number of channels in use */ >> just use bitmap_weight with the long above and >> length set to num_channels. >>> + u8 num_channels_used; > > I didn't know bitmap functions before you mentioned it, but if I read it > correctly, while I agree with your point, I will lose the ability to > define a combination of enabled and disabled channel in a board specific > manner. > > In the third patch, I defined channels_used as an array of one. But > let's say that instead I want all channels except the second one. With > what's in the driver for now, I initialise it to {1, 0, 1, 1}. I don't > think it's possible to do so with bitmap functions, or am I missing > something ? unsigned long bitmask = BIT(0) | BIT(1) | BIT(3); Bitmaps are just arrays of unsigned longs. If you 'know' you have less than 32 elements, you can just use a single unsigned long and this assignment gets easier. Most of the stuff you want comes from bitops.h anyway (maybe all come to think of it?) > > Or put the calls to bitmap_set in the board_init function ? Just edit the underlying unsigned long directly. The fact that the bitops.h stuff is supposed to work on a bitmap as well means that will always work. Jonathan ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-04 16:28 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 16:28 UTC (permalink / raw) To: linux-arm-kernel On 11/04/2011 03:22 PM, Maxime Ripard wrote: > Hi Jonathan, > > On 04/11/2011 11:34, Jonathan Cameron wrote: >> On 11/03/2011 10:11 AM, Maxime Ripard wrote: >>> +/* ADC */ >>> +#define AT91_ADC_MAX_CHANNELS 16 >>> + >>> +struct at91_adc_data { >>> + /* ADC Clock as specified by the datasheet, in Hz. */ >>> + unsigned int adc_clock; >>> + /* >>> + * Global number of channels available (to specify which channels are >>> + * indeed used on the board, see the channels_used array). >>> + */ >>> + u8 num_channels; >>> + /* Channels in use on the board */ >> put it in a unsigned long and use the bitmap functions. >>> + u8 channels_used[AT91_ADC_MAX_CHANNELS]; >>> + /* Number of channels in use */ >> just use bitmap_weight with the long above and >> length set to num_channels. >>> + u8 num_channels_used; > > I didn't know bitmap functions before you mentioned it, but if I read it > correctly, while I agree with your point, I will lose the ability to > define a combination of enabled and disabled channel in a board specific > manner. > > In the third patch, I defined channels_used as an array of one. But > let's say that instead I want all channels except the second one. With > what's in the driver for now, I initialise it to {1, 0, 1, 1}. I don't > think it's possible to do so with bitmap functions, or am I missing > something ? unsigned long bitmask = BIT(0) | BIT(1) | BIT(3); Bitmaps are just arrays of unsigned longs. If you 'know' you have less than 32 elements, you can just use a single unsigned long and this assignment gets easier. Most of the stuff you want comes from bitops.h anyway (maybe all come to think of it?) > > Or put the calls to bitmap_set in the board_init function ? Just edit the underlying unsigned long directly. The fact that the bitops.h stuff is supposed to work on a bitmap as well means that will always work. Jonathan ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-03 10:11 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/staging/iio/adc/Kconfig | 6 + drivers/staging/iio/adc/Makefile | 1 + drivers/staging/iio/adc/at91adc.c | 310 +++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/adc/at91adc.c diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index d9decea..c384894 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -169,6 +169,12 @@ config AD7280 To compile this driver as a module, choose M here: the module will be called ad7280a +config AT91ADC + tristate "Atmel AT91 ADC" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel AT91 ADC. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index ceee7f3..d250d06 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_ADT7310) += adt7310.o obj-$(CONFIG_ADT7410) += adt7410.o obj-$(CONFIG_AD7280) += ad7280a.o +obj-$(CONFIG_AT91ADC) += at91adc.o diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c new file mode 100644 index 0000000..acf656d --- /dev/null +++ b/drivers/staging/iio/adc/at91adc.c @@ -0,0 +1,310 @@ +/* + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 + * evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include "../iio.h" + +#include <mach/at91_adc.h> +#include <mach/board.h> + +#define at91adc_reg_read(base, reg) readl_relaxed((base) + (reg)) +#define at91adc_reg_write(base, reg, val) writel_relaxed((val), (base) + (reg)) + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + struct iio_chan_spec *channels; + int nb_chan; + int irq; + wait_queue_head_t wq_data_avail; + u16 lcdr; + void __iomem *reg_base; + unsigned int vref_mv; +}; + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct at91adc_state *st = private; + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < st->nb_chan; chan++) { + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->lcdr = at91adc_reg_read(st->reg_base, + AT91_ADC_CHR(chan)); + } + } + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct at91adc_state *st, + struct at91_adc_data *pdata) +{ + int i, idx = 0; + st->channels = kcalloc(pdata->num_channels_used, + sizeof(struct iio_chan_spec), GFP_KERNEL); + if (st->channels == NULL) + return -ENOMEM; + + for (i = 0; i < st->nb_chan; i++) { + if(pdata->channels_used[i]) { + struct iio_chan_spec *chan = st->channels + idx; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = 10; + chan->scan_type.storagebits = 32; + chan->scan_type.shift = 0; + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; + ++idx; + } + } + + return st->nb_chan; +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + unsigned int scale_uv; + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); + + wait_event_interruptible_timeout(st->wq_data_avail, st->done, + msecs_to_jiffies(10 * 1000)); + *val = st->lcdr; + + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->lcdr = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_allocate_device(sizeof(struct at91adc_state)); + if (idev == NULL) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = dev_name(&pdev->dev); + idev->modes = INDIO_DIRECT_MODE; + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st->reg_base, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + st->nb_chan = pdata->num_channels; + ret = at91adc_channel_init(st, pdata); + if (ret < 0) { + goto error_free_clk; + } + + idev->channels = st->channels; + idev->num_channels = pdata->num_channels_used; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + st->vref_mv = pdata->vref; + + ret = iio_device_register(idev); + if (ret < 0) { + goto error_free_clk; + } + + return 0; + +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_free_device(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + iio_device_unregister(idev); + free_irq(st->irq, st); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_free_device(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +static int __init at91adc_init(void) +{ + return platform_driver_register(&at91adc_driver); +} + +static void __exit at91adc_exit(void) +{ + platform_driver_unregister(&at91adc_driver); +} + +module_init(at91adc_init); +module_exit(at91adc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-03 10:11 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/staging/iio/adc/Kconfig | 6 + drivers/staging/iio/adc/Makefile | 1 + drivers/staging/iio/adc/at91adc.c | 310 +++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/iio/adc/at91adc.c diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index d9decea..c384894 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -169,6 +169,12 @@ config AD7280 To compile this driver as a module, choose M here: the module will be called ad7280a +config AT91ADC + tristate "Atmel AT91 ADC" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel AT91 ADC. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index ceee7f3..d250d06 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_ADT7310) += adt7310.o obj-$(CONFIG_ADT7410) += adt7410.o obj-$(CONFIG_AD7280) += ad7280a.o +obj-$(CONFIG_AT91ADC) += at91adc.o diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c new file mode 100644 index 0000000..acf656d --- /dev/null +++ b/drivers/staging/iio/adc/at91adc.c @@ -0,0 +1,310 @@ +/* + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 + * evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include "../iio.h" + +#include <mach/at91_adc.h> +#include <mach/board.h> + +#define at91adc_reg_read(base, reg) readl_relaxed((base) + (reg)) +#define at91adc_reg_write(base, reg, val) writel_relaxed((val), (base) + (reg)) + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + struct iio_chan_spec *channels; + int nb_chan; + int irq; + wait_queue_head_t wq_data_avail; + u16 lcdr; + void __iomem *reg_base; + unsigned int vref_mv; +}; + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct at91adc_state *st = private; + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < st->nb_chan; chan++) { + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->lcdr = at91adc_reg_read(st->reg_base, + AT91_ADC_CHR(chan)); + } + } + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct at91adc_state *st, + struct at91_adc_data *pdata) +{ + int i, idx = 0; + st->channels = kcalloc(pdata->num_channels_used, + sizeof(struct iio_chan_spec), GFP_KERNEL); + if (st->channels == NULL) + return -ENOMEM; + + for (i = 0; i < st->nb_chan; i++) { + if(pdata->channels_used[i]) { + struct iio_chan_spec *chan = st->channels + idx; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = 10; + chan->scan_type.storagebits = 32; + chan->scan_type.shift = 0; + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; + ++idx; + } + } + + return st->nb_chan; +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + unsigned int scale_uv; + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); + + wait_event_interruptible_timeout(st->wq_data_avail, st->done, + msecs_to_jiffies(10 * 1000)); + *val = st->lcdr; + + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->lcdr = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_allocate_device(sizeof(struct at91adc_state)); + if (idev == NULL) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = dev_name(&pdev->dev); + idev->modes = INDIO_DIRECT_MODE; + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st->reg_base, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + st->nb_chan = pdata->num_channels; + ret = at91adc_channel_init(st, pdata); + if (ret < 0) { + goto error_free_clk; + } + + idev->channels = st->channels; + idev->num_channels = pdata->num_channels_used; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + st->vref_mv = pdata->vref; + + ret = iio_device_register(idev); + if (ret < 0) { + goto error_free_clk; + } + + return 0; + +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_free_device(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + iio_device_unregister(idev); + free_irq(st->irq, st); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_free_device(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +static int __init at91adc_init(void) +{ + return platform_driver_register(&at91adc_driver); +} + +static void __exit at91adc_exit(void) +{ + platform_driver_unregister(&at91adc_driver); +} + +module_init(at91adc_init); +module_exit(at91adc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-04 10:27 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > drivers/staging/iio/adc/Kconfig | 6 + > drivers/staging/iio/adc/Makefile | 1 + > drivers/staging/iio/adc/at91adc.c | 310 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 317 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/adc/at91adc.c > > diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig > index d9decea..c384894 100644 > --- a/drivers/staging/iio/adc/Kconfig > +++ b/drivers/staging/iio/adc/Kconfig > @@ -169,6 +169,12 @@ config AD7280 > To compile this driver as a module, choose M here: the > module will be called ad7280a > > +config AT91ADC > + tristate "Atmel AT91 ADC" > + depends on SYSFS && ARCH_AT91 > + help > + Say yes here to build support for Atmel AT91 ADC. > + > config MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile > index ceee7f3..d250d06 100644 > --- a/drivers/staging/iio/adc/Makefile > +++ b/drivers/staging/iio/adc/Makefile > @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o > obj-$(CONFIG_ADT7310) += adt7310.o > obj-$(CONFIG_ADT7410) += adt7410.o > obj-$(CONFIG_AD7280) += ad7280a.o > +obj-$(CONFIG_AT91ADC) += at91adc.o > diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c > new file mode 100644 > index 0000000..acf656d > --- /dev/null > +++ b/drivers/staging/iio/adc/at91adc.c > @@ -0,0 +1,310 @@ > +/* > + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 > + * evaluation boards. I'd add something here to make it clear that it isn't being used for touchscreen input in this driver. > + * > + * Copyright 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + */ > + > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > + > +#include "../iio.h" > + > +#include <mach/at91_adc.h> > +#include <mach/board.h> > + I'd marginally prefer to see these as inlines rather than macros. Far from critical though! > +#define at91adc_reg_read(base, reg) readl_relaxed((base) + (reg)) > +#define at91adc_reg_write(base, reg, val) writel_relaxed((val), (base) + (reg)) > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; why hold a local pointer? Just put these directly into iio_dev->channels. > + struct iio_chan_spec *channels; likewise, we already have iio_dev->num_channels. > + int nb_chan; > + int irq; > + wait_queue_head_t wq_data_avail; cryptic element. Nice to have everything documented, but this one is most important as it isn't self explanatory. > + u16 lcdr; > + void __iomem *reg_base; > + unsigned int vref_mv; > +}; > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct at91adc_state *st = private; > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + extra unneeded brackets for the for loop. > + for (chan = 0; chan < st->nb_chan; chan++) { > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->lcdr = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); > + } > + } > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct at91adc_state *st, > + struct at91_adc_data *pdata) > +{ > + int i, idx = 0; > + st->channels = kcalloc(pdata->num_channels_used, > + sizeof(struct iio_chan_spec), GFP_KERNEL); > + if (st->channels == NULL) > + return -ENOMEM; > + Again, extra brackets. Also, some wrong spacing for the if. can you run checkpatch.pl over this again and make sure you have fixed everything. > + for (i = 0; i < st->nb_chan; i++) { as stated for the previous patch, this should be a bitmap rather than array of u8's. You can then use for_each_bit_set to clean this code up nicely ;) > + if(pdata->channels_used[i]) { > + struct iio_chan_spec *chan = st->channels + idx; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; > + chan->scan_type.sign = 's'; > + chan->scan_type.realbits = 10; > + chan->scan_type.storagebits = 32; That is pretty inefficient storage! If we do implement buffering on this, given mass reads don't look to be quicker, we'll just munge it down to 16 bits. > + chan->scan_type.shift = 0; don't bother initialising this value as it is zero (and that's an obvious default). > + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; > + ++idx; > + } > + } > + > + return st->nb_chan; > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + unsigned int scale_uv; > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible_timeout(st->wq_data_avail, st->done, > + msecs_to_jiffies(10 * 1000)); > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; > + *val = scale_uv / 1000; > + *val2 = (scale_uv % 1000) * 1000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(struct at91adc_state)); sizeof(*st) is a little neater? > + if (idev == NULL) { I'm not sure this message adds anything or is even necessarily correct as that function doesn't just allocate memory! > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = dev_name(&pdev->dev); > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; > + > + if (!pdata->startup_time) { > + dev_err(&pdev->dev, "No startup time available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; > + at91adc_reg_write(st->reg_base, AT91_ADC_MR, > + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | > + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); > + > + /* Setup the ADC channels available on the board */ > + st->nb_chan = pdata->num_channels; > + ret = at91adc_channel_init(st, pdata); > + if (ret < 0) { no brackets please. Again, checkpatch would probably have shouted about this. > + goto error_free_clk; > + } > + This is what I meant when I asked why you have two copies of the pointer and count? > + idev->channels = st->channels; > + idev->num_channels = pdata->num_channels_used; > + > + init_waitqueue_head(&st->wq_data_avail); > + mutex_init(&st->lock); > + > + st->vref_mv = pdata->vref; > + > + ret = iio_device_register(idev); again incorrect brackets for the code style. > + if (ret < 0) { unwind the channel init? > + goto error_free_clk; > + } > + > + return 0; > + > +error_free_clk: > + clk_disable(st->clk); > + clk_put(st->clk); > +error_free_irq: > + free_irq(st->irq, st); > +error_unmap_reg: > + iounmap(st->reg_base); > +error_release_mem: > + release_mem_region(res->start, resource_size(res)); > +error_free_device: > + iio_free_device(idev); > +error_ret: > + return ret; > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + iio_device_unregister(idev); Free channels array? > + free_irq(st->irq, st); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_free_device(idev); > + > + return 0; > +} > + > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, > +}; > + > +static int __init at91adc_init(void) > +{ > + return platform_driver_register(&at91adc_driver); > +} > + > +static void __exit at91adc_exit(void) > +{ > + platform_driver_unregister(&at91adc_driver); > +} > + Some neat boiler plate removal stuff was just merged for platform drivers. See module_platform_driver in include/linux/platform.h Definitely want to use that! I'm hoping the spi and i2c versions turn up soon as it will save us a lot of silly cut and paste code. > +module_init(at91adc_init); > +module_exit(at91adc_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-04 10:27 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:27 UTC (permalink / raw) To: linux-arm-kernel On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > drivers/staging/iio/adc/Kconfig | 6 + > drivers/staging/iio/adc/Makefile | 1 + > drivers/staging/iio/adc/at91adc.c | 310 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 317 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/adc/at91adc.c > > diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig > index d9decea..c384894 100644 > --- a/drivers/staging/iio/adc/Kconfig > +++ b/drivers/staging/iio/adc/Kconfig > @@ -169,6 +169,12 @@ config AD7280 > To compile this driver as a module, choose M here: the > module will be called ad7280a > > +config AT91ADC > + tristate "Atmel AT91 ADC" > + depends on SYSFS && ARCH_AT91 > + help > + Say yes here to build support for Atmel AT91 ADC. > + > config MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile > index ceee7f3..d250d06 100644 > --- a/drivers/staging/iio/adc/Makefile > +++ b/drivers/staging/iio/adc/Makefile > @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o > obj-$(CONFIG_ADT7310) += adt7310.o > obj-$(CONFIG_ADT7410) += adt7410.o > obj-$(CONFIG_AD7280) += ad7280a.o > +obj-$(CONFIG_AT91ADC) += at91adc.o > diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c > new file mode 100644 > index 0000000..acf656d > --- /dev/null > +++ b/drivers/staging/iio/adc/at91adc.c > @@ -0,0 +1,310 @@ > +/* > + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 > + * evaluation boards. I'd add something here to make it clear that it isn't being used for touchscreen input in this driver. > + * > + * Copyright 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + */ > + > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > + > +#include "../iio.h" > + > +#include <mach/at91_adc.h> > +#include <mach/board.h> > + I'd marginally prefer to see these as inlines rather than macros. Far from critical though! > +#define at91adc_reg_read(base, reg) readl_relaxed((base) + (reg)) > +#define at91adc_reg_write(base, reg, val) writel_relaxed((val), (base) + (reg)) > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; why hold a local pointer? Just put these directly into iio_dev->channels. > + struct iio_chan_spec *channels; likewise, we already have iio_dev->num_channels. > + int nb_chan; > + int irq; > + wait_queue_head_t wq_data_avail; cryptic element. Nice to have everything documented, but this one is most important as it isn't self explanatory. > + u16 lcdr; > + void __iomem *reg_base; > + unsigned int vref_mv; > +}; > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct at91adc_state *st = private; > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + extra unneeded brackets for the for loop. > + for (chan = 0; chan < st->nb_chan; chan++) { > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->lcdr = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); > + } > + } > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct at91adc_state *st, > + struct at91_adc_data *pdata) > +{ > + int i, idx = 0; > + st->channels = kcalloc(pdata->num_channels_used, > + sizeof(struct iio_chan_spec), GFP_KERNEL); > + if (st->channels == NULL) > + return -ENOMEM; > + Again, extra brackets. Also, some wrong spacing for the if. can you run checkpatch.pl over this again and make sure you have fixed everything. > + for (i = 0; i < st->nb_chan; i++) { as stated for the previous patch, this should be a bitmap rather than array of u8's. You can then use for_each_bit_set to clean this code up nicely ;) > + if(pdata->channels_used[i]) { > + struct iio_chan_spec *chan = st->channels + idx; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = i; > + chan->scan_type.sign = 's'; > + chan->scan_type.realbits = 10; > + chan->scan_type.storagebits = 32; That is pretty inefficient storage! If we do implement buffering on this, given mass reads don't look to be quicker, we'll just munge it down to 16 bits. > + chan->scan_type.shift = 0; don't bother initialising this value as it is zero (and that's an obvious default). > + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; > + ++idx; > + } > + } > + > + return st->nb_chan; > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + unsigned int scale_uv; > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible_timeout(st->wq_data_avail, st->done, > + msecs_to_jiffies(10 * 1000)); > + *val = st->lcdr; > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->lcdr = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; > + *val = scale_uv / 1000; > + *val2 = (scale_uv % 1000) * 1000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_allocate_device(sizeof(struct at91adc_state)); sizeof(*st) is a little neater? > + if (idev == NULL) { I'm not sure this message adds anything or is even necessarily correct as that function doesn't just allocate memory! > + dev_err(&pdev->dev, "Failed to allocate memory.\n"); > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = dev_name(&pdev->dev); > + idev->modes = INDIO_DIRECT_MODE; > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; > + > + if (!pdata->startup_time) { > + dev_err(&pdev->dev, "No startup time available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; > + at91adc_reg_write(st->reg_base, AT91_ADC_MR, > + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | > + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); > + > + /* Setup the ADC channels available on the board */ > + st->nb_chan = pdata->num_channels; > + ret = at91adc_channel_init(st, pdata); > + if (ret < 0) { no brackets please. Again, checkpatch would probably have shouted about this. > + goto error_free_clk; > + } > + This is what I meant when I asked why you have two copies of the pointer and count? > + idev->channels = st->channels; > + idev->num_channels = pdata->num_channels_used; > + > + init_waitqueue_head(&st->wq_data_avail); > + mutex_init(&st->lock); > + > + st->vref_mv = pdata->vref; > + > + ret = iio_device_register(idev); again incorrect brackets for the code style. > + if (ret < 0) { unwind the channel init? > + goto error_free_clk; > + } > + > + return 0; > + > +error_free_clk: > + clk_disable(st->clk); > + clk_put(st->clk); > +error_free_irq: > + free_irq(st->irq, st); > +error_unmap_reg: > + iounmap(st->reg_base); > +error_release_mem: > + release_mem_region(res->start, resource_size(res)); > +error_free_device: > + iio_free_device(idev); > +error_ret: > + return ret; > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + iio_device_unregister(idev); Free channels array? > + free_irq(st->irq, st); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_free_device(idev); > + > + return 0; > +} > + > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, > +}; > + > +static int __init at91adc_init(void) > +{ > + return platform_driver_register(&at91adc_driver); > +} > + > +static void __exit at91adc_exit(void) > +{ > + platform_driver_unregister(&at91adc_driver); > +} > + Some neat boiler plate removal stuff was just merged for platform drivers. See module_platform_driver in include/linux/platform.h Definitely want to use that! I'm hoping the spi and i2c versions turn up soon as it will save us a lot of silly cut and paste code. > +module_init(at91adc_init); > +module_exit(at91adc_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-04 10:27 ` Jonathan Cameron (?) @ 2011-11-04 16:29 ` Maxime Ripard 2011-11-04 16:40 ` Jonathan Cameron -1 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-04 16:29 UTC (permalink / raw) To: linux-arm-kernel On 04/11/2011 11:27, Jonathan Cameron wrote: > On 11/03/2011 10:11 AM, Maxime Ripard wrote: >> diff --git a/drivers/staging/iio/adc/at91adc.c b/drivers/staging/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..acf656d >> --- /dev/null >> +++ b/drivers/staging/iio/adc/at91adc.c >> @@ -0,0 +1,310 @@ >> +/* >> + * Driver for the TouchScreen ADC Controller present in the Atmel AT91 >> + * evaluation boards. > I'd add something here to make it clear that it isn't being used for > touchscreen input in this driver. Indeed, I forgot to change it here as well. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include "../iio.h" >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + > I'd marginally prefer to see these as inlines rather than macros. Far > from critical though! Ok. >> +#define at91adc_reg_read(base, reg) readl_relaxed((base) + (reg)) >> +#define at91adc_reg_write(base, reg, val) writel_relaxed((val), (base) + (reg)) >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; > why hold a local pointer? Just put these > directly into iio_dev->channels. >> + struct iio_chan_spec *channels; > likewise, we already have iio_dev->num_channels. >> + int nb_chan; Indeed >> + int irq; >> + wait_queue_head_t wq_data_avail; > cryptic element. Nice to have everything documented, but > this one is most important as it isn't self explanatory. >> + u16 lcdr; >> + void __iomem *reg_base; >> + unsigned int vref_mv; >> +}; >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct at91adc_state *st = private; >> + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + > extra unneeded brackets for the for loop. I'm confused here. While it makes sense, I find it more readable around large blocks of code and checkpatch doesn't complain on that one. But coding style is a huge troll, so will change. >> + for (chan = 0; chan < st->nb_chan; chan++) { >> + if (status & AT91_ADC_EOC(chan)) { >> + st->done = true; >> + st->lcdr = at91adc_reg_read(st->reg_base, >> + AT91_ADC_CHR(chan)); >> + } >> + } >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct at91adc_state *st, >> + struct at91_adc_data *pdata) >> +{ >> + int i, idx = 0; >> + st->channels = kcalloc(pdata->num_channels_used, >> + sizeof(struct iio_chan_spec), GFP_KERNEL); >> + if (st->channels == NULL) >> + return -ENOMEM; >> + > Again, extra brackets. Also, some wrong spacing for the if. > can you run checkpatch.pl over this again and make sure you > have fixed everything. >> + for (i = 0; i < st->nb_chan; i++) { > as stated for the previous patch, this should be a bitmap rather > than array of u8's. You can then use for_each_bit_set > to clean this code up nicely ;) >> + if(pdata->channels_used[i]) { >> + struct iio_chan_spec *chan = st->channels + idx; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = i; >> + chan->scan_type.sign = 's'; >> + chan->scan_type.realbits = 10; >> + chan->scan_type.storagebits = 32; > That is pretty inefficient storage! If we do implement buffering > on this, given mass reads don't look to be quicker, we'll just > munge it down to 16 bits. Well, I thought that storage bits was here to represent how the hardware stored the values. Since these values are stored on 10 bits alone in a 32 bits register, I thought that these were the right values, but we can definitely lower the storagebits to 16 here. > >> + chan->scan_type.shift = 0; > don't bother initialising this value as it is zero (and that's an > obvious default). >> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >> + ++idx; >> + } >> + } >> + >> + return st->nb_chan; >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int scale_uv; >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); >> + >> + wait_event_interruptible_timeout(st->wq_data_avail, st->done, >> + msecs_to_jiffies(10 * 1000)); >> + *val = st->lcdr; >> + >> + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->lcdr = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + case IIO_CHAN_INFO_SCALE: >> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >> + *val = scale_uv / 1000; >> + *val2 = (scale_uv % 1000) * 1000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_allocate_device(sizeof(struct at91adc_state)); > sizeof(*st) is a little neater? Well, I find sizeof(struct at91adc_state) clearer, but ok. >> + if (idev == NULL) { > I'm not sure this message adds anything or is even necessarily > correct as that function doesn't just allocate memory! Right >> + dev_err(&pdev->dev, "Failed to allocate memory.\n"); >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = dev_name(&pdev->dev); >> + idev->modes = INDIO_DIRECT_MODE; >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, st); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st->reg_base, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + st->nb_chan = pdata->num_channels; >> + ret = at91adc_channel_init(st, pdata); >> + if (ret < 0) { > no brackets please. Again, checkpatch would probably have shouted > about this. >> + goto error_free_clk; >> + } >> + > This is what I meant when I asked why you have two copies of the > pointer and count? >> + idev->channels = st->channels; >> + idev->num_channels = pdata->num_channels_used; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + st->vref_mv = pdata->vref; >> + >> + ret = iio_device_register(idev); > again incorrect brackets for the code style. >> + if (ret < 0) { > unwind the channel init? Oooh, right... >> + goto error_free_clk; >> + } >> + >> + return 0; >> + >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_free_device(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + iio_device_unregister(idev); > Free channels array? >> + free_irq(st->irq, st); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_free_device(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +static int __init at91adc_init(void) >> +{ >> + return platform_driver_register(&at91adc_driver); >> +} >> + >> +static void __exit at91adc_exit(void) >> +{ >> + platform_driver_unregister(&at91adc_driver); >> +} >> + > > Some neat boiler plate removal stuff was just merged for > platform drivers. See module_platform_driver in include/linux/platform.h > > Definitely want to use that! I'm hoping the spi and i2c versions > turn up soon as it will save us a lot of silly cut and paste code. Aah, nice :) Thanks, -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-04 16:29 ` Maxime Ripard @ 2011-11-04 16:40 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 16:40 UTC (permalink / raw) To: linux-arm-kernel ... >>> + if(pdata->channels_used[i]) { >>> + struct iio_chan_spec *chan = st->channels + idx; >>> + chan->type = IIO_VOLTAGE; >>> + chan->indexed = 1; >>> + chan->channel = i; >>> + chan->scan_type.sign = 's'; >>> + chan->scan_type.realbits = 10; >>> + chan->scan_type.storagebits = 32; >> That is pretty inefficient storage! If we do implement buffering >> on this, given mass reads don't look to be quicker, we'll just >> munge it down to 16 bits. > > Well, I thought that storage bits was here to represent how the hardware > stored the values. Since these values are stored on 10 bits alone in a > 32 bits register, I thought that these were the right values, but we can > definitely lower the storagebits to 16 here. > It's actually the storage in a buffer if we use one. scan_type doesn't actually need defining at all if we have no buffered support (it's not used by the core). Clearly if buffering is used, 32 bits 'might' give us slightly fewer copies, but at the cost of double the memory usage. It would take some pretty impressive benchmark figures to convince that it was worth the space. >>> + goto error_ret; >>> + } >>> + >>> + idev = iio_allocate_device(sizeof(struct at91adc_state)); >> sizeof(*st) is a little neater? > > Well, I find sizeof(struct at91adc_state) clearer, but ok. I don't care so stick with it if you prefer! ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-03 10:11 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel, linux-iio; +Cc: Nicolas Ferre, Patrice Vilchez Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ 2 files changed, 64 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..bfc50ac 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + if(data->channels_used[0]) + at91_set_A_periph(AT91_PIN_PC0, 0); + if(data->channels_used[1]) + at91_set_A_periph(AT91_PIN_PC1, 0); + if(data->channels_used[2]) + at91_set_A_periph(AT91_PIN_PC2, 0); + if(data->channels_used[3]) + at91_set_A_periph(AT91_PIN_PC3, 0); + + data->adc_clock = 5000000; + data->num_channels = 4; + data->startup_time = 10; + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..6f2542fb 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels_used = { 1, 1, 1, 1} , + .num_channels_used = 4, + .vref = 3300, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +399,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-11-03 10:11 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-03 10:11 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ 2 files changed, 64 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..bfc50ac 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + if(data->channels_used[0]) + at91_set_A_periph(AT91_PIN_PC0, 0); + if(data->channels_used[1]) + at91_set_A_periph(AT91_PIN_PC1, 0); + if(data->channels_used[2]) + at91_set_A_periph(AT91_PIN_PC2, 0); + if(data->channels_used[3]) + at91_set_A_periph(AT91_PIN_PC3, 0); + + data->adc_clock = 5000000; + data->num_channels = 4; + data->startup_time = 10; + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..6f2542fb 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels_used = { 1, 1, 1, 1} , + .num_channels_used = 4, + .vref = 3300, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +399,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-03 10:11 ` Maxime Ripard @ 2011-11-04 10:33 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:33 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez Forgot to put some general comments in the previous patch reviews. Please run checkpatch.pl over all patches as it will clean up quite a few issues. Mostly looking good. At the moment it's simple enough that we can rapidly move this one out of staging once the core has gone. Then we can build up more interesting stuff as support goes into the non staging core. Thanks, Jonathan On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ > arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ > 2 files changed, 64 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c > index 39f81f4..bfc50ac 100644 > --- a/arch/arm/mach-at91/at91sam9260_devices.c > +++ b/arch/arm/mach-at91/at91sam9260_devices.c > @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) > void __init at91_add_device_cf(struct at91_cf_data * data) {} > #endif > > +/* -------------------------------------------------------------------- > + * ADCs > + * -------------------------------------------------------------------- */ > + > +static struct at91_adc_data adc_data; > + > +static struct resource adc_resources[] = { > + [0] = { > + .start = AT91SAM9260_BASE_ADC, > + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, > + .flags = IORESOURCE_MEM, > + }, > + [1] = { > + .start = AT91SAM9260_ID_ADC, > + .end = AT91SAM9260_ID_ADC, > + .flags = IORESOURCE_IRQ, > + }, > +}; > + > +static struct platform_device at91_adc_device = { > + .name = "at91adc", > + .id = -1, > + .dev = { > + .platform_data = &adc_data, > + }, > + .resource = adc_resources, > + .num_resources = ARRAY_SIZE(adc_resources), > +}; > + > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + if(data->channels_used[0]) > + at91_set_A_periph(AT91_PIN_PC0, 0); > + if(data->channels_used[1]) > + at91_set_A_periph(AT91_PIN_PC1, 0); > + if(data->channels_used[2]) > + at91_set_A_periph(AT91_PIN_PC2, 0); > + if(data->channels_used[3]) > + at91_set_A_periph(AT91_PIN_PC3, 0); formatting issues so another one for checkpatch. Also define AT91_PIN_PC(n) then make this another for_each_bit_set call. That should get you down from 8 lines to about 3. > + > + data->adc_clock = 5000000; > + data->num_channels = 4; > + data->startup_time = 10; > + > + adc_data = *data; > + platform_device_register(&at91_adc_device); > +} > + > + > /* -------------------------------------------------------------------- */ > /* > * These devices are always present and don't need any board-specific > diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c > index 817f59d..6f2542fb 100644 > --- a/arch/arm/mach-at91/board-sam9g20ek.c > +++ b/arch/arm/mach-at91/board-sam9g20ek.c > @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) > static void __init ek_add_device_buttons(void) {} > #endif > > +/* > + * ADCs > + */ > + > +static struct at91_adc_data ek_adc_data = { > + .channels_used = { 1, 1, 1, 1} , > + .num_channels_used = 4, > + .vref = 3300, > +}; > + > #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) > static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { > REGULATOR_SUPPLY("AVDD", "0-001b"), > @@ -389,6 +399,8 @@ static void __init ek_board_init(void) > ek_add_device_gpio_leds(); > /* Push Buttons */ > ek_add_device_buttons(); > + /* ADCs */ > + at91_add_device_adc(&ek_adc_data); > /* PCK0 provides MCLK to the WM8731 */ > at91_set_B_periph(AT91_PIN_PC1, 0); > /* SSC (for WM8731) */ ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-11-04 10:33 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 10:33 UTC (permalink / raw) To: linux-arm-kernel Forgot to put some general comments in the previous patch reviews. Please run checkpatch.pl over all patches as it will clean up quite a few issues. Mostly looking good. At the moment it's simple enough that we can rapidly move this one out of staging once the core has gone. Then we can build up more interesting stuff as support goes into the non staging core. Thanks, Jonathan On 11/03/2011 10:11 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ > arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ > 2 files changed, 64 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c > index 39f81f4..bfc50ac 100644 > --- a/arch/arm/mach-at91/at91sam9260_devices.c > +++ b/arch/arm/mach-at91/at91sam9260_devices.c > @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) > void __init at91_add_device_cf(struct at91_cf_data * data) {} > #endif > > +/* -------------------------------------------------------------------- > + * ADCs > + * -------------------------------------------------------------------- */ > + > +static struct at91_adc_data adc_data; > + > +static struct resource adc_resources[] = { > + [0] = { > + .start = AT91SAM9260_BASE_ADC, > + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, > + .flags = IORESOURCE_MEM, > + }, > + [1] = { > + .start = AT91SAM9260_ID_ADC, > + .end = AT91SAM9260_ID_ADC, > + .flags = IORESOURCE_IRQ, > + }, > +}; > + > +static struct platform_device at91_adc_device = { > + .name = "at91adc", > + .id = -1, > + .dev = { > + .platform_data = &adc_data, > + }, > + .resource = adc_resources, > + .num_resources = ARRAY_SIZE(adc_resources), > +}; > + > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + if(data->channels_used[0]) > + at91_set_A_periph(AT91_PIN_PC0, 0); > + if(data->channels_used[1]) > + at91_set_A_periph(AT91_PIN_PC1, 0); > + if(data->channels_used[2]) > + at91_set_A_periph(AT91_PIN_PC2, 0); > + if(data->channels_used[3]) > + at91_set_A_periph(AT91_PIN_PC3, 0); formatting issues so another one for checkpatch. Also define AT91_PIN_PC(n) then make this another for_each_bit_set call. That should get you down from 8 lines to about 3. > + > + data->adc_clock = 5000000; > + data->num_channels = 4; > + data->startup_time = 10; > + > + adc_data = *data; > + platform_device_register(&at91_adc_device); > +} > + > + > /* -------------------------------------------------------------------- */ > /* > * These devices are always present and don't need any board-specific > diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c > index 817f59d..6f2542fb 100644 > --- a/arch/arm/mach-at91/board-sam9g20ek.c > +++ b/arch/arm/mach-at91/board-sam9g20ek.c > @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) > static void __init ek_add_device_buttons(void) {} > #endif > > +/* > + * ADCs > + */ > + > +static struct at91_adc_data ek_adc_data = { > + .channels_used = { 1, 1, 1, 1} , > + .num_channels_used = 4, > + .vref = 3300, > +}; > + > #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) > static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { > REGULATOR_SUPPLY("AVDD", "0-001b"), > @@ -389,6 +399,8 @@ static void __init ek_board_init(void) > ek_add_device_gpio_leds(); > /* Push Buttons */ > ek_add_device_buttons(); > + /* ADCs */ > + at91_add_device_adc(&ek_adc_data); > /* PCK0 provides MCLK to the WM8731 */ > at91_set_B_periph(AT91_PIN_PC1, 0); > /* SSC (for WM8731) */ ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-04 10:33 ` Jonathan Cameron (?) @ 2011-11-04 11:25 ` Maxime Ripard 2011-11-04 15:52 ` Linus Walleij 2011-11-04 16:32 ` Jonathan Cameron -1 siblings, 2 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-04 11:25 UTC (permalink / raw) To: linux-arm-kernel Hi Jonathan, Thanks for your review, I'm working on it and will submit a new version asap. On 04/11/2011 11:33, Jonathan Cameron wrote: > Please run checkpatch.pl over all patches as it will clean up quite a few > issues. Yep, I forgot to run it, my bad. > Mostly looking good. At the moment it's simple enough that we can > rapidly move this one out of staging once the core has gone. Ok. Rebasing on your outofstaging branch for the next version would be a good idea ? Or do you want the driver to still go through the staging step ? Linus, I guess that if we put the driver directly into the main tree you are ok with the changes made to boards files (code reviews apart) ? Thanks, Maxime > On 11/03/2011 10:11 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> --- >> arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ >> arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ >> 2 files changed, 64 insertions(+), 0 deletions(-) >> >> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c >> index 39f81f4..bfc50ac 100644 >> --- a/arch/arm/mach-at91/at91sam9260_devices.c >> +++ b/arch/arm/mach-at91/at91sam9260_devices.c >> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) >> void __init at91_add_device_cf(struct at91_cf_data * data) {} >> #endif >> >> +/* -------------------------------------------------------------------- >> + * ADCs >> + * -------------------------------------------------------------------- */ >> + >> +static struct at91_adc_data adc_data; >> + >> +static struct resource adc_resources[] = { >> + [0] = { >> + .start = AT91SAM9260_BASE_ADC, >> + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, >> + .flags = IORESOURCE_MEM, >> + }, >> + [1] = { >> + .start = AT91SAM9260_ID_ADC, >> + .end = AT91SAM9260_ID_ADC, >> + .flags = IORESOURCE_IRQ, >> + }, >> +}; >> + >> +static struct platform_device at91_adc_device = { >> + .name = "at91adc", >> + .id = -1, >> + .dev = { >> + .platform_data = &adc_data, >> + }, >> + .resource = adc_resources, >> + .num_resources = ARRAY_SIZE(adc_resources), >> +}; >> + >> +void __init at91_add_device_adc(struct at91_adc_data *data) >> +{ >> + if (!data) >> + return; >> + >> + if(data->channels_used[0]) >> + at91_set_A_periph(AT91_PIN_PC0, 0); >> + if(data->channels_used[1]) >> + at91_set_A_periph(AT91_PIN_PC1, 0); >> + if(data->channels_used[2]) >> + at91_set_A_periph(AT91_PIN_PC2, 0); >> + if(data->channels_used[3]) >> + at91_set_A_periph(AT91_PIN_PC3, 0); > formatting issues so another one for checkpatch. > > Also define AT91_PIN_PC(n) then make this another > for_each_bit_set call. That should get you down from > 8 lines to about 3. >> + >> + data->adc_clock = 5000000; >> + data->num_channels = 4; >> + data->startup_time = 10; >> + >> + adc_data = *data; >> + platform_device_register(&at91_adc_device); >> +} >> + >> + >> /* -------------------------------------------------------------------- */ >> /* >> * These devices are always present and don't need any board-specific >> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c >> index 817f59d..6f2542fb 100644 >> --- a/arch/arm/mach-at91/board-sam9g20ek.c >> +++ b/arch/arm/mach-at91/board-sam9g20ek.c >> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) >> static void __init ek_add_device_buttons(void) {} >> #endif >> >> +/* >> + * ADCs >> + */ >> + >> +static struct at91_adc_data ek_adc_data = { >> + .channels_used = { 1, 1, 1, 1} , >> + .num_channels_used = 4, >> + .vref = 3300, >> +}; >> + >> #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) >> static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { >> REGULATOR_SUPPLY("AVDD", "0-001b"), >> @@ -389,6 +399,8 @@ static void __init ek_board_init(void) >> ek_add_device_gpio_leds(); >> /* Push Buttons */ >> ek_add_device_buttons(); >> + /* ADCs */ >> + at91_add_device_adc(&ek_adc_data); >> /* PCK0 provides MCLK to the WM8731 */ >> at91_set_B_periph(AT91_PIN_PC1, 0); >> /* SSC (for WM8731) */ > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-04 11:25 ` Maxime Ripard @ 2011-11-04 15:52 ` Linus Walleij 2011-11-04 16:32 ` Jonathan Cameron 1 sibling, 0 replies; 131+ messages in thread From: Linus Walleij @ 2011-11-04 15:52 UTC (permalink / raw) To: linux-arm-kernel On Fri, Nov 4, 2011 at 12:25 PM, Maxime Ripard <maxime.ripard@free-electrons.com> wrote: > Ok. Rebasing on your outofstaging branch for the next version would be a > good idea ? > > Or do you want the driver to still go through the staging step ? > > Linus, I guess that if we put the driver directly into the main tree you > are ok with the changes made to boards files (code reviews apart) ? Yes that's fine. Linus Walleij ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-04 11:25 ` Maxime Ripard @ 2011-11-04 16:32 ` Jonathan Cameron 2011-11-04 16:32 ` Jonathan Cameron 1 sibling, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 16:32 UTC (permalink / raw) To: Maxime Ripard Cc: Linus Walleij, linux-iio, Patrice Vilchez, Nicolas Ferre, linux-arm-kernel On 11/04/2011 11:25 AM, Maxime Ripard wrote: > Hi Jonathan, > > Thanks for your review, I'm working on it and will submit a new version > asap. > > On 04/11/2011 11:33, Jonathan Cameron wrote: >> Please run checkpatch.pl over all patches as it will clean up quite a few >> issues. > > Yep, I forgot to run it, my bad. > >> Mostly looking good. At the moment it's simple enough that we can >> rapidly move this one out of staging once the core has gone. > > Ok. Rebasing on your outofstaging branch for the next version would be a > good idea ? > > Or do you want the driver to still go through the staging step ? I don't really mind. I doubt Greg would send it on in this merge window anyway. Going from one to the other is trivial change of about 4 function names anyway for a simple driver like this. It probably mostly depends on whether you want to mess around with the more complex bits that are going to stay in staging for sometime yet (buffering, in kernel push interface, events). > > Linus, I guess that if we put the driver directly into the main tree you > are ok with the changes made to boards files (code reviews apart) ? > > Thanks, > Maxime > >> On 11/03/2011 10:11 AM, Maxime Ripard wrote: >>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >>> --- >>> arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ >>> arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ >>> 2 files changed, 64 insertions(+), 0 deletions(-) >>> >>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c >>> index 39f81f4..bfc50ac 100644 >>> --- a/arch/arm/mach-at91/at91sam9260_devices.c >>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c >>> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) >>> void __init at91_add_device_cf(struct at91_cf_data * data) {} >>> #endif >>> >>> +/* -------------------------------------------------------------------- >>> + * ADCs >>> + * -------------------------------------------------------------------- */ >>> + >>> +static struct at91_adc_data adc_data; >>> + >>> +static struct resource adc_resources[] = { >>> + [0] = { >>> + .start = AT91SAM9260_BASE_ADC, >>> + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, >>> + .flags = IORESOURCE_MEM, >>> + }, >>> + [1] = { >>> + .start = AT91SAM9260_ID_ADC, >>> + .end = AT91SAM9260_ID_ADC, >>> + .flags = IORESOURCE_IRQ, >>> + }, >>> +}; >>> + >>> +static struct platform_device at91_adc_device = { >>> + .name = "at91adc", >>> + .id = -1, >>> + .dev = { >>> + .platform_data = &adc_data, >>> + }, >>> + .resource = adc_resources, >>> + .num_resources = ARRAY_SIZE(adc_resources), >>> +}; >>> + >>> +void __init at91_add_device_adc(struct at91_adc_data *data) >>> +{ >>> + if (!data) >>> + return; >>> + >>> + if(data->channels_used[0]) >>> + at91_set_A_periph(AT91_PIN_PC0, 0); >>> + if(data->channels_used[1]) >>> + at91_set_A_periph(AT91_PIN_PC1, 0); >>> + if(data->channels_used[2]) >>> + at91_set_A_periph(AT91_PIN_PC2, 0); >>> + if(data->channels_used[3]) >>> + at91_set_A_periph(AT91_PIN_PC3, 0); >> formatting issues so another one for checkpatch. >> >> Also define AT91_PIN_PC(n) then make this another >> for_each_bit_set call. That should get you down from >> 8 lines to about 3. >>> + >>> + data->adc_clock = 5000000; >>> + data->num_channels = 4; >>> + data->startup_time = 10; >>> + >>> + adc_data = *data; >>> + platform_device_register(&at91_adc_device); >>> +} >>> + >>> + >>> /* -------------------------------------------------------------------- */ >>> /* >>> * These devices are always present and don't need any board-specific >>> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c >>> index 817f59d..6f2542fb 100644 >>> --- a/arch/arm/mach-at91/board-sam9g20ek.c >>> +++ b/arch/arm/mach-at91/board-sam9g20ek.c >>> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) >>> static void __init ek_add_device_buttons(void) {} >>> #endif >>> >>> +/* >>> + * ADCs >>> + */ >>> + >>> +static struct at91_adc_data ek_adc_data = { >>> + .channels_used = { 1, 1, 1, 1} , >>> + .num_channels_used = 4, >>> + .vref = 3300, >>> +}; >>> + >>> #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) >>> static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { >>> REGULATOR_SUPPLY("AVDD", "0-001b"), >>> @@ -389,6 +399,8 @@ static void __init ek_board_init(void) >>> ek_add_device_gpio_leds(); >>> /* Push Buttons */ >>> ek_add_device_buttons(); >>> + /* ADCs */ >>> + at91_add_device_adc(&ek_adc_data); >>> /* PCK0 provides MCLK to the WM8731 */ >>> at91_set_B_periph(AT91_PIN_PC1, 0); >>> /* SSC (for WM8731) */ >> >> >> _______________________________________________ >> linux-arm-kernel mailing list >> linux-arm-kernel@lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > > ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-11-04 16:32 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-04 16:32 UTC (permalink / raw) To: linux-arm-kernel On 11/04/2011 11:25 AM, Maxime Ripard wrote: > Hi Jonathan, > > Thanks for your review, I'm working on it and will submit a new version > asap. > > On 04/11/2011 11:33, Jonathan Cameron wrote: >> Please run checkpatch.pl over all patches as it will clean up quite a few >> issues. > > Yep, I forgot to run it, my bad. > >> Mostly looking good. At the moment it's simple enough that we can >> rapidly move this one out of staging once the core has gone. > > Ok. Rebasing on your outofstaging branch for the next version would be a > good idea ? > > Or do you want the driver to still go through the staging step ? I don't really mind. I doubt Greg would send it on in this merge window anyway. Going from one to the other is trivial change of about 4 function names anyway for a simple driver like this. It probably mostly depends on whether you want to mess around with the more complex bits that are going to stay in staging for sometime yet (buffering, in kernel push interface, events). > > Linus, I guess that if we put the driver directly into the main tree you > are ok with the changes made to boards files (code reviews apart) ? > > Thanks, > Maxime > >> On 11/03/2011 10:11 AM, Maxime Ripard wrote: >>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >>> --- >>> arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ >>> arch/arm/mach-at91/board-sam9g20ek.c | 12 +++++++ >>> 2 files changed, 64 insertions(+), 0 deletions(-) >>> >>> diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c >>> index 39f81f4..bfc50ac 100644 >>> --- a/arch/arm/mach-at91/at91sam9260_devices.c >>> +++ b/arch/arm/mach-at91/at91sam9260_devices.c >>> @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) >>> void __init at91_add_device_cf(struct at91_cf_data * data) {} >>> #endif >>> >>> +/* -------------------------------------------------------------------- >>> + * ADCs >>> + * -------------------------------------------------------------------- */ >>> + >>> +static struct at91_adc_data adc_data; >>> + >>> +static struct resource adc_resources[] = { >>> + [0] = { >>> + .start = AT91SAM9260_BASE_ADC, >>> + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, >>> + .flags = IORESOURCE_MEM, >>> + }, >>> + [1] = { >>> + .start = AT91SAM9260_ID_ADC, >>> + .end = AT91SAM9260_ID_ADC, >>> + .flags = IORESOURCE_IRQ, >>> + }, >>> +}; >>> + >>> +static struct platform_device at91_adc_device = { >>> + .name = "at91adc", >>> + .id = -1, >>> + .dev = { >>> + .platform_data = &adc_data, >>> + }, >>> + .resource = adc_resources, >>> + .num_resources = ARRAY_SIZE(adc_resources), >>> +}; >>> + >>> +void __init at91_add_device_adc(struct at91_adc_data *data) >>> +{ >>> + if (!data) >>> + return; >>> + >>> + if(data->channels_used[0]) >>> + at91_set_A_periph(AT91_PIN_PC0, 0); >>> + if(data->channels_used[1]) >>> + at91_set_A_periph(AT91_PIN_PC1, 0); >>> + if(data->channels_used[2]) >>> + at91_set_A_periph(AT91_PIN_PC2, 0); >>> + if(data->channels_used[3]) >>> + at91_set_A_periph(AT91_PIN_PC3, 0); >> formatting issues so another one for checkpatch. >> >> Also define AT91_PIN_PC(n) then make this another >> for_each_bit_set call. That should get you down from >> 8 lines to about 3. >>> + >>> + data->adc_clock = 5000000; >>> + data->num_channels = 4; >>> + data->startup_time = 10; >>> + >>> + adc_data = *data; >>> + platform_device_register(&at91_adc_device); >>> +} >>> + >>> + >>> /* -------------------------------------------------------------------- */ >>> /* >>> * These devices are always present and don't need any board-specific >>> diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c >>> index 817f59d..6f2542fb 100644 >>> --- a/arch/arm/mach-at91/board-sam9g20ek.c >>> +++ b/arch/arm/mach-at91/board-sam9g20ek.c >>> @@ -314,6 +314,16 @@ static void __init ek_add_device_buttons(void) >>> static void __init ek_add_device_buttons(void) {} >>> #endif >>> >>> +/* >>> + * ADCs >>> + */ >>> + >>> +static struct at91_adc_data ek_adc_data = { >>> + .channels_used = { 1, 1, 1, 1} , >>> + .num_channels_used = 4, >>> + .vref = 3300, >>> +}; >>> + >>> #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) >>> static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { >>> REGULATOR_SUPPLY("AVDD", "0-001b"), >>> @@ -389,6 +399,8 @@ static void __init ek_board_init(void) >>> ek_add_device_gpio_leds(); >>> /* Push Buttons */ >>> ek_add_device_buttons(); >>> + /* ADCs */ >>> + at91_add_device_adc(&ek_adc_data); >>> /* PCK0 provides MCLK to the WM8731 */ >>> at91_set_B_periph(AT91_PIN_PC1, 0); >>> /* SSC (for WM8731) */ >> >> >> _______________________________________________ >> linux-arm-kernel mailing list >> linux-arm-kernel at lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > > ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCHv3] AT91: Add a driver for the ADC 2011-10-19 16:18 ` Maxime Ripard @ 2011-11-07 16:08 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The core" patchset from Jonathan Cameron, applied on top of 3.1 Improvements since v2: - Move the driver out of staging - Slightly modify the definition of channels to use bitmaps - The driver no longer leaks the channels array - Various minor fixes and codestyle improvements Improvements since v1: - Rebase on top of commit 85d8ff8 - Initialise scan_types - Added scale informations in the driver - Allow to use only a subset of adc channels available on the SoC - Various fix according to reviews Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCHv3] AT91: Add a driver for the ADC @ 2011-11-07 16:08 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The core" patchset from Jonathan Cameron, applied on top of 3.1 Improvements since v2: - Move the driver out of staging - Slightly modify the definition of channels to use bitmaps - The driver no longer leaks the channels array - Various minor fixes and codestyle improvements Improvements since v1: - Rebase on top of commit 85d8ff8 - Initialise scan_types - Added scale informations in the driver - Allow to use only a subset of adc channels available on the SoC - Various fix according to reviews Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-07 16:08 ` Maxime Ripard @ 2011-11-07 16:08 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..afdcac0 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used array). + */ + u8 num_channels; + /* Channels in use on the board */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-07 16:08 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..afdcac0 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used array). + */ + u8 num_channels; + /* Channels in use on the board */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-07 16:08 ` Maxime Ripard @ 2011-11-07 16:27 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-07 16:27 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Looks sensible to me though obviously this is bit is really for the at91 lot to comment on. On 11/07/2011 04:08 PM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ > 1 files changed, 18 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..afdcac0 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). > + */ > + u8 num_channels; > + /* Channels in use on the board */ One nitpick here. You could make it more obvious that this is a mask... > + unsigned long channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-07 16:27 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-07 16:27 UTC (permalink / raw) To: linux-arm-kernel Looks sensible to me though obviously this is bit is really for the at91 lot to comment on. On 11/07/2011 04:08 PM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ > 1 files changed, 18 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..afdcac0 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). > + */ > + u8 num_channels; > + /* Channels in use on the board */ One nitpick here. You could make it more obvious that this is a mask... > + unsigned long channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-07 16:08 ` Maxime Ripard @ 2011-11-08 13:19 ` Thomas Petazzoni -1 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-11-08 13:19 UTC (permalink / raw) To: Maxime Ripard; +Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Nicolas Ferre Le Mon, 7 Nov 2011 17:08:31 +0100, Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit : > +/* ADC */ > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels a= re > + * indeed used on the board, see the channels_used array). channels_used is no longer an array, but a bitmap. Regards, Thomas --=20 Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-08 13:19 ` Thomas Petazzoni 0 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-11-08 13:19 UTC (permalink / raw) To: linux-arm-kernel Le Mon, 7 Nov 2011 17:08:31 +0100, Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit : > +/* ADC */ > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used array). channels_used is no longer an array, but a bitmap. Regards, Thomas -- Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-07 16:08 ` Maxime Ripard @ 2011-11-07 16:08 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/Makefile | 4 +- drivers/iio/adc/at91adc.c | 311 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 1 deletions(-) create mode 100644 drivers/iio/adc/at91adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3d97b21..74f4d9f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -14,6 +14,12 @@ config IIO_AD799X i2c analog to digital convertors (ADC). Provides direct access via sysfs. +config IIO_AT91ADC + tristate "Atmel AT91 ADC" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel AT91 ADC. + config IIO_MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c197334..776b56f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o iio_max1363-y := max1363_core.o -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o \ No newline at end of file +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o + +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c new file mode 100644 index 0000000..3ca2a64 --- /dev/null +++ b/drivers/iio/adc/at91adc.c @@ -0,0 +1,311 @@ +/* + * Driver for the ADC present in the Atmel AT91 evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include <linux/iio/iio.h> + +#include <mach/at91_adc.h> +#include <mach/board.h> + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + int irq; + wait_queue_head_t wq_data_avail; + u16 last_value; + void __iomem *reg_base; + unsigned int vref_mv; +}; + +static inline u32 at91adc_reg_read(void *base, u8 reg) +{ + return readl_relaxed(base + reg); +} + +static inline void at91adc_reg_write(void *base, u8 reg, u32 val) +{ + writel_relaxed(val, base + reg); +} + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct iio_dev *idev = private; + struct at91adc_state *st = iio_priv(idev); + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < idev->num_channels; chan++) + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->last_value = at91adc_reg_read(st->reg_base, + AT91_ADC_CHR(chan)); + } + + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct iio_dev *idev, + struct at91_adc_data *pdata) +{ + struct iio_chan_spec *chan_array; + int bit, idx = 0; + + idev->num_channels = bitmap_weight(&(pdata->channels_used), + pdata->num_channels); + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), + GFP_KERNEL); + + if (chan_array == NULL) + return -ENOMEM; + + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { + struct iio_chan_spec *chan = chan_array + idx; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = 10; + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; + ++idx; + } + + idev->channels = chan_array; + return idev->num_channels; +} + +static void at91adc_channel_remove(struct iio_dev *idev) +{ + if (idev->channels == NULL) + return; + + kfree(idev->channels); +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + unsigned int scale_uv; + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); + + wait_event_interruptible_timeout(st->wq_data_avail, st->done, + msecs_to_jiffies(10 * 1000)); + *val = st->last_value; + + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->last_value = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_device_allocate(sizeof(struct at91adc_state)); + if (idev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = dev_name(&pdev->dev); + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st->reg_base, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + ret = at91adc_channel_init(idev, pdata); + if (ret < 0) + goto error_free_clk; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + st->vref_mv = pdata->vref; + + ret = iio_device_register(idev); + if (ret < 0) + goto error_free_channels; + + return 0; + +error_free_channels: + at91adc_channel_remove(idev); +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_device_free(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + at91adc_channel_remove(idev); + iio_device_unregister(idev); + free_irq(st->irq, idev); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_device_free(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +module_platform_driver(at91adc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-07 16:08 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/Makefile | 4 +- drivers/iio/adc/at91adc.c | 311 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 1 deletions(-) create mode 100644 drivers/iio/adc/at91adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3d97b21..74f4d9f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -14,6 +14,12 @@ config IIO_AD799X i2c analog to digital convertors (ADC). Provides direct access via sysfs. +config IIO_AT91ADC + tristate "Atmel AT91 ADC" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel AT91 ADC. + config IIO_MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c197334..776b56f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o iio_max1363-y := max1363_core.o -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o \ No newline at end of file +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o + +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c new file mode 100644 index 0000000..3ca2a64 --- /dev/null +++ b/drivers/iio/adc/at91adc.c @@ -0,0 +1,311 @@ +/* + * Driver for the ADC present in the Atmel AT91 evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include <linux/iio/iio.h> + +#include <mach/at91_adc.h> +#include <mach/board.h> + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + int irq; + wait_queue_head_t wq_data_avail; + u16 last_value; + void __iomem *reg_base; + unsigned int vref_mv; +}; + +static inline u32 at91adc_reg_read(void *base, u8 reg) +{ + return readl_relaxed(base + reg); +} + +static inline void at91adc_reg_write(void *base, u8 reg, u32 val) +{ + writel_relaxed(val, base + reg); +} + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct iio_dev *idev = private; + struct at91adc_state *st = iio_priv(idev); + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < idev->num_channels; chan++) + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->last_value = at91adc_reg_read(st->reg_base, + AT91_ADC_CHR(chan)); + } + + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct iio_dev *idev, + struct at91_adc_data *pdata) +{ + struct iio_chan_spec *chan_array; + int bit, idx = 0; + + idev->num_channels = bitmap_weight(&(pdata->channels_used), + pdata->num_channels); + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), + GFP_KERNEL); + + if (chan_array == NULL) + return -ENOMEM; + + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { + struct iio_chan_spec *chan = chan_array + idx; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = 10; + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; + ++idx; + } + + idev->channels = chan_array; + return idev->num_channels; +} + +static void at91adc_channel_remove(struct iio_dev *idev) +{ + if (idev->channels == NULL) + return; + + kfree(idev->channels); +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + unsigned int scale_uv; + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); + + wait_event_interruptible_timeout(st->wq_data_avail, st->done, + msecs_to_jiffies(10 * 1000)); + *val = st->last_value; + + at91adc_reg_write(st->reg_base, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->last_value = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_device_allocate(sizeof(struct at91adc_state)); + if (idev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = dev_name(&pdev->dev); + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st->reg_base, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + ret = at91adc_channel_init(idev, pdata); + if (ret < 0) + goto error_free_clk; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + st->vref_mv = pdata->vref; + + ret = iio_device_register(idev); + if (ret < 0) + goto error_free_channels; + + return 0; + +error_free_channels: + at91adc_channel_remove(idev); +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_device_free(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + at91adc_channel_remove(idev); + iio_device_unregister(idev); + free_irq(st->irq, idev); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_device_free(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +module_platform_driver(at91adc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-07 16:08 ` Maxime Ripard @ 2011-11-08 13:30 ` Thomas Petazzoni -1 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-11-08 13:30 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Nicolas Ferre Le Mon, 7 Nov 2011 17:08:32 +0100, Maxime Ripard <maxime.ripard@free-electrons.com> a =C3=A9crit : > +static inline u32 at91adc_reg_read(void *base, u8 reg) > +{ > + return readl_relaxed(base + reg); > +} > + > +static inline void at91adc_reg_write(void *base, u8 reg, u32 val) > +{ > + writel_relaxed(val, base + reg); > +} Those could probably be written to take a at91adc_state structure as argument, in order to simplify the call sites. I.e: static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) { return readl_relaxed(st->reg_base + reg); } static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, = u32 val) { writel_relaxed(val, st->reg_base + reg); } > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct iio_dev *idev =3D private; > + struct at91adc_state *st =3D iio_priv(idev); > + unsigned int status =3D at91adc_reg_read(st->reg_base, AT91_ADC_SR)= ; would become + unsigned int status =3D at91adc_reg_read(st, AT91_ADC_SR); > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan =3D 0; chan < idev->num_channels; chan++) > + if (status & AT91_ADC_EOC(chan)) { > + st->done =3D true; > + st->last_value =3D at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); would become + st->last_value =3D at91adc_reg_read(st, AT91_ADC_CHR(chan)); > +static int at91adc_channel_init(struct iio_dev *idev, > + struct at91_adc_data *pdata) > +{ > + struct iio_chan_spec *chan_array; > + int bit, idx =3D 0; > + > + idev->num_channels =3D bitmap_weight(&(pdata->channels_used), > + pdata->num_channels); > + chan_array =3D kcalloc(idev->num_channels, sizeof(struct iio_chan_s= pec), > + GFP_KERNEL); > + > + if (chan_array =3D=3D NULL) > + return -ENOMEM; > + > + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels)= { > + struct iio_chan_spec *chan =3D chan_array + idx; > + chan->type =3D IIO_VOLTAGE; > + chan->indexed =3D 1; > + chan->channel =3D bit; > + chan->scan_type.sign =3D 's'; It's an unsigned value that you're reading from the ADC, so maybe this should be =3D 'u'. > +static void at91adc_channel_remove(struct iio_dev *idev) > +{ > + if (idev->channels =3D=3D NULL) > + return; > + > + kfree(idev->channels); kfree() is fine with having its argument being NULL, so the if test is useless here. See http://lxr.free-electrons.com/source/mm/slab.c#L3863. > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st =3D iio_priv(idev); > + unsigned int scale_uv; > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible_timeout(st->wq_data_avail, st->done, > + msecs_to_jiffies(10 * 1000)); You forgot to handle the cases where wait_event_interruptible_timeout() exits because of a signal or because of the timeout, and make the assumption that the wait_event_interruptible_timeout() is also successful. > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret =3D request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk =3D clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret =3D PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk =3D clk_get_rate(st->clk); You do a clk_get()/clk_enable() here but nowhere in the ->remove() function you do a clk_disable()/clk_put(). > + prsc =3D (mstrclk / (2 * pdata->adc_clock)) - 1; [...] > + ticks =3D round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; Both of those computations would be better with a small comment explaining what's happening. > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev =3D platform_get_drvdata(pdev); > + struct resource *res =3D platform_get_resource(pdev, IORESOURCE_MEM= , 0); > + struct at91adc_state *st =3D iio_priv(idev); > + > + at91adc_channel_remove(idev); > + iio_device_unregister(idev); > + free_irq(st->irq, idev); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_free(idev); As said above, clk_disable()/clk_put() is missing. > +static struct platform_driver at91adc_driver =3D { > + .probe =3D at91adc_probe, > + .remove =3D __devexit_p(at91adc_remove), > + .driver =3D { > + .name =3D "at91adc", > + }, Nitpick: wrongly placed parenthesis. Regards, Thomas --=20 Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-08 13:30 ` Thomas Petazzoni 0 siblings, 0 replies; 131+ messages in thread From: Thomas Petazzoni @ 2011-11-08 13:30 UTC (permalink / raw) To: linux-arm-kernel Le Mon, 7 Nov 2011 17:08:32 +0100, Maxime Ripard <maxime.ripard@free-electrons.com> a ?crit : > +static inline u32 at91adc_reg_read(void *base, u8 reg) > +{ > + return readl_relaxed(base + reg); > +} > + > +static inline void at91adc_reg_write(void *base, u8 reg, u32 val) > +{ > + writel_relaxed(val, base + reg); > +} Those could probably be written to take a at91adc_state structure as argument, in order to simplify the call sites. I.e: static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) { return readl_relaxed(st->reg_base + reg); } static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) { writel_relaxed(val, st->reg_base + reg); } > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct iio_dev *idev = private; > + struct at91adc_state *st = iio_priv(idev); > + unsigned int status = at91adc_reg_read(st->reg_base, AT91_ADC_SR); would become + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < idev->num_channels; chan++) > + if (status & AT91_ADC_EOC(chan)) { > + st->done = true; > + st->last_value = at91adc_reg_read(st->reg_base, > + AT91_ADC_CHR(chan)); would become + st->last_value = at91adc_reg_read(st, AT91_ADC_CHR(chan)); > +static int at91adc_channel_init(struct iio_dev *idev, > + struct at91_adc_data *pdata) > +{ > + struct iio_chan_spec *chan_array; > + int bit, idx = 0; > + > + idev->num_channels = bitmap_weight(&(pdata->channels_used), > + pdata->num_channels); > + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), > + GFP_KERNEL); > + > + if (chan_array == NULL) > + return -ENOMEM; > + > + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { > + struct iio_chan_spec *chan = chan_array + idx; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = bit; > + chan->scan_type.sign = 's'; It's an unsigned value that you're reading from the ADC, so maybe this should be = 'u'. > +static void at91adc_channel_remove(struct iio_dev *idev) > +{ > + if (idev->channels == NULL) > + return; > + > + kfree(idev->channels); kfree() is fine with having its argument being NULL, so the if test is useless here. See http://lxr.free-electrons.com/source/mm/slab.c#L3863. > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + unsigned int scale_uv; > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st->reg_base, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_START); > + > + wait_event_interruptible_timeout(st->wq_data_avail, st->done, > + msecs_to_jiffies(10 * 1000)); You forgot to handle the cases where wait_event_interruptible_timeout() exits because of a signal or because of the timeout, and make the assumption that the wait_event_interruptible_timeout() is also successful. > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st->reg_base, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st->reg_base, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); You do a clk_get()/clk_enable() here but nowhere in the ->remove() function you do a clk_disable()/clk_put(). > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; [...] > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; Both of those computations would be better with a small comment explaining what's happening. > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + at91adc_channel_remove(idev); > + iio_device_unregister(idev); > + free_irq(st->irq, idev); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_free(idev); As said above, clk_disable()/clk_put() is missing. > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, Nitpick: wrongly placed parenthesis. Regards, Thomas -- Thomas Petazzoni, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-07 16:08 ` Maxime Ripard @ 2011-11-07 16:08 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 11 ++++++ 2 files changed, 63 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..0859553 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + if (test_bit(0, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC0, 0); + if (test_bit(1, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC1, 0); + if (test_bit(2, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC2, 0); + if (test_bit(3, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC3, 0); + + data->adc_clock = 5000000; + data->num_channels = 4; + data->startup_time = 10; + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..e34d41a 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3), + .vref = 3300, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +398,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-11-07 16:08 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-07 16:08 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 11 ++++++ 2 files changed, 63 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..0859553 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + if (test_bit(0, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC0, 0); + if (test_bit(1, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC1, 0); + if (test_bit(2, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC2, 0); + if (test_bit(3, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC3, 0); + + data->adc_clock = 5000000; + data->num_channels = 4; + data->startup_time = 10; + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..e34d41a 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3), + .vref = 3300, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +398,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCHv4] AT91: Add a driver for the ADC 2011-10-19 16:18 ` Maxime Ripard @ 2011-11-09 10:19 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The core" patchset from Jonathan Cameron, applied on top of 3.1 Improvements since v3: - Various fixes (Remove the clock at drivers's remove, change the registers access functions prototypes,... ) Improvements since v2: - Move the driver out of staging - Slightly modify the definition of channels to use bitmaps - The driver no longer leaks the channels array - Various minor fixes and codestyle improvements Improvements since v1: - Rebase on top of commit 85d8ff8 - Initialise scan_types - Added scale informations in the driver - Allow to use only a subset of adc channels available on the SoC - Various fix according to reviews Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCHv4] AT91: Add a driver for the ADC @ 2011-11-09 10:19 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel Hi all, This patchset adds the driver for the ADC in the AT91 SoC. It has been tested on the at91sam9g20ek and should work on sam9g45 as well. For now, it only reads values when asked for by sysfs, but eventually will support hardware triggers and more boards. This patchset is based on the "[PATCH 0/6 V2] IIO: Out of staging step 1: The core" patchset from Jonathan Cameron, applied on top of 3.1 Improvements since v3: - Various fixes (Remove the clock at drivers's remove, change the registers access functions prototypes,... ) Improvements since v2: - Move the driver out of staging - Slightly modify the definition of channels to use bitmaps - The driver no longer leaks the channels array - Various minor fixes and codestyle improvements Improvements since v1: - Rebase on top of commit 85d8ff8 - Initialise scan_types - Added scale informations in the driver - Allow to use only a subset of adc channels available on the SoC - Various fix according to reviews Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-09 10:19 ` Maxime Ripard @ 2011-11-09 10:19 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-09 10:19 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-09 10:19 ` Maxime Ripard @ 2011-11-09 10:19 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/Makefile | 4 +- drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+), 1 deletions(-) create mode 100644 drivers/iio/adc/at91adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3d97b21..74f4d9f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -14,6 +14,12 @@ config IIO_AD799X i2c analog to digital convertors (ADC). Provides direct access via sysfs. +config IIO_AT91ADC + tristate "Atmel AT91 ADC" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel AT91 ADC. + config IIO_MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c197334..776b56f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o iio_max1363-y := max1363_core.o -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o \ No newline at end of file +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o + +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c new file mode 100644 index 0000000..6aef5d6 --- /dev/null +++ b/drivers/iio/adc/at91adc.c @@ -0,0 +1,326 @@ +/* + * Driver for the ADC present in the Atmel AT91 evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include <linux/iio/iio.h> + +#include <mach/at91_adc.h> +#include <mach/board.h> + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + int irq; + wait_queue_head_t wq_data_avail; + u16 last_value; + void __iomem *reg_base; + unsigned int vref_mv; +}; + +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) +{ + return readl_relaxed(st->reg_base + reg); +} + +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) +{ + writel_relaxed(val, st->reg_base + reg); +} + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct iio_dev *idev = private; + struct at91adc_state *st = iio_priv(idev); + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < idev->num_channels; chan++) + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->last_value = at91adc_reg_read(st, + AT91_ADC_CHR(chan)); + } + + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct iio_dev *idev, + struct at91_adc_data *pdata) +{ + struct iio_chan_spec *chan_array; + int bit, idx = 0; + + idev->num_channels = bitmap_weight(&(pdata->channels_used), + pdata->num_channels); + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), + GFP_KERNEL); + + if (chan_array == NULL) + return -ENOMEM; + + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { + struct iio_chan_spec *chan = chan_array + idx; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 10; + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; + ++idx; + } + + idev->channels = chan_array; + return idev->num_channels; +} + +static void at91adc_channel_remove(struct iio_dev *idev) +{ + kfree(idev->channels); +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + unsigned int scale_uv; + short ret; + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); + + ret = wait_event_interruptible_timeout(st->wq_data_avail, + st->done, + msecs_to_jiffies(1000)); + if (ret == -ERESTARTSYS) + break; + + *val = st->last_value; + + at91adc_reg_write(st, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->last_value = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_device_allocate(sizeof(struct at91adc_state)); + if (idev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = dev_name(&pdev->dev); + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + /* + * Prescaler rate computation using the formula from the Atmel's + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being + * specified by the electrical characteristics of the board. + */ + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + /* + * Number of ticks needed to cover the startup time of the ADC as + * defined in the electrical characteristics of the board, divided by 8. + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock + */ + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + ret = at91adc_channel_init(idev, pdata); + if (ret < 0) + goto error_free_clk; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + st->vref_mv = pdata->vref; + + ret = iio_device_register(idev); + if (ret < 0) + goto error_free_channels; + + return 0; + +error_free_channels: + at91adc_channel_remove(idev); +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_device_free(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + iio_device_unregister(idev); + at91adc_channel_remove(idev); + clk_disable(st->clk); + clk_put(st->clk); + free_irq(st->irq, idev); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_device_free(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +module_platform_driver(at91adc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-09 10:19 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/Makefile | 4 +- drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+), 1 deletions(-) create mode 100644 drivers/iio/adc/at91adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 3d97b21..74f4d9f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -14,6 +14,12 @@ config IIO_AD799X i2c analog to digital convertors (ADC). Provides direct access via sysfs. +config IIO_AT91ADC + tristate "Atmel AT91 ADC" + depends on SYSFS && ARCH_AT91 + help + Say yes here to build support for Atmel AT91 ADC. + config IIO_MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index c197334..776b56f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o iio_max1363-y := max1363_core.o -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o \ No newline at end of file +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o + +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c new file mode 100644 index 0000000..6aef5d6 --- /dev/null +++ b/drivers/iio/adc/at91adc.c @@ -0,0 +1,326 @@ +/* + * Driver for the ADC present in the Atmel AT91 evaluation boards. + * + * Copyright 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include <linux/iio/iio.h> + +#include <mach/at91_adc.h> +#include <mach/board.h> + +struct at91adc_state { + struct clk *clk; + bool done; + struct mutex lock; + int irq; + wait_queue_head_t wq_data_avail; + u16 last_value; + void __iomem *reg_base; + unsigned int vref_mv; +}; + +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) +{ + return readl_relaxed(st->reg_base + reg); +} + +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) +{ + writel_relaxed(val, st->reg_base + reg); +} + +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) +{ + int chan; + struct iio_dev *idev = private; + struct at91adc_state *st = iio_priv(idev); + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); + + if (!(status & AT91_ADC_DRDY)) + return IRQ_HANDLED; + + for (chan = 0; chan < idev->num_channels; chan++) + if (status & AT91_ADC_EOC(chan)) { + st->done = true; + st->last_value = at91adc_reg_read(st, + AT91_ADC_CHR(chan)); + } + + wake_up_interruptible(&st->wq_data_avail); + + return IRQ_HANDLED; +} + +static int at91adc_channel_init(struct iio_dev *idev, + struct at91_adc_data *pdata) +{ + struct iio_chan_spec *chan_array; + int bit, idx = 0; + + idev->num_channels = bitmap_weight(&(pdata->channels_used), + pdata->num_channels); + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), + GFP_KERNEL); + + if (chan_array == NULL) + return -ENOMEM; + + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { + struct iio_chan_spec *chan = chan_array + idx; + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 10; + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; + ++idx; + } + + idev->channels = chan_array; + return idev->num_channels; +} + +static void at91adc_channel_remove(struct iio_dev *idev) +{ + kfree(idev->channels); +} + +static int at91adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91adc_state *st = iio_priv(idev); + unsigned int scale_uv; + short ret; + + switch (mask) { + case 0: + mutex_lock(&st->lock); + + at91adc_reg_write(st, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st, AT91_ADC_IER, + AT91_ADC_EOC(chan->channel)); + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); + + ret = wait_event_interruptible_timeout(st->wq_data_avail, + st->done, + msecs_to_jiffies(1000)); + if (ret == -ERESTARTSYS) + break; + + *val = st->last_value; + + at91adc_reg_write(st, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + at91adc_reg_write(st, AT91_ADC_IDR, + AT91_ADC_EOC(chan->channel)); + + st->last_value = 0; + st->done = false; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; + *val = scale_uv / 1000; + *val2 = (scale_uv % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info at91adc_info = { + .driver_module = THIS_MODULE, + .read_raw = &at91adc_read_raw, +}; + +static int __devinit at91adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks; + int ret; + struct iio_dev *idev; + struct at91adc_state *st; + struct resource *res; + struct at91_adc_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No resource defined\n"); + ret = -ENXIO; + goto error_ret; + } + + idev = iio_device_allocate(sizeof(struct at91adc_state)); + if (idev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + platform_set_drvdata(pdev, idev); + + idev->dev.parent = &pdev->dev; + idev->name = dev_name(&pdev->dev); + idev->info = &at91adc_info; + + st = iio_priv(idev); + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) { + dev_err(&pdev->dev, "No IRQ ID is designated\n"); + ret = -ENODEV; + goto error_free_device; + } + + if (!request_mem_region(res->start, resource_size(res), + "AT91 adc registers")) { + dev_err(&pdev->dev, "Resources are unavailable.\n"); + ret = -EBUSY; + goto error_free_device; + } + + st->reg_base = ioremap(res->start, resource_size(res)); + if (!st->reg_base) { + dev_err(&pdev->dev, "Failed to map registers.\n"); + ret = -ENOMEM; + goto error_release_mem; + } + + /* + * Disable all IRQs before setting up the handler + */ + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); + ret = request_irq(st->irq, + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + goto error_unmap_reg; + } + + st->clk = clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + clk_enable(st->clk); + mstrclk = clk_get_rate(st->clk); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + if (!pdata->adc_clock) { + dev_err(&pdev->dev, "No ADCClock available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + /* + * Prescaler rate computation using the formula from the Atmel's + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being + * specified by the electrical characteristics of the board. + */ + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; + + if (!pdata->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_free_clk; + } + + /* + * Number of ticks needed to cover the startup time of the ADC as + * defined in the electrical characteristics of the board, divided by 8. + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock + */ + ticks = round_up((pdata->startup_time * pdata->adc_clock / + 1000000) - 1, 8) / 8; + at91adc_reg_write(st, AT91_ADC_MR, + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); + + /* Setup the ADC channels available on the board */ + ret = at91adc_channel_init(idev, pdata); + if (ret < 0) + goto error_free_clk; + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + st->vref_mv = pdata->vref; + + ret = iio_device_register(idev); + if (ret < 0) + goto error_free_channels; + + return 0; + +error_free_channels: + at91adc_channel_remove(idev); +error_free_clk: + clk_disable(st->clk); + clk_put(st->clk); +error_free_irq: + free_irq(st->irq, st); +error_unmap_reg: + iounmap(st->reg_base); +error_release_mem: + release_mem_region(res->start, resource_size(res)); +error_free_device: + iio_device_free(idev); +error_ret: + return ret; +} + +static int __devexit at91adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct at91adc_state *st = iio_priv(idev); + + iio_device_unregister(idev); + at91adc_channel_remove(idev); + clk_disable(st->clk); + clk_put(st->clk); + free_irq(st->irq, idev); + iounmap(st->reg_base); + release_mem_region(res->start, resource_size(res)); + iio_device_free(idev); + + return 0; +} + +static struct platform_driver at91adc_driver = { + .probe = at91adc_probe, + .remove = __devexit_p(at91adc_remove), + .driver = { + .name = "at91adc", + }, +}; + +module_platform_driver(at91adc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-09 10:19 ` Maxime Ripard @ 2011-11-10 17:35 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-10 17:35 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Thomas Petazzoni On 11/09/2011 10:19 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > One little comment / query inline. I'm happy with this either way though. Beware as you are dependent on a series under review though and it's always possible little things in there might change! > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > drivers/iio/adc/Kconfig | 6 + > drivers/iio/adc/Makefile | 4 +- > drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 335 insertions(+), 1 deletions(-) > create mode 100644 drivers/iio/adc/at91adc.c > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 3d97b21..74f4d9f 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -14,6 +14,12 @@ config IIO_AD799X > i2c analog to digital convertors (ADC). Provides direct access > via sysfs. > > +config IIO_AT91ADC > + tristate "Atmel AT91 ADC" > + depends on SYSFS && ARCH_AT91 > + help > + Say yes here to build support for Atmel AT91 ADC. > + > config IIO_MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index c197334..776b56f 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o > obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o > > iio_max1363-y := max1363_core.o > -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o > \ No newline at end of file > +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o > + > +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o > diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c > new file mode 100644 > index 0000000..6aef5d6 > --- /dev/null > +++ b/drivers/iio/adc/at91adc.c > @@ -0,0 +1,326 @@ > +/* > + * Driver for the ADC present in the Atmel AT91 evaluation boards. > + * > + * Copyright 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + */ > + > +#include <linux/bitmap.h> > +#include <linux/bitops.h> > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > + > +#include <linux/iio/iio.h> > + > +#include <mach/at91_adc.h> > +#include <mach/board.h> > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; > + int irq; > + wait_queue_head_t wq_data_avail; > + u16 last_value; > + void __iomem *reg_base; > + unsigned int vref_mv; > +}; > + > +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) > +{ > + return readl_relaxed(st->reg_base + reg); > +} > + > +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) > +{ > + writel_relaxed(val, st->reg_base + reg); > +} > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct iio_dev *idev = private; > + struct at91adc_state *st = iio_priv(idev); > + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < idev->num_channels; chan++) Given we know only one bit should be set, it really feels like there should be a cleaner way of doing this. Could it use find_first_bit(&status, idev->num_channels)? It kind of bypasses the defines though so it's far from clean in that sense. > + if (status & AT91_ADC_EOC(chan)) { The logic in here very much assumes that only one channel is being converted and the interrupt indicates that one of the channels has finished. Hence logically this st->done doesn't need to be in the loop. > + st->done = true; > + st->last_value = at91adc_reg_read(st, > + AT91_ADC_CHR(chan)); > + } > + > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct iio_dev *idev, > + struct at91_adc_data *pdata) > +{ > + struct iio_chan_spec *chan_array; > + int bit, idx = 0; > + > + idev->num_channels = bitmap_weight(&(pdata->channels_used), > + pdata->num_channels); > + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), > + GFP_KERNEL); > + > + if (chan_array == NULL) > + return -ENOMEM; > + > + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { > + struct iio_chan_spec *chan = chan_array + idx; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = bit; > + chan->scan_type.sign = 'u'; > + chan->scan_type.realbits = 10; > + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; > + ++idx; > + } > + > + idev->channels = chan_array; > + return idev->num_channels; > +} > + > +static void at91adc_channel_remove(struct iio_dev *idev) > +{ > + kfree(idev->channels); > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + unsigned int scale_uv; > + short ret; > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); > + > + ret = wait_event_interruptible_timeout(st->wq_data_avail, > + st->done, > + msecs_to_jiffies(1000)); > + if (ret == -ERESTARTSYS) > + break; > + > + *val = st->last_value; > + > + at91adc_reg_write(st, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->last_value = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + > + case IIO_CHAN_INFO_SCALE: > + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; > + *val = scale_uv / 1000; > + *val2 = (scale_uv % 1000) * 1000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_device_allocate(sizeof(struct at91adc_state)); > + if (idev == NULL) { > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = dev_name(&pdev->dev); > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + /* > + * Prescaler rate computation using the formula from the Atmel's > + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being > + * specified by the electrical characteristics of the board. > + */ > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; > + > + if (!pdata->startup_time) { > + dev_err(&pdev->dev, "No startup time available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + /* > + * Number of ticks needed to cover the startup time of the ADC as > + * defined in the electrical characteristics of the board, divided by 8. > + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock > + */ > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; > + at91adc_reg_write(st, AT91_ADC_MR, > + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | > + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); > + > + /* Setup the ADC channels available on the board */ > + ret = at91adc_channel_init(idev, pdata); > + if (ret < 0) > + goto error_free_clk; > + > + init_waitqueue_head(&st->wq_data_avail); > + mutex_init(&st->lock); > + > + st->vref_mv = pdata->vref; > + > + ret = iio_device_register(idev); > + if (ret < 0) > + goto error_free_channels; > + > + return 0; > + > +error_free_channels: > + at91adc_channel_remove(idev); > +error_free_clk: > + clk_disable(st->clk); > + clk_put(st->clk); > +error_free_irq: > + free_irq(st->irq, st); > +error_unmap_reg: > + iounmap(st->reg_base); > +error_release_mem: > + release_mem_region(res->start, resource_size(res)); > +error_free_device: > + iio_device_free(idev); > +error_ret: > + return ret; > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + iio_device_unregister(idev); > + at91adc_channel_remove(idev); > + clk_disable(st->clk); > + clk_put(st->clk); > + free_irq(st->irq, idev); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_free(idev); > + > + return 0; > +} > + > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, > +}; > + > +module_platform_driver(at91adc_driver); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-10 17:35 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-10 17:35 UTC (permalink / raw) To: linux-arm-kernel On 11/09/2011 10:19 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > One little comment / query inline. I'm happy with this either way though. Beware as you are dependent on a series under review though and it's always possible little things in there might change! > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > drivers/iio/adc/Kconfig | 6 + > drivers/iio/adc/Makefile | 4 +- > drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 335 insertions(+), 1 deletions(-) > create mode 100644 drivers/iio/adc/at91adc.c > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 3d97b21..74f4d9f 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -14,6 +14,12 @@ config IIO_AD799X > i2c analog to digital convertors (ADC). Provides direct access > via sysfs. > > +config IIO_AT91ADC > + tristate "Atmel AT91 ADC" > + depends on SYSFS && ARCH_AT91 > + help > + Say yes here to build support for Atmel AT91 ADC. > + > config IIO_MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index c197334..776b56f 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o > obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o > > iio_max1363-y := max1363_core.o > -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o > \ No newline at end of file > +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o > + > +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o > diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c > new file mode 100644 > index 0000000..6aef5d6 > --- /dev/null > +++ b/drivers/iio/adc/at91adc.c > @@ -0,0 +1,326 @@ > +/* > + * Driver for the ADC present in the Atmel AT91 evaluation boards. > + * > + * Copyright 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + */ > + > +#include <linux/bitmap.h> > +#include <linux/bitops.h> > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/interrupt.h> > +#include <linux/jiffies.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/wait.h> > + > +#include <linux/iio/iio.h> > + > +#include <mach/at91_adc.h> > +#include <mach/board.h> > + > +struct at91adc_state { > + struct clk *clk; > + bool done; > + struct mutex lock; > + int irq; > + wait_queue_head_t wq_data_avail; > + u16 last_value; > + void __iomem *reg_base; > + unsigned int vref_mv; > +}; > + > +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) > +{ > + return readl_relaxed(st->reg_base + reg); > +} > + > +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) > +{ > + writel_relaxed(val, st->reg_base + reg); > +} > + > +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) > +{ > + int chan; > + struct iio_dev *idev = private; > + struct at91adc_state *st = iio_priv(idev); > + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); > + > + if (!(status & AT91_ADC_DRDY)) > + return IRQ_HANDLED; > + > + for (chan = 0; chan < idev->num_channels; chan++) Given we know only one bit should be set, it really feels like there should be a cleaner way of doing this. Could it use find_first_bit(&status, idev->num_channels)? It kind of bypasses the defines though so it's far from clean in that sense. > + if (status & AT91_ADC_EOC(chan)) { The logic in here very much assumes that only one channel is being converted and the interrupt indicates that one of the channels has finished. Hence logically this st->done doesn't need to be in the loop. > + st->done = true; > + st->last_value = at91adc_reg_read(st, > + AT91_ADC_CHR(chan)); > + } > + > + wake_up_interruptible(&st->wq_data_avail); > + > + return IRQ_HANDLED; > +} > + > +static int at91adc_channel_init(struct iio_dev *idev, > + struct at91_adc_data *pdata) > +{ > + struct iio_chan_spec *chan_array; > + int bit, idx = 0; > + > + idev->num_channels = bitmap_weight(&(pdata->channels_used), > + pdata->num_channels); > + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), > + GFP_KERNEL); > + > + if (chan_array == NULL) > + return -ENOMEM; > + > + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { > + struct iio_chan_spec *chan = chan_array + idx; > + chan->type = IIO_VOLTAGE; > + chan->indexed = 1; > + chan->channel = bit; > + chan->scan_type.sign = 'u'; > + chan->scan_type.realbits = 10; > + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; > + ++idx; > + } > + > + idev->channels = chan_array; > + return idev->num_channels; > +} > + > +static void at91adc_channel_remove(struct iio_dev *idev) > +{ > + kfree(idev->channels); > +} > + > +static int at91adc_read_raw(struct iio_dev *idev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct at91adc_state *st = iio_priv(idev); > + unsigned int scale_uv; > + short ret; > + > + switch (mask) { > + case 0: > + mutex_lock(&st->lock); > + > + at91adc_reg_write(st, AT91_ADC_CHER, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st, AT91_ADC_IER, > + AT91_ADC_EOC(chan->channel)); > + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); > + > + ret = wait_event_interruptible_timeout(st->wq_data_avail, > + st->done, > + msecs_to_jiffies(1000)); > + if (ret == -ERESTARTSYS) > + break; > + > + *val = st->last_value; > + > + at91adc_reg_write(st, AT91_ADC_CHDR, > + AT91_ADC_CH(chan->channel)); > + at91adc_reg_write(st, AT91_ADC_IDR, > + AT91_ADC_EOC(chan->channel)); > + > + st->last_value = 0; > + st->done = false; > + mutex_unlock(&st->lock); > + return IIO_VAL_INT; > + > + case IIO_CHAN_INFO_SCALE: > + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; > + *val = scale_uv / 1000; > + *val2 = (scale_uv % 1000) * 1000; > + return IIO_VAL_INT_PLUS_MICRO; > + default: > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info at91adc_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &at91adc_read_raw, > +}; > + > +static int __devinit at91adc_probe(struct platform_device *pdev) > +{ > + unsigned int prsc, mstrclk, ticks; > + int ret; > + struct iio_dev *idev; > + struct at91adc_state *st; > + struct resource *res; > + struct at91_adc_data *pdata = pdev->dev.platform_data; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "No resource defined\n"); > + ret = -ENXIO; > + goto error_ret; > + } > + > + idev = iio_device_allocate(sizeof(struct at91adc_state)); > + if (idev == NULL) { > + ret = -ENOMEM; > + goto error_ret; > + } > + platform_set_drvdata(pdev, idev); > + > + idev->dev.parent = &pdev->dev; > + idev->name = dev_name(&pdev->dev); > + idev->info = &at91adc_info; > + > + st = iio_priv(idev); > + st->irq = platform_get_irq(pdev, 0); > + if (st->irq < 0) { > + dev_err(&pdev->dev, "No IRQ ID is designated\n"); > + ret = -ENODEV; > + goto error_free_device; > + } > + > + if (!request_mem_region(res->start, resource_size(res), > + "AT91 adc registers")) { > + dev_err(&pdev->dev, "Resources are unavailable.\n"); > + ret = -EBUSY; > + goto error_free_device; > + } > + > + st->reg_base = ioremap(res->start, resource_size(res)); > + if (!st->reg_base) { > + dev_err(&pdev->dev, "Failed to map registers.\n"); > + ret = -ENOMEM; > + goto error_release_mem; > + } > + > + /* > + * Disable all IRQs before setting up the handler > + */ > + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); > + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); > + ret = request_irq(st->irq, > + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); > + if (ret) { > + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); > + goto error_unmap_reg; > + } > + > + st->clk = clk_get(&pdev->dev, "adc_clk"); > + if (IS_ERR(st->clk)) { > + dev_err(&pdev->dev, "Failed to get the clock.\n"); > + ret = PTR_ERR(st->clk); > + goto error_free_irq; > + } > + > + clk_enable(st->clk); > + mstrclk = clk_get_rate(st->clk); > + > + if (!pdata) { > + dev_err(&pdev->dev, "No platform data available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + if (!pdata->adc_clock) { > + dev_err(&pdev->dev, "No ADCClock available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + /* > + * Prescaler rate computation using the formula from the Atmel's > + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being > + * specified by the electrical characteristics of the board. > + */ > + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; > + > + if (!pdata->startup_time) { > + dev_err(&pdev->dev, "No startup time available.\n"); > + ret = -EINVAL; > + goto error_free_clk; > + } > + > + /* > + * Number of ticks needed to cover the startup time of the ADC as > + * defined in the electrical characteristics of the board, divided by 8. > + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock > + */ > + ticks = round_up((pdata->startup_time * pdata->adc_clock / > + 1000000) - 1, 8) / 8; > + at91adc_reg_write(st, AT91_ADC_MR, > + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | > + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); > + > + /* Setup the ADC channels available on the board */ > + ret = at91adc_channel_init(idev, pdata); > + if (ret < 0) > + goto error_free_clk; > + > + init_waitqueue_head(&st->wq_data_avail); > + mutex_init(&st->lock); > + > + st->vref_mv = pdata->vref; > + > + ret = iio_device_register(idev); > + if (ret < 0) > + goto error_free_channels; > + > + return 0; > + > +error_free_channels: > + at91adc_channel_remove(idev); > +error_free_clk: > + clk_disable(st->clk); > + clk_put(st->clk); > +error_free_irq: > + free_irq(st->irq, st); > +error_unmap_reg: > + iounmap(st->reg_base); > +error_release_mem: > + release_mem_region(res->start, resource_size(res)); > +error_free_device: > + iio_device_free(idev); > +error_ret: > + return ret; > +} > + > +static int __devexit at91adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *idev = platform_get_drvdata(pdev); > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct at91adc_state *st = iio_priv(idev); > + > + iio_device_unregister(idev); > + at91adc_channel_remove(idev); > + clk_disable(st->clk); > + clk_put(st->clk); > + free_irq(st->irq, idev); > + iounmap(st->reg_base); > + release_mem_region(res->start, resource_size(res)); > + iio_device_free(idev); > + > + return 0; > +} > + > +static struct platform_driver at91adc_driver = { > + .probe = at91adc_probe, > + .remove = __devexit_p(at91adc_remove), > + .driver = { > + .name = "at91adc", > + }, > +}; > + > +module_platform_driver(at91adc_driver); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); > +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-10 17:35 ` Jonathan Cameron @ 2011-11-11 12:34 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-11 12:34 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Thomas Petazzoni On 11/10/2011 05:35 PM, Jonathan Cameron wrote: > On 11/09/2011 10:19 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> >> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >> > One little comment / query inline. I'm happy with this either way > though. Beware as you are dependent on a series under review though > and it's always possible little things in there might change! > >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > >> --- >> drivers/iio/adc/Kconfig | 6 + >> drivers/iio/adc/Makefile | 4 +- >> drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 335 insertions(+), 1 deletions(-) >> create mode 100644 drivers/iio/adc/at91adc.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 3d97b21..74f4d9f 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -14,6 +14,12 @@ config IIO_AD799X >> i2c analog to digital convertors (ADC). Provides direct access >> via sysfs. >> >> +config IIO_AT91ADC >> + tristate "Atmel AT91 ADC" >> + depends on SYSFS && ARCH_AT91 >> + help >> + Say yes here to build support for Atmel AT91 ADC. >> + >> config IIO_MAX1363 >> tristate "Maxim max1363 ADC driver" >> depends on I2C >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index c197334..776b56f 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o >> obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o >> >> iio_max1363-y := max1363_core.o >> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> \ No newline at end of file >> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> + >> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o >> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..6aef5d6 >> --- /dev/null >> +++ b/drivers/iio/adc/at91adc.c >> @@ -0,0 +1,326 @@ >> +/* >> + * Driver for the ADC present in the Atmel AT91 evaluation boards. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/bitmap.h> >> +#include <linux/bitops.h> >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include <linux/iio/iio.h> >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 last_value; >> + void __iomem *reg_base; >> + unsigned int vref_mv; >> +}; >> + >> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) >> +{ >> + return readl_relaxed(st->reg_base + reg); >> +} >> + >> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) >> +{ >> + writel_relaxed(val, st->reg_base + reg); >> +} >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct iio_dev *idev = private; >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < idev->num_channels; chan++) > Given we know only one bit should be set, it really feels like there > should be > a cleaner way of doing this. Could it use find_first_bit(&status, > idev->num_channels)? > It kind of bypasses the defines though so it's far from clean in that sense. >> + if (status & AT91_ADC_EOC(chan)) { > > The logic in here very much assumes that only one channel is being converted > and the interrupt indicates that one of the channels has finished. > Hence logically this st->done doesn't need to be in the loop. > Having had a look at the datasheet, could you just check against the whole mask and use the lastdata converted register? Should under current conditions always give you the right thing I think? >> + st->done = true; >> + st->last_value = at91adc_reg_read(st, >> + AT91_ADC_CHR(chan)); >> + } >> + >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct iio_dev *idev, >> + struct at91_adc_data *pdata) >> +{ >> + struct iio_chan_spec *chan_array; >> + int bit, idx = 0; >> + >> + idev->num_channels = bitmap_weight(&(pdata->channels_used), >> + pdata->num_channels); >> + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), >> + GFP_KERNEL); >> + >> + if (chan_array == NULL) >> + return -ENOMEM; >> + >> + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { >> + struct iio_chan_spec *chan = chan_array + idx; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = bit; >> + chan->scan_type.sign = 'u'; >> + chan->scan_type.realbits = 10; >> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >> + ++idx; >> + } >> + >> + idev->channels = chan_array; >> + return idev->num_channels; >> +} >> + >> +static void at91adc_channel_remove(struct iio_dev *idev) >> +{ >> + kfree(idev->channels); >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int scale_uv; >> + short ret; >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); >> + >> + ret = wait_event_interruptible_timeout(st->wq_data_avail, >> + st->done, >> + msecs_to_jiffies(1000)); >> + if (ret == -ERESTARTSYS) >> + break; >> + >> + *val = st->last_value; >> + >> + at91adc_reg_write(st, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->last_value = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_SCALE: >> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >> + *val = scale_uv / 1000; >> + *val2 = (scale_uv % 1000) * 1000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_device_allocate(sizeof(struct at91adc_state)); >> + if (idev == NULL) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = dev_name(&pdev->dev); >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Prescaler rate computation using the formula from the Atmel's >> + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being >> + * specified by the electrical characteristics of the board. >> + */ >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Number of ticks needed to cover the startup time of the ADC as >> + * defined in the electrical characteristics of the board, divided by 8. >> + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock >> + */ >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + ret = at91adc_channel_init(idev, pdata); >> + if (ret < 0) >> + goto error_free_clk; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + st->vref_mv = pdata->vref; >> + >> + ret = iio_device_register(idev); >> + if (ret < 0) >> + goto error_free_channels; >> + >> + return 0; >> + >> +error_free_channels: >> + at91adc_channel_remove(idev); >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_device_free(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + iio_device_unregister(idev); >> + at91adc_channel_remove(idev); >> + clk_disable(st->clk); >> + clk_put(st->clk); >> + free_irq(st->irq, idev); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_free(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +module_platform_driver(at91adc_driver); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-11 12:34 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-11 12:34 UTC (permalink / raw) To: linux-arm-kernel On 11/10/2011 05:35 PM, Jonathan Cameron wrote: > On 11/09/2011 10:19 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> >> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >> > One little comment / query inline. I'm happy with this either way > though. Beware as you are dependent on a series under review though > and it's always possible little things in there might change! > >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > >> --- >> drivers/iio/adc/Kconfig | 6 + >> drivers/iio/adc/Makefile | 4 +- >> drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 335 insertions(+), 1 deletions(-) >> create mode 100644 drivers/iio/adc/at91adc.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 3d97b21..74f4d9f 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -14,6 +14,12 @@ config IIO_AD799X >> i2c analog to digital convertors (ADC). Provides direct access >> via sysfs. >> >> +config IIO_AT91ADC >> + tristate "Atmel AT91 ADC" >> + depends on SYSFS && ARCH_AT91 >> + help >> + Say yes here to build support for Atmel AT91 ADC. >> + >> config IIO_MAX1363 >> tristate "Maxim max1363 ADC driver" >> depends on I2C >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index c197334..776b56f 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o >> obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o >> >> iio_max1363-y := max1363_core.o >> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> \ No newline at end of file >> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> + >> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o >> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..6aef5d6 >> --- /dev/null >> +++ b/drivers/iio/adc/at91adc.c >> @@ -0,0 +1,326 @@ >> +/* >> + * Driver for the ADC present in the Atmel AT91 evaluation boards. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/bitmap.h> >> +#include <linux/bitops.h> >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include <linux/iio/iio.h> >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 last_value; >> + void __iomem *reg_base; >> + unsigned int vref_mv; >> +}; >> + >> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) >> +{ >> + return readl_relaxed(st->reg_base + reg); >> +} >> + >> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) >> +{ >> + writel_relaxed(val, st->reg_base + reg); >> +} >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct iio_dev *idev = private; >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < idev->num_channels; chan++) > Given we know only one bit should be set, it really feels like there > should be > a cleaner way of doing this. Could it use find_first_bit(&status, > idev->num_channels)? > It kind of bypasses the defines though so it's far from clean in that sense. >> + if (status & AT91_ADC_EOC(chan)) { > > The logic in here very much assumes that only one channel is being converted > and the interrupt indicates that one of the channels has finished. > Hence logically this st->done doesn't need to be in the loop. > Having had a look at the datasheet, could you just check against the whole mask and use the lastdata converted register? Should under current conditions always give you the right thing I think? >> + st->done = true; >> + st->last_value = at91adc_reg_read(st, >> + AT91_ADC_CHR(chan)); >> + } >> + >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct iio_dev *idev, >> + struct at91_adc_data *pdata) >> +{ >> + struct iio_chan_spec *chan_array; >> + int bit, idx = 0; >> + >> + idev->num_channels = bitmap_weight(&(pdata->channels_used), >> + pdata->num_channels); >> + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), >> + GFP_KERNEL); >> + >> + if (chan_array == NULL) >> + return -ENOMEM; >> + >> + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { >> + struct iio_chan_spec *chan = chan_array + idx; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = bit; >> + chan->scan_type.sign = 'u'; >> + chan->scan_type.realbits = 10; >> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >> + ++idx; >> + } >> + >> + idev->channels = chan_array; >> + return idev->num_channels; >> +} >> + >> +static void at91adc_channel_remove(struct iio_dev *idev) >> +{ >> + kfree(idev->channels); >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int scale_uv; >> + short ret; >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); >> + >> + ret = wait_event_interruptible_timeout(st->wq_data_avail, >> + st->done, >> + msecs_to_jiffies(1000)); >> + if (ret == -ERESTARTSYS) >> + break; >> + >> + *val = st->last_value; >> + >> + at91adc_reg_write(st, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->last_value = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_SCALE: >> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >> + *val = scale_uv / 1000; >> + *val2 = (scale_uv % 1000) * 1000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_device_allocate(sizeof(struct at91adc_state)); >> + if (idev == NULL) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = dev_name(&pdev->dev); >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Prescaler rate computation using the formula from the Atmel's >> + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being >> + * specified by the electrical characteristics of the board. >> + */ >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Number of ticks needed to cover the startup time of the ADC as >> + * defined in the electrical characteristics of the board, divided by 8. >> + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock >> + */ >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + ret = at91adc_channel_init(idev, pdata); >> + if (ret < 0) >> + goto error_free_clk; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + st->vref_mv = pdata->vref; >> + >> + ret = iio_device_register(idev); >> + if (ret < 0) >> + goto error_free_channels; >> + >> + return 0; >> + >> +error_free_channels: >> + at91adc_channel_remove(idev); >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_device_free(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + iio_device_unregister(idev); >> + at91adc_channel_remove(idev); >> + clk_disable(st->clk); >> + clk_put(st->clk); >> + free_irq(st->irq, idev); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_free(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +module_platform_driver(at91adc_driver); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-11 12:34 ` Jonathan Cameron @ 2011-11-14 9:59 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 9:59 UTC (permalink / raw) To: Jonathan Cameron Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Thomas Petazzoni On 11/11/2011 13:34, Jonathan Cameron wrote: > On 11/10/2011 05:35 PM, Jonathan Cameron wrote: >> On 11/09/2011 10:19 AM, Maxime Ripard wrote: >>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> >>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >>> >> One little comment / query inline. I'm happy with this either way >> though. Beware as you are dependent on a series under review though >> and it's always possible little things in there might change! >> >>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> >> >>> --- >>> drivers/iio/adc/Kconfig | 6 + >>> drivers/iio/adc/Makefile | 4 +- >>> drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ >>> 3 files changed, 335 insertions(+), 1 deletions(-) >>> create mode 100644 drivers/iio/adc/at91adc.c >>> >>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >>> index 3d97b21..74f4d9f 100644 >>> --- a/drivers/iio/adc/Kconfig >>> +++ b/drivers/iio/adc/Kconfig >>> @@ -14,6 +14,12 @@ config IIO_AD799X >>> i2c analog to digital convertors (ADC). Provides direct access >>> via sysfs. >>> >>> +config IIO_AT91ADC >>> + tristate "Atmel AT91 ADC" >>> + depends on SYSFS && ARCH_AT91 >>> + help >>> + Say yes here to build support for Atmel AT91 ADC. >>> + >>> config IIO_MAX1363 >>> tristate "Maxim max1363 ADC driver" >>> depends on I2C >>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >>> index c197334..776b56f 100644 >>> --- a/drivers/iio/adc/Makefile >>> +++ b/drivers/iio/adc/Makefile >>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o >>> obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o >>> >>> iio_max1363-y := max1363_core.o >>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >>> \ No newline at end of file >>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >>> + >>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o >>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c >>> new file mode 100644 >>> index 0000000..6aef5d6 >>> --- /dev/null >>> +++ b/drivers/iio/adc/at91adc.c >>> @@ -0,0 +1,326 @@ >>> +/* >>> + * Driver for the ADC present in the Atmel AT91 evaluation boards. >>> + * >>> + * Copyright 2011 Free Electrons >>> + * >>> + * Licensed under the GPLv2 or later. >>> + */ >>> + >>> +#include <linux/bitmap.h> >>> +#include <linux/bitops.h> >>> +#include <linux/clk.h> >>> +#include <linux/err.h> >>> +#include <linux/io.h> >>> +#include <linux/interrupt.h> >>> +#include <linux/jiffies.h> >>> +#include <linux/kernel.h> >>> +#include <linux/module.h> >>> +#include <linux/platform_device.h> >>> +#include <linux/slab.h> >>> +#include <linux/wait.h> >>> + >>> +#include <linux/iio/iio.h> >>> + >>> +#include <mach/at91_adc.h> >>> +#include <mach/board.h> >>> + >>> +struct at91adc_state { >>> + struct clk *clk; >>> + bool done; >>> + struct mutex lock; >>> + int irq; >>> + wait_queue_head_t wq_data_avail; >>> + u16 last_value; >>> + void __iomem *reg_base; >>> + unsigned int vref_mv; >>> +}; >>> + >>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) >>> +{ >>> + return readl_relaxed(st->reg_base + reg); >>> +} >>> + >>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) >>> +{ >>> + writel_relaxed(val, st->reg_base + reg); >>> +} >>> + >>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >>> +{ >>> + int chan; >>> + struct iio_dev *idev = private; >>> + struct at91adc_state *st = iio_priv(idev); >>> + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); >>> + >>> + if (!(status & AT91_ADC_DRDY)) >>> + return IRQ_HANDLED; >>> + >>> + for (chan = 0; chan < idev->num_channels; chan++) >> Given we know only one bit should be set, it really feels like there >> should be >> a cleaner way of doing this. Could it use find_first_bit(&status, >> idev->num_channels)? >> It kind of bypasses the defines though so it's far from clean in that sense. >>> + if (status & AT91_ADC_EOC(chan)) { >> >> The logic in here very much assumes that only one channel is being converted >> and the interrupt indicates that one of the channels has finished. >> Hence logically this st->done doesn't need to be in the loop. >> > Having had a look at the datasheet, could you just check against the > whole mask and use the lastdata converted register? Should under > current conditions always give you the right thing I think? You're right, It will be more consistent and avoid to loop over each channels. I send a new version of the patchset. >>> + st->done = true; >>> + st->last_value = at91adc_reg_read(st, >>> + AT91_ADC_CHR(chan)); >>> + } >>> + >>> + wake_up_interruptible(&st->wq_data_avail); >>> + >>> + return IRQ_HANDLED; >>> +} >>> + >>> +static int at91adc_channel_init(struct iio_dev *idev, >>> + struct at91_adc_data *pdata) >>> +{ >>> + struct iio_chan_spec *chan_array; >>> + int bit, idx = 0; >>> + >>> + idev->num_channels = bitmap_weight(&(pdata->channels_used), >>> + pdata->num_channels); >>> + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), >>> + GFP_KERNEL); >>> + >>> + if (chan_array == NULL) >>> + return -ENOMEM; >>> + >>> + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { >>> + struct iio_chan_spec *chan = chan_array + idx; >>> + chan->type = IIO_VOLTAGE; >>> + chan->indexed = 1; >>> + chan->channel = bit; >>> + chan->scan_type.sign = 'u'; >>> + chan->scan_type.realbits = 10; >>> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >>> + ++idx; >>> + } >>> + >>> + idev->channels = chan_array; >>> + return idev->num_channels; >>> +} >>> + >>> +static void at91adc_channel_remove(struct iio_dev *idev) >>> +{ >>> + kfree(idev->channels); >>> +} >>> + >>> +static int at91adc_read_raw(struct iio_dev *idev, >>> + struct iio_chan_spec const *chan, >>> + int *val, int *val2, long mask) >>> +{ >>> + struct at91adc_state *st = iio_priv(idev); >>> + unsigned int scale_uv; >>> + short ret; >>> + >>> + switch (mask) { >>> + case 0: >>> + mutex_lock(&st->lock); >>> + >>> + at91adc_reg_write(st, AT91_ADC_CHER, >>> + AT91_ADC_CH(chan->channel)); >>> + at91adc_reg_write(st, AT91_ADC_IER, >>> + AT91_ADC_EOC(chan->channel)); >>> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); >>> + >>> + ret = wait_event_interruptible_timeout(st->wq_data_avail, >>> + st->done, >>> + msecs_to_jiffies(1000)); >>> + if (ret == -ERESTARTSYS) >>> + break; >>> + >>> + *val = st->last_value; >>> + >>> + at91adc_reg_write(st, AT91_ADC_CHDR, >>> + AT91_ADC_CH(chan->channel)); >>> + at91adc_reg_write(st, AT91_ADC_IDR, >>> + AT91_ADC_EOC(chan->channel)); >>> + >>> + st->last_value = 0; >>> + st->done = false; >>> + mutex_unlock(&st->lock); >>> + return IIO_VAL_INT; >>> + >>> + case IIO_CHAN_INFO_SCALE: >>> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >>> + *val = scale_uv / 1000; >>> + *val2 = (scale_uv % 1000) * 1000; >>> + return IIO_VAL_INT_PLUS_MICRO; >>> + default: >>> + break; >>> + } >>> + return -EINVAL; >>> +} >>> + >>> +static const struct iio_info at91adc_info = { >>> + .driver_module = THIS_MODULE, >>> + .read_raw = &at91adc_read_raw, >>> +}; >>> + >>> +static int __devinit at91adc_probe(struct platform_device *pdev) >>> +{ >>> + unsigned int prsc, mstrclk, ticks; >>> + int ret; >>> + struct iio_dev *idev; >>> + struct at91adc_state *st; >>> + struct resource *res; >>> + struct at91_adc_data *pdata = pdev->dev.platform_data; >>> + >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + if (!res) { >>> + dev_err(&pdev->dev, "No resource defined\n"); >>> + ret = -ENXIO; >>> + goto error_ret; >>> + } >>> + >>> + idev = iio_device_allocate(sizeof(struct at91adc_state)); >>> + if (idev == NULL) { >>> + ret = -ENOMEM; >>> + goto error_ret; >>> + } >>> + platform_set_drvdata(pdev, idev); >>> + >>> + idev->dev.parent = &pdev->dev; >>> + idev->name = dev_name(&pdev->dev); >>> + idev->info = &at91adc_info; >>> + >>> + st = iio_priv(idev); >>> + st->irq = platform_get_irq(pdev, 0); >>> + if (st->irq < 0) { >>> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >>> + ret = -ENODEV; >>> + goto error_free_device; >>> + } >>> + >>> + if (!request_mem_region(res->start, resource_size(res), >>> + "AT91 adc registers")) { >>> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >>> + ret = -EBUSY; >>> + goto error_free_device; >>> + } >>> + >>> + st->reg_base = ioremap(res->start, resource_size(res)); >>> + if (!st->reg_base) { >>> + dev_err(&pdev->dev, "Failed to map registers.\n"); >>> + ret = -ENOMEM; >>> + goto error_release_mem; >>> + } >>> + >>> + /* >>> + * Disable all IRQs before setting up the handler >>> + */ >>> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); >>> + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); >>> + ret = request_irq(st->irq, >>> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); >>> + if (ret) { >>> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >>> + goto error_unmap_reg; >>> + } >>> + >>> + st->clk = clk_get(&pdev->dev, "adc_clk"); >>> + if (IS_ERR(st->clk)) { >>> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >>> + ret = PTR_ERR(st->clk); >>> + goto error_free_irq; >>> + } >>> + >>> + clk_enable(st->clk); >>> + mstrclk = clk_get_rate(st->clk); >>> + >>> + if (!pdata) { >>> + dev_err(&pdev->dev, "No platform data available.\n"); >>> + ret = -EINVAL; >>> + goto error_free_clk; >>> + } >>> + >>> + if (!pdata->adc_clock) { >>> + dev_err(&pdev->dev, "No ADCClock available.\n"); >>> + ret = -EINVAL; >>> + goto error_free_clk; >>> + } >>> + >>> + /* >>> + * Prescaler rate computation using the formula from the Atmel's >>> + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being >>> + * specified by the electrical characteristics of the board. >>> + */ >>> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >>> + >>> + if (!pdata->startup_time) { >>> + dev_err(&pdev->dev, "No startup time available.\n"); >>> + ret = -EINVAL; >>> + goto error_free_clk; >>> + } >>> + >>> + /* >>> + * Number of ticks needed to cover the startup time of the ADC as >>> + * defined in the electrical characteristics of the board, divided by 8. >>> + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock >>> + */ >>> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >>> + 1000000) - 1, 8) / 8; >>> + at91adc_reg_write(st, AT91_ADC_MR, >>> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >>> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >>> + >>> + /* Setup the ADC channels available on the board */ >>> + ret = at91adc_channel_init(idev, pdata); >>> + if (ret < 0) >>> + goto error_free_clk; >>> + >>> + init_waitqueue_head(&st->wq_data_avail); >>> + mutex_init(&st->lock); >>> + >>> + st->vref_mv = pdata->vref; >>> + >>> + ret = iio_device_register(idev); >>> + if (ret < 0) >>> + goto error_free_channels; >>> + >>> + return 0; >>> + >>> +error_free_channels: >>> + at91adc_channel_remove(idev); >>> +error_free_clk: >>> + clk_disable(st->clk); >>> + clk_put(st->clk); >>> +error_free_irq: >>> + free_irq(st->irq, st); >>> +error_unmap_reg: >>> + iounmap(st->reg_base); >>> +error_release_mem: >>> + release_mem_region(res->start, resource_size(res)); >>> +error_free_device: >>> + iio_device_free(idev); >>> +error_ret: >>> + return ret; >>> +} >>> + >>> +static int __devexit at91adc_remove(struct platform_device *pdev) >>> +{ >>> + struct iio_dev *idev = platform_get_drvdata(pdev); >>> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + struct at91adc_state *st = iio_priv(idev); >>> + >>> + iio_device_unregister(idev); >>> + at91adc_channel_remove(idev); >>> + clk_disable(st->clk); >>> + clk_put(st->clk); >>> + free_irq(st->irq, idev); >>> + iounmap(st->reg_base); >>> + release_mem_region(res->start, resource_size(res)); >>> + iio_device_free(idev); >>> + >>> + return 0; >>> +} >>> + >>> +static struct platform_driver at91adc_driver = { >>> + .probe = at91adc_probe, >>> + .remove = __devexit_p(at91adc_remove), >>> + .driver = { >>> + .name = "at91adc", >>> + }, >>> +}; >>> + >>> +module_platform_driver(at91adc_driver); >>> + >>> +MODULE_LICENSE("GPL"); >>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-iio" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-14 9:59 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 9:59 UTC (permalink / raw) To: linux-arm-kernel On 11/11/2011 13:34, Jonathan Cameron wrote: > On 11/10/2011 05:35 PM, Jonathan Cameron wrote: >> On 11/09/2011 10:19 AM, Maxime Ripard wrote: >>> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >>> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >>> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> >>> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >>> >> One little comment / query inline. I'm happy with this either way >> though. Beware as you are dependent on a series under review though >> and it's always possible little things in there might change! >> >>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> >> >>> --- >>> drivers/iio/adc/Kconfig | 6 + >>> drivers/iio/adc/Makefile | 4 +- >>> drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ >>> 3 files changed, 335 insertions(+), 1 deletions(-) >>> create mode 100644 drivers/iio/adc/at91adc.c >>> >>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >>> index 3d97b21..74f4d9f 100644 >>> --- a/drivers/iio/adc/Kconfig >>> +++ b/drivers/iio/adc/Kconfig >>> @@ -14,6 +14,12 @@ config IIO_AD799X >>> i2c analog to digital convertors (ADC). Provides direct access >>> via sysfs. >>> >>> +config IIO_AT91ADC >>> + tristate "Atmel AT91 ADC" >>> + depends on SYSFS && ARCH_AT91 >>> + help >>> + Say yes here to build support for Atmel AT91 ADC. >>> + >>> config IIO_MAX1363 >>> tristate "Maxim max1363 ADC driver" >>> depends on I2C >>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >>> index c197334..776b56f 100644 >>> --- a/drivers/iio/adc/Makefile >>> +++ b/drivers/iio/adc/Makefile >>> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o >>> obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o >>> >>> iio_max1363-y := max1363_core.o >>> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >>> \ No newline at end of file >>> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >>> + >>> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o >>> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c >>> new file mode 100644 >>> index 0000000..6aef5d6 >>> --- /dev/null >>> +++ b/drivers/iio/adc/at91adc.c >>> @@ -0,0 +1,326 @@ >>> +/* >>> + * Driver for the ADC present in the Atmel AT91 evaluation boards. >>> + * >>> + * Copyright 2011 Free Electrons >>> + * >>> + * Licensed under the GPLv2 or later. >>> + */ >>> + >>> +#include <linux/bitmap.h> >>> +#include <linux/bitops.h> >>> +#include <linux/clk.h> >>> +#include <linux/err.h> >>> +#include <linux/io.h> >>> +#include <linux/interrupt.h> >>> +#include <linux/jiffies.h> >>> +#include <linux/kernel.h> >>> +#include <linux/module.h> >>> +#include <linux/platform_device.h> >>> +#include <linux/slab.h> >>> +#include <linux/wait.h> >>> + >>> +#include <linux/iio/iio.h> >>> + >>> +#include <mach/at91_adc.h> >>> +#include <mach/board.h> >>> + >>> +struct at91adc_state { >>> + struct clk *clk; >>> + bool done; >>> + struct mutex lock; >>> + int irq; >>> + wait_queue_head_t wq_data_avail; >>> + u16 last_value; >>> + void __iomem *reg_base; >>> + unsigned int vref_mv; >>> +}; >>> + >>> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) >>> +{ >>> + return readl_relaxed(st->reg_base + reg); >>> +} >>> + >>> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) >>> +{ >>> + writel_relaxed(val, st->reg_base + reg); >>> +} >>> + >>> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >>> +{ >>> + int chan; >>> + struct iio_dev *idev = private; >>> + struct at91adc_state *st = iio_priv(idev); >>> + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); >>> + >>> + if (!(status & AT91_ADC_DRDY)) >>> + return IRQ_HANDLED; >>> + >>> + for (chan = 0; chan < idev->num_channels; chan++) >> Given we know only one bit should be set, it really feels like there >> should be >> a cleaner way of doing this. Could it use find_first_bit(&status, >> idev->num_channels)? >> It kind of bypasses the defines though so it's far from clean in that sense. >>> + if (status & AT91_ADC_EOC(chan)) { >> >> The logic in here very much assumes that only one channel is being converted >> and the interrupt indicates that one of the channels has finished. >> Hence logically this st->done doesn't need to be in the loop. >> > Having had a look at the datasheet, could you just check against the > whole mask and use the lastdata converted register? Should under > current conditions always give you the right thing I think? You're right, It will be more consistent and avoid to loop over each channels. I send a new version of the patchset. >>> + st->done = true; >>> + st->last_value = at91adc_reg_read(st, >>> + AT91_ADC_CHR(chan)); >>> + } >>> + >>> + wake_up_interruptible(&st->wq_data_avail); >>> + >>> + return IRQ_HANDLED; >>> +} >>> + >>> +static int at91adc_channel_init(struct iio_dev *idev, >>> + struct at91_adc_data *pdata) >>> +{ >>> + struct iio_chan_spec *chan_array; >>> + int bit, idx = 0; >>> + >>> + idev->num_channels = bitmap_weight(&(pdata->channels_used), >>> + pdata->num_channels); >>> + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), >>> + GFP_KERNEL); >>> + >>> + if (chan_array == NULL) >>> + return -ENOMEM; >>> + >>> + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { >>> + struct iio_chan_spec *chan = chan_array + idx; >>> + chan->type = IIO_VOLTAGE; >>> + chan->indexed = 1; >>> + chan->channel = bit; >>> + chan->scan_type.sign = 'u'; >>> + chan->scan_type.realbits = 10; >>> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >>> + ++idx; >>> + } >>> + >>> + idev->channels = chan_array; >>> + return idev->num_channels; >>> +} >>> + >>> +static void at91adc_channel_remove(struct iio_dev *idev) >>> +{ >>> + kfree(idev->channels); >>> +} >>> + >>> +static int at91adc_read_raw(struct iio_dev *idev, >>> + struct iio_chan_spec const *chan, >>> + int *val, int *val2, long mask) >>> +{ >>> + struct at91adc_state *st = iio_priv(idev); >>> + unsigned int scale_uv; >>> + short ret; >>> + >>> + switch (mask) { >>> + case 0: >>> + mutex_lock(&st->lock); >>> + >>> + at91adc_reg_write(st, AT91_ADC_CHER, >>> + AT91_ADC_CH(chan->channel)); >>> + at91adc_reg_write(st, AT91_ADC_IER, >>> + AT91_ADC_EOC(chan->channel)); >>> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); >>> + >>> + ret = wait_event_interruptible_timeout(st->wq_data_avail, >>> + st->done, >>> + msecs_to_jiffies(1000)); >>> + if (ret == -ERESTARTSYS) >>> + break; >>> + >>> + *val = st->last_value; >>> + >>> + at91adc_reg_write(st, AT91_ADC_CHDR, >>> + AT91_ADC_CH(chan->channel)); >>> + at91adc_reg_write(st, AT91_ADC_IDR, >>> + AT91_ADC_EOC(chan->channel)); >>> + >>> + st->last_value = 0; >>> + st->done = false; >>> + mutex_unlock(&st->lock); >>> + return IIO_VAL_INT; >>> + >>> + case IIO_CHAN_INFO_SCALE: >>> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >>> + *val = scale_uv / 1000; >>> + *val2 = (scale_uv % 1000) * 1000; >>> + return IIO_VAL_INT_PLUS_MICRO; >>> + default: >>> + break; >>> + } >>> + return -EINVAL; >>> +} >>> + >>> +static const struct iio_info at91adc_info = { >>> + .driver_module = THIS_MODULE, >>> + .read_raw = &at91adc_read_raw, >>> +}; >>> + >>> +static int __devinit at91adc_probe(struct platform_device *pdev) >>> +{ >>> + unsigned int prsc, mstrclk, ticks; >>> + int ret; >>> + struct iio_dev *idev; >>> + struct at91adc_state *st; >>> + struct resource *res; >>> + struct at91_adc_data *pdata = pdev->dev.platform_data; >>> + >>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + if (!res) { >>> + dev_err(&pdev->dev, "No resource defined\n"); >>> + ret = -ENXIO; >>> + goto error_ret; >>> + } >>> + >>> + idev = iio_device_allocate(sizeof(struct at91adc_state)); >>> + if (idev == NULL) { >>> + ret = -ENOMEM; >>> + goto error_ret; >>> + } >>> + platform_set_drvdata(pdev, idev); >>> + >>> + idev->dev.parent = &pdev->dev; >>> + idev->name = dev_name(&pdev->dev); >>> + idev->info = &at91adc_info; >>> + >>> + st = iio_priv(idev); >>> + st->irq = platform_get_irq(pdev, 0); >>> + if (st->irq < 0) { >>> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >>> + ret = -ENODEV; >>> + goto error_free_device; >>> + } >>> + >>> + if (!request_mem_region(res->start, resource_size(res), >>> + "AT91 adc registers")) { >>> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >>> + ret = -EBUSY; >>> + goto error_free_device; >>> + } >>> + >>> + st->reg_base = ioremap(res->start, resource_size(res)); >>> + if (!st->reg_base) { >>> + dev_err(&pdev->dev, "Failed to map registers.\n"); >>> + ret = -ENOMEM; >>> + goto error_release_mem; >>> + } >>> + >>> + /* >>> + * Disable all IRQs before setting up the handler >>> + */ >>> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); >>> + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); >>> + ret = request_irq(st->irq, >>> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); >>> + if (ret) { >>> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >>> + goto error_unmap_reg; >>> + } >>> + >>> + st->clk = clk_get(&pdev->dev, "adc_clk"); >>> + if (IS_ERR(st->clk)) { >>> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >>> + ret = PTR_ERR(st->clk); >>> + goto error_free_irq; >>> + } >>> + >>> + clk_enable(st->clk); >>> + mstrclk = clk_get_rate(st->clk); >>> + >>> + if (!pdata) { >>> + dev_err(&pdev->dev, "No platform data available.\n"); >>> + ret = -EINVAL; >>> + goto error_free_clk; >>> + } >>> + >>> + if (!pdata->adc_clock) { >>> + dev_err(&pdev->dev, "No ADCClock available.\n"); >>> + ret = -EINVAL; >>> + goto error_free_clk; >>> + } >>> + >>> + /* >>> + * Prescaler rate computation using the formula from the Atmel's >>> + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being >>> + * specified by the electrical characteristics of the board. >>> + */ >>> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >>> + >>> + if (!pdata->startup_time) { >>> + dev_err(&pdev->dev, "No startup time available.\n"); >>> + ret = -EINVAL; >>> + goto error_free_clk; >>> + } >>> + >>> + /* >>> + * Number of ticks needed to cover the startup time of the ADC as >>> + * defined in the electrical characteristics of the board, divided by 8. >>> + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock >>> + */ >>> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >>> + 1000000) - 1, 8) / 8; >>> + at91adc_reg_write(st, AT91_ADC_MR, >>> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >>> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >>> + >>> + /* Setup the ADC channels available on the board */ >>> + ret = at91adc_channel_init(idev, pdata); >>> + if (ret < 0) >>> + goto error_free_clk; >>> + >>> + init_waitqueue_head(&st->wq_data_avail); >>> + mutex_init(&st->lock); >>> + >>> + st->vref_mv = pdata->vref; >>> + >>> + ret = iio_device_register(idev); >>> + if (ret < 0) >>> + goto error_free_channels; >>> + >>> + return 0; >>> + >>> +error_free_channels: >>> + at91adc_channel_remove(idev); >>> +error_free_clk: >>> + clk_disable(st->clk); >>> + clk_put(st->clk); >>> +error_free_irq: >>> + free_irq(st->irq, st); >>> +error_unmap_reg: >>> + iounmap(st->reg_base); >>> +error_release_mem: >>> + release_mem_region(res->start, resource_size(res)); >>> +error_free_device: >>> + iio_device_free(idev); >>> +error_ret: >>> + return ret; >>> +} >>> + >>> +static int __devexit at91adc_remove(struct platform_device *pdev) >>> +{ >>> + struct iio_dev *idev = platform_get_drvdata(pdev); >>> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + struct at91adc_state *st = iio_priv(idev); >>> + >>> + iio_device_unregister(idev); >>> + at91adc_channel_remove(idev); >>> + clk_disable(st->clk); >>> + clk_put(st->clk); >>> + free_irq(st->irq, idev); >>> + iounmap(st->reg_base); >>> + release_mem_region(res->start, resource_size(res)); >>> + iio_device_free(idev); >>> + >>> + return 0; >>> +} >>> + >>> +static struct platform_driver at91adc_driver = { >>> + .probe = at91adc_probe, >>> + .remove = __devexit_p(at91adc_remove), >>> + .driver = { >>> + .name = "at91adc", >>> + }, >>> +}; >>> + >>> +module_platform_driver(at91adc_driver); >>> + >>> +MODULE_LICENSE("GPL"); >>> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >>> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-iio" in >> the body of a message to majordomo at vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. 2011-11-10 17:35 ` Jonathan Cameron @ 2011-11-14 9:06 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 9:06 UTC (permalink / raw) To: Jonathan Cameron Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Jean-Christophe Plagniol-Villard, Thomas Petazzoni On 10/11/2011 18:35, Jonathan Cameron wrote: > On 11/09/2011 10:19 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> >> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >> > One little comment / query inline. I'm happy with this either way > though. Beware as you are dependent on a series under review though > and it's always possible little things in there might change! Yes, I know. Maybe it would be better if you took this patchset for that reason. I guess you have more control over the move out of staging than AT91 maintaners have. >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > >> --- >> drivers/iio/adc/Kconfig | 6 + >> drivers/iio/adc/Makefile | 4 +- >> drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 335 insertions(+), 1 deletions(-) >> create mode 100644 drivers/iio/adc/at91adc.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 3d97b21..74f4d9f 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -14,6 +14,12 @@ config IIO_AD799X >> i2c analog to digital convertors (ADC). Provides direct access >> via sysfs. >> >> +config IIO_AT91ADC >> + tristate "Atmel AT91 ADC" >> + depends on SYSFS && ARCH_AT91 >> + help >> + Say yes here to build support for Atmel AT91 ADC. >> + >> config IIO_MAX1363 >> tristate "Maxim max1363 ADC driver" >> depends on I2C >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index c197334..776b56f 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o >> obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o >> >> iio_max1363-y := max1363_core.o >> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> \ No newline at end of file >> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> + >> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o >> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..6aef5d6 >> --- /dev/null >> +++ b/drivers/iio/adc/at91adc.c >> @@ -0,0 +1,326 @@ >> +/* >> + * Driver for the ADC present in the Atmel AT91 evaluation boards. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/bitmap.h> >> +#include <linux/bitops.h> >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include <linux/iio/iio.h> >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 last_value; >> + void __iomem *reg_base; >> + unsigned int vref_mv; >> +}; >> + >> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) >> +{ >> + return readl_relaxed(st->reg_base + reg); >> +} >> + >> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) >> +{ >> + writel_relaxed(val, st->reg_base + reg); >> +} >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct iio_dev *idev = private; >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < idev->num_channels; chan++) > Given we know only one bit should be set, it really feels like there > should be > a cleaner way of doing this. Could it use find_first_bit(&status, > idev->num_channels)? > It kind of bypasses the defines though so it's far from clean in that sense. >> + if (status & AT91_ADC_EOC(chan)) { > > The logic in here very much assumes that only one channel is being converted > and the interrupt indicates that one of the channels has finished. > Hence logically this st->done doesn't need to be in the loop. > >> + st->done = true; >> + st->last_value = at91adc_reg_read(st, >> + AT91_ADC_CHR(chan)); >> + } >> + >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct iio_dev *idev, >> + struct at91_adc_data *pdata) >> +{ >> + struct iio_chan_spec *chan_array; >> + int bit, idx = 0; >> + >> + idev->num_channels = bitmap_weight(&(pdata->channels_used), >> + pdata->num_channels); >> + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), >> + GFP_KERNEL); >> + >> + if (chan_array == NULL) >> + return -ENOMEM; >> + >> + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { >> + struct iio_chan_spec *chan = chan_array + idx; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = bit; >> + chan->scan_type.sign = 'u'; >> + chan->scan_type.realbits = 10; >> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >> + ++idx; >> + } >> + >> + idev->channels = chan_array; >> + return idev->num_channels; >> +} >> + >> +static void at91adc_channel_remove(struct iio_dev *idev) >> +{ >> + kfree(idev->channels); >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int scale_uv; >> + short ret; >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); >> + >> + ret = wait_event_interruptible_timeout(st->wq_data_avail, >> + st->done, >> + msecs_to_jiffies(1000)); >> + if (ret == -ERESTARTSYS) >> + break; >> + >> + *val = st->last_value; >> + >> + at91adc_reg_write(st, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->last_value = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_SCALE: >> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >> + *val = scale_uv / 1000; >> + *val2 = (scale_uv % 1000) * 1000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_device_allocate(sizeof(struct at91adc_state)); >> + if (idev == NULL) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = dev_name(&pdev->dev); >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Prescaler rate computation using the formula from the Atmel's >> + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being >> + * specified by the electrical characteristics of the board. >> + */ >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Number of ticks needed to cover the startup time of the ADC as >> + * defined in the electrical characteristics of the board, divided by 8. >> + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock >> + */ >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + ret = at91adc_channel_init(idev, pdata); >> + if (ret < 0) >> + goto error_free_clk; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + st->vref_mv = pdata->vref; >> + >> + ret = iio_device_register(idev); >> + if (ret < 0) >> + goto error_free_channels; >> + >> + return 0; >> + >> +error_free_channels: >> + at91adc_channel_remove(idev); >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_device_free(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + iio_device_unregister(idev); >> + at91adc_channel_remove(idev); >> + clk_disable(st->clk); >> + clk_put(st->clk); >> + free_irq(st->irq, idev); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_free(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +module_platform_driver(at91adc_driver); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver. @ 2011-11-14 9:06 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 9:06 UTC (permalink / raw) To: linux-arm-kernel On 10/11/2011 18:35, Jonathan Cameron wrote: > On 11/09/2011 10:19 AM, Maxime Ripard wrote: >> Cc: Nicolas Ferre <nicolas.ferre@atmel.com> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com> >> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >> > One little comment / query inline. I'm happy with this either way > though. Beware as you are dependent on a series under review though > and it's always possible little things in there might change! Yes, I know. Maybe it would be better if you took this patchset for that reason. I guess you have more control over the move out of staging than AT91 maintaners have. >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > >> --- >> drivers/iio/adc/Kconfig | 6 + >> drivers/iio/adc/Makefile | 4 +- >> drivers/iio/adc/at91adc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 335 insertions(+), 1 deletions(-) >> create mode 100644 drivers/iio/adc/at91adc.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 3d97b21..74f4d9f 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -14,6 +14,12 @@ config IIO_AD799X >> i2c analog to digital convertors (ADC). Provides direct access >> via sysfs. >> >> +config IIO_AT91ADC >> + tristate "Atmel AT91 ADC" >> + depends on SYSFS && ARCH_AT91 >> + help >> + Say yes here to build support for Atmel AT91 ADC. >> + >> config IIO_MAX1363 >> tristate "Maxim max1363 ADC driver" >> depends on I2C >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index c197334..776b56f 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -6,4 +6,6 @@ iio_ad799x-y := ad799x_core.o >> obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o >> >> iio_max1363-y := max1363_core.o >> -obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> \ No newline at end of file >> +obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o >> + >> +obj-$(CONFIG_IIO_AT91ADC) += at91adc.o >> diff --git a/drivers/iio/adc/at91adc.c b/drivers/iio/adc/at91adc.c >> new file mode 100644 >> index 0000000..6aef5d6 >> --- /dev/null >> +++ b/drivers/iio/adc/at91adc.c >> @@ -0,0 +1,326 @@ >> +/* >> + * Driver for the ADC present in the Atmel AT91 evaluation boards. >> + * >> + * Copyright 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + */ >> + >> +#include <linux/bitmap.h> >> +#include <linux/bitops.h> >> +#include <linux/clk.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/interrupt.h> >> +#include <linux/jiffies.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/slab.h> >> +#include <linux/wait.h> >> + >> +#include <linux/iio/iio.h> >> + >> +#include <mach/at91_adc.h> >> +#include <mach/board.h> >> + >> +struct at91adc_state { >> + struct clk *clk; >> + bool done; >> + struct mutex lock; >> + int irq; >> + wait_queue_head_t wq_data_avail; >> + u16 last_value; >> + void __iomem *reg_base; >> + unsigned int vref_mv; >> +}; >> + >> +static inline u32 at91adc_reg_read(struct at91adc_state *st, u8 reg) >> +{ >> + return readl_relaxed(st->reg_base + reg); >> +} >> + >> +static inline void at91adc_reg_write(struct at91adc_state *st, u8 reg, u32 val) >> +{ >> + writel_relaxed(val, st->reg_base + reg); >> +} >> + >> +static irqreturn_t at91adc_eoc_trigger(int irq, void *private) >> +{ >> + int chan; >> + struct iio_dev *idev = private; >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int status = at91adc_reg_read(st, AT91_ADC_SR); >> + >> + if (!(status & AT91_ADC_DRDY)) >> + return IRQ_HANDLED; >> + >> + for (chan = 0; chan < idev->num_channels; chan++) > Given we know only one bit should be set, it really feels like there > should be > a cleaner way of doing this. Could it use find_first_bit(&status, > idev->num_channels)? > It kind of bypasses the defines though so it's far from clean in that sense. >> + if (status & AT91_ADC_EOC(chan)) { > > The logic in here very much assumes that only one channel is being converted > and the interrupt indicates that one of the channels has finished. > Hence logically this st->done doesn't need to be in the loop. > >> + st->done = true; >> + st->last_value = at91adc_reg_read(st, >> + AT91_ADC_CHR(chan)); >> + } >> + >> + wake_up_interruptible(&st->wq_data_avail); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int at91adc_channel_init(struct iio_dev *idev, >> + struct at91_adc_data *pdata) >> +{ >> + struct iio_chan_spec *chan_array; >> + int bit, idx = 0; >> + >> + idev->num_channels = bitmap_weight(&(pdata->channels_used), >> + pdata->num_channels); >> + chan_array = kcalloc(idev->num_channels, sizeof(struct iio_chan_spec), >> + GFP_KERNEL); >> + >> + if (chan_array == NULL) >> + return -ENOMEM; >> + >> + for_each_set_bit(bit, &(pdata->channels_used), pdata->num_channels) { >> + struct iio_chan_spec *chan = chan_array + idx; >> + chan->type = IIO_VOLTAGE; >> + chan->indexed = 1; >> + chan->channel = bit; >> + chan->scan_type.sign = 'u'; >> + chan->scan_type.realbits = 10; >> + chan->info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT; >> + ++idx; >> + } >> + >> + idev->channels = chan_array; >> + return idev->num_channels; >> +} >> + >> +static void at91adc_channel_remove(struct iio_dev *idev) >> +{ >> + kfree(idev->channels); >> +} >> + >> +static int at91adc_read_raw(struct iio_dev *idev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long mask) >> +{ >> + struct at91adc_state *st = iio_priv(idev); >> + unsigned int scale_uv; >> + short ret; >> + >> + switch (mask) { >> + case 0: >> + mutex_lock(&st->lock); >> + >> + at91adc_reg_write(st, AT91_ADC_CHER, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IER, >> + AT91_ADC_EOC(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_START); >> + >> + ret = wait_event_interruptible_timeout(st->wq_data_avail, >> + st->done, >> + msecs_to_jiffies(1000)); >> + if (ret == -ERESTARTSYS) >> + break; >> + >> + *val = st->last_value; >> + >> + at91adc_reg_write(st, AT91_ADC_CHDR, >> + AT91_ADC_CH(chan->channel)); >> + at91adc_reg_write(st, AT91_ADC_IDR, >> + AT91_ADC_EOC(chan->channel)); >> + >> + st->last_value = 0; >> + st->done = false; >> + mutex_unlock(&st->lock); >> + return IIO_VAL_INT; >> + >> + case IIO_CHAN_INFO_SCALE: >> + scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; >> + *val = scale_uv / 1000; >> + *val2 = (scale_uv % 1000) * 1000; >> + return IIO_VAL_INT_PLUS_MICRO; >> + default: >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info at91adc_info = { >> + .driver_module = THIS_MODULE, >> + .read_raw = &at91adc_read_raw, >> +}; >> + >> +static int __devinit at91adc_probe(struct platform_device *pdev) >> +{ >> + unsigned int prsc, mstrclk, ticks; >> + int ret; >> + struct iio_dev *idev; >> + struct at91adc_state *st; >> + struct resource *res; >> + struct at91_adc_data *pdata = pdev->dev.platform_data; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(&pdev->dev, "No resource defined\n"); >> + ret = -ENXIO; >> + goto error_ret; >> + } >> + >> + idev = iio_device_allocate(sizeof(struct at91adc_state)); >> + if (idev == NULL) { >> + ret = -ENOMEM; >> + goto error_ret; >> + } >> + platform_set_drvdata(pdev, idev); >> + >> + idev->dev.parent = &pdev->dev; >> + idev->name = dev_name(&pdev->dev); >> + idev->info = &at91adc_info; >> + >> + st = iio_priv(idev); >> + st->irq = platform_get_irq(pdev, 0); >> + if (st->irq < 0) { >> + dev_err(&pdev->dev, "No IRQ ID is designated\n"); >> + ret = -ENODEV; >> + goto error_free_device; >> + } >> + >> + if (!request_mem_region(res->start, resource_size(res), >> + "AT91 adc registers")) { >> + dev_err(&pdev->dev, "Resources are unavailable.\n"); >> + ret = -EBUSY; >> + goto error_free_device; >> + } >> + >> + st->reg_base = ioremap(res->start, resource_size(res)); >> + if (!st->reg_base) { >> + dev_err(&pdev->dev, "Failed to map registers.\n"); >> + ret = -ENOMEM; >> + goto error_release_mem; >> + } >> + >> + /* >> + * Disable all IRQs before setting up the handler >> + */ >> + at91adc_reg_write(st, AT91_ADC_CR, AT91_ADC_SWRST); >> + at91adc_reg_write(st, AT91_ADC_IDR, 0xFFFFFFFF); >> + ret = request_irq(st->irq, >> + at91adc_eoc_trigger, 0, pdev->dev.driver->name, idev); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); >> + goto error_unmap_reg; >> + } >> + >> + st->clk = clk_get(&pdev->dev, "adc_clk"); >> + if (IS_ERR(st->clk)) { >> + dev_err(&pdev->dev, "Failed to get the clock.\n"); >> + ret = PTR_ERR(st->clk); >> + goto error_free_irq; >> + } >> + >> + clk_enable(st->clk); >> + mstrclk = clk_get_rate(st->clk); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform data available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + if (!pdata->adc_clock) { >> + dev_err(&pdev->dev, "No ADCClock available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Prescaler rate computation using the formula from the Atmel's >> + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being >> + * specified by the electrical characteristics of the board. >> + */ >> + prsc = (mstrclk / (2 * pdata->adc_clock)) - 1; >> + >> + if (!pdata->startup_time) { >> + dev_err(&pdev->dev, "No startup time available.\n"); >> + ret = -EINVAL; >> + goto error_free_clk; >> + } >> + >> + /* >> + * Number of ticks needed to cover the startup time of the ADC as >> + * defined in the electrical characteristics of the board, divided by 8. >> + * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock >> + */ >> + ticks = round_up((pdata->startup_time * pdata->adc_clock / >> + 1000000) - 1, 8) / 8; >> + at91adc_reg_write(st, AT91_ADC_MR, >> + (AT91_ADC_PRESCAL_(prsc) & AT91_ADC_PRESCAL) | >> + (AT91_ADC_STARTUP_(ticks) & AT91_ADC_STARTUP)); >> + >> + /* Setup the ADC channels available on the board */ >> + ret = at91adc_channel_init(idev, pdata); >> + if (ret < 0) >> + goto error_free_clk; >> + >> + init_waitqueue_head(&st->wq_data_avail); >> + mutex_init(&st->lock); >> + >> + st->vref_mv = pdata->vref; >> + >> + ret = iio_device_register(idev); >> + if (ret < 0) >> + goto error_free_channels; >> + >> + return 0; >> + >> +error_free_channels: >> + at91adc_channel_remove(idev); >> +error_free_clk: >> + clk_disable(st->clk); >> + clk_put(st->clk); >> +error_free_irq: >> + free_irq(st->irq, st); >> +error_unmap_reg: >> + iounmap(st->reg_base); >> +error_release_mem: >> + release_mem_region(res->start, resource_size(res)); >> +error_free_device: >> + iio_device_free(idev); >> +error_ret: >> + return ret; >> +} >> + >> +static int __devexit at91adc_remove(struct platform_device *pdev) >> +{ >> + struct iio_dev *idev = platform_get_drvdata(pdev); >> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + struct at91adc_state *st = iio_priv(idev); >> + >> + iio_device_unregister(idev); >> + at91adc_channel_remove(idev); >> + clk_disable(st->clk); >> + clk_put(st->clk); >> + free_irq(st->irq, idev); >> + iounmap(st->reg_base); >> + release_mem_region(res->start, resource_size(res)); >> + iio_device_free(idev); >> + >> + return 0; >> +} >> + >> +static struct platform_driver at91adc_driver = { >> + .probe = at91adc_probe, >> + .remove = __devexit_p(at91adc_remove), >> + .driver = { >> + .name = "at91adc", >> + }, >> +}; >> + >> +module_platform_driver(at91adc_driver); >> + >> +MODULE_LICENSE("GPL"); >> +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); >> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-09 10:19 ` Maxime Ripard @ 2011-11-09 10:19 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 11 ++++++ 2 files changed, 63 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..0859553 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + if (test_bit(0, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC0, 0); + if (test_bit(1, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC1, 0); + if (test_bit(2, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC2, 0); + if (test_bit(3, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC3, 0); + + data->adc_clock = 5000000; + data->num_channels = 4; + data->startup_time = 10; + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..e34d41a 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3), + .vref = 3300, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +398,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-11-09 10:19 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-09 10:19 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ arch/arm/mach-at91/board-sam9g20ek.c | 11 ++++++ 2 files changed, 63 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index 39f81f4..0859553 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) void __init at91_add_device_cf(struct at91_cf_data * data) {} #endif +/* -------------------------------------------------------------------- + * ADCs + * -------------------------------------------------------------------- */ + +static struct at91_adc_data adc_data; + +static struct resource adc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_ADC, + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_ID_ADC, + .end = AT91SAM9260_ID_ADC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_adc_device = { + .name = "at91adc", + .id = -1, + .dev = { + .platform_data = &adc_data, + }, + .resource = adc_resources, + .num_resources = ARRAY_SIZE(adc_resources), +}; + +void __init at91_add_device_adc(struct at91_adc_data *data) +{ + if (!data) + return; + + if (test_bit(0, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC0, 0); + if (test_bit(1, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC1, 0); + if (test_bit(2, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC2, 0); + if (test_bit(3, &(data->channels_used))) + at91_set_A_periph(AT91_PIN_PC3, 0); + + data->adc_clock = 5000000; + data->num_channels = 4; + data->startup_time = 10; + + adc_data = *data; + platform_device_register(&at91_adc_device); +} + + /* -------------------------------------------------------------------- */ /* * These devices are always present and don't need any board-specific diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index 817f59d..e34d41a 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void) static void __init ek_add_device_buttons(void) {} #endif +/* + * ADCs + */ + +static struct at91_adc_data ek_adc_data = { + .channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3), + .vref = 3300, +}; + #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { REGULATOR_SUPPLY("AVDD", "0-001b"), @@ -389,6 +398,8 @@ static void __init ek_board_init(void) ek_add_device_gpio_leds(); /* Push Buttons */ ek_add_device_buttons(); + /* ADCs */ + at91_add_device_adc(&ek_adc_data); /* PCK0 provides MCLK to the WM8731 */ at91_set_B_periph(AT91_PIN_PC1, 0); /* SSC (for WM8731) */ -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board 2011-11-09 10:19 ` Maxime Ripard @ 2011-11-10 17:37 ` Jonathan Cameron -1 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-10 17:37 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni On 11/09/2011 10:19 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > Fine as far as I am concerned but this one is definitely one for acks from others as well. > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ > arch/arm/mach-at91/board-sam9g20ek.c | 11 ++++++ > 2 files changed, 63 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c > index 39f81f4..0859553 100644 > --- a/arch/arm/mach-at91/at91sam9260_devices.c > +++ b/arch/arm/mach-at91/at91sam9260_devices.c > @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) > void __init at91_add_device_cf(struct at91_cf_data * data) {} > #endif > > +/* -------------------------------------------------------------------- > + * ADCs > + * -------------------------------------------------------------------- */ > + > +static struct at91_adc_data adc_data; > + > +static struct resource adc_resources[] = { > + [0] = { > + .start = AT91SAM9260_BASE_ADC, > + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, > + .flags = IORESOURCE_MEM, > + }, > + [1] = { > + .start = AT91SAM9260_ID_ADC, > + .end = AT91SAM9260_ID_ADC, > + .flags = IORESOURCE_IRQ, > + }, > +}; > + > +static struct platform_device at91_adc_device = { > + .name = "at91adc", > + .id = -1, > + .dev = { > + .platform_data = &adc_data, > + }, > + .resource = adc_resources, > + .num_resources = ARRAY_SIZE(adc_resources), > +}; > + > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + if (test_bit(0, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC0, 0); > + if (test_bit(1, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC1, 0); > + if (test_bit(2, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC2, 0); > + if (test_bit(3, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC3, 0); > + > + data->adc_clock = 5000000; > + data->num_channels = 4; > + data->startup_time = 10; > + > + adc_data = *data; > + platform_device_register(&at91_adc_device); > +} > + > + > /* -------------------------------------------------------------------- */ > /* > * These devices are always present and don't need any board-specific > diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c > index 817f59d..e34d41a 100644 > --- a/arch/arm/mach-at91/board-sam9g20ek.c > +++ b/arch/arm/mach-at91/board-sam9g20ek.c > @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void) > static void __init ek_add_device_buttons(void) {} > #endif > > +/* > + * ADCs > + */ > + > +static struct at91_adc_data ek_adc_data = { > + .channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3), > + .vref = 3300, > +}; > + > #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) > static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { > REGULATOR_SUPPLY("AVDD", "0-001b"), > @@ -389,6 +398,8 @@ static void __init ek_board_init(void) > ek_add_device_gpio_leds(); > /* Push Buttons */ > ek_add_device_buttons(); > + /* ADCs */ > + at91_add_device_adc(&ek_adc_data); > /* PCK0 provides MCLK to the WM8731 */ > at91_set_B_periph(AT91_PIN_PC1, 0); > /* SSC (for WM8731) */ ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board @ 2011-11-10 17:37 ` Jonathan Cameron 0 siblings, 0 replies; 131+ messages in thread From: Jonathan Cameron @ 2011-11-10 17:37 UTC (permalink / raw) To: linux-arm-kernel On 11/09/2011 10:19 AM, Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > Fine as far as I am concerned but this one is definitely one for acks from others as well. > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > --- > arch/arm/mach-at91/at91sam9260_devices.c | 52 ++++++++++++++++++++++++++++++ > arch/arm/mach-at91/board-sam9g20ek.c | 11 ++++++ > 2 files changed, 63 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c > index 39f81f4..0859553 100644 > --- a/arch/arm/mach-at91/at91sam9260_devices.c > +++ b/arch/arm/mach-at91/at91sam9260_devices.c > @@ -1312,6 +1312,58 @@ void __init at91_add_device_cf(struct at91_cf_data *data) > void __init at91_add_device_cf(struct at91_cf_data * data) {} > #endif > > +/* -------------------------------------------------------------------- > + * ADCs > + * -------------------------------------------------------------------- */ > + > +static struct at91_adc_data adc_data; > + > +static struct resource adc_resources[] = { > + [0] = { > + .start = AT91SAM9260_BASE_ADC, > + .end = AT91SAM9260_BASE_ADC + SZ_16K - 1, > + .flags = IORESOURCE_MEM, > + }, > + [1] = { > + .start = AT91SAM9260_ID_ADC, > + .end = AT91SAM9260_ID_ADC, > + .flags = IORESOURCE_IRQ, > + }, > +}; > + > +static struct platform_device at91_adc_device = { > + .name = "at91adc", > + .id = -1, > + .dev = { > + .platform_data = &adc_data, > + }, > + .resource = adc_resources, > + .num_resources = ARRAY_SIZE(adc_resources), > +}; > + > +void __init at91_add_device_adc(struct at91_adc_data *data) > +{ > + if (!data) > + return; > + > + if (test_bit(0, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC0, 0); > + if (test_bit(1, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC1, 0); > + if (test_bit(2, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC2, 0); > + if (test_bit(3, &(data->channels_used))) > + at91_set_A_periph(AT91_PIN_PC3, 0); > + > + data->adc_clock = 5000000; > + data->num_channels = 4; > + data->startup_time = 10; > + > + adc_data = *data; > + platform_device_register(&at91_adc_device); > +} > + > + > /* -------------------------------------------------------------------- */ > /* > * These devices are always present and don't need any board-specific > diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c > index 817f59d..e34d41a 100644 > --- a/arch/arm/mach-at91/board-sam9g20ek.c > +++ b/arch/arm/mach-at91/board-sam9g20ek.c > @@ -314,6 +314,15 @@ static void __init ek_add_device_buttons(void) > static void __init ek_add_device_buttons(void) {} > #endif > > +/* > + * ADCs > + */ > + > +static struct at91_adc_data ek_adc_data = { > + .channels_used = BIT(0) | BIT(1) | BIT(2) | BIT(3), > + .vref = 3300, > +}; > + > #if defined(CONFIG_REGULATOR_FIXED_VOLTAGE) || defined(CONFIG_REGULATOR_FIXED_VOLTAGE_MODULE) > static struct regulator_consumer_supply ek_audio_consumer_supplies[] = { > REGULATOR_SUPPLY("AVDD", "0-001b"), > @@ -389,6 +398,8 @@ static void __init ek_board_init(void) > ek_add_device_gpio_leds(); > /* Push Buttons */ > ek_add_device_buttons(); > + /* ADCs */ > + at91_add_device_adc(&ek_adc_data); > /* PCK0 provides MCLK to the WM8731 */ > at91_set_B_periph(AT91_PIN_PC1, 0); > /* SSC (for WM8731) */ ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCHv5] AT91: Add a driver for the ADC @ 2011-11-14 10:06 Maxime Ripard 2011-11-14 10:06 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 10:06 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni 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 <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-14 10:06 [PATCHv5] AT91: Add a driver for the ADC Maxime Ripard @ 2011-11-14 10:06 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 10:06 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-14 10:06 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 10:06 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-14 10:06 ` Maxime Ripard @ 2011-11-14 11:29 ` Nicolas Ferre -1 siblings, 0 replies; 131+ messages in thread From: Nicolas Ferre @ 2011-11-14 11:29 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Thomas Petazzoni On 11/14/2011 11:06 AM, Maxime Ripard : > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> > --- > arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ > 1 files changed, 18 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..4f27797 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used bitmask). > + */ > + u8 num_channels; > + /* Channels in use on the board as a bitmask */ > + unsigned long channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif -- Nicolas Ferre ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-14 11:29 ` Nicolas Ferre 0 siblings, 0 replies; 131+ messages in thread From: Nicolas Ferre @ 2011-11-14 11:29 UTC (permalink / raw) To: linux-arm-kernel On 11/14/2011 11:06 AM, Maxime Ripard : > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> > --- > arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ > 1 files changed, 18 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h > index ed544a0..4f27797 100644 > --- a/arch/arm/mach-at91/include/mach/board.h > +++ b/arch/arm/mach-at91/include/mach/board.h > @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); > /* FIXME: this needs a better location, but gets stuff building again */ > extern int at91_suspend_entering_slow_clock(void); > > +/* ADC */ > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used bitmask). > + */ > + u8 num_channels; > + /* Channels in use on the board as a bitmask */ > + unsigned long channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > #endif -- Nicolas Ferre ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v6] AT91: Add a driver for the ADC @ 2011-11-14 17:30 Maxime Ripard 2011-11-14 17:30 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 17:30 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni 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 from v5: - Don't run the SoC specific code if the driver is not selected in the configuration - Fix the waitqueue timeout error handling to have a more sensible behaviour - Minor cosmetic improvements Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-14 17:30 [PATCH v6] AT91: Add a driver for the ADC Maxime Ripard @ 2011-11-14 17:30 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 17:30 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-14 17:30 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-14 17:30 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v7] AT91: Add a driver for the ADC @ 2011-11-15 10:54 Maxime Ripard 2011-11-15 10:54 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-15 10:54 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni 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 from v6: - Fix the unit in the scale read - Minor type fix Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-15 10:54 [PATCH v7] AT91: Add a driver for the ADC Maxime Ripard @ 2011-11-15 10:54 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-15 10:54 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-15 10:54 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-15 10:54 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v8] AT91: Add a driver for the ADC @ 2011-11-18 10:12 Maxime Ripard 2011-11-18 10:12 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-18 10:12 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni 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 from v7: - Fix the channels mask bug Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-18 10:12 [PATCH v8] AT91: Add a driver for the ADC Maxime Ripard @ 2011-11-18 10:12 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-18 10:12 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-18 10:12 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-18 10:12 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v9] AT91: Add a driver for the ADC @ 2011-11-24 11:27 Maxime Ripard 2011-11-24 11:27 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-24 11:27 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Hi all, The last version had a bug in the error handling of the probe function of the adc driver, the free_irq call having the wrong dev_id. This version corrects it. Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-24 11:27 [PATCH v9] AT91: Add a driver for the ADC Maxime Ripard @ 2011-11-24 11:27 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-24 11:27 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-24 11:27 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-24 11:27 UTC (permalink / raw) To: linux-arm-kernel Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> --- arch/arm/mach-at91/include/mach/board.h | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index ed544a0..4f27797 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -207,4 +207,22 @@ extern void __init at91_pwm_leds(struct gpio_led *leds, int nr); /* FIXME: this needs a better location, but gets stuff building again */ extern int at91_suspend_entering_slow_clock(void); +/* ADC */ +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; +extern void __init at91_add_device_adc(struct at91_adc_data *data); + #endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-24 11:27 ` Maxime Ripard @ 2011-11-24 14:29 ` Jean-Christophe PLAGNIOL-VILLARD -1 siblings, 0 replies; 131+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-24 14:29 UTC (permalink / raw) To: Maxime Ripard Cc: linux-arm-kernel, linux-iio, Thomas Petazzoni, Patrice Vilchez, Nicolas Ferre On 12:27 Thu 24 Nov , Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Best Regards, J. ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-24 14:29 ` Jean-Christophe PLAGNIOL-VILLARD 0 siblings, 0 replies; 131+ messages in thread From: Jean-Christophe PLAGNIOL-VILLARD @ 2011-11-24 14:29 UTC (permalink / raw) To: linux-arm-kernel On 12:27 Thu 24 Nov , Maxime Ripard wrote: > Cc: Nicolas Ferre <nicolas.ferre@atmel.com> > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Best Regards, J. ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v11] AT91: Add a driver for the ADC @ 2011-11-30 9:14 Maxime Ripard 2011-11-30 9:15 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-11-30 9:14 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Hi all, As suggested by Arnd, I moved the platform data from the board.h additions in the first patch to a new include/linux/platform_data/at91_adc.h file for this version. Maxime Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-30 9:14 [PATCH v11] AT91: Add a driver for the ADC Maxime Ripard @ 2011-11-30 9:15 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-30 9:15 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Patrice Vilchez, Thomas Petazzoni, Nicolas Ferre Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- include/linux/platform_data/at91_adc.h | 36 ++++++++++++++++++++++++++++++++ 1 files changed, 36 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..1e1813d --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-30 9:15 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-11-30 9:15 UTC (permalink / raw) To: linux-arm-kernel Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> --- include/linux/platform_data/at91_adc.h | 36 ++++++++++++++++++++++++++++++++ 1 files changed, 36 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..1e1813d --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-11-30 9:15 ` Maxime Ripard @ 2011-11-30 9:45 ` Nicolas Ferre -1 siblings, 0 replies; 131+ messages in thread From: Nicolas Ferre @ 2011-11-30 9:45 UTC (permalink / raw) To: Maxime Ripard, Jonathan Cameron Cc: linux-arm-kernel, linux-iio, Patrice Vilchez, Thomas Petazzoni Hi Maxime, On 11/30/2011 10:15 AM, Maxime Ripard : > Cc: Patrice Vilchez<patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni<thomas.petazzoni@free-electrons.com> "CC:" lines should not be located here: place them just *after* "Signed-off-by:" and "Acked-by:" lines. > Signed-off-by: Maxime Ripard<maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron<jic23@cam.ac.uk> > Signed-off-by: Nicolas Ferre<nicolas.ferre@atmel.com> > Acked-by: Jean-Christophe PLAGNIOL-VILLARD<plagnioj@jcrosoft.com> > --- > include/linux/platform_data/at91_adc.h | 36 ++++++++++++++++++++++++++++++++ > 1 files changed, 36 insertions(+), 0 deletions(-) > create mode 100644 include/linux/platform_data/at91_adc.h > > diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h > new file mode 100644 > index 0000000..1e1813d > --- /dev/null > +++ b/include/linux/platform_data/at91_adc.h > @@ -0,0 +1,36 @@ > +/* > + * Copyright (C) 2011 Free Electrons > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. Can be shorten in: "Licensed under GPLv2." "Licensed under the GPL v2." "License terms: GNU General Public License (GPL) version 2." > + * > + */ > + > +#ifndef _AT91_ADC_H_ > +#define _AT91_ADC_H_ > + > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used bitmask). > + */ > + u8 num_channels; > + /* Channels in use on the board as a bitmask */ > + unsigned long channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > + > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > +#endif I think that now it is located at this generic directory, it can easily go mainline through IIO subsystem (patches 1 and 2/3): Jonathan, do you agree with this? Best regards, -- Nicolas Ferre ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-11-30 9:45 ` Nicolas Ferre 0 siblings, 0 replies; 131+ messages in thread From: Nicolas Ferre @ 2011-11-30 9:45 UTC (permalink / raw) To: linux-arm-kernel Hi Maxime, On 11/30/2011 10:15 AM, Maxime Ripard : > Cc: Patrice Vilchez<patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni<thomas.petazzoni@free-electrons.com> "CC:" lines should not be located here: place them just *after* "Signed-off-by:" and "Acked-by:" lines. > Signed-off-by: Maxime Ripard<maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron<jic23@cam.ac.uk> > Signed-off-by: Nicolas Ferre<nicolas.ferre@atmel.com> > Acked-by: Jean-Christophe PLAGNIOL-VILLARD<plagnioj@jcrosoft.com> > --- > include/linux/platform_data/at91_adc.h | 36 ++++++++++++++++++++++++++++++++ > 1 files changed, 36 insertions(+), 0 deletions(-) > create mode 100644 include/linux/platform_data/at91_adc.h > > diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h > new file mode 100644 > index 0000000..1e1813d > --- /dev/null > +++ b/include/linux/platform_data/at91_adc.h > @@ -0,0 +1,36 @@ > +/* > + * Copyright (C) 2011 Free Electrons > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. Can be shorten in: "Licensed under GPLv2." "Licensed under the GPL v2." "License terms: GNU General Public License (GPL) version 2." > + * > + */ > + > +#ifndef _AT91_ADC_H_ > +#define _AT91_ADC_H_ > + > +struct at91_adc_data { > + /* ADC Clock as specified by the datasheet, in Hz. */ > + unsigned int adc_clock; > + /* > + * Global number of channels available (to specify which channels are > + * indeed used on the board, see the channels_used bitmask). > + */ > + u8 num_channels; > + /* Channels in use on the board as a bitmask */ > + unsigned long channels_used; > + /* Startup time of the ADC, in microseconds. */ > + u8 startup_time; > + /* Reference voltage for the ADC in millivolts */ > + unsigned short vref; > +}; > + > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > +#endif I think that now it is located at this generic directory, it can easily go mainline through IIO subsystem (patches 1 and 2/3): Jonathan, do you agree with this? Best regards, -- Nicolas Ferre ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH v12] AT91: Add a driver for the ADC @ 2011-12-02 13:17 Maxime Ripard 2011-12-02 13:17 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-12-02 13:17 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Hi all, Since everyone seemed to be ok with the v11 of this patchset, it seems that it is now near inclusion. Maxime ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-12-02 13:17 [PATCH v12] AT91: Add a driver for the ADC Maxime Ripard @ 2011-12-02 13:17 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-12-02 13:17 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- include/linux/platform_data/at91_adc.h | 29 +++++++++++++++++++++++++++++ 1 files changed, 29 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..1212c78 --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-12-02 13:17 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-12-02 13:17 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- include/linux/platform_data/at91_adc.h | 29 +++++++++++++++++++++++++++++ 1 files changed, 29 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..1212c78 --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +struct at91_adc_data { + /* ADC Clock as specified by the datasheet, in Hz. */ + unsigned int adc_clock; + /* + * Global number of channels available (to specify which channels are + * indeed used on the board, see the channels_used bitmask). + */ + u8 num_channels; + /* Channels in use on the board as a bitmask */ + unsigned long channels_used; + /* Startup time of the ADC, in microseconds. */ + u8 startup_time; + /* Reference voltage for the ADC in millivolts */ + unsigned short vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH v13] AT91: Add a driver for the ADC @ 2011-12-14 10:01 Maxime Ripard 2011-12-14 10:01 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-12-14 10:01 UTC (permalink / raw) To: linux-arm-kernel, linux-iio Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Hi, Working on supporting the hardware triggers for these drivers, I made some patches that Jonathan suggested I merge with this patchset. So, basically, what changed from v12: * Indentation fixes, added some comments * Rework of the platform data. Now the SoC-specific data are directly in the driver, as it was kind of odd to declare all the triggers for the driver in the SoC files. So the driver handles all of this now. It has nice side effects, as it will also ease the transition to DT. As it introduce heavy changes, I dropped the Acked-by and Signed-off-by from the two last patches. Maxime Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-12-14 10:01 [PATCH v13] AT91: Add a driver for the ADC Maxime Ripard @ 2011-12-14 10:01 ` Maxime Ripard 2011-12-14 10:27 ` Alexander Stein 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2011-12-14 10:01 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ 1 files changed, 23 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..bad80b7 --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +/** + * struct at91_adc_data - platform data for ADC driver + * @channels_use: channels in use on the board as a bitmask + * @vref: Reference voltage for the ADC in millvolts + */ +struct at91_adc_data { + unsigned long channels_used; + u16 vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-12-14 10:01 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard @ 2011-12-14 10:27 ` Alexander Stein 0 siblings, 0 replies; 131+ messages in thread From: Alexander Stein @ 2011-12-14 10:27 UTC (permalink / raw) To: linux-arm-kernel Cc: Maxime Ripard, linux-iio, Thomas Petazzoni, Patrice Vilchez, Nicolas Ferre On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote: > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> > Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> > > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > --- > include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ > 1 files changed, 23 insertions(+), 0 deletions(-) > create mode 100644 include/linux/platform_data/at91_adc.h > > diff --git a/include/linux/platform_data/at91_adc.h > b/include/linux/platform_data/at91_adc.h new file mode 100644 > index 0000000..bad80b7 > --- /dev/null > +++ b/include/linux/platform_data/at91_adc.h > @@ -0,0 +1,23 @@ > +/* > + * Copyright (C) 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + * > + */ > + > +#ifndef _AT91_ADC_H_ > +#define _AT91_ADC_H_ > + > +/** > + * struct at91_adc_data - platform data for ADC driver > + * @channels_use: channels in use on the board as a bitmask > + * @vref: Reference voltage for the ADC in millvolts Nitpick, this should write millivolts, no? > + */ > +struct at91_adc_data { > + unsigned long channels_used; > + u16 vref; > +}; > + > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > +#endif Regards, Alexander ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-12-14 10:27 ` Alexander Stein 0 siblings, 0 replies; 131+ messages in thread From: Alexander Stein @ 2011-12-14 10:27 UTC (permalink / raw) To: linux-arm-kernel On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote: > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > Acked-by: Jonathan Cameron <jic23@cam.ac.uk> > Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> > Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> > > Cc: Patrice Vilchez <patrice.vilchez@atmel.com> > Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > --- > include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ > 1 files changed, 23 insertions(+), 0 deletions(-) > create mode 100644 include/linux/platform_data/at91_adc.h > > diff --git a/include/linux/platform_data/at91_adc.h > b/include/linux/platform_data/at91_adc.h new file mode 100644 > index 0000000..bad80b7 > --- /dev/null > +++ b/include/linux/platform_data/at91_adc.h > @@ -0,0 +1,23 @@ > +/* > + * Copyright (C) 2011 Free Electrons > + * > + * Licensed under the GPLv2 or later. > + * > + */ > + > +#ifndef _AT91_ADC_H_ > +#define _AT91_ADC_H_ > + > +/** > + * struct at91_adc_data - platform data for ADC driver > + * @channels_use: channels in use on the board as a bitmask > + * @vref: Reference voltage for the ADC in millvolts Nitpick, this should write millivolts, no? > + */ > +struct at91_adc_data { > + unsigned long channels_used; > + u16 vref; > +}; > + > +extern void __init at91_add_device_adc(struct at91_adc_data *data); > + > +#endif Regards, Alexander ^ permalink raw reply [flat|nested] 131+ messages in thread
* Re: [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2011-12-14 10:27 ` Alexander Stein @ 2011-12-15 19:37 ` Maxime Ripard -1 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-12-15 19:37 UTC (permalink / raw) To: Alexander Stein Cc: linux-arm-kernel, linux-iio, Thomas Petazzoni, Patrice Vilchez, Nicolas Ferre On 14/12/2011 11:27, Alexander Stein wrote: > On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote: >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> >> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> >> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> >> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >> --- >> include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ >> 1 files changed, 23 insertions(+), 0 deletions(-) >> create mode 100644 include/linux/platform_data/at91_adc.h >> >> diff --git a/include/linux/platform_data/at91_adc.h >> b/include/linux/platform_data/at91_adc.h new file mode 100644 >> index 0000000..bad80b7 >> --- /dev/null >> +++ b/include/linux/platform_data/at91_adc.h >> @@ -0,0 +1,23 @@ >> +/* >> + * Copyright (C) 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + * >> + */ >> + >> +#ifndef _AT91_ADC_H_ >> +#define _AT91_ADC_H_ >> + >> +/** >> + * struct at91_adc_data - platform data for ADC driver >> + * @channels_use: channels in use on the board as a bitmask >> + * @vref: Reference voltage for the ADC in millvolts > > Nitpick, this should write millivolts, no? Yes, of course :) > >> + */ >> +struct at91_adc_data { >> + unsigned long channels_used; >> + u16 vref; >> +}; >> + >> +extern void __init at91_add_device_adc(struct at91_adc_data *data); >> + >> +#endif > > Regards, > Alexander -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2011-12-15 19:37 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2011-12-15 19:37 UTC (permalink / raw) To: linux-arm-kernel On 14/12/2011 11:27, Alexander Stein wrote: > On Wednesday 14 December 2011 11:01:15 Maxime Ripard wrote: >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> >> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> >> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> >> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> >> >> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> >> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> >> --- >> include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ >> 1 files changed, 23 insertions(+), 0 deletions(-) >> create mode 100644 include/linux/platform_data/at91_adc.h >> >> diff --git a/include/linux/platform_data/at91_adc.h >> b/include/linux/platform_data/at91_adc.h new file mode 100644 >> index 0000000..bad80b7 >> --- /dev/null >> +++ b/include/linux/platform_data/at91_adc.h >> @@ -0,0 +1,23 @@ >> +/* >> + * Copyright (C) 2011 Free Electrons >> + * >> + * Licensed under the GPLv2 or later. >> + * >> + */ >> + >> +#ifndef _AT91_ADC_H_ >> +#define _AT91_ADC_H_ >> + >> +/** >> + * struct at91_adc_data - platform data for ADC driver >> + * @channels_use: channels in use on the board as a bitmask >> + * @vref: Reference voltage for the ADC in millvolts > > Nitpick, this should write millivolts, no? Yes, of course :) > >> + */ >> +struct at91_adc_data { >> + unsigned long channels_used; >> + u16 vref; >> +}; >> + >> +extern void __init at91_add_device_adc(struct at91_adc_data *data); >> + >> +#endif > > Regards, > Alexander -- Maxime Ripard, Free Electrons Kernel, drivers, real-time and embedded Linux development, consulting, training and support. http://free-electrons.com ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH RESEND v13] AT91: Add a driver for the ADC @ 2012-01-16 21:36 Maxime Ripard 2012-01-16 21:36 ` Maxime Ripard 0 siblings, 1 reply; 131+ messages in thread From: Maxime Ripard @ 2012-01-16 21:36 UTC (permalink / raw) To: linux-iio, linux-arm-kernel Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Hi, Working on supporting the hardware triggers for these drivers, I made some patches that Jonathan suggested I merge with this patchset. So, basically, what changed from v12: * Indentation fixes, added some comments * Rework of the platform data. Now the SoC-specific data are directly in the driver, as it was kind of odd to declare all the triggers for the driver in the SoC files. So the driver handles all of this now. It has nice side effects, as it will also ease the transition to DT. As it introduce heavy changes, I dropped the Acked-by and Signed-off-by from the two last patches. Maxime Cc: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ^ permalink raw reply [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs 2012-01-16 21:36 [PATCH RESEND v13] AT91: Add a driver for the ADC Maxime Ripard @ 2012-01-16 21:36 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2012-01-16 21:36 UTC (permalink / raw) To: linux-iio, linux-arm-kernel Cc: Nicolas Ferre, Patrice Vilchez, Thomas Petazzoni Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ 1 files changed, 23 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..c27b502 --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +/** + * struct at91_adc_data - platform data for ADC driver + * @channels_use: channels in use on the board as a bitmask + * @vref: Reference voltage for the ADC in millivolts + */ +struct at91_adc_data { + unsigned long channels_used; + u16 vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 131+ messages in thread
* [PATCH 1/3] ARM: AT91: Add platform data for the ADCs @ 2012-01-16 21:36 ` Maxime Ripard 0 siblings, 0 replies; 131+ messages in thread From: Maxime Ripard @ 2012-01-16 21:36 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> Cc: Patrice Vilchez <patrice.vilchez@atmel.com> Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> --- include/linux/platform_data/at91_adc.h | 23 +++++++++++++++++++++++ 1 files changed, 23 insertions(+), 0 deletions(-) create mode 100644 include/linux/platform_data/at91_adc.h diff --git a/include/linux/platform_data/at91_adc.h b/include/linux/platform_data/at91_adc.h new file mode 100644 index 0000000..c27b502 --- /dev/null +++ b/include/linux/platform_data/at91_adc.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2011 Free Electrons + * + * Licensed under the GPLv2 or later. + * + */ + +#ifndef _AT91_ADC_H_ +#define _AT91_ADC_H_ + +/** + * struct at91_adc_data - platform data for ADC driver + * @channels_use: channels in use on the board as a bitmask + * @vref: Reference voltage for the ADC in millivolts + */ +struct at91_adc_data { + unsigned long channels_used; + u16 vref; +}; + +extern void __init at91_add_device_adc(struct at91_adc_data *data); + +#endif -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 131+ messages in thread
end of thread, other threads:[~2012-01-16 21:36 UTC | newest] Thread overview: 131+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-10-19 16:18 [PATCH] AT91: Add a driver for the ADC Maxime Ripard 2011-10-19 16:18 ` Maxime Ripard 2011-10-19 16:18 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-10-19 16:18 ` Maxime Ripard 2011-10-19 16:18 ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard 2011-10-19 16:18 ` Maxime Ripard 2011-10-19 16:42 ` Jonathan Cameron 2011-10-19 16:42 ` Jonathan Cameron 2011-10-19 18:23 ` Maxime Ripard 2011-10-19 18:23 ` Maxime Ripard 2011-10-20 7:05 ` Thomas Petazzoni 2011-10-20 7:05 ` Thomas Petazzoni 2011-10-20 8:33 ` Jonathan Cameron 2011-10-20 8:33 ` Jonathan Cameron 2011-10-20 8:49 ` Thomas Petazzoni 2011-10-20 8:49 ` Thomas Petazzoni 2011-10-20 9:19 ` Jonathan Cameron 2011-10-20 9:19 ` Jonathan Cameron 2011-10-20 9:52 ` Mark Brown 2011-10-20 9:52 ` Mark Brown 2011-10-20 7:09 ` Lars-Peter Clausen 2011-10-20 7:09 ` Lars-Peter Clausen 2011-10-21 17:54 ` Maxime Ripard 2011-10-21 17:54 ` Maxime Ripard 2011-10-21 17:55 ` Lars-Peter Clausen 2011-10-21 17:55 ` Lars-Peter Clausen 2011-10-23 9:08 ` Jean-Christophe PLAGNIOL-VILLARD 2011-10-23 9:08 ` Jean-Christophe PLAGNIOL-VILLARD 2011-10-24 8:21 ` Maxime Ripard 2011-10-24 8:21 ` Maxime Ripard 2011-10-19 16:18 ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard 2011-10-19 16:18 ` Maxime Ripard 2011-10-20 6:28 ` Alexander Stein 2011-10-20 6:28 ` Alexander Stein 2011-10-21 17:47 ` Maxime Ripard 2011-10-21 17:47 ` Maxime Ripard 2011-10-20 7:14 ` Thomas Petazzoni 2011-10-20 7:14 ` Thomas Petazzoni 2011-11-03 10:11 ` [PATCHv2] AT91: Add a driver for the ADC Maxime Ripard 2011-11-03 10:11 ` Maxime Ripard 2011-11-03 10:11 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-03 10:11 ` Maxime Ripard 2011-11-03 11:27 ` Linus Walleij 2011-11-03 11:27 ` Linus Walleij 2011-11-03 16:27 ` Maxime Ripard 2011-11-03 16:27 ` Maxime Ripard 2011-11-03 16:38 ` Linus Walleij 2011-11-03 18:05 ` Jean-Christophe PLAGNIOL-VILLARD 2011-11-03 18:05 ` Jean-Christophe PLAGNIOL-VILLARD 2011-11-04 10:27 ` Jonathan Cameron 2011-11-04 10:27 ` Jonathan Cameron 2011-11-04 10:36 ` Jonathan Cameron 2011-11-04 10:36 ` Jonathan Cameron 2011-11-04 10:34 ` Jonathan Cameron 2011-11-04 10:34 ` Jonathan Cameron 2011-11-04 15:22 ` Maxime Ripard 2011-11-04 16:28 ` Jonathan Cameron 2011-11-04 16:28 ` Jonathan Cameron 2011-11-03 10:11 ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard 2011-11-03 10:11 ` Maxime Ripard 2011-11-04 10:27 ` Jonathan Cameron 2011-11-04 10:27 ` Jonathan Cameron 2011-11-04 16:29 ` Maxime Ripard 2011-11-04 16:40 ` Jonathan Cameron 2011-11-03 10:11 ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard 2011-11-03 10:11 ` Maxime Ripard 2011-11-04 10:33 ` Jonathan Cameron 2011-11-04 10:33 ` Jonathan Cameron 2011-11-04 11:25 ` Maxime Ripard 2011-11-04 15:52 ` Linus Walleij 2011-11-04 16:32 ` Jonathan Cameron 2011-11-04 16:32 ` Jonathan Cameron 2011-11-07 16:08 ` [PATCHv3] AT91: Add a driver for the ADC Maxime Ripard 2011-11-07 16:08 ` Maxime Ripard 2011-11-07 16:08 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-07 16:08 ` Maxime Ripard 2011-11-07 16:27 ` Jonathan Cameron 2011-11-07 16:27 ` Jonathan Cameron 2011-11-08 13:19 ` Thomas Petazzoni 2011-11-08 13:19 ` Thomas Petazzoni 2011-11-07 16:08 ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard 2011-11-07 16:08 ` Maxime Ripard 2011-11-08 13:30 ` Thomas Petazzoni 2011-11-08 13:30 ` Thomas Petazzoni 2011-11-07 16:08 ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard 2011-11-07 16:08 ` Maxime Ripard 2011-11-09 10:19 ` [PATCHv4] AT91: Add a driver for the ADC Maxime Ripard 2011-11-09 10:19 ` Maxime Ripard 2011-11-09 10:19 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-09 10:19 ` Maxime Ripard 2011-11-09 10:19 ` [PATCH 2/3] ARM: AT91: IIO: Add AT91 ADC driver Maxime Ripard 2011-11-09 10:19 ` Maxime Ripard 2011-11-10 17:35 ` Jonathan Cameron 2011-11-10 17:35 ` Jonathan Cameron 2011-11-11 12:34 ` Jonathan Cameron 2011-11-11 12:34 ` Jonathan Cameron 2011-11-14 9:59 ` Maxime Ripard 2011-11-14 9:59 ` Maxime Ripard 2011-11-14 9:06 ` Maxime Ripard 2011-11-14 9:06 ` Maxime Ripard 2011-11-09 10:19 ` [PATCH 3/3] ARM: AT91: Add the ADC to the sam9g20ek board Maxime Ripard 2011-11-09 10:19 ` Maxime Ripard 2011-11-10 17:37 ` Jonathan Cameron 2011-11-10 17:37 ` Jonathan Cameron -- strict thread matches above, loose matches on Subject: below -- 2011-11-14 10:06 [PATCHv5] AT91: Add a driver for the ADC Maxime Ripard 2011-11-14 10:06 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-14 10:06 ` Maxime Ripard 2011-11-14 11:29 ` Nicolas Ferre 2011-11-14 11:29 ` Nicolas Ferre 2011-11-14 17:30 [PATCH v6] AT91: Add a driver for the ADC Maxime Ripard 2011-11-14 17:30 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-14 17:30 ` Maxime Ripard 2011-11-15 10:54 [PATCH v7] AT91: Add a driver for the ADC Maxime Ripard 2011-11-15 10:54 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-15 10:54 ` Maxime Ripard 2011-11-18 10:12 [PATCH v8] AT91: Add a driver for the ADC Maxime Ripard 2011-11-18 10:12 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-18 10:12 ` Maxime Ripard 2011-11-24 11:27 [PATCH v9] AT91: Add a driver for the ADC Maxime Ripard 2011-11-24 11:27 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-24 11:27 ` Maxime Ripard 2011-11-24 14:29 ` Jean-Christophe PLAGNIOL-VILLARD 2011-11-24 14:29 ` Jean-Christophe PLAGNIOL-VILLARD 2011-11-30 9:14 [PATCH v11] AT91: Add a driver for the ADC Maxime Ripard 2011-11-30 9:15 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-11-30 9:15 ` Maxime Ripard 2011-11-30 9:45 ` Nicolas Ferre 2011-11-30 9:45 ` Nicolas Ferre 2011-12-02 13:17 [PATCH v12] AT91: Add a driver for the ADC Maxime Ripard 2011-12-02 13:17 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-12-02 13:17 ` Maxime Ripard 2011-12-14 10:01 [PATCH v13] AT91: Add a driver for the ADC Maxime Ripard 2011-12-14 10:01 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2011-12-14 10:27 ` Alexander Stein 2011-12-14 10:27 ` Alexander Stein 2011-12-15 19:37 ` Maxime Ripard 2011-12-15 19:37 ` Maxime Ripard 2012-01-16 21:36 [PATCH RESEND v13] AT91: Add a driver for the ADC Maxime Ripard 2012-01-16 21:36 ` [PATCH 1/3] ARM: AT91: Add platform data for the ADCs Maxime Ripard 2012-01-16 21:36 ` Maxime Ripard
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.