Thank you Greg for comments.
Thank you Dan for comments.

I've done my best to apply yours comments.

Signed-off-by: Alexander Pazdnikov <[email protected]>
---
 drivers/staging/comedi/Kconfig          |   22 ++
 drivers/staging/comedi/drivers/Makefile |    3 +
 drivers/staging/comedi/drivers/ad7739.c |  402 +++++++++++++++++++++++++++++++
 include/linux/platform_data/ad7739.h    |    9 +
 4 files changed, 436 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/comedi/drivers/ad7739.c
 create mode 100644 include/linux/platform_data/ad7739.h

diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
index 4c77e50..d713031 100644
--- a/drivers/staging/comedi/Kconfig
+++ b/drivers/staging/comedi/Kconfig
@@ -1381,3 +1381,25 @@ config COMEDI_FC
 
          To compile this driver as a module, choose M here: the module will be
          called comedi_fc.
+
+menuconfig COMEDI_SPI_DRIVERS
+       tristate "Comedi SPI drivers"
+       depends on COMEDI && SPI
+       default N
+       ---help---
+         Enable comedi SPI drivers to be built
+
+         Note that the answer to this question won't directly affect the
+         kernel: saying N will just cause the configurator to skip all
+         the questions about SPI comedi drivers.
+
+config COMEDI_AD7739
+       tristate "AD7739 driver"
+       depends on COMEDI_SPI_DRIVERS
+       select SPI_SPIDEV
+       default N
+       ---help---
+         Enable support for AD7739 A/D converter.
+
+         To compile this driver as a module, choose M here: the module will be
+         called ad7739.
diff --git a/drivers/staging/comedi/drivers/Makefile 
b/drivers/staging/comedi/drivers/Makefile
index 170da60..844d51c 100644
--- a/drivers/staging/comedi/drivers/Makefile
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -138,3 +138,6 @@ obj-$(CONFIG_COMEDI_NI_LABPC)               += ni_labpc.o
 obj-$(CONFIG_COMEDI_8255)              += 8255.o
 obj-$(CONFIG_COMEDI_DAS08)             += das08.o
 obj-$(CONFIG_COMEDI_FC)                        += comedi_fc.o
+
+# Comedi SPI drivers
+obj-$(CONFIG_COMEDI_AD7739)            += ad7739.o
diff --git a/drivers/staging/comedi/drivers/ad7739.c 
b/drivers/staging/comedi/drivers/ad7739.c
new file mode 100644
index 0000000..5c7655c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ad7739.c
@@ -0,0 +1,402 @@
+/*
+    comedi/drivers/ad7739.c
+    Driver for AD7739 A/D converter chip on an SPI bus
+
+    Copyright (C) 2011 Prosoft Systems Ltd. <http://www.prosoftsystems.ru/>
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1998,2000 David A. Schleef <[email protected]>
+
+    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.
+*/
+/*
+Driver: ad7739
+Description: Analog Devices AD7739
+Author: Alexander Pazdnikov <[email protected]> <[email protected]>
+Devices: AD7739
+Updated: Thu, 15 Mar 2012 16:20:29 +0600
+Status: experimental
+
+Supports:
+
+  - ai_insn read
+       24 bit mode support only
+       using max measurement time only
+
+Configuration Options:
+  [0] - Bits: 0-7 - ChipSelect, 8-16 - SPI Bus Number
+
+*/
+/*
+    Usage example through board-setup.
+static const struct ad7739_platform_data dd11_adc = {
+       .chanselect_p0p1 = 1,
+};
+
+static struct spi_board_info spi_devices[] = {
+       {
+        .modalias = "spidev",
+        .irq = AT91_PIN_PC7,
+        .chip_select = 5,
+        .max_speed_hz = 0,
+        .bus_num = 1,
+        .platform_data = &dd11_adc,
+        },
+};
+
+*/
+
+#include "../comedidev.h"
+#include <linux/gpio.h>
+#include <linux/completion.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/ad7739.h>
+
+#define MAX_DATA 0xFFFFFF      /* 24-bit mode only */
+#define CONVERT_TIMEOUT_MSECS 100      /* spec max conv time = 2689 usecs */
+#define DRIVER_NAME KBUILD_MODNAME
+
+static const char *board_name = DRIVER_NAME;
+
+static int ad7739_attach(struct comedi_device *dev,
+                        struct comedi_devconfig *it);
+static int ad7739_detach(struct comedi_device *dev);
+
+static struct comedi_driver driver_ad7739 = {
+       .driver_name = DRIVER_NAME,
+       .module = THIS_MODULE,
+       .attach = ad7739_attach,
+       .detach = ad7739_detach,
+};
+
+static int __init ad7739_init_module(void)
+{
+       return comedi_driver_register(&driver_ad7739);
+}
+
+static void __exit ad7739_cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_ad7739);
+}
+
+module_init(ad7739_init_module);
+module_exit(ad7739_cleanup_module);
+
+/* analog input ranges */
+static const struct comedi_lrange range_ad7739 = {
+       6,
+       {
+               RANGE(-1.25, 1.25),
+               RANGE(0, 1.25),
+               RANGE(-0.625, 0.625),
+               RANGE(0, 0.625),
+               RANGE(-2.5, 2.5),
+               RANGE(0, 2.5),
+       }
+};
+
+struct ad7739_private {
+       struct completion ready;        /* channel data ready */
+       struct spi_device *spi; /* appropriate spi device */
+};
+
+static struct ad7739_private *devpriv(struct comedi_device *dev)
+{
+       return dev->private;
+}
+
+/* write buffer */
+static int ad7739_write_msg(struct comedi_device *dev, const u8 *buf,
+                           size_t len)
+{
+       return spi_write(devpriv(dev)->spi, buf, len);
+}
+
+#define COMM_REG_READ 0x40
+
+/* write 8-bit register */
+static int ad7739_write(struct comedi_device *dev, u8 reg, u8 val)
+{
+       struct spi_device *spi = devpriv(dev)->spi;
+       u8 out[2];
+
+       out[0] = reg & ~COMM_REG_READ;
+       out[1] = val;
+
+       dev_dbg(&spi->dev, "write reg %#x, val %#x\n", reg, val);
+
+       return spi_write(spi, out, sizeof(out));
+}
+
+/* read register of desired size */
+static int ad7739_read(struct comedi_device *dev, u8 reg, u8 *in,
+                      size_t size)
+{
+       struct spi_device *spi = devpriv(dev)->spi;
+       u8 cmd = reg | COMM_REG_READ;
+
+       int ret = spi_write_then_read(spi, &cmd, 1, in, size);
+
+       switch (size) {
+       case 1:
+               dev_dbg(&spi->dev, "read reg %#x, ret %#x, in %#x",
+                       reg, ret, in[0]);
+               break;
+       case 2:
+               dev_dbg(&spi->dev, "read reg %#x, ret %#x, in %#x, %#x",
+                       reg, ret, in[0], in[1]);
+               break;
+       case 3:
+               dev_dbg(&spi->dev, "read reg %#x, ret %#x, in %#x, %#x, %#x",
+                       reg, ret, in[0], in[1], in[2]);
+               break;
+       default:
+               dev_dbg(&spi->dev,
+                       "read reg %#x, ret %#x, in %#x, %#x, %#x, %#x", reg,
+                       ret, in[0], in[1], in[2], in[3]);
+       }
+
+       if (ret != 0)
+               dev_info(&spi->dev, "read error %i\n", ret);
+
+       return ret;
+}
+
+/* software reset */
+static void ad7739_reset(struct comedi_device *dev)
+{
+       static const char buf[] = { 0, 0xFF, 0xFF, 0xFF, 0xFF };
+
+       ad7739_write_msg(dev, buf, sizeof(buf));
+}
+
+#define CHAN_STATUS 0x20
+#define     STATUS_CHAN_NUM(x) (((x) & 0xE0) >> 5)
+#define     STATUS_NOREF 0x04
+#define     STATUS_SIGN 0x02
+#define     STATUS_OVERFLOW 0x01
+
+#define CHAN_DATA 0x08
+
+#define CHAN_SETUP 0x28
+#define     SETUP_COM0 0x40
+#define     SETUP_COM1 0x20
+#define     SETUP_DIFFER (SETUP_COM0 | SETUP_COM1)
+#define     SETUP_ENABLE 0x08
+
+#define TIME_CONVERSION 0x30
+#define     CHOP 0x80
+#define     FILTER_MAX  0x7F
+
+#define CHAN_MODE 0x38
+#define     MODE_SINGLE 0x40
+#define     MODE_DUMP  0x08
+#define     MODE_24BIT 0x02
+#define     MODE_CLAMP 0x01
+
+#define IOPORT 0x01
+#define     P0_STATE 0x80
+#define     P0_HIGH  0x80
+#define     P1_STATE 0x40
+#define     P1_HIGH 0x40
+#define     P0_INPUT 0x20
+#define     P1_INPUT 0x10
+#define     READY_ON_ALL_CHANS 0x80
+
+static int ai_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
+                       struct comedi_insn *insn, unsigned int *data)
+{
+       u8 buf[4];
+       u8 status;
+       int ret;
+       struct ad7739_platform_data *pdata;
+       const unsigned chan = CR_CHAN(insn->chanspec);
+       const unsigned range = CR_RANGE(insn->chanspec);
+       const unsigned aref = CR_AREF(insn->chanspec);
+
+       u8 setup = range
+                  + ((aref == AREF_DIFF) ? SETUP_DIFFER : 0) + SETUP_ENABLE;
+
+       /* select chan in demux */
+       pdata = devpriv(dev)->spi->dev.platform_data;
+
+       if (pdata->chanselect_p0p1)
+               ad7739_write(dev, IOPORT, (chan & 0x03) << 6);
+
+       ret = ad7739_write(dev, TIME_CONVERSION + chan, CHOP + FILTER_MAX);
+       if (ret != 0)
+               return ret;
+
+       ret = ad7739_write(dev, CHAN_SETUP + chan, setup);
+       if (ret != 0)
+               return ret;
+
+       INIT_COMPLETION(devpriv(dev)->ready);
+
+       /* start conversion */
+       ret = ad7739_write(dev, CHAN_MODE + chan,
+                          MODE_SINGLE | MODE_DUMP | MODE_24BIT | MODE_CLAMP);
+
+       if (ret != 0)
+               return ret;
+
+       ret = wait_for_completion_interruptible_timeout(&devpriv(dev)->ready,
+                       msecs_to_jiffies(CONVERT_TIMEOUT_MSECS));
+
+       if (0 == ret)
+               return -ETIME;
+       if (ret < 0)
+               return ret;
+
+       ret = ad7739_read(dev, CHAN_DATA + chan, buf, sizeof(buf));
+       if (ret != 0)
+               return ret;
+
+       *data = buf[1] + (buf[2] << 8) + (buf[3] << 16);
+       status = buf[0];
+
+       dev_dbg(dev->class_dev, "data %#x, status %#x", *data, status);
+
+       if (chan != STATUS_CHAN_NUM(status)) {
+               /* invalid chan, spi sync error */
+               dev_dbg(dev->class_dev, "chan %#x, reply chan %#x",
+                       chan, (status & 0xE0) >> 5);
+
+               return -EBADE;
+       }
+
+       if (status & STATUS_NOREF) {
+
+               dev_dbg(dev->class_dev, "NOREF");
+
+               return -ERANGE;
+       }
+
+       if (status & STATUS_OVERFLOW) {
+
+               *data = (status & STATUS_SIGN) ? 0 : MAX_DATA;
+
+               dev_dbg(dev->class_dev, "OVERFLOW");
+
+               return -EOVERFLOW;
+       }
+
+       if (status & STATUS_SIGN) {
+               /* polarity mismatch */
+               *data = ~*data + 1;
+       }
+
+       /* good conversion */
+
+       return 0;
+}
+
+static irqreturn_t ad7739_irq(int irq, void *data)
+{
+       struct ad7739_private *priv = data;
+
+       complete(&priv->ready);
+
+       /* dev_dbg(&priv->spi->dev, "IRQ handled %u", gpio_get_value(irq)); */
+
+       return IRQ_HANDLED;
+}
+
+static
+int ad7739_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+       struct comedi_subdevice *s = NULL;
+       struct ad7739_private *priv = NULL;
+       struct device *d = NULL;
+       char devname[64];
+       int ret;
+
+       ret = alloc_private(dev, sizeof(struct ad7739_private));
+       if (ret < 0)
+               return ret;
+
+       ret = alloc_subdevices(dev, 1);
+       if (ret < 0)
+               return ret;
+
+       priv = dev->private;
+
+       dev->board_name = board_name;
+
+       s = dev->subdevices;
+
+       /* ai */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE;
+       s->maxdata = MAX_DATA;
+       s->n_chan = 8;
+       s->insn_read = ai_insn_read;
+       s->range_table = &range_ad7739;
+
+       /* Bits: 0-7 - ChipSelect, 8-16 - SPI Bus Number */
+       dev->iobase = it->options[0];
+
+       snprintf(devname, sizeof(devname), "%s%u.%u", spi_bus_type.name,
+                (unsigned)((dev->iobase >> 8) & 0xFF),
+                (unsigned)(dev->iobase & 0xFF));
+
+       d = bus_find_device_by_name(&spi_bus_type, NULL, devname);
+       if (!d) {
+               dev_err(dev->class_dev, "devices %s not found\n", devname);
+               return -EINVAL;
+       }
+
+       priv->spi = to_spi_device(d);
+
+       /* hold device */
+       get_device(&priv->spi->dev);
+
+       /* Reset the chip */
+       ad7739_reset(dev);
+
+       init_completion(&priv->ready);
+
+       ret = request_irq(priv->spi->irq, ad7739_irq, 0, dev->board_name, priv);
+       if (ret) {
+               dev_err(dev->class_dev, "IRQ %u request failed\n",
+                       priv->spi->irq);
+               return ret;
+       }
+
+       dev->irq = priv->spi->irq;
+
+       dev_info(dev->class_dev, "connected to device %s, irq %u\n",
+                devname, priv->spi->irq);
+
+       return 0;
+}
+
+static int ad7739_detach(struct comedi_device *dev)
+{
+       struct ad7739_private *priv = devpriv(dev);
+
+       if (!priv)
+               return 0;
+
+       /* Free the interrupt */
+       if (dev->irq)
+               free_irq(dev->irq, priv);
+
+       if (priv->spi) {
+               dev_info(dev->class_dev, "removed %s\n",
+                        dev_name(&priv->spi->dev));
+
+               /* release device */
+               put_device(&priv->spi->dev);
+       }
+
+       return 0;
+}
+
+MODULE_AUTHOR("Alexander Pazdnikov <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AD7739 SPI based A/D converter chip");
+MODULE_ALIAS("spi:" DRIVER_NAME);
diff --git a/include/linux/platform_data/ad7739.h 
b/include/linux/platform_data/ad7739.h
new file mode 100644
index 0000000..2aa0a0d
--- /dev/null
+++ b/include/linux/platform_data/ad7739.h
@@ -0,0 +1,9 @@
+#ifndef AD7739_H
+#define AD7739_H
+
+struct ad7739_platform_data {
+       /* if p0,p1 output chans are used as multiplexer for ai chans */
+       u8 chanselect_p0p1;
+};
+
+#endif /* AD7739_H */
-- 
1.7.4.1

_______________________________________________
devel mailing list
[email protected]
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel

Reply via email to