All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices
@ 2010-10-23 20:29 Mike Frysinger
  2010-10-23 20:29 ` [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
                   ` (13 more replies)
  0 siblings, 14 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Barry Song

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7152.c |  610 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 618 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7152.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 98dd0a8..4bfe372 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -33,3 +33,10 @@ config AD7150
 	help
 	  Say yes here to build support for Analog Devices capacitive sensors.
 	  (ad7150, ad7151, ad7156) Provides direct access via sysfs.
+
+config AD7152
+	tristate "Analog Devices ad7152/3 capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (ad7152, ad7153) Provides direct access via sysfs.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 3148a2c..fca1eff 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -7,3 +7,4 @@ max1363-y += max1363_ring.o
 
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_AD7150) += ad7150.o
+obj-$(CONFIG_AD7152) += ad7152.o
diff --git a/drivers/staging/iio/adc/ad7152.c b/drivers/staging/iio/adc/ad7152.c
new file mode 100644
index 0000000..3612350
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7152.c
@@ -0,0 +1,610 @@
+/*
+ * AD7152 capacitive sensor driver supporting AD7152/3
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7152 registers definition
+ */
+
+#define AD7152_STATUS              0
+#define AD7152_STATUS_RDY1         (1 << 0)
+#define AD7152_STATUS_RDY2         (1 << 1)
+#define AD7152_CH1_DATA_HIGH       1
+#define AD7152_CH1_DATA_LOW        2
+#define AD7152_CH2_DATA_HIGH       3
+#define AD7152_CH2_DATA_LOW        4
+#define AD7152_CH1_OFFS_HIGH       5
+#define AD7152_CH1_OFFS_LOW        6
+#define AD7152_CH2_OFFS_HIGH       7
+#define AD7152_CH2_OFFS_LOW        8
+#define AD7152_CH1_GAIN_HIGH       9
+#define AD7152_CH1_GAIN_LOW        10
+#define AD7152_CH1_SETUP           11
+#define AD7152_CH2_GAIN_HIGH       12
+#define AD7152_CH2_GAIN_LOW        13
+#define AD7152_CH2_SETUP           14
+#define AD7152_CFG                 15
+#define AD7152_RESEVERD            16
+#define AD7152_CAPDAC_POS          17
+#define AD7152_CAPDAC_NEG          18
+#define AD7152_CFG2                26
+
+#define AD7152_MAX_CONV_MODE       6
+
+/*
+ * struct ad7152_chip_info - chip specifc information
+ */
+
+struct ad7152_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	u16 ch1_offset;     /* Channel 1 offset calibration coefficient */
+	u16 ch1_gain;       /* Channel 1 gain coefficient */
+	u8  ch1_setup;
+	u16 ch2_offset;     /* Channel 2 offset calibration coefficient */
+	u16 ch2_gain;       /* Channel 1 gain coefficient */
+	u8  ch2_setup;
+	u8  filter_rate_setup; /* Capacitive channel digital filter setup; conversion time/update rate setup per channel */
+	char *conversion_mode;
+};
+
+struct ad7152_conversion_mode {
+	char *name;
+	u8 reg_cfg;
+};
+
+struct ad7152_conversion_mode ad7152_conv_mode_table[AD7152_MAX_CONV_MODE] = {
+	{ "idle", 0 },
+	{ "continuous-conversion", 1 },
+	{ "single-conversion", 2 },
+	{ "power-down", 3 },
+	{ "offset-calibration", 5 },
+	{ "gain-calibration", 6 },
+};
+
+/*
+ * ad7152 register access by I2C
+ */
+
+static int ad7152_i2c_read(struct ad7152_chip_info *chip, u8 reg, u8 *data, int len)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ad7152_i2c_write(struct ad7152_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	u8 tx[2] = {
+		reg,
+		data,
+	};
+
+	ret = i2c_master_send(client, tx, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/*
+ * sysfs nodes
+ */
+
+#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_OFFSET(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_offset, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_OFFSET(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_offset, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch2_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH1_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch1_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH2_VALUE(_show)		\
+	IIO_DEVICE_ATTR(ch2_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CH1_SETUP(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ch1_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CH2_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(ch2_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_FILTER_RATE_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(filter_rate_setup, _mode, _show, _store, 0)
+
+static ssize_t ad7152_show_conversion_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; i < AD7152_MAX_CONV_MODE; i++)
+		len += sprintf(buf + len, "%s\n", ad7152_conv_mode_table[i].name);
+
+	return len;
+}
+
+static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad7152_show_conversion_modes);
+
+static ssize_t ad7152_show_ch1_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	u8 data[2];
+
+	ad7152_i2c_read(chip, AD7152_CH1_DATA_HIGH, data, 2);
+	return sprintf(buf, "%d\n", ((int)data[0] << 8) | data[1]);
+}
+
+static IIO_DEV_ATTR_CH1_VALUE(ad7152_show_ch1_value);
+
+static ssize_t ad7152_show_ch2_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	u8 data[2];
+
+	ad7152_i2c_read(chip, AD7152_CH2_DATA_HIGH, data, 2);
+	return sprintf(buf, "%d\n", ((int)data[0] << 8) | data[1]);
+}
+
+static IIO_DEV_ATTR_CH2_VALUE(ad7152_show_ch2_value);
+
+static ssize_t ad7152_show_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->conversion_mode);
+}
+
+static ssize_t ad7152_store_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+	int i;
+
+	ad7152_i2c_read(chip, AD7152_CFG, &cfg, 1);
+
+	for (i = 0; i < AD7152_MAX_CONV_MODE; i++) {
+		if (strncmp(buf, ad7152_conv_mode_table[i].name,
+				strlen(ad7152_conv_mode_table[i].name) - 1) == 0) {
+			chip->conversion_mode = ad7152_conv_mode_table[i].name;
+			cfg |= 0x18 | ad7152_conv_mode_table[i].reg_cfg;
+			ad7152_i2c_write(chip, AD7152_CFG, cfg);
+			return len;
+		}
+	}
+
+	dev_err(dev, "not supported conversion mode\n");
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
+		ad7152_show_conversion_mode,
+		ad7152_store_conversion_mode);
+
+static ssize_t ad7152_show_ch1_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_offset);
+}
+
+static ssize_t ad7152_store_ch1_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH1_OFFS_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH1_OFFS_LOW, data);
+		chip->ch1_offset = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_OFFSET(S_IRUGO | S_IWUSR,
+		ad7152_show_ch1_offset,
+		ad7152_store_ch1_offset);
+
+static ssize_t ad7152_show_ch2_offset(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_offset);
+}
+
+static ssize_t ad7152_store_ch2_offset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH2_OFFS_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH2_OFFS_LOW, data);
+		chip->ch2_offset = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_OFFSET(S_IRUGO | S_IWUSR,
+		ad7152_show_ch2_offset,
+		ad7152_store_ch2_offset);
+
+static ssize_t ad7152_show_ch1_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch1_gain);
+}
+
+static ssize_t ad7152_store_ch1_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH1_GAIN_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH1_GAIN_LOW, data);
+		chip->ch1_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_GAIN(S_IRUGO | S_IWUSR,
+		ad7152_show_ch1_gain,
+		ad7152_store_ch1_gain);
+
+static ssize_t ad7152_show_ch2_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->ch2_gain);
+}
+
+static ssize_t ad7152_store_ch2_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad7152_i2c_write(chip, AD7152_CH2_GAIN_HIGH, data >> 8);
+		ad7152_i2c_write(chip, AD7152_CH2_GAIN_LOW, data);
+		chip->ch2_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_GAIN(S_IRUGO | S_IWUSR,
+		ad7152_show_ch2_gain,
+		ad7152_store_ch2_gain);
+
+static ssize_t ad7152_show_ch1_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->ch1_setup);
+}
+
+static ssize_t ad7152_store_ch1_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7152_i2c_write(chip, AD7152_CH1_SETUP, data);
+		chip->ch1_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH1_SETUP(S_IRUGO | S_IWUSR,
+		ad7152_show_ch1_setup,
+		ad7152_store_ch1_setup);
+
+static ssize_t ad7152_show_ch2_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->ch2_setup);
+}
+
+static ssize_t ad7152_store_ch2_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7152_i2c_write(chip, AD7152_CH2_SETUP, data);
+		chip->ch2_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CH2_SETUP(S_IRUGO | S_IWUSR,
+		ad7152_show_ch2_setup,
+		ad7152_store_ch2_setup);
+
+static ssize_t ad7152_show_filter_rate_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->filter_rate_setup);
+}
+
+static ssize_t ad7152_store_filter_rate_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad7152_i2c_write(chip, AD7152_CFG2, data);
+		chip->filter_rate_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_FILTER_RATE_SETUP(S_IRUGO | S_IWUSR,
+		ad7152_show_filter_rate_setup,
+		ad7152_store_filter_rate_setup);
+
+static ssize_t ad7152_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7152_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7152_show_name, NULL, 0);
+
+static struct attribute *ad7152_attributes[] = {
+	&iio_dev_attr_available_conversion_modes.dev_attr.attr,
+	&iio_dev_attr_conversion_mode.dev_attr.attr,
+	&iio_dev_attr_ch1_gain.dev_attr.attr,
+	&iio_dev_attr_ch2_gain.dev_attr.attr,
+	&iio_dev_attr_ch1_offset.dev_attr.attr,
+	&iio_dev_attr_ch2_offset.dev_attr.attr,
+	&iio_dev_attr_ch1_value.dev_attr.attr,
+	&iio_dev_attr_ch2_value.dev_attr.attr,
+	&iio_dev_attr_ch1_setup.dev_attr.attr,
+	&iio_dev_attr_ch2_setup.dev_attr.attr,
+	&iio_dev_attr_filter_rate_setup.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7152_attribute_group = {
+	.attrs = ad7152_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7152_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct ad7152_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	/* Echipabilish that the iio_dev is a child of the i2c device */
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7152_attribute_group;
+	chip->indio_dev->dev_data = (void *)(chip);
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	dev_err(&client->dev, "%s capacitive sensor registered\n", id->name);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad7152_remove(struct i2c_client *client)
+{
+	struct ad7152_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7152_id[] = {
+	{ "ad7152", 0 },
+	{ "ad7153", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7152_id);
+
+static struct i2c_driver ad7152_driver = {
+	.driver = {
+		.name = "ad7152",
+	},
+	.probe = ad7152_probe,
+	.remove = __devexit_p(ad7152_remove),
+	.id_table = ad7152_id,
+};
+
+static __init int ad7152_init(void)
+{
+	return i2c_add_driver(&ad7152_driver);
+}
+
+static __exit void ad7152_exit(void)
+{
+	i2c_del_driver(&ad7152_driver);
+}
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ad7152/3 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7152_init);
+module_exit(ad7152_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 21:32   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 03/14] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7291.c | 1039 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1047 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7291.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 4bfe372..a4c41dc 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -40,3 +40,10 @@ config AD7152
 	help
 	  Say yes here to build support for Analog Devices capacitive sensors.
 	  (ad7152, ad7153) Provides direct access via sysfs.
+
+config AD7291
+	tristate "Analog Devices AD7291 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices AD7291
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index fca1eff..7d8ac9a 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -8,3 +8,4 @@ max1363-y += max1363_ring.o
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
+obj-$(CONFIG_AD7291) += ad7291.o
diff --git a/drivers/staging/iio/adc/ad7291.c b/drivers/staging/iio/adc/ad7291.c
new file mode 100644
index 0000000..116248f
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7291.c
@@ -0,0 +1,1039 @@
+/*
+ * AD7291 digital temperature sensor driver supporting AD7291
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7291 registers definition
+ */
+#define AD7291_COMMAND			0
+#define AD7291_VOLTAGE			1
+#define AD7291_T_SENSE			2
+#define AD7291_T_AVERAGE		3
+#define AD7291_VOLTAGE_LIMIT_BASE	4
+#define AD7291_VOLTAGE_LIMIT_COUNT	8
+#define AD7291_T_SENSE_HIGH		0x1c
+#define AD7291_T_SENSE_LOW		0x1d
+#define AD7291_T_SENSE_HYST		0x1e
+#define AD7291_VOLTAGE_ALERT_STATUS	0x1f
+#define AD7291_T_ALERT_STATUS		0x20
+
+/*
+ * AD7291 command
+ */
+#define AD7291_AUTOCYCLE		0x1
+#define AD7291_RESET			0x2
+#define AD7291_ALART_CLEAR		0x4
+#define AD7291_ALART_POLARITY		0x8
+#define AD7291_EXT_REF			0x10
+#define AD7291_NOISE_DELAY		0x20
+#define AD7291_T_SENSE_MASK		0x40
+#define AD7291_VOLTAGE_MASK		0xff00
+#define AD7291_VOLTAGE_OFFSET		0x8
+
+/*
+ * AD7291 value masks
+ */
+#define AD7291_CHANNEL_MASK		0xf000
+#define AD7291_VALUE_MASK		0xfff
+#define AD7291_T_VALUE_SIGN		0x400
+#define AD7291_T_VALUE_FLOAT_OFFSET	2
+#define AD7291_T_VALUE_FLOAT_MASK	0x2
+
+/*
+ * struct ad7291_chip_info - chip specifc information
+ */
+
+struct ad7291_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u16 command;
+	u8  channels;	/* Active voltage channels */
+};
+
+/*
+ * struct ad7291_chip_info - chip specifc information
+ */
+
+struct ad7291_limit_regs {
+	u16	data_high;
+	u16	data_low;
+	u16	hysteresis;
+};
+
+/*
+ * ad7291 register access by I2C
+ */
+static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->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 ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/* Returns negative errno, or else the number of words read. */
+static int ad7291_i2c_read_data(struct ad7291_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->client;
+	u8 commands[4];
+	int ret = 0;
+	int i, count;
+
+	if (reg == AD7291_T_SENSE || reg == AD7291_T_AVERAGE)
+		count = 2;
+	else if (reg == AD7291_VOLTAGE) {
+		if (!chip->channels) {
+			dev_err(&client->dev, "No voltage channel is selected.\n");
+			return -EINVAL;
+		}
+		count = 2 + chip->channels * 2;
+	} else {
+		dev_err(&client->dev, "I2C wrong data register\n");
+		return -EINVAL;
+	}
+
+	commands[0] = 0;
+	commands[1] = (chip->command >> 8) & 0xff;
+	commands[2] = chip->command & 0xff;
+	commands[3] = reg;
+
+	ret = i2c_master_send(client, commands, 4);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C master send error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, (u8 *)data, count);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C master receive error\n");
+		return ret;
+	}
+	ret >>= 2;
+
+	for (i = 0; i < ret; i++)
+		data[i] = swab16(data[i]);
+
+	return ret;
+}
+
+static ssize_t ad7291_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	if (chip->command & AD7291_AUTOCYCLE)
+		return sprintf(buf, "autocycle\n");
+	else
+		return sprintf(buf, "command\n");
+}
+
+static ssize_t ad7291_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7291_AUTOCYCLE);
+	if (strcmp(buf, "autocycle"))
+		command |= AD7291_AUTOCYCLE;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7291_show_mode,
+		ad7291_store_mode,
+		0);
+
+static ssize_t ad7291_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "command\nautocycle\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7291_show_available_modes, NULL, 0);
+
+static ssize_t ad7291_store_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command | AD7291_RESET;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR,
+		NULL,
+		ad7291_store_reset,
+		0);
+
+static ssize_t ad7291_show_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->command & AD7291_EXT_REF));
+}
+
+static ssize_t ad7291_store_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7291_EXT_REF);
+	if (strcmp(buf, "1"))
+		command |= AD7291_EXT_REF;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR,
+		ad7291_show_ext_ref,
+		ad7291_store_ext_ref,
+		0);
+
+static ssize_t ad7291_show_noise_delay(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->command & AD7291_NOISE_DELAY));
+}
+
+static ssize_t ad7291_store_noise_delay(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7291_NOISE_DELAY);
+	if (strcmp(buf, "1"))
+		command |= AD7291_NOISE_DELAY;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(noise_delay, S_IRUGO | S_IWUSR,
+		ad7291_show_noise_delay,
+		ad7291_store_noise_delay,
+		0);
+
+static ssize_t ad7291_show_t_sense(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7291_i2c_read_data(chip, AD7291_T_SENSE, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7291_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> AD7291_T_VALUE_FLOAT_OFFSET),
+		(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7291_show_t_sense, NULL, 0);
+
+static ssize_t ad7291_show_t_average(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7291_i2c_read_data(chip, AD7291_T_AVERAGE, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7291_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> AD7291_T_VALUE_FLOAT_OFFSET),
+		(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7291_show_t_average, NULL, 0);
+
+static ssize_t ad7291_show_voltage(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data[AD7291_VOLTAGE_LIMIT_COUNT];
+	int i, size, ret;
+
+	ret = ad7291_i2c_read_data(chip, AD7291_VOLTAGE, data);
+	if (ret)
+		return -EIO;
+
+	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7291_T_SENSE_MASK << i)) {
+			ret = sprintf(buf, "channel[%d]=%d\n", i,
+					data[i] & AD7291_VALUE_MASK);
+			if (ret < 0)
+				break;
+			buf += ret;
+			size += ret;
+		}
+	}
+
+	return size;
+}
+
+static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7291_show_voltage, NULL, 0);
+
+static ssize_t ad7291_show_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%x\n", (chip->command & AD7291_VOLTAGE_MASK) >>
+			AD7291_VOLTAGE_OFFSET);
+}
+
+static ssize_t ad7291_store_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	unsigned long data;
+	int i, ret;
+
+	ret = strict_strtoul(buf, 16, &data);
+	if (ret || data > 0xff)
+		return -EINVAL;
+
+	command = chip->command & (~AD7291_VOLTAGE_MASK);
+	command |= data << AD7291_VOLTAGE_OFFSET;
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	for (i = 0, chip->channels = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7291_T_SENSE_MASK << i))
+			chip->channels++;
+	}
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR,
+		ad7291_show_channel_mask,
+		ad7291_store_channel_mask,
+		0);
+
+static ssize_t ad7291_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7291_show_name, NULL, 0);
+
+static struct attribute *ad7291_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_ext_ref.dev_attr.attr,
+	&iio_dev_attr_noise_delay.dev_attr.attr,
+	&iio_dev_attr_t_sense.dev_attr.attr,
+	&iio_dev_attr_t_average.dev_attr.attr,
+	&iio_dev_attr_voltage.dev_attr.attr,
+	&iio_dev_attr_channel_mask.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7291_attribute_group = {
+	.attrs = ad7291_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7291_T_SENSE_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_AD7291_T_SENSE_LOW    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+#define IIO_EVENT_CODE_AD7291_T_AVG_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
+#define IIO_EVENT_CODE_AD7291_T_AVG_LOW    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 4)
+#define IIO_EVENT_CODE_AD7291_VOLTAGE_BASE    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 5)
+
+static void ad7291_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad7291_chip_info *chip =
+		container_of(work_s, struct ad7291_chip_info, thresh_work);
+	u16 t_status, v_status;
+	u16 command;
+	int i;
+
+	if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status))
+		return;
+
+	if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status))
+		return;
+
+	if (!(t_status || v_status))
+		return;
+
+	command = chip->command | AD7291_ALART_CLEAR;
+	ad7291_i2c_write(chip, AD7291_COMMAND, command);
+
+	command = chip->command & ~AD7291_ALART_CLEAR;
+	ad7291_i2c_write(chip, AD7291_COMMAND, command);
+
+	enable_irq(chip->client->irq);
+
+	for (i = 0; i < 4; i++) {
+		if (t_status & (1 << i))
+			iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_AD7291_T_SENSE_HIGH + i,
+				chip->last_timestamp);
+	}
+
+	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT*2; i++) {
+		if (v_status & (1 << i))
+			iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_AD7291_VOLTAGE_BASE + i,
+				chip->last_timestamp);
+	}
+}
+
+static int ad7291_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(ad7291, &ad7291_interrupt);
+
+static inline ssize_t ad7291_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7291_i2c_read(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	data &= AD7291_VALUE_MASK;
+	if (data & AD7291_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+			data >> AD7291_T_VALUE_FLOAT_OFFSET,
+			(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static inline ssize_t ad7291_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+		if (len > AD7291_T_VALUE_FLOAT_OFFSET)
+			len = AD7291_T_VALUE_FLOAT_OFFSET;
+		pos[len] = 0;
+		ret = strict_strtol(pos, 10, &tmp2);
+
+		if (!ret)
+			tmp2 = (tmp2 / 25) * 25;
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+	data = (data << AD7291_T_VALUE_FLOAT_OFFSET) |
+		(tmp2 & AD7291_T_VALUE_FLOAT_MASK);
+	if (tmp1 < 0)
+		/* convert positive value to supplyment */
+		data = (AD7291_T_VALUE_SIGN << 1) - data;
+
+	ret = ad7291_i2c_write(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t ad7291_show_t_sense_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7291_show_t_bound(dev, attr,
+			AD7291_T_SENSE_HIGH, buf);
+}
+
+static inline ssize_t ad7291_set_t_sense_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7291_set_t_bound(dev, attr,
+			AD7291_T_SENSE_HIGH, buf, len);
+}
+
+static ssize_t ad7291_show_t_sense_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7291_show_t_bound(dev, attr,
+			AD7291_T_SENSE_LOW, buf);
+}
+
+static inline ssize_t ad7291_set_t_sense_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7291_set_t_bound(dev, attr,
+			AD7291_T_SENSE_LOW, buf, len);
+}
+
+static ssize_t ad7291_show_t_sense_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7291_show_t_bound(dev, attr,
+			AD7291_T_SENSE_HYST, buf);
+}
+
+static inline ssize_t ad7291_set_t_sense_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7291_set_t_bound(dev, attr,
+			AD7291_T_SENSE_HYST, buf, len);
+}
+
+static inline ssize_t ad7291_show_v_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	int ret;
+
+	if (bound_reg < AD7291_VOLTAGE_LIMIT_BASE ||
+		bound_reg >= AD7291_VOLTAGE_LIMIT_BASE +
+		AD7291_VOLTAGE_LIMIT_COUNT)
+		return -EINVAL;
+
+	ret = ad7291_i2c_read(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	data &= AD7291_VALUE_MASK;
+
+	return sprintf(buf, "%d\n", data);
+}
+
+static inline ssize_t ad7291_set_v_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7291_chip_info *chip = dev_info->dev_data;
+	unsigned long value;
+	u16 data;
+	int ret;
+
+	if (bound_reg < AD7291_VOLTAGE_LIMIT_BASE ||
+		bound_reg >= AD7291_VOLTAGE_LIMIT_BASE +
+		AD7291_VOLTAGE_LIMIT_COUNT)
+		return -EINVAL;
+
+	ret = strict_strtoul(buf, 10, &value);
+
+	if (ret || value >= 4096)
+		return -EINVAL;
+
+	data = (u16)value;
+	ret = ad7291_i2c_write(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static int ad7291_get_voltage_limit_regs(const char *channel)
+{
+	int index;
+
+	if (strlen(channel) < 3 && channel[0] != 'v')
+		return -EINVAL;
+
+	index = channel[1] - '0';
+	if (index >= AD7291_VOLTAGE_LIMIT_COUNT)
+		return -EINVAL;
+
+	return index;
+}
+
+static ssize_t ad7291_show_voltage_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_show_t_bound(dev, attr, regs, buf);
+}
+
+static inline ssize_t ad7291_set_voltage_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_set_t_bound(dev, attr, regs, buf, len);
+}
+
+static ssize_t ad7291_show_voltage_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_show_t_bound(dev, attr, regs+1, buf);
+}
+
+static inline ssize_t ad7291_set_voltage_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_set_t_bound(dev, attr, regs+1, buf, len);
+}
+
+static ssize_t ad7291_show_voltage_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_show_t_bound(dev, attr, regs+2, buf);
+}
+
+static inline ssize_t ad7291_set_voltage_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	int regs;
+
+	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
+
+	if (regs < 0)
+		return regs;
+
+	return ad7291_set_t_bound(dev, attr, regs+2, buf, len);
+}
+
+IIO_EVENT_ATTR_SH(t_sense_high, iio_event_ad7291,
+		ad7291_show_t_sense_high, ad7291_set_t_sense_high, 0);
+IIO_EVENT_ATTR_SH(t_sense_low, iio_event_ad7291,
+		ad7291_show_t_sense_low, ad7291_set_t_sense_low, 0);
+IIO_EVENT_ATTR_SH(t_sense_hyst, iio_event_ad7291,
+		ad7291_show_t_sense_hyst, ad7291_set_t_sense_hyst, 0);
+
+IIO_EVENT_ATTR_SH(v0_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v0_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v0_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v1_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v1_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v1_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v2_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v2_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v2_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v3_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v3_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v3_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v4_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v4_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v4_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v5_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v5_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v5_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v6_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v6_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v6_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+IIO_EVENT_ATTR_SH(v7_high, iio_event_ad7291,
+		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
+IIO_EVENT_ATTR_SH(v7_low, iio_event_ad7291,
+		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
+IIO_EVENT_ATTR_SH(v7_hyst, iio_event_ad7291,
+		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
+
+static struct attribute *ad7291_event_attributes[] = {
+	&iio_event_attr_t_sense_high.dev_attr.attr,
+	&iio_event_attr_t_sense_low.dev_attr.attr,
+	&iio_event_attr_t_sense_hyst.dev_attr.attr,
+	&iio_event_attr_v0_high.dev_attr.attr,
+	&iio_event_attr_v0_low.dev_attr.attr,
+	&iio_event_attr_v0_hyst.dev_attr.attr,
+	&iio_event_attr_v1_high.dev_attr.attr,
+	&iio_event_attr_v1_low.dev_attr.attr,
+	&iio_event_attr_v1_hyst.dev_attr.attr,
+	&iio_event_attr_v2_high.dev_attr.attr,
+	&iio_event_attr_v2_low.dev_attr.attr,
+	&iio_event_attr_v2_hyst.dev_attr.attr,
+	&iio_event_attr_v3_high.dev_attr.attr,
+	&iio_event_attr_v3_low.dev_attr.attr,
+	&iio_event_attr_v3_hyst.dev_attr.attr,
+	&iio_event_attr_v4_high.dev_attr.attr,
+	&iio_event_attr_v4_low.dev_attr.attr,
+	&iio_event_attr_v4_hyst.dev_attr.attr,
+	&iio_event_attr_v5_high.dev_attr.attr,
+	&iio_event_attr_v5_low.dev_attr.attr,
+	&iio_event_attr_v5_hyst.dev_attr.attr,
+	&iio_event_attr_v6_high.dev_attr.attr,
+	&iio_event_attr_v6_low.dev_attr.attr,
+	&iio_event_attr_v6_hyst.dev_attr.attr,
+	&iio_event_attr_v7_high.dev_attr.attr,
+	&iio_event_attr_v7_low.dev_attr.attr,
+	&iio_event_attr_v7_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7291_event_attribute_group = {
+	.attrs = ad7291_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7291_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ad7291_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct ad7291_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+	chip->command = AD7291_NOISE_DELAY | AD7291_T_SENSE_MASK;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7291_attribute_group;
+	chip->indio_dev->event_attrs = &ad7291_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq > 0) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				client->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_ad7291.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad7291,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7291_interrupt_bh);
+
+		if (client->irq_flags & IRQF_TRIGGER_LOW)
+			chip->command |= AD7291_ALART_POLARITY;
+	}
+
+	ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command);
+	if (ret) {
+		ret = -EIO;
+		goto error_unreg_irq;
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7291_remove(struct i2c_client *client)
+{
+	struct ad7291_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7291_id[] = {
+	{ "ad7291", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7291_id);
+
+static struct i2c_driver ad7291_driver = {
+	.driver = {
+		.name = "ad7291",
+	},
+	.probe = ad7291_probe,
+	.remove = __devexit_p(ad7291_remove),
+	.id_table = ad7291_id,
+};
+
+static __init int ad7291_init(void)
+{
+	return i2c_add_driver(&ad7291_driver);
+}
+
+static __exit void ad7291_exit(void)
+{
+	i2c_del_driver(&ad7291_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7291 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7291_init);
+module_exit(ad7291_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 03/14] staging: iio: adc: new driver for AD7298 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
  2010-10-23 20:29 ` [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 21:49   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7298.c |  501 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 509 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7298.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index a4c41dc..847f5f2 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -47,3 +47,10 @@ config AD7291
 	help
 	  Say yes here to build support for Analog Devices AD7291
 	  temperature sensors.
+
+config AD7298
+	tristate "Analog Devices AD7298 temperature sensor and ADC driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7298
+	  temperature sensors and ADC.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 7d8ac9a..d0ea747 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
 obj-$(CONFIG_AD7291) += ad7291.o
+obj-$(CONFIG_AD7298) += ad7298.o
diff --git a/drivers/staging/iio/adc/ad7298.c b/drivers/staging/iio/adc/ad7298.c
new file mode 100644
index 0000000..1a080c9
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7298.c
@@ -0,0 +1,501 @@
+/*
+ * AD7298 digital temperature sensor driver supporting AD7298
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7298 command
+ */
+#define AD7298_PD			0x1
+#define AD7298_T_AVG_MASK		0x2
+#define AD7298_EXT_REF			0x4
+#define AD7298_T_SENSE_MASK		0x20
+#define AD7298_VOLTAGE_MASK		0x3fc0
+#define AD7298_VOLTAGE_OFFSET		0x6
+#define AD7298_VOLTAGE_LIMIT_COUNT	8
+#define AD7298_REPEAT			0x40
+#define AD7298_WRITE			0x80
+
+/*
+ * AD7298 value masks
+ */
+#define AD7298_CHANNEL_MASK		0xf000
+#define AD7298_VALUE_MASK		0xfff
+#define AD7298_T_VALUE_SIGN		0x400
+#define AD7298_T_VALUE_FLOAT_OFFSET	2
+#define AD7298_T_VALUE_FLOAT_MASK	0x2
+
+/*
+ * struct ad7298_chip_info - chip specifc information
+ */
+
+struct ad7298_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	u16 command;
+	u16 busy_pin;
+	u8  channels;	/* Active voltage channels */
+};
+
+/*
+ * ad7298 register access by SPI
+ */
+static int ad7298_spi_write(struct ad7298_chip_info *chip, u16 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+
+	data |= AD7298_WRITE;
+	data = cpu_to_be16(data);
+	ret = spi_write(spi_dev, (u8 *)&data, sizeof(data));
+	if (ret < 0)
+		dev_err(&spi_dev->dev, "SPI write error\n");
+
+	return ret;
+}
+
+static int ad7298_spi_read(struct ad7298_chip_info *chip, u16 mask, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+	u8 count = chip->channels;
+	u16 command;
+	int i;
+
+	if (mask & AD7298_T_SENSE_MASK) {
+		command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_VOLTAGE_MASK);
+		command |= AD7298_T_SENSE_MASK;
+		count = 1;
+	} else if (mask & AD7298_T_AVG_MASK) {
+		command = chip->command & ~AD7298_VOLTAGE_MASK;
+		command |= AD7298_T_SENSE_MASK | AD7298_T_AVG_MASK;
+		count = 2;
+	} else if (mask & AD7298_VOLTAGE_MASK) {
+		command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_T_SENSE_MASK);
+		count = chip->channels;
+	}
+
+	ret = ad7298_spi_write(chip, chip->command);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi_dev, (u8 *)&command, sizeof(command));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read error\n");
+		return ret;
+	}
+
+	i = 10000;
+	while (i && gpio_get_value(chip->busy_pin)) {
+		cpu_relax();
+		i--;
+	}
+	if (!i) {
+		dev_err(&spi_dev->dev, "Always in busy convertion.\n");
+		return -EBUSY;
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = spi_read(spi_dev, (u8 *)&data[i], sizeof(data[i]));
+		if (ret < 0) {
+			dev_err(&spi_dev->dev, "SPI read error\n");
+			return ret;
+		}
+		*data = be16_to_cpu(data[i]);
+	}
+
+	return 0;
+}
+
+static ssize_t ad7298_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	if (chip->command & AD7298_REPEAT)
+		return sprintf(buf, "repeat\n");
+	else
+		return sprintf(buf, "normal\n");
+}
+
+static ssize_t ad7298_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	if (strcmp(buf, "repeat"))
+		chip->command |= AD7298_REPEAT;
+	else
+		chip->command &= (~AD7298_REPEAT);
+
+	return 1;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7298_show_mode,
+		ad7298_store_mode,
+		0);
+
+static ssize_t ad7298_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "normal\nrepeat\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7298_show_available_modes, NULL, 0);
+
+static ssize_t ad7298_store_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & ~AD7298_PD;
+
+	ret = ad7298_spi_write(chip, command);
+	if (ret)
+		return -EIO;
+
+	command = chip->command | AD7298_PD;
+
+	ret = ad7298_spi_write(chip, command);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR,
+		NULL,
+		ad7298_store_reset,
+		0);
+
+static ssize_t ad7298_show_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->command & AD7298_EXT_REF));
+}
+
+static ssize_t ad7298_store_ext_ref(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 command;
+	int ret;
+
+	command = chip->command & (~AD7298_EXT_REF);
+	if (strcmp(buf, "1"))
+		command |= AD7298_EXT_REF;
+
+	ret = ad7298_spi_write(chip, command);
+	if (ret)
+		return -EIO;
+
+	chip->command = command;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR,
+		ad7298_show_ext_ref,
+		ad7298_store_ext_ref,
+		0);
+
+static ssize_t ad7298_show_t_sense(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = ad7298_spi_read(chip, AD7298_T_SENSE_MASK, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7298_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (AD7298_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data >> AD7298_T_VALUE_FLOAT_OFFSET),
+		(data & AD7298_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7298_show_t_sense, NULL, 0);
+
+static ssize_t ad7298_show_t_average(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 data[2];
+	char sign = ' ';
+	int ret;
+
+	ret = ad7298_spi_read(chip, AD7298_T_AVG_MASK, data);
+	if (ret)
+		return -EIO;
+
+	if (data[1] & AD7298_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data[1] = (AD7298_T_VALUE_SIGN << 1) - data[1];
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+		(data[1] >> AD7298_T_VALUE_FLOAT_OFFSET),
+		(data[1] & AD7298_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7298_show_t_average, NULL, 0);
+
+static ssize_t ad7298_show_voltage(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	u16 data[AD7298_VOLTAGE_LIMIT_COUNT];
+	int i, size, ret;
+
+	ret = ad7298_spi_read(chip, AD7298_VOLTAGE_MASK, data);
+	if (ret)
+		return -EIO;
+
+	for (i = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7298_T_SENSE_MASK << i)) {
+			ret = sprintf(buf, "channel[%d]=%d\n", i,
+					data[i] & AD7298_VALUE_MASK);
+			if (ret < 0)
+				break;
+			buf += ret;
+			size += ret;
+		}
+	}
+
+	return size;
+}
+
+static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7298_show_voltage, NULL, 0);
+
+static ssize_t ad7298_show_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%x\n", (chip->command & AD7298_VOLTAGE_MASK) >>
+			AD7298_VOLTAGE_OFFSET);
+}
+
+static ssize_t ad7298_store_channel_mask(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int i, ret;
+
+	ret = strict_strtoul(buf, 16, &data);
+	if (ret || data > 0xff)
+		return -EINVAL;
+
+	chip->command &= (~AD7298_VOLTAGE_MASK);
+	chip->command |= data << AD7298_VOLTAGE_OFFSET;
+
+	for (i = 0, chip->channels = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) {
+		if (chip->command & (AD7298_T_SENSE_MASK << i))
+			chip->channels++;
+	}
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR,
+		ad7298_show_channel_mask,
+		ad7298_store_channel_mask,
+		0);
+
+static ssize_t ad7298_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7298_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7298_show_name, NULL, 0);
+
+static struct attribute *ad7298_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_dev_attr_ext_ref.dev_attr.attr,
+	&iio_dev_attr_t_sense.dev_attr.attr,
+	&iio_dev_attr_t_average.dev_attr.attr,
+	&iio_dev_attr_voltage.dev_attr.attr,
+	&iio_dev_attr_channel_mask.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7298_attribute_group = {
+	.attrs = ad7298_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+static int __devinit ad7298_probe(struct spi_device *spi_dev)
+{
+	struct ad7298_chip_info *chip;
+	unsigned short *pins = spi_dev->dev.platform_data;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct ad7298_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+	chip->busy_pin = pins[0];
+
+	ret = gpio_request(chip->busy_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
+			chip->busy_pin);
+		goto error_free_chip;
+	}
+	gpio_direction_input(chip->busy_pin);
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_gpio;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &ad7298_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
+			 chip->name);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_gpio:
+	gpio_free(chip->busy_pin);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7298_remove(struct spi_device *spi_dev)
+{
+	struct ad7298_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	gpio_free(chip->busy_pin);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7298_id[] = {
+	{ "ad7298", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, ad7298_id);
+
+static struct spi_driver ad7298_driver = {
+	.driver = {
+		.name = "ad7298",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad7298_probe,
+	.remove = __devexit_p(ad7298_remove),
+	.id_table = ad7298_id,
+};
+
+static __init int ad7298_init(void)
+{
+	return spi_register_driver(&ad7298_driver);
+}
+
+static __exit void ad7298_exit(void)
+{
+	spi_unregister_driver(&ad7298_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7298 digital"
+			" temperature sensor and ADC driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7298_init);
+module_exit(ad7298_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
  2010-10-23 20:29 ` [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
  2010-10-23 20:29 ` [PATCH 03/14] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 21:56   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 05/14] staging: iio: adc: new driver for AD7414/5 devices Mike Frysinger
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7314.c |  308 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 316 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7314.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 847f5f2..6d3b8bc 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -54,3 +54,10 @@ config AD7298
 	help
 	  Say yes here to build support for Analog Devices AD7298
 	  temperature sensors and ADC.
+
+config AD7314
+	tristate "Analog Devices AD7314 temperature sensor driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7314
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index d0ea747..04fd93b 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
 obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
+obj-$(CONFIG_AD7314) += ad7314.o
diff --git a/drivers/staging/iio/adc/ad7314.c b/drivers/staging/iio/adc/ad7314.c
new file mode 100644
index 0000000..8c17b1f
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7314.c
@@ -0,0 +1,308 @@
+/*
+ * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7314 power mode
+ */
+#define AD7314_PD		0x2000
+
+/*
+ * AD7314 temperature masks
+ */
+#define AD7314_TEMP_SIGN		0x200
+#define AD7314_TEMP_MASK		0x7FE0
+#define AD7314_TEMP_OFFSET		5
+#define AD7314_TEMP_FLOAT_OFFSET	2
+#define AD7314_TEMP_FLOAT_MASK		0x3
+
+/*
+ * ADT7301 and ADT7302 temperature masks
+ */
+#define ADT7301_TEMP_SIGN		0x2000
+#define ADT7301_TEMP_MASK		0x2FFF
+#define ADT7301_TEMP_FLOAT_OFFSET	5
+#define ADT7301_TEMP_FLOAT_MASK		0x1F
+
+/*
+ * struct ad7314_chip_info - chip specifc information
+ */
+
+struct ad7314_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	s64 last_timestamp;
+	u8  mode;
+};
+
+/*
+ * ad7314 register access by SPI
+ */
+
+static int ad7314_spi_read(struct ad7314_chip_info *chip, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+	u16 value;
+
+	ret = spi_read(spi_dev, (u8 *)&value, sizeof(value));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu((u16)value);
+
+	return ret;
+}
+
+static int ad7314_spi_write(struct ad7314_chip_info *chip, u16 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+	u16 value = cpu_to_be16(data);
+
+	ret = spi_write(spi_dev, (u8 *)&value, sizeof(value));
+	if (ret < 0)
+		dev_err(&spi_dev->dev, "SPI write error\n");
+
+	return ret;
+}
+
+static ssize_t ad7314_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+
+	if (chip->mode)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t ad7314_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+	u16 mode = 0;
+	int ret;
+
+	if (!strcmp(buf, "full"))
+		mode = AD7314_PD;
+
+	ret = ad7314_spi_write(chip, mode);
+	if (ret)
+		return -EIO;
+
+	chip->mode = mode;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7314_show_mode,
+		ad7314_store_mode,
+		0);
+
+static ssize_t ad7314_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7314_show_available_modes, NULL, 0);
+
+static ssize_t ad7314_show_temperature(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->mode) {
+		ret = ad7314_spi_write(chip, 0);
+		if (ret)
+			return -EIO;
+	}
+
+	ret = ad7314_spi_read(chip, &data);
+	if (ret)
+		return -EIO;
+
+	if (chip->mode)
+		ad7314_spi_write(chip, chip->mode);
+
+	if (strcmp(chip->name, "ad7314")) {
+		data = (data & AD7314_TEMP_MASK) >>
+			AD7314_TEMP_OFFSET;
+		if (data & AD7314_TEMP_SIGN) {
+			data = (AD7314_TEMP_SIGN << 1) - data;
+			sign = '-';
+		}
+
+		return sprintf(buf, "%c%d.%.2d\n", sign,
+				data >> AD7314_TEMP_FLOAT_OFFSET,
+				(data & AD7314_TEMP_FLOAT_MASK) * 25);
+	} else {
+		data &= ADT7301_TEMP_MASK;
+		if (data & ADT7301_TEMP_SIGN) {
+			data = (ADT7301_TEMP_SIGN << 1) - data;
+			sign = '-';
+		}
+
+		return sprintf(buf, "%c%d.%.5d\n", sign,
+				data >> ADT7301_TEMP_FLOAT_OFFSET,
+				(data & ADT7301_TEMP_FLOAT_MASK) * 3125);
+	}
+}
+
+static IIO_DEVICE_ATTR(temperature, S_IRUGO, ad7314_show_temperature, NULL, 0);
+
+static ssize_t ad7314_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7314_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL, 0);
+
+static struct attribute *ad7314_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_temperature.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7314_attribute_group = {
+	.attrs = ad7314_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7314_probe(struct spi_device *spi_dev)
+{
+	struct ad7314_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct ad7314_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &ad7314_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	dev_info(&spi_dev->dev, "%s temperature sensor registered.\n",
+			 chip->name);
+
+	return 0;
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7314_remove(struct spi_device *spi_dev)
+{
+	struct ad7314_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	if (spi_dev->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7314_id[] = {
+	{ "adt7301", 0 },
+	{ "adt7302", 0 },
+	{ "ad7314", 0 },
+	{}
+};
+
+static struct spi_driver ad7314_driver = {
+	.driver = {
+		.name = "ad7314",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad7314_probe,
+	.remove = __devexit_p(ad7314_remove),
+	.id_table = ad7314_id,
+};
+
+static __init int ad7314_init(void)
+{
+	return spi_register_driver(&ad7314_driver);
+}
+
+static __exit void ad7314_exit(void)
+{
+	spi_unregister_driver(&ad7314_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7314_init);
+module_exit(ad7314_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 05/14] staging: iio: adc: new driver for AD7414/5 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (2 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 22:03   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 06/14] staging: iio: adc: new driver for AD7416/7/8 devices Mike Frysinger
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7414.c |  573 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 581 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7414.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 6d3b8bc..07fd1a7 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -61,3 +61,10 @@ config AD7314
 	help
 	  Say yes here to build support for Analog Devices AD7314
 	  temperature sensors.
+
+config AD7414
+	tristate "Analog Devices AD7414 and AD7415 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices AD7414 and AD7415
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 04fd93b..c1ab11f 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_AD7152) += ad7152.o
 obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7314) += ad7314.o
+obj-$(CONFIG_AD7414) += ad7414.o
diff --git a/drivers/staging/iio/adc/ad7414.c b/drivers/staging/iio/adc/ad7414.c
new file mode 100644
index 0000000..b92c880
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7414.c
@@ -0,0 +1,573 @@
+/*
+ * AD7414 digital temperature sensor driver supporting AD7414 and AD7415
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7414 registers definition
+ */
+
+#define AD7414_TEMPERATURE		0
+#define AD7414_CONFIG			1
+#define AD7414_TEMPERATURE_HIGH		2
+#define AD7414_TEMPERATURE_LOW		3
+
+/*
+ * AD7414 config bits
+ */
+#define AD7414_TEST_MODE	0x3
+#define AD7414_ONE_SHOT		0x4
+#define AD7414_ALERT_RESET	0x8
+#define AD7414_ALERT_POLARITY	0x10
+#define AD7414_ALERT_EN		0x20
+#define AD7414_FLTR		0x40
+#define AD7414_PD		0x80
+
+/*
+ * AD7414 masks
+ */
+#define AD7414_TEMP_SIGN	0x200
+#define AD7414_TEMP_MASK	0xFFC0
+#define AD7414_TEMP_OFFSET	6
+#define AD7414_ALERT_FLAG	0x20
+#define AD7414_T_SIGN		0x80
+#define AD7414_T_HIGH_FLAG	0x10
+#define AD7414_T_LOW_FLAG	0x8
+
+
+/*
+ * struct ad7414_chip_info - chip specifc information
+ */
+
+struct ad7414_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  mode;
+};
+
+/*
+ * ad7414 register access by I2C
+ */
+
+static int ad7414_i2c_read(struct ad7414_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0, len;
+
+	ret = i2c_smbus_write_byte(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read register address error\n");
+		return ret;
+	}
+
+	if (reg == AD7414_TEMPERATURE)
+		len = 2;
+	else
+		len = 1;
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ad7414_i2c_write(struct ad7414_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static ssize_t ad7414_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+
+	if (chip->mode)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t ad7414_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	u8 config;
+	int ret;
+
+	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	if (strcmp(buf, "full")) {
+		chip->mode = 0;
+		config &= ~AD7414_PD;
+	} else {
+		chip->mode = 1;
+		config |= AD7414_PD;
+	}
+
+	ret = ad7414_i2c_write(chip, AD7414_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7414_show_mode,
+		ad7414_store_mode,
+		0);
+
+static ssize_t ad7414_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7414_show_available_modes, NULL, 0);
+
+static ssize_t ad7414_show_temperature(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	u8 config;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->mode) {
+		ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
+		if (ret)
+			return -EIO;
+		ret = ad7414_i2c_write(chip, AD7414_CONFIG,
+				config | AD7414_ONE_SHOT);
+		if (ret)
+			return -EIO;
+	}
+
+	ret = ad7414_i2c_read(chip, AD7414_TEMPERATURE, (u8 *)&data);
+	if (ret)
+		return -EIO;
+
+	data = swab16(data) >> AD7414_TEMP_OFFSET;
+	if (data & AD7414_TEMP_SIGN) {
+		data = (AD7414_TEMP_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign, (data >> 2), (data & 3) * 25);
+}
+
+static IIO_DEVICE_ATTR(temperature, S_IRUGO, ad7414_show_temperature, NULL, 0);
+
+static ssize_t ad7414_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7414_show_name, NULL, 0);
+
+static struct attribute *ad7414_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_temperature.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7414_attribute_group = {
+	.attrs = ad7414_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7414_T_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_AD7414_T_LOW     (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+
+static void ad7414_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad7414_chip_info *chip =
+		container_of(work_s, struct ad7414_chip_info, thresh_work);
+	u16 status;
+	u8 config;
+	int ret;
+
+	/* retrive ALART status */
+	ret = ad7414_i2c_read(chip, AD7414_TEMPERATURE, (u8 *)&status);
+	if (ret)
+		return;
+	status = swab16(status);
+
+	/* clear ALART pin in chip configuration register */
+	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
+	if (ret)
+		return;
+	ret = ad7414_i2c_write(chip, AD7414_CONFIG,
+			config | AD7414_ALERT_RESET);
+	if (ret)
+		return;
+
+	enable_irq(chip->client->irq);
+
+	if (status & AD7414_T_HIGH_FLAG)
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_AD7414_T_HIGH,
+				chip->last_timestamp);
+	else if (status & AD7414_T_LOW_FLAG)
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_AD7414_T_LOW,
+				chip->last_timestamp);
+}
+
+static int ad7414_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(ad7414, &ad7414_interrupt);
+
+static ssize_t ad7414_show_enabled(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	u8 config;
+	int ret;
+
+	/* retrive ALART status */
+	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(config & AD7414_ALERT_EN));
+}
+
+static ssize_t ad7414_set_enabled(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u8 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	if (data)
+		ret = ad7414_i2c_write(chip, AD7414_CONFIG,
+			config & ~AD7414_ALERT_EN);
+	else
+		ret = ad7414_i2c_write(chip, AD7414_CONFIG,
+			config | AD7414_ALERT_EN);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static inline ssize_t ad7414_show_temperature_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	s8 data;
+	int ret;
+
+	ret = ad7414_i2c_read(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & AD7414_T_SIGN)
+		data = (data&(~AD7414_T_SIGN)) - AD7414_T_SIGN;
+
+	return sprintf(buf, "%d\n", data);
+}
+
+static inline ssize_t ad7414_set_temperature_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7414_chip_info *chip = dev_info->dev_data;
+	long data;
+	s8 value;
+	int ret;
+
+	ret = strict_strtol(buf, 10, &data);
+
+	if (ret || data > 127 || data < -128)
+		return -EINVAL;
+
+	value = (s8)data;
+	if (value < 0)
+		value = (AD7414_T_SIGN + value) | AD7414_T_SIGN;
+
+	ret = ad7414_i2c_write(chip, bound_reg, value);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t ad7414_show_temperature_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7414_show_temperature_bound(dev, attr,
+			AD7414_TEMPERATURE_HIGH, buf);
+}
+
+static inline ssize_t ad7414_set_temperature_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7414_set_temperature_bound(dev, attr,
+			AD7414_TEMPERATURE_HIGH, buf, len);
+}
+
+static ssize_t ad7414_show_temperature_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7414_show_temperature_bound(dev, attr,
+			AD7414_TEMPERATURE_LOW, buf);
+}
+
+static inline ssize_t ad7414_set_temperature_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7414_set_temperature_bound(dev, attr,
+			AD7414_TEMPERATURE_LOW, buf, len);
+}
+
+IIO_EVENT_ATTR_SH(t_bound_enabled, iio_event_ad7414,
+		ad7414_show_enabled, ad7414_set_enabled, 0);
+IIO_EVENT_ATTR_SH(temperature_high, iio_event_ad7414,
+		ad7414_show_temperature_high, ad7414_set_temperature_high, 0);
+IIO_EVENT_ATTR_SH(temperature_low, iio_event_ad7414,
+		ad7414_show_temperature_low, ad7414_set_temperature_low, 0);
+
+static struct attribute *ad7414_event_attributes[] = {
+	&iio_event_attr_t_bound_enabled.dev_attr.attr,
+	&iio_event_attr_temperature_high.dev_attr.attr,
+	&iio_event_attr_temperature_low.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7414_event_attribute_group = {
+	.attrs = ad7414_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7414_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ad7414_chip_info *chip;
+	int ret = 0;
+	u8 config;
+
+	chip = kzalloc(sizeof(struct ad7414_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7414_attribute_group;
+	if (strcmp(id->name, "ad7414") == 0)
+		chip->indio_dev->event_attrs = &ad7414_event_attribute_group;
+	else
+		client->irq = 0;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				client->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_ad7414.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad7414,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7414_interrupt_bh);
+
+		ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+
+		if (client->irq_flags & IRQF_TRIGGER_HIGH)
+			ret = ad7414_i2c_write(chip, AD7414_CONFIG,
+				config | AD7414_ALERT_POLARITY);
+		else
+			ret = ad7414_i2c_write(chip, AD7414_CONFIG,
+				config & ~AD7414_ALERT_POLARITY);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7414_remove(struct i2c_client *client)
+{
+	struct ad7414_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7414_id[] = {
+	{ "ad7414", 0 },
+	{ "ad7415", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7414_id);
+
+static struct i2c_driver ad7414_driver = {
+	.driver = {
+		.name = "ad7414",
+	},
+	.probe = ad7414_probe,
+	.remove = __devexit_p(ad7414_remove),
+	.id_table = ad7414_id,
+};
+
+static __init int ad7414_init(void)
+{
+	return i2c_add_driver(&ad7414_driver);
+}
+
+static __exit void ad7414_exit(void)
+{
+	i2c_del_driver(&ad7414_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7414 and AD7415 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7414_init);
+module_exit(ad7414_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 06/14] staging: iio: adc: new driver for AD7416/7/8 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (3 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 05/14] staging: iio: adc: new driver for AD7414/5 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 22:19   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices Mike Frysinger
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7416.c |  777 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 785 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7416.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 07fd1a7..59d6fb6 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -68,3 +68,10 @@ config AD7414
 	help
 	  Say yes here to build support for Analog Devices AD7414 and AD7415
 	  temperature sensors.
+
+config AD7416
+	tristate "Analog Devices AD7416/7/8 temperature sensor and ADC driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices AD74167/8
+	  temperature sensors and ADC.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index c1ab11f..e95c8d4 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_AD7291) += ad7291.o
 obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7314) += ad7314.o
 obj-$(CONFIG_AD7414) += ad7414.o
+obj-$(CONFIG_AD7416) += ad7416.o
diff --git a/drivers/staging/iio/adc/ad7416.c b/drivers/staging/iio/adc/ad7416.c
new file mode 100644
index 0000000..b89fc86
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7416.c
@@ -0,0 +1,777 @@
+/*
+ * AD7416 digital temperature sensor driver supporting AD7416/7/8
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7416 registers definition
+ */
+#define AD7416_TEMPERATURE		0
+#define AD7416_CONFIG			1
+#define AD7416_T_OTI			2
+#define AD7416_T_HYST			3
+#define AD7416_ADC_VALUE		4
+#define AD7416_CONFIG2			5
+
+/*
+ * AD7416 config
+ */
+#define AD7416_PD			0x1
+#define AD7416_OTI_INT			0x2
+#define AD7416_OTI_POLARITY		0x4
+#define AD7416_FAULT_QUEUE_MASK		0x18
+#define AD7416_FAULT_QUEUE_OFFSET	3
+#define AD7416_CS_MASK			0xE0
+#define AD7416_CS_OFFSET		5
+
+/*
+ * AD7416 config
+ */
+#define AD7416_CONVERT			0x80
+
+/*
+ * AD7416 mode
+ */
+#define AD7416_MODE_FULL		0
+#define AD7416_MODE_SHUTDOWN		1
+#define AD7416_MODE_CONVERT		2
+
+/*
+ * AD7416/7/8 channel maks
+ */
+#define AD7416_CHANNEL_MASK	0x1
+#define AD7417_CHANNEL_MASK	0x1F
+#define AD7418_CHANNEL_MASK	0x11
+
+/*
+ * AD7416 masks
+ */
+#define AD7416_VALUE_SIGN	0x200
+#define AD7416_VALUE_OFFSET	6
+#define AD7416_BOUND_VALUE_SIGN	0x100
+
+
+/*
+ * struct ad7416_chip_info - chip specifc information
+ */
+
+struct ad7416_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u16 convert_pin;
+	u8  mode;
+	u8  channel_id;	/* 0 always be temperature */
+	u8  channel_mask;
+};
+
+/*
+ * ad7416 register access by I2C
+ */
+
+static int ad7416_i2c_read(struct ad7416_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0, len;
+
+	ret = i2c_smbus_write_byte(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read register address error\n");
+		return ret;
+	}
+
+	if (reg == AD7416_CONFIG || reg == AD7416_CONFIG2)
+		len = 1;
+	else
+		len = 2;
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ad7416_i2c_write(struct ad7416_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static ssize_t ad7416_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+
+	switch (chip->mode) {
+	case AD7416_MODE_SHUTDOWN:
+		return sprintf(buf, "power-save\n");
+	case AD7416_MODE_CONVERT:
+		return sprintf(buf, "convert\n");
+	default:
+		return sprintf(buf, "full\n");
+	}
+}
+
+static ssize_t ad7416_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	u8 config, config2;
+	int ret;
+
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG2, &config2);
+	if (ret)
+		return -EIO;
+
+	if (strcmp(buf, "full")) {
+		config2 &= ~AD7416_CONVERT;
+		config &= ~AD7416_PD;
+
+		ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
+		if (ret)
+			return -EIO;
+
+		if (chip->mode == AD7416_MODE_CONVERT) {
+			ret = ad7416_i2c_write(chip, AD7416_CONFIG2, config2);
+			if (ret)
+				return -EIO;
+		}
+
+		chip->mode = AD7416_MODE_FULL;
+	} else if (chip->convert_pin && strcmp(buf, "convert")) {
+		config2 |= AD7416_CONVERT;
+
+		ret = ad7416_i2c_write(chip, AD7416_CONFIG2, config2);
+		if (ret)
+			return -EIO;
+
+		chip->mode = AD7416_MODE_CONVERT;
+	} else {
+		config |= AD7416_PD;
+		config2 &= ~AD7416_CONVERT;
+
+		ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
+		if (ret)
+			return -EIO;
+
+		if (chip->mode == AD7416_MODE_CONVERT) {
+			ret = ad7416_i2c_write(chip, AD7416_CONFIG2, config2);
+			if (ret)
+				return -EIO;
+		}
+
+		chip->mode = AD7416_MODE_SHUTDOWN;
+	}
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7416_show_mode,
+		ad7416_store_mode,
+		0);
+
+static ssize_t ad7416_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+
+	if (chip->convert_pin)
+		return sprintf(buf, "full\npower-save\nconvert\n");
+	else
+		return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7416_show_available_modes, NULL, 0);
+
+static ssize_t ad7416_show_channel(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->channel_id);
+}
+
+static ssize_t ad7416_store_channel(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u8 channel_mask;
+	u8 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	channel_mask = 1 << data;
+	if (!(channel_mask & chip->channel_mask)) {
+		dev_err(&chip->client->dev, "Invalid channel id %lu.\n", data);
+		dev_err(&chip->client->dev, "Available channel mask 0x%x.\n",
+				chip->channel_mask);
+		return -EINVAL;
+	} else if (data == chip->channel_id)
+		return ret;
+
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	config |= ((data << AD7416_CS_OFFSET) && AD7416_CS_MASK);
+	ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR,
+		ad7416_show_channel,
+		ad7416_store_channel,
+		0);
+
+
+static ssize_t ad7416_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->mode == AD7416_MODE_SHUTDOWN) {
+		/* write to active converter */
+		ret = i2c_smbus_write_byte(chip->client, AD7416_TEMPERATURE);
+		if (ret)
+			return -EIO;
+	} else if (chip->mode == AD7416_MODE_CONVERT) {
+		gpio_set_value(chip->convert_pin, 1);
+		gpio_set_value(chip->convert_pin, 0);
+	}
+
+	if (chip->channel_id == 0) {
+		ret = ad7416_i2c_read(chip, AD7416_TEMPERATURE, (u8 *)&data);
+		if (ret)
+			return -EIO;
+
+		data = swab16(data);
+		data >>= AD7416_VALUE_OFFSET;
+		if (data & AD7416_VALUE_SIGN) {
+			data = (AD7416_VALUE_SIGN << 1) - data;
+			sign = '-';
+		}
+
+		return sprintf(buf, "%c%d.%.2d\n", sign, (data >> 2),
+			 (data & 3) * 25);
+	} else {
+		ret = ad7416_i2c_read(chip, AD7416_ADC_VALUE, (u8 *)&data);
+		if (ret)
+			return -EIO;
+
+		data = swab16(data);
+		data >>= AD7416_VALUE_OFFSET;
+
+		return sprintf(buf, "%u\n", data);
+	}
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, ad7416_show_value, NULL, 0);
+
+static ssize_t ad7416_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7416_show_name, NULL, 0);
+
+static struct attribute *ad7416_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_channel.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7416_attribute_group = {
+	.attrs = ad7416_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7416_OTI    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+
+static void ad7416_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad7416_chip_info *chip =
+		container_of(work_s, struct ad7416_chip_info, thresh_work);
+
+	enable_irq(chip->client->irq);
+
+	iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_AD7416_OTI,
+			chip->last_timestamp);
+}
+
+static int ad7416_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(ad7416, &ad7416_interrupt);
+
+static ssize_t ad7416_show_oti_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	u8 config;
+	int ret;
+
+	/* retrive ALART status */
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	if (config & AD7416_OTI_INT)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t ad7416_set_oti_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	u8 config;
+	int ret;
+
+	/* retrive ALART status */
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	if (strcmp(buf, "comparator") == 0)
+		ret = ad7416_i2c_write(chip, AD7416_CONFIG,
+			config & ~AD7416_OTI_INT);
+	else
+		ret = ad7416_i2c_write(chip, AD7416_CONFIG,
+			config | AD7416_OTI_INT);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t ad7416_show_available_oti_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t ad7416_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	u8 config;
+	int ret;
+
+	/* retrive ALART status */
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", (config & AD7416_FAULT_QUEUE_MASK) >>
+			AD7416_FAULT_QUEUE_OFFSET);
+}
+
+static ssize_t ad7416_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u8 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+	if (ret)
+		return -EIO;
+
+	config &= ~AD7416_FAULT_QUEUE_MASK;
+	config |= (data << AD7416_FAULT_QUEUE_OFFSET);
+	ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+static inline ssize_t ad7416_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	s16 value;
+	int ret;
+
+	ret = ad7416_i2c_read(chip, bound_reg, (u8 *)&data);
+	if (ret)
+		return -EIO;
+
+	data = swab16(data);
+	data >>= (AD7416_VALUE_OFFSET + 1);
+
+	if (chip->channel_id == 0) {
+		if (data & AD7416_BOUND_VALUE_SIGN) {
+			value = (s16)(data&(~AD7416_BOUND_VALUE_SIGN));
+			value = value - AD7416_BOUND_VALUE_SIGN;
+		} else
+			value = (s16)data;
+
+		return sprintf(buf, "%d\n", value >> 1);
+	} else
+		return sprintf(buf, "%u\n", data);
+}
+
+static inline ssize_t ad7416_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7416_chip_info *chip = dev_info->dev_data;
+	long value;
+	u16 data;
+	int ret;
+
+	if (chip->channel_id == 0) {
+		ret = strict_strtol(buf, 10, &value);
+
+		if (ret || value < -128 || value > 127)
+			return -EINVAL;
+		if (value < 0)
+			value = (AD7416_BOUND_VALUE_SIGN + value) |
+				AD7416_BOUND_VALUE_SIGN;
+
+		data = (u16)value << 1;
+	} else {
+		ret = strict_strtoul(buf, 10, &value);
+
+		if (ret || value > 511 || value < 0)
+			return -EINVAL;
+
+		data = (u16)value;
+	}
+
+	data <<= (AD7416_VALUE_OFFSET + 1);
+	data = swab16(data);
+	ret = ad7416_i2c_write(chip, bound_reg, (u8)data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t ad7416_show_t_oti(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7416_show_t_bound(dev, attr,
+			AD7416_T_OTI, buf);
+}
+
+static inline ssize_t ad7416_set_t_oti(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7416_set_t_bound(dev, attr,
+			AD7416_T_OTI, buf, len);
+}
+
+static ssize_t ad7416_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return ad7416_show_t_bound(dev, attr,
+			AD7416_T_HYST, buf);
+}
+
+static inline ssize_t ad7416_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return ad7416_set_t_bound(dev, attr,
+			AD7416_T_HYST, buf, len);
+}
+
+IIO_EVENT_ATTR_SH(oti_mode, iio_event_ad7416,
+		ad7416_show_oti_mode, ad7416_set_oti_mode, 0);
+IIO_EVENT_ATTR_SH(available_oti_modes, iio_event_ad7416,
+		ad7416_show_available_oti_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_ad7416,
+		ad7416_show_fault_queue, ad7416_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_oti, iio_event_ad7416,
+		ad7416_show_t_oti, ad7416_set_t_oti, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_ad7416,
+		ad7416_show_t_hyst, ad7416_set_t_hyst, 0);
+
+static struct attribute *ad7416_event_attributes[] = {
+	&iio_event_attr_oti_mode.dev_attr.attr,
+	&iio_event_attr_available_oti_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_oti.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7416_event_attribute_group = {
+	.attrs = ad7416_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7416_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ad7416_chip_info *chip;
+	unsigned long convert_pin = (unsigned long)client->dev.platform_data;
+	int ret = 0;
+	u8 config;
+
+	chip = kzalloc(sizeof(struct ad7416_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+	if (strcmp(chip->name, "ad7418") == 0) {
+		chip->channel_mask = AD7418_CHANNEL_MASK;
+		if (convert_pin) {
+			ret = gpio_request(convert_pin, chip->name);
+			if (!ret) {
+				chip->convert_pin = (u16)convert_pin;
+				gpio_set_value(chip->convert_pin, 0);
+			}
+		}
+	} else if (strcmp(chip->name, "ad7417") == 0) {
+		chip->channel_mask = AD7417_CHANNEL_MASK;
+		if (convert_pin) {
+			ret = gpio_request(convert_pin, chip->name);
+			if (!ret) {
+				chip->convert_pin = (u16)convert_pin;
+				gpio_set_value(chip->convert_pin, 0);
+			}
+		}
+	} else
+		chip->channel_mask = AD7416_CHANNEL_MASK;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad7416_attribute_group;
+	chip->indio_dev->event_attrs = &ad7416_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				client->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_ad7416.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad7416,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7416_interrupt_bh);
+
+		ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+
+		if (client->irq_flags & IRQF_TRIGGER_HIGH)
+			ret = ad7416_i2c_write(chip, AD7416_CONFIG,
+				config | AD7416_OTI_POLARITY);
+		else
+			ret = ad7416_i2c_write(chip, AD7416_CONFIG,
+				config & ~AD7416_OTI_POLARITY);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor and ADC registered.\n",
+			 id->name);
+
+	return 0;
+
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7416_remove(struct i2c_client *client)
+{
+	struct ad7416_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	if (chip->convert_pin)
+		gpio_free(chip->convert_pin);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad7416_id[] = {
+	{ "ad7416", 0 },
+	{ "ad7417", 0 },
+	{ "ad7418", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7416_id);
+
+static struct i2c_driver ad7416_driver = {
+	.driver = {
+		.name = "ad7416",
+	},
+	.probe = ad7416_probe,
+	.remove = __devexit_p(ad7416_remove),
+	.id_table = ad7416_id,
+};
+
+static __init int ad7416_init(void)
+{
+	return i2c_add_driver(&ad7416_driver);
+}
+
+static __exit void ad7416_exit(void)
+{
+	i2c_del_driver(&ad7416_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7416/7/8 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7416_init);
+module_exit(ad7416_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (4 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 06/14] staging: iio: adc: new driver for AD7416/7/8 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 21:14   ` [Device-drivers-devel] " Mike Frysinger
  2010-10-24 22:21   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 08/14] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
                   ` (7 subsequent siblings)
  13 siblings, 2 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig       |   11 ++
 drivers/staging/iio/adc/Makefile      |    4 +
 drivers/staging/iio/adc/ad7476.h      |   66 +++++++++
 drivers/staging/iio/adc/ad7476_core.c |  203 ++++++++++++++++++++++++++++
 drivers/staging/iio/adc/ad7476_ring.c |  240 +++++++++++++++++++++++++++++++++
 5 files changed, 524 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7476.h
 create mode 100644 drivers/staging/iio/adc/ad7476_core.c
 create mode 100644 drivers/staging/iio/adc/ad7476_ring.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 59d6fb6..4cb1542 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -75,3 +75,14 @@ config AD7416
 	help
 	  Say yes here to build support for Analog Devices AD74167/8
 	  temperature sensors and ADC.
+
+config AD7476
+	tristate "Analog Devices AD7475/6/7/8 and AD7495 ADC driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7475/6/7/8,
+	  AD7495 ADC driver.
+	  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 ad7476.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index e95c8d4..67e6341 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -5,6 +5,9 @@
 max1363-y := max1363_core.o
 max1363-y += max1363_ring.o
 
+ad7476-y := ad7476_core.o
+ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o
+
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
@@ -13,3 +16,4 @@ obj-$(CONFIG_AD7298) += ad7298.o
 obj-$(CONFIG_AD7314) += ad7314.o
 obj-$(CONFIG_AD7414) += ad7414.o
 obj-$(CONFIG_AD7416) += ad7416.o
+obj-$(CONFIG_AD7476) += ad7476.o
diff --git a/drivers/staging/iio/adc/ad7476.h b/drivers/staging/iio/adc/ad7476.h
new file mode 100644
index 0000000..fc69f83
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7476.h
@@ -0,0 +1,66 @@
+#ifndef _AD7476_H_
+#define  _AD7476_H_
+
+struct ad7476_mode {
+	const char	*name;
+	int		numvals;
+};
+
+struct ad7476_state {
+	struct iio_dev			*indio_dev;
+	struct spi_device		*spi;
+	char				setupbyte;
+	char				configbyte;
+	const struct ad7476_chip_info	*chip_info;
+	const struct ad7476_mode	*current_mode;
+	struct work_struct		poll_work;
+	atomic_t			protect_ring;
+	struct iio_trigger		*trig;
+	struct spi_transfer		xfer;
+	struct spi_message		msg;
+	unsigned char			data[2];
+};
+
+#define CHIP_NAME "AD7876/7/8"
+
+#ifdef CONFIG_IIO_RING_BUFFER
+
+ssize_t ad7476_scan_from_ring(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf);
+int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev);
+void ad7476_ring_cleanup(struct iio_dev *indio_dev);
+
+int ad7476_initialize_ring(struct iio_ring_buffer *ring);
+void ad7476_uninitialize_ring(struct iio_ring_buffer *ring);
+
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void ad7476_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+static inline int ad7476_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+
+static inline ssize_t ad7476_scan_from_ring(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+	return 0;
+}
+
+static inline int
+ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void ad7476_ring_cleanup(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* _AD7476_H_ */
diff --git a/drivers/staging/iio/adc/ad7476_core.c b/drivers/staging/iio/adc/ad7476_core.c
new file mode 100644
index 0000000..7181440
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7476_core.c
@@ -0,0 +1,203 @@
+/*
+ * AD7476/5/7/8 (A) SPI ADC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#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 "../iio.h"
+#include "../sysfs.h"
+
+#include "ad7476.h"
+
+static const struct ad7476_mode ad7476_mode_table[] = {
+	{
+		.name = "s0",
+		.numvals = 1,
+	},
+};
+
+static ssize_t ad7476_scan_direct(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7476_state *st = dev_info->dev_data;
+	int ret;
+	struct spi_device *spi = st->spi;
+
+	ret = spi_sync(spi, &st->msg);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%d\n", (st->data[0] << 8) | st->data[1]);
+}
+
+static ssize_t ad7476_scan(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&dev_info->mlock);
+	if (dev_info->currentmode == INDIO_RING_TRIGGERED)
+		ret = ad7476_scan_from_ring(dev, attr, buf);
+	else
+		ret = ad7476_scan_direct(dev, attr, buf);
+	mutex_unlock(&dev_info->mlock);
+
+	return ret;
+}
+
+/* Cannot query the device, so use local copy of state */
+static ssize_t ad7476_show_scan_mode(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7476_state *st = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", st->current_mode->name);
+}
+
+
+static IIO_DEV_ATTR_AVAIL_SCAN_MODES(ad7476_show_scan_mode);
+static IIO_DEV_ATTR_SCAN_MODE(S_IRUGO | S_IWUSR,
+		       ad7476_show_scan_mode, NULL);
+
+static IIO_DEV_ATTR_SCAN(ad7476_scan);
+
+static ssize_t ad7476_show_name(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	return sprintf(buf, "%s\n", CHIP_NAME);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7476_show_name, NULL, 0);
+
+/*name export */
+
+static struct attribute *ad7476_attributes[] = {
+	&iio_dev_attr_available_scan_modes.dev_attr.attr,
+	&iio_dev_attr_scan_mode.dev_attr.attr,
+	&iio_dev_attr_scan.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7476_attribute_group = {
+	.attrs = ad7476_attributes,
+};
+
+static int __devinit ad7476_probe(struct spi_device *spi)
+{
+	int ret;
+	struct ad7476_state *st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	spi_set_drvdata(spi, st);
+
+	atomic_set(&st->protect_ring, 0);
+	st->spi = spi;
+
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+
+	/* Estabilish that the iio_dev is a child of the i2c device */
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->attrs = &ad7476_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	st->current_mode = &ad7476_mode_table[0];
+
+	/* Setup default message */
+
+	st->xfer.rx_buf = &st->data;
+	st->xfer.len = st->current_mode->numvals * 2;
+
+	spi_message_init(&st->msg);
+	spi_message_add_tail(&st->xfer, &st->msg);
+
+	ret = ad7476_register_ring_funcs_and_init(st->indio_dev);
+	if (ret)
+		goto error_free_device;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_free_device;
+
+	ret = ad7476_initialize_ring(st->indio_dev->ring);
+	if (ret)
+		goto error_cleanup_ring;
+	return 0;
+
+error_cleanup_ring:
+	ad7476_ring_cleanup(st->indio_dev);
+	iio_device_unregister(st->indio_dev);
+error_free_device:
+	iio_free_device(st->indio_dev);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int ad7476_remove(struct spi_device *spi)
+{
+	struct ad7476_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+	ad7476_uninitialize_ring(indio_dev->ring);
+	ad7476_ring_cleanup(indio_dev);
+	iio_device_unregister(indio_dev);
+	kfree(st);
+
+	return 0;
+}
+
+
+static struct spi_driver ad7476_driver = {
+	.driver = {
+		.name	= "ad7476",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad7476_probe,
+	.remove		= __devexit_p(ad7476_remove),
+};
+
+static int __init ad7476_init(void)
+{
+	return spi_register_driver(&ad7476_driver);
+}
+module_init(ad7476_init);
+
+static void __exit ad7476_exit(void)
+{
+	spi_unregister_driver(&ad7476_driver);
+}
+module_exit(ad7476_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) ADC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad7476");
diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c
new file mode 100644
index 0000000..c1fe346
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7476_ring.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2010 Analog Devices Inc.
+ * Copyright (C) 2008 Jonathan Cameron
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * ad7476_ring.c
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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 "../iio.h"
+#include "../ring_generic.h"
+#include "../ring_sw.h"
+#include "../trigger.h"
+#include "../sysfs.h"
+
+#include "ad7476.h"
+
+ssize_t ad7476_scan_from_ring(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7476_state *info = dev_info->dev_data;
+	int i, ret, len = 0;
+	char *ring_data;
+
+	ring_data = kmalloc(info->current_mode->numvals*2, GFP_KERNEL);
+	if (ring_data == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	ret = dev_info->ring->access.read_last(dev_info->ring, ring_data);
+	if (ret)
+		goto error_free_ring_data;
+	len += sprintf(buf+len, "ring ");
+	for (i = 0; i < info->current_mode->numvals; i++)
+		len += sprintf(buf + len, "%d ",
+			       ((int)(ring_data[i*2 + 0] & 0xFF) << 8)
+			       + ((int)(ring_data[i*2 + 1])));
+	len += sprintf(buf + len, "\n");
+	kfree(ring_data);
+
+	return len;
+
+error_free_ring_data:
+	kfree(ring_data);
+error_ret:
+	return ret;
+}
+
+/**
+ * ad7476_ring_preenable() setup the parameters of the ring before enabling
+ *
+ * The complex nature of the setting of the nuber of bytes per datum is due
+ * to this driver currently ensuring that the timestamp is stored at an 8
+ * byte boundary.
+ **/
+static int ad7476_ring_preenable(struct iio_dev *indio_dev)
+{
+	struct ad7476_state *st = indio_dev->dev_data;
+	size_t d_size;
+
+	if (indio_dev->ring->access.set_bpd) {
+		d_size = st->current_mode->numvals*2 + sizeof(s64);
+		if (d_size % 8)
+			d_size += 8 - (d_size % 8);
+		indio_dev->ring->access.set_bpd(indio_dev->ring, d_size);
+	}
+
+	return 0;
+}
+
+/**
+ * ad7476_ring_postenable() typical ring post enable
+ *
+ * Only not moved into the core for the hardware ring buffer cases
+ * that are more sophisticated.
+ **/
+static int ad7476_ring_postenable(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig == NULL)
+		return 0;
+	return iio_trigger_attach_poll_func(indio_dev->trig,
+					    indio_dev->pollfunc);
+}
+
+/**
+ * ad7476_ring_predisable() runs just prior to ring buffer being disabled
+ *
+ * Typical predisable function which ensures that no trigger events can
+ * occur before we disable the ring buffer (and hence would have no idea
+ * what to do with them)
+ **/
+static int ad7476_ring_predisable(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig)
+		return iio_trigger_dettach_poll_func(indio_dev->trig,
+						     indio_dev->pollfunc);
+	else
+		return 0;
+}
+
+/**
+ * ad7476_poll_func_th() th of trigger launched polling to ring buffer
+ *
+ * As sampling only occurs on i2c comms occuring, leave timestamping until
+ * then.  Some triggers will generate their own time stamp.  Currently
+ * there is no way of notifying them when no one cares.
+ **/
+void ad7476_poll_func_th(struct iio_dev *indio_dev)
+{
+	struct ad7476_state *st = indio_dev->dev_data;
+
+	schedule_work(&st->poll_work);
+
+	return;
+}
+/**
+ * ad7476_poll_bh_to_ring() bh of trigger launched polling to ring buffer
+ * @work_s:	the work struct through which this was scheduled
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ * I think the one copy of this at a time was to avoid problems if the
+ * trigger was set far too high and the reads then locked up the computer.
+ **/
+static void ad7476_poll_bh_to_ring(struct work_struct *work_s)
+{
+	struct ad7476_state *st = container_of(work_s, struct ad7476_state,
+						  poll_work);
+	struct iio_dev *indio_dev = st->indio_dev;
+	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(indio_dev->ring);
+	s64 time_ns;
+	__u8 *rxbuf;
+	int b_sent;
+	size_t d_size;
+
+	/* Ensure the timestamp is 8 byte aligned */
+	d_size = st->current_mode->numvals*2 + sizeof(s64);
+	if (d_size % sizeof(s64))
+		d_size += sizeof(s64) - (d_size % sizeof(s64));
+
+	/* Ensure only one copy of this function running at a time */
+	if (atomic_inc_return(&st->protect_ring) > 1)
+		return;
+
+	/* Monitor mode prevents reading. Whilst not currently implemented
+	 * might as well have this test in here in the meantime as it does
+	 * no harm.
+	 */
+	if (st->current_mode->numvals == 0)
+		return;
+
+	rxbuf = kmalloc(d_size,	GFP_KERNEL);
+	if (rxbuf == NULL)
+		return;
+
+	b_sent = spi_read(st->spi, rxbuf,
+				 st->current_mode->numvals * 2);
+	if (b_sent < 0)
+		goto done;
+
+	time_ns = iio_get_time_ns();
+
+	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
+
+	indio_dev->ring->access.store_to(&ring->buf, rxbuf, time_ns);
+done:
+	kfree(rxbuf);
+	atomic_dec(&st->protect_ring);
+}
+
+
+int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	struct ad7476_state *st = indio_dev->dev_data;
+	int ret = 0;
+
+	indio_dev->ring = iio_sw_rb_allocate(indio_dev);
+	if (!indio_dev->ring) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&st->indio_dev->ring->access);
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_deallocate_sw_rb;
+	}
+	/* Configure the polling function called on trigger interrupts */
+	indio_dev->pollfunc->poll_func_main = &ad7476_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+
+	/* Ring buffer functions - here trigger setup related */
+	indio_dev->ring->postenable = &ad7476_ring_postenable;
+	indio_dev->ring->preenable = &ad7476_ring_preenable;
+	indio_dev->ring->predisable = &ad7476_ring_predisable;
+	INIT_WORK(&st->poll_work, &ad7476_poll_bh_to_ring);
+
+	/* Flag that polled ring buffering is possible */
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+error_deallocate_sw_rb:
+	iio_sw_rb_free(indio_dev->ring);
+error_ret:
+	return ret;
+}
+
+void ad7476_ring_cleanup(struct iio_dev *indio_dev)
+{
+	/* ensure that the trigger has been detached */
+	if (indio_dev->trig) {
+		iio_put_trigger(indio_dev->trig);
+		iio_trigger_dettach_poll_func(indio_dev->trig,
+					      indio_dev->pollfunc);
+	}
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+void ad7476_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
+
+int ad7476_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 08/14] staging: iio: adc: new driver for AD7745/6/7 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (5 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 22:36   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 09/14] staging: iio: adc: new driver for AD7816 devices Mike Frysinger
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Barry Song

From: Barry Song <barry.song@analog.com>

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad774x.c |  719 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 727 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad774x.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 4cb1542..780c2c5 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -86,3 +86,10 @@ config AD7476
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called ad7476.
+
+config AD774X
+	tristate "Analog Devices ad7745/6/7 capacitive sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices capacitive sensors.
+	  (ad7745, ad7746, ad7747) Provides direct access via sysfs.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 67e6341..ac2663a 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_AD7314) += ad7314.o
 obj-$(CONFIG_AD7414) += ad7414.o
 obj-$(CONFIG_AD7416) += ad7416.o
 obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD774X) += ad774x.o
diff --git a/drivers/staging/iio/adc/ad774x.c b/drivers/staging/iio/adc/ad774x.c
new file mode 100644
index 0000000..dd1df08
--- /dev/null
+++ b/drivers/staging/iio/adc/ad774x.c
@@ -0,0 +1,719 @@
+/*
+ * AD774X capacitive sensor driver supporting AD7745/6/7
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD774X registers definition
+ */
+
+#define AD774X_STATUS              0
+#define AD774X_STATUS_RDY          (1 << 2)
+#define AD774X_STATUS_RDYVT        (1 << 1)
+#define AD774X_STATUS_RDYCAP       (1 << 0)
+#define AD774X_CAP_DATA_HIGH       1
+#define AD774X_CAP_DATA_MID        2
+#define AD774X_CAP_DATA_LOW        3
+#define AD774X_VT_DATA_HIGH        4
+#define AD774X_VT_DATA_MID         5
+#define AD774X_VT_DATA_LOW         6
+#define AD774X_CAP_SETUP           7
+#define AD774X_VT_SETUP            8
+#define AD774X_EXEC_SETUP          9
+#define AD774X_CFG                 10
+#define AD774X_CAPDACA             11
+#define AD774X_CAPDACB             12
+#define AD774X_CAP_OFFH            13
+#define AD774X_CAP_OFFL            14
+#define AD774X_CAP_GAINH           15
+#define AD774X_CAP_GAINL           16
+#define AD774X_VOLT_GAINH          17
+#define AD774X_VOLT_GAINL          18
+
+#define AD774X_MAX_CONV_MODE       6
+
+/*
+ * struct ad774x_chip_info - chip specifc information
+ */
+
+struct ad774x_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	bool inter;
+	s64 last_timestamp;
+	u16 cap_offs;                   /* Capacitive offset */
+	u16 cap_gain;                   /* Capacitive gain calibration */
+	u16 volt_gain;                  /* Voltage gain calibration */
+	u8  cap_setup;
+	u8  vt_setup;
+	u8  exec_setup;
+
+	char *conversion_mode;
+};
+
+struct ad774x_conversion_mode {
+	char *name;
+	u8 reg_cfg;
+};
+
+struct ad774x_conversion_mode ad774x_conv_mode_table[AD774X_MAX_CONV_MODE] = {
+	{ "idle", 0 },
+	{ "continuous-conversion", 1 },
+	{ "single-conversion", 2 },
+	{ "power-down", 3 },
+	{ "offset-calibration", 5 },
+	{ "gain-calibration", 6 },
+};
+
+/*
+ * ad774x register access by I2C
+ */
+
+static int ad774x_i2c_read(struct ad774x_chip_info *chip, u8 reg, u8 *data, int len)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C write error\n");
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int ad774x_i2c_write(struct ad774x_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	u8 tx[2] = {
+		reg,
+		data,
+	};
+
+	ret = i2c_master_send(client, tx, 2);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+/*
+ * sysfs nodes
+ */
+
+#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_SETUP(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(cap_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_VT_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(vt_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_EXEC_SETUP(_mode, _show, _store)              \
+	IIO_DEVICE_ATTR(exec_setup, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_VOLT_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(volt_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_OFFS(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(cap_offs, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_CAP_GAIN(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(cap_gain, _mode, _show, _store, 0)
+#define IIO_DEV_ATTR_DACA_VALUE(_show)		\
+	IIO_DEVICE_ATTR(daca_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_DACB_VALUE(_show)		\
+	IIO_DEVICE_ATTR(dacb_value, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_CAP_DATA(_show)		\
+	IIO_DEVICE_ATTR(cap_data, S_IRUGO, _show, NULL, 0)
+#define IIO_DEV_ATTR_VT_DATA(_show)		\
+	IIO_DEVICE_ATTR(vt_data, S_IRUGO, _show, NULL, 0)
+
+static ssize_t ad774x_show_conversion_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int i;
+	int len = 0;
+
+	for (i = 0; i < AD774X_MAX_CONV_MODE; i++)
+		len += sprintf(buf + len, "%s\n", ad774x_conv_mode_table[i].name);
+
+	return len;
+}
+
+static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad774x_show_conversion_modes);
+
+static ssize_t ad774x_show_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%s\n", chip->conversion_mode);
+}
+
+static ssize_t ad774x_store_conversion_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	u8 cfg;
+	int i;
+
+	ad774x_i2c_read(chip, AD774X_CFG, &cfg, 1);
+
+	for (i = 0; i < AD774X_MAX_CONV_MODE; i++) {
+		if (strncmp(buf, ad774x_conv_mode_table[i].name,
+				strlen(ad774x_conv_mode_table[i].name) - 1) == 0) {
+			chip->conversion_mode = ad774x_conv_mode_table[i].name;
+			cfg |= 0x18 | ad774x_conv_mode_table[i].reg_cfg;
+			ad774x_i2c_write(chip, AD774X_CFG, cfg);
+			return len;
+		}
+	}
+
+	dev_err(dev, "not supported conversion mode\n");
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
+		ad774x_show_conversion_mode,
+		ad774x_store_conversion_mode);
+
+static ssize_t ad774x_show_daca_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	u8 data;
+
+	ad774x_i2c_read(chip, AD774X_CAPDACA, &data, 1);
+
+	return sprintf(buf, "%02x\n", data);
+}
+
+static IIO_DEV_ATTR_DACA_VALUE(ad774x_show_daca_value);
+
+static ssize_t ad774x_show_dacb_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	u8 data;
+
+	ad774x_i2c_read(chip, AD774X_CAPDACB, &data, 1);
+
+	return sprintf(buf, "%02x\n", data);
+}
+
+static IIO_DEV_ATTR_DACB_VALUE(ad774x_show_dacb_value);
+
+static ssize_t ad774x_show_cap_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->cap_setup);
+}
+
+static ssize_t ad774x_store_cap_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad774x_i2c_write(chip, AD774X_CAP_SETUP, data);
+		chip->cap_setup = data;
+		return len;
+	}
+
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CAP_SETUP(S_IRUGO | S_IWUSR,
+		ad774x_show_cap_setup,
+		ad774x_store_cap_setup);
+
+static ssize_t ad774x_show_vt_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->vt_setup);
+}
+
+static ssize_t ad774x_store_vt_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad774x_i2c_write(chip, AD774X_VT_SETUP, data);
+		chip->vt_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_VT_SETUP(S_IRUGO | S_IWUSR,
+		ad774x_show_vt_setup,
+		ad774x_store_vt_setup);
+
+static ssize_t ad774x_show_exec_setup(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "0x%02x\n", chip->exec_setup);
+}
+
+static ssize_t ad774x_store_exec_setup(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x100)) {
+		ad774x_i2c_write(chip, AD774X_EXEC_SETUP, data);
+		chip->exec_setup = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_EXEC_SETUP(S_IRUGO | S_IWUSR,
+		ad774x_show_exec_setup,
+		ad774x_store_exec_setup);
+
+static ssize_t ad774x_show_volt_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->volt_gain);
+}
+
+static ssize_t ad774x_store_volt_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad774x_i2c_write(chip, AD774X_VOLT_GAINH, data >> 8);
+		ad774x_i2c_write(chip, AD774X_VOLT_GAINL, data);
+		chip->volt_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_VOLT_GAIN(S_IRUGO | S_IWUSR,
+		ad774x_show_volt_gain,
+		ad774x_store_volt_gain);
+
+static ssize_t ad774x_show_cap_data(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	char tmp[3];
+
+	ad774x_i2c_read(chip, AD774X_CAP_DATA_HIGH, tmp, 3);
+	data = ((int)tmp[0] << 16) | ((int)tmp[1] << 8) | (int)tmp[2];
+
+	return sprintf(buf, "%ld\n", data);
+}
+
+static IIO_DEV_ATTR_CAP_DATA(ad774x_show_cap_data);
+
+static ssize_t ad774x_show_vt_data(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	char tmp[3];
+
+	ad774x_i2c_read(chip, AD774X_VT_DATA_HIGH, tmp, 3);
+	data = ((int)tmp[0] << 16) | ((int)tmp[1] << 8) | (int)tmp[2];
+
+	return sprintf(buf, "%ld\n", data);
+}
+
+static IIO_DEV_ATTR_VT_DATA(ad774x_show_vt_data);
+
+static ssize_t ad774x_show_cap_offs(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->cap_offs);
+}
+
+static ssize_t ad774x_store_cap_offs(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad774x_i2c_write(chip, AD774X_CAP_OFFH, data >> 8);
+		ad774x_i2c_write(chip, AD774X_CAP_OFFL, data);
+		chip->cap_offs = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CAP_OFFS(S_IRUGO | S_IWUSR,
+		ad774x_show_cap_offs,
+		ad774x_store_cap_offs);
+
+static ssize_t ad774x_show_cap_gain(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->cap_gain);
+}
+
+static ssize_t ad774x_store_cap_gain(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+
+	if ((!ret) && (data < 0x10000)) {
+		ad774x_i2c_write(chip, AD774X_CAP_GAINH, data >> 8);
+		ad774x_i2c_write(chip, AD774X_CAP_GAINL, data);
+		chip->cap_gain = data;
+		return len;
+	}
+
+	return -EINVAL;
+}
+
+static IIO_DEV_ATTR_CAP_GAIN(S_IRUGO | S_IWUSR,
+		ad774x_show_cap_gain,
+		ad774x_store_cap_gain);
+
+static ssize_t ad774x_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad774x_show_name, NULL, 0);
+
+static struct attribute *ad774x_attributes[] = {
+	&iio_dev_attr_available_conversion_modes.dev_attr.attr,
+	&iio_dev_attr_conversion_mode.dev_attr.attr,
+	&iio_dev_attr_cap_setup.dev_attr.attr,
+	&iio_dev_attr_vt_setup.dev_attr.attr,
+	&iio_dev_attr_exec_setup.dev_attr.attr,
+	&iio_dev_attr_cap_offs.dev_attr.attr,
+	&iio_dev_attr_cap_gain.dev_attr.attr,
+	&iio_dev_attr_volt_gain.dev_attr.attr,
+	&iio_dev_attr_cap_data.dev_attr.attr,
+	&iio_dev_attr_daca_value.dev_attr.attr,
+	&iio_dev_attr_dacb_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad774x_attribute_group = {
+	.attrs = ad774x_attributes,
+};
+
+/*
+ * data ready events
+ */
+
+#define IIO_EVENT_CODE_CAP_RDY     (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_VT_RDY      (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+
+#define IIO_EVENT_ATTR_CAP_RDY_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(cap_rdy, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_ATTR_VT_RDY_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(vt_rdy, _evlist, _show, _store, _mask)
+
+static void ad774x_interrupt_handler_bh(struct work_struct *work_s)
+{
+	struct ad774x_chip_info *chip =
+		container_of(work_s, struct ad774x_chip_info, thresh_work);
+	u8 int_status;
+
+	enable_irq(chip->client->irq);
+
+	ad774x_i2c_read(chip, AD774X_STATUS, &int_status, 1);
+
+	if (int_status & AD774X_STATUS_RDYCAP)
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_CAP_RDY,
+				chip->last_timestamp);
+
+	if (int_status & AD774X_STATUS_RDYVT)
+		iio_push_event(chip->indio_dev, 0,
+				IIO_EVENT_CODE_VT_RDY,
+				chip->last_timestamp);
+}
+
+static int ad774x_interrupt_handler_th(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad774x_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(data_rdy, &ad774x_interrupt_handler_th);
+
+static ssize_t ad774x_query_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	/*
+	 * AD774X provides one /RDY pin, which can be used as interrupt
+	 * but the pin is not configurable
+	 */
+	return sprintf(buf, "1\n");
+}
+
+static ssize_t ad774x_set_out_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return len;
+}
+
+IIO_EVENT_ATTR_CAP_RDY_SH(iio_event_data_rdy, ad774x_query_out_mode, ad774x_set_out_mode, 0);
+IIO_EVENT_ATTR_VT_RDY_SH(iio_event_data_rdy, ad774x_query_out_mode, ad774x_set_out_mode, 0);
+
+static struct attribute *ad774x_event_attributes[] = {
+	&iio_event_attr_cap_rdy.dev_attr.attr,
+	&iio_event_attr_vt_rdy.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad774x_event_attribute_group = {
+	.attrs = ad774x_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad774x_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	int ret = 0, regdone = 0;
+	struct ad774x_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	/* Echipabilish that the iio_dev is a child of the i2c device */
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &ad774x_attribute_group;
+	chip->indio_dev->event_attrs = &ad774x_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)(chip);
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+	regdone = 1;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				"ad774x");
+		if (ret)
+			goto error_free_dev;
+
+		iio_add_event_to_list(iio_event_attr_cap_rdy.listel,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad774x_interrupt_handler_bh);
+	}
+
+	dev_err(&client->dev, "%s capacitive sensor registered, irq: %d\n", id->name, client->irq);
+
+	return 0;
+
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(chip->indio_dev);
+	else
+		iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+error_ret:
+	return ret;
+}
+
+static int __devexit ad774x_remove(struct i2c_client *client)
+{
+	struct ad774x_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad774x_id[] = {
+	{ "ad7745", 0 },
+	{ "ad7746", 0 },
+	{ "ad7747", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad774x_id);
+
+static struct i2c_driver ad774x_driver = {
+	.driver = {
+		.name = "ad774x",
+	},
+	.probe = ad774x_probe,
+	.remove = __devexit_p(ad774x_remove),
+	.id_table = ad774x_id,
+};
+
+static __init int ad774x_init(void)
+{
+	return i2c_add_driver(&ad774x_driver);
+}
+
+static __exit void ad774x_exit(void)
+{
+	i2c_del_driver(&ad774x_driver);
+}
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ad7745/6/7 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad774x_init);
+module_exit(ad774x_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 09/14] staging: iio: adc: new driver for AD7816 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (6 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 08/14] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-23 20:29 ` [PATCH 10/14] staging: iio: adc: new driver for ADT75 temperature sensors Mike Frysinger
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/ad7816.c |  538 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 546 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad7816.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 780c2c5..303485a 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -93,3 +93,10 @@ config AD774X
 	help
 	  Say yes here to build support for Analog Devices capacitive sensors.
 	  (ad7745, ad7746, ad7747) Provides direct access via sysfs.
+
+config AD7816
+	tristate "Analog Devices AD7816/7/8 temperature sensor and ADC driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices AD7816/7/8
+	  temperature sensors and ADC.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index ac2663a..cdd3929 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -18,3 +18,4 @@ obj-$(CONFIG_AD7414) += ad7414.o
 obj-$(CONFIG_AD7416) += ad7416.o
 obj-$(CONFIG_AD7476) += ad7476.o
 obj-$(CONFIG_AD774X) += ad774x.o
+obj-$(CONFIG_AD7816) += ad7816.o
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
new file mode 100644
index 0000000..b459311
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -0,0 +1,538 @@
+/*
+ * AD7816 digital temperature sensor driver supporting AD7816/7/8
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * AD7816 config masks
+ */
+#define AD7816_FULL			0x1
+#define AD7816_PD			0x2
+#define AD7816_CS_MASK			0x7
+#define AD7816_CS_MAX			0x4
+
+/*
+ * AD7816 temperature masks
+ */
+#define AD7816_VALUE_OFFSET		6
+#define AD7816_BOUND_VALUE_BASE		0x8
+#define AD7816_BOUND_VALUE_MIN		-95
+#define AD7816_BOUND_VALUE_MAX		152
+#define AD7816_TEMP_FLOAT_OFFSET	2
+#define AD7816_TEMP_FLOAT_MASK		0x3
+
+
+/*
+ * struct ad7816_chip_info - chip specifc information
+ */
+
+struct ad7816_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u16 rdwr_pin;
+	u16 convert_pin;
+	u16 busy_pin;
+	u8  oti_data[AD7816_CS_MAX+1];
+	u8  channel_id;	/* 0 always be temperature */
+	u8  mode;
+};
+
+/*
+ * ad7816 data access by SPI
+ */
+static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+
+	gpio_set_value(chip->rdwr_pin, 1);
+	gpio_set_value(chip->rdwr_pin, 0);
+	ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI channel setting error\n");
+		return ret;
+	}
+	gpio_set_value(chip->rdwr_pin, 1);
+
+
+	if (chip->mode == AD7816_PD) { /* operating mode 2 */
+		gpio_set_value(chip->convert_pin, 1);
+		gpio_set_value(chip->convert_pin, 0);
+	} else { /* operating mode 1 */
+		gpio_set_value(chip->convert_pin, 0);
+		gpio_set_value(chip->convert_pin, 1);
+	}
+
+	while (gpio_get_value(chip->busy_pin))
+		cpu_relax();
+
+	gpio_set_value(chip->rdwr_pin, 0);
+	gpio_set_value(chip->rdwr_pin, 1);
+	ret = spi_read(spi_dev, (u8 *)data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI data read error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu(*data);
+
+	return ret;
+}
+
+static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	int ret = 0;
+
+	gpio_set_value(chip->rdwr_pin, 1);
+	gpio_set_value(chip->rdwr_pin, 0);
+	ret = spi_write(spi_dev, &data, sizeof(data));
+	if (ret < 0)
+		dev_err(&spi_dev->dev, "SPI oti data write error\n");
+
+	return ret;
+}
+
+static ssize_t ad7816_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	if (chip->mode)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t ad7816_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	if (strcmp(buf, "full")) {
+		gpio_set_value(chip->rdwr_pin, 1);
+		chip->mode = AD7816_FULL;
+	} else {
+		gpio_set_value(chip->rdwr_pin, 0);
+		chip->mode = AD7816_PD;
+	}
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		ad7816_show_mode,
+		ad7816_store_mode,
+		0);
+
+static ssize_t ad7816_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7816_show_available_modes, NULL, 0);
+
+static ssize_t ad7816_show_channel(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", chip->channel_id);
+}
+
+static ssize_t ad7816_store_channel(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	if (data > AD7816_CS_MAX && data != AD7816_CS_MASK) {
+		dev_err(&chip->spi_dev->dev, "Invalid channel id %lu for %s.\n",
+			data, chip->name);
+		return -EINVAL;
+	} else if (strcmp(chip->name, "ad7818") == 0 && data > 1) {
+		dev_err(&chip->spi_dev->dev,
+			"Invalid channel id %lu for ad7818.\n", data);
+		return -EINVAL;
+	} else if (strcmp(chip->name, "ad7816") == 0 && data > 0) {
+		dev_err(&chip->spi_dev->dev,
+			"Invalid channel id %lu for ad7816.\n", data);
+		return -EINVAL;
+	}
+
+	chip->channel_id = data;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR,
+		ad7816_show_channel,
+		ad7816_store_channel,
+		0);
+
+
+static ssize_t ad7816_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	s8 value;
+	int ret;
+
+	ret = ad7816_spi_read(chip, &data);
+	if (ret)
+		return -EIO;
+
+	data >>= AD7816_VALUE_OFFSET;
+
+	if (chip->channel_id == 0) {
+		value = (s8)((data >> AD7816_TEMP_FLOAT_OFFSET) - 103);
+		data &= AD7816_TEMP_FLOAT_MASK;
+		if (value < 0)
+			data = (1 << AD7816_TEMP_FLOAT_OFFSET) - data;
+		return sprintf(buf, "%d.%.2d\n", value, data * 25);
+	} else
+		return sprintf(buf, "%u\n", data);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, ad7816_show_value, NULL, 0);
+
+static ssize_t ad7816_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad7816_show_name, NULL, 0);
+
+static struct attribute *ad7816_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_channel.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ad7816_attribute_group = {
+	.attrs = ad7816_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7816_OTI    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+
+static void ad7816_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad7816_chip_info *chip =
+		container_of(work_s, struct ad7816_chip_info, thresh_work);
+
+	enable_irq(chip->spi_dev->irq);
+
+	iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_AD7816_OTI,
+			chip->last_timestamp);
+}
+
+static int ad7816_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(ad7816, &ad7816_interrupt);
+
+static ssize_t ad7816_show_oti(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	int value;
+
+	if (chip->channel_id > AD7816_CS_MAX) {
+		dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
+		return -EINVAL;
+	} else if (chip->channel_id == 0) {
+		value = AD7816_BOUND_VALUE_MIN +
+			(chip->oti_data[chip->channel_id] -
+			AD7816_BOUND_VALUE_BASE);
+		return sprintf(buf, "%d\n", value);
+	} else
+		return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]);
+}
+
+static inline ssize_t ad7816_set_oti(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad7816_chip_info *chip = dev_info->dev_data;
+	long value;
+	u8 data;
+	int ret;
+
+	ret = strict_strtol(buf, 10, &value);
+
+	if (chip->channel_id > AD7816_CS_MAX) {
+		dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
+		return -EINVAL;
+	} else if (chip->channel_id == 0) {
+		if (ret || value < AD7816_BOUND_VALUE_MIN ||
+			value > AD7816_BOUND_VALUE_MAX)
+			return -EINVAL;
+
+		data = (u8)(value - AD7816_BOUND_VALUE_MIN +
+			AD7816_BOUND_VALUE_BASE);
+	} else {
+		if (ret || value < AD7816_BOUND_VALUE_BASE || value > 255)
+			return -EINVAL;
+
+		data = (u8)value;
+	}
+
+	ret = ad7816_spi_write(chip, data);
+	if (ret)
+		return -EIO;
+
+	chip->oti_data[chip->channel_id] = data;
+
+	return len;
+}
+
+IIO_EVENT_ATTR_SH(oti, iio_event_ad7816,
+		ad7816_show_oti, ad7816_set_oti, 0);
+
+static struct attribute *ad7816_event_attributes[] = {
+	&iio_event_attr_oti.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7816_event_attribute_group = {
+	.attrs = ad7816_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit ad7816_probe(struct spi_device *spi_dev)
+{
+	struct ad7816_chip_info *chip;
+	unsigned short *pins = spi_dev->dev.platform_data;
+	int ret = 0;
+	int i;
+
+	if (!pins) {
+		dev_err(&spi_dev->dev, "No necessary GPIO platform data.\n");
+		return -EINVAL;
+	}
+
+	chip = kzalloc(sizeof(struct ad7816_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+	for (i = 0; i <= AD7816_CS_MAX; i++)
+		chip->oti_data[i] = 203;
+	chip->rdwr_pin = pins[0];
+	chip->convert_pin = pins[1];
+	chip->busy_pin = pins[2];
+
+	ret = gpio_request(chip->rdwr_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request rdwr gpio PIN %d.\n",
+			chip->rdwr_pin);
+		goto error_free_chip;
+	}
+	gpio_direction_input(chip->rdwr_pin);
+	ret = gpio_request(chip->convert_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request convert gpio PIN %d.\n",
+			chip->convert_pin);
+		goto error_free_gpio_rdwr;
+	}
+	gpio_direction_input(chip->convert_pin);
+	ret = gpio_request(chip->busy_pin, chip->name);
+	if (ret) {
+		dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
+			chip->busy_pin);
+		goto error_free_gpio_convert;
+	}
+	gpio_direction_input(chip->busy_pin);
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_gpio;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &ad7816_attribute_group;
+	chip->indio_dev->event_attrs = &ad7816_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (spi_dev->irq) {
+		/* Only low trigger is supported in ad7816/7/8 */
+		spi_dev->irq_flags &= ~IRQF_TRIGGER_MASK;
+		spi_dev->irq_flags |= IRQF_TRIGGER_LOW;
+
+		ret = iio_register_interrupt_line(spi_dev->irq,
+				chip->indio_dev,
+				0,
+				spi_dev->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_ad7816.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad7816,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, ad7816_interrupt_bh);
+	}
+
+	dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
+			 chip->name);
+
+	return 0;
+
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_gpio:
+	gpio_free(chip->busy_pin);
+error_free_gpio_convert:
+	gpio_free(chip->convert_pin);
+error_free_gpio_rdwr:
+	gpio_free(chip->rdwr_pin);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit ad7816_remove(struct spi_device *spi_dev)
+{
+	struct ad7816_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	if (spi_dev->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	gpio_free(chip->busy_pin);
+	gpio_free(chip->convert_pin);
+	gpio_free(chip->rdwr_pin);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id ad7816_id[] = {
+	{ "ad7816", 0 },
+	{ "ad7817", 0 },
+	{ "ad7818", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, ad7816_id);
+
+static struct spi_driver ad7816_driver = {
+	.driver = {
+		.name = "ad7816",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = ad7816_probe,
+	.remove = __devexit_p(ad7816_remove),
+	.id_table = ad7816_id,
+};
+
+static __init int ad7816_init(void)
+{
+	return spi_register_driver(&ad7816_driver);
+}
+
+static __exit void ad7816_exit(void)
+{
+	spi_unregister_driver(&ad7816_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7816/7/8 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(ad7816_init);
+module_exit(ad7816_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 10/14] staging: iio: adc: new driver for ADT75 temperature sensors
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (7 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 09/14] staging: iio: adc: new driver for AD7816 devices Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-23 20:29 ` [PATCH 11/14] staging: iio: adc: new driver for ADT7310 " Mike Frysinger
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig  |    7 +
 drivers/staging/iio/adc/Makefile |    1 +
 drivers/staging/iio/adc/adt75.c  |  734 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 742 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt75.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 303485a..5c7f042 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -100,3 +100,10 @@ config AD7816
 	help
 	  Say yes here to build support for Analog Devices AD7816/7/8
 	  temperature sensors and ADC.
+
+config ADT75
+	tristate "Analog Devices ADT75 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices ADT75
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index cdd3929..e88dfc4 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_AD7416) += ad7416.o
 obj-$(CONFIG_AD7476) += ad7476.o
 obj-$(CONFIG_AD774X) += ad774x.o
 obj-$(CONFIG_AD7816) += ad7816.o
+obj-$(CONFIG_ADT75) += adt75.o
diff --git a/drivers/staging/iio/adc/adt75.c b/drivers/staging/iio/adc/adt75.c
new file mode 100644
index 0000000..d685fa0
--- /dev/null
+++ b/drivers/staging/iio/adc/adt75.c
@@ -0,0 +1,734 @@
+/*
+ * ADT75 digital temperature sensor driver supporting ADT75
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT75 registers definition
+ */
+
+#define ADT75_TEMPERATURE		0
+#define ADT75_CONFIG			1
+#define ADT75_T_HYST			2
+#define ADT75_T_OS			3
+#define ADT75_ONESHOT			4
+
+/*
+ * ADT75 config
+ */
+#define ADT75_PD			0x1
+#define ADT75_OS_INT			0x2
+#define ADT75_OS_POLARITY		0x4
+#define ADT75_FAULT_QUEUE_MASK		0x18
+#define ADT75_FAULT_QUEUE_OFFSET	3
+#define ADT75_SMBUS_ALART		0x8
+
+/*
+ * ADT75 masks
+ */
+#define ADT75_VALUE_SIGN		0x800
+#define ADT75_VALUE_OFFSET		4
+#define ADT75_VALUE_FLOAT_OFFSET	4
+#define ADT75_VALUE_FLOAT_MASK		0xF
+
+
+/*
+ * struct adt75_chip_info - chip specifc information
+ */
+
+struct adt75_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  config;
+};
+
+/*
+ * adt75 register access by I2C
+ */
+
+static int adt75_i2c_read(struct adt75_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0, len;
+
+	ret = i2c_smbus_write_byte(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read register address error\n");
+		return ret;
+	}
+
+	if (reg == ADT75_CONFIG || reg == ADT75_ONESHOT)
+		len = 1;
+	else
+		len = 2;
+
+	ret = i2c_master_recv(client, data, len);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int adt75_i2c_write(struct adt75_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	if (reg == ADT75_CONFIG || reg == ADT75_ONESHOT)
+		ret = i2c_smbus_write_byte_data(client, reg, data);
+	else
+		ret = i2c_smbus_write_word_data(client, reg, data);
+
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static ssize_t adt75_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+
+	if (chip->config & ADT75_PD)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t adt75_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 config;
+
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_PD;
+	if (!strcmp(buf, "full"))
+		config |= ADT75_PD;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt75_show_mode,
+		adt75_store_mode,
+		0);
+
+static ssize_t adt75_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt75_show_available_modes, NULL, 0);
+
+static ssize_t adt75_show_oneshot(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT75_ONESHOT));
+}
+
+static ssize_t adt75_store_oneshot(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	unsigned long data = 0;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_ONESHOT;
+	if (data)
+		config |= ADT75_ONESHOT;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(oneshot, S_IRUGO | S_IWUSR,
+		adt75_show_oneshot,
+		adt75_store_oneshot,
+		0);
+
+static ssize_t adt75_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->config & ADT75_PD) {
+		dev_err(dev, "Can't read value in power-down mode.\n");
+		return -EIO;
+	}
+
+	if (chip->config & ADT75_ONESHOT) {
+		/* write to active converter */
+		ret = i2c_smbus_write_byte(chip->client, ADT75_ONESHOT);
+		if (ret)
+			return -EIO;
+	}
+
+	ret = adt75_i2c_read(chip, ADT75_TEMPERATURE, (u8 *)&data);
+	if (ret)
+		return -EIO;
+
+	data = swab16(data) >> ADT75_VALUE_OFFSET;
+	if (data & ADT75_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT75_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.4d\n", sign,
+		(data >> ADT75_VALUE_FLOAT_OFFSET),
+		(data & ADT75_VALUE_FLOAT_MASK) * 625);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt75_show_value, NULL, 0);
+
+static ssize_t adt75_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt75_show_name, NULL, 0);
+
+static struct attribute *adt75_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_oneshot.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt75_attribute_group = {
+	.attrs = adt75_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT75_OTI    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+
+static void adt75_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt75_chip_info *chip =
+		container_of(work_s, struct adt75_chip_info, thresh_work);
+
+	enable_irq(chip->client->irq);
+
+	iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT75_OTI,
+			chip->last_timestamp);
+}
+
+static int adt75_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt75_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt75, &adt75_interrupt);
+
+static ssize_t adt75_show_oti_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT75_OS_INT)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt75_set_oti_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 config;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_OS_INT;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT75_OS_INT;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt75_show_available_oti_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt75_show_smbus_alart(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT75_SMBUS_ALART));
+}
+
+static ssize_t adt75_set_smbus_alart(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	unsigned long data = 0;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_SMBUS_ALART;
+	if (data)
+		config |= ADT75_SMBUS_ALART;
+
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt75_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", (chip->config & ADT75_FAULT_QUEUE_MASK) >>
+				ADT75_FAULT_QUEUE_OFFSET);
+}
+
+static ssize_t adt75_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT75_FAULT_QUEUE_MASK;
+	config |= (data << ADT75_FAULT_QUEUE_OFFSET);
+	ret = adt75_i2c_write(chip, ADT75_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+static inline ssize_t adt75_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = adt75_i2c_read(chip, bound_reg, (u8 *)&data);
+	if (ret)
+		return -EIO;
+
+	data = swab16(data) >> ADT75_VALUE_OFFSET;
+	if (data & ADT75_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT75_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.4d\n", sign,
+		(data >> ADT75_VALUE_FLOAT_OFFSET),
+		(data & ADT75_VALUE_FLOAT_MASK) * 625);
+}
+
+static inline ssize_t adt75_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt75_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+		if (len > ADT75_VALUE_FLOAT_OFFSET)
+			len = ADT75_VALUE_FLOAT_OFFSET;
+		pos[len] = 0;
+		ret = strict_strtol(pos, 10, &tmp2);
+
+		if (!ret)
+			tmp2 = (tmp2 / 625) * 625;
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+	data = (data << ADT75_VALUE_FLOAT_OFFSET) | (tmp2 & ADT75_VALUE_FLOAT_MASK);
+	if (tmp1 < 0)
+		/* convert positive value to supplyment */
+		data = (ADT75_VALUE_SIGN << 1) - data;
+	data <<= ADT75_VALUE_OFFSET;
+	data = swab16(data);
+
+	ret = adt75_i2c_write(chip, bound_reg, (u8)data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t adt75_show_t_os(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt75_show_t_bound(dev, attr,
+			ADT75_T_OS, buf);
+}
+
+static inline ssize_t adt75_set_t_os(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt75_set_t_bound(dev, attr,
+			ADT75_T_OS, buf, len);
+}
+
+static ssize_t adt75_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt75_show_t_bound(dev, attr,
+			ADT75_T_HYST, buf);
+}
+
+static inline ssize_t adt75_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt75_set_t_bound(dev, attr,
+			ADT75_T_HYST, buf, len);
+}
+
+IIO_EVENT_ATTR_SH(oti_mode, iio_event_adt75,
+		adt75_show_oti_mode, adt75_set_oti_mode, 0);
+IIO_EVENT_ATTR_SH(available_oti_modes, iio_event_adt75,
+		adt75_show_available_oti_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(smbus_alart, iio_event_adt75,
+		adt75_show_smbus_alart, adt75_set_smbus_alart, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt75,
+		adt75_show_fault_queue, adt75_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_os, iio_event_adt75,
+		adt75_show_t_os, adt75_set_t_os, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt75,
+		adt75_show_t_hyst, adt75_set_t_hyst, 0);
+
+static struct attribute *adt75_event_attributes[] = {
+	&iio_event_attr_oti_mode.dev_attr.attr,
+	&iio_event_attr_available_oti_modes.dev_attr.attr,
+	&iio_event_attr_smbus_alart.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_os.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt75_event_attribute_group = {
+	.attrs = adt75_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt75_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct adt75_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct adt75_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &adt75_attribute_group;
+	chip->indio_dev->event_attrs = &adt75_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq > 0) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				client->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt75.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_adt75,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, adt75_interrupt_bh);
+
+		ret = adt75_i2c_read(chip, ADT75_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+
+		if (client->irq_flags & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT75_OS_POLARITY;
+		else
+			chip->config &= ~ADT75_OS_POLARITY;
+
+		ret = adt75_i2c_write(chip, ADT75_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt75_remove(struct i2c_client *client)
+{
+	struct adt75_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id adt75_id[] = {
+	{ "adt75", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, adt75_id);
+
+static struct i2c_driver adt75_driver = {
+	.driver = {
+		.name = "adt75",
+	},
+	.probe = adt75_probe,
+	.remove = __devexit_p(adt75_remove),
+	.id_table = adt75_id,
+};
+
+static __init int adt75_init(void)
+{
+	return i2c_add_driver(&adt75_driver);
+}
+
+static __exit void adt75_exit(void)
+{
+	i2c_del_driver(&adt75_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT75 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt75_init);
+module_exit(adt75_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 11/14] staging: iio: adc: new driver for ADT7310 temperature sensors
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (8 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 10/14] staging: iio: adc: new driver for ADT75 temperature sensors Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-23 20:29 ` [PATCH 12/14] staging: iio: adc: new driver for ADT7408 " Mike Frysinger
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig   |    7 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/adt7310.c |  951 +++++++++++++++++++++++++++++++++++++
 3 files changed, 959 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt7310.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 5c7f042..5d13918 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -107,3 +107,10 @@ config ADT75
 	help
 	  Say yes here to build support for Analog Devices ADT75
 	  temperature sensors.
+
+config ADT7408
+	tristate "Analog Devices ADT7408 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices ADT7408
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index e88dfc4..6c11363 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_AD7476) += ad7476.o
 obj-$(CONFIG_AD774X) += ad774x.o
 obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_ADT75) += adt75.o
+obj-$(CONFIG_ADT7310) += adt7310.o
diff --git a/drivers/staging/iio/adc/adt7310.c b/drivers/staging/iio/adc/adt7310.c
new file mode 100644
index 0000000..cb53506
--- /dev/null
+++ b/drivers/staging/iio/adc/adt7310.c
@@ -0,0 +1,951 @@
+/*
+ * ADT7310 digital temperature sensor driver supporting ADT7310
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT7310 registers definition
+ */
+
+#define ADT7310_STATUS			0
+#define ADT7310_CONFIG			1
+#define ADT7310_TEMPERATURE		2
+#define ADT7310_ID			3
+#define ADT7310_T_CRIT			4
+#define ADT7310_T_HYST			5
+#define ADT7310_T_ALARM_HIGH		6
+#define ADT7310_T_ALARM_LOW		7
+
+/*
+ * ADT7310 status
+ */
+#define ADT7310_STAT_T_LOW		0x10
+#define ADT7310_STAT_T_HIGH		0x20
+#define ADT7310_STAT_T_CRIT		0x40
+#define ADT7310_STAT_NOT_RDY		0x80
+
+/*
+ * ADT7310 config
+ */
+#define ADT7310_FAULT_QUEUE_MASK	0x3
+#define ADT7310_CT_POLARITY		0x4
+#define ADT7310_INT_POLARITY		0x8
+#define ADT7310_EVENT_MODE		0x10
+#define ADT7310_MODE_MASK		0x60
+#define ADT7310_ONESHOT			0x20
+#define ADT7310_SPS			0x40
+#define ADT7310_PD			0x60
+#define ADT7310_RESOLUTION		0x80
+
+/*
+ * ADT7310 masks
+ */
+#define ADT7310_T16_VALUE_SIGN			0x8000
+#define ADT7310_T16_VALUE_FLOAT_OFFSET		7
+#define ADT7310_T16_VALUE_FLOAT_MASK		0x7F
+#define ADT7310_T13_VALUE_SIGN			0x1000
+#define ADT7310_T13_VALUE_OFFSET		3
+#define ADT7310_T13_VALUE_FLOAT_OFFSET		4
+#define ADT7310_T13_VALUE_FLOAT_MASK		0xF
+#define ADT7310_T_HYST_MASK			0xF
+#define ADT7310_DEVICE_ID_MASK			0x7
+#define ADT7310_MANUFACTORY_ID_MASK		0xF8
+#define ADT7310_MANUFACTORY_ID_OFFSET		3
+
+
+#define ADT7310_CMD_REG_MASK			0x28
+#define ADT7310_CMD_REG_OFFSET			3
+#define ADT7310_CMD_READ			0x40
+#define ADT7310_CMD_CON_READ			0x4
+
+#define ADT7310_IRQS				2
+
+/*
+ * struct adt7310_chip_info - chip specifc information
+ */
+
+struct adt7310_chip_info {
+	const char *name;
+	struct spi_device *spi_dev;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  config;
+};
+
+/*
+ * adt7310 register access by SPI
+ */
+
+static int adt7310_spi_read_word(struct adt7310_chip_info *chip, u8 reg, u16 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 command = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	int ret = 0;
+
+	command |= ADT7310_CMD_READ;
+	ret = spi_write(spi_dev, &command, sizeof(command));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi_dev, (u8 *)data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read word error\n");
+		return ret;
+	}
+
+	*data = be16_to_cpu(*data);
+
+	return 0;
+}
+
+static int adt7310_spi_write_word(struct adt7310_chip_info *chip, u8 reg, u16 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 buf[3];
+	int ret = 0;
+
+	buf[0] = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	buf[1] = (u8)(data >> 8);
+	buf[2] = (u8)(data & 0xFF);
+
+	ret = spi_write(spi_dev, buf, 3);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write word error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int adt7310_spi_read_byte(struct adt7310_chip_info *chip, u8 reg, u8 *data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 command = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	int ret = 0;
+
+	command |= ADT7310_CMD_READ;
+	ret = spi_write(spi_dev, &command, sizeof(command));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write command error\n");
+		return ret;
+	}
+
+	ret = spi_read(spi_dev, data, sizeof(*data));
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI read byte error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int adt7310_spi_write_byte(struct adt7310_chip_info *chip, u8 reg, u8 data)
+{
+	struct spi_device *spi_dev = chip->spi_dev;
+	u8 buf[2];
+	int ret = 0;
+
+	buf[0] = (reg << ADT7310_CMD_REG_OFFSET) & ADT7310_CMD_REG_MASK;
+	buf[1] = data;
+
+	ret = spi_write(spi_dev, buf, 2);
+	if (ret < 0) {
+		dev_err(&spi_dev->dev, "SPI write byte error\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static ssize_t adt7310_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u8 config;
+
+	config = chip->config & ADT7310_MODE_MASK;
+
+	switch (config) {
+	case ADT7310_PD:
+		return sprintf(buf, "power-down\n");
+	case ADT7310_ONESHOT:
+		return sprintf(buf, "one-shot\n");
+	case ADT7310_SPS:
+		return sprintf(buf, "sps\n");
+	default:
+		return sprintf(buf, "full\n");
+	}
+}
+
+static ssize_t adt7310_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7310_MODE_MASK);
+	if (strcmp(buf, "power-down"))
+		config |= ADT7310_PD;
+	else if (strcmp(buf, "one-shot"))
+		config |= ADT7310_ONESHOT;
+	else if (strcmp(buf, "sps"))
+		config |= ADT7310_SPS;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt7310_show_mode,
+		adt7310_store_mode,
+		0);
+
+static ssize_t adt7310_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7310_show_available_modes, NULL, 0);
+
+static ssize_t adt7310_show_resolution(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+	int bits;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7310_RESOLUTION)
+		bits = 16;
+	else
+		bits = 13;
+
+	return sprintf(buf, "%d bits\n", bits);
+}
+
+static ssize_t adt7310_store_resolution(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7310_RESOLUTION);
+	if (data)
+		config |= ADT7310_RESOLUTION;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
+		adt7310_show_resolution,
+		adt7310_store_resolution,
+		0);
+
+static ssize_t adt7310_show_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u8 id;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
+			id & ADT7310_DEVICE_ID_MASK,
+			(id & ADT7310_MANUFACTORY_ID_MASK) >> ADT7310_MANUFACTORY_ID_OFFSET);
+}
+
+static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
+		adt7310_show_id,
+		NULL,
+		0);
+
+static ssize_t adt7310_convert_temperature(struct adt7310_chip_info *chip,
+		u16 data, char *buf)
+{
+	char sign = ' ';
+
+	if (chip->config & ADT7310_RESOLUTION) {
+		if (data & ADT7310_T16_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data = (u16)((ADT7310_T16_VALUE_SIGN << 1) - (u32)data);
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.7d\n", sign,
+				(data >> ADT7310_T16_VALUE_FLOAT_OFFSET),
+				(data & ADT7310_T16_VALUE_FLOAT_MASK) * 78125);
+	} else {
+		if (data & ADT7310_T13_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data >>= ADT7310_T13_VALUE_OFFSET;
+			data = (ADT7310_T13_VALUE_SIGN << 1) - data;
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.4d\n", sign,
+				(data >> ADT7310_T13_VALUE_FLOAT_OFFSET),
+				(data & ADT7310_T13_VALUE_FLOAT_MASK) * 625);
+	}
+}
+
+static ssize_t adt7310_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u8 status;
+	u16 data;
+	int ret, i = 0;
+
+	do {
+		ret = adt7310_spi_read_byte(chip, ADT7310_STATUS, &status);
+		if (ret)
+			return -EIO;
+		i++;
+		if (i == 10000)
+			return -EIO;
+	} while (status & ADT7310_STAT_NOT_RDY);
+
+	ret = adt7310_spi_read_word(chip, ADT7310_TEMPERATURE, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7310_convert_temperature(chip, data, buf);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt7310_show_value, NULL, 0);
+
+static ssize_t adt7310_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt7310_show_name, NULL, 0);
+
+static struct attribute *adt7310_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_resolution.dev_attr.attr,
+	&iio_dev_attr_id.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7310_attribute_group = {
+	.attrs = adt7310_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT7310_ABOVE_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_ADT7310_BELLOW_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+#define IIO_EVENT_CODE_ADT7310_ABOVE_CRIT    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
+
+static void adt7310_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt7310_chip_info *chip =
+		container_of(work_s, struct adt7310_chip_info, thresh_work);
+	u8 status;
+
+	if (adt7310_spi_read_byte(chip, ADT7310_STATUS, &status))
+		return;
+
+	if (status & ADT7310_STAT_T_HIGH)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7310_ABOVE_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7310_STAT_T_LOW)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7310_BELLOW_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7310_STAT_T_CRIT)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7310_ABOVE_CRIT,
+			chip->last_timestamp);
+}
+
+static int adt7310_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt7310, &adt7310_interrupt);
+IIO_EVENT_SH(adt7310_ct, &adt7310_interrupt);
+
+static ssize_t adt7310_show_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7310_EVENT_MODE)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt7310_set_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config &= ~ADT7310_EVENT_MODE;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT7310_EVENT_MODE;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static ssize_t adt7310_show_available_event_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt7310_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", chip->config & ADT7310_FAULT_QUEUE_MASK);
+}
+
+static ssize_t adt7310_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7310_FAULT_QUEUE_MASK;
+	config |= data;
+	ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return len;
+}
+
+static inline ssize_t adt7310_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	int ret;
+
+	ret = adt7310_spi_read_word(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7310_convert_temperature(chip, data, buf);
+}
+
+static inline ssize_t adt7310_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+
+		if (chip->config & ADT7310_RESOLUTION) {
+			if (len > ADT7310_T16_VALUE_FLOAT_OFFSET)
+				len = ADT7310_T16_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 78125) * 78125;
+		} else {
+			if (len > ADT7310_T13_VALUE_FLOAT_OFFSET)
+				len = ADT7310_T13_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 625) * 625;
+		}
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+
+	if (chip->config & ADT7310_RESOLUTION) {
+		data = (data << ADT7310_T16_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7310_T16_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (u16)((ADT7310_T16_VALUE_SIGN << 1) - (u32)data);
+	} else {
+		data = (data << ADT7310_T13_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7310_T13_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (ADT7310_T13_VALUE_SIGN << 1) - data;
+		data <<= ADT7310_T13_VALUE_OFFSET;
+	}
+
+	ret = adt7310_spi_write_word(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static ssize_t adt7310_show_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7310_show_t_bound(dev, attr,
+			ADT7310_T_ALARM_HIGH, buf);
+}
+
+static inline ssize_t adt7310_set_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7310_set_t_bound(dev, attr,
+			ADT7310_T_ALARM_HIGH, buf, len);
+}
+
+static ssize_t adt7310_show_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7310_show_t_bound(dev, attr,
+			ADT7310_T_ALARM_LOW, buf);
+}
+
+static inline ssize_t adt7310_set_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7310_set_t_bound(dev, attr,
+			ADT7310_T_ALARM_LOW, buf, len);
+}
+
+static ssize_t adt7310_show_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7310_show_t_bound(dev, attr,
+			ADT7310_T_CRIT, buf);
+}
+
+static inline ssize_t adt7310_set_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7310_set_t_bound(dev, attr,
+			ADT7310_T_CRIT, buf, len);
+}
+
+static ssize_t adt7310_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 t_hyst;
+
+	ret = adt7310_spi_read_byte(chip, ADT7310_T_HYST, &t_hyst);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", t_hyst & ADT7310_T_HYST_MASK);
+}
+
+static inline ssize_t adt7310_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7310_chip_info *chip = dev_info->dev_data;
+	int ret;
+	unsigned long data;
+	u8 t_hyst;
+
+	ret = strict_strtol(buf, 10, &data);
+
+	if (ret || data > ADT7310_T_HYST_MASK)
+		return -EINVAL;
+
+	t_hyst = (u8)data;
+
+	ret = adt7310_spi_write_byte(chip, ADT7310_T_HYST, t_hyst);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7310,
+		adt7310_show_event_mode, adt7310_set_event_mode, 0);
+IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7310,
+		adt7310_show_available_event_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt7310,
+		adt7310_show_fault_queue, adt7310_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7310,
+		adt7310_show_t_alarm_high, adt7310_set_t_alarm_high, 0);
+IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7310,
+		adt7310_show_t_alarm_low, adt7310_set_t_alarm_low, 0);
+IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7310_ct,
+		adt7310_show_t_crit, adt7310_set_t_crit, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7310,
+		adt7310_show_t_hyst, adt7310_set_t_hyst, 0);
+
+static struct attribute *adt7310_event_int_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_alarm_high.dev_attr.attr,
+	&iio_event_attr_t_alarm_low.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *adt7310_event_ct_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_crit.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7310_event_attribute_group[ADT7310_IRQS] = {
+	{
+		.attrs = adt7310_event_int_attributes,
+	},
+	{
+		.attrs = adt7310_event_ct_attributes,
+	}
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7310_probe(struct spi_device *spi_dev)
+{
+	struct adt7310_chip_info *chip;
+	int ret = 0;
+	unsigned long *adt7310_platform_data = spi_dev->dev.platform_data;
+
+	chip = kzalloc(sizeof(struct adt7310_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	dev_set_drvdata(&spi_dev->dev, chip);
+
+	chip->spi_dev = spi_dev;
+	chip->name = spi_dev->modalias;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &spi_dev->dev;
+	chip->indio_dev->attrs = &adt7310_attribute_group;
+	chip->indio_dev->event_attrs = adt7310_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = ADT7310_IRQS;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	/* CT critcal temperature event. line 0 */
+	if (spi_dev->irq) {
+		if (adt7310_platform_data[2])
+			spi_dev->irq_flags = adt7310_platform_data[2];
+		ret = iio_register_interrupt_line(spi_dev->irq,
+				chip->indio_dev,
+				0,
+				spi_dev->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7310.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7310,
+				&chip->indio_dev->interrupts[0]->ev_list);
+	}
+
+	/* INT bound temperature alarm event. line 1 */
+	if (adt7310_platform_data[0]) {
+		ret = iio_register_interrupt_line(adt7310_platform_data[0],
+				chip->indio_dev,
+				1,
+				adt7310_platform_data[1],
+				chip->name);
+		if (ret)
+			goto error_unreg_ct_irq;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7310.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7310_ct,
+				&chip->indio_dev->interrupts[1]->ev_list);
+	}
+
+	if (spi_dev->irq && adt7310_platform_data[0]) {
+		INIT_WORK(&chip->thresh_work, adt7310_interrupt_bh);
+
+		ret = adt7310_spi_read_byte(chip, ADT7310_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+
+		if (spi_dev->irq_flags & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7310_CT_POLARITY;
+		else
+			chip->config &= ~ADT7310_CT_POLARITY;
+
+		if (adt7310_platform_data[1] & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7310_INT_POLARITY;
+		else
+			chip->config &= ~ADT7310_INT_POLARITY;
+
+		ret = adt7310_spi_write_byte(chip, ADT7310_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+	}
+
+	dev_info(&spi_dev->dev, "%s temperature sensor registered.\n",
+			chip->name);
+
+	return 0;
+
+error_unreg_int_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 1);
+error_unreg_ct_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt7310_remove(struct spi_device *spi_dev)
+{
+	struct adt7310_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
+	struct iio_dev *indio_dev = chip->indio_dev;
+	unsigned long *adt7310_platform_data = spi_dev->dev.platform_data;
+
+	dev_set_drvdata(&spi_dev->dev, NULL);
+	if (adt7310_platform_data[0])
+		iio_unregister_interrupt_line(indio_dev, 1);
+	if (spi_dev->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct spi_device_id adt7310_id[] = {
+	{ "adt7310", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, adt7310_id);
+
+static struct spi_driver adt7310_driver = {
+	.driver = {
+		.name = "adt7310",
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+	},
+	.probe = adt7310_probe,
+	.remove = __devexit_p(adt7310_remove),
+	.id_table = adt7310_id,
+};
+
+static __init int adt7310_init(void)
+{
+	return spi_register_driver(&adt7310_driver);
+}
+
+static __exit void adt7310_exit(void)
+{
+	spi_unregister_driver(&adt7310_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7310 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7310_init);
+module_exit(adt7310_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (9 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 11/14] staging: iio: adc: new driver for ADT7310 " Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 22:53   ` Jonathan Cameron
  2010-10-23 20:29 ` [PATCH 13/14] staging: iio: adc: new driver for ADT7410 " Mike Frysinger
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig   |    7 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/adt7408.c | 1006 +++++++++++++++++++++++++++++++++++++
 3 files changed, 1014 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt7408.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 5d13918..ea75700 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -114,3 +114,10 @@ config ADT7408
 	help
 	  Say yes here to build support for Analog Devices ADT7408
 	  temperature sensors.
+
+config ADT7410
+	tristate "Analog Devices ADT7410 temperature sensor driver"
+	depends on I2C
+	help
+	  Say yes here to build support for Analog Devices ADT7410
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 6c11363..dc2bdbe 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_AD774X) += ad774x.o
 obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_ADT75) += adt75.o
 obj-$(CONFIG_ADT7310) += adt7310.o
+obj-$(CONFIG_ADT7408) += adt7408.o
diff --git a/drivers/staging/iio/adc/adt7408.c b/drivers/staging/iio/adc/adt7408.c
new file mode 100644
index 0000000..25bd594
--- /dev/null
+++ b/drivers/staging/iio/adc/adt7408.c
@@ -0,0 +1,1006 @@
+/*
+ * ADT7408 digital temperature sensor driver supporting ADT7408
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT7408 registers definition
+ */
+
+#define ADT7408_CAPABILITY		0
+#define ADT7408_CONFIG			1
+#define ADT7408_T_ALARM_HIGH		2
+#define ADT7408_T_ALARM_LOW		3
+#define ADT7408_T_CRIT			4
+#define ADT7408_TEMPERATURE		5
+#define ADT7408_MANUFACTURER_ID		6
+#define ADT7408_DEVICE_ID		7
+
+/*
+ * ADT7408 capability
+ */
+#define ADT7408_CAP_ALARM_CRIT_TRIPS	0x1
+#define ADT7408_CAP_HIGH_PRECISION	0x2
+#define ADT7408_CAP_WIDER_RANGE		0x4
+#define ADT7408_CAP_T_RESOLUTION_MASK	0x18
+#define ADT7408_CAP_T_RESOLUTION_HIGH	0x18
+#define ADT7408_CAP_T_RESOLUTION_LOW	0x8
+
+/*
+ * ADT7408 config
+ */
+#define ADT7408_EVENT_MODE		0x1
+#define ADT7408_EVENT_POLARITY		0x2
+#define ADT7408_EVENT_CRIT_ONLY		0x4
+#define ADT7408_EVENT_ENABLE		0x8
+#define ADT7408_EVENT_STATUS		0x10
+#define ADT7408_EVENT_CLEAR		0x20
+#define ADT7408_EVENT_ALARM_LOCK	0x40
+#define ADT7408_EVENT_CRIT_LOCK		0x80
+#define ADT7408_PD			0x100
+#define ADT7408_HISTERESIS_MASK		0x600
+#define ADT7408_HISTERESIS_1_5		0x200
+#define ADT7408_HISTERESIS_3		0x400
+#define ADT7408_HISTERESIS_6		0x600
+
+/*
+ * ADT7408 masks
+ */
+#define ADT7408_BOUND_VALUE_SIGN		0x400
+#define ADT7408_BOUND_VALUE_OFFSET		2
+#define ADT7408_BOUND_VALUE_FLOAT_OFFSET	2
+#define ADT7408_BOUND_VALUE_FLOAT_MASK		0x3
+#define ADT7408_T_VALUE_SIGN			0x1000
+#define ADT7408_T_VALUE_FLOAT_OFFSET		4
+#define ADT7408_T_VALUE_FLOAT_MASK		0xF
+
+/*
+ * ADT7408 event source
+ */
+#define ADT7408_T_BELLOW_ALARM			0x2000
+#define ADT7408_T_ABOVE_ALARM			0x4000
+#define ADT7408_T_ABOVE_CRIT			0x8000
+
+
+/*
+ * struct adt7408_chip_info - chip specifc information
+ */
+
+struct adt7408_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u16 config;
+};
+
+/*
+ * adt7408 register access by I2C
+ */
+
+static int adt7408_i2c_read(struct adt7408_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->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 adt7408_i2c_write(struct adt7408_chip_info *chip, u8 reg, u16 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static int adt7408_is_event_locked(struct adt7408_chip_info *chip)
+{
+	return chip->config & (ADT7408_EVENT_ALARM_LOCK | ADT7408_EVENT_ALARM_LOCK);
+}
+
+static ssize_t adt7408_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+
+	if (chip->config & ADT7408_PD)
+		return sprintf(buf, "power-save\n");
+	else
+		return sprintf(buf, "full\n");
+}
+
+static ssize_t adt7408_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	if (adt7408_is_event_locked(chip)) {
+		dev_err(dev, "Warning: Events are locked.\n");
+		return -EIO;
+	}
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7408_PD);
+	if (!strcmp(buf, "full"))
+		config |= ADT7408_PD;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt7408_show_mode,
+		adt7408_store_mode,
+		0);
+
+static ssize_t adt7408_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7408_show_available_modes, NULL, 0);
+
+static ssize_t adt7408_show_capability(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 capability;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CAPABILITY, &capability);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "0x%x\n", capability);
+}
+
+static IIO_DEVICE_ATTR(capability, S_IRUGO | S_IWUSR,
+		adt7408_show_capability,
+		NULL,
+		0);
+
+static ssize_t adt7408_show_manufactory_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 id;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "0x%x\n", id);
+}
+
+static IIO_DEVICE_ATTR(manufactory_id, S_IRUGO | S_IWUSR,
+		adt7408_show_manufactory_id,
+		NULL,
+		0);
+
+static ssize_t adt7408_show_device_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 id;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "0x%x\n", id);
+}
+
+static IIO_DEVICE_ATTR(device_id, S_IRUGO | S_IWUSR,
+		adt7408_show_device_id,
+		NULL,
+		0);
+
+static ssize_t adt7408_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	if (chip->config & ADT7408_PD) {
+		dev_err(dev, "Can't read value in power-down mode.\n");
+		return -EIO;
+	}
+
+	ret = adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data);
+	if (ret)
+		return -EIO;
+
+	if (data & ADT7408_T_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT7408_T_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.4d\n", sign,
+		(data >> ADT7408_T_VALUE_FLOAT_OFFSET),
+		(data & ADT7408_T_VALUE_FLOAT_MASK) * 625);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt7408_show_value, NULL, 0);
+
+static ssize_t adt7408_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt7408_show_name, NULL, 0);
+
+static struct attribute *adt7408_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_capability.dev_attr.attr,
+	&iio_dev_attr_device_id.dev_attr.attr,
+	&iio_dev_attr_manufactory_id.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7408_attribute_group = {
+	.attrs = adt7408_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT7408_ABOVE_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_ADT7408_BELLOW_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+#define IIO_EVENT_CODE_ADT7408_ABOVE_CRIT    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
+
+static void adt7408_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt7408_chip_info *chip =
+		container_of(work_s, struct adt7408_chip_info, thresh_work);
+	u16 config;
+	u16 data;
+
+	if (adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config))
+		return;
+
+	if (!(chip->config & ADT7408_EVENT_STATUS))
+		return;
+
+	config = chip->config & ~ADT7408_EVENT_CLEAR;
+	if (data)
+		config |= ADT7408_EVENT_CLEAR;
+
+	adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+
+	if (adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data))
+		goto exit;
+
+	if (data & ADT7408_T_ABOVE_ALARM)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7408_ABOVE_ALARM,
+			chip->last_timestamp);
+	if (data & ADT7408_T_BELLOW_ALARM)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7408_BELLOW_ALARM,
+			chip->last_timestamp);
+	if (data & ADT7408_T_ABOVE_CRIT)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7408_ABOVE_CRIT,
+			chip->last_timestamp);
+exit:
+	enable_irq(chip->client->irq);
+}
+
+static int adt7408_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt7408, &adt7408_interrupt);
+
+static ssize_t adt7408_show_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7408_EVENT_MODE)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt7408_set_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	if (adt7408_is_event_locked(chip)) {
+		dev_err(dev, "Warning: Events are locked.\n");
+		return -EIO;
+	}
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config &= ~ADT7408_EVENT_MODE;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT7408_EVENT_MODE;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt7408_show_available_event_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt7408_show_event_crit_only(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_CRIT_ONLY));
+}
+
+static ssize_t adt7408_set_event_crit_only(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	unsigned long data = 0;
+	u16 config;
+	int ret;
+
+	if (adt7408_is_event_locked(chip)) {
+		dev_err(dev, "Warning: Events are locked.\n");
+		return -EIO;
+	}
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config &= ~ADT7408_EVENT_CRIT_ONLY;
+	if (data)
+		config |= ADT7408_EVENT_CRIT_ONLY;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt7408_show_event_enable(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_ENABLE));
+}
+
+static ssize_t adt7408_set_event_enable(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	if (adt7408_is_event_locked(chip)) {
+		dev_err(dev, "Warning: Events are locked.\n");
+		return -EIO;
+	}
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7408_EVENT_ENABLE;
+	if (data)
+		config |= ADT7408_EVENT_ENABLE;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt7408_show_alarm_lock(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_ALARM_LOCK));
+}
+
+static ssize_t adt7408_set_alarm_lock(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7408_EVENT_ALARM_LOCK;
+	if (data)
+		config |= ADT7408_EVENT_ALARM_LOCK;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt7408_show_crit_lock(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_CRIT_LOCK));
+}
+
+static ssize_t adt7408_set_crit_lock(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7408_EVENT_CRIT_LOCK;
+	if (data)
+		config |= ADT7408_EVENT_CRIT_LOCK;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+
+static inline ssize_t adt7408_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	char sign = ' ';
+	int ret;
+
+	ret = adt7408_i2c_read(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	data >>= ADT7408_BOUND_VALUE_OFFSET;
+	if (data & ADT7408_BOUND_VALUE_SIGN) {
+		/* convert supplement to positive value */
+		data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
+		sign = '-';
+	}
+
+	return sprintf(buf, "%c%d.%.2d\n", sign,
+			data >> ADT7408_BOUND_VALUE_FLOAT_OFFSET,
+			(data & ADT7408_BOUND_VALUE_FLOAT_MASK) * 25);
+}
+
+static inline ssize_t adt7408_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+		if (len > ADT7408_BOUND_VALUE_FLOAT_OFFSET)
+			len = ADT7408_BOUND_VALUE_FLOAT_OFFSET;
+		pos[len] = 0;
+		ret = strict_strtol(pos, 10, &tmp2);
+
+		if (!ret)
+			tmp2 = (tmp2 / 25) * 25;
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+	data = (data << ADT7408_BOUND_VALUE_FLOAT_OFFSET) |
+		(tmp2 & ADT7408_BOUND_VALUE_FLOAT_MASK);
+	if (tmp1 < 0)
+		/* convert positive value to supplyment */
+		data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
+	data <<= ADT7408_BOUND_VALUE_OFFSET;
+
+	ret = adt7408_i2c_write(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t adt7408_show_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7408_show_t_bound(dev, attr,
+			ADT7408_T_ALARM_HIGH, buf);
+}
+
+static inline ssize_t adt7408_set_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7408_set_t_bound(dev, attr,
+			ADT7408_T_ALARM_HIGH, buf, len);
+}
+
+static ssize_t adt7408_show_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7408_show_t_bound(dev, attr,
+			ADT7408_T_ALARM_LOW, buf);
+}
+
+static inline ssize_t adt7408_set_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7408_set_t_bound(dev, attr,
+			ADT7408_T_ALARM_LOW, buf, len);
+}
+
+static ssize_t adt7408_show_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7408_show_t_bound(dev, attr,
+			ADT7408_T_CRIT, buf);
+}
+
+static inline ssize_t adt7408_set_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7408_set_t_bound(dev, attr,
+			ADT7408_T_CRIT, buf, len);
+}
+
+static ssize_t adt7408_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	/* retrive ALART status */
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	switch (chip->config & ADT7408_HISTERESIS_MASK) {
+	case ADT7408_HISTERESIS_1_5:
+		return sprintf(buf, "1.5\n");
+	case ADT7408_HISTERESIS_3:
+		return sprintf(buf, "3\n");
+	case ADT7408_HISTERESIS_6:
+		return sprintf(buf, "6\n");
+	default:
+		return sprintf(buf, "Disabled\n");
+	}
+}
+
+static inline ssize_t adt7408_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7408_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u16 config = 0;
+
+	if (strcmp(buf, "disble"))
+		config = ADT7408_HISTERESIS_MASK;
+	else if (strcmp(buf, "1.5"))
+		config = ADT7408_HISTERESIS_1_5;
+	else if (len > 1 && buf[0] == '3')
+		config = ADT7408_HISTERESIS_6;
+	else if (len > 1 && buf[0] == '6')
+		config = ADT7408_HISTERESIS_6;
+
+	if (!config)
+		return -EINVAL;
+
+	/* retrive ALART status */
+	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config |= chip->config & ~ADT7408_HISTERESIS_MASK;
+
+	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+	return ret;
+}
+
+static ssize_t adt7408_show_available_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "1.5\n3\n6\ndisable\n");
+}
+
+IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7408,
+		adt7408_show_event_mode, adt7408_set_event_mode, 0);
+IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7408,
+		adt7408_show_available_event_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(event_crit_only, iio_event_adt7408,
+		adt7408_show_event_crit_only, adt7408_set_event_crit_only, 0);
+IIO_EVENT_ATTR_SH(event_enable, iio_event_adt7408,
+		adt7408_show_event_enable, adt7408_set_event_enable, 0);
+IIO_EVENT_ATTR_SH(alarm_lock, iio_event_adt7408,
+		adt7408_show_alarm_lock, adt7408_set_alarm_lock, 0);
+IIO_EVENT_ATTR_SH(crit_lock, iio_event_adt7408,
+		adt7408_show_crit_lock, adt7408_set_crit_lock, 0);
+IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7408,
+		adt7408_show_t_alarm_high, adt7408_set_t_alarm_high, 0);
+IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7408,
+		adt7408_show_t_alarm_low, adt7408_set_t_alarm_low, 0);
+IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7408,
+		adt7408_show_t_crit, adt7408_set_t_crit, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7408,
+		adt7408_show_t_hyst, adt7408_set_t_hyst, 0);
+IIO_EVENT_ATTR_SH(available_t_hyst, iio_event_adt7408,
+		adt7408_show_available_t_hyst, NULL, 0);
+
+static struct attribute *adt7408_event_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_event_crit_only.dev_attr.attr,
+	&iio_event_attr_event_enable.dev_attr.attr,
+	&iio_event_attr_alarm_lock.dev_attr.attr,
+	&iio_event_attr_crit_lock.dev_attr.attr,
+	&iio_event_attr_t_alarm_high.dev_attr.attr,
+	&iio_event_attr_t_alarm_low.dev_attr.attr,
+	&iio_event_attr_t_crit.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	&iio_event_attr_available_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7408_event_attribute_group = {
+	.attrs = adt7408_event_attributes,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7408_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct adt7408_chip_info *chip;
+	int ret = 0;
+
+	chip = kzalloc(sizeof(struct adt7408_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &adt7408_attribute_group;
+	chip->indio_dev->event_attrs = &adt7408_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = 1;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	if (client->irq) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				client->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7408.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_adt7408,
+				&chip->indio_dev->interrupts[0]->ev_list);
+
+		INIT_WORK(&chip->thresh_work, adt7408_interrupt_bh);
+
+		ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+
+		if (client->irq_flags & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7408_EVENT_POLARITY;
+		else
+			chip->config &= ~ADT7408_EVENT_POLARITY;
+
+		ret = adt7408_i2c_write(chip, ADT7408_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+
+error_unreg_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt7408_remove(struct i2c_client *client)
+{
+	struct adt7408_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id adt7408_id[] = {
+	{ "adt7408", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, adt7408_id);
+
+static struct i2c_driver adt7408_driver = {
+	.driver = {
+		.name = "adt7408",
+	},
+	.probe = adt7408_probe,
+	.remove = __devexit_p(adt7408_remove),
+	.id_table = adt7408_id,
+};
+
+static __init int adt7408_init(void)
+{
+	return i2c_add_driver(&adt7408_driver);
+}
+
+static __exit void adt7408_exit(void)
+{
+	i2c_del_driver(&adt7408_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7408 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7408_init);
+module_exit(adt7408_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 13/14] staging: iio: adc: new driver for ADT7410 temperature sensors
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (10 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 12/14] staging: iio: adc: new driver for ADT7408 " Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-23 20:29 ` [PATCH 14/14] staging: iio: adc: new ad799x driver Mike Frysinger
  2010-10-24 21:09 ` [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Jonathan Cameron
  13 siblings, 0 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Sonic Zhang

From: Sonic Zhang <sonic.zhang@analog.com>

Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig   |    7 +
 drivers/staging/iio/adc/Makefile  |    1 +
 drivers/staging/iio/adc/adt7410.c |  917 +++++++++++++++++++++++++++++++++++++
 3 files changed, 925 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/adt7410.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index ea75700..ff479c4 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -121,3 +121,10 @@ config ADT7410
 	help
 	  Say yes here to build support for Analog Devices ADT7410
 	  temperature sensors.
+
+config ADT7310
+	tristate "Analog Devices ADT7310 temperature sensor driver"
+	depends on SPI
+	help
+	  Say yes here to build support for Analog Devices ADT7310
+	  temperature sensors.
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index dc2bdbe..0a2d7ea 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_AD7816) += ad7816.o
 obj-$(CONFIG_ADT75) += adt75.o
 obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7408) += adt7408.o
+obj-$(CONFIG_ADT7410) += adt7410.o
diff --git a/drivers/staging/iio/adc/adt7410.c b/drivers/staging/iio/adc/adt7410.c
new file mode 100644
index 0000000..f005931
--- /dev/null
+++ b/drivers/staging/iio/adc/adt7410.c
@@ -0,0 +1,917 @@
+/*
+ * ADT7410 digital temperature sensor driver supporting ADT7410
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.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/i2c.h>
+#include <linux/rtc.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+/*
+ * ADT7410 registers definition
+ */
+
+#define ADT7410_TEMPERATURE		0
+#define ADT7410_STATUS			2
+#define ADT7410_CONFIG			3
+#define ADT7410_T_ALARM_HIGH		4
+#define ADT7410_T_ALARM_LOW		6
+#define ADT7410_T_CRIT			8
+#define ADT7410_T_HYST			0xA
+#define ADT7410_ID			0xB
+#define ADT7410_RESET			0x2F
+
+/*
+ * ADT7410 status
+ */
+#define ADT7410_STAT_T_LOW		0x10
+#define ADT7410_STAT_T_HIGH		0x20
+#define ADT7410_STAT_T_CRIT		0x40
+#define ADT7410_STAT_NOT_RDY		0x80
+
+/*
+ * ADT7410 config
+ */
+#define ADT7410_FAULT_QUEUE_MASK	0x3
+#define ADT7410_CT_POLARITY		0x4
+#define ADT7410_INT_POLARITY		0x8
+#define ADT7410_EVENT_MODE		0x10
+#define ADT7410_MODE_MASK		0x60
+#define ADT7410_ONESHOT			0x20
+#define ADT7410_SPS			0x40
+#define ADT7410_PD			0x60
+#define ADT7410_RESOLUTION		0x80
+
+/*
+ * ADT7410 masks
+ */
+#define ADT7410_T16_VALUE_SIGN			0x8000
+#define ADT7410_T16_VALUE_FLOAT_OFFSET		7
+#define ADT7410_T16_VALUE_FLOAT_MASK		0x7F
+#define ADT7410_T13_VALUE_SIGN			0x1000
+#define ADT7410_T13_VALUE_OFFSET		3
+#define ADT7410_T13_VALUE_FLOAT_OFFSET		4
+#define ADT7410_T13_VALUE_FLOAT_MASK		0xF
+#define ADT7410_T_HYST_MASK			0xF
+#define ADT7410_DEVICE_ID_MASK			0xF
+#define ADT7410_MANUFACTORY_ID_MASK		0xF0
+#define ADT7410_MANUFACTORY_ID_OFFSET		4
+
+#define ADT7410_IRQS				2
+
+/*
+ * struct adt7410_chip_info - chip specifc information
+ */
+
+struct adt7410_chip_info {
+	const char *name;
+	struct i2c_client *client;
+	struct iio_dev *indio_dev;
+	struct work_struct thresh_work;
+	s64 last_timestamp;
+	u8  config;
+};
+
+/*
+ * adt7410 register access by I2C
+ */
+
+static int adt7410_i2c_read_word(struct adt7410_chip_info *chip, u8 reg, u16 *data)
+{
+	struct i2c_client *client = chip->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 adt7410_i2c_write_word(struct adt7410_chip_info *chip, u8 reg, u16 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static int adt7410_i2c_read_byte(struct adt7410_chip_info *chip, u8 reg, u8 *data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	*data = (u8)ret;
+
+	return 0;
+}
+
+static int adt7410_i2c_write_byte(struct adt7410_chip_info *chip, u8 reg, u8 data)
+{
+	struct i2c_client *client = chip->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static ssize_t adt7410_show_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u8 config;
+
+	config = chip->config & ADT7410_MODE_MASK;
+
+	switch (config) {
+	case ADT7410_PD:
+		return sprintf(buf, "power-down\n");
+	case ADT7410_ONESHOT:
+		return sprintf(buf, "one-shot\n");
+	case ADT7410_SPS:
+		return sprintf(buf, "sps\n");
+	default:
+		return sprintf(buf, "full\n");
+	}
+}
+
+static ssize_t adt7410_store_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7410_MODE_MASK);
+	if (strcmp(buf, "power-down"))
+		config |= ADT7410_PD;
+	else if (strcmp(buf, "one-shot"))
+		config |= ADT7410_ONESHOT;
+	else if (strcmp(buf, "sps"))
+		config |= ADT7410_SPS;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+		adt7410_show_mode,
+		adt7410_store_mode,
+		0);
+
+static ssize_t adt7410_show_available_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "full\none-shot\nsps\npower-down\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7410_show_available_modes, NULL, 0);
+
+static ssize_t adt7410_show_resolution(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+	int bits;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7410_RESOLUTION)
+		bits = 16;
+	else
+		bits = 13;
+
+	return sprintf(buf, "%d bits\n", bits);
+}
+
+static ssize_t adt7410_store_resolution(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	u16 config;
+	int ret;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret)
+		return -EINVAL;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & (~ADT7410_RESOLUTION);
+	if (data)
+		config |= ADT7410_RESOLUTION;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR,
+		adt7410_show_resolution,
+		adt7410_store_resolution,
+		0);
+
+static ssize_t adt7410_show_id(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u8 id;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_ID, &id);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "device id: 0x%x\nmanufactory id: 0x%x\n",
+			id & ADT7410_DEVICE_ID_MASK,
+			(id & ADT7410_MANUFACTORY_ID_MASK) >> ADT7410_MANUFACTORY_ID_OFFSET);
+}
+
+static IIO_DEVICE_ATTR(id, S_IRUGO | S_IWUSR,
+		adt7410_show_id,
+		NULL,
+		0);
+
+static ssize_t adt7410_convert_temperature(struct adt7410_chip_info *chip,
+		u16 data, char *buf)
+{
+	char sign = ' ';
+
+	if (chip->config & ADT7410_RESOLUTION) {
+		if (data & ADT7410_T16_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.7d\n", sign,
+				(data >> ADT7410_T16_VALUE_FLOAT_OFFSET),
+				(data & ADT7410_T16_VALUE_FLOAT_MASK) * 78125);
+	} else {
+		if (data & ADT7410_T13_VALUE_SIGN) {
+			/* convert supplement to positive value */
+			data >>= ADT7410_T13_VALUE_OFFSET;
+			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
+			sign = '-';
+		}
+		return sprintf(buf, "%c%d.%.4d\n", sign,
+				(data >> ADT7410_T13_VALUE_FLOAT_OFFSET),
+				(data & ADT7410_T13_VALUE_FLOAT_MASK) * 625);
+	}
+}
+
+static ssize_t adt7410_show_value(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u8 status;
+	u16 data;
+	int ret, i = 0;
+
+	do {
+		ret = adt7410_i2c_read_byte(chip, ADT7410_STATUS, &status);
+		if (ret)
+			return -EIO;
+		i++;
+		if (i == 10000)
+			return -EIO;
+	} while (status & ADT7410_STAT_NOT_RDY);
+
+	ret = adt7410_i2c_read_word(chip, ADT7410_TEMPERATURE, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7410_convert_temperature(chip, data, buf);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, adt7410_show_value, NULL, 0);
+
+static ssize_t adt7410_show_name(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	return sprintf(buf, "%s\n", chip->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, adt7410_show_name, NULL, 0);
+
+static struct attribute *adt7410_attributes[] = {
+	&iio_dev_attr_available_modes.dev_attr.attr,
+	&iio_dev_attr_mode.dev_attr.attr,
+	&iio_dev_attr_resolution.dev_attr.attr,
+	&iio_dev_attr_id.dev_attr.attr,
+	&iio_dev_attr_value.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group adt7410_attribute_group = {
+	.attrs = adt7410_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_ADT7410_ABOVE_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_ADT7410_BELLOW_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+#define IIO_EVENT_CODE_ADT7410_ABOVE_CRIT    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
+
+static void adt7410_interrupt_bh(struct work_struct *work_s)
+{
+	struct adt7410_chip_info *chip =
+		container_of(work_s, struct adt7410_chip_info, thresh_work);
+	u8 status;
+
+	if (adt7410_i2c_read_byte(chip, ADT7410_STATUS, &status))
+		return;
+
+	enable_irq(chip->client->irq);
+
+	if (status & ADT7410_STAT_T_HIGH)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7410_ABOVE_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7410_STAT_T_LOW)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7410_BELLOW_ALARM,
+			chip->last_timestamp);
+	if (status & ADT7410_STAT_T_CRIT)
+		iio_push_event(chip->indio_dev, 0,
+			IIO_EVENT_CODE_ADT7410_ABOVE_CRIT,
+			chip->last_timestamp);
+}
+
+static int adt7410_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+
+	chip->last_timestamp = timestamp;
+	schedule_work(&chip->thresh_work);
+
+	return 0;
+}
+
+IIO_EVENT_SH(adt7410, &adt7410_interrupt);
+IIO_EVENT_SH(adt7410_ct, &adt7410_interrupt);
+
+static ssize_t adt7410_show_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	if (chip->config & ADT7410_EVENT_MODE)
+		return sprintf(buf, "interrupt\n");
+	else
+		return sprintf(buf, "comparator\n");
+}
+
+static ssize_t adt7410_set_event_mode(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u16 config;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config &= ~ADT7410_EVENT_MODE;
+	if (strcmp(buf, "comparator") != 0)
+		config |= ADT7410_EVENT_MODE;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static ssize_t adt7410_show_available_event_modes(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "comparator\ninterrupt\n");
+}
+
+static ssize_t adt7410_show_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", chip->config & ADT7410_FAULT_QUEUE_MASK);
+}
+
+static ssize_t adt7410_set_fault_queue(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	unsigned long data;
+	int ret;
+	u8 config;
+
+	ret = strict_strtoul(buf, 10, &data);
+	if (ret || data > 3)
+		return -EINVAL;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+	if (ret)
+		return -EIO;
+
+	config = chip->config & ~ADT7410_FAULT_QUEUE_MASK;
+	config |= data;
+	ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, config);
+	if (ret)
+		return -EIO;
+
+	chip->config = config;
+
+	return ret;
+}
+
+static inline ssize_t adt7410_show_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	u16 data;
+	int ret;
+
+	ret = adt7410_i2c_read_word(chip, bound_reg, &data);
+	if (ret)
+		return -EIO;
+
+	return adt7410_convert_temperature(chip, data, buf);
+}
+
+static inline ssize_t adt7410_set_t_bound(struct device *dev,
+		struct device_attribute *attr,
+		u8 bound_reg,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	long tmp1, tmp2;
+	u16 data;
+	char *pos;
+	int ret;
+
+	pos = strchr(buf, '.');
+
+	ret = strict_strtol(buf, 10, &tmp1);
+
+	if (ret || tmp1 > 127 || tmp1 < -128)
+		return -EINVAL;
+
+	if (pos) {
+		len = strlen(pos);
+
+		if (chip->config & ADT7410_RESOLUTION) {
+			if (len > ADT7410_T16_VALUE_FLOAT_OFFSET)
+				len = ADT7410_T16_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 78125) * 78125;
+		} else {
+			if (len > ADT7410_T13_VALUE_FLOAT_OFFSET)
+				len = ADT7410_T13_VALUE_FLOAT_OFFSET;
+			pos[len] = 0;
+			ret = strict_strtol(pos, 10, &tmp2);
+
+			if (!ret)
+				tmp2 = (tmp2 / 625) * 625;
+		}
+	}
+
+	if (tmp1 < 0)
+		data = (u16)(-tmp1);
+	else
+		data = (u16)tmp1;
+
+	if (chip->config & ADT7410_RESOLUTION) {
+		data = (data << ADT7410_T16_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7410_T16_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (u16)((ADT7410_T16_VALUE_SIGN << 1) - (u32)data);
+	} else {
+		data = (data << ADT7410_T13_VALUE_FLOAT_OFFSET) |
+			(tmp2 & ADT7410_T13_VALUE_FLOAT_MASK);
+
+		if (tmp1 < 0)
+			/* convert positive value to supplyment */
+			data = (ADT7410_T13_VALUE_SIGN << 1) - data;
+		data <<= ADT7410_T13_VALUE_OFFSET;
+	}
+
+	ret = adt7410_i2c_write_word(chip, bound_reg, data);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+static ssize_t adt7410_show_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7410_show_t_bound(dev, attr,
+			ADT7410_T_ALARM_HIGH, buf);
+}
+
+static inline ssize_t adt7410_set_t_alarm_high(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7410_set_t_bound(dev, attr,
+			ADT7410_T_ALARM_HIGH, buf, len);
+}
+
+static ssize_t adt7410_show_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7410_show_t_bound(dev, attr,
+			ADT7410_T_ALARM_LOW, buf);
+}
+
+static inline ssize_t adt7410_set_t_alarm_low(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7410_set_t_bound(dev, attr,
+			ADT7410_T_ALARM_LOW, buf, len);
+}
+
+static ssize_t adt7410_show_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return adt7410_show_t_bound(dev, attr,
+			ADT7410_T_CRIT, buf);
+}
+
+static inline ssize_t adt7410_set_t_crit(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	return adt7410_set_t_bound(dev, attr,
+			ADT7410_T_CRIT, buf, len);
+}
+
+static ssize_t adt7410_show_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+	u8 t_hyst;
+
+	ret = adt7410_i2c_read_byte(chip, ADT7410_T_HYST, &t_hyst);
+	if (ret)
+		return -EIO;
+
+	return sprintf(buf, "%d\n", t_hyst & ADT7410_T_HYST_MASK);
+}
+
+static inline ssize_t adt7410_set_t_hyst(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct adt7410_chip_info *chip = dev_info->dev_data;
+	int ret;
+	unsigned long data;
+	u8 t_hyst;
+
+	ret = strict_strtol(buf, 10, &data);
+
+	if (ret || data > ADT7410_T_HYST_MASK)
+		return -EINVAL;
+
+	t_hyst = (u8)data;
+
+	ret = adt7410_i2c_write_byte(chip, ADT7410_T_HYST, t_hyst);
+	if (ret)
+		return -EIO;
+
+	return ret;
+}
+
+IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7410,
+		adt7410_show_event_mode, adt7410_set_event_mode, 0);
+IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7410,
+		adt7410_show_available_event_modes, NULL, 0);
+IIO_EVENT_ATTR_SH(fault_queue, iio_event_adt7410,
+		adt7410_show_fault_queue, adt7410_set_fault_queue, 0);
+IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7410,
+		adt7410_show_t_alarm_high, adt7410_set_t_alarm_high, 0);
+IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7410,
+		adt7410_show_t_alarm_low, adt7410_set_t_alarm_low, 0);
+IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7410_ct,
+		adt7410_show_t_crit, adt7410_set_t_crit, 0);
+IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7410,
+		adt7410_show_t_hyst, adt7410_set_t_hyst, 0);
+
+static struct attribute *adt7410_event_int_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_alarm_high.dev_attr.attr,
+	&iio_event_attr_t_alarm_low.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *adt7410_event_ct_attributes[] = {
+	&iio_event_attr_event_mode.dev_attr.attr,
+	&iio_event_attr_available_event_modes.dev_attr.attr,
+	&iio_event_attr_fault_queue.dev_attr.attr,
+	&iio_event_attr_t_crit.dev_attr.attr,
+	&iio_event_attr_t_hyst.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adt7410_event_attribute_group[ADT7410_IRQS] = {
+	{
+		.attrs = adt7410_event_int_attributes,
+	},
+	{
+		.attrs = adt7410_event_ct_attributes,
+	}
+};
+
+/*
+ * device probe and remove
+ */
+
+static int __devinit adt7410_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct adt7410_chip_info *chip;
+	int ret = 0;
+	unsigned long *adt7410_platform_data = client->dev.platform_data;
+
+	chip = kzalloc(sizeof(struct adt7410_chip_info), GFP_KERNEL);
+
+	if (chip == NULL)
+		return -ENOMEM;
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, chip);
+
+	chip->client = client;
+	chip->name = id->name;
+
+	chip->indio_dev = iio_allocate_device();
+	if (chip->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_chip;
+	}
+
+	chip->indio_dev->dev.parent = &client->dev;
+	chip->indio_dev->attrs = &adt7410_attribute_group;
+	chip->indio_dev->event_attrs = adt7410_event_attribute_group;
+	chip->indio_dev->dev_data = (void *)chip;
+	chip->indio_dev->driver_module = THIS_MODULE;
+	chip->indio_dev->num_interrupt_lines = ADT7410_IRQS;
+	chip->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(chip->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	/* CT critcal temperature event. line 0 */
+	if (client->irq) {
+		ret = iio_register_interrupt_line(client->irq,
+				chip->indio_dev,
+				0,
+				client->irq_flags,
+				chip->name);
+		if (ret)
+			goto error_unreg_dev;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7410.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7410,
+				&chip->indio_dev->interrupts[0]->ev_list);
+	}
+
+	/* INT bound temperature alarm event. line 1 */
+	if (adt7410_platform_data[0]) {
+		ret = iio_register_interrupt_line(adt7410_platform_data[0],
+				chip->indio_dev,
+				1,
+				adt7410_platform_data[1],
+				chip->name);
+		if (ret)
+			goto error_unreg_ct_irq;
+
+		/*
+		 * The event handler list element refer to iio_event_adt7410.
+		 * All event attributes bind to the same event handler.
+		 * One event handler can only be added to one event list.
+		 */
+		iio_add_event_to_list(&iio_event_adt7410_ct,
+				&chip->indio_dev->interrupts[1]->ev_list);
+	}
+
+	if (client->irq && adt7410_platform_data[0]) {
+		INIT_WORK(&chip->thresh_work, adt7410_interrupt_bh);
+
+		ret = adt7410_i2c_read_byte(chip, ADT7410_CONFIG, &chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+
+		if (client->irq_flags & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7410_CT_POLARITY;
+		else
+			chip->config &= ~ADT7410_CT_POLARITY;
+
+		if (adt7410_platform_data[1] & IRQF_TRIGGER_HIGH)
+			chip->config |= ADT7410_INT_POLARITY;
+		else
+			chip->config &= ~ADT7410_INT_POLARITY;
+
+		ret = adt7410_i2c_write_byte(chip, ADT7410_CONFIG, chip->config);
+		if (ret) {
+			ret = -EIO;
+			goto error_unreg_int_irq;
+		}
+	}
+
+	dev_info(&client->dev, "%s temperature sensor registered.\n",
+			 id->name);
+
+	return 0;
+
+error_unreg_int_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 1);
+error_unreg_ct_irq:
+	iio_unregister_interrupt_line(chip->indio_dev, 0);
+error_unreg_dev:
+	iio_device_unregister(chip->indio_dev);
+error_free_dev:
+	iio_free_device(chip->indio_dev);
+error_free_chip:
+	kfree(chip);
+
+	return ret;
+}
+
+static int __devexit adt7410_remove(struct i2c_client *client)
+{
+	struct adt7410_chip_info *chip = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = chip->indio_dev;
+	unsigned long *adt7410_platform_data = client->dev.platform_data;
+
+	if (adt7410_platform_data[0])
+		iio_unregister_interrupt_line(indio_dev, 1);
+	if (client->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+	iio_free_device(chip->indio_dev);
+	kfree(chip);
+
+	return 0;
+}
+
+static const struct i2c_device_id adt7410_id[] = {
+	{ "adt7410", 0 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, adt7410_id);
+
+static struct i2c_driver adt7410_driver = {
+	.driver = {
+		.name = "adt7410",
+	},
+	.probe = adt7410_probe,
+	.remove = __devexit_p(adt7410_remove),
+	.id_table = adt7410_id,
+};
+
+static __init int adt7410_init(void)
+{
+	return i2c_add_driver(&adt7410_driver);
+}
+
+static __exit void adt7410_exit(void)
+{
+	i2c_del_driver(&adt7410_driver);
+}
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7410 digital"
+			" temperature sensor driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(adt7410_init);
+module_exit(adt7410_exit);
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [PATCH 14/14] staging: iio: adc: new ad799x driver
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (11 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 13/14] staging: iio: adc: new driver for ADT7410 " Mike Frysinger
@ 2010-10-23 20:29 ` Mike Frysinger
  2010-10-24 21:14   ` [Device-drivers-devel] " Mike Frysinger
  2010-10-24 22:55   ` Jonathan Cameron
  2010-10-24 21:09 ` [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Jonathan Cameron
  13 siblings, 2 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:29 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Michael Hennerich

From: Michael Hennerich <michael.hennerich@analog.com>

Driver for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997 and
ad7998 multichannel ADC.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/adc/Kconfig       |   20 +
 drivers/staging/iio/adc/Makefile      |    4 +
 drivers/staging/iio/adc/ad799x.h      |  168 ++++++
 drivers/staging/iio/adc/ad799x_core.c |  912 +++++++++++++++++++++++++++++++++
 drivers/staging/iio/adc/ad799x_ring.c |  281 ++++++++++
 5 files changed, 1385 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/adc/ad799x.h
 create mode 100644 drivers/staging/iio/adc/ad799x_core.c
 create mode 100644 drivers/staging/iio/adc/ad799x_ring.c

diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index ff479c4..b3173c7 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -101,6 +101,26 @@ config AD7816
 	  Say yes here to build support for Analog Devices AD7816/7/8
 	  temperature sensors and ADC.
 
+config AD799X
+	tristate "Analog Devices AD799x ADC driver"
+	depends on I2C
+	select IIO_TRIGGER if IIO_RING_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 AD799X_RING_BUFFER
+	bool "Analog Devices AD799x: use ring buffer"
+	depends on AD799X
+	select IIO_RING_BUFFER
+	select IIO_SW_RING
+	help
+	  Say yes here to include ring buffer support in the AD799X
+	  ADC driver.
+
 config ADT75
 	tristate "Analog Devices ADT75 temperature sensor driver"
 	depends on I2C
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 0a2d7ea..1f17e37 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -8,6 +8,9 @@ max1363-y += max1363_ring.o
 ad7476-y := ad7476_core.o
 ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o
 
+ad799x-y := ad799x_core.o
+ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o
+
 obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_AD7150) += ad7150.o
 obj-$(CONFIG_AD7152) += ad7152.o
@@ -19,6 +22,7 @@ obj-$(CONFIG_AD7416) += ad7416.o
 obj-$(CONFIG_AD7476) += ad7476.o
 obj-$(CONFIG_AD774X) += ad774x.o
 obj-$(CONFIG_AD7816) += ad7816.o
+obj-$(CONFIG_AD799X) += ad799x.o
 obj-$(CONFIG_ADT75) += adt75.o
 obj-$(CONFIG_ADT7310) += adt7310.o
 obj-$(CONFIG_ADT7408) += adt7408.o
diff --git a/drivers/staging/iio/adc/ad799x.h b/drivers/staging/iio/adc/ad799x.h
new file mode 100644
index 0000000..d11885f
--- /dev/null
+++ b/drivers/staging/iio/adc/ad799x.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 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.
+ *
+ * ad799x.h
+ */
+
+#ifndef _AD799X_H_
+#define  _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
+
+enum {
+	ad7991,
+	ad7995,
+	ad7999,
+	ad7992,
+	ad7993,
+	ad7994,
+	ad7997,
+	ad7998
+};
+
+struct ad799x_state;
+
+/**
+ * struct ad799x_chip_info - chip specifc information
+ * @num_inputs:		number of physical inputs on chip
+ * @bits:		accuracy of the adc in bits
+ * @int_vref_mv:	the internal reference voltage
+ * @monitor_mode:	whether the chip supports monitor interrupts
+ * @default_config:	device default configuration
+ * @dev_attrs:		pointer to the device attribute group
+ * @scan_attrs:		pointer to the scan element attribute group
+ * @event_attrs:	pointer to the monitor event attribute group
+ * @ad799x_set_scan_mode: function pointer to the device specific mode function
+
+ */
+struct ad799x_chip_info {
+	u8				num_inputs;
+	u8				bits;
+	u16				int_vref_mv;
+	bool				monitor_mode;
+	u16				default_config;
+	struct attribute_group		*dev_attrs;
+	struct attribute_group		*scan_attrs;
+	struct attribute_group		*event_attrs;
+	int (*ad799x_set_scan_mode)	(struct ad799x_state *st,
+					unsigned mask);
+};
+
+struct ad799x_state {
+	struct iio_dev			*indio_dev;
+	struct i2c_client		*client;
+	const struct ad799x_chip_info	*chip_info;
+	struct work_struct		poll_work;
+	struct work_struct		work_thresh;
+	atomic_t			protect_ring;
+	struct iio_trigger		*trig;
+	struct regulator		*reg;
+	s64				last_timestamp;
+	u16				int_vref_mv;
+	unsigned			id;
+	char				*name;
+	u16				config;
+};
+
+/*
+ * TODO: struct ad799x_platform_data needs to go into inlude/linux/iio
+ */
+
+struct ad799x_platform_data {
+	u16				vref_mv;
+};
+
+int ad799x_set_scan_mode(struct ad799x_state *st, unsigned mask);
+
+#ifdef CONFIG_AD799X_RING_BUFFER
+int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask);
+int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev);
+void ad799x_ring_cleanup(struct iio_dev *indio_dev);
+int ad799x_initialize_ring(struct iio_ring_buffer *ring);
+void ad799x_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_AD799X_RING_BUFFER */
+static inline void ad799x_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+static inline int ad799x_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask)
+{
+	return -EINVAL;
+}
+
+
+static inline int
+ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void ad799x_ring_cleanup(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_AD799X_RING_BUFFER */
+#endif /* _AD799X_H_ */
diff --git a/drivers/staging/iio/adc/ad799x_core.c b/drivers/staging/iio/adc/ad799x_core.c
new file mode 100644
index 0000000..17a718a
--- /dev/null
+++ b/drivers/staging/iio/adc/ad799x_core.c
@@ -0,0 +1,912 @@
+/*
+ * iio/adc/ad799x.c
+ * Copyright (C) 2010 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/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/err.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+
+#include "../ring_generic.h"
+#include "adc.h"
+#include "ad799x.h"
+
+/*
+ * 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_i2c_read8(struct ad799x_state *st, u8 reg, u8 *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 = ret;
+
+	return 0;
+}
+
+static int ad799x_i2c_write16(struct ad799x_state *st, u8 reg, u16 data)
+{
+	struct i2c_client *client = st->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static int ad799x_i2c_write8(struct ad799x_state *st, u8 reg, u8 data)
+{
+	struct i2c_client *client = st->client;
+	int ret = 0;
+
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0)
+		dev_err(&client->dev, "I2C write error\n");
+
+	return ret;
+}
+
+static int ad799x_scan_el_set_state(struct iio_scan_el *scan_el,
+				       struct iio_dev *indio_dev,
+				       bool state)
+{
+	struct ad799x_state *st = indio_dev->dev_data;
+	return ad799x_set_scan_mode(st, st->indio_dev->scan_mask);
+}
+
+/* Here we claim all are 16 bits. This currently does no harm and saves
+ * us a lot of scan element listings */
+
+#define AD799X_SCAN_EL(number)						\
+	IIO_SCAN_EL_C(in##number, number, IIO_UNSIGNED(16), 0,		\
+	ad799x_scan_el_set_state);
+
+static AD799X_SCAN_EL(0);
+static AD799X_SCAN_EL(1);
+static AD799X_SCAN_EL(2);
+static AD799X_SCAN_EL(3);
+static AD799X_SCAN_EL(4);
+static AD799X_SCAN_EL(5);
+static AD799X_SCAN_EL(6);
+static AD799X_SCAN_EL(7);
+
+static ssize_t ad799x_show_precision(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+	return sprintf(buf, "%d\n", st->chip_info->bits);
+}
+
+static IIO_DEVICE_ATTR(in_precision, S_IRUGO, ad799x_show_precision,
+		       NULL, 0);
+
+static int ad7991_5_9_set_scan_mode(struct ad799x_state *st, unsigned mask)
+{
+	return i2c_smbus_write_byte(st->client,
+		st->config | (mask << AD799X_CHANNEL_SHIFT));
+}
+
+static int ad7992_3_4_set_scan_mode(struct ad799x_state *st, unsigned mask)
+{
+	return ad799x_i2c_write8(st, AD7998_CONF_REG,
+		st->config | (mask << AD799X_CHANNEL_SHIFT));
+}
+
+static int ad7997_8_set_scan_mode(struct ad799x_state *st, unsigned mask)
+{
+	return ad799x_i2c_write16(st, AD7998_CONF_REG,
+		st->config | (mask << AD799X_CHANNEL_SHIFT));
+}
+
+int ad799x_set_scan_mode(struct ad799x_state *st, unsigned mask)
+{
+	int ret;
+
+	if (st->chip_info->ad799x_set_scan_mode != NULL) {
+		ret = st->chip_info->ad799x_set_scan_mode(st, mask);
+		return (ret > 0) ? 0 : ret;
+	}
+
+	return 0;
+}
+
+static ssize_t ad799x_read_single_channel(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret = 0, len = 0;
+	u32 data ;
+	u16 rxbuf[1];
+	u8 cmd;
+	long mask;
+
+	mutex_lock(&dev_info->mlock);
+	mask = 1 << this_attr->address;
+	/* If ring buffer capture is occuring, query the buffer */
+	if (iio_ring_enabled(dev_info)) {
+		data = ad799x_single_channel_from_ring(st, mask);
+		if (data < 0) {
+			ret = data;
+			goto error_ret;
+		}
+	} else {
+		switch (st->id) {
+		case ad7991:
+		case ad7995:
+		case ad7999:
+			cmd = st->config | (mask << AD799X_CHANNEL_SHIFT);
+			break;
+		case ad7992:
+		case ad7993:
+		case ad7994:
+			cmd = mask << AD799X_CHANNEL_SHIFT;
+			break;
+		case ad7997:
+		case ad7998:
+			cmd = (this_attr->address <<
+				AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
+			break;
+		default:
+			cmd = 0;
+
+		}
+		ret = ad799x_i2c_read16(st, cmd, rxbuf);
+		if (ret < 0)
+			goto error_ret;
+
+		data = rxbuf[0] & 0xFFF;
+	}
+
+	/* Pretty print the result */
+	len = sprintf(buf, "%u\n", data);
+
+error_ret:
+	mutex_unlock(&dev_info->mlock);
+	return ret ? ret : len;
+}
+
+static ssize_t ad799x_read_frequency(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+
+	int ret, len = 0;
+	u8 val;
+	ret = ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &val);
+	if (ret)
+		return ret;
+
+	val &= AD7998_CYC_MASK;
+
+	switch (val) {
+	case AD7998_CYC_DIS:
+		len = sprintf(buf, "0\n");
+		break;
+	case AD7998_CYC_TCONF_32:
+		len = sprintf(buf, "15625\n");
+		break;
+	case AD7998_CYC_TCONF_64:
+		len = sprintf(buf, "7812\n");
+		break;
+	case AD7998_CYC_TCONF_128:
+		len = sprintf(buf, "3906\n");
+		break;
+	case AD7998_CYC_TCONF_256:
+		len = sprintf(buf, "1953\n");
+		break;
+	case AD7998_CYC_TCONF_512:
+		len = sprintf(buf, "976\n");
+		break;
+	case AD7998_CYC_TCONF_1024:
+		len = sprintf(buf, "488\n");
+		break;
+	case AD7998_CYC_TCONF_2048:
+		len = sprintf(buf, "244\n");
+		break;
+	}
+	return len;
+}
+
+static ssize_t ad799x_write_frequency(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+
+	long val;
+	int ret;
+	u8 t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&dev_info->mlock);
+	ret = ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &t);
+	if (ret)
+		goto error_ret_mutex;
+	/* Wipe the bits clean */
+	t &= ~AD7998_CYC_MASK;
+
+	switch (val) {
+	case 15625:
+		t |= AD7998_CYC_TCONF_32;
+		break;
+	case 7812:
+		t |= AD7998_CYC_TCONF_64;
+		break;
+	case 3906:
+		t |= AD7998_CYC_TCONF_128;
+		break;
+	case 1953:
+		t |= AD7998_CYC_TCONF_256;
+		break;
+	case 976:
+		t |= AD7998_CYC_TCONF_512;
+		break;
+	case 488:
+		t |= AD7998_CYC_TCONF_1024;
+		break;
+	case 244:
+		t |= AD7998_CYC_TCONF_2048;
+		break;
+	case  0:
+		t |= AD7998_CYC_DIS;
+		break;
+	default:
+		ret = -EINVAL;
+		goto error_ret_mutex;
+	}
+
+	ret = ad799x_i2c_write8(st, AD7998_CYCLE_TMR_REG, t);
+
+error_ret_mutex:
+	mutex_unlock(&dev_info->mlock);
+
+	return ret ? ret : len;
+}
+
+
+static ssize_t ad799x_read_channel_config(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+	int ret;
+	u16 val;
+	ret = ad799x_i2c_read16(st, this_attr->mask, &val);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t ad799x_write_channel_config(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+	long val;
+	int ret;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&dev_info->mlock);
+	ret = ad799x_i2c_write16(st, this_attr->mask, val);
+	mutex_unlock(&dev_info->mlock);
+
+	return ret ? ret : len;
+}
+
+#define IIO_EVENT_CODE_AD7998_IN0_LOW	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 0)
+#define IIO_EVENT_CODE_AD7998_IN0_HIGH	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
+#define IIO_EVENT_CODE_AD7998_IN1_LOW	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
+#define IIO_EVENT_CODE_AD7998_IN1_HIGH	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
+#define IIO_EVENT_CODE_AD7998_IN2_LOW	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 4)
+#define IIO_EVENT_CODE_AD7998_IN2_HIGH	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 5)
+#define IIO_EVENT_CODE_AD7998_IN3_LOW	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 6)
+#define IIO_EVENT_CODE_AD7998_IN3_HIGH	(IIO_EVENT_CODE_DEVICE_SPECIFIC + 7)
+
+static void ad799x_interrupt_bh(struct work_struct *work_s)
+{
+	struct ad799x_state *st = container_of(work_s,
+		struct ad799x_state, work_thresh);
+	u8 status;
+	int i;
+
+	if (ad799x_i2c_read8(st, AD7998_ALERT_STAT_REG, &status))
+		goto err_out;
+
+	if (!status)
+		goto err_out;
+
+	ad799x_i2c_write8(st, AD7998_ALERT_STAT_REG, AD7998_ALERT_STAT_CLEAR);
+
+	for (i = 0; i < 8; i++) {
+		if (status & (1 << i))
+			iio_push_event(st->indio_dev, 0,
+				IIO_EVENT_CODE_AD7998_IN0_LOW + i,
+				st->last_timestamp);
+	}
+
+err_out:
+	enable_irq(st->client->irq);
+}
+
+static int ad799x_interrupt(struct iio_dev *dev_info,
+		int index,
+		s64 timestamp,
+		int no_test)
+{
+	struct ad799x_state *st = dev_info->dev_data;
+
+	st->last_timestamp = timestamp;
+	schedule_work(&st->work_thresh);
+	return 0;
+}
+
+IIO_EVENT_SH(ad799x, &ad799x_interrupt);
+
+/* Direct read attribtues */
+static IIO_DEV_ATTR_IN_RAW(0, ad799x_read_single_channel, 0);
+static IIO_DEV_ATTR_IN_RAW(1, ad799x_read_single_channel, 1);
+static IIO_DEV_ATTR_IN_RAW(2, ad799x_read_single_channel, 2);
+static IIO_DEV_ATTR_IN_RAW(3, ad799x_read_single_channel, 3);
+static IIO_DEV_ATTR_IN_RAW(4, ad799x_read_single_channel, 4);
+static IIO_DEV_ATTR_IN_RAW(5, ad799x_read_single_channel, 5);
+static IIO_DEV_ATTR_IN_RAW(6, ad799x_read_single_channel, 6);
+static IIO_DEV_ATTR_IN_RAW(7, ad799x_read_single_channel, 7);
+
+static ssize_t ad799x_show_scale(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	/* Driver currently only support internal vref */
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+	/* Corresponds to Vref / 2^(bits) */
+
+	if ((1 << (st->chip_info->bits + 1))
+	    > st->int_vref_mv)
+		return sprintf(buf, "0.5\n");
+	else
+		return sprintf(buf, "%d\n",
+			st->int_vref_mv >> st->chip_info->bits);
+}
+
+static IIO_DEVICE_ATTR(in_scale, S_IRUGO, ad799x_show_scale, NULL, 0);
+
+static ssize_t ad799x_show_name(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct ad799x_state *st = iio_dev_get_devdata(dev_info);
+	return sprintf(buf, "%s\n", st->client->name);
+}
+
+static IIO_DEVICE_ATTR(name, S_IRUGO, ad799x_show_name, NULL, 0);
+
+static struct attribute *ad7991_5_9_3_4_device_attrs[] = {
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_dev_attr_in2_raw.dev_attr.attr,
+	&iio_dev_attr_in3_raw.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	&iio_dev_attr_in_scale.dev_attr.attr,
+	NULL
+};
+
+static struct attribute_group ad7991_5_9_3_4_dev_attr_group = {
+	.attrs = ad7991_5_9_3_4_device_attrs,
+};
+
+static struct attribute *ad7991_5_9_3_4_scan_el_attrs[] = {
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_scan_el_in1.dev_attr.attr,
+	&iio_scan_el_in2.dev_attr.attr,
+	&iio_scan_el_in3.dev_attr.attr,
+	&iio_dev_attr_in_precision.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7991_5_9_3_4_scan_el_group = {
+	.name = "scan_elements",
+	.attrs = ad7991_5_9_3_4_scan_el_attrs,
+};
+
+static struct attribute *ad7992_device_attrs[] = {
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	&iio_dev_attr_in_scale.dev_attr.attr,
+	NULL
+};
+
+static struct attribute_group ad7992_dev_attr_group = {
+	.attrs = ad7992_device_attrs,
+};
+
+static struct attribute *ad7992_scan_el_attrs[] = {
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_scan_el_in1.dev_attr.attr,
+	&iio_dev_attr_in_precision.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7992_scan_el_group = {
+	.name = "scan_elements",
+	.attrs = ad7992_scan_el_attrs,
+};
+
+static struct attribute *ad7997_8_device_attrs[] = {
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_dev_attr_in1_raw.dev_attr.attr,
+	&iio_dev_attr_in2_raw.dev_attr.attr,
+	&iio_dev_attr_in3_raw.dev_attr.attr,
+	&iio_dev_attr_in4_raw.dev_attr.attr,
+	&iio_dev_attr_in5_raw.dev_attr.attr,
+	&iio_dev_attr_in6_raw.dev_attr.attr,
+	&iio_dev_attr_in7_raw.dev_attr.attr,
+	&iio_dev_attr_name.dev_attr.attr,
+	&iio_dev_attr_in_scale.dev_attr.attr,
+	NULL
+};
+
+static struct attribute_group ad7997_8_dev_attr_group = {
+	.attrs = ad7997_8_device_attrs,
+};
+
+static struct attribute *ad7997_8_scan_el_attrs[] = {
+	&iio_scan_el_in0.dev_attr.attr,
+	&iio_scan_el_in1.dev_attr.attr,
+	&iio_scan_el_in2.dev_attr.attr,
+	&iio_scan_el_in3.dev_attr.attr,
+	&iio_scan_el_in4.dev_attr.attr,
+	&iio_scan_el_in5.dev_attr.attr,
+	&iio_scan_el_in6.dev_attr.attr,
+	&iio_scan_el_in7.dev_attr.attr,
+	&iio_dev_attr_in_precision.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7997_8_scan_el_group = {
+	.name = "scan_elements",
+	.attrs = ad7997_8_scan_el_attrs,
+};
+
+IIO_EVENT_ATTR_SH(in0_thresh_low_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATALOW_CH1_REG);
+
+IIO_EVENT_ATTR_SH(in0_thresh_high_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATAHIGH_CH1_REG);
+
+IIO_EVENT_ATTR_SH(in0_thresh_both_hyst_raw,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_HYST_CH1_REG);
+
+IIO_EVENT_ATTR_SH(in1_thresh_low_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATALOW_CH2_REG);
+
+IIO_EVENT_ATTR_SH(in1_thresh_high_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATAHIGH_CH2_REG);
+
+IIO_EVENT_ATTR_SH(in1_thresh_both_hyst_raw,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_HYST_CH2_REG);
+
+IIO_EVENT_ATTR_SH(in2_thresh_low_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATALOW_CH3_REG);
+
+IIO_EVENT_ATTR_SH(in2_thresh_high_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATAHIGH_CH3_REG);
+
+IIO_EVENT_ATTR_SH(in2_thresh_both_hyst_raw,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_HYST_CH3_REG);
+
+IIO_EVENT_ATTR_SH(in3_thresh_low_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATALOW_CH4_REG);
+
+IIO_EVENT_ATTR_SH(in3_thresh_high_value,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_DATAHIGH_CH4_REG);
+
+IIO_EVENT_ATTR_SH(in3_thresh_both_hyst_raw,
+		  iio_event_ad799x,
+		  ad799x_read_channel_config,
+		  ad799x_write_channel_config,
+		  AD7998_HYST_CH4_REG);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			      ad799x_read_frequency,
+			      ad799x_write_frequency);
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0");
+
+static struct attribute *ad7993_4_7_8_event_attributes[] = {
+	&iio_event_attr_in0_thresh_low_value.dev_attr.attr,
+	&iio_event_attr_in0_thresh_high_value.dev_attr.attr,
+	&iio_event_attr_in0_thresh_both_hyst_raw.dev_attr.attr,
+	&iio_event_attr_in1_thresh_low_value.dev_attr.attr,
+	&iio_event_attr_in1_thresh_high_value.dev_attr.attr,
+	&iio_event_attr_in1_thresh_both_hyst_raw.dev_attr.attr,
+	&iio_event_attr_in2_thresh_low_value.dev_attr.attr,
+	&iio_event_attr_in2_thresh_high_value.dev_attr.attr,
+	&iio_event_attr_in2_thresh_both_hyst_raw.dev_attr.attr,
+	&iio_event_attr_in3_thresh_low_value.dev_attr.attr,
+	&iio_event_attr_in3_thresh_high_value.dev_attr.attr,
+	&iio_event_attr_in3_thresh_both_hyst_raw.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7993_4_7_8_event_attrs_group = {
+	.attrs = ad7993_4_7_8_event_attributes,
+};
+
+static struct attribute *ad7992_event_attributes[] = {
+	&iio_event_attr_in0_thresh_low_value.dev_attr.attr,
+	&iio_event_attr_in0_thresh_high_value.dev_attr.attr,
+	&iio_event_attr_in0_thresh_both_hyst_raw.dev_attr.attr,
+	&iio_event_attr_in1_thresh_low_value.dev_attr.attr,
+	&iio_event_attr_in1_thresh_high_value.dev_attr.attr,
+	&iio_event_attr_in1_thresh_both_hyst_raw.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group ad7992_event_attrs_group = {
+	.attrs = ad7992_event_attributes,
+};
+
+static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
+	[ad7991] = {
+		.num_inputs = 4,
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
+		.scan_attrs = &ad7991_5_9_3_4_scan_el_group,
+		.ad799x_set_scan_mode = ad7991_5_9_set_scan_mode,
+	},
+	[ad7995] = {
+		.num_inputs = 4,
+		.bits = 10,
+		.int_vref_mv = 1024,
+		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
+		.scan_attrs = &ad7991_5_9_3_4_scan_el_group,
+		.ad799x_set_scan_mode = ad7991_5_9_set_scan_mode,
+	},
+	[ad7999] = {
+		.num_inputs = 4,
+		.bits = 10,
+		.int_vref_mv = 1024,
+		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
+		.scan_attrs = &ad7991_5_9_3_4_scan_el_group,
+		.ad799x_set_scan_mode = ad7991_5_9_set_scan_mode,
+	},
+	[ad7992] = {
+		.num_inputs = 2,
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.monitor_mode = true,
+		.default_config = AD7998_ALERT_EN,
+		.dev_attrs = &ad7992_dev_attr_group,
+		.scan_attrs = &ad7992_scan_el_group,
+		.event_attrs = &ad7992_event_attrs_group,
+		.ad799x_set_scan_mode = ad7992_3_4_set_scan_mode,
+	},
+	[ad7993] = {
+		.num_inputs = 4,
+		.bits = 10,
+		.int_vref_mv = 1024,
+		.monitor_mode = true,
+		.default_config = AD7998_ALERT_EN,
+		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
+		.scan_attrs = &ad7991_5_9_3_4_scan_el_group,
+		.event_attrs = &ad7993_4_7_8_event_attrs_group,
+		.ad799x_set_scan_mode = ad7992_3_4_set_scan_mode,
+	},
+	[ad7994] = {
+		.num_inputs = 4,
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.monitor_mode = true,
+		.default_config = AD7998_ALERT_EN,
+		.dev_attrs = &ad7991_5_9_3_4_dev_attr_group,
+		.scan_attrs = &ad7991_5_9_3_4_scan_el_group,
+		.event_attrs = &ad7993_4_7_8_event_attrs_group,
+		.ad799x_set_scan_mode = ad7992_3_4_set_scan_mode,
+	},
+	[ad7997] = {
+		.num_inputs = 8,
+		.bits = 10,
+		.int_vref_mv = 1024,
+		.monitor_mode = true,
+		.default_config = AD7998_ALERT_EN,
+		.dev_attrs = &ad7997_8_dev_attr_group,
+		.scan_attrs = &ad7997_8_scan_el_group,
+		.event_attrs = &ad7993_4_7_8_event_attrs_group,
+		.ad799x_set_scan_mode = ad7997_8_set_scan_mode,
+	},
+	[ad7998] = {
+		.num_inputs = 8,
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.monitor_mode = true,
+		.default_config = AD7998_ALERT_EN,
+		.dev_attrs = &ad7997_8_dev_attr_group,
+		.scan_attrs = &ad7997_8_scan_el_group,
+		.event_attrs = &ad7993_4_7_8_event_attrs_group,
+		.ad799x_set_scan_mode = ad7997_8_set_scan_mode,
+	},
+};
+
+static int __devinit ad799x_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	int ret, regdone = 0;
+	struct ad799x_platform_data *pdata = client->dev.platform_data;
+	struct ad799x_state *st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, st);
+
+	atomic_set(&st->protect_ring, 0);
+	st->id = id->driver_data;
+	st->chip_info = &ad799x_chip_info_tbl[st->id];
+	st->config = st->chip_info->default_config;
+
+	/* 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;
+
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_disable_reg;
+	}
+
+	/* Estabilish that the iio_dev is a child of the i2c device */
+	st->indio_dev->dev.parent = &client->dev;
+	st->indio_dev->attrs = st->chip_info->dev_attrs;
+	st->indio_dev->scan_el_attrs = st->chip_info->scan_attrs;
+	st->indio_dev->event_attrs = st->chip_info->event_attrs;
+
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+	st->indio_dev->num_interrupt_lines = 1;
+
+	ret = ad799x_set_scan_mode(st, 0);
+	if (ret)
+		goto error_free_device;
+
+	ret = ad799x_register_ring_funcs_and_init(st->indio_dev);
+	if (ret)
+		goto error_free_device;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_cleanup_ring;
+	regdone = 1;
+	ret = ad799x_initialize_ring(st->indio_dev->ring);
+	if (ret)
+		goto error_cleanup_ring;
+
+	if (client->irq > 0 && st->chip_info->monitor_mode) {
+		INIT_WORK(&st->work_thresh, ad799x_interrupt_bh);
+
+		ret = iio_register_interrupt_line(client->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_FALLING,
+				client->name);
+		if (ret)
+			goto error_cleanup_ring;
+
+		/*
+		 * The event handler list element refer to iio_event_ad799x.
+		 * All event attributes bind to the same event handler.
+		 * So, only register event handler once.
+		 */
+		iio_add_event_to_list(&iio_event_ad799x,
+				&st->indio_dev->interrupts[0]->ev_list);
+	}
+
+	return 0;
+error_cleanup_ring:
+	ad799x_ring_cleanup(st->indio_dev);
+error_free_device:
+	if (!regdone)
+		iio_free_device(st->indio_dev);
+	else
+		iio_device_unregister(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 __devexit int ad799x_remove(struct i2c_client *client)
+{
+	struct ad799x_state *st = i2c_get_clientdata(client);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	if (client->irq > 0 && st->chip_info->monitor_mode)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	ad799x_uninitialize_ring(indio_dev->ring);
+	ad799x_ring_cleanup(indio_dev);
+	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 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@blackfin.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/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c
new file mode 100644
index 0000000..9179c1c
--- /dev/null
+++ b/drivers/staging/iio/adc/ad799x_ring.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2010 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.
+ *
+ * ad799x_ring.c
+ */
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/bitops.h>
+
+#include "../iio.h"
+#include "../ring_generic.h"
+#include "../ring_sw.h"
+#include "../trigger.h"
+#include "../sysfs.h"
+
+#include "ad799x.h"
+
+int ad799x_single_channel_from_ring(struct ad799x_state *st, long mask)
+{
+	unsigned long numvals;
+	int count = 0, ret;
+	u16 *ring_data;
+	if (!(st->indio_dev->scan_mask & mask)) {
+		ret = -EBUSY;
+		goto error_ret;
+	}
+	numvals = st->indio_dev->scan_count;
+
+	ring_data = kmalloc(numvals*2, GFP_KERNEL);
+	if (ring_data == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	ret = st->indio_dev->ring->access.read_last(st->indio_dev->ring,
+						(u8 *) ring_data);
+	if (ret)
+		goto error_free_ring_data;
+	/* Need a count of channels prior to this one */
+	mask >>= 1;
+	while (mask) {
+		if (mask & st->indio_dev->scan_mask)
+			count++;
+		mask >>= 1;
+	}
+
+	ret = be16_to_cpu(ring_data[count]) & 0xFFF;
+
+error_free_ring_data:
+	kfree(ring_data);
+error_ret:
+	return ret;
+}
+
+/**
+ * ad799x_ring_preenable() setup the parameters of the ring before enabling
+ *
+ * The complex nature of the setting of the nuber of bytes per datum is due
+ * to this driver currently ensuring that the timestamp is stored at an 8
+ * byte boundary.
+ **/
+static int ad799x_ring_preenable(struct iio_dev *indio_dev)
+{
+	struct ad799x_state *st = indio_dev->dev_data;
+	size_t d_size;
+	unsigned long numvals;
+
+	/*
+	 * Need to figure out the current mode based upon the requested
+	 * scan mask in iio_dev
+	 */
+
+	if (st->id == ad7997 || st->id == ad7998)
+		ad799x_set_scan_mode(st, st->indio_dev->scan_mask);
+
+	numvals = st->indio_dev->scan_count;
+
+	if (indio_dev->ring->access.set_bpd) {
+		d_size = numvals*2 + sizeof(s64);
+		if (d_size % 8)
+			d_size += 8 - (d_size % 8);
+		indio_dev->ring->access.set_bpd(indio_dev->ring, d_size);
+	}
+
+	return 0;
+}
+
+/**
+ * ad799x_ring_postenable() typical ring post enable
+ *
+ * Only not moved into the core for the hardware ring buffer cases
+ * that are more sophisticated.
+ **/
+static int ad799x_ring_postenable(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig == NULL)
+		return 0;
+	return iio_trigger_attach_poll_func(indio_dev->trig,
+					    indio_dev->pollfunc);
+}
+
+/**
+ * ad799x_ring_predisable() runs just prior to ring buffer being disabled
+ *
+ * Typical predisable function which ensures that no trigger events can
+ * occur before we disable the ring buffer (and hence would have no idea
+ * what to do with them)
+ **/
+static int ad799x_ring_predisable(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig)
+		return iio_trigger_dettach_poll_func(indio_dev->trig,
+						     indio_dev->pollfunc);
+	else
+		return 0;
+}
+
+/**
+ * ad799x_poll_func_th() th of trigger launched polling to ring buffer
+ *
+ * As sampling only occurs on i2c comms occuring, leave timestamping until
+ * then.  Some triggers will generate their own time stamp.  Currently
+ * there is no way of notifying them when no one cares.
+ **/
+static void ad799x_poll_func_th(struct iio_dev *indio_dev)
+{
+	struct ad799x_state *st = indio_dev->dev_data;
+
+	schedule_work(&st->poll_work);
+
+	return;
+}
+/**
+ * ad799x_poll_bh_to_ring() bh of trigger launched polling to ring buffer
+ * @work_s:	the work struct through which this was scheduled
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ * I think the one copy of this at a time was to avoid problems if the
+ * trigger was set far too high and the reads then locked up the computer.
+ **/
+static void ad799x_poll_bh_to_ring(struct work_struct *work_s)
+{
+	struct ad799x_state *st = container_of(work_s, struct ad799x_state,
+						  poll_work);
+	struct iio_dev *indio_dev = st->indio_dev;
+	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(indio_dev->ring);
+	s64 time_ns;
+	__u8 *rxbuf;
+	int b_sent;
+	size_t d_size;
+	u8 cmd;
+
+	unsigned long numvals = st->indio_dev->scan_count;
+
+	/* Ensure the timestamp is 8 byte aligned */
+	d_size = numvals*2 + sizeof(s64);
+
+	if (d_size % sizeof(s64))
+		d_size += sizeof(s64) - (d_size % sizeof(s64));
+
+	/* Ensure only one copy of this function running at a time */
+	if (atomic_inc_return(&st->protect_ring) > 1)
+		return;
+
+	/* Monitor mode prevents reading. Whilst not currently implemented
+	 * might as well have this test in here in the meantime as it does
+	 * no harm.
+	 */
+	if (numvals == 0)
+		return;
+
+	rxbuf = kmalloc(d_size,	GFP_KERNEL);
+	if (rxbuf == NULL)
+		return;
+
+	switch (st->id) {
+	case ad7991:
+	case ad7995:
+	case ad7999:
+		cmd = st->config |
+			(st->indio_dev->scan_mask << AD799X_CHANNEL_SHIFT);
+		break;
+	case ad7992:
+	case ad7993:
+	case ad7994:
+		cmd = (st->indio_dev->scan_mask <<
+			AD799X_CHANNEL_SHIFT) | AD7998_CONV_RES_REG;
+		break;
+	case ad7997:
+	case ad7998:
+		cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG;
+		break;
+	default:
+		cmd = 0;
+	}
+
+	b_sent = i2c_smbus_read_i2c_block_data(st->client,
+			cmd, numvals*2, rxbuf);
+	if (b_sent < 0)
+		goto done;
+
+	time_ns = iio_get_time_ns();
+
+	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
+
+	indio_dev->ring->access.store_to(&ring->buf, rxbuf, time_ns);
+done:
+	kfree(rxbuf);
+	atomic_dec(&st->protect_ring);
+}
+
+
+int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	struct ad799x_state *st = indio_dev->dev_data;
+	int ret = 0;
+
+	indio_dev->ring = iio_sw_rb_allocate(indio_dev);
+	if (!indio_dev->ring) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&st->indio_dev->ring->access);
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_deallocate_sw_rb;
+	}
+	/* Configure the polling function called on trigger interrupts */
+	indio_dev->pollfunc->poll_func_main = &ad799x_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+
+	/* Ring buffer functions - here trigger setup related */
+	indio_dev->ring->postenable = &ad799x_ring_postenable;
+	indio_dev->ring->preenable = &ad799x_ring_preenable;
+	indio_dev->ring->predisable = &ad799x_ring_predisable;
+	INIT_WORK(&st->poll_work, &ad799x_poll_bh_to_ring);
+
+	/* Flag that polled ring buffering is possible */
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+error_deallocate_sw_rb:
+	iio_sw_rb_free(indio_dev->ring);
+error_ret:
+	return ret;
+}
+
+void ad799x_ring_cleanup(struct iio_dev *indio_dev)
+{
+	/* ensure that the trigger has been detached */
+	if (indio_dev->trig) {
+		iio_put_trigger(indio_dev->trig);
+		iio_trigger_dettach_poll_func(indio_dev->trig,
+					      indio_dev->pollfunc);
+	}
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+void ad799x_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
+
+int ad799x_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
-- 
1.7.3.2

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices
  2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
                   ` (12 preceding siblings ...)
  2010-10-23 20:29 ` [PATCH 14/14] staging: iio: adc: new ad799x driver Mike Frysinger
@ 2010-10-24 21:09 ` Jonathan Cameron
  13 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 21:09 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Barry Song <barry.song@analog.com>
> 
For new device classes like this I'd prefer to see the
abi posted on the mailing list at an early stage as we'd
have changed a few things.  As you said in your other
email we can fix these at a later date as long as everyone
is aware the abi isn't fixed as of yet!

Lots of new abi that we will want to standardize, document
and pin down fully including some dreaded magic register
writing :) Also some current abi breakage that needs fixing
soonish.

Few nitpicks inline.

Looks like it would benefit from a quick checkpatch run.

There is stuff I'm not happy with but as you have said that
can get fixed afterwards, so subject to the changes needed
to make it build, send it on to Greg.
> Signed-off-by: Barry Song <barry.song@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7152.c |  610 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 618 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7152.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 98dd0a8..4bfe372 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -33,3 +33,10 @@ config AD7150
>  	help
>  	  Say yes here to build support for Analog Devices capacitive sensors.
>  	  (ad7150, ad7151, ad7156) Provides direct access via sysfs.
> +
> +config AD7152
> +	tristate "Analog Devices ad7152/3 capacitive sensor driver"
> +	depends on I2C
> +	help
> +	  Say yes here to build support for Analog Devices capacitive sensors.
> +	  (ad7152, ad7153) Provides direct access via sysfs.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 3148a2c..fca1eff 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -7,3 +7,4 @@ max1363-y += max1363_ring.o
>  
>  obj-$(CONFIG_MAX1363) += max1363.o
>  obj-$(CONFIG_AD7150) += ad7150.o
> +obj-$(CONFIG_AD7152) += ad7152.o
> diff --git a/drivers/staging/iio/adc/ad7152.c b/drivers/staging/iio/adc/ad7152.c
> new file mode 100644
> index 0000000..3612350
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7152.c
> @@ -0,0 +1,610 @@
> +/*
> + * AD7152 capacitive sensor driver supporting AD7152/3
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/i2c.h>
> +#include <linux/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD7152 registers definition
> + */
> +
> +#define AD7152_STATUS              0
> +#define AD7152_STATUS_RDY1         (1 << 0)
> +#define AD7152_STATUS_RDY2         (1 << 1)
> +#define AD7152_CH1_DATA_HIGH       1
> +#define AD7152_CH1_DATA_LOW        2
> +#define AD7152_CH2_DATA_HIGH       3
> +#define AD7152_CH2_DATA_LOW        4
> +#define AD7152_CH1_OFFS_HIGH       5
> +#define AD7152_CH1_OFFS_LOW        6
> +#define AD7152_CH2_OFFS_HIGH       7
> +#define AD7152_CH2_OFFS_LOW        8
> +#define AD7152_CH1_GAIN_HIGH       9
> +#define AD7152_CH1_GAIN_LOW        10
> +#define AD7152_CH1_SETUP           11
> +#define AD7152_CH2_GAIN_HIGH       12
> +#define AD7152_CH2_GAIN_LOW        13
> +#define AD7152_CH2_SETUP           14
> +#define AD7152_CFG                 15
> +#define AD7152_RESEVERD            16
> +#define AD7152_CAPDAC_POS          17
> +#define AD7152_CAPDAC_NEG          18
> +#define AD7152_CFG2                26
> +
> +#define AD7152_MAX_CONV_MODE       6
> +
> +/*
> + * struct ad7152_chip_info - chip specifc information
> + */
> +
> +struct ad7152_chip_info {
> +	const char *name;
> +	struct i2c_client *client;
> +	struct iio_dev *indio_dev;
> +	u16 ch1_offset;     /* Channel 1 offset calibration coefficient */
> +	u16 ch1_gain;       /* Channel 1 gain coefficient */
> +	u8  ch1_setup;
> +	u16 ch2_offset;     /* Channel 2 offset calibration coefficient */
> +	u16 ch2_gain;       /* Channel 1 gain coefficient */
> +	u8  ch2_setup;
Checkpatch overlong line.
> +	u8  filter_rate_setup; /* Capacitive channel digital filter setup; conversion time/update rate setup per channel */
> +	char *conversion_mode;
> +};
> +
> +struct ad7152_conversion_mode {
> +	char *name;
> +	u8 reg_cfg;
> +};
> +
My usual comment on this sort of mode by string option is that it doesn't
generalize.  If we can possibly blugeon things into a form that does
I will be much happier. This matters a lot to userspace code writers.
> +struct ad7152_conversion_mode ad7152_conv_mode_table[AD7152_MAX_CONV_MODE] = {
> +	{ "idle", 0 },
Guessing this means off?  Should probably be handled internally.  If the read
doesn't care, then it shouldn't be enabled.
> +	{ "continuous-conversion", 1 },
Possibly only enable it event that needs it is turned on?
> +	{ "single-conversion", 2 },
Typical one shot mode.  Should be written on demmand (by all means cache
whether it is already in the right mode)
> +	{ "power-down", 3 },
Separate attr for power down.

> +	{ "offset-calibration", 5 },
> +	{ "gain-calibration", 6 },
These look fun. No idea how these should be described ;)
> +};
> +
> +/*
> + * ad7152 register access by I2C
> + */
> +
> +static int ad7152_i2c_read(struct ad7152_chip_info *chip, u8 reg, u8 *data, int len)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
> +
> +	ret = i2c_master_send(client, &reg, 1);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C write error\n");
> +		return ret;
> +	}
> +
> +	ret = i2c_master_recv(client, data, len);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read error\n");
pointless return ret
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ad7152_i2c_write(struct ad7152_chip_info *chip, u8 reg, u8 data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
no need to initialize ret
> +
> +	u8 tx[2] = {
> +		reg,
> +		data,
> +	};
> +
> +	ret = i2c_master_send(client, tx, 2);
> +	if (ret < 0)
> +		dev_err(&client->dev, "I2C write error\n");
> +
> +	return ret;
> +}
> +
> +/*
> + * sysfs nodes
> + */
> +
Those of the following that generalize should move out into a generic header.

> +#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
> +	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
standard form is conversion_mode_available (makes processing in userspace easy ;)
> +#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
> +	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
I'm not keen on this for reasons stated above.

> +#define IIO_DEV_ATTR_CH1_OFFSET(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(ch1_offset, _mode, _show, _store, 0)
Lets define a 'type' for capcitance measurement. 'capacitance' seems
like a sensible option for me.  Base unit, Farads.
Hence this becomes capacitance0_offset (we index from 0, though don't
really care on that as long as you are consistent - userspace should
be able to cope as it's common for particular channels to be missing
on some devices)

> +#define IIO_DEV_ATTR_CH2_OFFSET(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(ch2_offset, _mode, _show, _store, 0)
> +#define IIO_DEV_ATTR_CH1_GAIN(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(ch1_gain, _mode, _show, _store, 0)
capacitance0_scale if to be applied by userspace to end up in farads or
capacitance0_calibscale if it is an internal gain setting applied on the
device and that doesn't need to be applied by userspace to get to Farads.
> +#define IIO_DEV_ATTR_CH2_GAIN(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(ch2_gain, _mode, _show, _store, 0)
> +#define IIO_DEV_ATTR_CH1_VALUE(_show)		\
> +	IIO_DEVICE_ATTR(ch1_value, S_IRUGO, _show, NULL, 0)
convention is <type>[m]_raw if it's not in si units (or _input if it is).
capacitance0_raw
> +#define IIO_DEV_ATTR_CH2_VALUE(_show)		\
> +	IIO_DEVICE_ATTR(ch2_value, S_IRUGO, _show, NULL, 0)
capacitance1_raw
> +#define IIO_DEV_ATTR_CH1_SETUP(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(ch1_setup, _mode, _show, _store, 0)
This smacks of magic value. Really needs to be on the todo list
to clean this up to some 'standard' interface.  Without the datasheet
I have no idea what these magic values do.
> +#define IIO_DEV_ATTR_CH2_SETUP(_mode, _show, _store)              \
> +	IIO_DEVICE_ATTR(ch2_setup, _mode, _show, _store, 0)
> +#define IIO_DEV_ATTR_FILTER_RATE_SETUP(_mode, _show, _store)              \
> +	IIO_DEVICE_ATTR(filter_rate_setup, _mode, _show, _store, 0)
Filters are one area we realy don't have a clean abi for.  This might actually
be our first driver to support them (though quite a few devices we have drivers
for do).  Abi needs discussion at somepoint soon.  May be rather fiddly to get
right, so beware abi may not be stable for a while.
> +
> +static ssize_t ad7152_show_conversion_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int i;
> +	int len = 0;
> +
> +	for (i = 0; i < AD7152_MAX_CONV_MODE; i++)
> +		len += sprintf(buf + len, "%s\n", ad7152_conv_mode_table[i].name);
space rather than newline please.

> +
> +	return len;
> +}
> +
> +static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad7152_show_conversion_modes);
> +
> +static ssize_t ad7152_show_ch1_value(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	u8 data[2];
> +
> +	ad7152_i2c_read(chip, AD7152_CH1_DATA_HIGH, data, 2);
> +	return sprintf(buf, "%d\n", ((int)data[0] << 8) | data[1]);
> +}
> +
> +static IIO_DEV_ATTR_CH1_VALUE(ad7152_show_ch1_value);
> +
> +static ssize_t ad7152_show_ch2_value(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	u8 data[2];
> +
> +	ad7152_i2c_read(chip, AD7152_CH2_DATA_HIGH, data, 2);
> +	return sprintf(buf, "%d\n", ((int)data[0] << 8) | data[1]);
> +}
> +
> +static IIO_DEV_ATTR_CH2_VALUE(ad7152_show_ch2_value);
> +
> +static ssize_t ad7152_show_conversion_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%s\n", chip->conversion_mode);
> +}
> +
> +static ssize_t ad7152_store_conversion_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	u8 cfg;
> +	int i;
> +
> +	ad7152_i2c_read(chip, AD7152_CFG, &cfg, 1);
> +
> +	for (i = 0; i < AD7152_MAX_CONV_MODE; i++) {
superfluous brackets.
> +		if (strncmp(buf, ad7152_conv_mode_table[i].name,
> +				strlen(ad7152_conv_mode_table[i].name) - 1) == 0) {
> +			chip->conversion_mode = ad7152_conv_mode_table[i].name;
> +			cfg |= 0x18 | ad7152_conv_mode_table[i].reg_cfg;
> +			ad7152_i2c_write(chip, AD7152_CFG, cfg);
> +			return len;
> +		}
> +	}
> +
> +	dev_err(dev, "not supported conversion mode\n");
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
> +		ad7152_show_conversion_mode,
> +		ad7152_store_conversion_mode);
> +
> +static ssize_t ad7152_show_ch1_offset(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->ch1_offset);
> +}
> +
> +static ssize_t ad7152_store_ch1_offset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad7152_i2c_write(chip, AD7152_CH1_OFFS_HIGH, data >> 8);
> +		ad7152_i2c_write(chip, AD7152_CH1_OFFS_LOW, data);
> +		chip->ch1_offset = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CH1_OFFSET(S_IRUGO | S_IWUSR,
> +		ad7152_show_ch1_offset,
> +		ad7152_store_ch1_offset);
> +
> +static ssize_t ad7152_show_ch2_offset(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->ch2_offset);
> +}
Can probably save a few functions round here easily enough.
> +
> +static ssize_t ad7152_store_ch2_offset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad7152_i2c_write(chip, AD7152_CH2_OFFS_HIGH, data >> 8);
> +		ad7152_i2c_write(chip, AD7152_CH2_OFFS_LOW, data);
> +		chip->ch2_offset = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CH2_OFFSET(S_IRUGO | S_IWUSR,
> +		ad7152_show_ch2_offset,
> +		ad7152_store_ch2_offset);
> +
> +static ssize_t ad7152_show_ch1_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->ch1_gain);
> +}
> +
> +static ssize_t ad7152_store_ch1_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad7152_i2c_write(chip, AD7152_CH1_GAIN_HIGH, data >> 8);
> +		ad7152_i2c_write(chip, AD7152_CH1_GAIN_LOW, data);
> +		chip->ch1_gain = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CH1_GAIN(S_IRUGO | S_IWUSR,
> +		ad7152_show_ch1_gain,
> +		ad7152_store_ch1_gain);
> +
> +static ssize_t ad7152_show_ch2_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->ch2_gain);
> +}
> +
> +static ssize_t ad7152_store_ch2_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad7152_i2c_write(chip, AD7152_CH2_GAIN_HIGH, data >> 8);
> +		ad7152_i2c_write(chip, AD7152_CH2_GAIN_LOW, data);
> +		chip->ch2_gain = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CH2_GAIN(S_IRUGO | S_IWUSR,
> +		ad7152_show_ch2_gain,
> +		ad7152_store_ch2_gain);
> +

Err, no to this as an interface.  Fine for initial bashing, but
we aren't going to keep something this 'magic' in here long term.
> +static ssize_t ad7152_show_ch1_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%02x\n", chip->ch1_setup);
> +}
> +
> +static ssize_t ad7152_store_ch1_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x100)) {
> +		ad7152_i2c_write(chip, AD7152_CH1_SETUP, data);
> +		chip->ch1_setup = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CH1_SETUP(S_IRUGO | S_IWUSR,
> +		ad7152_show_ch1_setup,
> +		ad7152_store_ch1_setup);
> +
> +static ssize_t ad7152_show_ch2_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%02x\n", chip->ch2_setup);
> +}
> +
> +static ssize_t ad7152_store_ch2_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x100)) {
> +		ad7152_i2c_write(chip, AD7152_CH2_SETUP, data);
> +		chip->ch2_setup = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CH2_SETUP(S_IRUGO | S_IWUSR,
> +		ad7152_show_ch2_setup,
> +		ad7152_store_ch2_setup);
> +
Again, fine for initial bashing, but this ain't going to generalize
unless all device use the same magic ;)
> +static ssize_t ad7152_show_filter_rate_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%02x\n", chip->filter_rate_setup);
> +}
> +
> +static ssize_t ad7152_store_filter_rate_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x100)) {
> +		ad7152_i2c_write(chip, AD7152_CFG2, data);
> +		chip->filter_rate_setup = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_FILTER_RATE_SETUP(S_IRUGO | S_IWUSR,
> +		ad7152_show_filter_rate_setup,
> +		ad7152_store_filter_rate_setup);
> +
> +static ssize_t ad7152_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7152_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7152_show_name, NULL, 0);
> +
comments on these are alongside the macros above.
> +static struct attribute *ad7152_attributes[] = {
> +	&iio_dev_attr_available_conversion_modes.dev_attr.attr,
> +	&iio_dev_attr_conversion_mode.dev_attr.attr,
> +	&iio_dev_attr_ch1_gain.dev_attr.attr,
> +	&iio_dev_attr_ch2_gain.dev_attr.attr,
> +	&iio_dev_attr_ch1_offset.dev_attr.attr,
> +	&iio_dev_attr_ch2_offset.dev_attr.attr,
> +	&iio_dev_attr_ch1_value.dev_attr.attr,
> +	&iio_dev_attr_ch2_value.dev_attr.attr,
> +	&iio_dev_attr_ch1_setup.dev_attr.attr,
> +	&iio_dev_attr_ch2_setup.dev_attr.attr,
> +	&iio_dev_attr_filter_rate_setup.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7152_attribute_group = {
> +	.attrs = ad7152_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit ad7152_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	int ret = 0;
> +	struct ad7152_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +	if (chip == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	/* this is only used for device removal purposes */
> +	i2c_set_clientdata(client, chip);
> +
> +	chip->client = client;
> +	chip->name = id->name;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	/* Echipabilish that the iio_dev is a child of the i2c device */
> +	chip->indio_dev->dev.parent = &client->dev;
> +	chip->indio_dev->attrs = &ad7152_attribute_group;
> +	chip->indio_dev->dev_data = (void *)(chip);
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	dev_err(&client->dev, "%s capacitive sensor registered\n", id->name);
> +
> +	return 0;
> +
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit ad7152_remove(struct i2c_client *client)
> +{
> +	struct ad7152_chip_info *chip = i2c_get_clientdata(client);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ad7152_id[] = {
> +	{ "ad7152", 0 },
> +	{ "ad7153", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ad7152_id);
> +
> +static struct i2c_driver ad7152_driver = {
> +	.driver = {
> +		.name = "ad7152",
> +	},
> +	.probe = ad7152_probe,
> +	.remove = __devexit_p(ad7152_remove),
> +	.id_table = ad7152_id,
> +};
> +
> +static __init int ad7152_init(void)
> +{
> +	return i2c_add_driver(&ad7152_driver);
> +}
> +
> +static __exit void ad7152_exit(void)
> +{
> +	i2c_del_driver(&ad7152_driver);
> +}
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices ad7152/3 capacitive sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad7152_init);
> +module_exit(ad7152_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Device-drivers-devel] [PATCH 14/14] staging: iio: adc: new ad799x driver
  2010-10-23 20:29 ` [PATCH 14/14] staging: iio: adc: new ad799x driver Mike Frysinger
@ 2010-10-24 21:14   ` Mike Frysinger
  2010-10-24 22:55   ` Jonathan Cameron
  1 sibling, 0 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-24 21:14 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel

i guess this one is already merged into staging-next, so it can be ignored
-mike

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [Device-drivers-devel] [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices
  2010-10-23 20:29 ` [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices Mike Frysinger
@ 2010-10-24 21:14   ` Mike Frysinger
  2010-10-24 22:21   ` Jonathan Cameron
  1 sibling, 0 replies; 48+ messages in thread
From: Mike Frysinger @ 2010-10-24 21:14 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel

i guess this one is already merged into staging-next, so it can be ignored
-mike

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices
  2010-10-23 20:29 ` [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
@ 2010-10-24 21:32   ` Jonathan Cameron
  0 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 21:32 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Sonic Zhang

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Sonic Zhang <sonic.zhang@analog.com>
> 
All sorts of abi fun and games.  Where did v come from as a channel
designation?  The event code stuff has confused event registration
with value setting.  This may all make sense for these devices
but it needs some documentation!

Checkpatch if time.

We have sysfs based scan modes in here. This almost always
means that a buffer mode is really needed. We can add that later.

More documentation would have helped in this one.  I've no idea
what noise_delay is (and sorry, not time to datasheet dive)

Please check the uses of swab16.

Lots of new interfaces and bits that need cleaning up, but
we are in staging, so send this one on as long as it builds.
> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7291.c | 1039 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1047 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7291.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 4bfe372..a4c41dc 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -40,3 +40,10 @@ config AD7152
>  	help
>  	  Say yes here to build support for Analog Devices capacitive sensors.
>  	  (ad7152, ad7153) Provides direct access via sysfs.
> +
> +config AD7291
> +	tristate "Analog Devices AD7291 temperature sensor driver"
> +	depends on I2C
> +	help
> +	  Say yes here to build support for Analog Devices AD7291
> +	  temperature sensors.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index fca1eff..7d8ac9a 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -8,3 +8,4 @@ max1363-y += max1363_ring.o
>  obj-$(CONFIG_MAX1363) += max1363.o
>  obj-$(CONFIG_AD7150) += ad7150.o
>  obj-$(CONFIG_AD7152) += ad7152.o
> +obj-$(CONFIG_AD7291) += ad7291.o
> diff --git a/drivers/staging/iio/adc/ad7291.c b/drivers/staging/iio/adc/ad7291.c
> new file mode 100644
> index 0000000..116248f
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7291.c
> @@ -0,0 +1,1039 @@
> +/*
> + * AD7291 digital temperature sensor driver supporting AD7291
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/i2c.h>
> +#include <linux/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD7291 registers definition
> + */
> +#define AD7291_COMMAND			0
> +#define AD7291_VOLTAGE			1
> +#define AD7291_T_SENSE			2
> +#define AD7291_T_AVERAGE		3
> +#define AD7291_VOLTAGE_LIMIT_BASE	4
> +#define AD7291_VOLTAGE_LIMIT_COUNT	8
> +#define AD7291_T_SENSE_HIGH		0x1c
> +#define AD7291_T_SENSE_LOW		0x1d
> +#define AD7291_T_SENSE_HYST		0x1e
> +#define AD7291_VOLTAGE_ALERT_STATUS	0x1f
> +#define AD7291_T_ALERT_STATUS		0x20
> +
> +/*
> + * AD7291 command
> + */
> +#define AD7291_AUTOCYCLE		0x1
> +#define AD7291_RESET			0x2
> +#define AD7291_ALART_CLEAR		0x4
> +#define AD7291_ALART_POLARITY		0x8
> +#define AD7291_EXT_REF			0x10
> +#define AD7291_NOISE_DELAY		0x20
> +#define AD7291_T_SENSE_MASK		0x40
> +#define AD7291_VOLTAGE_MASK		0xff00
> +#define AD7291_VOLTAGE_OFFSET		0x8
> +
> +/*
> + * AD7291 value masks
> + */
> +#define AD7291_CHANNEL_MASK		0xf000
> +#define AD7291_VALUE_MASK		0xfff
> +#define AD7291_T_VALUE_SIGN		0x400
> +#define AD7291_T_VALUE_FLOAT_OFFSET	2
> +#define AD7291_T_VALUE_FLOAT_MASK	0x2
> +
> +/*
> + * struct ad7291_chip_info - chip specifc information
> + */
> +
> +struct ad7291_chip_info {
> +	const char *name;
> +	struct i2c_client *client;
> +	struct iio_dev *indio_dev;
> +	struct work_struct thresh_work;
> +	s64 last_timestamp;
> +	u16 command;
> +	u8  channels;	/* Active voltage channels */
> +};
> +
> +/*
> + * struct ad7291_chip_info - chip specifc information
> + */
> +
I don't think this is used?
> +struct ad7291_limit_regs {
> +	u16	data_high;
> +	u16	data_low;
> +	u16	hysteresis;
> +};
> +
> +/*
> + * ad7291 register access by I2C
> + */
> +static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data)
> +{
> +	struct i2c_client *client = chip->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 ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
> +
Is that swab a general thing?  Looks like a cpu to smbus ordering conversion.

> +	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
> +	if (ret < 0)
> +		dev_err(&client->dev, "I2C write error\n");
> +
> +	return ret;
> +}
> +
> +/* Returns negative errno, or else the number of words read. */
> +static int ad7291_i2c_read_data(struct ad7291_chip_info *chip, u8 reg, u16 *data)
> +{
> +	struct i2c_client *client = chip->client;
> +	u8 commands[4];
> +	int ret = 0;
> +	int i, count;
> +
> +	if (reg == AD7291_T_SENSE || reg == AD7291_T_AVERAGE)
> +		count = 2;
> +	else if (reg == AD7291_VOLTAGE) {
> +		if (!chip->channels) {
> +			dev_err(&client->dev, "No voltage channel is selected.\n");
> +			return -EINVAL;
> +		}
> +		count = 2 + chip->channels * 2;
> +	} else {
> +		dev_err(&client->dev, "I2C wrong data register\n");
> +		return -EINVAL;
> +	}
> +
> +	commands[0] = 0;
> +	commands[1] = (chip->command >> 8) & 0xff;
> +	commands[2] = chip->command & 0xff;
> +	commands[3] = reg;
Can this init move up to initial definition of commands?  Cleaner code that way.
> +
> +	ret = i2c_master_send(client, commands, 4);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C master send error\n");
> +		return ret;
> +	}
> +
> +	ret = i2c_master_recv(client, (u8 *)data, count);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C master receive error\n");
> +		return ret;
> +	}
> +	ret >>= 2;
> +
> +	for (i = 0; i < ret; i++)
> +		data[i] = swab16(data[i]);
> +
> +	return ret;
> +}
> +
> +static ssize_t ad7291_show_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +
> +	if (chip->command & AD7291_AUTOCYCLE)
> +		return sprintf(buf, "autocycle\n");
> +	else
> +		return sprintf(buf, "command\n");
> +}
> +
> +static ssize_t ad7291_store_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	int ret;
> +
> +	command = chip->command & (~AD7291_AUTOCYCLE);
> +	if (strcmp(buf, "autocycle"))
> +		command |= AD7291_AUTOCYCLE;
> +
> +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->command = command;
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> +		ad7291_show_mode,
> +		ad7291_store_mode,
> +		0);
> +
> +static ssize_t ad7291_show_available_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "command\nautocycle\n");
Space separated rather than new line please.
> +}
> +
> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7291_show_available_modes, NULL, 0);
> +
> +static ssize_t ad7291_store_reset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	int ret;
> +
> +	command = chip->command | AD7291_RESET;
> +
> +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR,
> +		NULL,
> +		ad7291_store_reset,
> +		0);
> +
> +static ssize_t ad7291_show_ext_ref(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", !!(chip->command & AD7291_EXT_REF));
> +}
> +
> +static ssize_t ad7291_store_ext_ref(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	int ret;
> +
> +	command = chip->command & (~AD7291_EXT_REF);
> +	if (strcmp(buf, "1"))
> +		command |= AD7291_EXT_REF;
> +
> +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->command = command;
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR,
> +		ad7291_show_ext_ref,
> +		ad7291_store_ext_ref,
> +		0);
> +
> +static ssize_t ad7291_show_noise_delay(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", !!(chip->command & AD7291_NOISE_DELAY));
> +}
> +
> +static ssize_t ad7291_store_noise_delay(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	int ret;
> +
> +	command = chip->command & (~AD7291_NOISE_DELAY);
> +	if (strcmp(buf, "1"))
> +		command |= AD7291_NOISE_DELAY;
> +
> +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->command = command;
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(noise_delay, S_IRUGO | S_IWUSR,
> +		ad7291_show_noise_delay,
> +		ad7291_store_noise_delay,
> +		0);
> +
> +static ssize_t ad7291_show_t_sense(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	ret = ad7291_i2c_read_data(chip, AD7291_T_SENSE, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data & AD7291_T_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data = (AD7291_T_VALUE_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign,
> +		(data >> AD7291_T_VALUE_FLOAT_OFFSET),
> +		(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
> +}
> +
> +static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7291_show_t_sense, NULL, 0);
> +
> +static ssize_t ad7291_show_t_average(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	ret = ad7291_i2c_read_data(chip, AD7291_T_AVERAGE, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data & AD7291_T_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data = (AD7291_T_VALUE_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign,
> +		(data >> AD7291_T_VALUE_FLOAT_OFFSET),
> +		(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
This is where the offset scale pair of attributes are your friends.
Allow the complexity to move into userspace.
> +}
> +
> +static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7291_show_t_average, NULL, 0);
> +
> +static ssize_t ad7291_show_voltage(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 data[AD7291_VOLTAGE_LIMIT_COUNT];
> +	int i, size, ret;
> +
> +	ret = ad7291_i2c_read_data(chip, AD7291_VOLTAGE, data);
> +	if (ret)
> +		return -EIO;
> +
> +	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
> +		if (chip->command & (AD7291_T_SENSE_MASK << i)) {
> +			ret = sprintf(buf, "channel[%d]=%d\n", i,
> +					data[i] & AD7291_VALUE_MASK);
If you want to do scan's please use the buffer interface.
Doing it via sysfs just doesn't generalize well.
> +			if (ret < 0)
> +				break;
> +			buf += ret;
> +			size += ret;
> +		}
> +	}
> +
> +	return size;
> +}
> +
> +static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7291_show_voltage, NULL, 0);
> +
Not a chance on this interface.  We very carefully worked out how to handle
these sort of 'scanning' devices.  See max1363 or Michael's Analog adc
drivers.
> +static ssize_t ad7291_show_channel_mask(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%x\n", (chip->command & AD7291_VOLTAGE_MASK) >>
> +			AD7291_VOLTAGE_OFFSET);
> +}
> +
> +static ssize_t ad7291_store_channel_mask(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	unsigned long data;
> +	int i, ret;
> +
> +	ret = strict_strtoul(buf, 16, &data);
> +	if (ret || data > 0xff)
> +		return -EINVAL;
> +
> +	command = chip->command & (~AD7291_VOLTAGE_MASK);
> +	command |= data << AD7291_VOLTAGE_OFFSET;
> +
> +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->command = command;
> +
> +	for (i = 0, chip->channels = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
> +		if (chip->command & (AD7291_T_SENSE_MASK << i))
> +			chip->channels++;
> +	}
spurious brackets.
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR,
> +		ad7291_show_channel_mask,
> +		ad7291_store_channel_mask,
> +		0);
> +
> +static ssize_t ad7291_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7291_show_name, NULL, 0);
> +
As ever, I'm fussy about the abi.
> +static struct attribute *ad7291_attributes[] = {
Probably want to break mode up and pretty much hide it's existence.
I don't even know what control is!
> +	&iio_dev_attr_available_modes.dev_attr.attr,
> +	&iio_dev_attr_mode.dev_attr.attr,
> +	&iio_dev_attr_reset.dev_attr.attr,

This one needs some explanation.  Is it something that would be
better handled as a regulator?
> +	&iio_dev_attr_ext_ref.dev_attr.attr,
> +	&iio_dev_attr_noise_delay.dev_attr.attr,
no idea.
> +	&iio_dev_attr_t_sense.dev_attr.attr,
Need documetation. I suspect I won't agree with the naming though.
> +	&iio_dev_attr_t_average.dev_attr.attr,

This appears to be a multichannel read. Usually implies that the
device really needs a buffer based interface.
> +	&iio_dev_attr_voltage.dev_attr.attr,

Will get cleaned up in a move to a buffer based interface.
> +	&iio_dev_attr_channel_mask.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7291_attribute_group = {
> +	.attrs = ad7291_attributes,
> +};
> +
> +/*
> + * temperature bound events
> + */
> +
> +#define IIO_EVENT_CODE_AD7291_T_SENSE_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
> +#define IIO_EVENT_CODE_AD7291_T_SENSE_LOW    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
> +#define IIO_EVENT_CODE_AD7291_T_AVG_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
> +#define IIO_EVENT_CODE_AD7291_T_AVG_LOW    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 4)
> +#define IIO_EVENT_CODE_AD7291_VOLTAGE_BASE    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 5)
We'll be wanting to move a lot of these to general values rather than 
device specific ones.
> +
> +static void ad7291_interrupt_bh(struct work_struct *work_s)
> +{
> +	struct ad7291_chip_info *chip =
> +		container_of(work_s, struct ad7291_chip_info, thresh_work);
> +	u16 t_status, v_status;
> +	u16 command;
> +	int i;
> +
> +	if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status))
> +		return;
> +
> +	if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status))
> +		return;
> +
> +	if (!(t_status || v_status))
> +		return;
> +
> +	command = chip->command | AD7291_ALART_CLEAR;
> +	ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +
> +	command = chip->command & ~AD7291_ALART_CLEAR;
> +	ad7291_i2c_write(chip, AD7291_COMMAND, command);
> +
> +	enable_irq(chip->client->irq);
> +
> +	for (i = 0; i < 4; i++) {
> +		if (t_status & (1 << i))
> +			iio_push_event(chip->indio_dev, 0,
> +				IIO_EVENT_CODE_AD7291_T_SENSE_HIGH + i,
> +				chip->last_timestamp);
> +	}
> +
> +	for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT*2; i++) {
> +		if (v_status & (1 << i))
> +			iio_push_event(chip->indio_dev, 0,
> +				IIO_EVENT_CODE_AD7291_VOLTAGE_BASE + i,
> +				chip->last_timestamp);
> +	}
> +}
> +
> +static int ad7291_interrupt(struct iio_dev *dev_info,
> +		int index,
> +		s64 timestamp,
> +		int no_test)
> +{
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +
> +	chip->last_timestamp = timestamp;
> +	schedule_work(&chip->thresh_work);
> +
> +	return 0;
> +}
> +
> +IIO_EVENT_SH(ad7291, &ad7291_interrupt);
> +
> +static inline ssize_t ad7291_show_t_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	ret = ad7291_i2c_read(chip, bound_reg, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	data &= AD7291_VALUE_MASK;
> +	if (data & AD7291_T_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data = (AD7291_T_VALUE_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign,
> +			data >> AD7291_T_VALUE_FLOAT_OFFSET,
> +			(data & AD7291_T_VALUE_FLOAT_MASK) * 25);
> +}
> +
> +static inline ssize_t ad7291_set_t_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	long tmp1, tmp2;
> +	u16 data;
> +	char *pos;
> +	int ret;
> +
> +	pos = strchr(buf, '.');
> +
> +	ret = strict_strtol(buf, 10, &tmp1);
> +
> +	if (ret || tmp1 > 127 || tmp1 < -128)
> +		return -EINVAL;
> +
> +	if (pos) {
> +		len = strlen(pos);
> +		if (len > AD7291_T_VALUE_FLOAT_OFFSET)
> +			len = AD7291_T_VALUE_FLOAT_OFFSET;
> +		pos[len] = 0;
> +		ret = strict_strtol(pos, 10, &tmp2);
> +
> +		if (!ret)
> +			tmp2 = (tmp2 / 25) * 25;
> +	}
> +
> +	if (tmp1 < 0)
> +		data = (u16)(-tmp1);
> +	else
> +		data = (u16)tmp1;
> +	data = (data << AD7291_T_VALUE_FLOAT_OFFSET) |
> +		(tmp2 & AD7291_T_VALUE_FLOAT_MASK);
> +	if (tmp1 < 0)
> +		/* convert positive value to supplyment */
> +		data = (AD7291_T_VALUE_SIGN << 1) - data;
> +
> +	ret = ad7291_i2c_write(chip, bound_reg, data);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static ssize_t ad7291_show_t_sense_high(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7291_show_t_bound(dev, attr,
> +			AD7291_T_SENSE_HIGH, buf);
> +}
> +
Use the dataelements of the attribute and these can be trivially combined
(as far as I can see)
> +static inline ssize_t ad7291_set_t_sense_high(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7291_set_t_bound(dev, attr,
> +			AD7291_T_SENSE_HIGH, buf, len);
> +}
> +
> +static ssize_t ad7291_show_t_sense_low(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7291_show_t_bound(dev, attr,
> +			AD7291_T_SENSE_LOW, buf);
> +}
> +
> +static inline ssize_t ad7291_set_t_sense_low(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7291_set_t_bound(dev, attr,
> +			AD7291_T_SENSE_LOW, buf, len);
> +}
> +
> +static ssize_t ad7291_show_t_sense_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7291_show_t_bound(dev, attr,
> +			AD7291_T_SENSE_HYST, buf);
> +}
> +
> +static inline ssize_t ad7291_set_t_sense_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7291_set_t_bound(dev, attr,
> +			AD7291_T_SENSE_HYST, buf, len);
> +}
> +
> +static inline ssize_t ad7291_show_v_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	int ret;
> +
> +	if (bound_reg < AD7291_VOLTAGE_LIMIT_BASE ||
> +		bound_reg >= AD7291_VOLTAGE_LIMIT_BASE +
> +		AD7291_VOLTAGE_LIMIT_COUNT)
> +		return -EINVAL;
> +
> +	ret = ad7291_i2c_read(chip, bound_reg, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	data &= AD7291_VALUE_MASK;
> +
> +	return sprintf(buf, "%d\n", data);
> +}
> +
> +static inline ssize_t ad7291_set_v_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7291_chip_info *chip = dev_info->dev_data;
> +	unsigned long value;
> +	u16 data;
> +	int ret;
> +
> +	if (bound_reg < AD7291_VOLTAGE_LIMIT_BASE ||
> +		bound_reg >= AD7291_VOLTAGE_LIMIT_BASE +
> +		AD7291_VOLTAGE_LIMIT_COUNT)
> +		return -EINVAL;
> +
> +	ret = strict_strtoul(buf, 10, &value);
> +
> +	if (ret || value >= 4096)
> +		return -EINVAL;
> +
> +	data = (u16)value;
> +	ret = ad7291_i2c_write(chip, bound_reg, data);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static int ad7291_get_voltage_limit_regs(const char *channel)
> +{
> +	int index;
> +
> +	if (strlen(channel) < 3 && channel[0] != 'v')
> +		return -EINVAL;
> +
> +	index = channel[1] - '0';
> +	if (index >= AD7291_VOLTAGE_LIMIT_COUNT)
> +		return -EINVAL;
> +
> +	return index;
> +}
> +
I think a lot of these functions could be easily combined into
one...
> +static ssize_t ad7291_show_voltage_high(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int regs;
> +
> +	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
> +
> +	if (regs < 0)
> +		return regs;
> +
> +	return ad7291_show_t_bound(dev, attr, regs, buf);
> +}
> +
> +static inline ssize_t ad7291_set_voltage_high(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	int regs;
> +
> +	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
> +
> +	if (regs < 0)
> +		return regs;
> +
> +	return ad7291_set_t_bound(dev, attr, regs, buf, len);
> +}
> +
> +static ssize_t ad7291_show_voltage_low(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int regs;
> +
> +	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
> +
> +	if (regs < 0)
> +		return regs;
> +
> +	return ad7291_show_t_bound(dev, attr, regs+1, buf);
> +}
> +
> +static inline ssize_t ad7291_set_voltage_low(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	int regs;
> +
> +	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
> +
> +	if (regs < 0)
> +		return regs;
> +
> +	return ad7291_set_t_bound(dev, attr, regs+1, buf, len);
> +}
> +
> +static ssize_t ad7291_show_voltage_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int regs;
> +
> +	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
> +
> +	if (regs < 0)
> +		return regs;
> +
> +	return ad7291_show_t_bound(dev, attr, regs+2, buf);
> +}
> +
> +static inline ssize_t ad7291_set_voltage_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	int regs;
> +
> +	regs = ad7291_get_voltage_limit_regs(attr->attr.name);
> +
> +	if (regs < 0)
> +		return regs;
> +
> +	return ad7291_set_t_bound(dev, attr, regs+2, buf, len);
> +}
> +
Where did t come from? And what is sense about?
These need bashing into the standard forms (perhaps with additions).

Things have gotten confused. The intent is that these macros use
files that simply turn the event on or off.  The value is typically
separate (gets confusing otherwise as many devices share a
single value across multiple channels).
> +IIO_EVENT_ATTR_SH(t_sense_high, iio_event_ad7291,
> +		ad7291_show_t_sense_high, ad7291_set_t_sense_high, 0);
> +IIO_EVENT_ATTR_SH(t_sense_low, iio_event_ad7291,
> +		ad7291_show_t_sense_low, ad7291_set_t_sense_low, 0);
> +IIO_EVENT_ATTR_SH(t_sense_hyst, iio_event_ad7291,
> +		ad7291_show_t_sense_hyst, ad7291_set_t_sense_hyst, 0);
> +
Where did v come from?
> +IIO_EVENT_ATTR_SH(v0_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v0_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v0_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v1_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v1_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v1_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v2_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v2_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v2_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v3_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v3_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v3_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v4_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v4_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v4_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v5_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v5_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v5_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v6_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v6_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v6_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +IIO_EVENT_ATTR_SH(v7_high, iio_event_ad7291,
> +		ad7291_show_voltage_high, ad7291_set_voltage_high, 0);
> +IIO_EVENT_ATTR_SH(v7_low, iio_event_ad7291,
> +		ad7291_show_voltage_low, ad7291_set_voltage_low, 0);
> +IIO_EVENT_ATTR_SH(v7_hyst, iio_event_ad7291,
> +		ad7291_show_voltage_hyst, ad7291_set_voltage_hyst, 0);
> +
> +static struct attribute *ad7291_event_attributes[] = {
> +	&iio_event_attr_t_sense_high.dev_attr.attr,
> +	&iio_event_attr_t_sense_low.dev_attr.attr,
> +	&iio_event_attr_t_sense_hyst.dev_attr.attr,
> +	&iio_event_attr_v0_high.dev_attr.attr,
> +	&iio_event_attr_v0_low.dev_attr.attr,
> +	&iio_event_attr_v0_hyst.dev_attr.attr,
> +	&iio_event_attr_v1_high.dev_attr.attr,
> +	&iio_event_attr_v1_low.dev_attr.attr,
> +	&iio_event_attr_v1_hyst.dev_attr.attr,
> +	&iio_event_attr_v2_high.dev_attr.attr,
> +	&iio_event_attr_v2_low.dev_attr.attr,
> +	&iio_event_attr_v2_hyst.dev_attr.attr,
> +	&iio_event_attr_v3_high.dev_attr.attr,
> +	&iio_event_attr_v3_low.dev_attr.attr,
> +	&iio_event_attr_v3_hyst.dev_attr.attr,
> +	&iio_event_attr_v4_high.dev_attr.attr,
> +	&iio_event_attr_v4_low.dev_attr.attr,
> +	&iio_event_attr_v4_hyst.dev_attr.attr,
> +	&iio_event_attr_v5_high.dev_attr.attr,
> +	&iio_event_attr_v5_low.dev_attr.attr,
> +	&iio_event_attr_v5_hyst.dev_attr.attr,
> +	&iio_event_attr_v6_high.dev_attr.attr,
> +	&iio_event_attr_v6_low.dev_attr.attr,
> +	&iio_event_attr_v6_hyst.dev_attr.attr,
> +	&iio_event_attr_v7_high.dev_attr.attr,
> +	&iio_event_attr_v7_low.dev_attr.attr,
> +	&iio_event_attr_v7_hyst.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ad7291_event_attribute_group = {
> +	.attrs = ad7291_event_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit ad7291_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	struct ad7291_chip_info *chip;
> +	int ret = 0;
> +
> +	chip = kzalloc(sizeof(struct ad7291_chip_info), GFP_KERNEL);
> +
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	/* this is only used for device removal purposes */
> +	i2c_set_clientdata(client, chip);
> +
> +	chip->client = client;
> +	chip->name = id->name;
> +	chip->command = AD7291_NOISE_DELAY | AD7291_T_SENSE_MASK;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	chip->indio_dev->dev.parent = &client->dev;
> +	chip->indio_dev->attrs = &ad7291_attribute_group;
> +	chip->indio_dev->event_attrs = &ad7291_event_attribute_group;
> +	chip->indio_dev->dev_data = (void *)chip;
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->num_interrupt_lines = 1;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	if (client->irq > 0) {
> +		ret = iio_register_interrupt_line(client->irq,
> +				chip->indio_dev,
> +				0,
> +				client->irq_flags,
> +				chip->name);
> +		if (ret)
> +			goto error_unreg_dev;
> +
> +		/*
> +		 * The event handler list element refer to iio_event_ad7291.
> +		 * All event attributes bind to the same event handler.
> +		 * So, only register event handler once.
> +		 */
> +		iio_add_event_to_list(&iio_event_ad7291,
> +				&chip->indio_dev->interrupts[0]->ev_list);
> +
> +		INIT_WORK(&chip->thresh_work, ad7291_interrupt_bh);
> +
> +		if (client->irq_flags & IRQF_TRIGGER_LOW)
> +			chip->command |= AD7291_ALART_POLARITY;
> +	}
> +
> +	ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command);
> +	if (ret) {
> +		ret = -EIO;
> +		goto error_unreg_irq;
> +	}
> +
> +	dev_info(&client->dev, "%s temperature sensor registered.\n",
> +			 id->name);
> +
> +	return 0;
> +
> +error_unreg_irq:
> +	iio_unregister_interrupt_line(chip->indio_dev, 0);
> +error_unreg_dev:
> +	iio_device_unregister(chip->indio_dev);
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +
> +	return ret;
> +}
> +
> +static int __devexit ad7291_remove(struct i2c_client *client)
> +{
> +	struct ad7291_chip_info *chip = i2c_get_clientdata(client);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	if (client->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	iio_free_device(chip->indio_dev);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ad7291_id[] = {
> +	{ "ad7291", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ad7291_id);
> +
> +static struct i2c_driver ad7291_driver = {
> +	.driver = {
> +		.name = "ad7291",
> +	},
> +	.probe = ad7291_probe,
> +	.remove = __devexit_p(ad7291_remove),
> +	.id_table = ad7291_id,
> +};
> +
> +static __init int ad7291_init(void)
> +{
> +	return i2c_add_driver(&ad7291_driver);
> +}
> +
> +static __exit void ad7291_exit(void)
> +{
> +	i2c_del_driver(&ad7291_driver);
> +}
> +
> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices AD7291 digital"
> +			" temperature sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad7291_init);
> +module_exit(ad7291_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 03/14] staging: iio: adc: new driver for AD7298 devices
  2010-10-23 20:29 ` [PATCH 03/14] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
@ 2010-10-24 21:49   ` Jonathan Cameron
  0 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 21:49 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Sonic Zhang

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Sonic Zhang <sonic.zhang@analog.com>
> 
For devices like this, the commit message should justify why they
should go in iio rather than hwmon. Based on a quick google, I'm
guessing the speed is the main reason.  Not many 1MSps adc's in hwmon ;)
Which one is most appopriate is down to what people are using the device
for.

Just to avoid arguements, I'd change the comments to say it's an adc
with a temperature sensor (which is afterall what the website calls it)

I'm getting lazy, so I'm not pointing out most generic stuff I've already
commented on elsewhere.

The busy wait is a bad thing. msleep please or mdelay if the precision is
needed.  Or even a oneshot interrupt?

Some error eating that could do to be cleaned up.

I'd like to see the abi breakage fixed, but the driver looks fundamentally
sound, so feel free to send onto Greg as long as it is build tested
and someone has the kit and time to do testing on any cleanup (or to
do it of course ;) I'm going to get cynical and move to reviewed by
on the drivers that break current abi spec.

Jonathan

> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7298.c |  501 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 509 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7298.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index a4c41dc..847f5f2 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -47,3 +47,10 @@ config AD7291
>  	help
>  	  Say yes here to build support for Analog Devices AD7291
>  	  temperature sensors.
> +
> +config AD7298
> +	tristate "Analog Devices AD7298 temperature sensor and ADC driver"
> +	depends on SPI
> +	help
> +	  Say yes here to build support for Analog Devices AD7298
> +	  temperature sensors and ADC.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 7d8ac9a..d0ea747 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_MAX1363) += max1363.o
>  obj-$(CONFIG_AD7150) += ad7150.o
>  obj-$(CONFIG_AD7152) += ad7152.o
>  obj-$(CONFIG_AD7291) += ad7291.o
> +obj-$(CONFIG_AD7298) += ad7298.o
> diff --git a/drivers/staging/iio/adc/ad7298.c b/drivers/staging/iio/adc/ad7298.c
> new file mode 100644
> index 0000000..1a080c9
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7298.c
> @@ -0,0 +1,501 @@
> +/*
> + * AD7298 digital temperature sensor driver supporting AD7298
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD7298 command
> + */
> +#define AD7298_PD			0x1
> +#define AD7298_T_AVG_MASK		0x2
> +#define AD7298_EXT_REF			0x4
> +#define AD7298_T_SENSE_MASK		0x20
> +#define AD7298_VOLTAGE_MASK		0x3fc0
> +#define AD7298_VOLTAGE_OFFSET		0x6
> +#define AD7298_VOLTAGE_LIMIT_COUNT	8
> +#define AD7298_REPEAT			0x40
> +#define AD7298_WRITE			0x80
> +
> +/*
> + * AD7298 value masks
> + */
> +#define AD7298_CHANNEL_MASK		0xf000
> +#define AD7298_VALUE_MASK		0xfff
> +#define AD7298_T_VALUE_SIGN		0x400
> +#define AD7298_T_VALUE_FLOAT_OFFSET	2
> +#define AD7298_T_VALUE_FLOAT_MASK	0x2
> +
> +/*
> + * struct ad7298_chip_info - chip specifc information
> + */
> +
> +struct ad7298_chip_info {
claims to only support one device, so why not hard code the name
where it is needed?
> +	const char *name;
> +	struct spi_device *spi_dev;
> +	struct iio_dev *indio_dev;
> +	u16 command;
> +	u16 busy_pin;
> +	u8  channels;	/* Active voltage channels */
> +};
> +
> +/*
> + * ad7298 register access by SPI
> + */
> +static int ad7298_spi_write(struct ad7298_chip_info *chip, u16 data)
> +{
> +	struct spi_device *spi_dev = chip->spi_dev;
> +	int ret = 0;
> +
> +	data |= AD7298_WRITE;
> +	data = cpu_to_be16(data);
> +	ret = spi_write(spi_dev, (u8 *)&data, sizeof(data));
> +	if (ret < 0)
> +		dev_err(&spi_dev->dev, "SPI write error\n");
> +
> +	return ret;
> +}
> +
> +static int ad7298_spi_read(struct ad7298_chip_info *chip, u16 mask, u16 *data)
> +{
> +	struct spi_device *spi_dev = chip->spi_dev;
> +	int ret = 0;
> +	u8 count = chip->channels;
> +	u16 command;
> +	int i;
> +
> +	if (mask & AD7298_T_SENSE_MASK) {
> +		command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_VOLTAGE_MASK);
> +		command |= AD7298_T_SENSE_MASK;
> +		count = 1;
> +	} else if (mask & AD7298_T_AVG_MASK) {
> +		command = chip->command & ~AD7298_VOLTAGE_MASK;
> +		command |= AD7298_T_SENSE_MASK | AD7298_T_AVG_MASK;
> +		count = 2;
> +	} else if (mask & AD7298_VOLTAGE_MASK) {
> +		command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_T_SENSE_MASK);
> +		count = chip->channels;
> +	}
> +
> +	ret = ad7298_spi_write(chip, chip->command);
> +	if (ret < 0) {
> +		dev_err(&spi_dev->dev, "SPI write command error\n");
> +		return ret;
> +	}
> +
> +	ret = spi_read(spi_dev, (u8 *)&command, sizeof(command));
> +	if (ret < 0) {
> +		dev_err(&spi_dev->dev, "SPI read error\n");
> +		return ret;
> +	}
> +
> +	i = 10000;
> +	while (i && gpio_get_value(chip->busy_pin)) {
> +		cpu_relax();
> +		i--;
> +	}
Please use msleep rather than this.
> +	if (!i) {
> +		dev_err(&spi_dev->dev, "Always in busy convertion.\n");
> +		return -EBUSY;
> +	}
> +
> +	for (i = 0; i < count; i++) {
> +		ret = spi_read(spi_dev, (u8 *)&data[i], sizeof(data[i]));
> +		if (ret < 0) {
> +			dev_err(&spi_dev->dev, "SPI read error\n");
> +			return ret;
> +		}
> +		*data = be16_to_cpu(data[i]);
> +	}
> +
> +	return 0;
> +}
> +
Again, please don't expose this directly. Just have the driver set
it as required for whatever is currently going on.
> +static ssize_t ad7298_show_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +
> +	if (chip->command & AD7298_REPEAT)
> +		return sprintf(buf, "repeat\n");
> +	else
> +		return sprintf(buf, "normal\n");
> +}
> +
> +static ssize_t ad7298_store_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +
> +	if (strcmp(buf, "repeat"))
Please sanity check the other mode has actually been entered.
Also, sysfs_strcmp is more reliable for this use (gets rid
of whitespace etc).
> +		chip->command |= AD7298_REPEAT;
> +	else
> +		chip->command &= (~AD7298_REPEAT);
> +
> +	return 1;
> +}
> +
> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> +		ad7298_show_mode,
> +		ad7298_store_mode,
> +		0);
> +
> +static ssize_t ad7298_show_available_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
space separated.
> +	return sprintf(buf, "normal\nrepeat\n");
> +}
> +
> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7298_show_available_modes, NULL, 0);
> +
> +static ssize_t ad7298_store_reset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	int ret;
> +
> +	command = chip->command & ~AD7298_PD;
> +
> +	ret = ad7298_spi_write(chip, command);
> +	if (ret)
  eating errors. 
> +		return -EIO;
> +
> +	command = chip->command | AD7298_PD;
> +
> +	ret = ad7298_spi_write(chip, command);
> +	if (ret)
eating errors.
> +		return -EIO;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR,
> +		NULL,
> +		ad7298_store_reset,
> +		0);
> +
> +static ssize_t ad7298_show_ext_ref(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", !!(chip->command & AD7298_EXT_REF));
> +}
Regulator subsystem perhaps?
> +
> +static ssize_t ad7298_store_ext_ref(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	u16 command;
> +	int ret;
> +
> +	command = chip->command & (~AD7298_EXT_REF);
> +	if (strcmp(buf, "1"))
sysfs_strcmp.
> +		command |= AD7298_EXT_REF;
> +
> +	ret = ad7298_spi_write(chip, command);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->command = command;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR,
> +		ad7298_show_ext_ref,
> +		ad7298_store_ext_ref,
> +		0);
> +
> +static ssize_t ad7298_show_t_sense(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	ret = ad7298_spi_read(chip, AD7298_T_SENSE_MASK, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data & AD7298_T_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data = (AD7298_T_VALUE_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign,
> +		(data >> AD7298_T_VALUE_FLOAT_OFFSET),
> +		(data & AD7298_T_VALUE_FLOAT_MASK) * 25);
> +}
> +
> +static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7298_show_t_sense, NULL, 0);
> +
> +static ssize_t ad7298_show_t_average(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	u16 data[2];
> +	char sign = ' ';
> +	int ret;
> +
> +	ret = ad7298_spi_read(chip, AD7298_T_AVG_MASK, data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data[1] & AD7298_T_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data[1] = (AD7298_T_VALUE_SIGN << 1) - data[1];
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign,
> +		(data[1] >> AD7298_T_VALUE_FLOAT_OFFSET),
> +		(data[1] & AD7298_T_VALUE_FLOAT_MASK) * 25);
> +}
> +
> +static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7298_show_t_average, NULL, 0);
> +
> +static ssize_t ad7298_show_voltage(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	u16 data[AD7298_VOLTAGE_LIMIT_COUNT];
> +	int i, size, ret;
> +
> +	ret = ad7298_spi_read(chip, AD7298_VOLTAGE_MASK, data);
> +	if (ret)
> +		return -EIO;
> +
> +	for (i = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) {
spurious bracket.
> +		if (chip->command & (AD7298_T_SENSE_MASK << i)) {
> +			ret = sprintf(buf, "channel[%d]=%d\n", i,
> +					data[i] & AD7298_VALUE_MASK);
> +			if (ret < 0)
> +				break;
> +			buf += ret;
Any reason to not just use, buf + size in the sprintf?  Cleaner code that
way.
> +			size += ret;
> +		}
> +	}
> +
> +	return size;
> +}
> +
> +static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7298_show_voltage, NULL, 0);
> +
Again, this pretty much implies the device ought to use the buffer interface.
> +static ssize_t ad7298_show_channel_mask(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%x\n", (chip->command & AD7298_VOLTAGE_MASK) >>
> +			AD7298_VOLTAGE_OFFSET);
> +}
> +
> +static ssize_t ad7298_store_channel_mask(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int i, ret;
> +
> +	ret = strict_strtoul(buf, 16, &data);
> +	if (ret || data > 0xff)
> +		return -EINVAL;
> +
> +	chip->command &= (~AD7298_VOLTAGE_MASK);
> +	chip->command |= data << AD7298_VOLTAGE_OFFSET;
> +
spurious bracket.
> +	for (i = 0, chip->channels = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) {
> +		if (chip->command & (AD7298_T_SENSE_MASK << i))
> +			chip->channels++;
> +	}
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR,
> +		ad7298_show_channel_mask,
> +		ad7298_store_channel_mask,
> +		0);
> +
> +static ssize_t ad7298_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7298_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7298_show_name, NULL, 0);
> +
> +static struct attribute *ad7298_attributes[] = {
> +	&iio_dev_attr_available_modes.dev_attr.attr,
> +	&iio_dev_attr_mode.dev_attr.attr,
> +	&iio_dev_attr_reset.dev_attr.attr,
> +	&iio_dev_attr_ext_ref.dev_attr.attr,
> +	&iio_dev_attr_t_sense.dev_attr.attr,
temp0_input
> +	&iio_dev_attr_t_average.dev_attr.attr,
temp0_mean_input
> +	&iio_dev_attr_voltage.dev_attr.attr,
> +	&iio_dev_attr_channel_mask.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7298_attribute_group = {
> +	.attrs = ad7298_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +static int __devinit ad7298_probe(struct spi_device *spi_dev)
> +{
> +	struct ad7298_chip_info *chip;
> +	unsigned short *pins = spi_dev->dev.platform_data;
> +	int ret = 0;
> +
> +	chip = kzalloc(sizeof(struct ad7298_chip_info), GFP_KERNEL);
> +
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	/* this is only used for device removal purposes */
> +	dev_set_drvdata(&spi_dev->dev, chip);
> +
> +	chip->spi_dev = spi_dev;
> +	chip->name = spi_dev->modalias;
> +	chip->busy_pin = pins[0];
> +
> +	ret = gpio_request(chip->busy_pin, chip->name);
> +	if (ret) {
> +		dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
> +			chip->busy_pin);
> +		goto error_free_chip;
> +	}
> +	gpio_direction_input(chip->busy_pin);
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_gpio;
> +	}
> +
> +	chip->indio_dev->dev.parent = &spi_dev->dev;
> +	chip->indio_dev->attrs = &ad7298_attribute_group;
> +	chip->indio_dev->dev_data = (void *)chip;
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
> +			 chip->name);
> +
> +	return 0;
> +
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_gpio:
> +	gpio_free(chip->busy_pin);
> +error_free_chip:
> +	kfree(chip);
> +
> +	return ret;
> +}
> +
> +static int __devexit ad7298_remove(struct spi_device *spi_dev)
> +{
> +	struct ad7298_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	dev_set_drvdata(&spi_dev->dev, NULL);
> +	iio_device_unregister(indio_dev);
> +	iio_free_device(chip->indio_dev);
> +	gpio_free(chip->busy_pin);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id ad7298_id[] = {
> +	{ "ad7298", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(spi, ad7298_id);
> +
> +static struct spi_driver ad7298_driver = {
> +	.driver = {
> +		.name = "ad7298",
> +		.bus = &spi_bus_type,
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = ad7298_probe,
> +	.remove = __devexit_p(ad7298_remove),
> +	.id_table = ad7298_id,
> +};
> +
> +static __init int ad7298_init(void)
> +{
> +	return spi_register_driver(&ad7298_driver);
> +}
> +
> +static __exit void ad7298_exit(void)
> +{
> +	spi_unregister_driver(&ad7298_driver);
> +}
> +
> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices AD7298 digital"
> +			" temperature sensor and ADC driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad7298_init);
> +module_exit(ad7298_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices
  2010-10-23 20:29 ` [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
@ 2010-10-24 21:56   ` Jonathan Cameron
  2010-10-26  3:35     ` Zhang, Sonic
  0 siblings, 1 reply; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 21:56 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Sonic Zhang

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Sonic Zhang <sonic.zhang@analog.com>
> 
Why IIO?  Do you have a use case that needs features off IIO
and wouldn't be covered by hwmon?

Not using any here, so I'm doubtful about this (Sonic and I
exchanged a few emails about this a while back).

Few comments inline and I guess iio then moving to hwmon is
fine with me if you want to do it... (hwmon is simpler!).

> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7314.c |  308 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 316 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7314.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 847f5f2..6d3b8bc 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -54,3 +54,10 @@ config AD7298
>  	help
>  	  Say yes here to build support for Analog Devices AD7298
>  	  temperature sensors and ADC.
> +
> +config AD7314
> +	tristate "Analog Devices AD7314 temperature sensor driver"
> +	depends on SPI
Please list all parts supported (appear to be several others in the
id table.)
> +	help
> +	  Say yes here to build support for Analog Devices AD7314
> +	  temperature sensors.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index d0ea747..04fd93b 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_AD7150) += ad7150.o
>  obj-$(CONFIG_AD7152) += ad7152.o
>  obj-$(CONFIG_AD7291) += ad7291.o
>  obj-$(CONFIG_AD7298) += ad7298.o
> +obj-$(CONFIG_AD7314) += ad7314.o
> diff --git a/drivers/staging/iio/adc/ad7314.c b/drivers/staging/iio/adc/ad7314.c
> new file mode 100644
> index 0000000..8c17b1f
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7314.c
> @@ -0,0 +1,308 @@
> +/*
> + * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD7314 power mode
> + */
> +#define AD7314_PD		0x2000
> +
> +/*
> + * AD7314 temperature masks
> + */
> +#define AD7314_TEMP_SIGN		0x200
> +#define AD7314_TEMP_MASK		0x7FE0
> +#define AD7314_TEMP_OFFSET		5
> +#define AD7314_TEMP_FLOAT_OFFSET	2
> +#define AD7314_TEMP_FLOAT_MASK		0x3
> +
> +/*
> + * ADT7301 and ADT7302 temperature masks
> + */
> +#define ADT7301_TEMP_SIGN		0x2000
> +#define ADT7301_TEMP_MASK		0x2FFF
> +#define ADT7301_TEMP_FLOAT_OFFSET	5
> +#define ADT7301_TEMP_FLOAT_MASK		0x1F
> +
> +/*
> + * struct ad7314_chip_info - chip specifc information
> + */
> +
> +struct ad7314_chip_info {
> +	const char *name;
> +	struct spi_device *spi_dev;
> +	struct iio_dev *indio_dev;
> +	s64 last_timestamp;
> +	u8  mode;
> +};
> +
> +/*
> + * ad7314 register access by SPI
> + */
> +
> +static int ad7314_spi_read(struct ad7314_chip_info *chip, u16 *data)
> +{
> +	struct spi_device *spi_dev = chip->spi_dev;
> +	int ret = 0;
> +	u16 value;
> +
> +	ret = spi_read(spi_dev, (u8 *)&value, sizeof(value));
> +	if (ret < 0) {
> +		dev_err(&spi_dev->dev, "SPI read error\n");
> +		return ret;
> +	}
> +
> +	*data = be16_to_cpu((u16)value);
> +
> +	return ret;
> +}
> +
> +static int ad7314_spi_write(struct ad7314_chip_info *chip, u16 data)
> +{
> +	struct spi_device *spi_dev = chip->spi_dev;
> +	int ret = 0;
> +	u16 value = cpu_to_be16(data);
> +
> +	ret = spi_write(spi_dev, (u8 *)&value, sizeof(value));
> +	if (ret < 0)
> +		dev_err(&spi_dev->dev, "SPI write error\n");
> +
> +	return ret;
> +}
> +
> +static ssize_t ad7314_show_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7314_chip_info *chip = dev_info->dev_data;
> +
> +	if (chip->mode)
> +		return sprintf(buf, "power-save\n");
> +	else
> +		return sprintf(buf, "full\n");
> +}
> +
> +static ssize_t ad7314_store_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7314_chip_info *chip = dev_info->dev_data;
> +	u16 mode = 0;
> +	int ret;
> +
> +	if (!strcmp(buf, "full"))
> +		mode = AD7314_PD;
> +
> +	ret = ad7314_spi_write(chip, mode);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->mode = mode;
> +
> +	return len;
> +}
> +
> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> +		ad7314_show_mode,
> +		ad7314_store_mode,
> +		0);
I'm still anti 'mode' attributes. They just don't generalize.
> +
> +static ssize_t ad7314_show_available_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "full\npower-save\n");
Do this via sampling_frequency (assuming that is what changes!)
> +}
> +
> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7314_show_available_modes, NULL, 0);
mode_available please.
> +
> +static ssize_t ad7314_show_temperature(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7314_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	if (chip->mode) {
> +		ret = ad7314_spi_write(chip, 0);
> +		if (ret)
> +			return -EIO;
Error eating.
> +	}
> +
> +	ret = ad7314_spi_read(chip, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (chip->mode)
> +		ad7314_spi_write(chip, chip->mode);
> +
> +	if (strcmp(chip->name, "ad7314")) {
> +		data = (data & AD7314_TEMP_MASK) >>
> +			AD7314_TEMP_OFFSET;
> +		if (data & AD7314_TEMP_SIGN) {
> +			data = (AD7314_TEMP_SIGN << 1) - data;
> +			sign = '-';
> +		}
> +
> +		return sprintf(buf, "%c%d.%.2d\n", sign,
> +				data >> AD7314_TEMP_FLOAT_OFFSET,
> +				(data & AD7314_TEMP_FLOAT_MASK) * 25);
> +	} else {
> +		data &= ADT7301_TEMP_MASK;
> +		if (data & ADT7301_TEMP_SIGN) {
> +			data = (ADT7301_TEMP_SIGN << 1) - data;
> +			sign = '-';
> +		}
> +
> +		return sprintf(buf, "%c%d.%.5d\n", sign,
> +				data >> ADT7301_TEMP_FLOAT_OFFSET,
> +				(data & ADT7301_TEMP_FLOAT_MASK) * 3125);
> +	}
> +}
> +
> +static IIO_DEVICE_ATTR(temperature, S_IRUGO, ad7314_show_temperature, NULL, 0);
> +
> +static ssize_t ad7314_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7314_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL, 0);
> +
> +static struct attribute *ad7314_attributes[] = {
> +	&iio_dev_attr_available_modes.dev_attr.attr,
> +	&iio_dev_attr_mode.dev_attr.attr,
> +	&iio_dev_attr_temperature.dev_attr.attr,
temp_input please (we match hwmon where possible).

> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7314_attribute_group = {
> +	.attrs = ad7314_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit ad7314_probe(struct spi_device *spi_dev)
> +{
> +	struct ad7314_chip_info *chip;
> +	int ret = 0;
> +
> +	chip = kzalloc(sizeof(struct ad7314_chip_info), GFP_KERNEL);
> +
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	/* this is only used for device removal purposes */
> +	dev_set_drvdata(&spi_dev->dev, chip);
> +
> +	chip->spi_dev = spi_dev;
> +	chip->name = spi_dev->modalias;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	chip->indio_dev->dev.parent = &spi_dev->dev;
> +	chip->indio_dev->attrs = &ad7314_attribute_group;
> +	chip->indio_dev->dev_data = (void *)chip;
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	dev_info(&spi_dev->dev, "%s temperature sensor registered.\n",
> +			 chip->name);
> +
> +	return 0;
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +
> +	return ret;
> +}
> +
> +static int __devexit ad7314_remove(struct spi_device *spi_dev)
> +{
> +	struct ad7314_chip_info *chip = dev_get_drvdata(&spi_dev->dev);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	dev_set_drvdata(&spi_dev->dev, NULL);
> +	if (spi_dev->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	iio_free_device(chip->indio_dev);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct spi_device_id ad7314_id[] = {
> +	{ "adt7301", 0 },
> +	{ "adt7302", 0 },
> +	{ "ad7314", 0 },
> +	{}
> +};
> +
> +static struct spi_driver ad7314_driver = {
> +	.driver = {
> +		.name = "ad7314",
> +		.bus = &spi_bus_type,
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = ad7314_probe,
> +	.remove = __devexit_p(ad7314_remove),
> +	.id_table = ad7314_id,
> +};
> +
> +static __init int ad7314_init(void)
> +{
> +	return spi_register_driver(&ad7314_driver);
> +}
> +
> +static __exit void ad7314_exit(void)
> +{
> +	spi_unregister_driver(&ad7314_driver);
> +}
> +
> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
> +			" temperature sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad7314_init);
> +module_exit(ad7314_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 05/14] staging: iio: adc: new driver for AD7414/5 devices
  2010-10-23 20:29 ` [PATCH 05/14] staging: iio: adc: new driver for AD7414/5 devices Mike Frysinger
@ 2010-10-24 22:03   ` Jonathan Cameron
  0 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 22:03 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Sonic Zhang

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Sonic Zhang <sonic.zhang@analog.com>
> 
Why IIO and not hwmon? Needs justification.
Guessing it's for the alarm elements?

Limited review follows.

Some abi breakage that needs fixing. This one falls entirely
in areas we have covered. It's also not an adc, so should probably
be in a different directory if it does go in iio

> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7414.c |  573 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 581 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7414.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 6d3b8bc..07fd1a7 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -61,3 +61,10 @@ config AD7314
>  	help
>  	  Say yes here to build support for Analog Devices AD7314
>  	  temperature sensors.
> +
> +config AD7414
> +	tristate "Analog Devices AD7414 and AD7415 temperature sensor driver"
> +	depends on I2C
> +	help
> +	  Say yes here to build support for Analog Devices AD7414 and AD7415
> +	  temperature sensors.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 04fd93b..c1ab11f 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -11,3 +11,4 @@ obj-$(CONFIG_AD7152) += ad7152.o
>  obj-$(CONFIG_AD7291) += ad7291.o
>  obj-$(CONFIG_AD7298) += ad7298.o
>  obj-$(CONFIG_AD7314) += ad7314.o
> +obj-$(CONFIG_AD7414) += ad7414.o
> diff --git a/drivers/staging/iio/adc/ad7414.c b/drivers/staging/iio/adc/ad7414.c
> new file mode 100644
> index 0000000..b92c880
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7414.c
> @@ -0,0 +1,573 @@
> +/*
> + * AD7414 digital temperature sensor driver supporting AD7414 and AD7415
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/i2c.h>
> +#include <linux/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD7414 registers definition
> + */
> +
> +#define AD7414_TEMPERATURE		0
> +#define AD7414_CONFIG			1
> +#define AD7414_TEMPERATURE_HIGH		2
> +#define AD7414_TEMPERATURE_LOW		3
> +
> +/*
> + * AD7414 config bits
> + */
> +#define AD7414_TEST_MODE	0x3
> +#define AD7414_ONE_SHOT		0x4
> +#define AD7414_ALERT_RESET	0x8
> +#define AD7414_ALERT_POLARITY	0x10
> +#define AD7414_ALERT_EN		0x20
> +#define AD7414_FLTR		0x40
> +#define AD7414_PD		0x80
> +
> +/*
> + * AD7414 masks
> + */
> +#define AD7414_TEMP_SIGN	0x200
> +#define AD7414_TEMP_MASK	0xFFC0
> +#define AD7414_TEMP_OFFSET	6
> +#define AD7414_ALERT_FLAG	0x20
> +#define AD7414_T_SIGN		0x80
> +#define AD7414_T_HIGH_FLAG	0x10
> +#define AD7414_T_LOW_FLAG	0x8
> +
> +
> +/*
> + * struct ad7414_chip_info - chip specifc information
> + */
> +
> +struct ad7414_chip_info {
> +	const char *name;
> +	struct i2c_client *client;
> +	struct iio_dev *indio_dev;
> +	struct work_struct thresh_work;
> +	s64 last_timestamp;
> +	u8  mode;
> +};
> +
> +/*
> + * ad7414 register access by I2C
> + */
> +
> +static int ad7414_i2c_read(struct ad7414_chip_info *chip, u8 reg, u8 *data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0, len;
> +
> +	ret = i2c_smbus_write_byte(client, reg);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read register address error\n");
> +		return ret;
> +	}
> +
> +	if (reg == AD7414_TEMPERATURE)
> +		len = 2;
> +	else
> +		len = 1;
> +
> +	ret = i2c_master_recv(client, data, len);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read error\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ad7414_i2c_write(struct ad7414_chip_info *chip, u8 reg, u8 data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
> +
> +	ret = i2c_smbus_write_byte_data(client, reg, data);
> +	if (ret < 0)
> +		dev_err(&client->dev, "I2C write error\n");
> +
> +	return ret;
> +}
> +
> +static ssize_t ad7414_show_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +
> +	if (chip->mode)
> +		return sprintf(buf, "power-save\n");
> +	else
> +		return sprintf(buf, "full\n");
> +}
> +
> +static ssize_t ad7414_store_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	u8 config;
> +	int ret;
> +
> +	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	if (strcmp(buf, "full")) {
> +		chip->mode = 0;
> +		config &= ~AD7414_PD;
> +	} else {
> +		chip->mode = 1;
> +		config |= AD7414_PD;
> +	}
> +
> +	ret = ad7414_i2c_write(chip, AD7414_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> +		ad7414_show_mode,
> +		ad7414_store_mode,
> +		0);
> +
> +static ssize_t ad7414_show_available_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "full\npower-save\n");
> +}
> +
> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7414_show_available_modes, NULL, 0);
> +
> +static ssize_t ad7414_show_temperature(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	u8 config;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	if (chip->mode) {
> +		ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
> +		if (ret)
> +			return -EIO;
> +		ret = ad7414_i2c_write(chip, AD7414_CONFIG,
> +				config | AD7414_ONE_SHOT);
> +		if (ret)
> +			return -EIO;
> +	}
> +
> +	ret = ad7414_i2c_read(chip, AD7414_TEMPERATURE, (u8 *)&data);
> +	if (ret)
> +		return -EIO;
> +
> +	data = swab16(data) >> AD7414_TEMP_OFFSET;
> +	if (data & AD7414_TEMP_SIGN) {
> +		data = (AD7414_TEMP_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign, (data >> 2), (data & 3) * 25);
> +}
> +
> +static IIO_DEVICE_ATTR(temperature, S_IRUGO, ad7414_show_temperature, NULL, 0);
> +
> +static ssize_t ad7414_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7414_show_name, NULL, 0);
> +
> +static struct attribute *ad7414_attributes[] = {
> +	&iio_dev_attr_available_modes.dev_attr.attr,
> +	&iio_dev_attr_mode.dev_attr.attr,
> +	&iio_dev_attr_temperature.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7414_attribute_group = {
> +	.attrs = ad7414_attributes,
> +};
> +
> +/*
> + * temperature bound events
> + */
> +
> +#define IIO_EVENT_CODE_AD7414_T_HIGH    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
> +#define IIO_EVENT_CODE_AD7414_T_LOW     (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
> +
> +static void ad7414_interrupt_bh(struct work_struct *work_s)
> +{
> +	struct ad7414_chip_info *chip =
> +		container_of(work_s, struct ad7414_chip_info, thresh_work);
> +	u16 status;
> +	u8 config;
> +	int ret;
> +
> +	/* retrive ALART status */
> +	ret = ad7414_i2c_read(chip, AD7414_TEMPERATURE, (u8 *)&status);
> +	if (ret)
> +		return;
> +	status = swab16(status);
> +
> +	/* clear ALART pin in chip configuration register */
> +	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
> +	if (ret)
> +		return;
> +	ret = ad7414_i2c_write(chip, AD7414_CONFIG,
> +			config | AD7414_ALERT_RESET);
> +	if (ret)
> +		return;
> +
> +	enable_irq(chip->client->irq);
> +
> +	if (status & AD7414_T_HIGH_FLAG)
> +		iio_push_event(chip->indio_dev, 0,
> +				IIO_EVENT_CODE_AD7414_T_HIGH,
> +				chip->last_timestamp);
> +	else if (status & AD7414_T_LOW_FLAG)
> +		iio_push_event(chip->indio_dev, 0,
> +				IIO_EVENT_CODE_AD7414_T_LOW,
> +				chip->last_timestamp);
> +}
> +
> +static int ad7414_interrupt(struct iio_dev *dev_info,
> +		int index,
> +		s64 timestamp,
> +		int no_test)
> +{
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +
> +	chip->last_timestamp = timestamp;
> +	schedule_work(&chip->thresh_work);
> +
> +	return 0;
> +}
> +
> +IIO_EVENT_SH(ad7414, &ad7414_interrupt);
> +
> +static ssize_t ad7414_show_enabled(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	u8 config;
> +	int ret;
> +
> +	/* retrive ALART status */
> +	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "%d\n", !!(config & AD7414_ALERT_EN));
> +}
> +
> +static ssize_t ad7414_set_enabled(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	u8 config;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	/* retrive ALART status */
> +	ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data)
> +		ret = ad7414_i2c_write(chip, AD7414_CONFIG,
> +			config & ~AD7414_ALERT_EN);
> +	else
> +		ret = ad7414_i2c_write(chip, AD7414_CONFIG,
> +			config | AD7414_ALERT_EN);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static inline ssize_t ad7414_show_temperature_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	s8 data;
> +	int ret;
> +
> +	ret = ad7414_i2c_read(chip, bound_reg, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data & AD7414_T_SIGN)
> +		data = (data&(~AD7414_T_SIGN)) - AD7414_T_SIGN;
> +
> +	return sprintf(buf, "%d\n", data);
> +}
> +
> +static inline ssize_t ad7414_set_temperature_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7414_chip_info *chip = dev_info->dev_data;
> +	long data;
> +	s8 value;
> +	int ret;
> +
> +	ret = strict_strtol(buf, 10, &data);
> +
> +	if (ret || data > 127 || data < -128)
> +		return -EINVAL;
> +
> +	value = (s8)data;
> +	if (value < 0)
> +		value = (AD7414_T_SIGN + value) | AD7414_T_SIGN;
> +
> +	ret = ad7414_i2c_write(chip, bound_reg, value);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
I think all these could be merged.
> +static ssize_t ad7414_show_temperature_high(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7414_show_temperature_bound(dev, attr,
> +			AD7414_TEMPERATURE_HIGH, buf);
> +}
> +
> +static inline ssize_t ad7414_set_temperature_high(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7414_set_temperature_bound(dev, attr,
> +			AD7414_TEMPERATURE_HIGH, buf, len);
> +}
> +
> +static ssize_t ad7414_show_temperature_low(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7414_show_temperature_bound(dev, attr,
> +			AD7414_TEMPERATURE_LOW, buf);
> +}
> +
> +static inline ssize_t ad7414_set_temperature_low(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7414_set_temperature_bound(dev, attr,
> +			AD7414_TEMPERATURE_LOW, buf, len);
> +}
> +
> +IIO_EVENT_ATTR_SH(t_bound_enabled, iio_event_ad7414,
> +		ad7414_show_enabled, ad7414_set_enabled, 0);
Sorry, Sonic. Same issue here. Use normal device_attrs unless
they are the enable / disable attributes.
> +IIO_EVENT_ATTR_SH(temperature_high, iio_event_ad7414,
> +		ad7414_show_temperature_high, ad7414_set_temperature_high, 0);
> +IIO_EVENT_ATTR_SH(temperature_low, iio_event_ad7414,
> +		ad7414_show_temperature_low, ad7414_set_temperature_low, 0);
> +
> +static struct attribute *ad7414_event_attributes[] = {
non abi compiant.

probably:
> +	&iio_event_attr_t_bound_enabled.dev_attr.attr,
iio_event_attr_temp_thresh_en
> +	&iio_event_attr_temperature_high.dev_attr.attr,
iio_device_attr_temp_thresh_rising_value
> +	&iio_event_attr_temperature_low.dev_attr.attr,
iio_device_attr_temp_thresh_falling_value.
> +	NULL,
> +};
> +
> +static struct attribute_group ad7414_event_attribute_group = {
> +	.attrs = ad7414_event_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit ad7414_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	struct ad7414_chip_info *chip;
> +	int ret = 0;
> +	u8 config;
> +
> +	chip = kzalloc(sizeof(struct ad7414_chip_info), GFP_KERNEL);
> +
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	/* this is only used for device removal purposes */
> +	i2c_set_clientdata(client, chip);
> +
> +	chip->client = client;
> +	chip->name = id->name;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	chip->indio_dev->dev.parent = &client->dev;
> +	chip->indio_dev->attrs = &ad7414_attribute_group;
> +	if (strcmp(id->name, "ad7414") == 0)
> +		chip->indio_dev->event_attrs = &ad7414_event_attribute_group;
> +	else
> +		client->irq = 0;
> +	chip->indio_dev->dev_data = (void *)chip;
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->num_interrupt_lines = 1;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	if (client->irq) {
> +		ret = iio_register_interrupt_line(client->irq,
> +				chip->indio_dev,
> +				0,
> +				client->irq_flags,
> +				chip->name);
> +		if (ret)
> +			goto error_unreg_dev;
> +
> +		/*
> +		 * The event handler list element refer to iio_event_ad7414.
> +		 * All event attributes bind to the same event handler.
> +		 * So, only register event handler once.
> +		 */
> +		iio_add_event_to_list(&iio_event_ad7414,
> +				&chip->indio_dev->interrupts[0]->ev_list);
> +
> +		INIT_WORK(&chip->thresh_work, ad7414_interrupt_bh);
> +
> +		ret = ad7414_i2c_read(chip, AD7414_CONFIG, &config);
> +		if (ret) {
> +			ret = -EIO;
> +			goto error_unreg_irq;
> +		}
> +
> +		if (client->irq_flags & IRQF_TRIGGER_HIGH)
> +			ret = ad7414_i2c_write(chip, AD7414_CONFIG,
> +				config | AD7414_ALERT_POLARITY);
> +		else
> +			ret = ad7414_i2c_write(chip, AD7414_CONFIG,
> +				config & ~AD7414_ALERT_POLARITY);
> +		if (ret) {
> +			ret = -EIO;
> +			goto error_unreg_irq;
> +		}
> +	}
> +
> +	dev_info(&client->dev, "%s temperature sensor registered.\n",
> +			 id->name);
> +
> +	return 0;
> +error_unreg_irq:
> +	iio_unregister_interrupt_line(chip->indio_dev, 0);
> +error_unreg_dev:
> +	iio_device_unregister(chip->indio_dev);
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +
> +	return ret;
> +}
> +
> +static int __devexit ad7414_remove(struct i2c_client *client)
> +{
> +	struct ad7414_chip_info *chip = i2c_get_clientdata(client);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	if (client->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	iio_free_device(chip->indio_dev);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ad7414_id[] = {
> +	{ "ad7414", 0 },
> +	{ "ad7415", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ad7414_id);
> +
> +static struct i2c_driver ad7414_driver = {
> +	.driver = {
> +		.name = "ad7414",
> +	},
> +	.probe = ad7414_probe,
> +	.remove = __devexit_p(ad7414_remove),
> +	.id_table = ad7414_id,
> +};
> +
> +static __init int ad7414_init(void)
> +{
> +	return i2c_add_driver(&ad7414_driver);
> +}
> +
> +static __exit void ad7414_exit(void)
> +{
> +	i2c_del_driver(&ad7414_driver);
> +}
> +
> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices AD7414 and AD7415 digital"
> +			" temperature sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad7414_init);
> +module_exit(ad7414_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 06/14] staging: iio: adc: new driver for AD7416/7/8 devices
  2010-10-23 20:29 ` [PATCH 06/14] staging: iio: adc: new driver for AD7416/7/8 devices Mike Frysinger
@ 2010-10-24 22:19   ` Jonathan Cameron
  0 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 22:19 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Sonic Zhang

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Sonic Zhang <sonic.zhang@analog.com>
Again, presence in IIO needs justifying comment.
(here for the events infrastructure I guess?)

Fair bit of abi breakage in this one. New elements too that
will need discussion.

Quite a few nitpicks including some that correspond directly
to the other drivers and might not be commented on here.

Don't abrieviate naming too far. Sometimes long attribute
names are useful.

Lots to fix in here, but if it builds send it on to Greg.
> 
> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Reviewed-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad7416.c |  777 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 785 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7416.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 07fd1a7..59d6fb6 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -68,3 +68,10 @@ config AD7414
>  	help
>  	  Say yes here to build support for Analog Devices AD7414 and AD7415
>  	  temperature sensors.
> +
> +config AD7416
> +	tristate "Analog Devices AD7416/7/8 temperature sensor and ADC driver"
> +	depends on I2C
> +	help
> +	  Say yes here to build support for Analog Devices AD74167/8
full names in the help text for easy grepping please.
> +	  temperature sensors and ADC.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index c1ab11f..e95c8d4 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -12,3 +12,4 @@ obj-$(CONFIG_AD7291) += ad7291.o
>  obj-$(CONFIG_AD7298) += ad7298.o
>  obj-$(CONFIG_AD7314) += ad7314.o
>  obj-$(CONFIG_AD7414) += ad7414.o
> +obj-$(CONFIG_AD7416) += ad7416.o
> diff --git a/drivers/staging/iio/adc/ad7416.c b/drivers/staging/iio/adc/ad7416.c
> new file mode 100644
> index 0000000..b89fc86
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7416.c
> @@ -0,0 +1,777 @@
> +/*
> + * AD7416 digital temperature sensor driver supporting AD7416/7/8
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/i2c.h>
> +#include <linux/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD7416 registers definition
> + */
> +#define AD7416_TEMPERATURE		0
> +#define AD7416_CONFIG			1
> +#define AD7416_T_OTI			2
> +#define AD7416_T_HYST			3
> +#define AD7416_ADC_VALUE		4
> +#define AD7416_CONFIG2			5
> +
> +/*
> + * AD7416 config
> + */
> +#define AD7416_PD			0x1
> +#define AD7416_OTI_INT			0x2
> +#define AD7416_OTI_POLARITY		0x4
> +#define AD7416_FAULT_QUEUE_MASK		0x18
> +#define AD7416_FAULT_QUEUE_OFFSET	3
> +#define AD7416_CS_MASK			0xE0
> +#define AD7416_CS_OFFSET		5
> +
> +/*
> + * AD7416 config
> + */
> +#define AD7416_CONVERT			0x80
> +
> +/*
> + * AD7416 mode
> + */
> +#define AD7416_MODE_FULL		0
> +#define AD7416_MODE_SHUTDOWN		1
> +#define AD7416_MODE_CONVERT		2
> +
> +/*
> + * AD7416/7/8 channel maks
> + */
> +#define AD7416_CHANNEL_MASK	0x1
> +#define AD7417_CHANNEL_MASK	0x1F
> +#define AD7418_CHANNEL_MASK	0x11
> +
> +/*
> + * AD7416 masks
> + */
> +#define AD7416_VALUE_SIGN	0x200
> +#define AD7416_VALUE_OFFSET	6
> +#define AD7416_BOUND_VALUE_SIGN	0x100
> +
> +
> +/*
> + * struct ad7416_chip_info - chip specifc information
> + */
> +
> +struct ad7416_chip_info {
> +	const char *name;
> +	struct i2c_client *client;
> +	struct iio_dev *indio_dev;
> +	struct work_struct thresh_work;
> +	s64 last_timestamp;
> +	u16 convert_pin;
> +	u8  mode;
> +	u8  channel_id;	/* 0 always be temperature */
> +	u8  channel_mask;
> +};
> +
> +/*
> + * ad7416 register access by I2C
> + */
> +
> +static int ad7416_i2c_read(struct ad7416_chip_info *chip, u8 reg, u8 *data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0, len;
no need to init ret;
> +
> +	ret = i2c_smbus_write_byte(client, reg);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read register address error\n");
> +		return ret;
> +	}
> +
> +	if (reg == AD7416_CONFIG || reg == AD7416_CONFIG2)
> +		len = 1;
> +	else
> +		len = 2;
> +
> +	ret = i2c_master_recv(client, data, len);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read error\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ad7416_i2c_write(struct ad7416_chip_info *chip, u8 reg, u8 data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
no need to init ret.
> +
> +	ret = i2c_smbus_write_byte_data(client, reg, data);
> +	if (ret < 0)
> +		dev_err(&client->dev, "I2C write error\n");
> +
> +	return ret;
> +}
> +
Should be moved to an automated mode setting based on what
the chip is currently doing.
> +static ssize_t ad7416_show_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +
> +	switch (chip->mode) {
> +	case AD7416_MODE_SHUTDOWN:
> +		return sprintf(buf, "power-save\n");
> +	case AD7416_MODE_CONVERT:
> +		return sprintf(buf, "convert\n");
> +	default:
> +		return sprintf(buf, "full\n");
> +	}
> +}
> +
> +static ssize_t ad7416_store_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	u8 config, config2;
> +	int ret;
> +
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +	if (ret)
error eating.
> +		return -EIO;
> +
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG2, &config2);
> +	if (ret)
> +		return -EIO;
> +
> +	if (strcmp(buf, "full")) {
sysfs_strcmp
> +		config2 &= ~AD7416_CONVERT;
> +		config &= ~AD7416_PD;
> +
> +		ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
> +		if (ret)
> +			return -EIO;
> +
> +		if (chip->mode == AD7416_MODE_CONVERT) {
> +			ret = ad7416_i2c_write(chip, AD7416_CONFIG2, config2);
> +			if (ret)
> +				return -EIO;
> +		}
> +
> +		chip->mode = AD7416_MODE_FULL;
> +	} else if (chip->convert_pin && strcmp(buf, "convert")) {
sysfs_strcmp
> +		config2 |= AD7416_CONVERT;
> +
> +		ret = ad7416_i2c_write(chip, AD7416_CONFIG2, config2);
> +		if (ret)
> +			return -EIO;
> +
> +		chip->mode = AD7416_MODE_CONVERT;
> +	} else {
> +		config |= AD7416_PD;
> +		config2 &= ~AD7416_CONVERT;
> +
> +		ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
> +		if (ret)
> +			return -EIO;
> +
> +		if (chip->mode == AD7416_MODE_CONVERT) {
> +			ret = ad7416_i2c_write(chip, AD7416_CONFIG2, config2);
> +			if (ret)
> +				return -EIO;
> +		}
> +
> +		chip->mode = AD7416_MODE_SHUTDOWN;
> +	}
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> +		ad7416_show_mode,
> +		ad7416_store_mode,
> +		0);
> +
> +static ssize_t ad7416_show_available_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +
> +	if (chip->convert_pin)
> +		return sprintf(buf, "full\npower-save\nconvert\n");
space separated please. Guessing convert is based on a trigger?
Not entirely sure how we should handle that...
> +	else
> +		return sprintf(buf, "full\npower-save\n");
> +}
> +
> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7416_show_available_modes, NULL, 0);
> +
> +static ssize_t ad7416_show_channel(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->channel_id);
> +}
> +
> +static ssize_t ad7416_store_channel(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	u8 channel_mask;
> +	u8 config;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	channel_mask = 1 << data;
> +	if (!(channel_mask & chip->channel_mask)) {
> +		dev_err(&chip->client->dev, "Invalid channel id %lu.\n", data);
> +		dev_err(&chip->client->dev, "Available channel mask 0x%x.\n",
> +				chip->channel_mask);
> +		return -EINVAL;
> +	} else if (data == chip->channel_id)
> +		return ret;
> +
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	config |= ((data << AD7416_CS_OFFSET) && AD7416_CS_MASK);
> +	ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
Channels should be seperately handled or if a 'scan' is desired
it should be done via the buffer interface.
> +static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR,
> +		ad7416_show_channel,
> +		ad7416_store_channel,
> +		0);
> +
> +
> +static ssize_t ad7416_show_value(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	if (chip->mode == AD7416_MODE_SHUTDOWN) {
probably wants to be runtime pm.
> +		/* write to active converter */
> +		ret = i2c_smbus_write_byte(chip->client, AD7416_TEMPERATURE);
> +		if (ret)
> +			return -EIO;
> +	} else if (chip->mode == AD7416_MODE_CONVERT) {
> +		gpio_set_value(chip->convert_pin, 1);
> +		gpio_set_value(chip->convert_pin, 0);
> +	}
> +
> +	if (chip->channel_id == 0) {
> +		ret = ad7416_i2c_read(chip, AD7416_TEMPERATURE, (u8 *)&data);
> +		if (ret)
> +			return -EIO;
> +
> +		data = swab16(data);
> +		data >>= AD7416_VALUE_OFFSET;
> +		if (data & AD7416_VALUE_SIGN) {
> +			data = (AD7416_VALUE_SIGN << 1) - data;
> +			sign = '-';
> +		}
> +
> +		return sprintf(buf, "%c%d.%.2d\n", sign, (data >> 2),
> +			 (data & 3) * 25);
> +	} else {
> +		ret = ad7416_i2c_read(chip, AD7416_ADC_VALUE, (u8 *)&data);
> +		if (ret)
> +			return -EIO;
> +
> +		data = swab16(data);
> +		data >>= AD7416_VALUE_OFFSET;
> +
> +		return sprintf(buf, "%u\n", data);
> +	}
> +}
> +
> +static IIO_DEVICE_ATTR(value, S_IRUGO, ad7416_show_value, NULL, 0);
should be broken out set of attributes, one per channel.
> +
> +static ssize_t ad7416_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7416_show_name, NULL, 0);
> +
> +static struct attribute *ad7416_attributes[] = {
> +	&iio_dev_attr_available_modes.dev_attr.attr,
> +	&iio_dev_attr_mode.dev_attr.attr,
> +	&iio_dev_attr_channel.dev_attr.attr,
> +	&iio_dev_attr_value.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7416_attribute_group = {
> +	.attrs = ad7416_attributes,
> +};
> +
> +/*
> + * temperature bound events
> + */
> +
> +#define IIO_EVENT_CODE_AD7416_OTI    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
Needs to be a general form.  Not sure what it is here though!
> +
> +static void ad7416_interrupt_bh(struct work_struct *work_s)
> +{
> +	struct ad7416_chip_info *chip =
> +		container_of(work_s, struct ad7416_chip_info, thresh_work);
> +
> +	enable_irq(chip->client->irq);
> +
> +	iio_push_event(chip->indio_dev, 0,
> +			IIO_EVENT_CODE_AD7416_OTI,
> +			chip->last_timestamp);
> +}
> +
> +static int ad7416_interrupt(struct iio_dev *dev_info,
> +		int index,
> +		s64 timestamp,
> +		int no_test)
> +{
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +
> +	chip->last_timestamp = timestamp;
> +	schedule_work(&chip->thresh_work);
> +
> +	return 0;
> +}
> +
> +IIO_EVENT_SH(ad7416, &ad7416_interrupt);
There only seems to be one type of event, so it shouldn't need the shared form
of this.
> +
> +static ssize_t ad7416_show_oti_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	u8 config;
> +	int ret;
> +
> +	/* retrive ALART status */
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	if (config & AD7416_OTI_INT)
This looks 'interesting'... Please document this. I would imagine
the interface will need discussion.
> +		return sprintf(buf, "interrupt\n");
> +	else
> +		return sprintf(buf, "comparator\n");
> +}
> +
> +static ssize_t ad7416_set_oti_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	u8 config;
> +	int ret;
> +
> +	/* retrive ALART status */
alert. 
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +	if (ret)
error eating.
> +		return -EIO;
> +
> +	if (strcmp(buf, "comparator") == 0)
> +		ret = ad7416_i2c_write(chip, AD7416_CONFIG,
> +			config & ~AD7416_OTI_INT);
> +	else
> +		ret = ad7416_i2c_write(chip, AD7416_CONFIG,
> +			config | AD7416_OTI_INT);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static ssize_t ad7416_show_available_oti_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
space separated please.
> +	return sprintf(buf, "comparator\ninterrupt\n");
> +}
> +
> +static ssize_t ad7416_show_fault_queue(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	u8 config;
> +	int ret;
> +
> +	/* retrive ALART status */
alert.
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "%d\n", (config & AD7416_FAULT_QUEUE_MASK) >>
> +			AD7416_FAULT_QUEUE_OFFSET);
> +}
> +

please document this one. Interface probably needs to be
generally useful.
> +static ssize_t ad7416_set_fault_queue(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	u8 config;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret || data > 3)
> +		return -EINVAL;
> +
> +	/* retrive ALART status */
> +	ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +	if (ret)
> +		return -EIO;
> +
> +	config &= ~AD7416_FAULT_QUEUE_MASK;
> +	config |= (data << AD7416_FAULT_QUEUE_OFFSET);
> +	ret = ad7416_i2c_write(chip, AD7416_CONFIG, config);
> +	if (ret)
more error eating.
> +		return -EIO;
> +
> +	return ret;
> +}
new line here please.
> +static inline ssize_t ad7416_show_t_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	s16 value;
> +	int ret;
> +
> +	ret = ad7416_i2c_read(chip, bound_reg, (u8 *)&data);
> +	if (ret)
> +		return -EIO;
> +
> +	data = swab16(data);
> +	data >>= (AD7416_VALUE_OFFSET + 1);
> +
> +	if (chip->channel_id == 0) {
> +		if (data & AD7416_BOUND_VALUE_SIGN) {
> +			value = (s16)(data&(~AD7416_BOUND_VALUE_SIGN));
> +			value = value - AD7416_BOUND_VALUE_SIGN;
> +		} else
> +			value = (s16)data;
> +
> +		return sprintf(buf, "%d\n", value >> 1);
> +	} else
> +		return sprintf(buf, "%u\n", data);
> +}
> +
> +static inline ssize_t ad7416_set_t_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7416_chip_info *chip = dev_info->dev_data;
> +	long value;
> +	u16 data;
> +	int ret;
> +
> +	if (chip->channel_id == 0) {
> +		ret = strict_strtol(buf, 10, &value);
> +
> +		if (ret || value < -128 || value > 127)
> +			return -EINVAL;
> +		if (value < 0)
> +			value = (AD7416_BOUND_VALUE_SIGN + value) |
> +				AD7416_BOUND_VALUE_SIGN;
> +
> +		data = (u16)value << 1;
> +	} else {
> +		ret = strict_strtoul(buf, 10, &value);
> +
> +		if (ret || value > 511 || value < 0)
> +			return -EINVAL;
> +
> +		data = (u16)value;
> +	}
> +
> +	data <<= (AD7416_VALUE_OFFSET + 1);
> +	data = swab16(data);
> +	ret = ad7416_i2c_write(chip, bound_reg, (u8)data);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +

could probably use one function for all of these.
> +static ssize_t ad7416_show_t_oti(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7416_show_t_bound(dev, attr,
> +			AD7416_T_OTI, buf);
> +}
> +
> +static inline ssize_t ad7416_set_t_oti(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7416_set_t_bound(dev, attr,
> +			AD7416_T_OTI, buf, len);
> +}
> +
> +static ssize_t ad7416_show_t_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return ad7416_show_t_bound(dev, attr,
> +			AD7416_T_HYST, buf);
> +}
> +
> +static inline ssize_t ad7416_set_t_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return ad7416_set_t_bound(dev, attr,
> +			AD7416_T_HYST, buf, len);
> +}
> +
Usual point here. Only attributes that enable / disable should
use the event macros.
> +IIO_EVENT_ATTR_SH(oti_mode, iio_event_ad7416,
> +		ad7416_show_oti_mode, ad7416_set_oti_mode, 0);
> +IIO_EVENT_ATTR_SH(available_oti_modes, iio_event_ad7416,
> +		ad7416_show_available_oti_modes, NULL, 0);
> +IIO_EVENT_ATTR_SH(fault_queue, iio_event_ad7416,
> +		ad7416_show_fault_queue, ad7416_set_fault_queue, 0);

Not sure what this is but that's not a standard abi...

> +IIO_EVENT_ATTR_SH(t_oti, iio_event_ad7416,
> +		ad7416_show_t_oti, ad7416_set_t_oti, 0);
> +IIO_EVENT_ATTR_SH(t_hyst, iio_event_ad7416,
> +		ad7416_show_t_hyst, ad7416_set_t_hyst, 0);
> +
> +static struct attribute *ad7416_event_attributes[] = {
> +	&iio_event_attr_oti_mode.dev_attr.attr,
> +	&iio_event_attr_available_oti_modes.dev_attr.attr,
> +	&iio_event_attr_fault_queue.dev_attr.attr,
> +	&iio_event_attr_t_oti.dev_attr.attr,
> +	&iio_event_attr_t_hyst.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ad7416_event_attribute_group = {
> +	.attrs = ad7416_event_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit ad7416_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	struct ad7416_chip_info *chip;
> +	unsigned long convert_pin = (unsigned long)client->dev.platform_data;
> +	int ret = 0;
> +	u8 config;
> +
> +	chip = kzalloc(sizeof(struct ad7416_chip_info), GFP_KERNEL);
> +
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	/* this is only used for device removal purposes */
> +	i2c_set_clientdata(client, chip);
> +
> +	chip->client = client;
> +	chip->name = id->name;
> +	if (strcmp(chip->name, "ad7418") == 0) {
> +		chip->channel_mask = AD7418_CHANNEL_MASK;
> +		if (convert_pin) {
> +			ret = gpio_request(convert_pin, chip->name);
> +			if (!ret) {
> +				chip->convert_pin = (u16)convert_pin;
> +				gpio_set_value(chip->convert_pin, 0);
> +			}
> +		}
> +	} else if (strcmp(chip->name, "ad7417") == 0) {
> +		chip->channel_mask = AD7417_CHANNEL_MASK;
> +		if (convert_pin) {
> +			ret = gpio_request(convert_pin, chip->name);
> +			if (!ret) {
> +				chip->convert_pin = (u16)convert_pin;
> +				gpio_set_value(chip->convert_pin, 0);
> +			}
> +		}
> +	} else
> +		chip->channel_mask = AD7416_CHANNEL_MASK;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	chip->indio_dev->dev.parent = &client->dev;
> +	chip->indio_dev->attrs = &ad7416_attribute_group;
> +	chip->indio_dev->event_attrs = &ad7416_event_attribute_group;
> +	chip->indio_dev->dev_data = (void *)chip;
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->num_interrupt_lines = 1;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	if (client->irq) {
> +		ret = iio_register_interrupt_line(client->irq,
> +				chip->indio_dev,
> +				0,
> +				client->irq_flags,
> +				chip->name);
> +		if (ret)
> +			goto error_unreg_dev;
> +
> +		/*
> +		 * The event handler list element refer to iio_event_ad7416.
> +		 * All event attributes bind to the same event handler.
> +		 * So, only register event handler once.
> +		 */
> +		iio_add_event_to_list(&iio_event_ad7416,
> +				&chip->indio_dev->interrupts[0]->ev_list);
> +
> +		INIT_WORK(&chip->thresh_work, ad7416_interrupt_bh);
> +
> +		ret = ad7416_i2c_read(chip, AD7416_CONFIG, &config);
> +		if (ret) {
> +			ret = -EIO;
> +			goto error_unreg_irq;
> +		}
> +
> +		if (client->irq_flags & IRQF_TRIGGER_HIGH)
> +			ret = ad7416_i2c_write(chip, AD7416_CONFIG,
> +				config | AD7416_OTI_POLARITY);
> +		else
> +			ret = ad7416_i2c_write(chip, AD7416_CONFIG,
> +				config & ~AD7416_OTI_POLARITY);
> +		if (ret) {
> +			ret = -EIO;
> +			goto error_unreg_irq;
> +		}
> +	}
> +
> +	dev_info(&client->dev, "%s temperature sensor and ADC registered.\n",
> +			 id->name);
> +
> +	return 0;
> +
> +error_unreg_irq:
> +	iio_unregister_interrupt_line(chip->indio_dev, 0);
> +error_unreg_dev:
> +	iio_device_unregister(chip->indio_dev);
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +
> +	return ret;
> +}
> +
> +static int __devexit ad7416_remove(struct i2c_client *client)
> +{
> +	struct ad7416_chip_info *chip = i2c_get_clientdata(client);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	if (client->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	iio_free_device(chip->indio_dev);
> +	if (chip->convert_pin)
> +		gpio_free(chip->convert_pin);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ad7416_id[] = {
> +	{ "ad7416", 0 },
> +	{ "ad7417", 0 },
> +	{ "ad7418", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ad7416_id);
> +
> +static struct i2c_driver ad7416_driver = {
> +	.driver = {
> +		.name = "ad7416",
> +	},
> +	.probe = ad7416_probe,
> +	.remove = __devexit_p(ad7416_remove),
> +	.id_table = ad7416_id,
> +};
> +
> +static __init int ad7416_init(void)
> +{
> +	return i2c_add_driver(&ad7416_driver);
> +}
> +
> +static __exit void ad7416_exit(void)
> +{
> +	i2c_del_driver(&ad7416_driver);
> +}
> +
> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices AD7416/7/8 digital"
> +			" temperature sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad7416_init);
> +module_exit(ad7416_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices
  2010-10-23 20:29 ` [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices Mike Frysinger
  2010-10-24 21:14   ` [Device-drivers-devel] " Mike Frysinger
@ 2010-10-24 22:21   ` Jonathan Cameron
  1 sibling, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 22:21 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Michael Hennerich

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>
> 

This one looks familiar ;) It's already merged
> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/adc/Kconfig       |   11 ++
>  drivers/staging/iio/adc/Makefile      |    4 +
>  drivers/staging/iio/adc/ad7476.h      |   66 +++++++++
>  drivers/staging/iio/adc/ad7476_core.c |  203 ++++++++++++++++++++++++++++
>  drivers/staging/iio/adc/ad7476_ring.c |  240 +++++++++++++++++++++++++++++++++
>  5 files changed, 524 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad7476.h
>  create mode 100644 drivers/staging/iio/adc/ad7476_core.c
>  create mode 100644 drivers/staging/iio/adc/ad7476_ring.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 59d6fb6..4cb1542 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -75,3 +75,14 @@ config AD7416
>  	help
>  	  Say yes here to build support for Analog Devices AD74167/8
>  	  temperature sensors and ADC.
> +
> +config AD7476
> +	tristate "Analog Devices AD7475/6/7/8 and AD7495 ADC driver"
> +	depends on SPI
> +	help
> +	  Say yes here to build support for Analog Devices AD7475/6/7/8,
> +	  AD7495 ADC driver.
> +	  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 ad7476.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index e95c8d4..67e6341 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -5,6 +5,9 @@
>  max1363-y := max1363_core.o
>  max1363-y += max1363_ring.o
>  
> +ad7476-y := ad7476_core.o
> +ad7476-$(CONFIG_IIO_RING_BUFFER) += ad7476_ring.o
> +
>  obj-$(CONFIG_MAX1363) += max1363.o
>  obj-$(CONFIG_AD7150) += ad7150.o
>  obj-$(CONFIG_AD7152) += ad7152.o
> @@ -13,3 +16,4 @@ obj-$(CONFIG_AD7298) += ad7298.o
>  obj-$(CONFIG_AD7314) += ad7314.o
>  obj-$(CONFIG_AD7414) += ad7414.o
>  obj-$(CONFIG_AD7416) += ad7416.o
> +obj-$(CONFIG_AD7476) += ad7476.o
> diff --git a/drivers/staging/iio/adc/ad7476.h b/drivers/staging/iio/adc/ad7476.h
> new file mode 100644
> index 0000000..fc69f83
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7476.h
> @@ -0,0 +1,66 @@
> +#ifndef _AD7476_H_
> +#define  _AD7476_H_
> +
> +struct ad7476_mode {
> +	const char	*name;
> +	int		numvals;
> +};
> +
> +struct ad7476_state {
> +	struct iio_dev			*indio_dev;
> +	struct spi_device		*spi;
> +	char				setupbyte;
> +	char				configbyte;
> +	const struct ad7476_chip_info	*chip_info;
> +	const struct ad7476_mode	*current_mode;
> +	struct work_struct		poll_work;
> +	atomic_t			protect_ring;
> +	struct iio_trigger		*trig;
> +	struct spi_transfer		xfer;
> +	struct spi_message		msg;
> +	unsigned char			data[2];
> +};
> +
> +#define CHIP_NAME "AD7876/7/8"
> +
> +#ifdef CONFIG_IIO_RING_BUFFER
> +
> +ssize_t ad7476_scan_from_ring(struct device *dev,
> +			       struct device_attribute *attr,
> +			       char *buf);
> +int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev);
> +void ad7476_ring_cleanup(struct iio_dev *indio_dev);
> +
> +int ad7476_initialize_ring(struct iio_ring_buffer *ring);
> +void ad7476_uninitialize_ring(struct iio_ring_buffer *ring);
> +
> +#else /* CONFIG_IIO_RING_BUFFER */
> +
> +static inline void ad7476_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +}
> +
> +static inline int ad7476_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return 0;
> +}
> +
> +
> +static inline ssize_t ad7476_scan_from_ring(struct device *dev,
> +					     struct device_attribute *attr,
> +					     char *buf)
> +{
> +	return 0;
> +}
> +
> +static inline int
> +ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline void ad7476_ring_cleanup(struct iio_dev *indio_dev)
> +{
> +}
> +#endif /* CONFIG_IIO_RING_BUFFER */
> +#endif /* _AD7476_H_ */
> diff --git a/drivers/staging/iio/adc/ad7476_core.c b/drivers/staging/iio/adc/ad7476_core.c
> new file mode 100644
> index 0000000..7181440
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7476_core.c
> @@ -0,0 +1,203 @@
> +/*
> + * AD7476/5/7/8 (A) SPI ADC driver
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#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 "../iio.h"
> +#include "../sysfs.h"
> +
> +#include "ad7476.h"
> +
> +static const struct ad7476_mode ad7476_mode_table[] = {
> +	{
> +		.name = "s0",
> +		.numvals = 1,
> +	},
> +};
> +
> +static ssize_t ad7476_scan_direct(struct device *dev,
> +				   struct device_attribute *attr,
> +				   char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7476_state *st = dev_info->dev_data;
> +	int ret;
> +	struct spi_device *spi = st->spi;
> +
> +	ret = spi_sync(spi, &st->msg);
> +	if (ret)
> +		return ret;
> +
> +	return sprintf(buf, "%d\n", (st->data[0] << 8) | st->data[1]);
> +}
> +
> +static ssize_t ad7476_scan(struct device *dev,
> +			    struct device_attribute *attr,
> +			    char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	int ret;
> +
> +	mutex_lock(&dev_info->mlock);
> +	if (dev_info->currentmode == INDIO_RING_TRIGGERED)
> +		ret = ad7476_scan_from_ring(dev, attr, buf);
> +	else
> +		ret = ad7476_scan_direct(dev, attr, buf);
> +	mutex_unlock(&dev_info->mlock);
> +
> +	return ret;
> +}
> +
> +/* Cannot query the device, so use local copy of state */
> +static ssize_t ad7476_show_scan_mode(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7476_state *st = dev_info->dev_data;
> +
> +	return sprintf(buf, "%s\n", st->current_mode->name);
> +}
> +
> +
> +static IIO_DEV_ATTR_AVAIL_SCAN_MODES(ad7476_show_scan_mode);
> +static IIO_DEV_ATTR_SCAN_MODE(S_IRUGO | S_IWUSR,
> +		       ad7476_show_scan_mode, NULL);
> +
> +static IIO_DEV_ATTR_SCAN(ad7476_scan);
> +
> +static ssize_t ad7476_show_name(struct device *dev,
> +				 struct device_attribute *attr,
> +				 char *buf)
> +{
> +	return sprintf(buf, "%s\n", CHIP_NAME);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7476_show_name, NULL, 0);
> +
> +/*name export */
> +
> +static struct attribute *ad7476_attributes[] = {
> +	&iio_dev_attr_available_scan_modes.dev_attr.attr,
> +	&iio_dev_attr_scan_mode.dev_attr.attr,
> +	&iio_dev_attr_scan.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad7476_attribute_group = {
> +	.attrs = ad7476_attributes,
> +};
> +
> +static int __devinit ad7476_probe(struct spi_device *spi)
> +{
> +	int ret;
> +	struct ad7476_state *st = kzalloc(sizeof(*st), GFP_KERNEL);
> +	if (st == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	spi_set_drvdata(spi, st);
> +
> +	atomic_set(&st->protect_ring, 0);
> +	st->spi = spi;
> +
> +	st->indio_dev = iio_allocate_device();
> +	if (st->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_st;
> +	}
> +
> +	/* Estabilish that the iio_dev is a child of the i2c device */
> +	st->indio_dev->dev.parent = &spi->dev;
> +	st->indio_dev->attrs = &ad7476_attribute_group;
> +	st->indio_dev->dev_data = (void *)(st);
> +	st->indio_dev->driver_module = THIS_MODULE;
> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	st->current_mode = &ad7476_mode_table[0];
> +
> +	/* Setup default message */
> +
> +	st->xfer.rx_buf = &st->data;
> +	st->xfer.len = st->current_mode->numvals * 2;
> +
> +	spi_message_init(&st->msg);
> +	spi_message_add_tail(&st->xfer, &st->msg);
> +
> +	ret = ad7476_register_ring_funcs_and_init(st->indio_dev);
> +	if (ret)
> +		goto error_free_device;
> +
> +	ret = iio_device_register(st->indio_dev);
> +	if (ret)
> +		goto error_free_device;
> +
> +	ret = ad7476_initialize_ring(st->indio_dev->ring);
> +	if (ret)
> +		goto error_cleanup_ring;
> +	return 0;
> +
> +error_cleanup_ring:
> +	ad7476_ring_cleanup(st->indio_dev);
> +	iio_device_unregister(st->indio_dev);
> +error_free_device:
> +	iio_free_device(st->indio_dev);
> +error_free_st:
> +	kfree(st);
> +error_ret:
> +	return ret;
> +}
> +
> +static int ad7476_remove(struct spi_device *spi)
> +{
> +	struct ad7476_state *st = spi_get_drvdata(spi);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	ad7476_uninitialize_ring(indio_dev->ring);
> +	ad7476_ring_cleanup(indio_dev);
> +	iio_device_unregister(indio_dev);
> +	kfree(st);
> +
> +	return 0;
> +}
> +
> +
> +static struct spi_driver ad7476_driver = {
> +	.driver = {
> +		.name	= "ad7476",
> +		.bus	= &spi_bus_type,
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= ad7476_probe,
> +	.remove		= __devexit_p(ad7476_remove),
> +};
> +
> +static int __init ad7476_init(void)
> +{
> +	return spi_register_driver(&ad7476_driver);
> +}
> +module_init(ad7476_init);
> +
> +static void __exit ad7476_exit(void)
> +{
> +	spi_unregister_driver(&ad7476_driver);
> +}
> +module_exit(ad7476_exit);
> +
> +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
> +MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) ADC");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("spi:ad7476");
> diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c
> new file mode 100644
> index 0000000..c1fe346
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad7476_ring.c
> @@ -0,0 +1,240 @@
> +/*
> + * Copyright 2010 Analog Devices Inc.
> + * Copyright (C) 2008 Jonathan Cameron
> + *
> + * Licensed under the GPL-2 or later.
> + *
> + * ad7476_ring.c
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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 "../iio.h"
> +#include "../ring_generic.h"
> +#include "../ring_sw.h"
> +#include "../trigger.h"
> +#include "../sysfs.h"
> +
> +#include "ad7476.h"
> +
> +ssize_t ad7476_scan_from_ring(struct device *dev,
> +			       struct device_attribute *attr,
> +			       char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad7476_state *info = dev_info->dev_data;
> +	int i, ret, len = 0;
> +	char *ring_data;
> +
> +	ring_data = kmalloc(info->current_mode->numvals*2, GFP_KERNEL);
> +	if (ring_data == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	ret = dev_info->ring->access.read_last(dev_info->ring, ring_data);
> +	if (ret)
> +		goto error_free_ring_data;
> +	len += sprintf(buf+len, "ring ");
> +	for (i = 0; i < info->current_mode->numvals; i++)
> +		len += sprintf(buf + len, "%d ",
> +			       ((int)(ring_data[i*2 + 0] & 0xFF) << 8)
> +			       + ((int)(ring_data[i*2 + 1])));
> +	len += sprintf(buf + len, "\n");
> +	kfree(ring_data);
> +
> +	return len;
> +
> +error_free_ring_data:
> +	kfree(ring_data);
> +error_ret:
> +	return ret;
> +}
> +
> +/**
> + * ad7476_ring_preenable() setup the parameters of the ring before enabling
> + *
> + * The complex nature of the setting of the nuber of bytes per datum is due
> + * to this driver currently ensuring that the timestamp is stored at an 8
> + * byte boundary.
> + **/
> +static int ad7476_ring_preenable(struct iio_dev *indio_dev)
> +{
> +	struct ad7476_state *st = indio_dev->dev_data;
> +	size_t d_size;
> +
> +	if (indio_dev->ring->access.set_bpd) {
> +		d_size = st->current_mode->numvals*2 + sizeof(s64);
> +		if (d_size % 8)
> +			d_size += 8 - (d_size % 8);
> +		indio_dev->ring->access.set_bpd(indio_dev->ring, d_size);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * ad7476_ring_postenable() typical ring post enable
> + *
> + * Only not moved into the core for the hardware ring buffer cases
> + * that are more sophisticated.
> + **/
> +static int ad7476_ring_postenable(struct iio_dev *indio_dev)
> +{
> +	if (indio_dev->trig == NULL)
> +		return 0;
> +	return iio_trigger_attach_poll_func(indio_dev->trig,
> +					    indio_dev->pollfunc);
> +}
> +
> +/**
> + * ad7476_ring_predisable() runs just prior to ring buffer being disabled
> + *
> + * Typical predisable function which ensures that no trigger events can
> + * occur before we disable the ring buffer (and hence would have no idea
> + * what to do with them)
> + **/
> +static int ad7476_ring_predisable(struct iio_dev *indio_dev)
> +{
> +	if (indio_dev->trig)
> +		return iio_trigger_dettach_poll_func(indio_dev->trig,
> +						     indio_dev->pollfunc);
> +	else
> +		return 0;
> +}
> +
> +/**
> + * ad7476_poll_func_th() th of trigger launched polling to ring buffer
> + *
> + * As sampling only occurs on i2c comms occuring, leave timestamping until
> + * then.  Some triggers will generate their own time stamp.  Currently
> + * there is no way of notifying them when no one cares.
> + **/
> +void ad7476_poll_func_th(struct iio_dev *indio_dev)
> +{
> +	struct ad7476_state *st = indio_dev->dev_data;
> +
> +	schedule_work(&st->poll_work);
> +
> +	return;
> +}
> +/**
> + * ad7476_poll_bh_to_ring() bh of trigger launched polling to ring buffer
> + * @work_s:	the work struct through which this was scheduled
> + *
> + * Currently there is no option in this driver to disable the saving of
> + * timestamps within the ring.
> + * I think the one copy of this at a time was to avoid problems if the
> + * trigger was set far too high and the reads then locked up the computer.
> + **/
> +static void ad7476_poll_bh_to_ring(struct work_struct *work_s)
> +{
> +	struct ad7476_state *st = container_of(work_s, struct ad7476_state,
> +						  poll_work);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	struct iio_sw_ring_buffer *ring = iio_to_sw_ring(indio_dev->ring);
> +	s64 time_ns;
> +	__u8 *rxbuf;
> +	int b_sent;
> +	size_t d_size;
> +
> +	/* Ensure the timestamp is 8 byte aligned */
> +	d_size = st->current_mode->numvals*2 + sizeof(s64);
> +	if (d_size % sizeof(s64))
> +		d_size += sizeof(s64) - (d_size % sizeof(s64));
> +
> +	/* Ensure only one copy of this function running at a time */
> +	if (atomic_inc_return(&st->protect_ring) > 1)
> +		return;
> +
> +	/* Monitor mode prevents reading. Whilst not currently implemented
> +	 * might as well have this test in here in the meantime as it does
> +	 * no harm.
> +	 */
> +	if (st->current_mode->numvals == 0)
> +		return;
> +
> +	rxbuf = kmalloc(d_size,	GFP_KERNEL);
> +	if (rxbuf == NULL)
> +		return;
> +
> +	b_sent = spi_read(st->spi, rxbuf,
> +				 st->current_mode->numvals * 2);
> +	if (b_sent < 0)
> +		goto done;
> +
> +	time_ns = iio_get_time_ns();
> +
> +	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
> +
> +	indio_dev->ring->access.store_to(&ring->buf, rxbuf, time_ns);
> +done:
> +	kfree(rxbuf);
> +	atomic_dec(&st->protect_ring);
> +}
> +
> +
> +int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
> +{
> +	struct ad7476_state *st = indio_dev->dev_data;
> +	int ret = 0;
> +
> +	indio_dev->ring = iio_sw_rb_allocate(indio_dev);
> +	if (!indio_dev->ring) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +	/* Effectively select the ring buffer implementation */
> +	iio_ring_sw_register_funcs(&st->indio_dev->ring->access);
> +	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
> +	if (indio_dev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_deallocate_sw_rb;
> +	}
> +	/* Configure the polling function called on trigger interrupts */
> +	indio_dev->pollfunc->poll_func_main = &ad7476_poll_func_th;
> +	indio_dev->pollfunc->private_data = indio_dev;
> +
> +	/* Ring buffer functions - here trigger setup related */
> +	indio_dev->ring->postenable = &ad7476_ring_postenable;
> +	indio_dev->ring->preenable = &ad7476_ring_preenable;
> +	indio_dev->ring->predisable = &ad7476_ring_predisable;
> +	INIT_WORK(&st->poll_work, &ad7476_poll_bh_to_ring);
> +
> +	/* Flag that polled ring buffering is possible */
> +	indio_dev->modes |= INDIO_RING_TRIGGERED;
> +	return 0;
> +error_deallocate_sw_rb:
> +	iio_sw_rb_free(indio_dev->ring);
> +error_ret:
> +	return ret;
> +}
> +
> +void ad7476_ring_cleanup(struct iio_dev *indio_dev)
> +{
> +	/* ensure that the trigger has been detached */
> +	if (indio_dev->trig) {
> +		iio_put_trigger(indio_dev->trig);
> +		iio_trigger_dettach_poll_func(indio_dev->trig,
> +					      indio_dev->pollfunc);
> +	}
> +	kfree(indio_dev->pollfunc);
> +	iio_sw_rb_free(indio_dev->ring);
> +}
> +
> +void ad7476_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +	iio_ring_buffer_unregister(ring);
> +}
> +
> +int ad7476_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return iio_ring_buffer_register(ring, 0);
> +}


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 08/14] staging: iio: adc: new driver for AD7745/6/7 devices
  2010-10-23 20:29 ` [PATCH 08/14] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
@ 2010-10-24 22:36   ` Jonathan Cameron
  0 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 22:36 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Barry Song

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Barry Song <barry.song@analog.com>
> 
This one is mostly clean.  Needs some abi cleanup
and a few new abi elements need pinning down and documenting...

> Signed-off-by: Barry Song <barry.song@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>  drivers/staging/iio/adc/Kconfig  |    7 +
>  drivers/staging/iio/adc/Makefile |    1 +
>  drivers/staging/iio/adc/ad774x.c |  719 ++++++++++++++++++++++++++++++++++++++
No generic names please unless every part covered by the wild card is supported.
Pick a device and name after that.
>  3 files changed, 727 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad774x.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 4cb1542..780c2c5 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -86,3 +86,10 @@ config AD7476
>  
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ad7476.
> +
> +config AD774X
> +	tristate "Analog Devices ad7745/6/7 capacitive sensor driver"
> +	depends on I2C
> +	help
> +	  Say yes here to build support for Analog Devices capacitive sensors.
> +	  (ad7745, ad7746, ad7747) Provides direct access via sysfs.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 67e6341..ac2663a 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -17,3 +17,4 @@ obj-$(CONFIG_AD7314) += ad7314.o
>  obj-$(CONFIG_AD7414) += ad7414.o
>  obj-$(CONFIG_AD7416) += ad7416.o
>  obj-$(CONFIG_AD7476) += ad7476.o
> +obj-$(CONFIG_AD774X) += ad774x.o
> diff --git a/drivers/staging/iio/adc/ad774x.c b/drivers/staging/iio/adc/ad774x.c
> new file mode 100644
> index 0000000..dd1df08
> --- /dev/null
> +++ b/drivers/staging/iio/adc/ad774x.c
> @@ -0,0 +1,719 @@
> +/*
> + * AD774X capacitive sensor driver supporting AD7745/6/7
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/i2c.h>
> +#include <linux/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * AD774X registers definition
> + */
> +
> +#define AD774X_STATUS              0
> +#define AD774X_STATUS_RDY          (1 << 2)
> +#define AD774X_STATUS_RDYVT        (1 << 1)
> +#define AD774X_STATUS_RDYCAP       (1 << 0)
> +#define AD774X_CAP_DATA_HIGH       1
> +#define AD774X_CAP_DATA_MID        2
> +#define AD774X_CAP_DATA_LOW        3
> +#define AD774X_VT_DATA_HIGH        4
> +#define AD774X_VT_DATA_MID         5
> +#define AD774X_VT_DATA_LOW         6
> +#define AD774X_CAP_SETUP           7
> +#define AD774X_VT_SETUP            8
> +#define AD774X_EXEC_SETUP          9
> +#define AD774X_CFG                 10
> +#define AD774X_CAPDACA             11
> +#define AD774X_CAPDACB             12
> +#define AD774X_CAP_OFFH            13
> +#define AD774X_CAP_OFFL            14
> +#define AD774X_CAP_GAINH           15
> +#define AD774X_CAP_GAINL           16
> +#define AD774X_VOLT_GAINH          17
> +#define AD774X_VOLT_GAINL          18
> +
> +#define AD774X_MAX_CONV_MODE       6
> +
> +/*
> + * struct ad774x_chip_info - chip specifc information
> + */
> +
> +struct ad774x_chip_info {
> +	const char *name;
> +	struct i2c_client *client;
> +	struct iio_dev *indio_dev;
> +	struct work_struct thresh_work;
> +	bool inter;
> +	s64 last_timestamp;
> +	u16 cap_offs;                   /* Capacitive offset */
> +	u16 cap_gain;                   /* Capacitive gain calibration */
> +	u16 volt_gain;                  /* Voltage gain calibration */
> +	u8  cap_setup;
> +	u8  vt_setup;
> +	u8  exec_setup;
> +
> +	char *conversion_mode;
> +};
> +
> +struct ad774x_conversion_mode {
> +	char *name;
> +	u8 reg_cfg;
> +};
> +
Same comments on these not being a good idea.
> +struct ad774x_conversion_mode ad774x_conv_mode_table[AD774X_MAX_CONV_MODE] = {
> +	{ "idle", 0 },
> +	{ "continuous-conversion", 1 },
> +	{ "single-conversion", 2 },
> +	{ "power-down", 3 },
> +	{ "offset-calibration", 5 },
> +	{ "gain-calibration", 6 },
> +};
> +
> +/*
> + * ad774x register access by I2C
> + */
> +
> +static int ad774x_i2c_read(struct ad774x_chip_info *chip, u8 reg, u8 *data, int len)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
No need to init ret.
> +
> +	ret = i2c_master_send(client, &reg, 1);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C write error\n");
> +		return ret;
> +	}
> +
> +	ret = i2c_master_recv(client, data, len);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read error\n");
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int ad774x_i2c_write(struct ad774x_chip_info *chip, u8 reg, u8 data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
no need to init ret.
> +
> +	u8 tx[2] = {
> +		reg,
> +		data,
> +	};
> +
> +	ret = i2c_master_send(client, tx, 2);
> +	if (ret < 0)
> +		dev_err(&client->dev, "I2C write error\n");
> +
> +	return ret;
> +}
> +
> +/*
> + * sysfs nodes
> + */
> +
> +#define IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(_show)				\
> +	IIO_DEVICE_ATTR(available_conversion_modes, S_IRUGO, _show, NULL, 0)
> +#define IIO_DEV_ATTR_CONVERSION_MODE(_mode, _show, _store)              \
> +	IIO_DEVICE_ATTR(conversion_mode, _mode, _show, _store, 0)
> +#define IIO_DEV_ATTR_CAP_SETUP(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(cap_setup, _mode, _show, _store, 0)
I sense magic values.... Get rid of these.

> +#define IIO_DEV_ATTR_VT_SETUP(_mode, _show, _store)              \
> +	IIO_DEVICE_ATTR(vt_setup, _mode, _show, _store, 0)
> +#define IIO_DEV_ATTR_EXEC_SETUP(_mode, _show, _store)              \
> +	IIO_DEVICE_ATTR(exec_setup, _mode, _show, _store, 0)
> +#define IIO_DEV_ATTR_VOLT_GAIN(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(volt_gain, _mode, _show, _store, 0)
in_gain
> +#define IIO_DEV_ATTR_CAP_OFFS(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(cap_offs, _mode, _show, _store, 0)#
capacitance_offset (or if this is common in your drivers we could pick cap_...)
> +#define IIO_DEV_ATTR_CAP_GAIN(_mode, _show, _store)		\
> +	IIO_DEVICE_ATTR(cap_gain, _mode, _show, _store, 0)
capacitance_scale (sorry this one got changed a while back).
> +#define IIO_DEV_ATTR_DACA_VALUE(_show)		\
> +	IIO_DEVICE_ATTR(daca_value, S_IRUGO, _show, NULL, 0)
dac0_raw
> +#define IIO_DEV_ATTR_DACB_VALUE(_show)		\
> +	IIO_DEVICE_ATTR(dacb_value, S_IRUGO, _show, NULL, 0)
dac1_raw
> +#define IIO_DEV_ATTR_CAP_DATA(_show)		\
> +	IIO_DEVICE_ATTR(cap_data, S_IRUGO, _show, NULL, 0)
> +#define IIO_DEV_ATTR_VT_DATA(_show)		\
> +	IIO_DEVICE_ATTR(vt_data, S_IRUGO, _show, NULL, 0)
probably break the various things this can be out and change mode
as is needed. 
> +
> +static ssize_t ad774x_show_conversion_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int i;
> +	int len = 0;
> +
> +	for (i = 0; i < AD774X_MAX_CONV_MODE; i++)
> +		len += sprintf(buf + len, "%s\n", ad774x_conv_mode_table[i].name);
> +
> +	return len;
> +}
> +
> +static IIO_DEV_ATTR_AVAIL_CONVERSION_MODES(ad774x_show_conversion_modes);
> +
> +static ssize_t ad774x_show_conversion_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%s\n", chip->conversion_mode);
> +}
> +
> +static ssize_t ad774x_store_conversion_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	u8 cfg;
> +	int i;
> +
> +	ad774x_i2c_read(chip, AD774X_CFG, &cfg, 1);
> +
> +	for (i = 0; i < AD774X_MAX_CONV_MODE; i++) {
> +		if (strncmp(buf, ad774x_conv_mode_table[i].name,
> +				strlen(ad774x_conv_mode_table[i].name) - 1) == 0) {
> +			chip->conversion_mode = ad774x_conv_mode_table[i].name;
> +			cfg |= 0x18 | ad774x_conv_mode_table[i].reg_cfg;
> +			ad774x_i2c_write(chip, AD774X_CFG, cfg);
> +			return len;
> +		}
> +	}
> +
> +	dev_err(dev, "not supported conversion mode\n");
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CONVERSION_MODE(S_IRUGO | S_IWUSR,
> +		ad774x_show_conversion_mode,
> +		ad774x_store_conversion_mode);
> +
Would have thought the next two could be easily combined?
> +static ssize_t ad774x_show_daca_value(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	u8 data;
> +
> +	ad774x_i2c_read(chip, AD774X_CAPDACA, &data, 1);
> +
> +	return sprintf(buf, "%02x\n", data);
> +}
> +
> +static IIO_DEV_ATTR_DACA_VALUE(ad774x_show_daca_value);
> +
> +static ssize_t ad774x_show_dacb_value(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	u8 data;
> +
> +	ad774x_i2c_read(chip, AD774X_CAPDACB, &data, 1);
> +
> +	return sprintf(buf, "%02x\n", data);
> +}
> +
> +static IIO_DEV_ATTR_DACB_VALUE(ad774x_show_dacb_value);
> +
> +static ssize_t ad774x_show_cap_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%02x\n", chip->cap_setup);
> +}
> +
> +static ssize_t ad774x_store_cap_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x100)) {
> +		ad774x_i2c_write(chip, AD774X_CAP_SETUP, data);
> +		chip->cap_setup = data;
> +		return len;
> +	}
> +
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CAP_SETUP(S_IRUGO | S_IWUSR,
> +		ad774x_show_cap_setup,
> +		ad774x_store_cap_setup);
> +
> +static ssize_t ad774x_show_vt_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%02x\n", chip->vt_setup);
> +}
> +
> +static ssize_t ad774x_store_vt_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x100)) {
> +		ad774x_i2c_write(chip, AD774X_VT_SETUP, data);
> +		chip->vt_setup = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_VT_SETUP(S_IRUGO | S_IWUSR,
> +		ad774x_show_vt_setup,
> +		ad774x_store_vt_setup);
> +
> +static ssize_t ad774x_show_exec_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "0x%02x\n", chip->exec_setup);
> +}
> +
> +static ssize_t ad774x_store_exec_setup(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x100)) {
> +		ad774x_i2c_write(chip, AD774X_EXEC_SETUP, data);
> +		chip->exec_setup = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_EXEC_SETUP(S_IRUGO | S_IWUSR,
> +		ad774x_show_exec_setup,
> +		ad774x_store_exec_setup);
> +
> +static ssize_t ad774x_show_volt_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->volt_gain);
> +}
> +
> +static ssize_t ad774x_store_volt_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad774x_i2c_write(chip, AD774X_VOLT_GAINH, data >> 8);
> +		ad774x_i2c_write(chip, AD774X_VOLT_GAINL, data);
> +		chip->volt_gain = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_VOLT_GAIN(S_IRUGO | S_IWUSR,
> +		ad774x_show_volt_gain,
> +		ad774x_store_volt_gain);
> +
The whole voltage thing was mysterious introduced in an early driver
from Barry, I thought it was cleaned out of the blackfin tree, but I
guess this one escaped. Always in0 etc (as per hwmon who got their
first on naming these!)
> +static ssize_t ad774x_show_cap_data(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	char tmp[3];
> +
> +	ad774x_i2c_read(chip, AD774X_CAP_DATA_HIGH, tmp, 3);
> +	data = ((int)tmp[0] << 16) | ((int)tmp[1] << 8) | (int)tmp[2];
> +
> +	return sprintf(buf, "%ld\n", data);
> +}
> +
> +static IIO_DEV_ATTR_CAP_DATA(ad774x_show_cap_data);
> +
> +static ssize_t ad774x_show_vt_data(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	char tmp[3];
> +
> +	ad774x_i2c_read(chip, AD774X_VT_DATA_HIGH, tmp, 3);
> +	data = ((int)tmp[0] << 16) | ((int)tmp[1] << 8) | (int)tmp[2];
> +
> +	return sprintf(buf, "%ld\n", data);
> +}
> +
> +static IIO_DEV_ATTR_VT_DATA(ad774x_show_vt_data);
> +
> +static ssize_t ad774x_show_cap_offs(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->cap_offs);
> +}
> +
> +static ssize_t ad774x_store_cap_offs(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad774x_i2c_write(chip, AD774X_CAP_OFFH, data >> 8);
> +		ad774x_i2c_write(chip, AD774X_CAP_OFFL, data);
> +		chip->cap_offs = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CAP_OFFS(S_IRUGO | S_IWUSR,
> +		ad774x_show_cap_offs,
> +		ad774x_store_cap_offs);
> +
> +static ssize_t ad774x_show_cap_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	return sprintf(buf, "%d\n", chip->cap_gain);
> +}
> +
> +static ssize_t ad774x_store_cap_gain(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +
> +	if ((!ret) && (data < 0x10000)) {
> +		ad774x_i2c_write(chip, AD774X_CAP_GAINH, data >> 8);
> +		ad774x_i2c_write(chip, AD774X_CAP_GAINL, data);
> +		chip->cap_gain = data;
> +		return len;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static IIO_DEV_ATTR_CAP_GAIN(S_IRUGO | S_IWUSR,
> +		ad774x_show_cap_gain,
> +		ad774x_store_cap_gain);
> +
> +static ssize_t ad774x_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad774x_show_name, NULL, 0);
> +
> +static struct attribute *ad774x_attributes[] = {
> +	&iio_dev_attr_available_conversion_modes.dev_attr.attr,
> +	&iio_dev_attr_conversion_mode.dev_attr.attr,
> +	&iio_dev_attr_cap_setup.dev_attr.attr,
> +	&iio_dev_attr_vt_setup.dev_attr.attr,
> +	&iio_dev_attr_exec_setup.dev_attr.attr,
> +	&iio_dev_attr_cap_offs.dev_attr.attr,
> +	&iio_dev_attr_cap_gain.dev_attr.attr,
> +	&iio_dev_attr_volt_gain.dev_attr.attr,
> +	&iio_dev_attr_cap_data.dev_attr.attr,
> +	&iio_dev_attr_daca_value.dev_attr.attr,
> +	&iio_dev_attr_dacb_value.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad774x_attribute_group = {
> +	.attrs = ad774x_attributes,
> +};
> +
> +/*
> + * data ready events
> + */
> +
> +#define IIO_EVENT_CODE_CAP_RDY     (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
> +#define IIO_EVENT_CODE_VT_RDY      (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
Depending on speed these might want to be handled as triggers rather than
generic events (moves acting on them into the kernel rather than userspace).
Naming may require some thought. This is the first device to do seperate
data ready signals for different channels.  hohum... I'm open to suggestions...
> +
> +#define IIO_EVENT_ATTR_CAP_RDY_SH(_evlist, _show, _store, _mask)	\
> +	IIO_EVENT_ATTR_SH(cap_rdy, _evlist, _show, _store, _mask)
> +
> +#define IIO_EVENT_ATTR_VT_RDY_SH(_evlist, _show, _store, _mask)	\
> +	IIO_EVENT_ATTR_SH(vt_rdy, _evlist, _show, _store, _mask)
> +
> +static void ad774x_interrupt_handler_bh(struct work_struct *work_s)
> +{
> +	struct ad774x_chip_info *chip =
> +		container_of(work_s, struct ad774x_chip_info, thresh_work);
> +	u8 int_status;
> +
> +	enable_irq(chip->client->irq);
> +
> +	ad774x_i2c_read(chip, AD774X_STATUS, &int_status, 1);
> +
> +	if (int_status & AD774X_STATUS_RDYCAP)
> +		iio_push_event(chip->indio_dev, 0,
> +				IIO_EVENT_CODE_CAP_RDY,
> +				chip->last_timestamp);
> +
> +	if (int_status & AD774X_STATUS_RDYVT)
> +		iio_push_event(chip->indio_dev, 0,
> +				IIO_EVENT_CODE_VT_RDY,
> +				chip->last_timestamp);
> +}
> +
> +static int ad774x_interrupt_handler_th(struct iio_dev *dev_info,
> +		int index,
> +		s64 timestamp,
> +		int no_test)
> +{
> +	struct ad774x_chip_info *chip = dev_info->dev_data;
> +
> +	chip->last_timestamp = timestamp;
> +	schedule_work(&chip->thresh_work);
> +
> +	return 0;
> +}
> +
> +IIO_EVENT_SH(data_rdy, &ad774x_interrupt_handler_th);
> +
> +static ssize_t ad774x_query_out_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	/*
> +	 * AD774X provides one /RDY pin, which can be used as interrupt
> +	 * but the pin is not configurable
> +	 */
> +	return sprintf(buf, "1\n");
> +}
> +
> +static ssize_t ad774x_set_out_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return len;
> +}
Looks like we need to allow for devices that don't allow event's
to be turned off. We can clean this up after this merges.
> +
> +IIO_EVENT_ATTR_CAP_RDY_SH(iio_event_data_rdy, ad774x_query_out_mode, ad774x_set_out_mode, 0);
> +IIO_EVENT_ATTR_VT_RDY_SH(iio_event_data_rdy, ad774x_query_out_mode, ad774x_set_out_mode, 0);
> +
> +static struct attribute *ad774x_event_attributes[] = {
> +	&iio_event_attr_cap_rdy.dev_attr.attr,
> +	&iio_event_attr_vt_rdy.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group ad774x_event_attribute_group = {
> +	.attrs = ad774x_event_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit ad774x_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	int ret = 0, regdone = 0;
> +	struct ad774x_chip_info *chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +	if (chip == NULL) {
> +		ret = -ENOMEM;
> +		goto error_ret;
> +	}
> +
> +	/* this is only used for device removal purposes */
> +	i2c_set_clientdata(client, chip);
> +
> +	chip->client = client;
> +	chip->name = id->name;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	/* Echipabilish that the iio_dev is a child of the i2c device */
Establish.
> +	chip->indio_dev->dev.parent = &client->dev;
> +	chip->indio_dev->attrs = &ad774x_attribute_group;
> +	chip->indio_dev->event_attrs = &ad774x_event_attribute_group;
> +	chip->indio_dev->dev_data = (void *)(chip);
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->num_interrupt_lines = 1;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +	regdone = 1;
> +
> +	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0) {
> +		ret = iio_register_interrupt_line(client->irq,
> +				chip->indio_dev,
> +				0,
> +				IRQF_TRIGGER_FALLING,
> +				"ad774x");
> +		if (ret)
> +			goto error_free_dev;
> +
> +		iio_add_event_to_list(iio_event_attr_cap_rdy.listel,
> +				&chip->indio_dev->interrupts[0]->ev_list);
> +
> +		INIT_WORK(&chip->thresh_work, ad774x_interrupt_handler_bh);
> +	}
> +
> +	dev_err(&client->dev, "%s capacitive sensor registered, irq: %d\n", id->name, client->irq);
> +
> +	return 0;
> +
> +error_free_dev:
> +	if (regdone)
> +		iio_device_unregister(chip->indio_dev);
> +	else
> +		iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +error_ret:
> +	return ret;
> +}
> +
> +static int __devexit ad774x_remove(struct i2c_client *client)
> +{
> +	struct ad774x_chip_info *chip = i2c_get_clientdata(client);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	if (client->irq && gpio_is_valid(irq_to_gpio(client->irq)) > 0)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ad774x_id[] = {
> +	{ "ad7745", 0 },
> +	{ "ad7746", 0 },
> +	{ "ad7747", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ad774x_id);
> +
> +static struct i2c_driver ad774x_driver = {
> +	.driver = {
> +		.name = "ad774x",
> +	},
> +	.probe = ad774x_probe,
> +	.remove = __devexit_p(ad774x_remove),
> +	.id_table = ad774x_id,
> +};
> +
> +static __init int ad774x_init(void)
> +{
> +	return i2c_add_driver(&ad774x_driver);
> +}
> +
> +static __exit void ad774x_exit(void)
> +{
> +	i2c_del_driver(&ad774x_driver);
> +}
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices ad7745/6/7 capacitive sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(ad774x_init);
> +module_exit(ad774x_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-23 20:29 ` [PATCH 12/14] staging: iio: adc: new driver for ADT7408 " Mike Frysinger
@ 2010-10-24 22:53   ` Jonathan Cameron
  2010-10-24 23:47     ` Guenter Roeck
                       ` (2 more replies)
  0 siblings, 3 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 22:53 UTC (permalink / raw)
  To: Mike Frysinger
  Cc: linux-iio, device-drivers-devel, Sonic Zhang, Guenter Roeck

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Sonic Zhang <sonic.zhang@analog.com>
Here we enter new territory. This device is already
supported in hwmon.  Do we have a usecase that is not
covered by that driver? 

If this is only for hardware monitoring by Guenter (cc'd) can
perhaps advise on how to support everything you have here...

I'm personally not against having drivers in IIO for devices
supported elsewhere, but the requirements for justification
are rather higher. Also care is needed to ensure no issues with
platform data etc.

Guenter, for your information we have a set of temp drivers coming,
as a small element of a larger set, from Analog's tree. Those
I've reviewed so far have wanted to use IIO's event infrastructure
(which is much more general than hwmon's handling of alarms)
or have been suitably high performance devices with general
adc's to satisfy me that they clearly have uses beyond
hardware monitoring.


> 
> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/adc/Kconfig   |    7 +
>  drivers/staging/iio/adc/Makefile  |    1 +
>  drivers/staging/iio/adc/adt7408.c | 1006 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 1014 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/adt7408.c
> 
> diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
> index 5d13918..ea75700 100644
> --- a/drivers/staging/iio/adc/Kconfig
> +++ b/drivers/staging/iio/adc/Kconfig
> @@ -114,3 +114,10 @@ config ADT7408
>  	help
>  	  Say yes here to build support for Analog Devices ADT7408
>  	  temperature sensors.
> +
> +config ADT7410
> +	tristate "Analog Devices ADT7410 temperature sensor driver"
> +	depends on I2C
> +	help
> +	  Say yes here to build support for Analog Devices ADT7410
> +	  temperature sensors.
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index 6c11363..dc2bdbe 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -21,3 +21,4 @@ obj-$(CONFIG_AD774X) += ad774x.o
>  obj-$(CONFIG_AD7816) += ad7816.o
>  obj-$(CONFIG_ADT75) += adt75.o
>  obj-$(CONFIG_ADT7310) += adt7310.o
> +obj-$(CONFIG_ADT7408) += adt7408.o
> diff --git a/drivers/staging/iio/adc/adt7408.c b/drivers/staging/iio/adc/adt7408.c
> new file mode 100644
> index 0000000..25bd594
> --- /dev/null
> +++ b/drivers/staging/iio/adc/adt7408.c
> @@ -0,0 +1,1006 @@
> +/*
> + * ADT7408 digital temperature sensor driver supporting ADT7408
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/gpio.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/i2c.h>
> +#include <linux/rtc.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/*
> + * ADT7408 registers definition
> + */
> +
> +#define ADT7408_CAPABILITY		0
> +#define ADT7408_CONFIG			1
> +#define ADT7408_T_ALARM_HIGH		2
> +#define ADT7408_T_ALARM_LOW		3
> +#define ADT7408_T_CRIT			4
> +#define ADT7408_TEMPERATURE		5
> +#define ADT7408_MANUFACTURER_ID		6
> +#define ADT7408_DEVICE_ID		7
> +
> +/*
> + * ADT7408 capability
> + */
> +#define ADT7408_CAP_ALARM_CRIT_TRIPS	0x1
> +#define ADT7408_CAP_HIGH_PRECISION	0x2
> +#define ADT7408_CAP_WIDER_RANGE		0x4
> +#define ADT7408_CAP_T_RESOLUTION_MASK	0x18
> +#define ADT7408_CAP_T_RESOLUTION_HIGH	0x18
> +#define ADT7408_CAP_T_RESOLUTION_LOW	0x8
> +
> +/*
> + * ADT7408 config
> + */
> +#define ADT7408_EVENT_MODE		0x1
> +#define ADT7408_EVENT_POLARITY		0x2
> +#define ADT7408_EVENT_CRIT_ONLY		0x4
> +#define ADT7408_EVENT_ENABLE		0x8
> +#define ADT7408_EVENT_STATUS		0x10
> +#define ADT7408_EVENT_CLEAR		0x20
> +#define ADT7408_EVENT_ALARM_LOCK	0x40
> +#define ADT7408_EVENT_CRIT_LOCK		0x80
> +#define ADT7408_PD			0x100
> +#define ADT7408_HISTERESIS_MASK		0x600
> +#define ADT7408_HISTERESIS_1_5		0x200
> +#define ADT7408_HISTERESIS_3		0x400
> +#define ADT7408_HISTERESIS_6		0x600
> +
> +/*
> + * ADT7408 masks
> + */
> +#define ADT7408_BOUND_VALUE_SIGN		0x400
> +#define ADT7408_BOUND_VALUE_OFFSET		2
> +#define ADT7408_BOUND_VALUE_FLOAT_OFFSET	2
> +#define ADT7408_BOUND_VALUE_FLOAT_MASK		0x3
> +#define ADT7408_T_VALUE_SIGN			0x1000
> +#define ADT7408_T_VALUE_FLOAT_OFFSET		4
> +#define ADT7408_T_VALUE_FLOAT_MASK		0xF
> +
> +/*
> + * ADT7408 event source
> + */
> +#define ADT7408_T_BELLOW_ALARM			0x2000
> +#define ADT7408_T_ABOVE_ALARM			0x4000
> +#define ADT7408_T_ABOVE_CRIT			0x8000
> +
> +
> +/*
> + * struct adt7408_chip_info - chip specifc information
> + */
> +
> +struct adt7408_chip_info {
> +	const char *name;
> +	struct i2c_client *client;
> +	struct iio_dev *indio_dev;
> +	struct work_struct thresh_work;
> +	s64 last_timestamp;
> +	u16 config;
> +};
> +
> +/*
> + * adt7408 register access by I2C
> + */
> +
> +static int adt7408_i2c_read(struct adt7408_chip_info *chip, u8 reg, u16 *data)
> +{
> +	struct i2c_client *client = chip->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 adt7408_i2c_write(struct adt7408_chip_info *chip, u8 reg, u16 data)
> +{
> +	struct i2c_client *client = chip->client;
> +	int ret = 0;
> +
> +	ret = i2c_smbus_write_word_data(client, reg, swab16(data));
> +	if (ret < 0)
> +		dev_err(&client->dev, "I2C write error\n");
> +
> +	return ret;
> +}
> +
> +static int adt7408_is_event_locked(struct adt7408_chip_info *chip)
> +{
> +	return chip->config & (ADT7408_EVENT_ALARM_LOCK | ADT7408_EVENT_ALARM_LOCK);
> +}
> +
> +static ssize_t adt7408_show_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +
> +	if (chip->config & ADT7408_PD)
> +		return sprintf(buf, "power-save\n");
> +	else
> +		return sprintf(buf, "full\n");
> +}
> +
> +static ssize_t adt7408_store_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 config;
> +	int ret;
> +
> +	if (adt7408_is_event_locked(chip)) {
> +		dev_err(dev, "Warning: Events are locked.\n");
> +		return -EIO;
> +	}
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config = chip->config & (~ADT7408_PD);
> +	if (!strcmp(buf, "full"))
> +		config |= ADT7408_PD;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +
> +	return ret;
> +}
> +
> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
> +		adt7408_show_mode,
> +		adt7408_store_mode,
> +		0);
> +
> +static ssize_t adt7408_show_available_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "full\npower-down\n");
> +}
> +
> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO, adt7408_show_available_modes, NULL, 0);
> +
> +static ssize_t adt7408_show_capability(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 capability;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CAPABILITY, &capability);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "0x%x\n", capability);
> +}
> +
> +static IIO_DEVICE_ATTR(capability, S_IRUGO | S_IWUSR,
> +		adt7408_show_capability,
> +		NULL,
> +		0);
> +
> +static ssize_t adt7408_show_manufactory_id(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 id;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "0x%x\n", id);
> +}
> +
> +static IIO_DEVICE_ATTR(manufactory_id, S_IRUGO | S_IWUSR,
> +		adt7408_show_manufactory_id,
> +		NULL,
> +		0);
> +
> +static ssize_t adt7408_show_device_id(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 id;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "0x%x\n", id);
> +}
> +
> +static IIO_DEVICE_ATTR(device_id, S_IRUGO | S_IWUSR,
> +		adt7408_show_device_id,
> +		NULL,
> +		0);
> +
> +static ssize_t adt7408_show_value(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	if (chip->config & ADT7408_PD) {
> +		dev_err(dev, "Can't read value in power-down mode.\n");
> +		return -EIO;
> +	}
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	if (data & ADT7408_T_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data = (ADT7408_T_VALUE_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.4d\n", sign,
> +		(data >> ADT7408_T_VALUE_FLOAT_OFFSET),
> +		(data & ADT7408_T_VALUE_FLOAT_MASK) * 625);
> +}
> +
> +static IIO_DEVICE_ATTR(value, S_IRUGO, adt7408_show_value, NULL, 0);
> +
> +static ssize_t adt7408_show_name(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	return sprintf(buf, "%s\n", chip->name);
> +}
> +
> +static IIO_DEVICE_ATTR(name, S_IRUGO, adt7408_show_name, NULL, 0);
> +
> +static struct attribute *adt7408_attributes[] = {
> +	&iio_dev_attr_available_modes.dev_attr.attr,
> +	&iio_dev_attr_mode.dev_attr.attr,
> +	&iio_dev_attr_capability.dev_attr.attr,
> +	&iio_dev_attr_device_id.dev_attr.attr,
> +	&iio_dev_attr_manufactory_id.dev_attr.attr,
> +	&iio_dev_attr_value.dev_attr.attr,
> +	&iio_dev_attr_name.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group adt7408_attribute_group = {
> +	.attrs = adt7408_attributes,
> +};
> +
> +/*
> + * temperature bound events
> + */
> +
> +#define IIO_EVENT_CODE_ADT7408_ABOVE_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
> +#define IIO_EVENT_CODE_ADT7408_BELLOW_ALARM    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
> +#define IIO_EVENT_CODE_ADT7408_ABOVE_CRIT    (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
> +
> +static void adt7408_interrupt_bh(struct work_struct *work_s)
> +{
> +	struct adt7408_chip_info *chip =
> +		container_of(work_s, struct adt7408_chip_info, thresh_work);
> +	u16 config;
> +	u16 data;
> +
> +	if (adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config))
> +		return;
> +
> +	if (!(chip->config & ADT7408_EVENT_STATUS))
> +		return;
> +
> +	config = chip->config & ~ADT7408_EVENT_CLEAR;
> +	if (data)
> +		config |= ADT7408_EVENT_CLEAR;
> +
> +	adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +
> +	if (adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data))
> +		goto exit;
> +
> +	if (data & ADT7408_T_ABOVE_ALARM)
> +		iio_push_event(chip->indio_dev, 0,
> +			IIO_EVENT_CODE_ADT7408_ABOVE_ALARM,
> +			chip->last_timestamp);
> +	if (data & ADT7408_T_BELLOW_ALARM)
> +		iio_push_event(chip->indio_dev, 0,
> +			IIO_EVENT_CODE_ADT7408_BELLOW_ALARM,
> +			chip->last_timestamp);
> +	if (data & ADT7408_T_ABOVE_CRIT)
> +		iio_push_event(chip->indio_dev, 0,
> +			IIO_EVENT_CODE_ADT7408_ABOVE_CRIT,
> +			chip->last_timestamp);
> +exit:
> +	enable_irq(chip->client->irq);
> +}
> +
> +static int adt7408_interrupt(struct iio_dev *dev_info,
> +		int index,
> +		s64 timestamp,
> +		int no_test)
> +{
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +
> +	chip->last_timestamp = timestamp;
> +	schedule_work(&chip->thresh_work);
> +
> +	return 0;
> +}
> +
> +IIO_EVENT_SH(adt7408, &adt7408_interrupt);
> +
> +static ssize_t adt7408_show_event_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	if (chip->config & ADT7408_EVENT_MODE)
> +		return sprintf(buf, "interrupt\n");
> +	else
> +		return sprintf(buf, "comparator\n");
> +}
> +
> +static ssize_t adt7408_set_event_mode(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 config;
> +	int ret;
> +
> +	if (adt7408_is_event_locked(chip)) {
> +		dev_err(dev, "Warning: Events are locked.\n");
> +		return -EIO;
> +	}
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config = chip->config &= ~ADT7408_EVENT_MODE;
> +	if (strcmp(buf, "comparator") != 0)
> +		config |= ADT7408_EVENT_MODE;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +
> +	return ret;
> +}
> +
> +static ssize_t adt7408_show_available_event_modes(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "comparator\ninterrupt\n");
> +}
> +
> +static ssize_t adt7408_show_event_crit_only(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_CRIT_ONLY));
> +}
> +
> +static ssize_t adt7408_set_event_crit_only(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	unsigned long data = 0;
> +	u16 config;
> +	int ret;
> +
> +	if (adt7408_is_event_locked(chip)) {
> +		dev_err(dev, "Warning: Events are locked.\n");
> +		return -EIO;
> +	}
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config = chip->config &= ~ADT7408_EVENT_CRIT_ONLY;
> +	if (data)
> +		config |= ADT7408_EVENT_CRIT_ONLY;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +
> +	return ret;
> +}
> +
> +static ssize_t adt7408_show_event_enable(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_ENABLE));
> +}
> +
> +static ssize_t adt7408_set_event_enable(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	u16 config;
> +	int ret;
> +
> +	if (adt7408_is_event_locked(chip)) {
> +		dev_err(dev, "Warning: Events are locked.\n");
> +		return -EIO;
> +	}
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config = chip->config & ~ADT7408_EVENT_ENABLE;
> +	if (data)
> +		config |= ADT7408_EVENT_ENABLE;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +
> +	return ret;
> +}
> +
> +static ssize_t adt7408_show_alarm_lock(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_ALARM_LOCK));
> +}
> +
> +static ssize_t adt7408_set_alarm_lock(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	u16 config;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config = chip->config & ~ADT7408_EVENT_ALARM_LOCK;
> +	if (data)
> +		config |= ADT7408_EVENT_ALARM_LOCK;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +
> +	return ret;
> +}
> +
> +static ssize_t adt7408_show_crit_lock(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	return sprintf(buf, "%d\n", !!(chip->config & ADT7408_EVENT_CRIT_LOCK));
> +}
> +
> +static ssize_t adt7408_set_crit_lock(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	unsigned long data;
> +	u16 config;
> +	int ret;
> +
> +	ret = strict_strtoul(buf, 10, &data);
> +	if (ret)
> +		return -EINVAL;
> +
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config = chip->config & ~ADT7408_EVENT_CRIT_LOCK;
> +	if (data)
> +		config |= ADT7408_EVENT_CRIT_LOCK;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +
> +	return ret;
> +}
> +
> +
> +static inline ssize_t adt7408_show_t_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	u16 data;
> +	char sign = ' ';
> +	int ret;
> +
> +	ret = adt7408_i2c_read(chip, bound_reg, &data);
> +	if (ret)
> +		return -EIO;
> +
> +	data >>= ADT7408_BOUND_VALUE_OFFSET;
> +	if (data & ADT7408_BOUND_VALUE_SIGN) {
> +		/* convert supplement to positive value */
> +		data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
> +		sign = '-';
> +	}
> +
> +	return sprintf(buf, "%c%d.%.2d\n", sign,
> +			data >> ADT7408_BOUND_VALUE_FLOAT_OFFSET,
> +			(data & ADT7408_BOUND_VALUE_FLOAT_MASK) * 25);
> +}
> +
> +static inline ssize_t adt7408_set_t_bound(struct device *dev,
> +		struct device_attribute *attr,
> +		u8 bound_reg,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	long tmp1, tmp2;
> +	u16 data;
> +	char *pos;
> +	int ret;
> +
> +	pos = strchr(buf, '.');
> +
> +	ret = strict_strtol(buf, 10, &tmp1);
> +
> +	if (ret || tmp1 > 127 || tmp1 < -128)
> +		return -EINVAL;
> +
> +	if (pos) {
> +		len = strlen(pos);
> +		if (len > ADT7408_BOUND_VALUE_FLOAT_OFFSET)
> +			len = ADT7408_BOUND_VALUE_FLOAT_OFFSET;
> +		pos[len] = 0;
> +		ret = strict_strtol(pos, 10, &tmp2);
> +
> +		if (!ret)
> +			tmp2 = (tmp2 / 25) * 25;
> +	}
> +
> +	if (tmp1 < 0)
> +		data = (u16)(-tmp1);
> +	else
> +		data = (u16)tmp1;
> +	data = (data << ADT7408_BOUND_VALUE_FLOAT_OFFSET) |
> +		(tmp2 & ADT7408_BOUND_VALUE_FLOAT_MASK);
> +	if (tmp1 < 0)
> +		/* convert positive value to supplyment */
> +		data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
> +	data <<= ADT7408_BOUND_VALUE_OFFSET;
> +
> +	ret = adt7408_i2c_write(chip, bound_reg, data);
> +	if (ret)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static ssize_t adt7408_show_t_alarm_high(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return adt7408_show_t_bound(dev, attr,
> +			ADT7408_T_ALARM_HIGH, buf);
> +}
> +
> +static inline ssize_t adt7408_set_t_alarm_high(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return adt7408_set_t_bound(dev, attr,
> +			ADT7408_T_ALARM_HIGH, buf, len);
> +}
> +
> +static ssize_t adt7408_show_t_alarm_low(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return adt7408_show_t_bound(dev, attr,
> +			ADT7408_T_ALARM_LOW, buf);
> +}
> +
> +static inline ssize_t adt7408_set_t_alarm_low(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return adt7408_set_t_bound(dev, attr,
> +			ADT7408_T_ALARM_LOW, buf, len);
> +}
> +
> +static ssize_t adt7408_show_t_crit(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return adt7408_show_t_bound(dev, attr,
> +			ADT7408_T_CRIT, buf);
> +}
> +
> +static inline ssize_t adt7408_set_t_crit(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	return adt7408_set_t_bound(dev, attr,
> +			ADT7408_T_CRIT, buf, len);
> +}
> +
> +static ssize_t adt7408_show_t_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +
> +	/* retrive ALART status */
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	switch (chip->config & ADT7408_HISTERESIS_MASK) {
> +	case ADT7408_HISTERESIS_1_5:
> +		return sprintf(buf, "1.5\n");
> +	case ADT7408_HISTERESIS_3:
> +		return sprintf(buf, "3\n");
> +	case ADT7408_HISTERESIS_6:
> +		return sprintf(buf, "6\n");
> +	default:
> +		return sprintf(buf, "Disabled\n");
> +	}
> +}
> +
> +static inline ssize_t adt7408_set_t_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct adt7408_chip_info *chip = dev_info->dev_data;
> +	int ret;
> +	u16 config = 0;
> +
> +	if (strcmp(buf, "disble"))
> +		config = ADT7408_HISTERESIS_MASK;
> +	else if (strcmp(buf, "1.5"))
> +		config = ADT7408_HISTERESIS_1_5;
> +	else if (len > 1 && buf[0] == '3')
> +		config = ADT7408_HISTERESIS_6;
> +	else if (len > 1 && buf[0] == '6')
> +		config = ADT7408_HISTERESIS_6;
> +
> +	if (!config)
> +		return -EINVAL;
> +
> +	/* retrive ALART status */
> +	ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +	if (ret)
> +		return -EIO;
> +
> +	config |= chip->config & ~ADT7408_HISTERESIS_MASK;
> +
> +	ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
> +	if (ret)
> +		return -EIO;
> +
> +	chip->config = config;
> +	return ret;
> +}
> +
> +static ssize_t adt7408_show_available_t_hyst(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	return sprintf(buf, "1.5\n3\n6\ndisable\n");
> +}
> +
> +IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7408,
> +		adt7408_show_event_mode, adt7408_set_event_mode, 0);
> +IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7408,
> +		adt7408_show_available_event_modes, NULL, 0);
> +IIO_EVENT_ATTR_SH(event_crit_only, iio_event_adt7408,
> +		adt7408_show_event_crit_only, adt7408_set_event_crit_only, 0);
> +IIO_EVENT_ATTR_SH(event_enable, iio_event_adt7408,
> +		adt7408_show_event_enable, adt7408_set_event_enable, 0);
> +IIO_EVENT_ATTR_SH(alarm_lock, iio_event_adt7408,
> +		adt7408_show_alarm_lock, adt7408_set_alarm_lock, 0);
> +IIO_EVENT_ATTR_SH(crit_lock, iio_event_adt7408,
> +		adt7408_show_crit_lock, adt7408_set_crit_lock, 0);
> +IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7408,
> +		adt7408_show_t_alarm_high, adt7408_set_t_alarm_high, 0);
> +IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7408,
> +		adt7408_show_t_alarm_low, adt7408_set_t_alarm_low, 0);
> +IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7408,
> +		adt7408_show_t_crit, adt7408_set_t_crit, 0);
> +IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7408,
> +		adt7408_show_t_hyst, adt7408_set_t_hyst, 0);
> +IIO_EVENT_ATTR_SH(available_t_hyst, iio_event_adt7408,
> +		adt7408_show_available_t_hyst, NULL, 0);
> +
> +static struct attribute *adt7408_event_attributes[] = {
> +	&iio_event_attr_event_mode.dev_attr.attr,
> +	&iio_event_attr_available_event_modes.dev_attr.attr,
> +	&iio_event_attr_event_crit_only.dev_attr.attr,
> +	&iio_event_attr_event_enable.dev_attr.attr,
> +	&iio_event_attr_alarm_lock.dev_attr.attr,
> +	&iio_event_attr_crit_lock.dev_attr.attr,
> +	&iio_event_attr_t_alarm_high.dev_attr.attr,
> +	&iio_event_attr_t_alarm_low.dev_attr.attr,
> +	&iio_event_attr_t_crit.dev_attr.attr,
> +	&iio_event_attr_t_hyst.dev_attr.attr,
> +	&iio_event_attr_available_t_hyst.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group adt7408_event_attribute_group = {
> +	.attrs = adt7408_event_attributes,
> +};
> +
> +/*
> + * device probe and remove
> + */
> +
> +static int __devinit adt7408_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	struct adt7408_chip_info *chip;
> +	int ret = 0;
> +
> +	chip = kzalloc(sizeof(struct adt7408_chip_info), GFP_KERNEL);
> +
> +	if (chip == NULL)
> +		return -ENOMEM;
> +
> +	/* this is only used for device removal purposes */
> +	i2c_set_clientdata(client, chip);
> +
> +	chip->client = client;
> +	chip->name = id->name;
> +
> +	chip->indio_dev = iio_allocate_device();
> +	if (chip->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_chip;
> +	}
> +
> +	chip->indio_dev->dev.parent = &client->dev;
> +	chip->indio_dev->attrs = &adt7408_attribute_group;
> +	chip->indio_dev->event_attrs = &adt7408_event_attribute_group;
> +	chip->indio_dev->dev_data = (void *)chip;
> +	chip->indio_dev->driver_module = THIS_MODULE;
> +	chip->indio_dev->num_interrupt_lines = 1;
> +	chip->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = iio_device_register(chip->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	if (client->irq) {
> +		ret = iio_register_interrupt_line(client->irq,
> +				chip->indio_dev,
> +				0,
> +				client->irq_flags,
> +				chip->name);
> +		if (ret)
> +			goto error_unreg_dev;
> +
> +		/*
> +		 * The event handler list element refer to iio_event_adt7408.
> +		 * All event attributes bind to the same event handler.
> +		 * So, only register event handler once.
> +		 */
> +		iio_add_event_to_list(&iio_event_adt7408,
> +				&chip->indio_dev->interrupts[0]->ev_list);
> +
> +		INIT_WORK(&chip->thresh_work, adt7408_interrupt_bh);
> +
> +		ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
> +		if (ret) {
> +			ret = -EIO;
> +			goto error_unreg_irq;
> +		}
> +
> +		if (client->irq_flags & IRQF_TRIGGER_HIGH)
> +			chip->config |= ADT7408_EVENT_POLARITY;
> +		else
> +			chip->config &= ~ADT7408_EVENT_POLARITY;
> +
> +		ret = adt7408_i2c_write(chip, ADT7408_CONFIG, chip->config);
> +		if (ret) {
> +			ret = -EIO;
> +			goto error_unreg_irq;
> +		}
> +	}
> +
> +	dev_info(&client->dev, "%s temperature sensor registered.\n",
> +			 id->name);
> +
> +	return 0;
> +
> +error_unreg_irq:
> +	iio_unregister_interrupt_line(chip->indio_dev, 0);
> +error_unreg_dev:
> +	iio_device_unregister(chip->indio_dev);
> +error_free_dev:
> +	iio_free_device(chip->indio_dev);
> +error_free_chip:
> +	kfree(chip);
> +
> +	return ret;
> +}
> +
> +static int __devexit adt7408_remove(struct i2c_client *client)
> +{
> +	struct adt7408_chip_info *chip = i2c_get_clientdata(client);
> +	struct iio_dev *indio_dev = chip->indio_dev;
> +
> +	if (client->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +	iio_device_unregister(indio_dev);
> +	iio_free_device(chip->indio_dev);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id adt7408_id[] = {
> +	{ "adt7408", 0 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, adt7408_id);
> +
> +static struct i2c_driver adt7408_driver = {
> +	.driver = {
> +		.name = "adt7408",
> +	},
> +	.probe = adt7408_probe,
> +	.remove = __devexit_p(adt7408_remove),
> +	.id_table = adt7408_id,
> +};
> +
> +static __init int adt7408_init(void)
> +{
> +	return i2c_add_driver(&adt7408_driver);
> +}
> +
> +static __exit void adt7408_exit(void)
> +{
> +	i2c_del_driver(&adt7408_driver);
> +}
> +
> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
> +MODULE_DESCRIPTION("Analog Devices ADT7408 digital"
> +			" temperature sensor driver");
> +MODULE_LICENSE("GPL v2");
> +
> +module_init(adt7408_init);
> +module_exit(adt7408_exit);


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 14/14] staging: iio: adc: new ad799x driver
  2010-10-23 20:29 ` [PATCH 14/14] staging: iio: adc: new ad799x driver Mike Frysinger
  2010-10-24 21:14   ` [Device-drivers-devel] " Mike Frysinger
@ 2010-10-24 22:55   ` Jonathan Cameron
  1 sibling, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-24 22:55 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Michael Hennerich

On 10/23/10 21:29, Mike Frysinger wrote:
> From: Michael Hennerich <michael.hennerich@analog.com>
> 
> Driver for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997 and
> ad7998 multichannel ADC.
Another one that has already merged.


> 
> Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/adc/Kconfig       |   20 +
>  drivers/staging/iio/adc/Makefile      |    4 +
>  drivers/staging/iio/adc/ad799x.h      |  168 ++++++
>  drivers/staging/iio/adc/ad799x_core.c |  912 +++++++++++++++++++++++++++++++++
>  drivers/staging/iio/adc/ad799x_ring.c |  281 ++++++++++
>  5 files changed, 1385 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/adc/ad799x.h
>  create mode 100644 drivers/staging/iio/adc/ad799x_core.c
>  create mode 100644 drivers/staging/iio/adc/ad799x_ring.c
...

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-24 22:53   ` Jonathan Cameron
@ 2010-10-24 23:47     ` Guenter Roeck
  2010-10-25 10:28       ` Jonathan Cameron
  2010-10-26  4:20       ` Zhang, Sonic
  2010-10-25  0:46     ` Guenter Roeck
  2010-10-26  3:27     ` Zhang, Sonic
  2 siblings, 2 replies; 48+ messages in thread
From: Guenter Roeck @ 2010-10-24 23:47 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On Sun, Oct 24, 2010 at 06:53:44PM -0400, Jonathan Cameron wrote:
> On 10/23/10 21:29, Mike Frysinger wrote:
> > From: Sonic Zhang <sonic.zhang@analog.com>
> Here we enter new territory. This device is already
> supported in hwmon.  Do we have a usecase that is not
> covered by that driver?
> 
> If this is only for hardware monitoring by Guenter (cc'd) can
> perhaps advise on how to support everything you have here...
> 

device_id and manufactory_id (shouldn't that be manufacturer_id ?) don't provide
much value since they are (or could be) already part of the device name.

"capability" reflects a raw register value. I would be opposed to any attribute
like that, since it does not mean anything beyond the driver for that specific chip.
The data it provides might make sense in the driver documentation, but what
is anyone supposed to do with the returned hex value ? That kind of ABI really
does not make any sense. If the reported values are really valuable enough to be
reported, a real abi (eg tempX_accuracy and tempX_resolution) would be much better.

For "power-save" (or "power-down" ?) and "full" mode I am not sure
why suspend/resume isn't used instead. Is that some new upcoming ABI ?

> I'm personally not against having drivers in IIO for devices
> supported elsewhere, but the requirements for justification
> are rather higher. Also care is needed to ensure no issues with
> platform data etc.
> 
> Guenter, for your information we have a set of temp drivers coming,
> as a small element of a larger set, from Analog's tree. Those
> I've reviewed so far have wanted to use IIO's event infrastructure
> (which is much more general than hwmon's handling of alarms)
> or have been suitably high performance devices with general
> adc's to satisfy me that they clearly have uses beyond
> hardware monitoring.
> 
There should be a generic part and a specific part for such generic devices.
The generic part can be in mfd or somewhere suitable (such as iio), but
the hwmon part should reside in hwmon.

The worst thing to do would be to duplicate functionality as it is proposed here.
That can only create chaos.

If the event mechanism in hwmon is deemed insufficient enough to cause drivers
to move elsewhere, maybe event mechanism in hwmon should be improved instead.
Or, even better, if there is a really need for a more detailed / improved event
mechanism, there should be a single event subsystem that works for both iio
and hwmon.

In short, if the hwmon ABI is deemed to be insufficient to support today's devices,
for whaever reason, it needs to get fixed. Moving hwmon drivers elsewhere to
avoid its perceived (or real) limitations should be an absolute no.

Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-24 22:53   ` Jonathan Cameron
  2010-10-24 23:47     ` Guenter Roeck
@ 2010-10-25  0:46     ` Guenter Roeck
  2010-10-25 10:32       ` Jonathan Cameron
  2010-10-26  3:27     ` Zhang, Sonic
  2 siblings, 1 reply; 48+ messages in thread
From: Guenter Roeck @ 2010-10-25  0:46 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On Sun, Oct 24, 2010 at 06:53:44PM -0400, Jonathan Cameron wrote:
> On 10/23/10 21:29, Mike Frysinger wrote:
> > From: Sonic Zhang <sonic.zhang@analog.com>
> Here we enter new territory. This device is already
> supported in hwmon.  Do we have a usecase that is not
> covered by that driver?
> 
> If this is only for hardware monitoring by Guenter (cc'd) can
> perhaps advise on how to support everything you have here...
> 
> I'm personally not against having drivers in IIO for devices
> supported elsewhere, but the requirements for justification
> are rather higher. Also care is needed to ensure no issues with
> platform data etc.
> 
> Guenter, for your information we have a set of temp drivers coming,
> as a small element of a larger set, from Analog's tree. Those
> I've reviewed so far have wanted to use IIO's event infrastructure
> (which is much more general than hwmon's handling of alarms)
> or have been suitably high performance devices with general
> adc's to satisfy me that they clearly have uses beyond
> hardware monitoring.
> 
Browsing through the adt patch list, there are several other drivers
which either already exist in hwmon, or should be there.

I'd love to see some reasoning why hardware monitoring drivers are
moved to or directly written in iio.

Also, I seem to be missing your point re "high performance devices".
Are you saying that hwmon is not suitable for high performance
hardware monitoring devices ?

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-24 23:47     ` Guenter Roeck
@ 2010-10-25 10:28       ` Jonathan Cameron
  2010-10-26  4:20       ` Zhang, Sonic
  1 sibling, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-25 10:28 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On 10/25/10 00:47, Guenter Roeck wrote:
> On Sun, Oct 24, 2010 at 06:53:44PM -0400, Jonathan Cameron wrote:
>> On 10/23/10 21:29, Mike Frysinger wrote:
>>> From: Sonic Zhang <sonic.zhang@analog.com>
>> Here we enter new territory. This device is already
>> supported in hwmon.  Do we have a usecase that is not
>> covered by that driver?
>>
>> If this is only for hardware monitoring by Guenter (cc'd) can
>> perhaps advise on how to support everything you have here...
>>
> 
> device_id and manufactory_id (shouldn't that be manufacturer_id ?) don't provide
> much value since they are (or could be) already part of the device name.
Agreed, they'll get dropped whatever happens to this driver. It's far from
clean as it currently stands.
> 
> "capability" reflects a raw register value. I would be opposed to any attribute
> like that, since it does not mean anything beyond the driver for that specific chip.
Agreed, that's certainly not staying there as is.  The intent is to merge
this series (36 drivers in all) into staging and clean up there.  Analog have
given commitments that they will put the man power on it.
> The data it provides might make sense in the driver documentation, but what
> is anyone supposed to do with the returned hex value ? That kind of ABI really
> does not make any sense. If the reported values are really valuable enough to be
> reported, a real abi (eg tempX_accuracy and tempX_resolution) would be much better.
> 
> For "power-save" (or "power-down" ?) and "full" mode I am not sure
> why suspend/resume isn't used instead. Is that some new upcoming ABI ?
Nope, it's a dirty hack instead of using the runtime pm (which to be fair is still
in relatively early days)  It'll go in the clean up (as for that matter will
any 'mode' type attributes as they never standardize well.). 
> 
>> I'm personally not against having drivers in IIO for devices
>> supported elsewhere, but the requirements for justification
>> are rather higher. Also care is needed to ensure no issues with
>> platform data etc.
>>
>> Guenter, for your information we have a set of temp drivers coming,
>> as a small element of a larger set, from Analog's tree. Those
>> I've reviewed so far have wanted to use IIO's event infrastructure
>> (which is much more general than hwmon's handling of alarms)
>> or have been suitably high performance devices with general
>> adc's to satisfy me that they clearly have uses beyond
>> hardware monitoring.
>>
> There should be a generic part and a specific part for such generic devices.
> The generic part can be in mfd or somewhere suitable (such as iio), but
> the hwmon part should reside in hwmon.
Tricky to agree on the divide.  Note we do match your interfaces anyway
(where they exist). The only exception to this is your alarm interfaces which
are way too restrictive (as one would expect they only cover cases that make
sense for hardware monitoring!)

We have numerous devices (including accelerometers, which you are quite rightly trying to
get out of hwmon) that have a voltage supply monitor channel.  It might seem obvious
that we should add a hwmon element to this but the value is only of interest alongside
the measurements from gyroscopes etc.  Typically it will be captured and buffered at
a couple of Khz which really doesn't make it fit into the scope of hwmon.  Anyone who
uses $400 of IMU as their voltage monitor gets to deal with the consequences.
> 
> The worst thing to do would be to duplicate functionality as it is proposed here.
> That can only create chaos.
It's an interesting boundary for what should go where and as per the discussions
about accelerometers going into input, I think it has to be decided based on what
people are using the device for.
> 
> If the event mechanism in hwmon is deemed insufficient enough to cause drivers
> to move elsewhere, maybe event mechanism in hwmon should be improved instead.
This comes back to the reason IIO started in the first place.  
hwmon is for hardware monitoring and hence has a different set of requirements from
generic sensing.  Basically you want to do things slowly.  There is no earthly point
in monitoring at 1MHz.  The problem lies in chips that sit on the edge.  There are plenty
of high end ADCs running at 1Hz or less to track some battery voltage or similar. If that's
what they are used for then I agree they should be in hwmon.  If you want to do high
speed capture or fast event capture then it is a non starter.  There is a reason IIO
is a whole lot more complex than hwmon. Requirements demand it.

> Or, even better, if there is a really need for a more detailed / improved event
> mechanism, there should be a single event subsystem that works for both iio
> and hwmon.
That's the equivalent of Linus' argument that all devices should use input's event
subsystem. It's massive overkill unless you want sophisticated amalgamation and filtering
of the incoming event stream. Also input are worried about bloat in their event structures
so are very much against adding anything that isn't a true human input device.
There may be some scope for sharing at some level but I don't think the whole event
mechanism can realistically be shared.  The requirements are simply too different.
For reference the way we handle the conversion from IIO events (which are more specific
and streamlined than input ones - their events include standard data, for reasons of
overhead our data goes a different route) for the odd device that sits at the boundary
of IIO and input is a userspace loopback tool via uinput (proof of concept only at this stage).
> 
> In short, if the hwmon ABI is deemed to be insufficient to support today's devices,
> for whaever reason, it needs to get fixed. Moving hwmon drivers elsewhere to
> avoid its perceived (or real) limitations should be an absolute no.
I agree. The issue here is where we draw the divide. I am very dubious about this particular
driver going in IIO simply because it doesn't seem to need any of the stuff that we exist
for.  Others are far less clear.

Jonathan
> Guenter
> 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25  0:46     ` Guenter Roeck
@ 2010-10-25 10:32       ` Jonathan Cameron
  2010-10-25 11:19         ` Guenter Roeck
  0 siblings, 1 reply; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-25 10:32 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On 10/25/10 01:46, Guenter Roeck wrote:
> On Sun, Oct 24, 2010 at 06:53:44PM -0400, Jonathan Cameron wrote:
>> On 10/23/10 21:29, Mike Frysinger wrote:
>>> From: Sonic Zhang <sonic.zhang@analog.com>
>> Here we enter new territory. This device is already
>> supported in hwmon.  Do we have a usecase that is not
>> covered by that driver?
>>
>> If this is only for hardware monitoring by Guenter (cc'd) can
>> perhaps advise on how to support everything you have here...
>>
>> I'm personally not against having drivers in IIO for devices
>> supported elsewhere, but the requirements for justification
>> are rather higher. Also care is needed to ensure no issues with
>> platform data etc.
>>
>> Guenter, for your information we have a set of temp drivers coming,
>> as a small element of a larger set, from Analog's tree. Those
>> I've reviewed so far have wanted to use IIO's event infrastructure
>> (which is much more general than hwmon's handling of alarms)
>> or have been suitably high performance devices with general
>> adc's to satisfy me that they clearly have uses beyond
>> hardware monitoring.
>>
> Browsing through the adt patch list, there are several other drivers
> which either already exist in hwmon, or should be there.
> 
> I'd love to see some reasoning why hardware monitoring drivers are
> moved to or directly written in iio.
> 
> Also, I seem to be missing your point re "high performance devices".
> Are you saying that hwmon is not suitable for high performance
> hardware monitoring devices ?
Yes.  Point me at someone doing 1MSps or higher via pretty printing through
a sysfs interface.  Admittedly none of the controversial drivers in this
set do that currently either, but that's why I have asked Analog to confirm
what they are using them for.  The point is that these devices are only
hardware monitoring to you because that is what you think they are for.
Some of them (not the one I forwarded initially) are general purpose ADC's
that have a temp sensor because the temperature can effect the calibration
of the outputs.

We went through this in a lot of depth back when IIO first came about.
There is a boundary. We just need to pin down where it is.
> 
> Thanks,
> Guenter
> 
> 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25 10:32       ` Jonathan Cameron
@ 2010-10-25 11:19         ` Guenter Roeck
  2010-10-25 11:43           ` Jonathan Cameron
  2010-10-25 11:47           ` [Device-drivers-devel] " Hennerich, Michael
  0 siblings, 2 replies; 48+ messages in thread
From: Guenter Roeck @ 2010-10-25 11:19 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On Mon, Oct 25, 2010 at 06:32:50AM -0400, Jonathan Cameron wrote:
> > 
> > I'd love to see some reasoning why hardware monitoring drivers are
> > moved to or directly written in iio.
> > 
> > Also, I seem to be missing your point re "high performance devices".
> > Are you saying that hwmon is not suitable for high performance
> > hardware monitoring devices ?
> Yes.  Point me at someone doing 1MSps or higher via pretty printing through
> a sysfs interface.  Admittedly none of the controversial drivers in this

Ok.

> set do that currently either, but that's why I have asked Analog to confirm
> what they are using them for.  The point is that these devices are only
> hardware monitoring to you because that is what you think they are for.
> Some of them (not the one I forwarded initially) are general purpose ADC's
> that have a temp sensor because the temperature can effect the calibration
> of the outputs.
> 
I did not refer to the chips with generic ADC sensors.
The chips I referred to are AD7414/15, ADT75, ADT7310, ADT7408, and ADT7410,
though I may have missed some.

> We went through this in a lot of depth back when IIO first came about.
> There is a boundary. We just need to pin down where it is.

For the ambient temperature sensors on the other chips - did you consider
adding hwmon device entries for those ? There may of course be reasons against
doing that, but it may be an option. There are other drivers outside the hwmon 
directory which call hwmon_device_register(), so it is not a new concept.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25 11:19         ` Guenter Roeck
@ 2010-10-25 11:43           ` Jonathan Cameron
  2010-10-25 14:12             ` Guenter Roeck
  2010-10-25 11:47           ` [Device-drivers-devel] " Hennerich, Michael
  1 sibling, 1 reply; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-25 11:43 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On 10/25/10 12:19, Guenter Roeck wrote:
> On Mon, Oct 25, 2010 at 06:32:50AM -0400, Jonathan Cameron wrote:
>>>
>>> I'd love to see some reasoning why hardware monitoring drivers are
>>> moved to or directly written in iio.
>>>
>>> Also, I seem to be missing your point re "high performance devices".
>>> Are you saying that hwmon is not suitable for high performance
>>> hardware monitoring devices ?
>> Yes.  Point me at someone doing 1MSps or higher via pretty printing through
>> a sysfs interface.  Admittedly none of the controversial drivers in this
> 
> Ok.
> 
>> set do that currently either, but that's why I have asked Analog to confirm
>> what they are using them for.  The point is that these devices are only
>> hardware monitoring to you because that is what you think they are for.
>> Some of them (not the one I forwarded initially) are general purpose ADC's
>> that have a temp sensor because the temperature can effect the calibration
>> of the outputs.
>>
> I did not refer to the chips with generic ADC sensors.
> The chips I referred to are AD7414/15, ADT75, ADT7310, ADT7408, and ADT7410,
> though I may have missed some.
You clearly got further through the set than I have so far! (I'd only come across
the first 2 of those)  Agreed, all of these appear to be temperature only and at
a quick look they are typically slow and low resolution so that set definitely
want to be in hwmon.

For those parts with actual hwmon drivers, ad7414/15 and adt7408 (based on a quick
grep unless Guenter has others queued). Lets drop them for now from the merge.

For the others lets put a todo in place to convert them to hwmon.  Nothing wrong
with putting them in staging (under IIO or otherwise) in the meantime as far
as I am concerned.

> 
>> We went through this in a lot of depth back when IIO first came about.
>> There is a boundary. We just need to pin down where it is.
> 
> For the ambient temperature sensors on the other chips - did you consider
> adding hwmon device entries for those ? There may of course be reasons against
> doing that, but it may be an option. There are other drivers outside the hwmon 
> directory which call hwmon_device_register(), so it is not a new concept.
It depends on whether they are generally useful for temperature monitoring.
On the whole they are giving one the value on a particular bit of silicon
in the chip (not the ambient temperature near by). They aren't reading it for
monitoring purposes, but because it is needed to calibrate the other sensors in
the package.  We even have devices with multiple temperature sensors, one on
each MEMs device. Also they often get read into a buffer with all the rest
of the channels.

Lets leave the decision on this up to individual driver writers.  If they are using
the device to do hwmon stuff as well as whatever else it is for then we encourage them
to register it with hwmon as you suggest.

Thanks for you quick response on this.

Jonathan


^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [Device-drivers-devel] [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25 11:19         ` Guenter Roeck
  2010-10-25 11:43           ` Jonathan Cameron
@ 2010-10-25 11:47           ` Hennerich, Michael
  2010-10-26  3:21             ` Zhang, Sonic
  1 sibling, 1 reply; 48+ messages in thread
From: Hennerich, Michael @ 2010-10-25 11:47 UTC (permalink / raw)
  To: Guenter Roeck, Jonathan Cameron
  Cc: linux-iio, Zhang, Sonic, Mike Frysinger, device-drivers-devel

Guenter Roeck wrote on 2010-10-25:
> On Mon, Oct 25, 2010 at 06:32:50AM -0400, Jonathan Cameron wrote:
>>>
>>> I'd love to see some reasoning why hardware monitoring drivers are
>>> moved to or directly written in iio.
>>>
>>> Also, I seem to be missing your point re "high performance devices".
>>> Are you saying that hwmon is not suitable for high performance
>>> hardware monitoring devices ?
>> Yes.  Point me at someone doing 1MSps or higher via pretty printing
>> through a sysfs interface.  Admittedly none of the controversial
>> drivers in this
>
> Ok.
>
>> set do that currently either, but that's why I have asked Analog to
>> confirm what they are using them for.

I don't remember why these drivers targeted IIO instead of HWMON, since
I didn't write those in question.

For some (without looking at a particular one) I would say the ringbuffer a=
nd
trigger capabilities in IIO were required.

>> The point is that these devices
>> are only hardware monitoring to you because that is what you think they
>> are for. Some of them (not the one I forwarded initially) are general
>> purpose ADC's that have a temp sensor because the temperature can
>> effect the calibration of the outputs.
>>
> I did not refer to the chips with generic ADC sensors.
> The chips I referred to are AD7414/15, ADT75, ADT7310, ADT7408, and
> ADT7410, though I may have missed some.

For the ones listed above -
>>From top of my head, and a quick glance at the driver source,
I also don't see a reason why these need to exist twice.
I asked the author to comment on, why these went into iio.
I think it's ok to drop the ones that already have suitable driver support =
in hwmon,
while adding support for the ones that are currently missing in hwmon.

>> We went through this in a lot of depth back when IIO first came about.
>> There is a boundary. We just need to pin down where it is.
>
> For the ambient temperature sensors on the other chips - did you
> consider adding hwmon device entries for those ? There may of course
> be reasons against doing that, but it may be an option. There are
> other drivers outside the hwmon directory which call
> hwmon_device_register(), so it is not a new concept.
>
> Thanks,
> Guenter
>
> _______________________________________________ Device-drivers-devel
> mailing list Device-drivers-devel@blackfin.uclinux.org
> https://blackfin.uclinux.org/mailman/listinfo/device-drivers-devel

Greetings,
Michael

Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 4036 Geschaeft=
sfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25 11:43           ` Jonathan Cameron
@ 2010-10-25 14:12             ` Guenter Roeck
  2010-10-25 16:18               ` Hennerich, Michael
  0 siblings, 1 reply; 48+ messages in thread
From: Guenter Roeck @ 2010-10-25 14:12 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Sonic Zhang

On Mon, Oct 25, 2010 at 07:43:12AM -0400, Jonathan Cameron wrote:
[ ... ]

> > I did not refer to the chips with generic ADC sensors.
> > The chips I referred to are AD7414/15, ADT75, ADT7310, ADT7408, and ADT7410,
> > though I may have missed some.
> You clearly got further through the set than I have so far! (I'd only come across
> the first 2 of those)  Agreed, all of these appear to be temperature only and at
> a quick look they are typically slow and low resolution so that set definitely
> want to be in hwmon.
> 
> For those parts with actual hwmon drivers, ad7414/15 and adt7408 (based on a quick
> grep unless Guenter has others queued). Lets drop them for now from the merge.
> 

Nothing queued here. Not that I would mind doing the driver conversions for ADT7310
and ADT7410 if needed, but I would need HW (eval boards) for testing.

> For the others lets put a todo in place to convert them to hwmon.  Nothing wrong
> with putting them in staging (under IIO or otherwise) in the meantime as far
> as I am concerned.
> 
> > 
> >> We went through this in a lot of depth back when IIO first came about.
> >> There is a boundary. We just need to pin down where it is.
> > 
> > For the ambient temperature sensors on the other chips - did you consider
> > adding hwmon device entries for those ? There may of course be reasons against
> > doing that, but it may be an option. There are other drivers outside the hwmon 
> > directory which call hwmon_device_register(), so it is not a new concept.
> It depends on whether they are generally useful for temperature monitoring.
> On the whole they are giving one the value on a particular bit of silicon
> in the chip (not the ambient temperature near by). They aren't reading it for
> monitoring purposes, but because it is needed to calibrate the other sensors in
> the package.  We even have devices with multiple temperature sensors, one on
> each MEMs device. Also they often get read into a buffer with all the rest
> of the channels.
> 
> Lets leave the decision on this up to individual driver writers.  If they are using
> the device to do hwmon stuff as well as whatever else it is for then we encourage them
> to register it with hwmon as you suggest.
> 
Ok, makes sense.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25 14:12             ` Guenter Roeck
@ 2010-10-25 16:18               ` Hennerich, Michael
  0 siblings, 0 replies; 48+ messages in thread
From: Hennerich, Michael @ 2010-10-25 16:18 UTC (permalink / raw)
  To: Guenter Roeck, Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Zhang, Sonic

Guenter Roeck wrote on 2010-10-25:
> On Mon, Oct 25, 2010 at 07:43:12AM -0400, Jonathan Cameron wrote:
> [ ... ]
>
>>> I did not refer to the chips with generic ADC sensors.
>>> The chips I referred to are AD7414/15, ADT75, ADT7310, ADT7408,
>>> and ADT7410, though I may have missed some.
>> You clearly got further through the set than I have so far! (I'd
>> only come across the first 2 of those)  Agreed, all of these appear
>> to be temperature only and at a quick look they are typically slow
>> and low resolution so that set definitely want to be in hwmon.
>>
>> For those parts with actual hwmon drivers, ad7414/15 and adt7408 (based
>> on a quick grep unless Guenter has others queued). Lets drop them for
>> now from the merge.
>>
>
> Nothing queued here. Not that I would mind doing the driver
> conversions for ADT7310 and ADT7410 if needed, but I would need HW
> (eval boards) for testing.

I'm sure that I can get you the EVAL-ADT7X10EBZ board.
Please send me your shipping address off list.

Greetings,
Michael

Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB 4036 Geschaeft=
sfuehrer Thomas Wessel, William A. Martin, Margaret Seif

^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [Device-drivers-devel] [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-25 11:47           ` [Device-drivers-devel] " Hennerich, Michael
@ 2010-10-26  3:21             ` Zhang, Sonic
  0 siblings, 0 replies; 48+ messages in thread
From: Zhang, Sonic @ 2010-10-26  3:21 UTC (permalink / raw)
  To: Hennerich, Michael, Guenter Roeck, Jonathan Cameron
  Cc: linux-iio, Mike Frysinger, device-drivers-devel



>-----Original Message-----
>From: Hennerich, Michael
>Sent: Monday, October 25, 2010 7:48 PM
>To: Guenter Roeck; Jonathan Cameron
>Cc: linux-iio@vger.kernel.org; Zhang, Sonic; Mike Frysinger;
>device-drivers-devel@blackfin.uclinux.org
>Subject: RE: [Device-drivers-devel] [PATCH 12/14] staging:
>iio: adc: new driver for ADT7408 temperature sensors
>
>Guenter Roeck wrote on 2010-10-25:
>> On Mon, Oct 25, 2010 at 06:32:50AM -0400, Jonathan Cameron wrote:
>>>>
>>>> I'd love to see some reasoning why hardware monitoring drivers are
>>>> moved to or directly written in iio.
>>>>
>>>> Also, I seem to be missing your point re "high performance
>devices".
>>>> Are you saying that hwmon is not suitable for high performance
>>>> hardware monitoring devices ?
>>> Yes.  Point me at someone doing 1MSps or higher via pretty printing
>>> through a sysfs interface.  Admittedly none of the controversial
>>> drivers in this
>>
>> Ok.
>>
>>> set do that currently either, but that's why I have asked Analog to
>>> confirm what they are using them for.
>
>I don't remember why these drivers targeted IIO instead of
>HWMON, since I didn't write those in question.
>
>For some (without looking at a particular one) I would say the
>ringbuffer and trigger capabilities in IIO were required.
>

One of the reason to target these driver in IIO is to enable the ringbuffer=
 and trigger capability, such as AD7414, ADT75, ADT7310, ADT7408 and ADT741=
0 mentioned bellow.

The other reason is that some chips has additional AD units other than the =
temperature one.


>>> The point is that these devices
>>> are only hardware monitoring to you because that is what you think
>>> they are for. Some of them (not the one I forwarded initially) are
>>> general purpose ADC's that have a temp sensor because the
>temperature
>>> can effect the calibration of the outputs.
>>>
>> I did not refer to the chips with generic ADC sensors.
>> The chips I referred to are AD7414/15, ADT75, ADT7310, ADT7408, and
>> ADT7410, though I may have missed some.
>

These IIO drivers were developed early this year, when there were no suppor=
t in hwmon framework in kernel mainline.


Sonic

>For the ones listed above -
>>From top of my head, and a quick glance at the driver source,
>I also don't see a reason why these need to exist twice.
>I asked the author to comment on, why these went into iio.
>I think it's ok to drop the ones that already have suitable
>driver support in hwmon, while adding support for the ones
>that are currently missing in hwmon.
>
>>> We went through this in a lot of depth back when IIO first
>came about.
>>> There is a boundary. We just need to pin down where it is.
>>
>> For the ambient temperature sensors on the other chips - did you
>> consider adding hwmon device entries for those ? There may of course
>> be reasons against doing that, but it may be an option. There are
>> other drivers outside the hwmon directory which call
>> hwmon_device_register(), so it is not a new concept.
>>
>> Thanks,
>> Guenter
>>
>> _______________________________________________ Device-drivers-devel
>> mailing list Device-drivers-devel@blackfin.uclinux.org
>> https://blackfin.uclinux.org/mailman/listinfo/device-drivers-devel
>
>Greetings,
>Michael
>
>Analog Devices GmbH      Wilhelm-Wagenfeld-Str. 6      80807 Muenchen
>Sitz der Gesellschaft Muenchen, Registergericht Muenchen HRB
>4036 Geschaeftsfuehrer Thomas Wessel, William A. Martin, Margaret Seif
>
>
>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-24 22:53   ` Jonathan Cameron
  2010-10-24 23:47     ` Guenter Roeck
  2010-10-25  0:46     ` Guenter Roeck
@ 2010-10-26  3:27     ` Zhang, Sonic
  2010-10-26  3:52       ` Guenter Roeck
  2010-10-26  9:15       ` Jonathan Cameron
  2 siblings, 2 replies; 48+ messages in thread
From: Zhang, Sonic @ 2010-10-26  3:27 UTC (permalink / raw)
  To: Jonathan Cameron, Mike Frysinger
  Cc: linux-iio, device-drivers-devel, Guenter Roeck



>-----Original Message-----
>From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>Sent: Monday, October 25, 2010 6:54 AM
>To: Mike Frysinger
>Cc: linux-iio@vger.kernel.org;
>device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
>Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>ADT7408 temperature sensors
>
>On 10/23/10 21:29, Mike Frysinger wrote:
>> From: Sonic Zhang <sonic.zhang@analog.com>
>Here we enter new territory. This device is already supported
>in hwmon.  Do we have a usecase that is not covered by that driver?
>

I don't find a way to get event notification other than poll in hwmon frame=
work. So, I move all temperature devic with interrupt available to IIO fram=
ework.

Sonic

>If this is only for hardware monitoring by Guenter (cc'd) can
>perhaps advise on how to support everything you have here...
>
>I'm personally not against having drivers in IIO for devices
>supported elsewhere, but the requirements for justification
>are rather higher. Also care is needed to ensure no issues
>with platform data etc.
>
>Guenter, for your information we have a set of temp drivers
>coming, as a small element of a larger set, from Analog's
>tree. Those I've reviewed so far have wanted to use IIO's
>event infrastructure (which is much more general than hwmon's
>handling of alarms) or have been suitably high performance
>devices with general adc's to satisfy me that they clearly
>have uses beyond hardware monitoring.
>
>
>>
>> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
>> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
>> ---
>>  drivers/staging/iio/adc/Kconfig   |    7 +
>>  drivers/staging/iio/adc/Makefile  |    1 +
>>  drivers/staging/iio/adc/adt7408.c | 1006
>> +++++++++++++++++++++++++++++++++++++
>>  3 files changed, 1014 insertions(+), 0 deletions(-)  create mode
>> 100644 drivers/staging/iio/adc/adt7408.c
>>
>> diff --git a/drivers/staging/iio/adc/Kconfig
>> b/drivers/staging/iio/adc/Kconfig index 5d13918..ea75700 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -114,3 +114,10 @@ config ADT7408
>>      help
>>        Say yes here to build support for Analog Devices ADT7408
>>        temperature sensors.
>> +
>> +config ADT7410
>> +    tristate "Analog Devices ADT7410 temperature sensor driver"
>> +    depends on I2C
>> +    help
>> +      Say yes here to build support for Analog Devices ADT7410
>> +      temperature sensors.
>> diff --git a/drivers/staging/iio/adc/Makefile
>> b/drivers/staging/iio/adc/Makefile
>> index 6c11363..dc2bdbe 100644
>> --- a/drivers/staging/iio/adc/Makefile
>> +++ b/drivers/staging/iio/adc/Makefile
>> @@ -21,3 +21,4 @@ obj-$(CONFIG_AD774X) +=3D ad774x.o
>>  obj-$(CONFIG_AD7816) +=3D ad7816.o
>>  obj-$(CONFIG_ADT75) +=3D adt75.o
>>  obj-$(CONFIG_ADT7310) +=3D adt7310.o
>> +obj-$(CONFIG_ADT7408) +=3D adt7408.o
>> diff --git a/drivers/staging/iio/adc/adt7408.c
>> b/drivers/staging/iio/adc/adt7408.c
>> new file mode 100644
>> index 0000000..25bd594
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/adt7408.c
>> @@ -0,0 +1,1006 @@
>> +/*
>> + * ADT7408 digital temperature sensor driver supporting ADT7408
>> + *
>> + * Copyright 2010 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2 or later.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/gpio.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/i2c.h>
>> +#include <linux/rtc.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +
>> +/*
>> + * ADT7408 registers definition
>> + */
>> +
>> +#define ADT7408_CAPABILITY          0
>> +#define ADT7408_CONFIG                      1
>> +#define ADT7408_T_ALARM_HIGH                2
>> +#define ADT7408_T_ALARM_LOW         3
>> +#define ADT7408_T_CRIT                      4
>> +#define ADT7408_TEMPERATURE         5
>> +#define ADT7408_MANUFACTURER_ID             6
>> +#define ADT7408_DEVICE_ID           7
>> +
>> +/*
>> + * ADT7408 capability
>> + */
>> +#define ADT7408_CAP_ALARM_CRIT_TRIPS        0x1
>> +#define ADT7408_CAP_HIGH_PRECISION  0x2
>> +#define ADT7408_CAP_WIDER_RANGE             0x4
>> +#define ADT7408_CAP_T_RESOLUTION_MASK       0x18
>> +#define ADT7408_CAP_T_RESOLUTION_HIGH       0x18
>> +#define ADT7408_CAP_T_RESOLUTION_LOW        0x8
>> +
>> +/*
>> + * ADT7408 config
>> + */
>> +#define ADT7408_EVENT_MODE          0x1
>> +#define ADT7408_EVENT_POLARITY              0x2
>> +#define ADT7408_EVENT_CRIT_ONLY             0x4
>> +#define ADT7408_EVENT_ENABLE                0x8
>> +#define ADT7408_EVENT_STATUS                0x10
>> +#define ADT7408_EVENT_CLEAR         0x20
>> +#define ADT7408_EVENT_ALARM_LOCK    0x40
>> +#define ADT7408_EVENT_CRIT_LOCK             0x80
>> +#define ADT7408_PD                  0x100
>> +#define ADT7408_HISTERESIS_MASK             0x600
>> +#define ADT7408_HISTERESIS_1_5              0x200
>> +#define ADT7408_HISTERESIS_3                0x400
>> +#define ADT7408_HISTERESIS_6                0x600
>> +
>> +/*
>> + * ADT7408 masks
>> + */
>> +#define ADT7408_BOUND_VALUE_SIGN            0x400
>> +#define ADT7408_BOUND_VALUE_OFFSET          2
>> +#define ADT7408_BOUND_VALUE_FLOAT_OFFSET    2
>> +#define ADT7408_BOUND_VALUE_FLOAT_MASK              0x3
>> +#define ADT7408_T_VALUE_SIGN                        0x1000
>> +#define ADT7408_T_VALUE_FLOAT_OFFSET                4
>> +#define ADT7408_T_VALUE_FLOAT_MASK          0xF
>> +
>> +/*
>> + * ADT7408 event source
>> + */
>> +#define ADT7408_T_BELLOW_ALARM                      0x2000
>> +#define ADT7408_T_ABOVE_ALARM                       0x4000
>> +#define ADT7408_T_ABOVE_CRIT                        0x8000
>> +
>> +
>> +/*
>> + * struct adt7408_chip_info - chip specifc information  */
>> +
>> +struct adt7408_chip_info {
>> +    const char *name;
>> +    struct i2c_client *client;
>> +    struct iio_dev *indio_dev;
>> +    struct work_struct thresh_work;
>> +    s64 last_timestamp;
>> +    u16 config;
>> +};
>> +
>> +/*
>> + * adt7408 register access by I2C
>> + */
>> +
>> +static int adt7408_i2c_read(struct adt7408_chip_info *chip, u8 reg,
>> +u16 *data) {
>> +    struct i2c_client *client =3D chip->client;
>> +    int ret =3D 0;
>> +
>> +    ret =3D i2c_smbus_read_word_data(client, reg);
>> +    if (ret < 0) {
>> +            dev_err(&client->dev, "I2C read error\n");
>> +            return ret;
>> +    }
>> +
>> +    *data =3D swab16((u16)ret);
>> +
>> +    return 0;
>> +}
>> +
>> +static int adt7408_i2c_write(struct adt7408_chip_info
>*chip, u8 reg,
>> +u16 data) {
>> +    struct i2c_client *client =3D chip->client;
>> +    int ret =3D 0;
>> +
>> +    ret =3D i2c_smbus_write_word_data(client, reg, swab16(data));
>> +    if (ret < 0)
>> +            dev_err(&client->dev, "I2C write error\n");
>> +
>> +    return ret;
>> +}
>> +
>> +static int adt7408_is_event_locked(struct adt7408_chip_info *chip) {
>> +    return chip->config & (ADT7408_EVENT_ALARM_LOCK |
>> +ADT7408_EVENT_ALARM_LOCK); }
>> +
>> +static ssize_t adt7408_show_mode(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +
>> +    if (chip->config & ADT7408_PD)
>> +            return sprintf(buf, "power-save\n");
>> +    else
>> +            return sprintf(buf, "full\n");
>> +}
>> +
>> +static ssize_t adt7408_store_mode(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 config;
>> +    int ret;
>> +
>> +    if (adt7408_is_event_locked(chip)) {
>> +            dev_err(dev, "Warning: Events are locked.\n");
>> +            return -EIO;
>> +    }
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config =3D chip->config & (~ADT7408_PD);
>> +    if (!strcmp(buf, "full"))
>> +            config |=3D ADT7408_PD;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +
>> +    return ret;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
>> +            adt7408_show_mode,
>> +            adt7408_store_mode,
>> +            0);
>> +
>> +static ssize_t adt7408_show_available_modes(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return sprintf(buf, "full\npower-down\n"); }
>> +
>> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO,
>> +adt7408_show_available_modes, NULL, 0);
>> +
>> +static ssize_t adt7408_show_capability(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 capability;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CAPABILITY, &capability);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "0x%x\n", capability); }
>> +
>> +static IIO_DEVICE_ATTR(capability, S_IRUGO | S_IWUSR,
>> +            adt7408_show_capability,
>> +            NULL,
>> +            0);
>> +
>> +static ssize_t adt7408_show_manufactory_id(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 id;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "0x%x\n", id);
>> +}
>> +
>> +static IIO_DEVICE_ATTR(manufactory_id, S_IRUGO | S_IWUSR,
>> +            adt7408_show_manufactory_id,
>> +            NULL,
>> +            0);
>> +
>> +static ssize_t adt7408_show_device_id(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 id;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "0x%x\n", id);
>> +}
>> +
>> +static IIO_DEVICE_ATTR(device_id, S_IRUGO | S_IWUSR,
>> +            adt7408_show_device_id,
>> +            NULL,
>> +            0);
>> +
>> +static ssize_t adt7408_show_value(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 data;
>> +    char sign =3D ' ';
>> +    int ret;
>> +
>> +    if (chip->config & ADT7408_PD) {
>> +            dev_err(dev, "Can't read value in power-down mode.\n");
>> +            return -EIO;
>> +    }
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    if (data & ADT7408_T_VALUE_SIGN) {
>> +            /* convert supplement to positive value */
>> +            data =3D (ADT7408_T_VALUE_SIGN << 1) - data;
>> +            sign =3D '-';
>> +    }
>> +
>> +    return sprintf(buf, "%c%d.%.4d\n", sign,
>> +            (data >> ADT7408_T_VALUE_FLOAT_OFFSET),
>> +            (data & ADT7408_T_VALUE_FLOAT_MASK) * 625); }
>> +
>> +static IIO_DEVICE_ATTR(value, S_IRUGO, adt7408_show_value, NULL, 0);
>> +
>> +static ssize_t adt7408_show_name(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    return sprintf(buf, "%s\n", chip->name); }
>> +
>> +static IIO_DEVICE_ATTR(name, S_IRUGO, adt7408_show_name, NULL, 0);
>> +
>> +static struct attribute *adt7408_attributes[] =3D {
>> +    &iio_dev_attr_available_modes.dev_attr.attr,
>> +    &iio_dev_attr_mode.dev_attr.attr,
>> +    &iio_dev_attr_capability.dev_attr.attr,
>> +    &iio_dev_attr_device_id.dev_attr.attr,
>> +    &iio_dev_attr_manufactory_id.dev_attr.attr,
>> +    &iio_dev_attr_value.dev_attr.attr,
>> +    &iio_dev_attr_name.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static const struct attribute_group adt7408_attribute_group =3D {
>> +    .attrs =3D adt7408_attributes,
>> +};
>> +
>> +/*
>> + * temperature bound events
>> + */
>> +
>> +#define IIO_EVENT_CODE_ADT7408_ABOVE_ALARM
>(IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
>> +#define IIO_EVENT_CODE_ADT7408_BELLOW_ALARM
>(IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
>> +#define IIO_EVENT_CODE_ADT7408_ABOVE_CRIT
>(IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
>> +
>> +static void adt7408_interrupt_bh(struct work_struct *work_s) {
>> +    struct adt7408_chip_info *chip =3D
>> +            container_of(work_s, struct adt7408_chip_info,
>thresh_work);
>> +    u16 config;
>> +    u16 data;
>> +
>> +    if (adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config))
>> +            return;
>> +
>> +    if (!(chip->config & ADT7408_EVENT_STATUS))
>> +            return;
>> +
>> +    config =3D chip->config & ~ADT7408_EVENT_CLEAR;
>> +    if (data)
>> +            config |=3D ADT7408_EVENT_CLEAR;
>> +
>> +    adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +
>> +    if (adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data))
>> +            goto exit;
>> +
>> +    if (data & ADT7408_T_ABOVE_ALARM)
>> +            iio_push_event(chip->indio_dev, 0,
>> +                    IIO_EVENT_CODE_ADT7408_ABOVE_ALARM,
>> +                    chip->last_timestamp);
>> +    if (data & ADT7408_T_BELLOW_ALARM)
>> +            iio_push_event(chip->indio_dev, 0,
>> +                    IIO_EVENT_CODE_ADT7408_BELLOW_ALARM,
>> +                    chip->last_timestamp);
>> +    if (data & ADT7408_T_ABOVE_CRIT)
>> +            iio_push_event(chip->indio_dev, 0,
>> +                    IIO_EVENT_CODE_ADT7408_ABOVE_CRIT,
>> +                    chip->last_timestamp);
>> +exit:
>> +    enable_irq(chip->client->irq);
>> +}
>> +
>> +static int adt7408_interrupt(struct iio_dev *dev_info,
>> +            int index,
>> +            s64 timestamp,
>> +            int no_test)
>> +{
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +
>> +    chip->last_timestamp =3D timestamp;
>> +    schedule_work(&chip->thresh_work);
>> +
>> +    return 0;
>> +}
>> +
>> +IIO_EVENT_SH(adt7408, &adt7408_interrupt);
>> +
>> +static ssize_t adt7408_show_event_mode(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    if (chip->config & ADT7408_EVENT_MODE)
>> +            return sprintf(buf, "interrupt\n");
>> +    else
>> +            return sprintf(buf, "comparator\n"); }
>> +
>> +static ssize_t adt7408_set_event_mode(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 config;
>> +    int ret;
>> +
>> +    if (adt7408_is_event_locked(chip)) {
>> +            dev_err(dev, "Warning: Events are locked.\n");
>> +            return -EIO;
>> +    }
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config =3D chip->config &=3D ~ADT7408_EVENT_MODE;
>> +    if (strcmp(buf, "comparator") !=3D 0)
>> +            config |=3D ADT7408_EVENT_MODE;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +
>> +    return ret;
>> +}
>> +
>> +static ssize_t adt7408_show_available_event_modes(struct
>device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return sprintf(buf, "comparator\ninterrupt\n"); }
>> +
>> +static ssize_t adt7408_show_event_crit_only(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "%d\n", !!(chip->config &
>> +ADT7408_EVENT_CRIT_ONLY)); }
>> +
>> +static ssize_t adt7408_set_event_crit_only(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    unsigned long data =3D 0;
>> +    u16 config;
>> +    int ret;
>> +
>> +    if (adt7408_is_event_locked(chip)) {
>> +            dev_err(dev, "Warning: Events are locked.\n");
>> +            return -EIO;
>> +    }
>> +
>> +    ret =3D strict_strtoul(buf, 10, &data);
>> +    if (ret)
>> +            return -EINVAL;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config =3D chip->config &=3D ~ADT7408_EVENT_CRIT_ONLY;
>> +    if (data)
>> +            config |=3D ADT7408_EVENT_CRIT_ONLY;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +
>> +    return ret;
>> +}
>> +
>> +static ssize_t adt7408_show_event_enable(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "%d\n", !!(chip->config &
>> +ADT7408_EVENT_ENABLE)); }
>> +
>> +static ssize_t adt7408_set_event_enable(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    unsigned long data;
>> +    u16 config;
>> +    int ret;
>> +
>> +    if (adt7408_is_event_locked(chip)) {
>> +            dev_err(dev, "Warning: Events are locked.\n");
>> +            return -EIO;
>> +    }
>> +
>> +    ret =3D strict_strtoul(buf, 10, &data);
>> +    if (ret)
>> +            return -EINVAL;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config =3D chip->config & ~ADT7408_EVENT_ENABLE;
>> +    if (data)
>> +            config |=3D ADT7408_EVENT_ENABLE;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +
>> +    return ret;
>> +}
>> +
>> +static ssize_t adt7408_show_alarm_lock(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "%d\n", !!(chip->config &
>> +ADT7408_EVENT_ALARM_LOCK)); }
>> +
>> +static ssize_t adt7408_set_alarm_lock(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    unsigned long data;
>> +    u16 config;
>> +    int ret;
>> +
>> +    ret =3D strict_strtoul(buf, 10, &data);
>> +    if (ret)
>> +            return -EINVAL;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config =3D chip->config & ~ADT7408_EVENT_ALARM_LOCK;
>> +    if (data)
>> +            config |=3D ADT7408_EVENT_ALARM_LOCK;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +
>> +    return ret;
>> +}
>> +
>> +static ssize_t adt7408_show_crit_lock(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return sprintf(buf, "%d\n", !!(chip->config &
>> +ADT7408_EVENT_CRIT_LOCK)); }
>> +
>> +static ssize_t adt7408_set_crit_lock(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    unsigned long data;
>> +    u16 config;
>> +    int ret;
>> +
>> +    ret =3D strict_strtoul(buf, 10, &data);
>> +    if (ret)
>> +            return -EINVAL;
>> +
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config =3D chip->config & ~ADT7408_EVENT_CRIT_LOCK;
>> +    if (data)
>> +            config |=3D ADT7408_EVENT_CRIT_LOCK;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +
>> +    return ret;
>> +}
>> +
>> +
>> +static inline ssize_t adt7408_show_t_bound(struct device *dev,
>> +            struct device_attribute *attr,
>> +            u8 bound_reg,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    u16 data;
>> +    char sign =3D ' ';
>> +    int ret;
>> +
>> +    ret =3D adt7408_i2c_read(chip, bound_reg, &data);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    data >>=3D ADT7408_BOUND_VALUE_OFFSET;
>> +    if (data & ADT7408_BOUND_VALUE_SIGN) {
>> +            /* convert supplement to positive value */
>> +            data =3D (ADT7408_BOUND_VALUE_SIGN << 1) - data;
>> +            sign =3D '-';
>> +    }
>> +
>> +    return sprintf(buf, "%c%d.%.2d\n", sign,
>> +                    data >> ADT7408_BOUND_VALUE_FLOAT_OFFSET,
>> +                    (data & ADT7408_BOUND_VALUE_FLOAT_MASK) * 25); }
>> +
>> +static inline ssize_t adt7408_set_t_bound(struct device *dev,
>> +            struct device_attribute *attr,
>> +            u8 bound_reg,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    long tmp1, tmp2;
>> +    u16 data;
>> +    char *pos;
>> +    int ret;
>> +
>> +    pos =3D strchr(buf, '.');
>> +
>> +    ret =3D strict_strtol(buf, 10, &tmp1);
>> +
>> +    if (ret || tmp1 > 127 || tmp1 < -128)
>> +            return -EINVAL;
>> +
>> +    if (pos) {
>> +            len =3D strlen(pos);
>> +            if (len > ADT7408_BOUND_VALUE_FLOAT_OFFSET)
>> +                    len =3D ADT7408_BOUND_VALUE_FLOAT_OFFSET;
>> +            pos[len] =3D 0;
>> +            ret =3D strict_strtol(pos, 10, &tmp2);
>> +
>> +            if (!ret)
>> +                    tmp2 =3D (tmp2 / 25) * 25;
>> +    }
>> +
>> +    if (tmp1 < 0)
>> +            data =3D (u16)(-tmp1);
>> +    else
>> +            data =3D (u16)tmp1;
>> +    data =3D (data << ADT7408_BOUND_VALUE_FLOAT_OFFSET) |
>> +            (tmp2 & ADT7408_BOUND_VALUE_FLOAT_MASK);
>> +    if (tmp1 < 0)
>> +            /* convert positive value to supplyment */
>> +            data =3D (ADT7408_BOUND_VALUE_SIGN << 1) - data;
>> +    data <<=3D ADT7408_BOUND_VALUE_OFFSET;
>> +
>> +    ret =3D adt7408_i2c_write(chip, bound_reg, data);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    return ret;
>> +}
>> +
>> +static ssize_t adt7408_show_t_alarm_high(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return adt7408_show_t_bound(dev, attr,
>> +                    ADT7408_T_ALARM_HIGH, buf);
>> +}
>> +
>> +static inline ssize_t adt7408_set_t_alarm_high(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    return adt7408_set_t_bound(dev, attr,
>> +                    ADT7408_T_ALARM_HIGH, buf, len);
>> +}
>> +
>> +static ssize_t adt7408_show_t_alarm_low(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return adt7408_show_t_bound(dev, attr,
>> +                    ADT7408_T_ALARM_LOW, buf);
>> +}
>> +
>> +static inline ssize_t adt7408_set_t_alarm_low(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    return adt7408_set_t_bound(dev, attr,
>> +                    ADT7408_T_ALARM_LOW, buf, len);
>> +}
>> +
>> +static ssize_t adt7408_show_t_crit(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return adt7408_show_t_bound(dev, attr,
>> +                    ADT7408_T_CRIT, buf);
>> +}
>> +
>> +static inline ssize_t adt7408_set_t_crit(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    return adt7408_set_t_bound(dev, attr,
>> +                    ADT7408_T_CRIT, buf, len);
>> +}
>> +
>> +static ssize_t adt7408_show_t_hyst(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +
>> +    /* retrive ALART status */
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    switch (chip->config & ADT7408_HISTERESIS_MASK) {
>> +    case ADT7408_HISTERESIS_1_5:
>> +            return sprintf(buf, "1.5\n");
>> +    case ADT7408_HISTERESIS_3:
>> +            return sprintf(buf, "3\n");
>> +    case ADT7408_HISTERESIS_6:
>> +            return sprintf(buf, "6\n");
>> +    default:
>> +            return sprintf(buf, "Disabled\n");
>> +    }
>> +}
>> +
>> +static inline ssize_t adt7408_set_t_hyst(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct adt7408_chip_info *chip =3D dev_info->dev_data;
>> +    int ret;
>> +    u16 config =3D 0;
>> +
>> +    if (strcmp(buf, "disble"))
>> +            config =3D ADT7408_HISTERESIS_MASK;
>> +    else if (strcmp(buf, "1.5"))
>> +            config =3D ADT7408_HISTERESIS_1_5;
>> +    else if (len > 1 && buf[0] =3D=3D '3')
>> +            config =3D ADT7408_HISTERESIS_6;
>> +    else if (len > 1 && buf[0] =3D=3D '6')
>> +            config =3D ADT7408_HISTERESIS_6;
>> +
>> +    if (!config)
>> +            return -EINVAL;
>> +
>> +    /* retrive ALART status */
>> +    ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    config |=3D chip->config & ~ADT7408_HISTERESIS_MASK;
>> +
>> +    ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->config =3D config;
>> +    return ret;
>> +}
>> +
>> +static ssize_t adt7408_show_available_t_hyst(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return sprintf(buf, "1.5\n3\n6\ndisable\n"); }
>> +
>> +IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7408,
>> +            adt7408_show_event_mode, adt7408_set_event_mode, 0);
>> +IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7408,
>> +            adt7408_show_available_event_modes, NULL, 0);
>> +IIO_EVENT_ATTR_SH(event_crit_only, iio_event_adt7408,
>> +            adt7408_show_event_crit_only,
>adt7408_set_event_crit_only, 0);
>> +IIO_EVENT_ATTR_SH(event_enable, iio_event_adt7408,
>> +            adt7408_show_event_enable,
>adt7408_set_event_enable, 0);
>> +IIO_EVENT_ATTR_SH(alarm_lock, iio_event_adt7408,
>> +            adt7408_show_alarm_lock, adt7408_set_alarm_lock, 0);
>> +IIO_EVENT_ATTR_SH(crit_lock, iio_event_adt7408,
>> +            adt7408_show_crit_lock, adt7408_set_crit_lock, 0);
>> +IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7408,
>> +            adt7408_show_t_alarm_high,
>adt7408_set_t_alarm_high, 0);
>> +IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7408,
>> +            adt7408_show_t_alarm_low, adt7408_set_t_alarm_low, 0);
>> +IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7408,
>> +            adt7408_show_t_crit, adt7408_set_t_crit, 0);
>> +IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7408,
>> +            adt7408_show_t_hyst, adt7408_set_t_hyst, 0);
>> +IIO_EVENT_ATTR_SH(available_t_hyst, iio_event_adt7408,
>> +            adt7408_show_available_t_hyst, NULL, 0);
>> +
>> +static struct attribute *adt7408_event_attributes[] =3D {
>> +    &iio_event_attr_event_mode.dev_attr.attr,
>> +    &iio_event_attr_available_event_modes.dev_attr.attr,
>> +    &iio_event_attr_event_crit_only.dev_attr.attr,
>> +    &iio_event_attr_event_enable.dev_attr.attr,
>> +    &iio_event_attr_alarm_lock.dev_attr.attr,
>> +    &iio_event_attr_crit_lock.dev_attr.attr,
>> +    &iio_event_attr_t_alarm_high.dev_attr.attr,
>> +    &iio_event_attr_t_alarm_low.dev_attr.attr,
>> +    &iio_event_attr_t_crit.dev_attr.attr,
>> +    &iio_event_attr_t_hyst.dev_attr.attr,
>> +    &iio_event_attr_available_t_hyst.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute_group adt7408_event_attribute_group =3D {
>> +    .attrs =3D adt7408_event_attributes,
>> +};
>> +
>> +/*
>> + * device probe and remove
>> + */
>> +
>> +static int __devinit adt7408_probe(struct i2c_client *client,
>> +            const struct i2c_device_id *id)
>> +{
>> +    struct adt7408_chip_info *chip;
>> +    int ret =3D 0;
>> +
>> +    chip =3D kzalloc(sizeof(struct adt7408_chip_info), GFP_KERNEL);
>> +
>> +    if (chip =3D=3D NULL)
>> +            return -ENOMEM;
>> +
>> +    /* this is only used for device removal purposes */
>> +    i2c_set_clientdata(client, chip);
>> +
>> +    chip->client =3D client;
>> +    chip->name =3D id->name;
>> +
>> +    chip->indio_dev =3D iio_allocate_device();
>> +    if (chip->indio_dev =3D=3D NULL) {
>> +            ret =3D -ENOMEM;
>> +            goto error_free_chip;
>> +    }
>> +
>> +    chip->indio_dev->dev.parent =3D &client->dev;
>> +    chip->indio_dev->attrs =3D &adt7408_attribute_group;
>> +    chip->indio_dev->event_attrs =3D &adt7408_event_attribute_group;
>> +    chip->indio_dev->dev_data =3D (void *)chip;
>> +    chip->indio_dev->driver_module =3D THIS_MODULE;
>> +    chip->indio_dev->num_interrupt_lines =3D 1;
>> +    chip->indio_dev->modes =3D INDIO_DIRECT_MODE;
>> +
>> +    ret =3D iio_device_register(chip->indio_dev);
>> +    if (ret)
>> +            goto error_free_dev;
>> +
>> +    if (client->irq) {
>> +            ret =3D iio_register_interrupt_line(client->irq,
>> +                            chip->indio_dev,
>> +                            0,
>> +                            client->irq_flags,
>> +                            chip->name);
>> +            if (ret)
>> +                    goto error_unreg_dev;
>> +
>> +            /*
>> +             * The event handler list element refer to
>iio_event_adt7408.
>> +             * All event attributes bind to the same event handler.
>> +             * So, only register event handler once.
>> +             */
>> +            iio_add_event_to_list(&iio_event_adt7408,
>> +
>&chip->indio_dev->interrupts[0]->ev_list);
>> +
>> +            INIT_WORK(&chip->thresh_work, adt7408_interrupt_bh);
>> +
>> +            ret =3D adt7408_i2c_read(chip, ADT7408_CONFIG,
>&chip->config);
>> +            if (ret) {
>> +                    ret =3D -EIO;
>> +                    goto error_unreg_irq;
>> +            }
>> +
>> +            if (client->irq_flags & IRQF_TRIGGER_HIGH)
>> +                    chip->config |=3D ADT7408_EVENT_POLARITY;
>> +            else
>> +                    chip->config &=3D ~ADT7408_EVENT_POLARITY;
>> +
>> +            ret =3D adt7408_i2c_write(chip, ADT7408_CONFIG,
>chip->config);
>> +            if (ret) {
>> +                    ret =3D -EIO;
>> +                    goto error_unreg_irq;
>> +            }
>> +    }
>> +
>> +    dev_info(&client->dev, "%s temperature sensor registered.\n",
>> +                     id->name);
>> +
>> +    return 0;
>> +
>> +error_unreg_irq:
>> +    iio_unregister_interrupt_line(chip->indio_dev, 0);
>> +error_unreg_dev:
>> +    iio_device_unregister(chip->indio_dev);
>> +error_free_dev:
>> +    iio_free_device(chip->indio_dev);
>> +error_free_chip:
>> +    kfree(chip);
>> +
>> +    return ret;
>> +}
>> +
>> +static int __devexit adt7408_remove(struct i2c_client *client) {
>> +    struct adt7408_chip_info *chip =3D i2c_get_clientdata(client);
>> +    struct iio_dev *indio_dev =3D chip->indio_dev;
>> +
>> +    if (client->irq)
>> +            iio_unregister_interrupt_line(indio_dev, 0);
>> +    iio_device_unregister(indio_dev);
>> +    iio_free_device(chip->indio_dev);
>> +    kfree(chip);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct i2c_device_id adt7408_id[] =3D {
>> +    { "adt7408", 0 },
>> +    {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(i2c, adt7408_id);
>> +
>> +static struct i2c_driver adt7408_driver =3D {
>> +    .driver =3D {
>> +            .name =3D "adt7408",
>> +    },
>> +    .probe =3D adt7408_probe,
>> +    .remove =3D __devexit_p(adt7408_remove),
>> +    .id_table =3D adt7408_id,
>> +};
>> +
>> +static __init int adt7408_init(void)
>> +{
>> +    return i2c_add_driver(&adt7408_driver); }
>> +
>> +static __exit void adt7408_exit(void) {
>> +    i2c_del_driver(&adt7408_driver);
>> +}
>> +
>> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
>> +MODULE_DESCRIPTION("Analog Devices ADT7408 digital"
>> +                    " temperature sensor driver");
>> +MODULE_LICENSE("GPL v2");
>> +
>> +module_init(adt7408_init);
>> +module_exit(adt7408_exit);
>
>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices
  2010-10-24 21:56   ` Jonathan Cameron
@ 2010-10-26  3:35     ` Zhang, Sonic
  0 siblings, 0 replies; 48+ messages in thread
From: Zhang, Sonic @ 2010-10-26  3:35 UTC (permalink / raw)
  To: Jonathan Cameron, Mike Frysinger; +Cc: linux-iio, device-drivers-devel



>-----Original Message-----
>From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>Sent: Monday, October 25, 2010 5:57 AM
>To: Mike Frysinger
>Cc: linux-iio@vger.kernel.org;
>device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic
>Subject: Re: [PATCH 04/14] staging: iio: adc: new driver for
>AD7314 devices
>
>On 10/23/10 21:29, Mike Frysinger wrote:
>> From: Sonic Zhang <sonic.zhang@analog.com>
>>
>Why IIO?  Do you have a use case that needs features off IIO
>and wouldn't be covered by hwmon?

You are right. AD7314 is not necessary to use IIO framework without interru=
pt available.

We may move it to hwmon framework late.

Sonic

>
>Not using any here, so I'm doubtful about this (Sonic and I
>exchanged a few emails about this a while back).
>
>Few comments inline and I guess iio then moving to hwmon is
>fine with me if you want to do it... (hwmon is simpler!).
>
>> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
>> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
>> ---
>>  drivers/staging/iio/adc/Kconfig  |    7 +
>>  drivers/staging/iio/adc/Makefile |    1 +
>>  drivers/staging/iio/adc/ad7314.c |  308
>> ++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 316 insertions(+), 0 deletions(-)  create mode
>> 100644 drivers/staging/iio/adc/ad7314.c
>>
>> diff --git a/drivers/staging/iio/adc/Kconfig
>> b/drivers/staging/iio/adc/Kconfig index 847f5f2..6d3b8bc 100644
>> --- a/drivers/staging/iio/adc/Kconfig
>> +++ b/drivers/staging/iio/adc/Kconfig
>> @@ -54,3 +54,10 @@ config AD7298
>>      help
>>        Say yes here to build support for Analog Devices AD7298
>>        temperature sensors and ADC.
>> +
>> +config AD7314
>> +    tristate "Analog Devices AD7314 temperature sensor driver"
>> +    depends on SPI
>Please list all parts supported (appear to be several others
>in the id table.)
>> +    help
>> +      Say yes here to build support for Analog Devices AD7314
>> +      temperature sensors.
>> diff --git a/drivers/staging/iio/adc/Makefile
>> b/drivers/staging/iio/adc/Makefile
>> index d0ea747..04fd93b 100644
>> --- a/drivers/staging/iio/adc/Makefile
>> +++ b/drivers/staging/iio/adc/Makefile
>> @@ -10,3 +10,4 @@ obj-$(CONFIG_AD7150) +=3D ad7150.o
>>  obj-$(CONFIG_AD7152) +=3D ad7152.o
>>  obj-$(CONFIG_AD7291) +=3D ad7291.o
>>  obj-$(CONFIG_AD7298) +=3D ad7298.o
>> +obj-$(CONFIG_AD7314) +=3D ad7314.o
>> diff --git a/drivers/staging/iio/adc/ad7314.c
>> b/drivers/staging/iio/adc/ad7314.c
>> new file mode 100644
>> index 0000000..8c17b1f
>> --- /dev/null
>> +++ b/drivers/staging/iio/adc/ad7314.c
>> @@ -0,0 +1,308 @@
>> +/*
>> + * AD7314 digital temperature sensor driver for AD7314, ADT7301 and
>> +ADT7302
>> + *
>> + * Copyright 2010 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2 or later.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/gpio.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/rtc.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +
>> +/*
>> + * AD7314 power mode
>> + */
>> +#define AD7314_PD           0x2000
>> +
>> +/*
>> + * AD7314 temperature masks
>> + */
>> +#define AD7314_TEMP_SIGN            0x200
>> +#define AD7314_TEMP_MASK            0x7FE0
>> +#define AD7314_TEMP_OFFSET          5
>> +#define AD7314_TEMP_FLOAT_OFFSET    2
>> +#define AD7314_TEMP_FLOAT_MASK              0x3
>> +
>> +/*
>> + * ADT7301 and ADT7302 temperature masks  */
>> +#define ADT7301_TEMP_SIGN           0x2000
>> +#define ADT7301_TEMP_MASK           0x2FFF
>> +#define ADT7301_TEMP_FLOAT_OFFSET   5
>> +#define ADT7301_TEMP_FLOAT_MASK             0x1F
>> +
>> +/*
>> + * struct ad7314_chip_info - chip specifc information  */
>> +
>> +struct ad7314_chip_info {
>> +    const char *name;
>> +    struct spi_device *spi_dev;
>> +    struct iio_dev *indio_dev;
>> +    s64 last_timestamp;
>> +    u8  mode;
>> +};
>> +
>> +/*
>> + * ad7314 register access by SPI
>> + */
>> +
>> +static int ad7314_spi_read(struct ad7314_chip_info *chip,
>u16 *data)
>> +{
>> +    struct spi_device *spi_dev =3D chip->spi_dev;
>> +    int ret =3D 0;
>> +    u16 value;
>> +
>> +    ret =3D spi_read(spi_dev, (u8 *)&value, sizeof(value));
>> +    if (ret < 0) {
>> +            dev_err(&spi_dev->dev, "SPI read error\n");
>> +            return ret;
>> +    }
>> +
>> +    *data =3D be16_to_cpu((u16)value);
>> +
>> +    return ret;
>> +}
>> +
>> +static int ad7314_spi_write(struct ad7314_chip_info *chip,
>u16 data)
>> +{
>> +    struct spi_device *spi_dev =3D chip->spi_dev;
>> +    int ret =3D 0;
>> +    u16 value =3D cpu_to_be16(data);
>> +
>> +    ret =3D spi_write(spi_dev, (u8 *)&value, sizeof(value));
>> +    if (ret < 0)
>> +            dev_err(&spi_dev->dev, "SPI write error\n");
>> +
>> +    return ret;
>> +}
>> +
>> +static ssize_t ad7314_show_mode(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct ad7314_chip_info *chip =3D dev_info->dev_data;
>> +
>> +    if (chip->mode)
>> +            return sprintf(buf, "power-save\n");
>> +    else
>> +            return sprintf(buf, "full\n");
>> +}
>> +
>> +static ssize_t ad7314_store_mode(struct device *dev,
>> +            struct device_attribute *attr,
>> +            const char *buf,
>> +            size_t len)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct ad7314_chip_info *chip =3D dev_info->dev_data;
>> +    u16 mode =3D 0;
>> +    int ret;
>> +
>> +    if (!strcmp(buf, "full"))
>> +            mode =3D AD7314_PD;
>> +
>> +    ret =3D ad7314_spi_write(chip, mode);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    chip->mode =3D mode;
>> +
>> +    return len;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
>> +            ad7314_show_mode,
>> +            ad7314_store_mode,
>> +            0);
>I'm still anti 'mode' attributes. They just don't generalize.
>> +
>> +static ssize_t ad7314_show_available_modes(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    return sprintf(buf, "full\npower-save\n");
>Do this via sampling_frequency (assuming that is what changes!)
>> +}
>> +
>> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO,
>> +ad7314_show_available_modes, NULL, 0);
>mode_available please.
>> +
>> +static ssize_t ad7314_show_temperature(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct ad7314_chip_info *chip =3D dev_info->dev_data;
>> +    u16 data;
>> +    char sign =3D ' ';
>> +    int ret;
>> +
>> +    if (chip->mode) {
>> +            ret =3D ad7314_spi_write(chip, 0);
>> +            if (ret)
>> +                    return -EIO;
>Error eating.
>> +    }
>> +
>> +    ret =3D ad7314_spi_read(chip, &data);
>> +    if (ret)
>> +            return -EIO;
>> +
>> +    if (chip->mode)
>> +            ad7314_spi_write(chip, chip->mode);
>> +
>> +    if (strcmp(chip->name, "ad7314")) {
>> +            data =3D (data & AD7314_TEMP_MASK) >>
>> +                    AD7314_TEMP_OFFSET;
>> +            if (data & AD7314_TEMP_SIGN) {
>> +                    data =3D (AD7314_TEMP_SIGN << 1) - data;
>> +                    sign =3D '-';
>> +            }
>> +
>> +            return sprintf(buf, "%c%d.%.2d\n", sign,
>> +                            data >> AD7314_TEMP_FLOAT_OFFSET,
>> +                            (data & AD7314_TEMP_FLOAT_MASK) * 25);
>> +    } else {
>> +            data &=3D ADT7301_TEMP_MASK;
>> +            if (data & ADT7301_TEMP_SIGN) {
>> +                    data =3D (ADT7301_TEMP_SIGN << 1) - data;
>> +                    sign =3D '-';
>> +            }
>> +
>> +            return sprintf(buf, "%c%d.%.5d\n", sign,
>> +                            data >> ADT7301_TEMP_FLOAT_OFFSET,
>> +                            (data &
>ADT7301_TEMP_FLOAT_MASK) * 3125);
>> +    }
>> +}
>> +
>> +static IIO_DEVICE_ATTR(temperature, S_IRUGO,
>ad7314_show_temperature,
>> +NULL, 0);
>> +
>> +static ssize_t ad7314_show_name(struct device *dev,
>> +            struct device_attribute *attr,
>> +            char *buf)
>> +{
>> +    struct iio_dev *dev_info =3D dev_get_drvdata(dev);
>> +    struct ad7314_chip_info *chip =3D dev_info->dev_data;
>> +    return sprintf(buf, "%s\n", chip->name); }
>> +
>> +static IIO_DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL, 0);
>> +
>> +static struct attribute *ad7314_attributes[] =3D {
>> +    &iio_dev_attr_available_modes.dev_attr.attr,
>> +    &iio_dev_attr_mode.dev_attr.attr,
>> +    &iio_dev_attr_temperature.dev_attr.attr,
>temp_input please (we match hwmon where possible).
>
>> +    &iio_dev_attr_name.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static const struct attribute_group ad7314_attribute_group =3D {
>> +    .attrs =3D ad7314_attributes,
>> +};
>> +
>> +/*
>> + * device probe and remove
>> + */
>> +
>> +static int __devinit ad7314_probe(struct spi_device *spi_dev) {
>> +    struct ad7314_chip_info *chip;
>> +    int ret =3D 0;
>> +
>> +    chip =3D kzalloc(sizeof(struct ad7314_chip_info), GFP_KERNEL);
>> +
>> +    if (chip =3D=3D NULL)
>> +            return -ENOMEM;
>> +
>> +    /* this is only used for device removal purposes */
>> +    dev_set_drvdata(&spi_dev->dev, chip);
>> +
>> +    chip->spi_dev =3D spi_dev;
>> +    chip->name =3D spi_dev->modalias;
>> +
>> +    chip->indio_dev =3D iio_allocate_device();
>> +    if (chip->indio_dev =3D=3D NULL) {
>> +            ret =3D -ENOMEM;
>> +            goto error_free_chip;
>> +    }
>> +
>> +    chip->indio_dev->dev.parent =3D &spi_dev->dev;
>> +    chip->indio_dev->attrs =3D &ad7314_attribute_group;
>> +    chip->indio_dev->dev_data =3D (void *)chip;
>> +    chip->indio_dev->driver_module =3D THIS_MODULE;
>> +
>> +    ret =3D iio_device_register(chip->indio_dev);
>> +    if (ret)
>> +            goto error_free_dev;
>> +
>> +    dev_info(&spi_dev->dev, "%s temperature sensor registered.\n",
>> +                     chip->name);
>> +
>> +    return 0;
>> +error_free_dev:
>> +    iio_free_device(chip->indio_dev);
>> +error_free_chip:
>> +    kfree(chip);
>> +
>> +    return ret;
>> +}
>> +
>> +static int __devexit ad7314_remove(struct spi_device *spi_dev) {
>> +    struct ad7314_chip_info *chip =3D dev_get_drvdata(&spi_dev->dev);
>> +    struct iio_dev *indio_dev =3D chip->indio_dev;
>> +
>> +    dev_set_drvdata(&spi_dev->dev, NULL);
>> +    if (spi_dev->irq)
>> +            iio_unregister_interrupt_line(indio_dev, 0);
>> +    iio_device_unregister(indio_dev);
>> +    iio_free_device(chip->indio_dev);
>> +    kfree(chip);
>> +
>> +    return 0;
>> +}
>> +
>> +static const struct spi_device_id ad7314_id[] =3D {
>> +    { "adt7301", 0 },
>> +    { "adt7302", 0 },
>> +    { "ad7314", 0 },
>> +    {}
>> +};
>> +
>> +static struct spi_driver ad7314_driver =3D {
>> +    .driver =3D {
>> +            .name =3D "ad7314",
>> +            .bus =3D &spi_bus_type,
>> +            .owner =3D THIS_MODULE,
>> +    },
>> +    .probe =3D ad7314_probe,
>> +    .remove =3D __devexit_p(ad7314_remove),
>> +    .id_table =3D ad7314_id,
>> +};
>> +
>> +static __init int ad7314_init(void)
>> +{
>> +    return spi_register_driver(&ad7314_driver);
>> +}
>> +
>> +static __exit void ad7314_exit(void)
>> +{
>> +    spi_unregister_driver(&ad7314_driver);
>> +}
>> +
>> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
>> +MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and
>ADT7302 digital"
>> +                    " temperature sensor driver");
>> +MODULE_LICENSE("GPL v2");
>> +
>> +module_init(ad7314_init);
>> +module_exit(ad7314_exit);
>
>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26  3:27     ` Zhang, Sonic
@ 2010-10-26  3:52       ` Guenter Roeck
  2010-10-26  9:15       ` Jonathan Cameron
  1 sibling, 0 replies; 48+ messages in thread
From: Guenter Roeck @ 2010-10-26  3:52 UTC (permalink / raw)
  To: Zhang, Sonic
  Cc: Jonathan Cameron, Mike Frysinger, linux-iio, device-drivers-devel

On Mon, Oct 25, 2010 at 11:27:42PM -0400, Zhang, Sonic wrote:
> 
> 
> >-----Original Message-----
> >From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
> >Sent: Monday, October 25, 2010 6:54 AM
> >To: Mike Frysinger
> >Cc: linux-iio@vger.kernel.org;
> >device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
> >Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
> >ADT7408 temperature sensors
> >
> >On 10/23/10 21:29, Mike Frysinger wrote:
> >> From: Sonic Zhang <sonic.zhang@analog.com>
> >Here we enter new territory. This device is already supported
> >in hwmon.  Do we have a usecase that is not covered by that driver?
> >
> 
> I don't find a way to get event notification other than poll in hwmon framework. So, I move all temperature devic with interrupt available to IIO framework.
> 
A much better approach, if such notifications are required and make sense,
would be to add it to the hwmon framework. Rewriting drivers just to get 
notifications is not a solution.

Besides, did you consider using kobject_uevent() ? The generic gpio driver
uses that mechanism to create udev notifications on alarms.

Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-24 23:47     ` Guenter Roeck
  2010-10-25 10:28       ` Jonathan Cameron
@ 2010-10-26  4:20       ` Zhang, Sonic
  2010-10-26  5:08         ` Guenter Roeck
  2010-10-26  9:14         ` Jonathan Cameron
  1 sibling, 2 replies; 48+ messages in thread
From: Zhang, Sonic @ 2010-10-26  4:20 UTC (permalink / raw)
  To: Guenter Roeck, Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel



>-----Original Message-----
>From: Guenter Roeck [mailto:guenter.roeck@ericsson.com]
>Sent: Monday, October 25, 2010 7:48 AM
>To: Jonathan Cameron
>Cc: Mike Frysinger; linux-iio@vger.kernel.org;
>device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic
>Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>ADT7408 temperature sensors
>
>On Sun, Oct 24, 2010 at 06:53:44PM -0400, Jonathan Cameron wrote:
>> On 10/23/10 21:29, Mike Frysinger wrote:
>> > From: Sonic Zhang <sonic.zhang@analog.com>
>> Here we enter new territory. This device is already supported in
>> hwmon.  Do we have a usecase that is not covered by that driver?
>>
>> If this is only for hardware monitoring by Guenter (cc'd)
>can perhaps
>> advise on how to support everything you have here...
>>
>
>device_id and manufactory_id (shouldn't that be
>manufacturer_id ?) don't provide much value since they are (or
>could be) already part of the device name.
>
>"capability" reflects a raw register value. I would be opposed
>to any attribute like that, since it does not mean anything
>beyond the driver for that specific chip.
>The data it provides might make sense in the driver
>documentation, but what is anyone supposed to do with the
>returned hex value ? That kind of ABI really does not make any
>sense. If the reported values are really valuable enough to be
>reported, a real abi (eg tempX_accuracy and tempX_resolution)
>would be much better.

With the raw register value, the driver gives application develop full flex=
ibility to control the device. Is it possible to define a unifiied ABI for =
all different ADCs in IIO framework? May IIO should document a policy on AB=
I difinition.


>
>For "power-save" (or "power-down" ?) and "full" mode I am not
>sure why suspend/resume isn't used instead. Is that some new
>upcoming ABI ?
>

I think the user may want to disable specific AD sensors while still keepin=
g system running.


Sonic

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26  4:20       ` Zhang, Sonic
@ 2010-10-26  5:08         ` Guenter Roeck
  2010-10-26  5:38           ` Zhang, Sonic
  2010-10-26  9:14         ` Jonathan Cameron
  1 sibling, 1 reply; 48+ messages in thread
From: Guenter Roeck @ 2010-10-26  5:08 UTC (permalink / raw)
  To: Zhang, Sonic
  Cc: Jonathan Cameron, Mike Frysinger, linux-iio, device-drivers-devel

On Tue, Oct 26, 2010 at 12:20:59AM -0400, Zhang, Sonic wrote:
[ ... ]

> 
> With the raw register value, the driver gives application develop full flexibility to control the device. Is it possible to define a unifiied ABI for all different ADCs in IIO framework? May IIO should document a policy on ABI difinition.
> 
The whole point of an ABI (or API) is that it is well defined and unified,
and can be used across multiple devices. Otherwise it is not an ABI.

Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* RE: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26  5:08         ` Guenter Roeck
@ 2010-10-26  5:38           ` Zhang, Sonic
  0 siblings, 0 replies; 48+ messages in thread
From: Zhang, Sonic @ 2010-10-26  5:38 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Jonathan Cameron, Mike Frysinger, linux-iio, device-drivers-devel



>-----Original Message-----
>From: Guenter Roeck [mailto:guenter.roeck@ericsson.com]
>Sent: Tuesday, October 26, 2010 1:09 PM
>To: Zhang, Sonic
>Cc: Jonathan Cameron; Mike Frysinger;
>linux-iio@vger.kernel.org; device-drivers-devel@blackfin.uclinux.org
>Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>ADT7408 temperature sensors
>
>On Tue, Oct 26, 2010 at 12:20:59AM -0400, Zhang, Sonic wrote:
>[ ... ]
>
>>
>> With the raw register value, the driver gives application
>develop full flexibility to control the device. Is it possible
>to define a unifiied ABI for all different ADCs in IIO
>framework? May IIO should document a policy on ABI difinition.
>>
>The whole point of an ABI (or API) is that it is well defined
>and unified, and can be used across multiple devices.
>Otherwise it is not an ABI.
>
>Guenter
>
Yes, I don't think the attribute values exported by the IIO ADC drivers are=
 ABI. Difference ADCs may differ.

Sonic

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26  4:20       ` Zhang, Sonic
  2010-10-26  5:08         ` Guenter Roeck
@ 2010-10-26  9:14         ` Jonathan Cameron
  1 sibling, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-26  9:14 UTC (permalink / raw)
  To: Zhang, Sonic
  Cc: Guenter Roeck, Mike Frysinger, linux-iio, device-drivers-devel

On 10/26/10 05:20, Zhang, Sonic wrote:
> 
> 
>> -----Original Message-----
>> From: Guenter Roeck [mailto:guenter.roeck@ericsson.com]
>> Sent: Monday, October 25, 2010 7:48 AM
>> To: Jonathan Cameron
>> Cc: Mike Frysinger; linux-iio@vger.kernel.org;
>> device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic
>> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>> ADT7408 temperature sensors
>>
>> On Sun, Oct 24, 2010 at 06:53:44PM -0400, Jonathan Cameron wrote:
>>> On 10/23/10 21:29, Mike Frysinger wrote:
>>>> From: Sonic Zhang <sonic.zhang@analog.com>
>>> Here we enter new territory. This device is already supported in
>>> hwmon.  Do we have a usecase that is not covered by that driver?
>>>
>>> If this is only for hardware monitoring by Guenter (cc'd)
>> can perhaps
>>> advise on how to support everything you have here...
>>>
>>
>> device_id and manufactory_id (shouldn't that be
>> manufacturer_id ?) don't provide much value since they are (or
>> could be) already part of the device name.
>>
>> "capability" reflects a raw register value. I would be opposed
>> to any attribute like that, since it does not mean anything
>> beyond the driver for that specific chip.
>> The data it provides might make sense in the driver
>> documentation, but what is anyone supposed to do with the
>> returned hex value ? That kind of ABI really does not make any
>> sense. If the reported values are really valuable enough to be
>> reported, a real abi (eg tempX_accuracy and tempX_resolution)
>> would be much better.
> 
> With the raw register value, the driver gives application develop
> full flexibility to control the device. Is it possible to define a
> unifiied ABI for all different ADCs in IIO framework? May IIO should
> document a policy on ABI difinition.

Yes. We have fairly comprehensive documentation (updated version was posted
to linux-iio the other day, but the stuff in place isn't that different).
drivers/staging/iio/Documentation/sysfs-bus-iio is a good starting point.

Take a look at how the other ADC drivers in tree handle similar functionality
(though I'm not entirely sure what this functionality is). max1363 or Michael's
recent drivers are good starting points.

If you want to extend the ABI, in common with every other kernel subsystem, you
propose the additions on the relevant mailing lists as an RFC.  Hardly seems
necessary to document that policy.
>>
>> For "power-save" (or "power-down" ?) and "full" mode I am not
>> sure why suspend/resume isn't used instead. Is that some new
>> upcoming ABI ?
>>
> 
> I think the user may want to disable specific AD sensors while still keeping system running.
Indeed, but it should be done using the runtime pm framework because then they can also
have the relevant spi / i2c controllers power down saving even more power.

There are some heuristics in various drivers (not in IIO) that handle auto power off.
I'm not sure if manual controls are currently supported.
> 
> 
> Sonic
> 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26  3:27     ` Zhang, Sonic
  2010-10-26  3:52       ` Guenter Roeck
@ 2010-10-26  9:15       ` Jonathan Cameron
  2010-10-26 14:33         ` Guenter Roeck
  1 sibling, 1 reply; 48+ messages in thread
From: Jonathan Cameron @ 2010-10-26  9:15 UTC (permalink / raw)
  To: Zhang, Sonic
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Guenter Roeck

On 10/26/10 04:27, Zhang, Sonic wrote:
> 
> 
>> -----Original Message-----
>> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>> Sent: Monday, October 25, 2010 6:54 AM
>> To: Mike Frysinger
>> Cc: linux-iio@vger.kernel.org;
>> device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
>> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>> ADT7408 temperature sensors
>>
>> On 10/23/10 21:29, Mike Frysinger wrote:
>>> From: Sonic Zhang <sonic.zhang@analog.com>
>> Here we enter new territory. This device is already supported
>> in hwmon.  Do we have a usecase that is not covered by that driver?
>>
> 
> I don't find a way to get event notification other than poll in hwmon
> framework. So, I move all temperature devic with interrupt available
> to IIO framework.

Whilst it isn't often done (and is a little clunky). It is possible to select
on sysfs attributes much like any other file. I'm sure Guenter can tell us
if any current hwmon devices are doing this?
> Sonic
> 
>> If this is only for hardware monitoring by Guenter (cc'd) can
>> perhaps advise on how to support everything you have here...
>>
>> I'm personally not against having drivers in IIO for devices
>> supported elsewhere, but the requirements for justification
>> are rather higher. Also care is needed to ensure no issues
>> with platform data etc.
>>
>> Guenter, for your information we have a set of temp drivers
>> coming, as a small element of a larger set, from Analog's
>> tree. Those I've reviewed so far have wanted to use IIO's
>> event infrastructure (which is much more general than hwmon's
>> handling of alarms) or have been suitably high performance
>> devices with general adc's to satisfy me that they clearly
>> have uses beyond hardware monitoring.
>>
>>
>>>
>>> Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
>>> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
>>> ---
>>>  drivers/staging/iio/adc/Kconfig   |    7 +
>>>  drivers/staging/iio/adc/Makefile  |    1 +
>>>  drivers/staging/iio/adc/adt7408.c | 1006
>>> +++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 1014 insertions(+), 0 deletions(-)  create mode
>>> 100644 drivers/staging/iio/adc/adt7408.c
>>>
>>> diff --git a/drivers/staging/iio/adc/Kconfig
>>> b/drivers/staging/iio/adc/Kconfig index 5d13918..ea75700 100644
>>> --- a/drivers/staging/iio/adc/Kconfig
>>> +++ b/drivers/staging/iio/adc/Kconfig
>>> @@ -114,3 +114,10 @@ config ADT7408
>>>      help
>>>        Say yes here to build support for Analog Devices ADT7408
>>>        temperature sensors.
>>> +
>>> +config ADT7410
>>> +    tristate "Analog Devices ADT7410 temperature sensor driver"
>>> +    depends on I2C
>>> +    help
>>> +      Say yes here to build support for Analog Devices ADT7410
>>> +      temperature sensors.
>>> diff --git a/drivers/staging/iio/adc/Makefile
>>> b/drivers/staging/iio/adc/Makefile
>>> index 6c11363..dc2bdbe 100644
>>> --- a/drivers/staging/iio/adc/Makefile
>>> +++ b/drivers/staging/iio/adc/Makefile
>>> @@ -21,3 +21,4 @@ obj-$(CONFIG_AD774X) += ad774x.o
>>>  obj-$(CONFIG_AD7816) += ad7816.o
>>>  obj-$(CONFIG_ADT75) += adt75.o
>>>  obj-$(CONFIG_ADT7310) += adt7310.o
>>> +obj-$(CONFIG_ADT7408) += adt7408.o
>>> diff --git a/drivers/staging/iio/adc/adt7408.c
>>> b/drivers/staging/iio/adc/adt7408.c
>>> new file mode 100644
>>> index 0000000..25bd594
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/adc/adt7408.c
>>> @@ -0,0 +1,1006 @@
>>> +/*
>>> + * ADT7408 digital temperature sensor driver supporting ADT7408
>>> + *
>>> + * Copyright 2010 Analog Devices Inc.
>>> + *
>>> + * Licensed under the GPL-2 or later.
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/gpio.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/i2c.h>
>>> +#include <linux/rtc.h>
>>> +
>>> +#include "../iio.h"
>>> +#include "../sysfs.h"
>>> +
>>> +/*
>>> + * ADT7408 registers definition
>>> + */
>>> +
>>> +#define ADT7408_CAPABILITY          0
>>> +#define ADT7408_CONFIG                      1
>>> +#define ADT7408_T_ALARM_HIGH                2
>>> +#define ADT7408_T_ALARM_LOW         3
>>> +#define ADT7408_T_CRIT                      4
>>> +#define ADT7408_TEMPERATURE         5
>>> +#define ADT7408_MANUFACTURER_ID             6
>>> +#define ADT7408_DEVICE_ID           7
>>> +
>>> +/*
>>> + * ADT7408 capability
>>> + */
>>> +#define ADT7408_CAP_ALARM_CRIT_TRIPS        0x1
>>> +#define ADT7408_CAP_HIGH_PRECISION  0x2
>>> +#define ADT7408_CAP_WIDER_RANGE             0x4
>>> +#define ADT7408_CAP_T_RESOLUTION_MASK       0x18
>>> +#define ADT7408_CAP_T_RESOLUTION_HIGH       0x18
>>> +#define ADT7408_CAP_T_RESOLUTION_LOW        0x8
>>> +
>>> +/*
>>> + * ADT7408 config
>>> + */
>>> +#define ADT7408_EVENT_MODE          0x1
>>> +#define ADT7408_EVENT_POLARITY              0x2
>>> +#define ADT7408_EVENT_CRIT_ONLY             0x4
>>> +#define ADT7408_EVENT_ENABLE                0x8
>>> +#define ADT7408_EVENT_STATUS                0x10
>>> +#define ADT7408_EVENT_CLEAR         0x20
>>> +#define ADT7408_EVENT_ALARM_LOCK    0x40
>>> +#define ADT7408_EVENT_CRIT_LOCK             0x80
>>> +#define ADT7408_PD                  0x100
>>> +#define ADT7408_HISTERESIS_MASK             0x600
>>> +#define ADT7408_HISTERESIS_1_5              0x200
>>> +#define ADT7408_HISTERESIS_3                0x400
>>> +#define ADT7408_HISTERESIS_6                0x600
>>> +
>>> +/*
>>> + * ADT7408 masks
>>> + */
>>> +#define ADT7408_BOUND_VALUE_SIGN            0x400
>>> +#define ADT7408_BOUND_VALUE_OFFSET          2
>>> +#define ADT7408_BOUND_VALUE_FLOAT_OFFSET    2
>>> +#define ADT7408_BOUND_VALUE_FLOAT_MASK              0x3
>>> +#define ADT7408_T_VALUE_SIGN                        0x1000
>>> +#define ADT7408_T_VALUE_FLOAT_OFFSET                4
>>> +#define ADT7408_T_VALUE_FLOAT_MASK          0xF
>>> +
>>> +/*
>>> + * ADT7408 event source
>>> + */
>>> +#define ADT7408_T_BELLOW_ALARM                      0x2000
>>> +#define ADT7408_T_ABOVE_ALARM                       0x4000
>>> +#define ADT7408_T_ABOVE_CRIT                        0x8000
>>> +
>>> +
>>> +/*
>>> + * struct adt7408_chip_info - chip specifc information  */
>>> +
>>> +struct adt7408_chip_info {
>>> +    const char *name;
>>> +    struct i2c_client *client;
>>> +    struct iio_dev *indio_dev;
>>> +    struct work_struct thresh_work;
>>> +    s64 last_timestamp;
>>> +    u16 config;
>>> +};
>>> +
>>> +/*
>>> + * adt7408 register access by I2C
>>> + */
>>> +
>>> +static int adt7408_i2c_read(struct adt7408_chip_info *chip, u8 reg,
>>> +u16 *data) {
>>> +    struct i2c_client *client = chip->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 adt7408_i2c_write(struct adt7408_chip_info
>> *chip, u8 reg,
>>> +u16 data) {
>>> +    struct i2c_client *client = chip->client;
>>> +    int ret = 0;
>>> +
>>> +    ret = i2c_smbus_write_word_data(client, reg, swab16(data));
>>> +    if (ret < 0)
>>> +            dev_err(&client->dev, "I2C write error\n");
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int adt7408_is_event_locked(struct adt7408_chip_info *chip) {
>>> +    return chip->config & (ADT7408_EVENT_ALARM_LOCK |
>>> +ADT7408_EVENT_ALARM_LOCK); }
>>> +
>>> +static ssize_t adt7408_show_mode(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +
>>> +    if (chip->config & ADT7408_PD)
>>> +            return sprintf(buf, "power-save\n");
>>> +    else
>>> +            return sprintf(buf, "full\n");
>>> +}
>>> +
>>> +static ssize_t adt7408_store_mode(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & (~ADT7408_PD);
>>> +    if (!strcmp(buf, "full"))
>>> +            config |= ADT7408_PD;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_mode,
>>> +            adt7408_store_mode,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_available_modes(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return sprintf(buf, "full\npower-down\n"); }
>>> +
>>> +static IIO_DEVICE_ATTR(available_modes, S_IRUGO,
>>> +adt7408_show_available_modes, NULL, 0);
>>> +
>>> +static ssize_t adt7408_show_capability(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 capability;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CAPABILITY, &capability);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "0x%x\n", capability); }
>>> +
>>> +static IIO_DEVICE_ATTR(capability, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_capability,
>>> +            NULL,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_manufactory_id(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 id;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "0x%x\n", id);
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(manufactory_id, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_manufactory_id,
>>> +            NULL,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_device_id(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 id;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_MANUFACTURER_ID, &id);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "0x%x\n", id);
>>> +}
>>> +
>>> +static IIO_DEVICE_ATTR(device_id, S_IRUGO | S_IWUSR,
>>> +            adt7408_show_device_id,
>>> +            NULL,
>>> +            0);
>>> +
>>> +static ssize_t adt7408_show_value(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 data;
>>> +    char sign = ' ';
>>> +    int ret;
>>> +
>>> +    if (chip->config & ADT7408_PD) {
>>> +            dev_err(dev, "Can't read value in power-down mode.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    if (data & ADT7408_T_VALUE_SIGN) {
>>> +            /* convert supplement to positive value */
>>> +            data = (ADT7408_T_VALUE_SIGN << 1) - data;
>>> +            sign = '-';
>>> +    }
>>> +
>>> +    return sprintf(buf, "%c%d.%.4d\n", sign,
>>> +            (data >> ADT7408_T_VALUE_FLOAT_OFFSET),
>>> +            (data & ADT7408_T_VALUE_FLOAT_MASK) * 625); }
>>> +
>>> +static IIO_DEVICE_ATTR(value, S_IRUGO, adt7408_show_value, NULL, 0);
>>> +
>>> +static ssize_t adt7408_show_name(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    return sprintf(buf, "%s\n", chip->name); }
>>> +
>>> +static IIO_DEVICE_ATTR(name, S_IRUGO, adt7408_show_name, NULL, 0);
>>> +
>>> +static struct attribute *adt7408_attributes[] = {
>>> +    &iio_dev_attr_available_modes.dev_attr.attr,
>>> +    &iio_dev_attr_mode.dev_attr.attr,
>>> +    &iio_dev_attr_capability.dev_attr.attr,
>>> +    &iio_dev_attr_device_id.dev_attr.attr,
>>> +    &iio_dev_attr_manufactory_id.dev_attr.attr,
>>> +    &iio_dev_attr_value.dev_attr.attr,
>>> +    &iio_dev_attr_name.dev_attr.attr,
>>> +    NULL,
>>> +};
>>> +
>>> +static const struct attribute_group adt7408_attribute_group = {
>>> +    .attrs = adt7408_attributes,
>>> +};
>>> +
>>> +/*
>>> + * temperature bound events
>>> + */
>>> +
>>> +#define IIO_EVENT_CODE_ADT7408_ABOVE_ALARM
>> (IIO_EVENT_CODE_DEVICE_SPECIFIC + 1)
>>> +#define IIO_EVENT_CODE_ADT7408_BELLOW_ALARM
>> (IIO_EVENT_CODE_DEVICE_SPECIFIC + 2)
>>> +#define IIO_EVENT_CODE_ADT7408_ABOVE_CRIT
>> (IIO_EVENT_CODE_DEVICE_SPECIFIC + 3)
>>> +
>>> +static void adt7408_interrupt_bh(struct work_struct *work_s) {
>>> +    struct adt7408_chip_info *chip =
>>> +            container_of(work_s, struct adt7408_chip_info,
>> thresh_work);
>>> +    u16 config;
>>> +    u16 data;
>>> +
>>> +    if (adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config))
>>> +            return;
>>> +
>>> +    if (!(chip->config & ADT7408_EVENT_STATUS))
>>> +            return;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_CLEAR;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_CLEAR;
>>> +
>>> +    adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +
>>> +    if (adt7408_i2c_read(chip, ADT7408_TEMPERATURE, &data))
>>> +            goto exit;
>>> +
>>> +    if (data & ADT7408_T_ABOVE_ALARM)
>>> +            iio_push_event(chip->indio_dev, 0,
>>> +                    IIO_EVENT_CODE_ADT7408_ABOVE_ALARM,
>>> +                    chip->last_timestamp);
>>> +    if (data & ADT7408_T_BELLOW_ALARM)
>>> +            iio_push_event(chip->indio_dev, 0,
>>> +                    IIO_EVENT_CODE_ADT7408_BELLOW_ALARM,
>>> +                    chip->last_timestamp);
>>> +    if (data & ADT7408_T_ABOVE_CRIT)
>>> +            iio_push_event(chip->indio_dev, 0,
>>> +                    IIO_EVENT_CODE_ADT7408_ABOVE_CRIT,
>>> +                    chip->last_timestamp);
>>> +exit:
>>> +    enable_irq(chip->client->irq);
>>> +}
>>> +
>>> +static int adt7408_interrupt(struct iio_dev *dev_info,
>>> +            int index,
>>> +            s64 timestamp,
>>> +            int no_test)
>>> +{
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +
>>> +    chip->last_timestamp = timestamp;
>>> +    schedule_work(&chip->thresh_work);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +IIO_EVENT_SH(adt7408, &adt7408_interrupt);
>>> +
>>> +static ssize_t adt7408_show_event_mode(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    if (chip->config & ADT7408_EVENT_MODE)
>>> +            return sprintf(buf, "interrupt\n");
>>> +    else
>>> +            return sprintf(buf, "comparator\n"); }
>>> +
>>> +static ssize_t adt7408_set_event_mode(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config &= ~ADT7408_EVENT_MODE;
>>> +    if (strcmp(buf, "comparator") != 0)
>>> +            config |= ADT7408_EVENT_MODE;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_available_event_modes(struct
>> device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return sprintf(buf, "comparator\ninterrupt\n"); }
>>> +
>>> +static ssize_t adt7408_show_event_crit_only(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_CRIT_ONLY)); }
>>> +
>>> +static ssize_t adt7408_set_event_crit_only(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    unsigned long data = 0;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = strict_strtoul(buf, 10, &data);
>>> +    if (ret)
>>> +            return -EINVAL;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config &= ~ADT7408_EVENT_CRIT_ONLY;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_CRIT_ONLY;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_event_enable(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_ENABLE)); }
>>> +
>>> +static ssize_t adt7408_set_event_enable(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    unsigned long data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    if (adt7408_is_event_locked(chip)) {
>>> +            dev_err(dev, "Warning: Events are locked.\n");
>>> +            return -EIO;
>>> +    }
>>> +
>>> +    ret = strict_strtoul(buf, 10, &data);
>>> +    if (ret)
>>> +            return -EINVAL;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_ENABLE;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_ENABLE;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_alarm_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_ALARM_LOCK)); }
>>> +
>>> +static ssize_t adt7408_set_alarm_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    unsigned long data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    ret = strict_strtoul(buf, 10, &data);
>>> +    if (ret)
>>> +            return -EINVAL;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_ALARM_LOCK;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_ALARM_LOCK;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_crit_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return sprintf(buf, "%d\n", !!(chip->config &
>>> +ADT7408_EVENT_CRIT_LOCK)); }
>>> +
>>> +static ssize_t adt7408_set_crit_lock(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    unsigned long data;
>>> +    u16 config;
>>> +    int ret;
>>> +
>>> +    ret = strict_strtoul(buf, 10, &data);
>>> +    if (ret)
>>> +            return -EINVAL;
>>> +
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config = chip->config & ~ADT7408_EVENT_CRIT_LOCK;
>>> +    if (data)
>>> +            config |= ADT7408_EVENT_CRIT_LOCK;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +
>>> +static inline ssize_t adt7408_show_t_bound(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            u8 bound_reg,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    u16 data;
>>> +    char sign = ' ';
>>> +    int ret;
>>> +
>>> +    ret = adt7408_i2c_read(chip, bound_reg, &data);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    data >>= ADT7408_BOUND_VALUE_OFFSET;
>>> +    if (data & ADT7408_BOUND_VALUE_SIGN) {
>>> +            /* convert supplement to positive value */
>>> +            data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
>>> +            sign = '-';
>>> +    }
>>> +
>>> +    return sprintf(buf, "%c%d.%.2d\n", sign,
>>> +                    data >> ADT7408_BOUND_VALUE_FLOAT_OFFSET,
>>> +                    (data & ADT7408_BOUND_VALUE_FLOAT_MASK) * 25); }
>>> +
>>> +static inline ssize_t adt7408_set_t_bound(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            u8 bound_reg,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    long tmp1, tmp2;
>>> +    u16 data;
>>> +    char *pos;
>>> +    int ret;
>>> +
>>> +    pos = strchr(buf, '.');
>>> +
>>> +    ret = strict_strtol(buf, 10, &tmp1);
>>> +
>>> +    if (ret || tmp1 > 127 || tmp1 < -128)
>>> +            return -EINVAL;
>>> +
>>> +    if (pos) {
>>> +            len = strlen(pos);
>>> +            if (len > ADT7408_BOUND_VALUE_FLOAT_OFFSET)
>>> +                    len = ADT7408_BOUND_VALUE_FLOAT_OFFSET;
>>> +            pos[len] = 0;
>>> +            ret = strict_strtol(pos, 10, &tmp2);
>>> +
>>> +            if (!ret)
>>> +                    tmp2 = (tmp2 / 25) * 25;
>>> +    }
>>> +
>>> +    if (tmp1 < 0)
>>> +            data = (u16)(-tmp1);
>>> +    else
>>> +            data = (u16)tmp1;
>>> +    data = (data << ADT7408_BOUND_VALUE_FLOAT_OFFSET) |
>>> +            (tmp2 & ADT7408_BOUND_VALUE_FLOAT_MASK);
>>> +    if (tmp1 < 0)
>>> +            /* convert positive value to supplyment */
>>> +            data = (ADT7408_BOUND_VALUE_SIGN << 1) - data;
>>> +    data <<= ADT7408_BOUND_VALUE_OFFSET;
>>> +
>>> +    ret = adt7408_i2c_write(chip, bound_reg, data);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_alarm_high(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return adt7408_show_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_HIGH, buf);
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_alarm_high(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    return adt7408_set_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_HIGH, buf, len);
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_alarm_low(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return adt7408_show_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_LOW, buf);
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_alarm_low(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    return adt7408_set_t_bound(dev, attr,
>>> +                    ADT7408_T_ALARM_LOW, buf, len);
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_crit(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return adt7408_show_t_bound(dev, attr,
>>> +                    ADT7408_T_CRIT, buf);
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_crit(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    return adt7408_set_t_bound(dev, attr,
>>> +                    ADT7408_T_CRIT, buf, len);
>>> +}
>>> +
>>> +static ssize_t adt7408_show_t_hyst(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +
>>> +    /* retrive ALART status */
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    switch (chip->config & ADT7408_HISTERESIS_MASK) {
>>> +    case ADT7408_HISTERESIS_1_5:
>>> +            return sprintf(buf, "1.5\n");
>>> +    case ADT7408_HISTERESIS_3:
>>> +            return sprintf(buf, "3\n");
>>> +    case ADT7408_HISTERESIS_6:
>>> +            return sprintf(buf, "6\n");
>>> +    default:
>>> +            return sprintf(buf, "Disabled\n");
>>> +    }
>>> +}
>>> +
>>> +static inline ssize_t adt7408_set_t_hyst(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            const char *buf,
>>> +            size_t len)
>>> +{
>>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>>> +    struct adt7408_chip_info *chip = dev_info->dev_data;
>>> +    int ret;
>>> +    u16 config = 0;
>>> +
>>> +    if (strcmp(buf, "disble"))
>>> +            config = ADT7408_HISTERESIS_MASK;
>>> +    else if (strcmp(buf, "1.5"))
>>> +            config = ADT7408_HISTERESIS_1_5;
>>> +    else if (len > 1 && buf[0] == '3')
>>> +            config = ADT7408_HISTERESIS_6;
>>> +    else if (len > 1 && buf[0] == '6')
>>> +            config = ADT7408_HISTERESIS_6;
>>> +
>>> +    if (!config)
>>> +            return -EINVAL;
>>> +
>>> +    /* retrive ALART status */
>>> +    ret = adt7408_i2c_read(chip, ADT7408_CONFIG, &chip->config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    config |= chip->config & ~ADT7408_HISTERESIS_MASK;
>>> +
>>> +    ret = adt7408_i2c_write(chip, ADT7408_CONFIG, config);
>>> +    if (ret)
>>> +            return -EIO;
>>> +
>>> +    chip->config = config;
>>> +    return ret;
>>> +}
>>> +
>>> +static ssize_t adt7408_show_available_t_hyst(struct device *dev,
>>> +            struct device_attribute *attr,
>>> +            char *buf)
>>> +{
>>> +    return sprintf(buf, "1.5\n3\n6\ndisable\n"); }
>>> +
>>> +IIO_EVENT_ATTR_SH(event_mode, iio_event_adt7408,
>>> +            adt7408_show_event_mode, adt7408_set_event_mode, 0);
>>> +IIO_EVENT_ATTR_SH(available_event_modes, iio_event_adt7408,
>>> +            adt7408_show_available_event_modes, NULL, 0);
>>> +IIO_EVENT_ATTR_SH(event_crit_only, iio_event_adt7408,
>>> +            adt7408_show_event_crit_only,
>> adt7408_set_event_crit_only, 0);
>>> +IIO_EVENT_ATTR_SH(event_enable, iio_event_adt7408,
>>> +            adt7408_show_event_enable,
>> adt7408_set_event_enable, 0);
>>> +IIO_EVENT_ATTR_SH(alarm_lock, iio_event_adt7408,
>>> +            adt7408_show_alarm_lock, adt7408_set_alarm_lock, 0);
>>> +IIO_EVENT_ATTR_SH(crit_lock, iio_event_adt7408,
>>> +            adt7408_show_crit_lock, adt7408_set_crit_lock, 0);
>>> +IIO_EVENT_ATTR_SH(t_alarm_high, iio_event_adt7408,
>>> +            adt7408_show_t_alarm_high,
>> adt7408_set_t_alarm_high, 0);
>>> +IIO_EVENT_ATTR_SH(t_alarm_low, iio_event_adt7408,
>>> +            adt7408_show_t_alarm_low, adt7408_set_t_alarm_low, 0);
>>> +IIO_EVENT_ATTR_SH(t_crit, iio_event_adt7408,
>>> +            adt7408_show_t_crit, adt7408_set_t_crit, 0);
>>> +IIO_EVENT_ATTR_SH(t_hyst, iio_event_adt7408,
>>> +            adt7408_show_t_hyst, adt7408_set_t_hyst, 0);
>>> +IIO_EVENT_ATTR_SH(available_t_hyst, iio_event_adt7408,
>>> +            adt7408_show_available_t_hyst, NULL, 0);
>>> +
>>> +static struct attribute *adt7408_event_attributes[] = {
>>> +    &iio_event_attr_event_mode.dev_attr.attr,
>>> +    &iio_event_attr_available_event_modes.dev_attr.attr,
>>> +    &iio_event_attr_event_crit_only.dev_attr.attr,
>>> +    &iio_event_attr_event_enable.dev_attr.attr,
>>> +    &iio_event_attr_alarm_lock.dev_attr.attr,
>>> +    &iio_event_attr_crit_lock.dev_attr.attr,
>>> +    &iio_event_attr_t_alarm_high.dev_attr.attr,
>>> +    &iio_event_attr_t_alarm_low.dev_attr.attr,
>>> +    &iio_event_attr_t_crit.dev_attr.attr,
>>> +    &iio_event_attr_t_hyst.dev_attr.attr,
>>> +    &iio_event_attr_available_t_hyst.dev_attr.attr,
>>> +    NULL,
>>> +};
>>> +
>>> +static struct attribute_group adt7408_event_attribute_group = {
>>> +    .attrs = adt7408_event_attributes,
>>> +};
>>> +
>>> +/*
>>> + * device probe and remove
>>> + */
>>> +
>>> +static int __devinit adt7408_probe(struct i2c_client *client,
>>> +            const struct i2c_device_id *id)
>>> +{
>>> +    struct adt7408_chip_info *chip;
>>> +    int ret = 0;
>>> +
>>> +    chip = kzalloc(sizeof(struct adt7408_chip_info), GFP_KERNEL);
>>> +
>>> +    if (chip == NULL)
>>> +            return -ENOMEM;
>>> +
>>> +    /* this is only used for device removal purposes */
>>> +    i2c_set_clientdata(client, chip);
>>> +
>>> +    chip->client = client;
>>> +    chip->name = id->name;
>>> +
>>> +    chip->indio_dev = iio_allocate_device();
>>> +    if (chip->indio_dev == NULL) {
>>> +            ret = -ENOMEM;
>>> +            goto error_free_chip;
>>> +    }
>>> +
>>> +    chip->indio_dev->dev.parent = &client->dev;
>>> +    chip->indio_dev->attrs = &adt7408_attribute_group;
>>> +    chip->indio_dev->event_attrs = &adt7408_event_attribute_group;
>>> +    chip->indio_dev->dev_data = (void *)chip;
>>> +    chip->indio_dev->driver_module = THIS_MODULE;
>>> +    chip->indio_dev->num_interrupt_lines = 1;
>>> +    chip->indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> +    ret = iio_device_register(chip->indio_dev);
>>> +    if (ret)
>>> +            goto error_free_dev;
>>> +
>>> +    if (client->irq) {
>>> +            ret = iio_register_interrupt_line(client->irq,
>>> +                            chip->indio_dev,
>>> +                            0,
>>> +                            client->irq_flags,
>>> +                            chip->name);
>>> +            if (ret)
>>> +                    goto error_unreg_dev;
>>> +
>>> +            /*
>>> +             * The event handler list element refer to
>> iio_event_adt7408.
>>> +             * All event attributes bind to the same event handler.
>>> +             * So, only register event handler once.
>>> +             */
>>> +            iio_add_event_to_list(&iio_event_adt7408,
>>> +
>> &chip->indio_dev->interrupts[0]->ev_list);
>>> +
>>> +            INIT_WORK(&chip->thresh_work, adt7408_interrupt_bh);
>>> +
>>> +            ret = adt7408_i2c_read(chip, ADT7408_CONFIG,
>> &chip->config);
>>> +            if (ret) {
>>> +                    ret = -EIO;
>>> +                    goto error_unreg_irq;
>>> +            }
>>> +
>>> +            if (client->irq_flags & IRQF_TRIGGER_HIGH)
>>> +                    chip->config |= ADT7408_EVENT_POLARITY;
>>> +            else
>>> +                    chip->config &= ~ADT7408_EVENT_POLARITY;
>>> +
>>> +            ret = adt7408_i2c_write(chip, ADT7408_CONFIG,
>> chip->config);
>>> +            if (ret) {
>>> +                    ret = -EIO;
>>> +                    goto error_unreg_irq;
>>> +            }
>>> +    }
>>> +
>>> +    dev_info(&client->dev, "%s temperature sensor registered.\n",
>>> +                     id->name);
>>> +
>>> +    return 0;
>>> +
>>> +error_unreg_irq:
>>> +    iio_unregister_interrupt_line(chip->indio_dev, 0);
>>> +error_unreg_dev:
>>> +    iio_device_unregister(chip->indio_dev);
>>> +error_free_dev:
>>> +    iio_free_device(chip->indio_dev);
>>> +error_free_chip:
>>> +    kfree(chip);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int __devexit adt7408_remove(struct i2c_client *client) {
>>> +    struct adt7408_chip_info *chip = i2c_get_clientdata(client);
>>> +    struct iio_dev *indio_dev = chip->indio_dev;
>>> +
>>> +    if (client->irq)
>>> +            iio_unregister_interrupt_line(indio_dev, 0);
>>> +    iio_device_unregister(indio_dev);
>>> +    iio_free_device(chip->indio_dev);
>>> +    kfree(chip);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct i2c_device_id adt7408_id[] = {
>>> +    { "adt7408", 0 },
>>> +    {}
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(i2c, adt7408_id);
>>> +
>>> +static struct i2c_driver adt7408_driver = {
>>> +    .driver = {
>>> +            .name = "adt7408",
>>> +    },
>>> +    .probe = adt7408_probe,
>>> +    .remove = __devexit_p(adt7408_remove),
>>> +    .id_table = adt7408_id,
>>> +};
>>> +
>>> +static __init int adt7408_init(void)
>>> +{
>>> +    return i2c_add_driver(&adt7408_driver); }
>>> +
>>> +static __exit void adt7408_exit(void) {
>>> +    i2c_del_driver(&adt7408_driver);
>>> +}
>>> +
>>> +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
>>> +MODULE_DESCRIPTION("Analog Devices ADT7408 digital"
>>> +                    " temperature sensor driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +
>>> +module_init(adt7408_init);
>>> +module_exit(adt7408_exit);
>>
>>
> 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26  9:15       ` Jonathan Cameron
@ 2010-10-26 14:33         ` Guenter Roeck
  2010-11-01 10:56           ` Jonathan Cameron
  0 siblings, 1 reply; 48+ messages in thread
From: Guenter Roeck @ 2010-10-26 14:33 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Zhang, Sonic, Mike Frysinger, linux-iio, device-drivers-devel

On Tue, Oct 26, 2010 at 05:15:30AM -0400, Jonathan Cameron wrote:
> On 10/26/10 04:27, Zhang, Sonic wrote:
> >
> >
> >> -----Original Message-----
> >> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
> >> Sent: Monday, October 25, 2010 6:54 AM
> >> To: Mike Frysinger
> >> Cc: linux-iio@vger.kernel.org;
> >> device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
> >> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
> >> ADT7408 temperature sensors
> >>
> >> On 10/23/10 21:29, Mike Frysinger wrote:
> >>> From: Sonic Zhang <sonic.zhang@analog.com>
> >> Here we enter new territory. This device is already supported
> >> in hwmon.  Do we have a usecase that is not covered by that driver?
> >>
> >
> > I don't find a way to get event notification other than poll in hwmon
> > framework. So, I move all temperature devic with interrupt available
> > to IIO framework.
> 
> Whilst it isn't often done (and is a little clunky). It is possible to select
> on sysfs attributes much like any other file. I'm sure Guenter can tell us
> if any current hwmon devices are doing this?

One can always use sysfs_notify(). Not sure I understand what is clunky about it.
gpio-fan uses it, and I have used it as well in an internal driver. Works pretty well.

I thought that is what was meant with "poll" [ie poll (2)], and somehow considered
to not be good enough. Would be nice to know the reasons, though, and how iio does
better than that. Maybe I need to spend some time reading the iio documentation.

Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-10-26 14:33         ` Guenter Roeck
@ 2010-11-01 10:56           ` Jonathan Cameron
  2010-11-01 14:37             ` Guenter Roeck
  0 siblings, 1 reply; 48+ messages in thread
From: Jonathan Cameron @ 2010-11-01 10:56 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Zhang, Sonic, Mike Frysinger, linux-iio, device-drivers-devel

On 10/26/10 15:33, Guenter Roeck wrote:
> On Tue, Oct 26, 2010 at 05:15:30AM -0400, Jonathan Cameron wrote:
>> On 10/26/10 04:27, Zhang, Sonic wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>>>> Sent: Monday, October 25, 2010 6:54 AM
>>>> To: Mike Frysinger
>>>> Cc: linux-iio@vger.kernel.org;
>>>> device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
>>>> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>>>> ADT7408 temperature sensors
>>>>
>>>> On 10/23/10 21:29, Mike Frysinger wrote:
>>>>> From: Sonic Zhang <sonic.zhang@analog.com>
>>>> Here we enter new territory. This device is already supported
>>>> in hwmon.  Do we have a usecase that is not covered by that driver?
>>>>
>>>
>>> I don't find a way to get event notification other than poll in hwmon
>>> framework. So, I move all temperature devic with interrupt available
>>> to IIO framework.
>>
>> Whilst it isn't often done (and is a little clunky). It is possible to select
>> on sysfs attributes much like any other file. I'm sure Guenter can tell us
>> if any current hwmon devices are doing this?
> 
> One can always use sysfs_notify(). Not sure I understand what is clunky about it.
> gpio-fan uses it, and I have used it as well in an internal driver. Works pretty well.
> 
> I thought that is what was meant with "poll" [ie poll (2)], and somehow considered
> to not be good enough. Would be nice to know the reasons, though, and how iio does
> better than that. Maybe I need to spend some time reading the iio documentation.
As a quick and dirty summary.  IIO does it roughly the same way as input, be it heavily
stripped down. Character device rather than sysfs for events.

This is from memory, but I think the main issue with sysfs_notify is that, whilst you can
poll on any file, all of those in a given directory will wake up on a call of sysfs_notify.
In devices with one or two alarms that is probably fine as you can just read them all,
but we have devices with 10s of different alarms.  Hence you don't want to be reading lots
of files to find out what is going on.  A single alarm file would be fine if only one could
trigger simultaneously, but that's rarely the case.  Any sort of 'what alarm has triggered'
mask is clunky and difficult to generalize.

We try to use sysfs for what it is good at (low bandwidth direct reading from devices and
configuration of those devices) and character devices for the high bandwidth stuff.

Jonathan



^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-11-01 10:56           ` Jonathan Cameron
@ 2010-11-01 14:37             ` Guenter Roeck
  2010-11-01 15:19               ` Jonathan Cameron
  0 siblings, 1 reply; 48+ messages in thread
From: Guenter Roeck @ 2010-11-01 14:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Zhang, Sonic, Mike Frysinger, linux-iio, device-drivers-devel

Hi Jonathan,

On Mon, Nov 01, 2010 at 06:56:02AM -0400, Jonathan Cameron wrote:
> On 10/26/10 15:33, Guenter Roeck wrote:
> > On Tue, Oct 26, 2010 at 05:15:30AM -0400, Jonathan Cameron wrote:
> >> On 10/26/10 04:27, Zhang, Sonic wrote:
> >>>
> >>>
> >>>> -----Original Message-----
> >>>> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
> >>>> Sent: Monday, October 25, 2010 6:54 AM
> >>>> To: Mike Frysinger
> >>>> Cc: linux-iio@vger.kernel.org;
> >>>> device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
> >>>> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
> >>>> ADT7408 temperature sensors
> >>>>
> >>>> On 10/23/10 21:29, Mike Frysinger wrote:
> >>>>> From: Sonic Zhang <sonic.zhang@analog.com>
> >>>> Here we enter new territory. This device is already supported
> >>>> in hwmon.  Do we have a usecase that is not covered by that driver?
> >>>>
> >>>
> >>> I don't find a way to get event notification other than poll in hwmon
> >>> framework. So, I move all temperature devic with interrupt available
> >>> to IIO framework.
> >>
> >> Whilst it isn't often done (and is a little clunky). It is possible to select
> >> on sysfs attributes much like any other file. I'm sure Guenter can tell us
> >> if any current hwmon devices are doing this?
> > 
> > One can always use sysfs_notify(). Not sure I understand what is clunky about it.
> > gpio-fan uses it, and I have used it as well in an internal driver. Works pretty well.
> > 
> > I thought that is what was meant with "poll" [ie poll (2)], and somehow considered
> > to not be good enough. Would be nice to know the reasons, though, and how iio does
> > better than that. Maybe I need to spend some time reading the iio documentation.
> As a quick and dirty summary.  IIO does it roughly the same way as input, be it heavily
> stripped down. Character device rather than sysfs for events.
> 
> This is from memory, but I think the main issue with sysfs_notify is that, whilst you can
> poll on any file, all of those in a given directory will wake up on a call of sysfs_notify.

>>From looking at the code, I don't think that is true. It should only happen if the attribute
name is not specified in the call. From a logical perspective, it would not make much sense
to have the attribute name as parameter if it is just ignored - and the call does not ignore it.

I'll do some testing to verify this. But even if it is true, I would assume it to be a bug
that should be fixed, and not be used as argument to move drivers out of sysfs.

> In devices with one or two alarms that is probably fine as you can just read them all,
> but we have devices with 10s of different alarms.  Hence you don't want to be reading lots
> of files to find out what is going on.  A single alarm file would be fine if only one could
> trigger simultaneously, but that's rarely the case.  Any sort of 'what alarm has triggered'
> mask is clunky and difficult to generalize.
> 
select() is clunky ?

> We try to use sysfs for what it is good at (low bandwidth direct reading from devices and
> configuration of those devices) and character devices for the high bandwidth stuff.
> 
Makes sense. But right now I am not entirely sure if some sysfs bashing got in there
along the way.

Note that I would not be opposed to some thinking about the hwmon user interface 
and how to improve it, or even merge it with iio if that makes sense. I have a system
which right now reports 301 (!) alarm files, and I am not even done with all drivers.
That _is_ getting a bit large. But there should be a discussion about it.
I don't think that providing parallel drivers in different subsystems is a solution.
And writing hwmon drivers into iio with no driver in hwmon isn't a solution either.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [PATCH 12/14] staging: iio: adc: new driver for ADT7408 temperature sensors
  2010-11-01 14:37             ` Guenter Roeck
@ 2010-11-01 15:19               ` Jonathan Cameron
  0 siblings, 0 replies; 48+ messages in thread
From: Jonathan Cameron @ 2010-11-01 15:19 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Zhang, Sonic, Mike Frysinger, linux-iio, device-drivers-devel

On 11/01/10 14:37, Guenter Roeck wrote:
> Hi Jonathan,
> 
> On Mon, Nov 01, 2010 at 06:56:02AM -0400, Jonathan Cameron wrote:
>> On 10/26/10 15:33, Guenter Roeck wrote:
>>> On Tue, Oct 26, 2010 at 05:15:30AM -0400, Jonathan Cameron wrote:
>>>> On 10/26/10 04:27, Zhang, Sonic wrote:
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>>>>>> Sent: Monday, October 25, 2010 6:54 AM
>>>>>> To: Mike Frysinger
>>>>>> Cc: linux-iio@vger.kernel.org;
>>>>>> device-drivers-devel@blackfin.uclinux.org; Zhang, Sonic; Guenter Roeck
>>>>>> Subject: Re: [PATCH 12/14] staging: iio: adc: new driver for
>>>>>> ADT7408 temperature sensors
>>>>>>
>>>>>> On 10/23/10 21:29, Mike Frysinger wrote:
>>>>>>> From: Sonic Zhang <sonic.zhang@analog.com>
>>>>>> Here we enter new territory. This device is already supported
>>>>>> in hwmon.  Do we have a usecase that is not covered by that driver?
>>>>>>
>>>>>
>>>>> I don't find a way to get event notification other than poll in hwmon
>>>>> framework. So, I move all temperature devic with interrupt available
>>>>> to IIO framework.
>>>>
>>>> Whilst it isn't often done (and is a little clunky). It is possible to select
>>>> on sysfs attributes much like any other file. I'm sure Guenter can tell us
>>>> if any current hwmon devices are doing this?
>>>
>>> One can always use sysfs_notify(). Not sure I understand what is clunky about it.
>>> gpio-fan uses it, and I have used it as well in an internal driver. Works pretty well.
>>>
>>> I thought that is what was meant with "poll" [ie poll (2)], and somehow considered
>>> to not be good enough. Would be nice to know the reasons, though, and how iio does
>>> better than that. Maybe I need to spend some time reading the iio documentation.
>> As a quick and dirty summary.  IIO does it roughly the same way as input, be it heavily
>> stripped down. Character device rather than sysfs for events.
>>
>> This is from memory, but I think the main issue with sysfs_notify is that, whilst you can
>> poll on any file, all of those in a given directory will wake up on a call of sysfs_notify.
> 
>>>From looking at the code, I don't think that is true. It should only happen if the attribute
> name is not specified in the call. From a logical perspective, it would not make much sense
> to have the attribute name as parameter if it is just ignored - and the call does not ignore it.
> 
> I'll do some testing to verify this. But even if it is true, I would assume it to be a bug
> that should be fixed, and not be used as argument to move drivers out of sysfs.
It's been a while since I looked at it, and it doesn't seem to be a terribly well documented
bit of sysfs.  I may well have just imagined the whole directory only thing... 
certainly looks to use the individual attributes.

So question is whether a big event system can be well covered by this. Next issue for
us is timed events (which we care about, but I'm guessing hwmon does not).
The only data we associate with an event at the moment (other than what it was)
is the timestamp...

There is clearly a fair bit of overhead in the notify functions, but no idea how significant
that will be.  The sysfs_find_dirent calls all use a strcmp to match against the attribute name
for starters.
> 
>> In devices with one or two alarms that is probably fine as you can just read them all,
>> but we have devices with 10s of different alarms.  Hence you don't want to be reading lots
>> of files to find out what is going on.  A single alarm file would be fine if only one could
>> trigger simultaneously, but that's rarely the case.  Any sort of 'what alarm has triggered'
>> mask is clunky and difficult to generalize.
>>
> select() is clunky ?
select is fine. My comment was based on the false assumption the notify was directory wide.
Really no idea what made me think that in the first place.... 
> 
>> We try to use sysfs for what it is good at (low bandwidth direct reading from devices and
>> configuration of those devices) and character devices for the high bandwidth stuff.
>>
> Makes sense. But right now I am not entirely sure if some sysfs bashing got in there
> along the way.
Certainly possible!
> 
> Note that I would not be opposed to some thinking about the hwmon user interface 
> and how to improve it, or even merge it with iio if that makes sense. I have a system
> which right now reports 301 (!) alarm files, and I am not even done with all drivers.
Ouch :) 
> That _is_ getting a bit large. But there should be a discussion about it.
> I don't think that providing parallel drivers in different subsystems is a solution.
> And writing hwmon drivers into iio with no driver in hwmon isn't a solution either.
Agreed, we need to work on this.  To us, these devices are a fairly small corner, but
one we would definitely like to handle sensibly and well.  The tricky bit comes in doing
this without causing problems for other types of device.

Jonathan

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2010-11-01 15:13 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-23 20:29 [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Mike Frysinger
2010-10-23 20:29 ` [PATCH 02/14] staging: iio: adc: new driver for AD7291 devices Mike Frysinger
2010-10-24 21:32   ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 03/14] staging: iio: adc: new driver for AD7298 devices Mike Frysinger
2010-10-24 21:49   ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 04/14] staging: iio: adc: new driver for AD7314 devices Mike Frysinger
2010-10-24 21:56   ` Jonathan Cameron
2010-10-26  3:35     ` Zhang, Sonic
2010-10-23 20:29 ` [PATCH 05/14] staging: iio: adc: new driver for AD7414/5 devices Mike Frysinger
2010-10-24 22:03   ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 06/14] staging: iio: adc: new driver for AD7416/7/8 devices Mike Frysinger
2010-10-24 22:19   ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 07/14] staging: iio: adc: new driver for AD7475/6/6A/7/7A/8/8A and AD7495 devices Mike Frysinger
2010-10-24 21:14   ` [Device-drivers-devel] " Mike Frysinger
2010-10-24 22:21   ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 08/14] staging: iio: adc: new driver for AD7745/6/7 devices Mike Frysinger
2010-10-24 22:36   ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 09/14] staging: iio: adc: new driver for AD7816 devices Mike Frysinger
2010-10-23 20:29 ` [PATCH 10/14] staging: iio: adc: new driver for ADT75 temperature sensors Mike Frysinger
2010-10-23 20:29 ` [PATCH 11/14] staging: iio: adc: new driver for ADT7310 " Mike Frysinger
2010-10-23 20:29 ` [PATCH 12/14] staging: iio: adc: new driver for ADT7408 " Mike Frysinger
2010-10-24 22:53   ` Jonathan Cameron
2010-10-24 23:47     ` Guenter Roeck
2010-10-25 10:28       ` Jonathan Cameron
2010-10-26  4:20       ` Zhang, Sonic
2010-10-26  5:08         ` Guenter Roeck
2010-10-26  5:38           ` Zhang, Sonic
2010-10-26  9:14         ` Jonathan Cameron
2010-10-25  0:46     ` Guenter Roeck
2010-10-25 10:32       ` Jonathan Cameron
2010-10-25 11:19         ` Guenter Roeck
2010-10-25 11:43           ` Jonathan Cameron
2010-10-25 14:12             ` Guenter Roeck
2010-10-25 16:18               ` Hennerich, Michael
2010-10-25 11:47           ` [Device-drivers-devel] " Hennerich, Michael
2010-10-26  3:21             ` Zhang, Sonic
2010-10-26  3:27     ` Zhang, Sonic
2010-10-26  3:52       ` Guenter Roeck
2010-10-26  9:15       ` Jonathan Cameron
2010-10-26 14:33         ` Guenter Roeck
2010-11-01 10:56           ` Jonathan Cameron
2010-11-01 14:37             ` Guenter Roeck
2010-11-01 15:19               ` Jonathan Cameron
2010-10-23 20:29 ` [PATCH 13/14] staging: iio: adc: new driver for ADT7410 " Mike Frysinger
2010-10-23 20:29 ` [PATCH 14/14] staging: iio: adc: new ad799x driver Mike Frysinger
2010-10-24 21:14   ` [Device-drivers-devel] " Mike Frysinger
2010-10-24 22:55   ` Jonathan Cameron
2010-10-24 21:09 ` [PATCH 01/14] staging: iio: adc: new driver for AD7152/3 devices Jonathan Cameron

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.