Re: [PATCH 2/2] iio: adc: Replace, rewrite ad7745 from scratch.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 09/15/2011 06:01 PM, Jonathan Cameron wrote:
On 09/15/11 16:04, michael.hennerich@xxxxxxxxxx wrote:
From: Michael Hennerich<michael.hennerich@xxxxxxxxxx>

The existing ad7745 driver didn't conform with the IIO spec for such devices.
It was way simpler to rewrite the existing driver, than actually fixing it.
Fair enough.

Mostly looks fine.  A few comments inline and the issue with the _bias
elements...

Few things that need documenting in here though.
Signed-off-by: Michael Hennerich<michael.hennerich@xxxxxxxxxx>
---
  drivers/staging/iio/adc/Kconfig  |    2 +-
  drivers/staging/iio/adc/Makefile |    2 +-
  drivers/staging/iio/adc/ad7745.c |  672 ---------------------------------
  drivers/staging/iio/adc/ad7746.c |  776 ++++++++++++++++++++++++++++++++++++++
  drivers/staging/iio/adc/ad7746.h |   29 ++
  5 files changed, 807 insertions(+), 674 deletions(-)
  delete mode 100644 drivers/staging/iio/adc/ad7745.c
  create mode 100644 drivers/staging/iio/adc/ad7746.c
  create mode 100644 drivers/staging/iio/adc/ad7746.h

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 7867ab1..0482073 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -144,7 +144,7 @@ config AD7793
         To compile this driver as a module, choose M here: the
         module will be called AD7793.

-config AD7745
+config AD7746
       tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
       depends on I2C
       help
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 990d3fa..5ba3cdb 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -33,7 +33,7 @@ obj-$(CONFIG_AD7150) += ad7150.o
  obj-$(CONFIG_AD7152) += ad7152.o
  obj-$(CONFIG_AD7291) += ad7291.o
  obj-$(CONFIG_AD7314) += ad7314.o
-obj-$(CONFIG_AD7745) += ad7745.o
+obj-$(CONFIG_AD7746) += ad7746.o
  obj-$(CONFIG_AD7780) += ad7780.o
  obj-$(CONFIG_AD7793) += ad7793.o
  obj-$(CONFIG_AD7816) += ad7816.o
snip to new code.
+++ /dev>  diff --git a/drivers/staging/iio/adc/ad7746.c b/drivers/staging/iio/adc/ad7746.c
new file mode 100644
index 0000000..db37ea3
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7746.c
@@ -0,0 +1,776 @@
+/*
+ * AD7746 capacitive sensor driver supporting AD7746/3
Err, that comment looks 'interesting', given you id table has 7745, 7746 and 7747
Thanks, thought I fixed that before.

+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include<linux/interrupt.h>
+#include<linux/device.h>
+#include<linux/kernel.h>
+#include<linux/slab.h>
+#include<linux/sysfs.h>
+#include<linux/i2c.h>
+#include<linux/delay.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#include "ad7746.h"
+
+/*
+ * AD7746 Register Definition
+ */
+
+#define AD7746_REG_STATUS            0
+#define AD7746_REG_CAP_DATA_HIGH     1
Don't bother with defines you are never going to use.
Fine to have unused ones that migh tbe implemented later,
but these mid and low won't be.
+#define AD7746_REG_CAP_DATA_MID              2
+#define AD7746_REG_CAP_DATA_LOW              3
+#define AD7746_REG_VT_DATA_HIGH              4
+#define AD7746_REG_VT_DATA_MID               5
+#define AD7746_REG_VT_DATA_LOW               6
+#define AD7746_REG_CAP_SETUP         7
+#define AD7746_REG_VT_SETUP          8
+#define AD7746_REG_EXC_SETUP         9
+#define AD7746_REG_CFG                       10
+#define AD7746_REG_CAPDACA           11
+#define AD7746_REG_CAPDACB           12
+#define AD7746_REG_CAP_OFFH          13
+#define AD7746_REG_CAP_OFFL          14
+#define AD7746_REG_CAP_GAINH         15
+#define AD7746_REG_CAP_GAINL         16
+#define AD7746_REG_VOLT_GAINH                17
+#define AD7746_REG_VOLT_GAINL                18
+
+/* Status Register Bit Designations (AD7746_REG_STATUS) */
+#define AD7746_STATUS_EXCERR         (1<<  3)
+#define AD7746_STATUS_RDY            (1<<  2)
+#define AD7746_STATUS_RDYVT          (1<<  1)
+#define AD7746_STATUS_RDYCAP         (1<<  0)
+
+/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */
+#define AD7746_CAPSETUP_CAPEN                (1<<  7)
+#define AD7746_CAPSETUP_CIN2         (1<<  6) /* AD7746 only */
+#define AD7746_CAPSETUP_CAPDIFF              (1<<  5)
+#define AD7746_CAPSETUP_CACHOP               (1<<  1)
That's bit 0 on the datasheet I'm looking at.
Good catch.
+
+/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */
+#define AD7746_VTSETUP_VTEN          (1<<  7)
I may be missing something but you never use this macro, and
I think you mean to do so! Looks to me like you are writing the
channel selection stuff into completely the wrong place in the
register.
Great catch. I tested INT_TEMP, which unsurprisingly worked well.
However I also tested VDD_MON, and also got expected results.
But this must have been by accident...

+#define AD7746_VTSETUP_VTMD(x)               ((x)<<  5)
+#define AD7746_VTSETUP_VTMD_INT_TEMP 0
+#define AD7746_VTSETUP_VTMD_EXT_TEMP 1
+#define AD7746_VTSETUP_VTMD_VDD_MON  2
+#define AD7746_VTSETUP_VTMD_EXT_VIN  3
+#define AD7746_VTSETUP_EXTREF                (1<<  4)
+#define AD7746_VTSETUP_VTSHORT               (1<<  1)
+#define AD7746_VTSETUP_VTCHOP                (1<<  0)
+
+/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */
+#define AD7746_EXCSETUP_CLKCTRL              (1<<  7)
+#define AD7746_EXCSETUP_EXCON                (1<<  6)
+#define AD7746_EXCSETUP_EXCB         (1<<  5)
+#define AD7746_EXCSETUP_NEXCB                (1<<  4)
+#define AD7746_EXCSETUP_EXCA         (1<<  3)
+#define AD7746_EXCSETUP_NEXCA                (1<<  2)
+#define AD7746_EXCSETUP_EXCLVL(x)    (((x)&  0x3)<<  0)
+
+/* Config Register Bit Designations (AD7746_REG_CFG) */
+#define AD7746_CONF_VTFS(x)          ((x)<<  6)
+#define AD7746_CONF_CAPFS(x)         ((x)<<  3)
+#define AD7746_CONF_MODE_IDLE                (0<<  0)
+#define AD7746_CONF_MODE_CONT_CONV   (1<<  0)
+#define AD7746_CONF_MODE_SINGLE_CONV (2<<  0)
+#define AD7746_CONF_MODE_PWRDN               (3<<  0)
+#define AD7746_CONF_MODE_OFFS_CAL    (5<<  0)
+#define AD7746_CONF_MODE_GAIN_CAL    (6<<  0)
+
+/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */
+#define AD7746_CAPDAC_DACEN          (1<<  7)
   Nice check macro, but unused....
+#define AD7746_CAPDAC_DACP(x)                ((x)&  0x1F)
+
+/*
+ * struct ad7746_chip_info - chip specifc information
+ */
+
+struct ad7746_chip_info {
+     struct i2c_client *client;
+     /*
+      * Capacitive channel digital filter setup;
+      * conversion time/update rate setup per channel
+      */
+     u8      config;
+     u8      cap_setup;
+     u8      vt_setup;
+     u8      capdac[2][2];
+     s8      capdac_set;
+};
+
+static const struct iio_chan_spec ad7746_channels[] = {
+     {
+             .type = IIO_VOLTAGE,
+             .indexed = 1,
+             .channel = 0,
+             .info_mask = (1<<  IIO_CHAN_INFO_SCALE_SHARED) |
+             (1<<  IIO_CHAN_INFO_SCALE_SEPARATE),
+             .address = AD7746_REG_VT_DATA_HIGH<<  8 |
+                     AD7746_VTSETUP_VTMD_EXT_VIN,
+     }, {
+             .type = IIO_VOLTAGE,
+             .indexed = 1,
+             .channel = 1,
+             .extend_name = "supply",
+             .info_mask = (1<<  IIO_CHAN_INFO_SCALE_SHARED),
+             .address = AD7746_REG_VT_DATA_HIGH<<  8 |
+                     AD7746_VTSETUP_VTMD_VDD_MON,
+     }, {
+             .type = IIO_TEMP,
+             .indexed = 1,
+             .channel = 0,
+             .processed_val = IIO_PROCESSED,
+             .address = AD7746_REG_VT_DATA_HIGH<<  8 |
+                     AD7746_VTSETUP_VTMD_INT_TEMP,
+     }, {
+             .type = IIO_TEMP,
+             .indexed = 1,
+             .channel = 1,
+             .processed_val = IIO_PROCESSED,
+             .address = AD7746_REG_VT_DATA_HIGH<<  8 |
+                     AD7746_VTSETUP_VTMD_EXT_TEMP,
+     }, {
+             .type = IIO_CAPACITANCE,
+             .indexed = 1,
+             .channel = 0,
+             .info_mask = (1<<  IIO_CHAN_INFO_CALIBSCALE_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_CALIBBIAS_SHARED) |
+             (1<<  IIO_CHAN_INFO_BIAS_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_SCALE_SHARED),
+             .address = AD7746_REG_CAP_DATA_HIGH<<  8,
+     }, {
+             .type = IIO_CAPACITANCE,
+             .differential = 1,
+             .indexed = 1,
+             .channel = 0,
+             .channel2 = 2,
+             .info_mask = (1<<  IIO_CHAN_INFO_CALIBSCALE_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_CALIBBIAS_SHARED) |
+             (1<<  IIO_CHAN_INFO_BIAS_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_SCALE_SHARED),
+             .address = AD7746_REG_CAP_DATA_HIGH<<  8 |
+                     AD7746_CAPSETUP_CAPDIFF
+     }, {
+             .type = IIO_CAPACITANCE,
+             .indexed = 1,
+             .channel = 1,
+             .info_mask = (1<<  IIO_CHAN_INFO_CALIBSCALE_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_CALIBBIAS_SHARED) |
+             (1<<  IIO_CHAN_INFO_BIAS_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_SCALE_SHARED),
+             .address = AD7746_REG_CAP_DATA_HIGH<<  8 |
+                     AD7746_CAPSETUP_CIN2,
+     }, {
+             .type = IIO_CAPACITANCE,
+             .differential = 1,
+             .indexed = 1,
+             .channel = 1,
+             .channel2 = 3,
+             .info_mask = (1<<  IIO_CHAN_INFO_CALIBSCALE_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_CALIBBIAS_SHARED) |
+             (1<<  IIO_CHAN_INFO_BIAS_SEPARATE) |
+             (1<<  IIO_CHAN_INFO_SCALE_SHARED),
+             .address = AD7746_REG_CAP_DATA_HIGH<<  8 |
+                     AD7746_CAPSETUP_CAPDIFF | AD7746_CAPSETUP_CIN2,
+     }
+};
+
+/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
+static const unsigned char ad7746_vt_filter_rate_table[][2] = {
+     {50, 20 + 1}, {31, 32 + 1}, {16, 62 + 1}, {8, 122 + 1},
+};
+
+static const unsigned char ad7746_cap_filter_rate_table[][2] = {
+     {91, 11 + 1}, {84, 12 + 1}, {50, 20 + 1}, {26, 38 + 1},
+     {16, 62 + 1}, {13, 77 + 1}, {11, 92 + 1}, {9, 110 + 1},
+};
+
+static int ad7746_select_channel(struct iio_dev *indio_dev,
+                         struct iio_chan_spec const *chan)
+{
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+     int ret, delay;
+     u8 vt_setup, cap_setup;
+
+     switch (chan->type) {
+     case IIO_CAPACITANCE:
+             cap_setup = (chan->address&  0xFF) | AD7746_CAPSETUP_CAPEN;
+             vt_setup = chip->vt_setup&  ~AD7746_VTSETUP_VTEN;
+             delay = ad7746_cap_filter_rate_table[(chip->config>>  3)&
+                     0x7][1];
+
+             if (chip->capdac_set != chan->channel) {
+                     ret = i2c_smbus_write_byte_data(chip->client,
+                             AD7746_REG_CAPDACA,
+                             chip->capdac[chan->channel][0]);
+                     if (ret<  0)
+                             return ret;
+                     ret = i2c_smbus_write_byte_data(chip->client,
+                             AD7746_REG_CAPDACB,
+                             chip->capdac[chan->channel][1]);
+                     if (ret<  0)
+                             return ret;
+
+                     chip->capdac_set = chan->channel;
+             }
+             break;
+     case IIO_VOLTAGE:
+     case IIO_TEMP:
+             vt_setup = (chan->address&  0xFF) | AD7746_VTSETUP_VTEN;
+             cap_setup = chip->cap_setup&  ~AD7746_CAPSETUP_CAPEN;
+             delay = ad7746_cap_filter_rate_table[(chip->config>>  6)&
+                     0x3][1];
+             break;
+     default:
+             return -EINVAL;
+     }
+
+     if (chip->cap_setup != cap_setup) {
+             ret = i2c_smbus_write_byte_data(chip->client,
+                                             AD7746_REG_CAP_SETUP,
+                                             cap_setup);
+             if (ret<  0)
+                     return ret;
+
+             chip->cap_setup = cap_setup;
+     }
+
+     if (chip->vt_setup != vt_setup) {
+             ret = i2c_smbus_write_byte_data(chip->client,
+                                             AD7746_REG_VT_SETUP,
+                                             vt_setup);
+             if (ret<  0)
+                     return ret;
+
+             chip->vt_setup = vt_setup;
+     }
+
+     return delay;
+}
+
+static inline ssize_t ad7746_start_calib(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf,
+                                      size_t len,
+                                      u8 regval)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+     bool doit;
+     int ret, timeout = 10;
+
+     ret = strtobool(buf,&doit);
+     if (ret<  0)
+             return ret;
+
+     if (!doit)
+             return 0;
+
+     mutex_lock(&indio_dev->mlock);
+     regval |= chip->config;
+     ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+     if (ret<  0) {
+             mutex_unlock(&indio_dev->mlock);
+             return ret;
+     }
+
+     do {
+             mdelay(20);
   sleep?  If not comment to say why.
Of course sleep. Guess I have to fix this in the AD7152 driver as well.

+             ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG);
+             if (ret<  0) {
+                     mutex_unlock(&indio_dev->mlock);
+                     return ret;
+             }
+     } while ((ret == regval)&&  timeout--);
+
+     mutex_unlock(&indio_dev->mlock);
+     return len;
+}
+static ssize_t ad7746_start_offset_calib(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf,
+                                      size_t len)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     int ret = ad7746_select_channel(indio_dev,
+&ad7746_channels[to_iio_dev_attr(attr)->address]);
+     if (ret<  0)
+             return ret;
+
+     return ad7746_start_calib(dev, attr, buf, len,
+                               AD7746_CONF_MODE_OFFS_CAL);
+}
+static ssize_t ad7746_start_gain_calib(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf,
+                                    size_t len)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     int ret = ad7746_select_channel(indio_dev,
+&ad7746_channels[to_iio_dev_attr(attr)->address]);
+     if (ret<  0)
+             return ret;
+
+     return ad7746_start_calib(dev, attr, buf, len,
+                               AD7746_CONF_MODE_GAIN_CAL);
+}
+
I'd be inclinded to use an enum for the addresses here then make sure
the channels are pinned to the expected places in the iio_chan_spec array.
These numbers currently look like black magic.
ok
+static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
+                    S_IWUSR, NULL, ad7746_start_offset_calib, 4);
+static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
+                    S_IWUSR, NULL, ad7746_start_offset_calib, 6);
+static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
+                    S_IWUSR, NULL, ad7746_start_gain_calib, 4);
+static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
+                    S_IWUSR, NULL, ad7746_start_gain_calib, 6);
+static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration,
+                    S_IWUSR, NULL, ad7746_start_gain_calib, 0);
+
+static ssize_t ad7746_show_cap_filter_rate_setup(struct device *dev,
+             struct device_attribute *attr,
+             char *buf)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+
+     return sprintf(buf, "%d\n", ad7746_cap_filter_rate_table[(chip->config
really can't break this somewhere less ugly?
+>>  3)&  0x7][0]);
+}
+
+static ssize_t ad7746_store_cap_filter_rate_setup(struct device *dev,
+             struct device_attribute *attr,
+             const char *buf,
+             size_t len)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+     u8 data;
+     int ret, i;
+
+     ret = kstrtou8(buf, 10,&data);
+     if (ret<  0)
+             return ret;
+
+     for (i = 0; i<  ARRAY_SIZE(ad7746_cap_filter_rate_table); i++)
+             if (data>= ad7746_cap_filter_rate_table[i][0])
+                     break;
+
+     if (i>= ARRAY_SIZE(ad7746_cap_filter_rate_table))
+             i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
+
+     mutex_lock(&indio_dev->mlock);
+     chip->config&= ~AD7746_CONF_CAPFS(0x7);
+     chip->config |= AD7746_CONF_CAPFS(i);
+     mutex_unlock(&indio_dev->mlock);
+
+     return len;
+}
+
+static ssize_t ad7746_show_vt_filter_rate_setup(struct device *dev,
+             struct device_attribute *attr,
+             char *buf)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+
+     return sprintf(buf, "%d\n", ad7746_vt_filter_rate_table[(chip->config
same for formatting here...
+>>  6)&  0x3][0]);
+}
+
+static ssize_t ad7746_store_vt_filter_rate_setup(struct device *dev,
+             struct device_attribute *attr,
+             const char *buf,
+             size_t len)
+{
+     struct iio_dev *indio_dev = dev_get_drvdata(dev);
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+     u8 data;
+     int ret, i;
+
+     ret = kstrtou8(buf, 10,&data);
+     if (ret<  0)
+             return ret;
+
+     for (i = 0; i<  ARRAY_SIZE(ad7746_vt_filter_rate_table); i++)
+             if (data>= ad7746_vt_filter_rate_table[i][0])
+                     break;
+
+     if (i>= ARRAY_SIZE(ad7746_vt_filter_rate_table))
+             i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
+
+     mutex_lock(&indio_dev->mlock);
+     chip->config&= ~AD7746_CONF_VTFS(0x3);
+     chip->config |= AD7746_CONF_VTFS(i);
+     mutex_unlock(&indio_dev->mlock);
+
+     return len;
+}
+
+static IIO_DEVICE_ATTR(in_capacitance_sampling_frequency,
+                    S_IRUGO | S_IWUSR, ad7746_show_cap_filter_rate_setup,
+                     ad7746_store_cap_filter_rate_setup, 0);
+
+static IIO_DEVICE_ATTR(in_voltage_sampling_frequency,
+                    S_IRUGO | S_IWUSR, ad7746_show_vt_filter_rate_setup,
+                    ad7746_store_vt_filter_rate_setup, 0);
+
+static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8");
+static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available,
+                    "91 84 50 26 16 13 11 9");
+
+static struct attribute *ad7746_attributes[] = {
+&iio_dev_attr_in_capacitance_sampling_frequency.dev_attr.attr,
+&iio_dev_attr_in_voltage_sampling_frequency.dev_attr.attr,
+&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
+&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
+&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
+&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
+&iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr,
+&iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr,
+&iio_const_attr_in_capacitance_sampling_frequency_available.
+     dev_attr.attr,
+     NULL,
+};
+
+static const struct attribute_group ad7746_attribute_group = {
+     .attrs = ad7746_attributes,
+};
+
+static int ad7746_write_raw(struct iio_dev *indio_dev,
+                         struct iio_chan_spec const *chan,
+                         int val,
+                         int val2,
+                         long mask)
+{
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+     int ret, reg;
+
+     mutex_lock(&indio_dev->mlock);
+
+     switch (mask) {
+     case (1<<  IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
+             if (val != 1) {
+                     ret = -EINVAL;
+                     goto out;
+             }
+
+             val = (val2 * 1024) / 15625;
+
+             switch (chan->type) {
+             case IIO_CAPACITANCE:
+                     reg = AD7746_REG_CAP_GAINH;
+                     break;
+             case IIO_VOLTAGE:
+                     reg = AD7746_REG_VOLT_GAINH;
+                     break;
+             default:
+                     ret = -EINVAL;
+                     goto out;
+             }
+
+             ret = i2c_smbus_write_word_data(chip->client, reg, swab16(val));
+             if (ret<  0)
+                     goto out;
+
+             ret = 0;
+             break;
+     case (1<<  IIO_CHAN_INFO_CALIBBIAS_SHARED):
+             if ((val<  0) | (val>  0xFFFF)) {
+                     ret = -EINVAL;
+                     goto out;
+             }
+             ret = i2c_smbus_write_word_data(chip->client,
+                             AD7746_REG_CAP_OFFH, swab16(val));
+             if (ret<  0)
+                     goto out;
+
+             ret = 0;
+             break;
+     case (1<<  IIO_CHAN_INFO_BIAS_SEPARATE):
+             if ((val<  0) | (val>  0x7F)) {
+                     ret = -EINVAL;
+                     goto out;
+             }
+
+             chip->capdac[chan->channel][chan->differential] = (val>  0 ?
+                     val | AD7746_CAPDAC_DACEN : 0);
+
+             ret = i2c_smbus_write_byte_data(chip->client,
+                     AD7746_REG_CAPDACA,
+                     chip->capdac[chan->channel][0]);
+             if (ret<  0)
+                     goto out;
+             ret = i2c_smbus_write_byte_data(chip->client,
+                     AD7746_REG_CAPDACB,
+                     chip->capdac[chan->channel][1]);
+             if (ret<  0)
+                     goto out;
+
+             chip->capdac_set = chan->channel;
+
+             ret = 0;
+             break;
+     default:
+             ret = -EINVAL;
+     }
+
+out:
+     mutex_unlock(&indio_dev->mlock);
+     return ret;
+}
+static int ad7746_read_raw(struct iio_dev *indio_dev,
+                        struct iio_chan_spec const *chan,
+                        int *val, int *val2,
+                        long mask)
+{
+     struct ad7746_chip_info *chip = iio_priv(indio_dev);
+     int ret, delay;
+     u8 regval, reg;
+
+     union {
+             u32 d32;
+             u8 d8[4];
+     } data;
+
+     mutex_lock(&indio_dev->mlock);
+
+     switch (mask) {
+     case 0:
+             ret = ad7746_select_channel(indio_dev, chan);
+             if (ret<  0)
+                     goto out;
+             delay = ret;
+
+             regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV;
+             ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG,
+                             regval);
+             if (ret<  0)
+                     goto out;
+
+             msleep(delay);
+             /* Now read the actual register */
+
+             ret = i2c_smbus_read_i2c_block_data(chip->client,
+                     chan->address>>  8, 3,&data.d8[1]);
+
+             if (ret<  0)
+                     goto out;
+
+             *val = (be32_to_cpu(data.d32)&  0xFFFFFF) - 0x800000;
+
+             switch (chan->type) {
+             case IIO_TEMP:
+             /* temperature in milli degrees Celsius
+              * T = ((*val / 2048) - 4096) * 1000
+              */
+                     *val = (*val * 125) / 256;
+                     break;
+             case IIO_VOLTAGE:
+                     if (chan->channel == 1) /* supply_raw*/
+                             *val = (*val + 0x800000) * 6;
+                             break;
+             default:
+                     break;
+             }
+
+             ret = IIO_VAL_INT;
+             break;
+     case (1<<  IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
+             switch (chan->type) {
+             case IIO_CAPACITANCE:
+                     reg = AD7746_REG_CAP_GAINH;
+                     break;
+             case IIO_VOLTAGE:
+                     reg = AD7746_REG_VOLT_GAINH;
+                     break;
+             default:
+                     ret = -EINVAL;
+                     goto out;
+             }
+
+             ret = i2c_smbus_read_word_data(chip->client, reg);
+             if (ret<  0)
+                     goto out;
+             /* 1 + gain_val / 2^16 */
+             *val = 1;
+             *val2 = (15625 * swab16(ret)) / 1024;
+
+             ret = IIO_VAL_INT_PLUS_MICRO;
+             break;
+     case (1<<  IIO_CHAN_INFO_CALIBBIAS_SHARED):
+             ret = i2c_smbus_read_word_data(chip->client,
+                                            AD7746_REG_CAP_OFFH);
+             if (ret<  0)
+                     goto out;
+             *val = swab16(ret);
+
+             ret = IIO_VAL_INT;
+             break;
+     case (1<<  IIO_CHAN_INFO_BIAS_SEPARATE):
+             *val = chip->capdac[chan->channel][chan->differential]&  0x7F;
+
+             ret = IIO_VAL_INT;
+             break;
+     case (1<<  IIO_CHAN_INFO_SCALE_SHARED):
+             switch (chan->type) {
+             case IIO_CAPACITANCE:
+                     /* 8pf / 2^24 */
+                     *val2 = 477;
+                     *val =  0;
+                     break;
+             case IIO_VOLTAGE:
+                     /* 1170mV / 2^24 */
+                     *val2 = 69737;
+                     *val =  0;
+                     break;
+             default:
+                     ret =  -EINVAL;
+                     goto out;
+             }
+
+             ret = IIO_VAL_INT_PLUS_NANO;
+             break;
+     default:
+             ret = -EINVAL;
+     };
+out:
+     mutex_unlock(&indio_dev->mlock);
+     return ret;
+}
+
+static const struct iio_info ad7746_info = {
+     .attrs =&ad7746_attribute_group,
+     .read_raw =&ad7746_read_raw,
+     .write_raw =&ad7746_write_raw,
+     .driver_module = THIS_MODULE,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7746_probe(struct i2c_client *client,
+             const struct i2c_device_id *id)
+{
+     struct ad7746_platform_data *pdata = client->dev.platform_data;
+     struct ad7746_chip_info *chip;
+     struct iio_dev *indio_dev;
+     int ret = 0;
+     unsigned char regval = 0;
+
+     indio_dev = iio_allocate_device(sizeof(*chip));
+     if (indio_dev == NULL) {
+             ret = -ENOMEM;
+             goto error_ret;
+     }
+     chip = iio_priv(indio_dev);
+     /* this is only used for device removal purposes */
+     i2c_set_clientdata(client, indio_dev);
+
+     chip->client = client;
+     chip->capdac_set = -1;
+
+     /* Establish that the iio_dev is a child of the i2c device */
+     indio_dev->name = id->name;
+     indio_dev->dev.parent =&client->dev;
+     indio_dev->info =&ad7746_info;
+     indio_dev->channels = ad7746_channels;
+     if (id->driver_data == 7746)
+             indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
+     else
+             indio_dev->num_channels =  ARRAY_SIZE(ad7746_channels) - 2;
+     indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
+     indio_dev->modes = INDIO_DIRECT_MODE;
+
+     if (pdata) {
+             if (pdata->exca_en) {
+                     if (pdata->exca_inv_en)
+                             regval |= AD7746_EXCSETUP_NEXCA;
+                     else
+                             regval |= AD7746_EXCSETUP_EXCA;
+             }
+
+             if (pdata->excb_en) {
+                     if (pdata->excb_inv_en)
+                             regval |= AD7746_EXCSETUP_NEXCB;
+                     else
+                             regval |= AD7746_EXCSETUP_EXCB;
+             }
+
+             regval |= AD7746_EXCSETUP_EXCLVL(pdata->exclvl);
+     } else {
+             dev_warn(&client->dev, "No platform data? using default\n");
+             regval = AD7746_EXCSETUP_EXCA | AD7746_EXCSETUP_EXCB |
+                     AD7746_EXCSETUP_EXCLVL(3);
+     }
+
+     ret = i2c_smbus_write_byte_data(chip->client,
+                                     AD7746_REG_EXC_SETUP, regval);
+     if (ret<  0)
+             goto error_free_dev;
+
+     ret = iio_device_register(indio_dev);
+     if (ret)
+             goto error_free_dev;
+
+     dev_info(&client->dev, "%s capacitive sensor registered\n", id->name);
+
+     return 0;
+
+error_free_dev:
+     iio_free_device(indio_dev);
+error_ret:
+     return ret;
+}
+
+static int __devexit ad7746_remove(struct i2c_client *client)
+{
+     struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+     iio_device_unregister(indio_dev);
+
+     return 0;
+}
+
+static const struct i2c_device_id ad7746_id[] = {
+     { "ad7745", 7745 },
+     { "ad7746", 7746 },
+     { "ad7747", 7747 },
+     {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7746_id);
+
+static struct i2c_driver ad7746_driver = {
+     .driver = {
+             .name = KBUILD_MODNAME,
+     },
+     .probe = ad7746_probe,
+     .remove = __devexit_p(ad7746_remove),
+     .id_table = ad7746_id,
+};
+
+static __init int ad7746_init(void)
+{
+     return i2c_add_driver(&ad7746_driver);
+}
+
+static __exit void ad7746_exit(void)
+{
+     i2c_del_driver(&ad7746_driver);
+}
+
+MODULE_AUTHOR("Michael Hennerich<hennerich@xxxxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7746_init);
+module_exit(ad7746_exit);
diff --git a/drivers/staging/iio/adc/ad7746.h b/drivers/staging/iio/adc/ad7746.h
new file mode 100644
index 0000000..b892a84
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7746.h
@@ -0,0 +1,29 @@
+/*
+ * AD7746 capacitive sensor driver supporting AD7746/3
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_CDC_AD7746_H_
+#define IIO_CDC_AD7746_H_
+
+/*
+ * TODO: struct ad7746_platform_data needs to go into include/linux/iio
+ */
+
+#define AD7466_EXCLVL_0              0 /* +-VDD/8 */
+#define AD7466_EXCLVL_1              1 /* +-VDD/4 */
+#define AD7466_EXCLVL_2              2 /* +-VDD * 3/8 */
+#define AD7466_EXCLVL_3              3 /* +-VDD/2 */
+
+struct ad7746_platform_data {
+     unsigned char exclvl;   /*Excitation Voltage Level */
+     bool exca_en;           /* enables EXCA pin as the excitation output */
+     bool exca_inv_en;       /* enables /EXCA pin as the excitation output */
+     bool excb_en;           /* enables EXCB pin as the excitation output */
+     bool excb_inv_en;       /* enables /EXCB pin as the excitation output */
+};
+
+#endif /* IIO_CDC_AD7746_H_ */


Thanks for the detailed review.

--
Greetings,
Michael

--
Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368;
Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin,
Margaret Seif


--
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


[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux