From: Marek Vasut <marek.vasut@gmail.com>
To: linux-iio@vger.kernel.org
Cc: jic23@kernel.org, Marek Vasut <marek.vasut@gmail.com>,
Wolfgang Denk <wd@denx.de>, Stefano Babic <sbabic@denx.de>,
Fabio Estevam <festevam@gmail.com>
Subject: [PATCH 2/2] [RFC] Basic support for MX28 LRADC IIO interface
Date: Fri, 10 Feb 2012 06:52:06 +0100 [thread overview]
Message-ID: <1328853126-16578-2-git-send-email-marek.vasut@gmail.com> (raw)
In-Reply-To: <1328853126-16578-1-git-send-email-marek.vasut@gmail.com>
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Cc: Wolfgang Denk <wd@denx.de>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Fabio Estevam <festevam@gmail.com>
---
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@gmail.com>");
+MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
+MODULE_LICENSE("GPL v2");
--
1.7.8.3
next prev parent reply other threads:[~2012-02-10 5:52 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-02-10 5:52 [PATCH 1/2] [RFC] Add per-channel oversampling count Marek Vasut
2012-02-10 5:52 ` Marek Vasut [this message]
2012-02-10 11:00 ` [PATCH 2/2] [RFC] Basic support for MX28 LRADC IIO interface Jonathan Cameron
2012-02-10 11:17 ` Marek Vasut
2012-02-10 11:26 ` Jonathan Cameron
2012-02-10 9:51 ` [PATCH 1/2] [RFC] Add per-channel oversampling count Jonathan Cameron
2012-02-10 10:44 ` Marek Vasut
2012-02-10 11:02 ` Jonathan Cameron
2012-02-10 11:19 ` Marek Vasut
2012-02-10 11:31 ` Jonathan Cameron
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=1328853126-16578-2-git-send-email-marek.vasut@gmail.com \
--to=marek.vasut@gmail.com \
--cc=festevam@gmail.com \
--cc=jic23@kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=sbabic@denx.de \
--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).