[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1318857381-13787-4-git-send-email-jic23@cam.ac.uk>
Date: Mon, 17 Oct 2011 14:16:18 +0100
From: Jonathan Cameron <jic23@....ac.uk>
To: linux-kernel@...r.kernel.org, linux-iio@...r.kernel.org
Cc: guenter.roeck@...csson.com, khali@...ux-fr.org,
dmitry.torokhov@...il.com, broonie@...nsource.wolfsonmicro.com,
gregkh@...e.de, alan@...rguk.ukuu.org.uk, arnd@...db.de,
Michael Hennerich <michael.hennerich@...log.com>,
Jonathan Cameron <jic23@....ac.uk>
Subject: [PATCH 3/6] IIO:ADC:ad799x initial import.
From: Michael Hennerich <michael.hennerich@...log.com>
Signed-off-by: Jonathan Cameron <jic23@....ac.uk>
Signed-off-by: Michael Hennerich<michael.hennerich@...log.com>
---
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 3 +
drivers/iio/adc/ad799x_core.c | 723 +++++++++++++++++++++++++++++++++++++++++
include/linux/iio/ad799x.h | 12 +
4 files changed, 749 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..b734547
--- /dev/null
+++ b/drivers/iio/adc/ad799x_core.c
@@ -0,0 +1,723 @@
+/*
+ * 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 <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/ad799x.h>
+
+#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 *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad799x_state *st = iio_priv(indio_dev);
+ unsigned int scale_uv;
+
+ switch (m) {
+ case 0:
+ mutex_lock(&indio_dev->mlock);
+ ret = ad799x_scan_direct(st, chan->channel);
+ mutex_unlock(&indio_dev->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);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_free(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 <hennerich@...ckfin.uclinux.org>");
+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.7
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists