All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jonathan Cameron <jic23@cam.ac.uk>
To: michael.hennerich@analog.com
Cc: linux-iio@vger.kernel.org,
	device-drivers-devel@blackfin.uclinux.org, drivers@analog.com
Subject: Re: [PATCH] iio: adc: Replace, rewrite ad7745 from scratch.
Date: Mon, 19 Sep 2011 13:19:22 +0100	[thread overview]
Message-ID: <4E77334A.1000207@cam.ac.uk> (raw)
In-Reply-To: <1316184432-22349-1-git-send-email-michael.hennerich@analog.com>


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@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.
> 
> 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@analog.com>
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  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@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..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_ */


  reply	other threads:[~2011-09-19 12:19 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-09-16 14:47 [PATCH] iio: adc: Replace, rewrite ad7745 from scratch michael.hennerich
2011-09-19 12:19 ` Jonathan Cameron [this message]
2011-09-19 12:54   ` Jonathan Cameron
2011-09-19 19:44   ` Hennerich, Michael
2011-09-20  9:15     ` Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2011-09-16 13:12 michael.hennerich

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=4E77334A.1000207@cam.ac.uk \
    --to=jic23@cam.ac.uk \
    --cc=device-drivers-devel@blackfin.uclinux.org \
    --cc=drivers@analog.com \
    --cc=linux-iio@vger.kernel.org \
    --cc=michael.hennerich@analog.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.