From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Message-ID: <4E82C875.1040507@analog.com> Date: Wed, 28 Sep 2011 09:10:45 +0200 From: Michael Hennerich Reply-To: MIME-Version: 1.0 To: Jonathan Cameron CC: "linux-iio@vger.kernel.org" , "dtor@mail.ru" , "khali@linux-fr.org" , "guenter.roeck@ericsson.com" Subject: Re: [PATCH 3/6] IIO:ADC:ad799x initial import. References: <1317133794-16397-1-git-send-email-jic23@cam.ac.uk> <1317133794-16397-4-git-send-email-jic23@cam.ac.uk> In-Reply-To: <1317133794-16397-4-git-send-email-jic23@cam.ac.uk> Content-Type: text/plain; charset="ISO-8859-1"; format=flowed List-ID: On 09/27/2011 04:29 PM, Jonathan Cameron wrote: > From: Michael Hennerich > > Signed-off-by: Jonathan Cameron Signed-off-by: Michael Hennerich > --- > drivers/iio/adc/Kconfig | 11 + > drivers/iio/adc/Makefile | 3 + > drivers/iio/adc/ad799x_core.c | 722 +++++++++++++++++++++++++++++++++++++++++ > include/linux/iio/ad799x.h | 12 + > 4 files changed, 748 insertions(+), 0 deletions(-) > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 0673d78..3d97b21 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -3,6 +3,17 @@ > # > menu "Analog to digital convertors" > > +config IIO_AD799X > + tristate "Analog Devices AD799x ADC driver" > + depends on I2C > + select IIO_TRIGGER if IIO_BUFFER > + select AD799X_RING_BUFFER > + help > + Say yes here to build support for Analog Devices: > + ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998 > + i2c analog to digital convertors (ADC). Provides direct access > + via sysfs. > + > config IIO_MAX1363 > tristate "Maxim max1363 ADC driver" > depends on I2C > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index 8d6a7f9..c197334 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -2,5 +2,8 @@ > # Makefile for IIO ADCs > # > > +iio_ad799x-y := ad799x_core.o > +obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o > + > iio_max1363-y := max1363_core.o > obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o > \ No newline at end of file > diff --git a/drivers/iio/adc/ad799x_core.c b/drivers/iio/adc/ad799x_core.c > new file mode 100644 > index 0000000..0833ee9 > --- /dev/null > +++ b/drivers/iio/adc/ad799x_core.c > @@ -0,0 +1,722 @@ > +/* > + * iio/adc/ad799x.c > + * Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc. > + * > + * based on iio/adc/max1363 > + * Copyright (C) 2008-2010 Jonathan Cameron > + * > + * based on linux/drivers/i2c/chips/max123x > + * Copyright (C) 2002-2004 Stefan Eletzhofer > + * > + * based on linux/drivers/acron/char/pcf8583.c > + * Copyright (C) 2000 Russell King > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * ad799x.c > + * > + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, > + * ad7998 and similar chips. > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +#define AD799X_CHANNEL_SHIFT 4 > +/* > + * AD7991, AD7995 and AD7999 defines > + */ > + > +#define AD7991_REF_SEL 0x08 > +#define AD7991_FLTR 0x04 > +#define AD7991_BIT_TRIAL_DELAY 0x02 > +#define AD7991_SAMPLE_DELAY 0x01 > + > +/* > + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines > + */ > + > +#define AD7998_FLTR 0x08 > +#define AD7998_ALERT_EN 0x04 > +#define AD7998_BUSY_ALERT 0x02 > +#define AD7998_BUSY_ALERT_POL 0x01 > + > +#define AD7998_CONV_RES_REG 0x0 > +#define AD7998_ALERT_STAT_REG 0x1 > +#define AD7998_CONF_REG 0x2 > +#define AD7998_CYCLE_TMR_REG 0x3 > +#define AD7998_DATALOW_CH1_REG 0x4 > +#define AD7998_DATAHIGH_CH1_REG 0x5 > +#define AD7998_HYST_CH1_REG 0x6 > +#define AD7998_DATALOW_CH2_REG 0x7 > +#define AD7998_DATAHIGH_CH2_REG 0x8 > +#define AD7998_HYST_CH2_REG 0x9 > +#define AD7998_DATALOW_CH3_REG 0xA > +#define AD7998_DATAHIGH_CH3_REG 0xB > +#define AD7998_HYST_CH3_REG 0xC > +#define AD7998_DATALOW_CH4_REG 0xD > +#define AD7998_DATAHIGH_CH4_REG 0xE > +#define AD7998_HYST_CH4_REG 0xF > + > +#define AD7998_CYC_MASK 0x7 > +#define AD7998_CYC_DIS 0x0 > +#define AD7998_CYC_TCONF_32 0x1 > +#define AD7998_CYC_TCONF_64 0x2 > +#define AD7998_CYC_TCONF_128 0x3 > +#define AD7998_CYC_TCONF_256 0x4 > +#define AD7998_CYC_TCONF_512 0x5 > +#define AD7998_CYC_TCONF_1024 0x6 > +#define AD7998_CYC_TCONF_2048 0x7 > + > +#define AD7998_ALERT_STAT_CLEAR 0xFF > + > +/* > + * AD7997 and AD7997 defines > + */ > + > +#define AD7997_8_READ_SINGLE 0x80 > +#define AD7997_8_READ_SEQUENCE 0x70 > +/* TODO: move this into a common header */ > +#define RES_MASK(bits) ((1<< (bits)) - 1) > + > +enum { > + ad7991, > + ad7995, > + ad7999, > + ad7992, > + ad7993, > + ad7994, > + ad7997, > + ad7998 > +}; > + > +struct ad799x_state; > + > +/** > + * struct ad799x_chip_info - chip specifc information > + * @channel: channel specification > + * @num_channels: number of channels > + * @int_vref_mv: the internal reference voltage > + */ > +struct ad799x_chip_info { > + struct iio_chan_spec channel[9]; > + int num_channels; > + u16 int_vref_mv; > +}; > + > +struct ad799x_state { > + struct i2c_client *client; > + const struct ad799x_chip_info *chip_info; > + struct regulator *reg; > + u16 int_vref_mv; > + unsigned id; > + char *name; > + u16 config; > +}; > + > +/* > + * ad799x register access by I2C > + */ > +static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data) > +{ > + struct i2c_client *client = st->client; > + int ret = 0; > + > + ret = i2c_smbus_read_word_data(client, reg); > + if (ret< 0) { > + dev_err(&client->dev, "I2C read error\n"); > + return ret; > + } > + > + *data = swab16((u16)ret); > + > + return 0; > +} > + > +static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch) > +{ > + u16 rxbuf; > + u8 cmd; > + int ret; > + > + switch (st->id) { > + case ad7991: > + case ad7995: > + case ad7999: > + cmd = st->config | ((1<< ch)<< AD799X_CHANNEL_SHIFT); > + break; > + case ad7992: > + case ad7993: > + case ad7994: > + cmd = (1<< ch)<< AD799X_CHANNEL_SHIFT; > + break; > + case ad7997: > + case ad7998: > + cmd = (ch<< AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE; > + break; > + default: > + return -EINVAL; > + } > + > + ret = ad799x_i2c_read16(st, cmd,&rxbuf); > + if (ret< 0) > + return ret; > + > + return rxbuf; > +} > + > +static int ad799x_read_raw(struct iio_dev *dev_info, > + struct iio_chan_spec const *chan, > + int *val, > + int *val2, > + long m) > +{ > + int ret; > + struct ad799x_state *st = iio_priv(dev_info); > + unsigned int scale_uv; > + > + switch (m) { > + case 0: > + mutex_lock(&dev_info->mlock); > + ret = ad799x_scan_direct(st, chan->channel); > + mutex_unlock(&dev_info->mlock); > + > + if (ret< 0) > + return ret; > + *val = (ret>> chan->scan_type.shift)& > + RES_MASK(chan->scan_type.realbits); > + return IIO_VAL_INT; > + case (1<< IIO_CHAN_INFO_SCALE_SHARED): > + scale_uv = (st->int_vref_mv * 1000)>> chan->scan_type.realbits; > + *val = scale_uv / 1000; > + *val2 = (scale_uv % 1000) * 1000; > + return IIO_VAL_INT_PLUS_MICRO; > + } > + return -EINVAL; > +} > + > +static const struct iio_info ad799X_info = { > + .read_raw =&ad799x_read_raw, > + .driver_module = THIS_MODULE, > +}; > + > + > +static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { > + [ad7991] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + }, > + .num_channels = 5, > + .int_vref_mv = 4096, > + }, > + [ad7995] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + }, > + .num_channels = 5, > + .int_vref_mv = 1024, > + }, > + [ad7999] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 8, > + .shift = 4, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 8, > + .shift = 4, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 8, > + .shift = 4, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 8, > + .shift = 4, > + }, > + }, > + }, > + .num_channels = 5, > + .int_vref_mv = 1024, > + }, > + [ad7992] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + }, > + .num_channels = 3, > + .int_vref_mv = 4096, > + }, > + [ad7993] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + }, > + .num_channels = 5, > + .int_vref_mv = 1024, > + }, > + [ad7994] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + }, > + .num_channels = 5, > + .int_vref_mv = 4096, > + }, > + [ad7997] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [4] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 4, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [5] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 5, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [6] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 6, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + [7] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 7, > + .scan_type = { > + .realbits = 10, > + .shift = 2, > + }, > + }, > + }, > + .num_channels = 9, > + .int_vref_mv = 1024, > + }, > + [ad7998] = { > + .channel = { > + [0] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 0, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [1] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 1, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [2] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 2, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [3] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 3, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [4] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 4, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [5] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 5, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [6] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 6, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + [7] = { > + .type = IIO_VOLTAGE, > + .indexed = 1, > + .channel = 7, > + .scan_type = { > + .realbits = 12, > + .shift = 0, > + }, > + }, > + }, > + .num_channels = 9, > + .int_vref_mv = 4096, > + }, > +}; > + > +static int __devinit ad799x_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + int ret; > + struct ad799x_platform_data *pdata = client->dev.platform_data; > + struct ad799x_state *st; > + struct iio_dev *indio_dev = iio_device_allocate(sizeof(*st)); > + > + if (indio_dev == NULL) > + return -ENOMEM; > + > + st = iio_priv(indio_dev); > + /* this is only used for device removal purposes */ > + i2c_set_clientdata(client, indio_dev); > + > + st->id = id->driver_data; > + st->chip_info =&ad799x_chip_info_tbl[st->id]; > + > + /* TODO: Add pdata options for filtering and bit delay */ > + > + if (pdata) > + st->int_vref_mv = pdata->vref_mv; > + else > + st->int_vref_mv = st->chip_info->int_vref_mv; > + > + st->reg = regulator_get(&client->dev, "vcc"); > + if (!IS_ERR(st->reg)) { > + ret = regulator_enable(st->reg); > + if (ret) > + goto error_put_reg; > + } > + st->client = client; > + > + indio_dev->dev.parent =&client->dev; > + indio_dev->name = id->name; > + indio_dev->info =&ad799X_info; > + > + indio_dev->channels = st->chip_info->channel; > + indio_dev->num_channels = st->chip_info->num_channels; > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto error_disable_reg; > + > + return 0; > + > +error_disable_reg: > + if (!IS_ERR(st->reg)) > + regulator_disable(st->reg); > +error_put_reg: > + if (!IS_ERR(st->reg)) > + regulator_put(st->reg); > + iio_device_free(indio_dev); > + > + return ret; > +} > + > +static __devexit int ad799x_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + struct ad799x_state *st = iio_priv(indio_dev); > + > + if (!IS_ERR(st->reg)) { > + regulator_disable(st->reg); > + regulator_put(st->reg); > + } > + iio_device_unregister(indio_dev); > + > + return 0; > +} > + > +static const struct i2c_device_id ad799x_id[] = { > + { "ad7991", ad7991 }, > + { "ad7995", ad7995 }, > + { "ad7999", ad7999 }, > + { "ad7992", ad7992 }, > + { "ad7993", ad7993 }, > + { "ad7994", ad7994 }, > + { "ad7997", ad7997 }, > + { "ad7998", ad7998 }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, ad799x_id); > + > +static struct i2c_driver ad799x_driver = { > + .driver = { > + .name = "ad799x", > + }, > + .probe = ad799x_probe, > + .remove = __devexit_p(ad799x_remove), > + .id_table = ad799x_id, > +}; > + > +static __init int ad799x_init(void) > +{ > + return i2c_add_driver(&ad799x_driver); > +} > + > +static __exit void ad799x_exit(void) > +{ > + i2c_del_driver(&ad799x_driver); > +} > + > +MODULE_AUTHOR("Michael Hennerich"); > +MODULE_DESCRIPTION("Analog Devices AD799x ADC"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("i2c:ad799x"); > + > +module_init(ad799x_init); > +module_exit(ad799x_exit); > diff --git a/include/linux/iio/ad799x.h b/include/linux/iio/ad799x.h > new file mode 100644 > index 0000000..38517be > --- /dev/null > +++ b/include/linux/iio/ad799x.h > @@ -0,0 +1,12 @@ > +/* > + * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc. > + * Copyright (C) 2008-2010 Jonathan Cameron > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +struct ad799x_platform_data { > + u16 vref_mv; > +}; > -- > 1.7.3.4 > > -- 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