From: Jonathan Cameron <jic23@kernel.org>
To: Marek Vasut <marex@denx.de>
Cc: linux-iio@vger.kernel.org, Juergen Beisert <jbe@pengutronix.de>,
Lars-Peter Clausen <lars@metafoo.de>,
Shawn Guo <shawn.guo@linaro.org>, Wolfgang Denk <wd@denx.de>
Subject: Re: [PATCH 2/3 V3] IIO: Add basic MXS LRADC driver
Date: Thu, 16 Aug 2012 20:21:17 +0100 [thread overview]
Message-ID: <502D482D.8070606@kernel.org> (raw)
In-Reply-To: <1344784918-32352-3-git-send-email-marex@denx.de>
On 08/12/2012 04:21 PM, Marek Vasut wrote:
> This driver is very basic. It supports userland trigger, buffer and
> raw access to channels. The support for delay channels is missing
> altogether.
All looks good to me. Merge to togreg branch of iio.git will send on
to Greg sometime in next few days.
>
> Signed-off-by: Marek Vasut <marex@denx.de>
> Cc: Jonathan Cameron <jic23@kernel.org>
> Cc: Juergen Beisert <jbe@pengutronix.de>
> Cc: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Shawn Guo <shawn.guo@linaro.org>
> Cc: Wolfgang Denk <wd@denx.de>
> ---
> .../bindings/staging/iio/adc/mxs-lradc.txt | 15 +
> drivers/staging/iio/adc/Kconfig | 12 +
> drivers/staging/iio/adc/Makefile | 1 +
> drivers/staging/iio/adc/mxs-lradc.c | 590 ++++++++++++++++++++
> 4 files changed, 618 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
> create mode 100644 drivers/staging/iio/adc/mxs-lradc.c
>
> V2: Use delay channel 0 in case of buffered sampling so the samples are deployed
> continuously.
> Disallow RAW sampling while buffered mode is enabled to simplify code
>
> V3: Fix the INIT_COMPLETION bug to prevent race
> Add binding documentation
> Drop redundant headers
> Use platform_set_drvdata
> Use HZ instead of msecs_to_jiffies(1000)
> Don't claim the driver supports MX233 until properly adjusted for MX233
>
> diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
> new file mode 100644
> index 0000000..801d58c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
> @@ -0,0 +1,15 @@
> +* Freescale i.MX28 LRADC device driver
> +
> +Required properties:
> +- compatible: Should be "fsl,imx28-lradc"
> +- reg: Address and length of the register set for the device
> +- interrupts: Should contain the LRADC interrupts
> +
> +Examples:
> +
> + lradc@80050000 {
> + compatible = "fsl,imx28-lradc";
> + reg = <0x80050000 0x2000>;
> + interrupts = <10 14 15 16 17 18 19
> + 20 21 22 23 24 25>;
> + };
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 67711b7..97ca697 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -200,6 +200,18 @@ config LPC32XX_ADC
> activate only one via device tree selection. Provides direct access
> via sysfs.
>
> +config MXS_LRADC
> + tristate "Freescale i.MX28 LRADC"
> + depends on ARCH_MXS
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say yes here to build support for i.MX28 LRADC convertor
> + built into these chips.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called mxs-lradc.
> +
> config SPEAR_ADC
> tristate "ST SPEAr ADC"
> depends on PLAT_SPEAR
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 14e98b6..ecac9a0 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -38,4 +38,5 @@ obj-$(CONFIG_ADT7310) += adt7310.o
> obj-$(CONFIG_ADT7410) += adt7410.o
> obj-$(CONFIG_AD7280) += ad7280a.o
> obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
> +obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
> obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
> diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c
> new file mode 100644
> index 0000000..b528f71
> --- /dev/null
> +++ b/drivers/staging/iio/adc/mxs-lradc.c
> @@ -0,0 +1,590 @@
> +/*
> + * Freescale i.MX28 LRADC driver
> + *
> + * Copyright (c) 2012 DENX Software Engineering, GmbH.
> + * Marek Vasut <marex@denx.de>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +#include <linux/stmp_device.h>
> +#include <linux/bitops.h>
> +#include <linux/completion.h>
> +
> +#include <mach/mxs.h>
> +#include <mach/common.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#define DRIVER_NAME "mxs-lradc"
> +
> +#define LRADC_MAX_DELAY_CHANS 4
> +#define LRADC_MAX_MAPPED_CHANS 8
> +#define LRADC_MAX_TOTAL_CHANS 16
> +
> +#define LRADC_DELAY_TIMER_HZ 2000
> +
> +/*
> + * Make this runtime configurable if necessary. Currently, if the buffered mode
> + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
> + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
> + * seconds. The result is that the samples arrive every 500mS.
> + */
> +#define LRADC_DELAY_TIMER_PER 200
> +#define LRADC_DELAY_TIMER_LOOP 5
> +
> +static const char * const mxs_lradc_irq_name[] = {
> + "mxs-lradc-touchscreen",
> + "mxs-lradc-thresh0",
> + "mxs-lradc-thresh1",
> + "mxs-lradc-channel0",
> + "mxs-lradc-channel1",
> + "mxs-lradc-channel2",
> + "mxs-lradc-channel3",
> + "mxs-lradc-channel4",
> + "mxs-lradc-channel5",
> + "mxs-lradc-channel6",
> + "mxs-lradc-channel7",
> + "mxs-lradc-button0",
> + "mxs-lradc-button1",
> +};
> +
> +struct mxs_lradc_chan {
> + uint8_t slot;
> + uint8_t flags;
> +};
> +
> +struct mxs_lradc {
> + struct device *dev;
> + void __iomem *base;
> + int irq[13];
> +
> + uint32_t *buffer;
> + struct iio_trigger *trig;
> +
> + struct mutex lock;
> +
> + uint8_t enable;
> +
> + struct completion completion;
> +};
> +
> +#define LRADC_CTRL0 0x00
> +#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
> +#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
> +
> +#define LRADC_CTRL1 0x10
> +#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
> +#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
> +#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
> +#define LRADC_CTRL1_LRADC_IRQ_EN_MASK (0x1fff << 16)
> +
> +#define LRADC_CTRL2 0x20
> +#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15)
> +
> +#define LRADC_CH(n) (0x50 + (0x10 * (n)))
> +#define LRADC_CH_ACCUMULATE (1 << 29)
> +#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
> +#define LRADC_CH_NUM_SAMPLES_OFFSET 24
> +#define LRADC_CH_VALUE_MASK 0x3ffff
> +#define LRADC_CH_VALUE_OFFSET 0
> +
> +#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
> +#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xff << 24)
> +#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
> +#define LRADC_DELAY_KICK (1 << 20)
> +#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
> +#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
> +#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
> +#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
> +#define LRADC_DELAY_DELAY_MASK 0x7ff
> +#define LRADC_DELAY_DELAY_OFFSET 0
> +
> +#define LRADC_CTRL4 0x140
> +#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
> +#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
> +
> +/*
> + * Raw I/O operations
> + */
> +static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
> + const struct iio_chan_spec *chan,
> + int *val, int *val2, long m)
> +{
> + struct mxs_lradc *lradc = iio_priv(iio_dev);
> + int ret;
> +
> + if (m != IIO_CHAN_INFO_RAW)
> + return -EINVAL;
> +
> + /* Check for invalid channel */
> + if (chan->channel > LRADC_MAX_TOTAL_CHANS)
> + return -EINVAL;
> +
> + /*
> + * See if there is no buffered operation in progess. If there is, simply
> + * bail out. This can be improved to support both buffered and raw IO at
> + * the same time, yet the code becomes horribly complicated. Therefore I
> + * applied KISS principle here.
> + */
> + ret = mutex_trylock(&lradc->lock);
> + if (!ret)
> + return -EBUSY;
> +
> + INIT_COMPLETION(lradc->completion);
> +
> + /*
> + * No buffered operation in progress, map the channel and trigger it.
> + * Virtual channel 0 is always used here as the others are always not
> + * used if doing raw sampling.
> + */
> + writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
> + writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
> +
> + writel(chan->channel, lradc->base + LRADC_CTRL4);
> + writel(0, lradc->base + LRADC_CH(0));
> +
> + /* Enable the IRQ and start sampling the channel. */
> + writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
> + writel(1 << 0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
> +
> + /* Wait for completion on the channel, 1 second max. */
> + ret = wait_for_completion_killable_timeout(&lradc->completion, HZ);
> + if (!ret)
> + ret = -ETIMEDOUT;
> + if (ret < 0)
> + goto err;
> +
> + /* Read the data. */
> + *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
> + ret = IIO_VAL_INT;
> +
> +err:
> + writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
> +
> + mutex_unlock(&lradc->lock);
> +
> + return ret;
> +}
> +
> +static const struct iio_info mxs_lradc_iio_info = {
> + .driver_module = THIS_MODULE,
> + .read_raw = mxs_lradc_read_raw,
> +};
> +
> +/*
> + * IRQ Handling
> + */
> +static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
> +{
> + struct iio_dev *iio = data;
> + struct mxs_lradc *lradc = iio_priv(iio);
> + unsigned long reg = readl(lradc->base + LRADC_CTRL1);
> +
> + if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
> + return IRQ_NONE;
> +
> + /*
> + * Touchscreen IRQ handling code shall probably have priority
> + * and therefore shall be placed here.
> + */
> +
> + if (iio_buffer_enabled(iio))
> + iio_trigger_poll(iio->trig, iio_get_time_ns());
> + else if (reg & LRADC_CTRL1_LRADC_IRQ(0))
> + complete(&lradc->completion);
> +
> + writel(reg & LRADC_CTRL1_LRADC_IRQ_MASK,
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * Trigger handling
> + */
> +static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *iio = pf->indio_dev;
> + struct mxs_lradc *lradc = iio_priv(iio);
> + struct iio_buffer *buffer = iio->buffer;
> + const uint32_t chan_value = LRADC_CH_ACCUMULATE |
> + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
> + int i, j = 0;
> +
> + for_each_set_bit(i, iio->active_scan_mask, iio->masklength) {
> + lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
> + writel(chan_value, lradc->base + LRADC_CH(j));
> + lradc->buffer[j] &= LRADC_CH_VALUE_MASK;
> + lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
> + j++;
> + }
> +
> + if (iio->scan_timestamp) {
> + s64 *timestamp = (s64 *)((u8 *)lradc->buffer +
> + ALIGN(j, sizeof(s64)));
> + *timestamp = pf->timestamp;
> + }
> +
> + iio_push_to_buffer(buffer, (u8 *)lradc->buffer, pf->timestamp);
> +
> + iio_trigger_notify_done(iio->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state)
> +{
> + struct iio_dev *iio = trig->private_data;
> + struct mxs_lradc *lradc = iio_priv(iio);
> + const uint32_t st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
> +
> + writel(LRADC_DELAY_KICK, lradc->base + LRADC_DELAY(0) + st);
> +
> + return 0;
> +}
> +
> +static const struct iio_trigger_ops mxs_lradc_trigger_ops = {
> + .owner = THIS_MODULE,
> + .set_trigger_state = &mxs_lradc_configure_trigger,
> +};
> +
> +static int mxs_lradc_trigger_init(struct iio_dev *iio)
> +{
> + int ret;
> + struct iio_trigger *trig;
> +
> + trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
> + if (trig == NULL)
> + return -ENOMEM;
> +
> + trig->dev.parent = iio->dev.parent;
> + trig->private_data = iio;
> + trig->ops = &mxs_lradc_trigger_ops;
> +
> + ret = iio_trigger_register(trig);
> + if (ret) {
> + iio_trigger_free(trig);
> + return ret;
> + }
> +
> + iio->trig = trig;
> +
> + return 0;
> +}
> +
> +static void mxs_lradc_trigger_remove(struct iio_dev *iio)
> +{
> + iio_trigger_unregister(iio->trig);
> + iio_trigger_free(iio->trig);
> +}
> +
> +static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
> +{
> + struct mxs_lradc *lradc = iio_priv(iio);
> + struct iio_buffer *buffer = iio->buffer;
> + int ret = 0, chan, ofs = 0, enable = 0;
> + uint32_t ctrl4 = 0;
> + uint32_t ctrl1_irq = 0;
> + const uint32_t chan_value = LRADC_CH_ACCUMULATE |
> + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
> + const int len = bitmap_weight(buffer->scan_mask, LRADC_MAX_TOTAL_CHANS);
> +
> + if (!len)
> + return -EINVAL;
> +
> + /*
> + * Lock the driver so raw access can not be done during buffered
> + * operation. This simplifies the code a lot.
> + */
> + ret = mutex_trylock(&lradc->lock);
> + if (!ret)
> + return -EBUSY;
> +
> + lradc->buffer = kmalloc(len * sizeof(*lradc->buffer), GFP_KERNEL);
> + if (!lradc->buffer) {
> + ret = -ENOMEM;
> + goto err_mem;
> + }
> +
> + ret = iio_sw_buffer_preenable(iio);
> + if (ret < 0)
> + goto err_buf;
> +
> + writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
> + writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
> +
> + for_each_set_bit(chan, buffer->scan_mask, LRADC_MAX_TOTAL_CHANS) {
> + ctrl4 |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
> + ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
> + writel(chan_value, lradc->base + LRADC_CH(ofs));
> + enable |= 1 << ofs;
> + ofs++;
> + };
> +
> + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
> + lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
> +
> + writel(ctrl4, lradc->base + LRADC_CTRL4);
> + writel(ctrl1_irq, lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
> +
> + writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
> + lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
> +
> + return 0;
> +
> +err_buf:
> + kfree(lradc->buffer);
> +err_mem:
> + mutex_unlock(&lradc->lock);
> + return ret;
> +}
> +
> +static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
> +{
> + struct mxs_lradc *lradc = iio_priv(iio);
> +
> + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
> + lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
> +
> + writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
> + writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
> +
> + kfree(lradc->buffer);
> + mutex_unlock(&lradc->lock);
> +
> + return 0;
> +}
> +
> +static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
> + const unsigned long *mask)
> +{
> + const int mw = bitmap_weight(mask, iio->masklength);
> +
> + return mw <= LRADC_MAX_MAPPED_CHANS;
> +}
> +
> +static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
> + .preenable = &mxs_lradc_buffer_preenable,
> + .postenable = &iio_triggered_buffer_postenable,
> + .predisable = &iio_triggered_buffer_predisable,
> + .postdisable = &mxs_lradc_buffer_postdisable,
> + .validate_scan_mask = &mxs_lradc_validate_scan_mask,
> +};
> +
> +/*
> + * Driver initialization
> + */
> +
> +#define MXS_ADC_CHAN(idx, chan_type) { \
> + .type = (chan_type), \
> + .indexed = 1, \
> + .scan_index = (idx), \
> + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
> + .channel = (idx), \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = 18, \
> + .storagebits = 32, \
> + }, \
> +}
> +
> +static const struct iio_chan_spec mxs_lradc_chan_spec[] = {
> + MXS_ADC_CHAN(0, IIO_VOLTAGE),
> + MXS_ADC_CHAN(1, IIO_VOLTAGE),
> + MXS_ADC_CHAN(2, IIO_VOLTAGE),
> + MXS_ADC_CHAN(3, IIO_VOLTAGE),
> + MXS_ADC_CHAN(4, IIO_VOLTAGE),
> + MXS_ADC_CHAN(5, IIO_VOLTAGE),
> + MXS_ADC_CHAN(6, IIO_VOLTAGE),
> + MXS_ADC_CHAN(7, IIO_VOLTAGE), /* VBATT */
> + MXS_ADC_CHAN(8, IIO_TEMP), /* Temp sense 0 */
> + MXS_ADC_CHAN(9, IIO_TEMP), /* Temp sense 1 */
> + MXS_ADC_CHAN(10, IIO_VOLTAGE), /* VDDIO */
> + MXS_ADC_CHAN(11, IIO_VOLTAGE), /* VTH */
> + MXS_ADC_CHAN(12, IIO_VOLTAGE), /* VDDA */
> + MXS_ADC_CHAN(13, IIO_VOLTAGE), /* VDDD */
> + MXS_ADC_CHAN(14, IIO_VOLTAGE), /* VBG */
> + MXS_ADC_CHAN(15, IIO_VOLTAGE), /* VDD5V */
> +};
> +
> +static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
> +{
> + int i;
> + const uint32_t cfg =
> + (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
> +
> + stmp_reset_block(lradc->base);
> +
> + for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
> + writel(cfg | (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + i)),
> + lradc->base + LRADC_DELAY(i));
> +
> + /* Start internal temperature sensing. */
> + writel(0, lradc->base + LRADC_CTRL2);
> +}
> +
> +static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
> +{
> + int i;
> +
> + writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
> + lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
> +
> + for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
> + writel(0, lradc->base + LRADC_DELAY(i));
> +}
> +
> +static int __devinit mxs_lradc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct mxs_lradc *lradc;
> + struct iio_dev *iio;
> + struct resource *iores;
> + int ret = 0;
> + int i;
> +
> + /* Allocate the IIO device. */
> + iio = iio_device_alloc(sizeof(*lradc));
> + if (!iio) {
> + dev_err(dev, "Failed to allocate IIO device\n");
> + return -ENOMEM;
> + }
> +
> + lradc = iio_priv(iio);
> +
> + /* Grab the memory area */
> + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + lradc->dev = &pdev->dev;
> + lradc->base = devm_request_and_ioremap(dev, iores);
> + if (!lradc->base) {
> + ret = -EADDRNOTAVAIL;
> + goto err_addr;
> + }
> +
> + /* Grab all IRQ sources */
> + for (i = 0; i < 13; i++) {
> + lradc->irq[i] = platform_get_irq(pdev, i);
> + if (lradc->irq[i] < 0) {
> + ret = -EINVAL;
> + goto err_addr;
> + }
> +
> + ret = devm_request_irq(dev, lradc->irq[i],
> + mxs_lradc_handle_irq, 0,
> + mxs_lradc_irq_name[i], iio);
> + if (ret)
> + goto err_addr;
> + }
> +
> + platform_set_drvdata(pdev, iio);
> +
> + init_completion(&lradc->completion);
> + mutex_init(&lradc->lock);
> +
> + iio->name = pdev->name;
> + iio->dev.parent = &pdev->dev;
> + iio->info = &mxs_lradc_iio_info;
> + iio->modes = INDIO_DIRECT_MODE;
> + iio->channels = mxs_lradc_chan_spec;
> + iio->num_channels = ARRAY_SIZE(mxs_lradc_chan_spec);
> +
> + ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
> + &mxs_lradc_trigger_handler,
> + &mxs_lradc_buffer_ops);
> + if (ret)
> + goto err_addr;
> +
> + ret = mxs_lradc_trigger_init(iio);
> + if (ret)
> + goto err_trig;
> +
> + /* Register IIO device. */
> + ret = iio_device_register(iio);
> + if (ret) {
> + dev_err(dev, "Failed to register IIO device\n");
> + goto err_dev;
> + }
> +
> + /* Configure the hardware. */
> + mxs_lradc_hw_init(lradc);
> +
> + return 0;
> +
> +err_dev:
> + mxs_lradc_trigger_remove(iio);
> +err_trig:
> + iio_triggered_buffer_cleanup(iio);
> +err_addr:
> + iio_device_free(iio);
> + return ret;
> +}
> +
> +static int __devexit mxs_lradc_remove(struct platform_device *pdev)
> +{
> + struct iio_dev *iio = platform_get_drvdata(pdev);
> + struct mxs_lradc *lradc = iio_priv(iio);
> +
> + mxs_lradc_hw_stop(lradc);
> +
> + iio_device_unregister(iio);
> + iio_triggered_buffer_cleanup(iio);
> + mxs_lradc_trigger_remove(iio);
> + iio_device_free(iio);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id mxs_lradc_dt_ids[] = {
> + { .compatible = "fsl,imx28-lradc", },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
> +
> +static struct platform_driver mxs_lradc_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .owner = THIS_MODULE,
> + .of_match_table = mxs_lradc_dt_ids,
> + },
> + .probe = mxs_lradc_probe,
> + .remove = __devexit_p(mxs_lradc_remove),
> +};
> +
> +module_platform_driver(mxs_lradc_driver);
> +
> +MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
> +MODULE_DESCRIPTION("Freescale i.MX28 LRADC driver");
> +MODULE_LICENSE("GPL v2");
>
next prev parent reply other threads:[~2012-08-16 19:21 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-08-12 15:21 [PATCH 0/3] i.MX28 LRADC driver Marek Vasut
2012-08-12 15:21 ` [PATCH 1/3 RESEND] IIO: Add 4-byte unsigned reads into generic-buffer example Marek Vasut
2012-08-12 15:21 ` [PATCH 2/3 V3] IIO: Add basic MXS LRADC driver Marek Vasut
2012-08-16 19:21 ` Jonathan Cameron [this message]
2012-08-16 19:29 ` Marek Vasut
2012-08-12 15:21 ` [PATCH 3/3] IIO: arm: Add LRADC to i.MX28 dts Marek Vasut
2012-08-14 1:56 ` Shawn Guo
2012-08-14 2:10 ` Marek Vasut
2012-08-14 2:23 ` Shawn Guo
2012-08-14 12:33 ` Marek Vasut
2012-08-14 13:15 ` Shawn Guo
2012-08-14 13:23 ` Marek Vasut
2012-08-17 2:48 ` Shawn Guo
2012-08-17 2:57 ` Marek Vasut
2012-08-17 3:02 ` Shawn Guo
2012-08-19 15:30 ` Marek Vasut
2012-08-20 7:29 ` Jonathan Cameron
2012-08-20 7:52 ` Shawn Guo
2012-08-20 7:57 ` Jonathan Cameron
2012-08-20 13:41 ` Marek Vasut
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=502D482D.8070606@kernel.org \
--to=jic23@kernel.org \
--cc=jbe@pengutronix.de \
--cc=lars@metafoo.de \
--cc=linux-iio@vger.kernel.org \
--cc=marex@denx.de \
--cc=shawn.guo@linaro.org \
--cc=wd@denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).