One quick nitpick on submission format: Please put version in the email subject [PATCH V3] iio:... That way it's easy to spot which one to bother reading ;) On 09/16/11 15:47, 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. > > Changes since V1: > > Fix file header description. > Fix erroneous defines. > Pin channels to expected places in the iio_chan_spec array, to avoid magic numbers. > Use msleep instead of busy waiting mdelay. > Fix indention style. > Drop _bias and use _offset attribute. > > Changes since V2: > Increase accuracy of cap scale. > Fix channel spec for VIN. Some formatting nitpicks. I'll fix them up. I don't think Greg's tree is visible anywhere so I'll pull this through mine and push on to him. Thanks, > > Signed-off-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx> Signed-off-by: Jonathan Cameron <jic23@xxxxxxxxx> > --- > 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 | 800 ++++++++++++++++++++++++++++++++++++++ > drivers/staging/iio/adc/ad7746.h | 29 ++ > 5 files changed, 831 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 ... snip old driver out as we don't care ;).... > diff --git a/drivers/staging/iio/adc/ad7746.c b/drivers/staging/iio/adc/ad7746.c > new file mode 100644 > index 0000000..b2011f5 > --- /dev/null > +++ b/drivers/staging/iio/adc/ad7746.c > @@ -0,0 +1,800 @@ > +/* > + * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 > + * > + * 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 > +#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 << 0) > + > +/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ > +#define AD7746_VTSETUP_VTEN (1 << 7) > +#define AD7746_VTSETUP_VTMD_INT_TEMP (0 << 5) > +#define AD7746_VTSETUP_VTMD_EXT_TEMP (1 << 5) > +#define AD7746_VTSETUP_VTMD_VDD_MON (2 << 5) > +#define AD7746_VTSETUP_VTMD_EXT_VIN (3 << 5) > +#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) > +#define AD7746_CAPDAC_DACP(x) ((x) & 0x7F) > + > +/* > + * 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; > +}; > + > +enum ad7746_chan { > + VIN, > + VIN_VDD, > + TEMP_INT, > + TEMP_EXT, > + CIN1, > + CIN1_DIFF, > + CIN2, > + CIN2_DIFF, > +}; > + > +static const struct iio_chan_spec ad7746_channels[] = { > + [VIN] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED), > + .address = AD7746_REG_VT_DATA_HIGH << 8 | > + AD7746_VTSETUP_VTMD_EXT_VIN, > + }, > + [VIN_VDD] = { > + .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, > + }, > + [TEMP_INT] = { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 0, > + .processed_val = IIO_PROCESSED, > + .address = AD7746_REG_VT_DATA_HIGH << 8 | > + AD7746_VTSETUP_VTMD_INT_TEMP, > + }, > + [TEMP_EXT] = { > + .type = IIO_TEMP, > + .indexed = 1, > + .channel = 1, > + .processed_val = IIO_PROCESSED, > + .address = AD7746_REG_VT_DATA_HIGH << 8 | > + AD7746_VTSETUP_VTMD_EXT_TEMP, > + }, > + [CIN1] = { > + .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_OFFSET_SEPARATE) | > + (1 << IIO_CHAN_INFO_SCALE_SHARED), > + .address = AD7746_REG_CAP_DATA_HIGH << 8, > + }, > + [CIN1_DIFF] = { > + .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_OFFSET_SEPARATE) | > + (1 << IIO_CHAN_INFO_SCALE_SHARED), > + .address = AD7746_REG_CAP_DATA_HIGH << 8 | > + AD7746_CAPSETUP_CAPDIFF > + }, > + [CIN2] = { > + .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_OFFSET_SEPARATE) | > + (1 << IIO_CHAN_INFO_SCALE_SHARED), > + .address = AD7746_REG_CAP_DATA_HIGH << 8 | > + AD7746_CAPSETUP_CIN2, > + }, > + [CIN2_DIFF] = { > + .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_OFFSET_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 { > + msleep(20); > + 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; > +} Blank line here please. > +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); > +} Blank line. > +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); > +} > + > +static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, > + S_IWUSR, NULL, ad7746_start_offset_calib, CIN1); > +static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, > + S_IWUSR, NULL, ad7746_start_offset_calib, CIN2); > +static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, > + S_IWUSR, NULL, ad7746_start_gain_calib, CIN1); > +static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, > + S_IWUSR, NULL, ad7746_start_gain_calib, CIN2); > +static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration, > + S_IWUSR, NULL, ad7746_start_gain_calib, VIN); > + > +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 >> 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 >> 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_OFFSET_SEPARATE): > + if ((val < 0) | (val > 43008000)) { /* 21pF */ > + ret = -EINVAL; > + goto out; > + } > + > + /* CAPDAC Scale = 21pF_typ / 127 > + * CIN Scale = 8.192pF / 2^24 > + * Offset Scale = CAPDAC Scale / CIN Scale = 338646 > + * */ > + > + val /= 338646; > + > + chip->capdac[chan->channel][chan->differential] = (val > 0 ? > + AD7746_CAPDAC_DACP(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 * 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_OFFSET_SEPARATE): > + *val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel] > + [chan->differential]) * 338646; > + > + ret = IIO_VAL_INT; > + break; > + case (1 << IIO_CHAN_INFO_SCALE_SHARED): > + switch (chan->type) { > + case IIO_CAPACITANCE: > + /* 8.192pf / 2^24 */ > + *val2 = 488; > + *val = 0; > + break; > + case IIO_VOLTAGE: > + /* 1170mV / 2^23 */ > + *val2 = 139475; > + *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..ea8572d > --- /dev/null > +++ b/drivers/staging/iio/adc/ad7746.h > @@ -0,0 +1,29 @@ > +/* > + * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747 > + * > + * 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_ */ -- 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