On 09/15/2011 06:01 PM, Jonathan Cameron wrote:
On 09/15/11 16:04, michael.hennerich@xxxxxxxxxx wrote:From: Michael Hennerich<michael.hennerich@xxxxxxxxxx> The existing ad7745 driver didn't conform with the IIO spec for such devices. It was way simpler to rewrite the existing driver, than actually fixing it.Fair enough. Mostly looks fine. A few comments inline and the issue with the _bias elements... Few things that need documenting in here though.Signed-off-by: Michael Hennerich<michael.hennerich@xxxxxxxxxx> --- drivers/staging/iio/adc/Kconfig | 2 +- drivers/staging/iio/adc/Makefile | 2 +- drivers/staging/iio/adc/ad7745.c | 672 --------------------------------- drivers/staging/iio/adc/ad7746.c | 776 ++++++++++++++++++++++++++++++++++++++ drivers/staging/iio/adc/ad7746.h | 29 ++ 5 files changed, 807 insertions(+), 674 deletions(-) delete mode 100644 drivers/staging/iio/adc/ad7745.c create mode 100644 drivers/staging/iio/adc/ad7746.c create mode 100644 drivers/staging/iio/adc/ad7746.h diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index 7867ab1..0482073 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -144,7 +144,7 @@ config AD7793 To compile this driver as a module, choose M here: the module will be called AD7793. -config AD7745 +config AD7746 tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver" depends on I2C help diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index 990d3fa..5ba3cdb 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -33,7 +33,7 @@ obj-$(CONFIG_AD7150) += ad7150.o obj-$(CONFIG_AD7152) += ad7152.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7314) += ad7314.o -obj-$(CONFIG_AD7745) += ad7745.o +obj-$(CONFIG_AD7746) += ad7746.o obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7816) += ad7816.osnip 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/3Err, 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 1Don'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->configreally 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->configsame for formatting here...+>> 6)& 0x3][0]); +} + +static ssize_t ad7746_store_vt_filter_rate_setup(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ad7746_chip_info *chip = iio_priv(indio_dev); + u8 data; + int ret, i; + + ret = kstrtou8(buf, 10,&data); + if (ret< 0) + return ret; + + for (i = 0; i< ARRAY_SIZE(ad7746_vt_filter_rate_table); i++) + if (data>= ad7746_vt_filter_rate_table[i][0]) + break; + + if (i>= ARRAY_SIZE(ad7746_vt_filter_rate_table)) + i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1; + + mutex_lock(&indio_dev->mlock); + chip->config&= ~AD7746_CONF_VTFS(0x3); + chip->config |= AD7746_CONF_VTFS(i); + mutex_unlock(&indio_dev->mlock); + + return len; +} + +static IIO_DEVICE_ATTR(in_capacitance_sampling_frequency, + S_IRUGO | S_IWUSR, ad7746_show_cap_filter_rate_setup, + ad7746_store_cap_filter_rate_setup, 0); + +static IIO_DEVICE_ATTR(in_voltage_sampling_frequency, + S_IRUGO | S_IWUSR, ad7746_show_vt_filter_rate_setup, + ad7746_store_vt_filter_rate_setup, 0); + +static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8"); +static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available, + "91 84 50 26 16 13 11 9"); + +static struct attribute *ad7746_attributes[] = { +&iio_dev_attr_in_capacitance_sampling_frequency.dev_attr.attr, +&iio_dev_attr_in_voltage_sampling_frequency.dev_attr.attr, +&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, +&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, +&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, +&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, +&iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr, +&iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr, +&iio_const_attr_in_capacitance_sampling_frequency_available. + dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7746_attribute_group = { + .attrs = ad7746_attributes, +}; + +static int ad7746_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, reg; + + mutex_lock(&indio_dev->mlock); + + switch (mask) { + case (1<< IIO_CHAN_INFO_CALIBSCALE_SEPARATE): + if (val != 1) { + ret = -EINVAL; + goto out; + } + + val = (val2 * 1024) / 15625; + + switch (chan->type) { + case IIO_CAPACITANCE: + reg = AD7746_REG_CAP_GAINH; + break; + case IIO_VOLTAGE: + reg = AD7746_REG_VOLT_GAINH; + break; + default: + ret = -EINVAL; + goto out; + } + + ret = i2c_smbus_write_word_data(chip->client, reg, swab16(val)); + if (ret< 0) + goto out; + + ret = 0; + break; + case (1<< IIO_CHAN_INFO_CALIBBIAS_SHARED): + if ((val< 0) | (val> 0xFFFF)) { + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_word_data(chip->client, + AD7746_REG_CAP_OFFH, swab16(val)); + if (ret< 0) + goto out; + + ret = 0; + break; + case (1<< IIO_CHAN_INFO_BIAS_SEPARATE): + if ((val< 0) | (val> 0x7F)) { + ret = -EINVAL; + goto out; + } + + chip->capdac[chan->channel][chan->differential] = (val> 0 ? + val | AD7746_CAPDAC_DACEN : 0); + + ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAPDACA, + chip->capdac[chan->channel][0]); + if (ret< 0) + goto out; + ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_CAPDACB, + chip->capdac[chan->channel][1]); + if (ret< 0) + goto out; + + chip->capdac_set = chan->channel; + + ret = 0; + break; + default: + ret = -EINVAL; + } + +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} +static int ad7746_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct ad7746_chip_info *chip = iio_priv(indio_dev); + int ret, delay; + u8 regval, reg; + + union { + u32 d32; + u8 d8[4]; + } data; + + mutex_lock(&indio_dev->mlock); + + switch (mask) { + case 0: + ret = ad7746_select_channel(indio_dev, chan); + if (ret< 0) + goto out; + delay = ret; + + regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV; + ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, + regval); + if (ret< 0) + goto out; + + msleep(delay); + /* Now read the actual register */ + + ret = i2c_smbus_read_i2c_block_data(chip->client, + chan->address>> 8, 3,&data.d8[1]); + + if (ret< 0) + goto out; + + *val = (be32_to_cpu(data.d32)& 0xFFFFFF) - 0x800000; + + switch (chan->type) { + case IIO_TEMP: + /* temperature in milli degrees Celsius + * T = ((*val / 2048) - 4096) * 1000 + */ + *val = (*val * 125) / 256; + break; + case IIO_VOLTAGE: + if (chan->channel == 1) /* supply_raw*/ + *val = (*val + 0x800000) * 6; + break; + default: + break; + } + + ret = IIO_VAL_INT; + break; + case (1<< IIO_CHAN_INFO_CALIBSCALE_SEPARATE): + switch (chan->type) { + case IIO_CAPACITANCE: + reg = AD7746_REG_CAP_GAINH; + break; + case IIO_VOLTAGE: + reg = AD7746_REG_VOLT_GAINH; + break; + default: + ret = -EINVAL; + goto out; + } + + ret = i2c_smbus_read_word_data(chip->client, reg); + if (ret< 0) + goto out; + /* 1 + gain_val / 2^16 */ + *val = 1; + *val2 = (15625 * swab16(ret)) / 1024; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case (1<< IIO_CHAN_INFO_CALIBBIAS_SHARED): + ret = i2c_smbus_read_word_data(chip->client, + AD7746_REG_CAP_OFFH); + if (ret< 0) + goto out; + *val = swab16(ret); + + ret = IIO_VAL_INT; + break; + case (1<< IIO_CHAN_INFO_BIAS_SEPARATE): + *val = chip->capdac[chan->channel][chan->differential]& 0x7F; + + ret = IIO_VAL_INT; + break; + case (1<< IIO_CHAN_INFO_SCALE_SHARED): + switch (chan->type) { + case IIO_CAPACITANCE: + /* 8pf / 2^24 */ + *val2 = 477; + *val = 0; + break; + case IIO_VOLTAGE: + /* 1170mV / 2^24 */ + *val2 = 69737; + *val = 0; + break; + default: + ret = -EINVAL; + goto out; + } + + ret = IIO_VAL_INT_PLUS_NANO; + break; + default: + ret = -EINVAL; + }; +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static const struct iio_info ad7746_info = { + .attrs =&ad7746_attribute_group, + .read_raw =&ad7746_read_raw, + .write_raw =&ad7746_write_raw, + .driver_module = THIS_MODULE, +}; + +/* + * device probe and remove + */ + +static int __devinit ad7746_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7746_platform_data *pdata = client->dev.platform_data; + struct ad7746_chip_info *chip; + struct iio_dev *indio_dev; + int ret = 0; + unsigned char regval = 0; + + indio_dev = iio_allocate_device(sizeof(*chip)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + chip = iio_priv(indio_dev); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + chip->client = client; + chip->capdac_set = -1; + + /* Establish that the iio_dev is a child of the i2c device */ + indio_dev->name = id->name; + indio_dev->dev.parent =&client->dev; + indio_dev->info =&ad7746_info; + indio_dev->channels = ad7746_channels; + if (id->driver_data == 7746) + indio_dev->num_channels = ARRAY_SIZE(ad7746_channels); + else + indio_dev->num_channels = ARRAY_SIZE(ad7746_channels) - 2; + indio_dev->num_channels = ARRAY_SIZE(ad7746_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + if (pdata) { + if (pdata->exca_en) { + if (pdata->exca_inv_en) + regval |= AD7746_EXCSETUP_NEXCA; + else + regval |= AD7746_EXCSETUP_EXCA; + } + + if (pdata->excb_en) { + if (pdata->excb_inv_en) + regval |= AD7746_EXCSETUP_NEXCB; + else + regval |= AD7746_EXCSETUP_EXCB; + } + + regval |= AD7746_EXCSETUP_EXCLVL(pdata->exclvl); + } else { + dev_warn(&client->dev, "No platform data? using default\n"); + regval = AD7746_EXCSETUP_EXCA | AD7746_EXCSETUP_EXCB | + AD7746_EXCSETUP_EXCLVL(3); + } + + ret = i2c_smbus_write_byte_data(chip->client, + AD7746_REG_EXC_SETUP, regval); + if (ret< 0) + goto error_free_dev; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_free_dev; + + dev_info(&client->dev, "%s capacitive sensor registered\n", id->name); + + return 0; + +error_free_dev: + iio_free_device(indio_dev); +error_ret: + return ret; +} + +static int __devexit ad7746_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct i2c_device_id ad7746_id[] = { + { "ad7745", 7745 }, + { "ad7746", 7746 }, + { "ad7747", 7747 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad7746_id); + +static struct i2c_driver ad7746_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = ad7746_probe, + .remove = __devexit_p(ad7746_remove), + .id_table = ad7746_id, +}; + +static __init int ad7746_init(void) +{ + return i2c_add_driver(&ad7746_driver); +} + +static __exit void ad7746_exit(void) +{ + i2c_del_driver(&ad7746_driver); +} + +MODULE_AUTHOR("Michael Hennerich<hennerich@xxxxxxxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); + +module_init(ad7746_init); +module_exit(ad7746_exit); diff --git a/drivers/staging/iio/adc/ad7746.h b/drivers/staging/iio/adc/ad7746.h new file mode 100644 index 0000000..b892a84 --- /dev/null +++ b/drivers/staging/iio/adc/ad7746.h @@ -0,0 +1,29 @@ +/* + * AD7746 capacitive sensor driver supporting AD7746/3 + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef IIO_CDC_AD7746_H_ +#define IIO_CDC_AD7746_H_ + +/* + * TODO: struct ad7746_platform_data needs to go into include/linux/iio + */ + +#define AD7466_EXCLVL_0 0 /* +-VDD/8 */ +#define AD7466_EXCLVL_1 1 /* +-VDD/4 */ +#define AD7466_EXCLVL_2 2 /* +-VDD * 3/8 */ +#define AD7466_EXCLVL_3 3 /* +-VDD/2 */ + +struct ad7746_platform_data { + unsigned char exclvl; /*Excitation Voltage Level */ + bool exca_en; /* enables EXCA pin as the excitation output */ + bool exca_inv_en; /* enables /EXCA pin as the excitation output */ + bool excb_en; /* enables EXCB pin as the excitation output */ + bool excb_inv_en; /* enables /EXCB pin as the excitation output */ +}; + +#endif /* IIO_CDC_AD7746_H_ */
Thanks for the detailed review. -- Greetings, Michael -- Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368; Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin, Margaret Seif -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html