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@xxxxxxx> > Cc: Jonathan Cameron <jic23@xxxxxxxxxx> > Cc: Juergen Beisert <jbe@xxxxxxxxxxxxxx> > Cc: Lars-Peter Clausen <lars@xxxxxxxxxx> > Cc: Shawn Guo <shawn.guo@xxxxxxxxxx> > Cc: Wolfgang Denk <wd@xxxxxxx> > --- > .../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@xxxxxxx> > + * > + * 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@xxxxxxx>"); > +MODULE_DESCRIPTION("Freescale i.MX28 LRADC driver"); > +MODULE_LICENSE("GPL v2"); > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html