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, drivers@analog.com,
	device-drivers-devel@blackfin.uclinux.org
Subject: Re: [PATCH] IIO: ADC: New driver for the AD7880 / AD7781 24-bit Sigma-Delta ADC
Date: Thu, 24 Mar 2011 15:04:38 +0000	[thread overview]
Message-ID: <4D8B5D86.8010503@cam.ac.uk> (raw)
In-Reply-To: <1300975814-14423-1-git-send-email-michael.hennerich@analog.com>

On 03/24/11 14:10, michael.hennerich@analog.com wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>

Hi Michael,

Some slightly inconsistent handling of the absence of pdata.
Is it possible to use this device without pdrst being connected?
If not, then check for pdata early in probe and fail if not present.

The datardy on the data out line is a delightful big of hardware
design and I guess what you have here is the only way to handle it.
Having said that, perhaps some explanatory comments, maybe in the
header, would be useful.  Do you have that irq pin dual wired to
the spi data out?

Another clean driver. Thanks,
> 
> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> ---
>  drivers/staging/iio/adc/Kconfig  |   11 ++
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7780.c |  324 ++++++++++++++++++++++++++++++++++++++
>  drivers/staging/iio/adc/ad7780.h |   20 +++
>  4 files changed, 356 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7780.c
>  create mode 100644 drivers/staging/iio/adc/ad7780.h
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 6692a3d..4159389 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -142,6 +142,17 @@ config AD7887
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7887.
>  
> +config AD7780
> +	tristate "Analog Devices AD7780 AD7781 ADC driver"
> +	depends on SPI
> +	help
> +	  Say yes here to build support for Analog Devices
> +	  AD7780 and AD7781 SPI analog to digital convertors (ADC).
> +	  If unsure, say N (but it's safe to say "Y").
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ad7780.
> +
>  config AD7745
>  	tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
>  	depends on I2C
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 31067de..1d9b3f5 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_AD7152) += ad7152.o
>  obj-$(CONFIG_AD7291) += ad7291.o
>  obj-$(CONFIG_AD7314) += ad7314.o
>  obj-$(CONFIG_AD7745) += ad7745.o
> +obj-$(CONFIG_AD7780) += ad7780.o
>  obj-$(CONFIG_AD7816) += ad7816.o
>  obj-$(CONFIG_ADT75) += adt75.o
>  obj-$(CONFIG_ADT7310) += adt7310.o
> diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
> new file mode 100644
> index 0000000..eba733d
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7780.c
> @@ -0,0 +1,324 @@
> +/*
> + * AD7780/AD7781 SPI ADC driver
> + *
> + * Copyright 2011 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/workqueue.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +#include <linux/spi/spi.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/err.h>
> +#include <linux/sched.h>
> +#include <linux/gpio.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../ring_generic.h"
> +#include "adc.h"
> +
> +#include "ad7780.h"
> +
> +#define AD7780_RDY	(1 << 7)
> +#define AD7780_FILTER	(1 << 6)
> +#define AD7780_ERR	(1 << 5)
> +#define AD7780_ID1	(1 << 4)
> +#define AD7780_ID0	(1 << 3)
> +#define AD7780_GAIN	(1 << 2)
> +#define AD7780_PAT1	(1 << 1)
> +#define AD7780_PAT0	(1 << 0)
> +
> +struct ad7780_chip_info {
> +	u8				bits;
> +	u8				storagebits;
> +	u8				res_shift;
> +};
> +
> +struct ad7780_state {
> +	struct iio_dev			*indio_dev;
> +	struct spi_device		*spi;
> +	const struct ad7780_chip_info	*chip_info;
> +	struct regulator		*reg;
> +	struct ad7780_platform_data	*pdata;
> +	wait_queue_head_t		wq_data_avail;
> +	bool				done;
> +	u16				int_vref_mv;
> +	struct spi_transfer		xfer;
> +	struct spi_message		msg;
> +	/*
> +	 * DMA (thus cache coherency maintenance) requires the
> +	 * transfer buffers to live in their own cache lines.
> +	 */
> +	unsigned char			data[4] ____cacheline_aligned;
> +};
> +
> +enum ad7780_supported_device_ids {
> +	ID_AD7780,
> +	ID_AD7781,
> +};
> +
> +static int ad7780_read(struct ad7780_state *st, int *val)
> +{
> +	int ret;
> +
> +	spi_bus_lock(st->spi->master);
> +
> +	enable_irq(st->spi->irq);
> +	st->done = false;
> +	gpio_set_value(st->pdata->gpio_pdrst, 1);
> +
> +	ret = wait_event_interruptible(st->wq_data_avail, st->done);
> +	disable_irq_nosync(st->spi->irq);
> +	if (ret)
> +		goto out;
> +
> +	ret = spi_sync_locked(st->spi, &st->msg);
> +	*val = be32_to_cpu(st->data);
> +out:
> +	gpio_set_value(st->pdata->gpio_pdrst, 0);
> +	spi_bus_unlock(st->spi->master);
> +
> +	return ret;
> +}
> +
> +static ssize_t ad7780_scan(struct device *dev,
> +			    struct device_attribute *attr,
> +			    char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7780_state *st = dev_info->dev_data;
> +	int ret, val, smpl;
> +
> +	mutex_lock(&dev_info->mlock);
> +	ret = ad7780_read(st, &smpl);
> +	mutex_unlock(&dev_info->mlock);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	if ((smpl & AD7780_ERR) ||
> +		!((smpl & AD7780_PAT0) && !(smpl & AD7780_PAT1)))
> +		return -EIO;
> +
> +	val = (smpl >> st->chip_info->res_shift) &
> +		((1 << (st->chip_info->bits)) - 1);
> +	val -= (1 << (st->chip_info->bits - 1));
> +
> +	if (!(smpl & AD7780_GAIN))
> +		val *= 128;
> +
> +	return sprintf(buf, "%d\n", val);
> +}
> +static IIO_DEV_ATTR_IN_RAW(0, ad7780_scan, 0);
> +
> +static ssize_t ad7780_show_scale(struct device *dev,
> +				struct device_attribute *attr,
> +				char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7780_state *st = iio_dev_get_devdata(dev_info);
> +	/* Corresponds to Vref / 2^(bits-1) */
> +	unsigned int scale = (st->int_vref_mv * 100000) >>
> +		(st->chip_info->bits - 1);
> +
> +	return sprintf(buf, "%d.%05d\n", scale / 100000, scale % 100000);
> +}
> +static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad7780_show_scale, NULL, 0);
> +
> +static ssize_t ad7780_show_name(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7780_state *st = iio_dev_get_devdata(dev_info);
> +
> +	return sprintf(buf, "%s\n", spi_get_device_id(st->spi)->name);
> +}
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7780_show_name, NULL, 0);
> +
> +static struct attribute *ad7780_attributes[] = {
> +	&iio_dev_attr_in0_raw.dev_attr.attr,
> +	&iio_dev_attr_in_scale.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7780_attribute_group = {
> +	.attrs = ad7780_attributes,
> +};
> +
> +static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
> +	[ID_AD7780] = {
> +		.bits = 24,
> +		.storagebits = 32,
> +		.res_shift = 8,
> +	},
> +	[ID_AD7781] = {
> +		.bits = 20,
> +		.storagebits = 32,
> +		.res_shift = 12,
> +	},
> +};
> +
> +/**
> + *  Interrupt handler
> + */
> +static irqreturn_t ad7780_interrupt(int irq, void *dev_id)
> +{
> +	struct ad7780_state *st = dev_id;
> +
> +	st->done = true;
> +	wake_up_interruptible(&st->wq_data_avail);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +static int __devinit ad7780_probe(struct spi_device *spi)
> +{
> +	struct ad7780_platform_data *pdata = spi->dev.platform_data;
> +	struct ad7780_state *st;
> +	int ret, voltage_uv = 0;
> +
> +	st = kzalloc(sizeof(*st), GFP_KERNEL);
> +	if (st == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	st->reg = regulator_get(&spi->dev, "vcc");
> +	if (!IS_ERR(st->reg)) {
> +		ret = regulator_enable(st->reg);
> +		if (ret)
> +			goto error_put_reg;
> +
> +		voltage_uv = regulator_get_voltage(st->reg);
> +	}
> +
> +	st->chip_info =
> +		&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
> +
> +	st->pdata = pdata;
> +
If you have no pdata the later use of it to specify the gpio is going
to fail. Hence your probably want a general error on it missing and
then don't need this check.

> +	if (pdata && pdata->vref_mv)
> +		st->int_vref_mv = pdata->vref_mv;
> +	else if (voltage_uv)
> +		st->int_vref_mv = voltage_uv / 1000;
> +	else
> +		dev_warn(&spi->dev, "reference voltage unspecified\n");
> +
> +	spi_set_drvdata(spi, st);
> +	st->spi = spi;
> +
> +	st->indio_dev = iio_allocate_device();
> +	if (st->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_disable_reg;
> +	}
> +
> +	/* Establish that the iio_dev is a child of the spi device */
> +	st->indio_dev->dev.parent = &spi->dev;
> +	st->indio_dev->attrs = &ad7780_attribute_group;
> +	st->indio_dev->dev_data = (void *)(st);
> +	st->indio_dev->driver_module = THIS_MODULE;
> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	init_waitqueue_head(&st->wq_data_avail);
> +
> +	/* Setup default message */
> +
> +	st->xfer.rx_buf = &st->data;
> +	st->xfer.len = st->chip_info->storagebits / 8;
> +
> +	spi_message_init(&st->msg);
> +	spi_message_add_tail(&st->xfer, &st->msg);
> +
> +	ret = gpio_request_one(st->pdata->gpio_pdrst, GPIOF_OUT_INIT_LOW,
> +			       "AD7877 /PDRST");
> +	if (ret) {
> +		dev_err(&spi->dev, "failed to request GPIO PDRST\n");
> +		goto error_free_device;
> +	}
> +
> +	ret = request_irq(spi->irq, ad7780_interrupt,
> +		IRQF_TRIGGER_FALLING, spi_get_device_id(spi)->name, st);
> +	if (ret)
> +		goto error_free_device;
> +
> +	disable_irq(spi->irq);
> +
> +	ret = iio_device_register(st->indio_dev);
> +	if (ret)
> +		goto error_free_irq;
> +
> +	return 0;
> +
> +error_free_irq:
> +	free_irq(spi->irq, st);
> +
> +error_free_device:
> +	iio_free_device(st->indio_dev);
> +error_disable_reg:
> +	if (!IS_ERR(st->reg))
> +		regulator_disable(st->reg);
> +error_put_reg:
> +	if (!IS_ERR(st->reg))
> +		regulator_put(st->reg);
> +	kfree(st);
> +error_ret:
> +	return ret;
> +}
> +
> +static int ad7780_remove(struct spi_device *spi)
> +{
> +	struct ad7780_state *st = spi_get_drvdata(spi);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	free_irq(spi->irq, st);
> +	gpio_free(st->pdata->gpio_pdrst);
> +	iio_device_unregister(indio_dev);
> +	if (!IS_ERR(st->reg)) {
> +		regulator_disable(st->reg);
> +		regulator_put(st->reg);
> +	}
> +	kfree(st);
> +	return 0;
> +}
> +
> +static const struct spi_device_id ad7780_id[] = {
> +	{"ad7780", ID_AD7780},
> +	{"ad7781", ID_AD7781},
> +	{}
> +};
> +
> +static struct spi_driver ad7780_driver = {
> +	.driver = {
> +		.name	= "ad7780",
> +		.bus	= &spi_bus_type,
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= ad7780_probe,
> +	.remove		= __devexit_p(ad7780_remove),
> +	.id_table	= ad7780_id,
> +};
> +
> +static int __init ad7780_init(void)
> +{
> +	return spi_register_driver(&ad7780_driver);
> +}
> +module_init(ad7780_init);
> +
> +static void __exit ad7780_exit(void)
> +{
> +	spi_unregister_driver(&ad7780_driver);
> +}
> +module_exit(ad7780_exit);
> +
> +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
> +MODULE_DESCRIPTION("Analog Devices AD7780/1 ADC");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/adc/ad7780.h b/drivers/staging/iio/adc/ad7780.h
> new file mode 100644
> index 0000000..ec77486
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7780.h
> @@ -0,0 +1,20 @@
> +/*
> + * AD7780/AD7781 SPI ADC driver
> + *
> + * Copyright 2011 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2.
> + */
> +#ifndef IIO_ADC_AD7780_H_
> +#define IIO_ADC_AD7780_H_
> +
> +/*
> + * TODO: struct ad7780_platform_data needs to go into include/linux/iio
> + */
> +
> +struct ad7780_platform_data {
> +	u16				vref_mv;
> +	int				gpio_pdrst;
> +};
> +
> +#endif /* IIO_ADC_AD7780_H_ */


  reply	other threads:[~2011-03-24 15:03 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-03-24 14:10 [PATCH] IIO: ADC: New driver for the AD7880 / AD7781 24-bit Sigma-Delta ADC michael.hennerich
2011-03-24 15:04 ` Jonathan Cameron [this message]
2011-03-24 15:12   ` Hennerich, Michael
2011-03-24 15:17     ` Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2011-03-24 15:52 michael.hennerich
2011-03-24 17:04 ` Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4D8B5D86.8010503@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.