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
next prev parent 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).