linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michael Hennerich <michael.hennerich@analog.com>
To: Jonathan Cameron <jic23@cam.ac.uk>
Cc: "linux-iio@vger.kernel.org" <linux-iio@vger.kernel.org>,
	"device-drivers-devel@blackfin.uclinux.org"
	<device-drivers-devel@blackfin.uclinux.org>,
	Drivers <Drivers@analog.com>
Subject: Re: [PATCH 2/2] iio: adc: Replace, rewrite ad7745 from scratch.
Date: Fri, 16 Sep 2011 12:57:43 +0200	[thread overview]
Message-ID: <4E732BA7.1070100@analog.com> (raw)
In-Reply-To: <4E722143.2040008@cam.ac.uk>

On 09/15/2011 06:01 PM, Jonathan Cameron wrote:
> On 09/15/11 16:04, michael.hennerich@analog.com wrote:
>> From: Michael Hennerich<michael.hennerich@analog.com>
>>
>> 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@analog.com>
>> ---
>>   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@blackfin.uclinux.org>");
>> +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

  reply	other threads:[~2011-09-16 10:57 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-09-15 15:04 [PATCH 1/2] iio: core: add _bias channel information michael.hennerich
2011-09-15 15:04 ` [PATCH 2/2] iio: adc: Replace, rewrite ad7745 from scratch michael.hennerich
2011-09-15 16:01   ` Jonathan Cameron
2011-09-16 10:57     ` Michael Hennerich [this message]
2011-09-15 15:31 ` [PATCH 1/2] iio: core: add _bias channel information Jonathan Cameron
2011-09-15 15:33   ` Hennerich, Michael
2011-09-15 16:04     ` 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=4E732BA7.1070100@analog.com \
    --to=michael.hennerich@analog.com \
    --cc=Drivers@analog.com \
    --cc=device-drivers-devel@blackfin.uclinux.org \
    --cc=jic23@cam.ac.uk \
    --cc=linux-iio@vger.kernel.org \
    /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).