Signed-off-by: Marek Vasut <marek.vasut@xxxxxxxxx> Cc: Wolfgang Denk <wd@xxxxxxx> Cc: Stefano Babic <sbabic@xxxxxxx> Cc: Fabio Estevam <festevam@xxxxxxxxx> --- arch/arm/mach-mxs/mach-m28evk.c | 35 ++- drivers/staging/iio/adc/Kconfig | 10 + drivers/staging/iio/adc/Makefile | 1 + drivers/staging/iio/adc/mxs-lradc.c | 666 ++++++++++++++++++++++++++++++++ 4 files changed, 700 insertions(+), 4 deletions(-) create mode 100644 drivers/staging/iio/adc/mxs-lradc.c NOTE: The quality of code is still really crap here, I'm more interested in knowing whether I'm going in the right direction about this. Thanks! diff --git a/arch/arm/mach-mxs/mach-m28evk.c b/arch/arm/mach-mxs/mach-m28evk.c index 2f27582..7a37d29 100644 --- a/arch/arm/mach-mxs/mach-m28evk.c +++ b/arch/arm/mach-mxs/mach-m28evk.c @@ -320,6 +320,31 @@ static struct mxs_mmc_platform_data m28evk_mmc_pdata[] __initdata = { }, }; +#define RES_IRQ(id, res) { .name = id, .start = (res), .end = (res), .flags = IORESOURCE_IRQ } + +static struct resource mxs_lradc_rsrc[] = { + [0] = { + .start = 0x80050000, + .end = 0x80050000 + SZ_8K - 1, + .flags = IORESOURCE_MEM, + }, + RES_IRQ("LRADC CH0", MX28_INT_LRADC_CH0), + RES_IRQ("LRADC CH1", MX28_INT_LRADC_CH1), + RES_IRQ("LRADC CH2", MX28_INT_LRADC_CH2), + RES_IRQ("LRADC CH3", MX28_INT_LRADC_CH3), + RES_IRQ("LRADC CH4", MX28_INT_LRADC_CH4), + RES_IRQ("LRADC CH5", MX28_INT_LRADC_CH5), + RES_IRQ("LRADC CH6", MX28_INT_LRADC_CH6), + RES_IRQ("LRADC CH7", MX28_INT_LRADC_CH7), +}; + +static struct platform_device mxs_lradc = { + .name = "mxs-lradc", + .id = -1, + .resource = mxs_lradc_rsrc, + .num_resources = ARRAY_SIZE(mxs_lradc_rsrc), +}; + static void __init m28evk_init(void) { mxs_iomux_setup_multiple_pads(m28evk_pads, ARRAY_SIZE(m28evk_pads)); @@ -347,6 +372,8 @@ static void __init m28evk_init(void) mx28_add_mxs_i2c(0); i2c_register_board_info(0, m28_stk5v3_i2c_boardinfo, ARRAY_SIZE(m28_stk5v3_i2c_boardinfo)); + + platform_device_register(&mxs_lradc); } static void __init m28evk_timer_init(void) diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index d9decea..7f33032 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -193,4 +193,14 @@ config MAX1363_RING_BUFFER Say yes here to include ring buffer support in the MAX1363 ADC driver. +config MXS_LRADC + tristate "Freescale i.MX23/i.MX28 LRADC" + depends on ARCH_MXS + help + Say yes here to build support for i.MX23/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. + endmenu diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index ceee7f3..2d764ec 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_MXS_LRADC) += mxs-lradc.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..da50088 --- /dev/null +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -0,0 +1,666 @@ +/* + * ADT7410 digital temperature sensor driver supporting ADT7410 + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.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 <mach/mxs.h> +#include <mach/common.h> + +#include "../iio.h" +#include "../sysfs.h" + +#define MAPPING_USED (1 << 7) +#define MAPPING_XXX (1 << 6) +#define MAPPING_SET_SLOT(m) (((m) & 0x3) << 4) +#define MAPPING_GET_SLOT(m) (((m) >> 4) & 0x3) +#define MAPPING_CHAN(m) ((m) & 0xf) + +struct mxs_lradc_mapped_channel { + wait_queue_head_t wq; + bool wq_done; + uint16_t chan_data; + uint16_t mapping; + uint32_t users; +}; + +struct mxs_lradc_drv_data { + struct iio_dev *iio[4]; + void __iomem *mmio_base; + + spinlock_t lock; + + uint16_t claimed; + + struct mxs_lradc_mapped_channel ch[8]; + uint8_t ch_oversample[16]; +}; + +struct mxs_lradc_data { + struct mxs_lradc_drv_data *drv_data; + int id; + spinlock_t lock; + + uint16_t claimed; + + uint32_t loop_interval; +}; + + +#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_CTRL0 0x00 + +#define LRADC_CTRL1 0x10 +#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n)) +#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16)) + +#define LRADC_CTRL2 0x20 +#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15) + +#define LRADC_CTRL3 0x30 + +#define LRADC_CTRL4 0x140 +#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4)) +#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4) + +/* + * Global IIO attributes + */ +static ssize_t mxs_lradc_show_sampling_rate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct mxs_lradc_data *data = iio_priv(iio_dev); + + return sprintf(buf, "%u\n", data->loop_interval); +} + +static ssize_t mxs_lradc_store_sampling_rate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct mxs_lradc_data *data = iio_priv(iio_dev); + struct mxs_lradc_drv_data *drv_data = data->drv_data; + uint32_t reg; + unsigned long lval; + unsigned long lflags; + + if (strict_strtoul(buf, 10, &lval)) + return -EINVAL; + + /* Check if the value is in requested range */ + if (lval >= 2048) + return -EINVAL; + + /* + * Noone is accessing our delay trigger in the LRADC reg space so we + * don't need to claim top-level spinlock. + */ + spin_lock_irqsave(&data->lock, lflags); + + if (data->loop_interval != lval) { + data->loop_interval = lval; + + /* Update the delay channel */ + reg = readl(drv_data->mmio_base + LRADC_DELAY(data->id)); + reg &= ~LRADC_DELAY_DELAY_MASK; + reg |= data->loop_interval; + writel(reg, drv_data->mmio_base + LRADC_DELAY(data->id)); + } + + spin_unlock_irqrestore(&data->lock, lflags); + + return count; +} + +static IIO_DEVICE_ATTR(sampling_rate, S_IRUGO | S_IWUSR, + mxs_lradc_show_sampling_rate, + mxs_lradc_store_sampling_rate, 0); +static IIO_CONST_ATTR(sampling_rate_available, + "0...2047 (in 1/2000 second steps)"); + +static struct attribute *mxs_lradc_attributes[] = { + &iio_dev_attr_sampling_rate.dev_attr.attr, + &iio_const_attr_sampling_rate_available.dev_attr.attr, + NULL, +}; + +static int mxs_lradc_can_claim_channel(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan) +{ + struct mxs_lradc_data *data = iio_priv(iio_dev); + struct mxs_lradc_drv_data *drv_data = data->drv_data; + int i, count = 0; + + /* The channel is already claimed by us. */ + if (data->claimed & (1 << chan->address)) + return 0; + + /* Check if someone else didn't claim this */ + if (drv_data->claimed & (1 << chan->address)) + return -EINVAL; + + for (i = 0; i < 16; i++) + if (drv_data->claimed & (1 << i)) + count++; + + /* Too many channels claimed */ + if (count == 8) + return -EINVAL; + + return 0; +} + +static int mxs_lradc_claim_channel(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan) +{ + struct mxs_lradc_data *data = iio_priv(iio_dev); + struct mxs_lradc_drv_data *drv_data = data->drv_data; + int i, ret; + unsigned long gflags; + uint32_t overspl = 0; + + spin_lock_irqsave(&drv_data->lock, gflags); + + ret = mxs_lradc_can_claim_channel(iio_dev, chan); + if (ret) + goto err; + + /* Claim the channel */ + drv_data->claimed |= 1 << chan->address; + data->claimed |= 1 << chan->address; + + /* Map the channel */ + for (i = 0; i < 8; i++) + if (!(drv_data->ch[i].mapping & MAPPING_USED)) + break; + + drv_data->ch[i].mapping = MAPPING_USED | + MAPPING_SET_SLOT(data->id) | + MAPPING_CHAN(chan->address); + + /* Setup the mapping */ + __mxs_clrl(LRADC_CTRL4_LRADCSELECT_MASK(i), + drv_data->mmio_base + LRADC_CTRL4); + __mxs_setl(chan->address << LRADC_CTRL4_LRADCSELECT_OFFSET(i), + drv_data->mmio_base + LRADC_CTRL4); + + spin_unlock_irqrestore(&drv_data->lock, gflags); + + overspl = + ((drv_data->ch_oversample[chan->address] ? 1 : 0) + * LRADC_CH_ACCUMULATE) | + (drv_data->ch_oversample[chan->address] + << LRADC_CH_NUM_SAMPLES_OFFSET); + writel(overspl, drv_data->mmio_base + LRADC_CH(i)); + + /* Enable IRQ on the channel */ + __mxs_clrl(LRADC_CTRL1_LRADC_IRQ(i), + drv_data->mmio_base + LRADC_CTRL1); + __mxs_setl(LRADC_CTRL1_LRADC_IRQ_EN(i), + drv_data->mmio_base + LRADC_CTRL1); + + /* Set out channel to be triggers by this delay queue */ + __mxs_setl(1 << (LRADC_DELAY_TRIGGER_LRADCS_OFFSET + i), + drv_data->mmio_base + LRADC_DELAY(data->id)); + + return 0; + +err: + spin_unlock_irqrestore(&drv_data->lock, gflags); + return -EINVAL; +} + +static void mxs_lradc_relinquish_channel(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, int chanidx) +{ + struct mxs_lradc_data *data = iio_priv(iio_dev); + struct mxs_lradc_drv_data *drv_data = data->drv_data; + unsigned long gflags; + int i; + + drv_data->ch[chanidx].users--; + if (drv_data->ch[chanidx].users == 0) { + /* No more users for this channel, stop generating interrupts */ + __mxs_setl(LRADC_CTRL1_LRADC_IRQ(i) | + LRADC_CTRL1_LRADC_IRQ_EN(i), + drv_data->mmio_base + LRADC_CTRL1); + + /* Don't trigger this channel */ + __mxs_clrl(1 << (LRADC_DELAY_TRIGGER_LRADCS_OFFSET + i), + drv_data->mmio_base + LRADC_DELAY(data->id)); + + spin_lock_irqsave(&drv_data->lock, gflags); + + /* Relinquish this channel */ + drv_data->claimed &= ~(1 << chan->address); + data->claimed &= ~(1 << chan->address); + drv_data->ch[chanidx].mapping = 0; + + spin_unlock_irqrestore(&drv_data->lock, gflags); + } +} + +/* + * 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_data *data = iio_priv(iio_dev); + struct mxs_lradc_drv_data *drv_data = data->drv_data; + unsigned long lflags; + int i, ret; + + switch (m) { + case 0: + spin_lock_irqsave(&data->lock, lflags); + + ret = mxs_lradc_claim_channel(iio_dev, chan); + if (ret) { + spin_unlock_irqrestore(&data->lock, lflags); + return ret; + } + + /* + * Once we are here, the channel is mapped by us already. + * Find the mapping. + */ + for (i = 0; i < 8; i++) { + if (!(drv_data->ch[i].mapping & MAPPING_USED)) + continue; + + if (MAPPING_CHAN(drv_data->ch[i].mapping) == + chan->address) + break; + } + + /* Wait until sampling is done */ + drv_data->ch[i].wq_done = false; + + drv_data->ch[i].users++; + + spin_unlock_irqrestore(&data->lock, lflags); + + ret = wait_event_interruptible(drv_data->ch[i].wq, + drv_data->ch[i].wq_done); + if (ret) + ret = -EINTR; + else { + *val = readl(drv_data->mmio_base + LRADC_CH(i)) & + LRADC_CH_VALUE_MASK; + *val /= (drv_data->ch_oversample[chan->address] + 1); + ret = IIO_VAL_INT; + } + + spin_lock_irqsave(&data->lock, lflags); + + mxs_lradc_relinquish_channel(iio_dev, chan, i); + + spin_unlock_irqrestore(&data->lock, lflags); + + return ret; + + case IIO_CHAN_INFO_OVERSAMPLE_COUNT: + *val = drv_data->ch_oversample[chan->address]; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int mxs_lradc_write_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long m) +{ + struct mxs_lradc_data *data = iio_priv(iio_dev); + struct mxs_lradc_drv_data *drv_data = data->drv_data; + + switch (m) { + case IIO_CHAN_INFO_OVERSAMPLE_COUNT: + if ((val <= 0) || (val >= 32)) + return -EINVAL; + + drv_data->ch_oversample[chan->address] = val - 1; + return 0; + } + + return -EINVAL; +} + +static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) +{ + struct mxs_lradc_drv_data *drv_data = data; + uint32_t reg = readl(drv_data->mmio_base + LRADC_CTRL1); + int i; + + for (i = 0; i < 8; i++) + if (reg & LRADC_CTRL1_LRADC_IRQ(i)) { + drv_data->ch[i].wq_done = true; + wake_up_interruptible(&drv_data->ch[i].wq); + } + + __mxs_clrl(0xffff, drv_data->mmio_base + LRADC_CTRL1); + + return IRQ_HANDLED; +} + +static const struct iio_info mxs_lradc_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = mxs_lradc_read_raw, + .write_raw = mxs_lradc_write_raw, +}; + +static const struct iio_chan_spec mxs_lradc_chan_spec[] = { + [0] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 0, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 0, 0, IIO_ST('u', 18, 32, 0), 0), + [1] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 1, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 1, 1, IIO_ST('u', 18, 32, 0), 0), + [2] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 2, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 2, 2, IIO_ST('u', 18, 32, 0), 0), + [3] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 3, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 3, 3, IIO_ST('u', 18, 32, 0), 0), + [4] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 4, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 4, 4, IIO_ST('u', 18, 32, 0), 0), + [5] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 5, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 5, 5, IIO_ST('u', 18, 32, 0), 0), + [6] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 6, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 6, 6, IIO_ST('u', 18, 32, 0), 0), + /* VBATT */ + [7] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 7, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 7, 7, IIO_ST('u', 18, 32, 0), 0), + /* Temp sense 0 */ + [8] = IIO_CHAN(IIO_TEMP, IIO_NO_MOD, 1, IIO_RAW, NULL, 8, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 8, 8, IIO_ST('u', 18, 32, 0), 0), + /* Temp sense 1 */ + [9] = IIO_CHAN(IIO_TEMP, IIO_NO_MOD, 1, IIO_RAW, NULL, 9, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 9, 9, IIO_ST('u', 18, 32, 0), 0), + /* VDDIO */ + [10] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 10, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 10, 10, IIO_ST('u', 18, 32, 0), 0), + /* VTH */ + [11] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 11, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 11, 11, IIO_ST('u', 18, 32, 0), 0), + /* VDDA */ + [12] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 12, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 12, 12, IIO_ST('u', 18, 32, 0), 0), + /* VDDD */ + [13] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 13, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 13, 13, IIO_ST('u', 18, 32, 0), 0), + /* VBG */ + [14] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 14, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 14, 14, IIO_ST('u', 18, 32, 0), 0), + /* VDD5V */ + [15] = IIO_CHAN(IIO_VOLTAGE, IIO_NO_MOD, 1, IIO_RAW, NULL, 15, 0, + IIO_CHAN_INFO_SCALE_SEPARATE_BIT | + IIO_CHAN_INFO_OVERSAMPLE_COUNT_SEPARATE_BIT, + 15, 15, IIO_ST('u', 18, 32, 0), 0), +}; + +static void mxs_lradc_config(struct mxs_lradc_drv_data *drv_data) +{ + /* FIXME */ + int freq = 0x3; /* 6MHz */ + int onchip_ground_ref = 0; + + int i; + + mxs_reset_block(drv_data->mmio_base + LRADC_CTRL0); + + if (onchip_ground_ref) + __mxs_setl(1 << 26, drv_data->mmio_base + LRADC_CTRL0); + else + __mxs_clrl(1 << 26, drv_data->mmio_base + LRADC_CTRL0); + + __mxs_clrl(0x3 << 8, drv_data->mmio_base + LRADC_CTRL3); + __mxs_setl(freq, drv_data->mmio_base + LRADC_CTRL3); + + /* The delay channels constantly retrigger themself */ + for (i = 0; i < 4; i++) + __mxs_setl(LRADC_DELAY_KICK | + (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + i)) | + 0x7ff, /* FIXME */ + drv_data->mmio_base + LRADC_DELAY(i)); + + /* Start temperature sensing */ + writel(0, drv_data->mmio_base + LRADC_CTRL2); +} + +/*static void mxs_lradc_config(struct mxs_lradc_pdata *pdata) +{ + +} +*/ +static int __devinit mxs_lradc_probe(struct platform_device *pdev) +{ + struct mxs_lradc_data *data[4]; + struct mxs_lradc_drv_data *drv_data; + struct iio_dev *iio; + struct resource *r; + int ret = 0; + int irq; + int i; + + /* + * DEVM management + */ + if (!devres_open_group(&pdev->dev, mxs_lradc_probe, GFP_KERNEL)) { + dev_err(&pdev->dev, "Can't open resource group\n"); + goto err0; + } + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) { + dev_err(&pdev->dev, "Failed to allocate driver data\n"); + ret = -ENOMEM; + goto err0; + } + + spin_lock_init(&drv_data->lock); + + /* + * IIO ops + */ + for (i = 0; i < 4; i++) { + iio = iio_allocate_device(sizeof(*data)); + if (!iio) { + dev_err(&pdev->dev, + "Failed to allocate IIO device %i\n", i); + ret = -ENOMEM; + goto err1; + } + + iio->name = pdev->name; + iio->dev.parent = &pdev->dev; + iio->info = &mxs_lradc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + /* Channels */ + iio->channels = mxs_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mxs_lradc_chan_spec); + iio->attrs = mxs_lradc_attributes; + + data[i] = iio_priv(iio); + data[i]->drv_data = drv_data; + data[i]->id = i; + + spin_lock_init(&data[i]->lock); + + drv_data->iio[i] = iio; + } + + for (i = 0; i < 8; i++) + init_waitqueue_head(&drv_data->ch[i].wq); + + dev_set_drvdata(&pdev->dev, drv_data); + + /* + * Allocate address space + */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "No I/O memory resource defined\n"); + ret = -ENODEV; + goto err1; + } + + r = devm_request_mem_region(&pdev->dev, r->start, + resource_size(r), pdev->name); + if (r == NULL) { + dev_err(&pdev->dev, "Failed to request I/O memory\n"); + ret = -EBUSY; + goto err1; + } + + drv_data->mmio_base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!drv_data->mmio_base) { + dev_err(&pdev->dev, "Failed to map I/O memory\n"); + ret = -EBUSY; + goto err1; + } + + /* + * Allocate IRQ + */ + for (irq = 0; irq < 8; irq++) { + r = platform_get_resource(pdev, IORESOURCE_IRQ, irq); + if (r == NULL) { + dev_err(&pdev->dev, "No IRQ resource defined\n"); + ret = -ENODEV; + goto err1; + } + + ret = request_irq(r->start, mxs_lradc_handle_irq, 0, r->name, drv_data); + if (ret) { + dev_err(&pdev->dev, "request_irq %i failed: %d\n", irq, ret); + ret = -EBUSY; + goto err1; + } + } + + /* + * Register IIO device + */ + for (i = 0; i < 4; i++) { + ret = iio_device_register(drv_data->iio[i]); + if (ret) { + dev_err(&pdev->dev, "Failed to register IIO device\n"); + ret = -EBUSY; + goto err2; + } + } + + devres_remove_group(&pdev->dev, mxs_lradc_probe); + + mxs_lradc_config(drv_data); + + return 0; + +err2: + while (--i >= 0) + iio_device_unregister(drv_data->iio[i]); +err1: + for (i = 0; i < 4; i++) + if (drv_data->iio[i]) + iio_free_device(drv_data->iio[i]); +err0: + devres_release_group(&pdev->dev, mxs_lradc_probe); + return ret; +} + +static int __devexit mxs_lradc_remove(struct platform_device *pdev) +{ + struct mxs_lradc_drv_data *drv_data = dev_get_drvdata(&pdev->dev); + struct resource *r; + int i, irq; + + for (i = 0; i < 4; i++) + iio_device_unregister(drv_data->iio[i]); + + for (irq = 0; irq < 8; irq++) { + r = platform_get_resource(pdev, IORESOURCE_IRQ, irq); + if (r != NULL) + free_irq(r->start, drv_data); + } + + for (i = 0; i < 4; i++) + iio_free_device(drv_data->iio[i]); + + return 0; +} + +static struct platform_driver mxs_lradc_driver = { + .driver = { + .name = "mxs-lradc", + }, + .probe = mxs_lradc_probe, + .remove = __devexit_p(mxs_lradc_remove), +}; + +module_platform_driver(mxs_lradc_driver); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@xxxxxxxxx>"); +MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.8.3 -- 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