From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Message-ID: <4E732BA7.1070100@analog.com> Date: Fri, 16 Sep 2011 12:57:43 +0200 From: Michael Hennerich Reply-To: MIME-Version: 1.0 To: Jonathan Cameron CC: "linux-iio@vger.kernel.org" , "device-drivers-devel@blackfin.uclinux.org" , Drivers Subject: Re: [PATCH 2/2] iio: adc: Replace, rewrite ad7745 from scratch. References: <1316099094-10931-1-git-send-email-michael.hennerich@analog.com> <1316099094-10931-2-git-send-email-michael.hennerich@analog.com> <4E722143.2040008@cam.ac.uk> In-Reply-To: <4E722143.2040008@cam.ac.uk> Content-Type: text/plain; charset="ISO-8859-1"; format=flowed List-ID: On 09/15/2011 06:01 PM, Jonathan Cameron wrote: > On 09/15/11 16:04, michael.hennerich@analog.com wrote: >> From: Michael Hennerich >> >> 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 >> --- >> 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 >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#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"); >> +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