* [PATCH 1/6] IIO: Core sysfs only support.
2011-09-27 14:29 [RFC PATCH 0/6 V2] IIO: Out of staging step 1: The core Jonathan Cameron
@ 2011-09-27 14:29 ` Jonathan Cameron
2011-09-27 14:29 ` [PATCH 2/6] IIO:ADC: max1363 initial import Jonathan Cameron
` (4 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Jonathan Cameron @ 2011-09-27 14:29 UTC (permalink / raw)
To: linux-iio; +Cc: dtor, khali, guenter.roeck, Jonathan Cameron
Add support for simple sysfs only interfaces.
Bulk of patch is concerned with taking struct iio_chan_spec
arrays and generating all the relevant interfaces from them.
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
drivers/Kconfig | 2 +
drivers/Makefile | 2 +
drivers/iio/Kconfig | 11 +
drivers/iio/Makefile | 6 +
drivers/iio/iio.c | 593 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/iio.h | 249 +++++++++++++++++++
include/linux/iio/sysfs.h | 68 +++++
7 files changed, 931 insertions(+), 0 deletions(-)
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 95b9e7e..331b3d5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -130,4 +130,6 @@ source "drivers/iommu/Kconfig"
source "drivers/virt/Kconfig"
+source "drivers/iio/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7fa433a..03c38ed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -127,3 +127,5 @@ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
# Virtualization drivers
obj-$(CONFIG_VIRT_DRIVERS) += virt/
+
+obj-$(CONFIG_IIO) += iio/
\ No newline at end of file
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
new file mode 100644
index 0000000..5d9a97d
--- /dev/null
+++ b/drivers/iio/Kconfig
@@ -0,0 +1,11 @@
+#
+# Industrial I/O subsystem
+#
+
+menuconfig IIO
+ tristate "Industrial I/O support"
+ depends on GENERIC_HARDIRQS
+ help
+ The Industrial input / output subsystem provides a unified
+ framework for many different types of embedded sensor.
+ See Documentation/iio for more information.
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
new file mode 100644
index 0000000..733846b
--- /dev/null
+++ b/drivers/iio/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Industrial I/O subsystem
+#
+
+obj-$(CONFIG_IIO) += iio.o
+industrialio-y := core.o
diff --git a/drivers/iio/iio.c b/drivers/iio/iio.c
new file mode 100644
index 0000000..ec046d8
--- /dev/null
+++ b/drivers/iio/iio.c
@@ -0,0 +1,593 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008-2011 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.
+ *
+ * Based on elements of hwmon and input subsystems.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+static DEFINE_IDA(iio_ida);
+
+static struct bus_type iio_bus_type = {
+ .name = "iio",
+};
+
+static const char * const iio_data_type_name[] = {
+ [IIO_RAW] = "raw",
+ [IIO_PROCESSED] = "input",
+};
+
+static const char * const iio_chan_type_name_spec[] = {
+ [IIO_VOLTAGE] = "voltage",
+ [IIO_CURRENT] = "current",
+ [IIO_POWER] = "power",
+ [IIO_ACCEL] = "accel",
+ [IIO_GYRO] = "gyro",
+ [IIO_MAGN] = "magn",
+ [IIO_LIGHT] = "illuminance",
+ [IIO_INTENSITY] = "intensity",
+ [IIO_PROXIMITY] = "proximity",
+ [IIO_TEMP] = "temp",
+ [IIO_INCLI] = "incli",
+ [IIO_ROT] = "rot",
+ [IIO_ANGL] = "angl",
+ [IIO_TIMESTAMP] = "timestamp",
+};
+
+static const char * const iio_direction_name[] = {
+ [IIO_IN] = "in",
+ [IIO_OUT] = "out",
+};
+
+static const char * const iio_modifier_names[] = {
+ [IIO_MOD_X] = "x",
+ [IIO_MOD_Y] = "y",
+ [IIO_MOD_Z] = "z",
+ [IIO_MOD_LIGHT_BOTH] = "both",
+ [IIO_MOD_LIGHT_IR] = "ir",
+};
+
+/* relies on pairs of these shared then separate */
+static const char * const iio_chan_info_postfix[] = {
+ [IIO_CHAN_INFO_SCALE_SHARED/2] = "scale",
+ [IIO_CHAN_INFO_OFFSET_SHARED/2] = "offset",
+ [IIO_CHAN_INFO_CALIBSCALE_SHARED/2] = "calibscale",
+ [IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
+ [IIO_CHAN_INFO_PEAK_SHARED/2] = "peak_raw",
+ [IIO_CHAN_INFO_PEAK_SCALE_SHARED/2] = "peak_scale",
+ [IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED/2]
+ = "quadrature_correction_raw",
+};
+
+static void iio_device_free_read_attr(struct iio_dev *indio_dev,
+ struct iio_dev_attr *p)
+{
+ kfree(p->dev_attr.attr.name);
+ kfree(p);
+}
+
+static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
+{
+
+ struct iio_dev_attr *p, *n;
+
+ list_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) {
+ list_del(&p->l);
+ iio_device_free_read_attr(indio_dev, p);
+ }
+ kfree(indio_dev->chan_attr_group.attrs);
+}
+
+static void iio_dev_release(struct device *device)
+{
+ struct iio_dev *indio_dev = container_of(device, struct iio_dev, dev);
+ iio_device_unregister_sysfs(indio_dev);
+ ida_simple_remove(&iio_ida, indio_dev->id);
+ kfree(indio_dev);
+}
+
+static struct device_type iio_dev_type = {
+ .name = "iio_device",
+ .release = iio_dev_release,
+};
+
+struct iio_dev *iio_device_allocate(int sizeof_priv)
+{
+ struct iio_dev *dev;
+ size_t alloc_size;
+
+ alloc_size = sizeof(struct iio_dev);
+ if (sizeof_priv) {
+ alloc_size = ALIGN(alloc_size, IIO_ALIGN);
+ alloc_size += sizeof_priv;
+ }
+ /* ensure cacheline alignment of whole construct */
+ alloc_size += IIO_ALIGN - 1;
+
+ dev = kzalloc(alloc_size, GFP_KERNEL);
+
+ if (dev) {
+ dev->dev.type = &iio_dev_type;
+ dev->dev.bus = &iio_bus_type;
+ device_initialize(&dev->dev);
+ dev_set_drvdata(&dev->dev, (void *)dev);
+ mutex_init(&dev->mlock);
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(iio_device_allocate);
+
+void iio_device_free(struct iio_dev *dev)
+{
+ if (dev)
+ iio_put_device(dev);
+}
+EXPORT_SYMBOL_GPL(iio_device_free);
+
+ssize_t __iio_read_const_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n",
+ container_of(attr,
+ struct iio_const_attr,
+ dev_attr)->string);
+}
+EXPORT_SYMBOL_GPL(__iio_read_const_attr);
+
+static ssize_t iio_read_channel_info(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int val, val2;
+ int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
+ &val, &val2, this_attr->address);
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == IIO_VAL_INT)
+ return sprintf(buf, "%d\n", val);
+ else if (ret == IIO_VAL_INT_PLUS_MICRO) {
+ if (val2 < 0)
+ return sprintf(buf, "-%d.%06u\n", val, -val2);
+ else
+ return sprintf(buf, "%d.%06u\n", val, val2);
+ } else if (ret == IIO_VAL_INT_PLUS_NANO) {
+ if (val2 < 0)
+ return sprintf(buf, "-%d.%09u\n", val, -val2);
+ else
+ return sprintf(buf, "%d.%09u\n", val, val2);
+ } else
+ return 0;
+}
+
+static ssize_t iio_write_channel_info(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret, integer = 0, fract = 0, fract_mult = 100000;
+ bool integer_part = true, negative = false;
+
+ /* Assumes decimal - precision based on number of digits */
+ if (!indio_dev->info->write_raw)
+ return -EINVAL;
+
+ if (indio_dev->info->write_raw_get_fmt)
+ switch (indio_dev->info->write_raw_get_fmt(indio_dev,
+ this_attr->c, this_attr->address)) {
+ case IIO_VAL_INT_PLUS_MICRO:
+ fract_mult = 100000;
+ break;
+ case IIO_VAL_INT_PLUS_NANO:
+ fract_mult = 100000000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (buf[0] == '-') {
+ negative = true;
+ buf++;
+ }
+
+ while (*buf) {
+ if ('0' <= *buf && *buf <= '9') {
+ if (integer_part)
+ integer = integer*10 + *buf - '0';
+ else {
+ fract += fract_mult*(*buf - '0');
+ if (fract_mult == 1)
+ break;
+ fract_mult /= 10;
+ }
+ } else if (*buf == '\n') {
+ if (*(buf + 1) == '\0')
+ break;
+ else
+ return -EINVAL;
+ } else if (*buf == '.') {
+ integer_part = false;
+ } else {
+ return -EINVAL;
+ }
+ buf++;
+ }
+ if (negative) {
+ if (integer)
+ integer = -integer;
+ else
+ fract = -fract;
+ }
+
+ ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
+ integer, fract, this_attr->address);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static
+int __iio_device_attr_init(struct device_attribute *dev_attr,
+ const char *postfix,
+ struct iio_chan_spec const *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ bool generic)
+{
+ int ret;
+ char *name_format, *full_postfix;
+ sysfs_attr_init(&dev_attr->attr);
+
+ /* Build up postfix of <extend_name>_<modifier>_postfix */
+ if (chan->modified) {
+ if (chan->extend_name)
+ full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_modifier_names[chan
+ ->channel2],
+ chan->extend_name,
+ postfix);
+ else
+ full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_modifier_names[chan
+ ->channel2],
+ postfix);
+ } else {
+ if (chan->extend_name == NULL)
+ full_postfix = kstrdup(postfix, GFP_KERNEL);
+ else
+ full_postfix = kasprintf(GFP_KERNEL,
+ "%s_%s",
+ chan->extend_name,
+ postfix);
+ }
+ if (full_postfix == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ if (chan->differential) { /* Differential can not have modifier */
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
+ iio_direction_name[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ else if (chan->indexed)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s",
+ iio_direction_name[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ iio_chan_type_name_spec[chan->type],
+ chan->channel2,
+ full_postfix);
+ else {
+ WARN_ON("Differential channels must be indexed\n");
+ ret = -EINVAL;
+ goto error_free_full_postfix;
+ }
+ } else { /* Single ended */
+ if (generic)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction_name[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ else if (chan->indexed)
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
+ iio_direction_name[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ chan->channel,
+ full_postfix);
+ else
+ name_format
+ = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_direction_name[chan->output],
+ iio_chan_type_name_spec[chan->type],
+ full_postfix);
+ }
+ if (name_format == NULL) {
+ ret = -ENOMEM;
+ goto error_free_full_postfix;
+ }
+ dev_attr->attr.name = kasprintf(GFP_KERNEL,
+ name_format,
+ chan->channel,
+ chan->channel2);
+ if (dev_attr->attr.name == NULL) {
+ ret = -ENOMEM;
+ goto error_free_name_format;
+ }
+
+ if (readfunc) {
+ dev_attr->attr.mode |= S_IRUGO;
+ dev_attr->show = readfunc;
+ }
+
+ if (writefunc) {
+ dev_attr->attr.mode |= S_IWUSR;
+ dev_attr->store = writefunc;
+ }
+ kfree(name_format);
+ kfree(full_postfix);
+
+ return 0;
+
+error_free_name_format:
+ kfree(name_format);
+error_free_full_postfix:
+ kfree(full_postfix);
+error_ret:
+ return ret;
+}
+
+static void __iio_device_attr_deinit(struct device_attribute *dev_attr)
+{
+ kfree(dev_attr->attr.name);
+}
+
+static
+int __iio_add_chan_devattr(const char *postfix,
+ struct iio_chan_spec const *chan,
+ ssize_t (*readfunc)(struct device *dev,
+ struct device_attribute *attr,
+ char *buf),
+ ssize_t (*writefunc)(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len),
+ u64 mask,
+ bool generic,
+ struct device *dev,
+ struct list_head *attr_list)
+{
+ int ret;
+ struct iio_dev_attr *iio_attr, *t;
+
+ iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL);
+ if (iio_attr == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ ret = __iio_device_attr_init(&iio_attr->dev_attr,
+ postfix, chan,
+ readfunc, writefunc, generic);
+ if (ret)
+ goto error_iio_dev_attr_free;
+ iio_attr->c = chan;
+ iio_attr->address = mask;
+ list_for_each_entry(t, attr_list, l)
+ if (strcmp(t->dev_attr.attr.name,
+ iio_attr->dev_attr.attr.name) == 0) {
+ if (!generic)
+ dev_err(dev, "tried to double register : %s\n",
+ t->dev_attr.attr.name);
+ ret = -EBUSY;
+ goto error_device_attr_deinit;
+ }
+
+ list_add(&iio_attr->l, attr_list);
+
+ return 0;
+
+error_device_attr_deinit:
+ __iio_device_attr_deinit(&iio_attr->dev_attr);
+error_iio_dev_attr_free:
+ kfree(iio_attr);
+error_ret:
+ return ret;
+}
+
+static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ int ret, i, attrcount = 0;
+
+ if (chan->channel < 0)
+ return 0;
+ ret = __iio_add_chan_devattr(iio_data_type_name[chan->processed_val],
+ chan,
+ &iio_read_channel_info,
+ (chan->output ?
+ &iio_write_channel_info : NULL),
+ 0,
+ 0,
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
+ if (ret)
+ goto error_ret;
+
+ for_each_set_bit(i, &chan->info_mask,
+ ARRAY_SIZE(iio_chan_info_postfix)*2) {
+ ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
+ chan,
+ &iio_read_channel_info,
+ &iio_write_channel_info,
+ (1 << i),
+ !(i%2),
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
+ if (ret == -EBUSY && (i%2 == 0)) {
+ ret = 0;
+ continue;
+ }
+ if (ret < 0)
+ goto error_ret;
+ attrcount++;
+ }
+ ret = attrcount;
+error_ret:
+ return ret;
+}
+
+static ssize_t iio_show_dev_name(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", indio_dev->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
+
+static int iio_device_register_sysfs(struct iio_dev *indio_dev)
+{
+ int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
+ struct iio_dev_attr *p, *n;
+ struct attribute **attr;
+
+ /* First count elements in any existing group */
+ if (indio_dev->info->attrs) {
+ attr = indio_dev->info->attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
+ }
+ attrcount = attrcount_orig;
+
+ INIT_LIST_HEAD(&indio_dev->channel_attr_list);
+ if (indio_dev->channels)
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = iio_device_add_channel_sysfs(indio_dev,
+ &indio_dev
+ ->channels[i]);
+ if (ret < 0)
+ goto error_clear_attrs;
+ attrcount += ret;
+ }
+ if (indio_dev->name)
+ attrcount++;
+
+ indio_dev->chan_attr_group.attrs
+ = kzalloc(sizeof(indio_dev->chan_attr_group.attrs[0])*
+ (attrcount + 1),
+ GFP_KERNEL);
+ if (indio_dev->chan_attr_group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_clear_attrs;
+ }
+ /* Copy across original attributes */
+ if (indio_dev->info->attrs)
+ memcpy(indio_dev->chan_attr_group.attrs,
+ indio_dev->info->attrs->attrs,
+ sizeof(indio_dev->chan_attr_group.attrs[0])
+ *attrcount_orig);
+ attrn = attrcount_orig;
+ /* Add all elements from the list. */
+ list_for_each_entry(p, &indio_dev->channel_attr_list, l)
+ indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
+ if (indio_dev->name)
+ indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+
+ indio_dev->groups[indio_dev->groupcounter++] =
+ &indio_dev->chan_attr_group;
+
+ return 0;
+
+error_clear_attrs:
+ list_for_each_entry_safe(p, n,
+ &indio_dev->channel_attr_list, l) {
+ list_del(&p->l);
+ iio_device_free_read_attr(indio_dev, p);
+ }
+
+ return ret;
+}
+
+int iio_device_register(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
+ if (indio_dev->id < 0) {
+ ret = indio_dev->id;
+ goto error_ret;
+ }
+
+ dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id);
+
+ ret = iio_device_register_sysfs(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to register sysfs interfaces\n");
+ goto error_free_ida;
+ }
+ ret = device_add(&indio_dev->dev);
+ if (ret)
+ goto error_free_sysfs;
+
+ return 0;
+
+error_free_sysfs:
+ iio_device_unregister_sysfs(indio_dev);
+error_free_ida:
+ ida_simple_remove(&iio_ida, indio_dev->id);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *indio_dev)
+{
+ device_unregister(&indio_dev->dev);
+}
+EXPORT_SYMBOL(iio_device_unregister);
+
+static int __init iio_init(void)
+{
+ return bus_register(&iio_bus_type);
+}
+subsys_initcall(iio_init);
+
+static void __exit iio_exit(void)
+{
+ bus_unregister(&iio_bus_type);
+}
+module_exit(iio_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Industrial I/O core");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
new file mode 100644
index 0000000..a3dbcfc
--- /dev/null
+++ b/include/linux/iio/iio.h
@@ -0,0 +1,249 @@
+/*
+ * The industrial I/O core
+ *
+ * Copyright (c) 2008-2011 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.
+ */
+#include <linux/klist.h>
+#include <linux/device.h>
+
+#ifndef _IIO_H_
+#define _IIO_H_
+
+/* Minimum alignment of priv within iio_dev */
+#define IIO_ALIGN L1_CACHE_BYTES
+
+enum iio_data_type {
+ IIO_RAW,
+ IIO_PROCESSED,
+};
+
+enum iio_chan_type {
+ IIO_VOLTAGE,
+ IIO_CURRENT,
+ IIO_POWER,
+ IIO_CAPACITANCE,
+ IIO_ACCEL,
+ IIO_GYRO,
+ IIO_MAGN,
+ IIO_LIGHT,
+ IIO_INTENSITY,
+ IIO_PROXIMITY,
+ IIO_TEMP,
+ IIO_INCLI,
+ IIO_ROT,
+ IIO_ANGL,
+ IIO_TIMESTAMP,
+};
+
+enum iio_modifier {
+ IIO_MOD_X = 1,
+ IIO_MOD_Y,
+ IIO_MOD_Z,
+ IIO_MOD_X_AND_Y,
+ IIO_MOD_X_ANX_Z,
+ IIO_MOD_Y_AND_Z,
+ IIO_MOD_X_AND_Y_AND_Z,
+ IIO_MOD_X_OR_Y,
+ IIO_MOD_X_OR_Z,
+ IIO_MOD_Y_OR_Z,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_MOD_LIGHT_BOTH,
+ IIO_MOD_LIGHT_IR,
+};
+
+enum iio_chan_info_enum {
+ IIO_CHAN_INFO_SCALE_SHARED,
+ IIO_CHAN_INFO_SCALE_SEPARATE,
+ IIO_CHAN_INFO_OFFSET_SHARED,
+ IIO_CHAN_INFO_OFFSET_SEPARATE,
+ IIO_CHAN_INFO_CALIBSCALE_SHARED,
+ IIO_CHAN_INFO_CALIBSCALE_SEPARATE,
+ IIO_CHAN_INFO_CALIBBIAS_SHARED,
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE,
+ IIO_CHAN_INFO_PEAK_SHARED,
+ IIO_CHAN_INFO_PEAK_SEPARATE,
+ IIO_CHAN_INFO_PEAK_SCALE_SHARED,
+ IIO_CHAN_INFO_PEAK_SCALE_SEPARATE,
+ IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED,
+ IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE,
+};
+
+enum iio_direction {
+ IIO_IN,
+ IIO_OUT,
+};
+
+#define IIO_VAL_INT 1
+#define IIO_VAL_INT_PLUS_MICRO 2
+#define IIO_VAL_INT_PLUS_NANO 3
+
+/**
+ * struct iio_chan_spec - specification of a single channel
+ * @type: What type of measurement is the channel making.
+ * @channel: What number or name do we wish to asign the channel.
+ * @channel2: If there is a second number for a differential
+ * channel then this is it. If modified is set then the
+ * value here specifies the modifier.
+ * @address: Driver specific identifier.
+ * @scan_type: sign: Set if signed value
+ * realbits: Number of valid bits of data
+ * shift: Shift right by this before masking out
+ * realbits.
+ * @info_mask: What information is to be exported about this channel.
+ * This includes calibbias, scale etc.
+ * @extend_name: Allows labeling of channel attributes with an
+ * informative name. Note this has no effect codes etc,
+ * unlike modifiers.
+ * @processed_val: Flag to specify the data access attribute should be
+ * *_input rather than *_raw.
+ * @modified: Does a modifier apply to this channel. What these are
+ * depends on the channel type. Modifier is set in
+ * channel2. Examples are IIO_MOD_X for axial sensors about
+ * the 'x' axis.
+ * @indexed: Specify the channel has a numerical index. If not,
+ * the value in channel will be suppressed for attribute
+ * but not for event codes. Typically set it to 0 when
+ * the index is false.
+ * @output: Specify the channel is an output channel (DAC).
+ * @differential: Is the channel a differential channel. Cannot coexist
+ * with modified and requires indexed.
+ */
+struct iio_chan_spec {
+ enum iio_chan_type type;
+ int channel;
+ int channel2;
+ unsigned long address;
+ struct {
+ char sign;
+ u8 realbits;
+ u8 shift;
+ } scan_type;
+ long info_mask;
+ char *extend_name;
+ unsigned processed_val:1;
+ unsigned modified:1;
+ unsigned indexed:1;
+ unsigned output:1;
+ unsigned differential:1;
+ unsigned sign:1;
+};
+
+struct iio_dev;
+
+/**
+ * struct iio_info - constant information about device
+ * @driver_module: module structure used to ensure correct
+ * ownership of chrdevs etc
+ * @attrs: general purpose device attributes
+ * @read_raw: function to request a value from the device.
+ * mask specifies which value. Note 0 means a reading of
+ * the channel in question. Return value will specify the
+ * type of value returned by the device. val and val2 will
+ * contain the elements making up the returned value.
+ * @write_raw: function to write a value to the device.
+ * Parameters are the same as for read_raw.
+ * @write_raw_get_fmt: callback function to query the expected
+ * format/precision. If not set by the driver, write_raw
+ * returns IIO_VAL_INT_PLUS_MICRO.
+ **/
+struct iio_info {
+ struct module *driver_module;
+ const struct attribute_group *attrs;
+
+ int (*read_raw)(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask);
+
+ int (*write_raw)(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask);
+
+ int (*write_raw_get_fmt)(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask);
+};
+
+/**
+ * struct iio_dev - industrial I/O device
+ * @id: [INTERN] used to identify device internally
+ * @dev: [DRIVER] device structure, should be assigned a parent
+ * and owner
+ * @mlock: [INTERN] lock used to prevent simultaneous device state
+ * changes
+ * @available_scan_masks: [DRIVER] optional array of allowed bitmasks
+ * @trig: [INTERN] current device trigger (ring buffer modes)
+ * @channels: [DRIVER] channel specification structure table
+ * @num_channels: [DRIVER] number of chanels specified in @channels
+ * @channel_attr_list: [INTERN] keep track of automatically created channel
+ * attributes
+ * @chan_attr_group: [INTERN] group for all attrs in base directory
+ * @name: [DRIVER] name of the device
+ * @info: [DRIVER] callbacks and constant info from driver
+ * @groups: [INTERN] attribute groups
+ * @groupcounter: [INTERN] index of next attribute group
+ **/
+struct iio_dev {
+ int id;
+ struct device dev;
+ struct mutex mlock;
+ unsigned long *available_scan_masks;
+ struct iio_chan_spec const *channels;
+ int num_channels;
+
+ struct list_head channel_attr_list;
+ struct attribute_group chan_attr_group;
+ const char *name;
+ const struct iio_info *info;
+#define IIO_MAX_GROUPS 1
+ const struct attribute_group *groups[IIO_MAX_GROUPS + 1];
+ int groupcounter;
+};
+
+/**
+ * iio_device_allocate() - allocate an iio_dev from a driver
+ * @sizeof_priv: Space to allocate for private structure.
+ **/
+struct iio_dev *iio_device_allocate(int sizeof_priv);
+
+static inline void *iio_priv(const struct iio_dev *dev)
+{
+ return (char *)dev + ALIGN(sizeof(struct iio_dev), IIO_ALIGN);
+}
+
+/**
+ * iio_device_free() - free an iio_dev from a driver
+ * @dev: the iio_dev associated with the device
+ **/
+void iio_device_free(struct iio_dev *dev);
+
+/**
+ * iio_device_register() - register a device with the IIO subsystem
+ * @dev_info: Device structure filled by the device driver
+ **/
+int iio_device_register(struct iio_dev *dev_info);
+
+/**
+ * iio_device_unregister() - unregister a device from the IIO subsystem
+ * @dev_info: Device structure representing the device.
+ **/
+void iio_device_unregister(struct iio_dev *dev_info);
+
+/**
+ * iio_put_device() - reference counted deallocation of struct device
+ * @dev: the iio_device containing the device
+ **/
+static inline void iio_put_device(struct iio_dev *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+};
+
+#endif /* _IIO_H_ */
diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h
new file mode 100644
index 0000000..c6735bf
--- /dev/null
+++ b/include/linux/iio/sysfs.h
@@ -0,0 +1,68 @@
+/*
+ * The industrial I/O core
+ *
+ * Copyright (c) 2008 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.
+ *
+ * General attributes
+ */
+
+#include <linux/klist.h>
+#include <linux/device.h>
+
+#ifndef _IIO_SYSFS_H_
+#define _IIO_SYSFS_H_
+
+struct iio_chan_spec;
+
+/**
+ * struct iio_dev_attr - iio specific device attribute
+ * @dev_attr: underlying device attribute
+ * @address: associated register address
+ * @l: list head for maintaining list of dynamically created attrs
+ * @c: channel spec for channel with which attr is associated if any
+ */
+struct iio_dev_attr {
+ struct device_attribute dev_attr;
+ int address;
+ struct list_head l;
+ struct iio_chan_spec const *c;
+};
+
+#define to_iio_dev_attr(_dev_attr) \
+ container_of(_dev_attr, struct iio_dev_attr, dev_attr)
+
+#define IIO_ATTR(_name, _mode, _show, _store, _addr) \
+ { .dev_attr = __ATTR(_name, _mode, _show, _store), \
+ .address = _addr }
+
+#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr) \
+ struct iio_dev_attr iio_dev_attr_##_name \
+ = IIO_ATTR(_name, _mode, _show, _store, _addr)
+
+/**
+ * struct iio_const_attr - constant device specific attribute
+ * often used for things like available modes
+ * @string: attribute string
+ * @dev_attr: underlying device attribute
+ */
+struct iio_const_attr {
+ const char *string;
+ struct device_attribute dev_attr;
+};
+
+#define to_iio_const_attr(_dev_attr) \
+ container_of(_dev_attr, struct iio_const_attr, dev_attr)
+
+ssize_t __iio_read_const_attr(struct device *dev,
+ struct device_attribute *attr,
+ char *len);
+
+#define IIO_CONST_ATTR(_name, _string) \
+ struct iio_const_attr iio_const_attr_##_name \
+ = { .string = _string, \
+ .dev_attr = __ATTR(_name, S_IRUGO, __iio_read_const_attr, NULL) }
+#endif /* _IIO_SYSFS_H_ */
--
1.7.3.4
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/6] IIO:ADC: max1363 initial import.
2011-09-27 14:29 [RFC PATCH 0/6 V2] IIO: Out of staging step 1: The core Jonathan Cameron
2011-09-27 14:29 ` [PATCH 1/6] IIO: Core sysfs only support Jonathan Cameron
@ 2011-09-27 14:29 ` Jonathan Cameron
2011-09-27 14:29 ` [PATCH 3/6] IIO:ADC:ad799x " Jonathan Cameron
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Jonathan Cameron @ 2011-09-27 14:29 UTC (permalink / raw)
To: linux-iio; +Cc: dtor, khali, guenter.roeck, Jonathan Cameron
Provides basic channel reading via sysfs.
For what is coming in this driver see the version still
in staging.
A couple of macros take more parameters than they use to reduce churn when
driver is updated to include events and buffer support.
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
drivers/iio/Kconfig | 6 +
drivers/iio/Makefile | 2 +
drivers/iio/adc/Kconfig | 19 +
drivers/iio/adc/Makefile | 6 +
drivers/iio/adc/max1363.h | 148 ++++++
drivers/iio/adc/max1363_core.c | 1000 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 1181 insertions(+), 0 deletions(-)
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 5d9a97d..cebe21f 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -9,3 +9,9 @@ menuconfig IIO
The Industrial input / output subsystem provides a unified
framework for many different types of embedded sensor.
See Documentation/iio for more information.
+
+if IIO
+
+source "drivers/iio/adc/Kconfig"
+
+endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 733846b..42d6d12 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -4,3 +4,5 @@
obj-$(CONFIG_IIO) += iio.o
industrialio-y := core.o
+
+obj-y += adc/
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
new file mode 100644
index 0000000..0673d78
--- /dev/null
+++ b/drivers/iio/adc/Kconfig
@@ -0,0 +1,19 @@
+#
+# ADC drivers
+#
+menu "Analog to digital convertors"
+
+config IIO_MAX1363
+ tristate "Maxim max1363 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for many Maxim i2c analog to digital
+ convertors (ADC). (max1361, max1362, max1363, max1364, max1036,
+ max1037, max1038, max1039, max1136, max1136, max1137, max1138,
+ max1139, max1236, max1237, max11238, max1239, max11600, max11601,
+ max11602, max11603, max11604, max11605, max11606, max11607,
+ max11608, max11609, max11610, max11611, max11612, max11613,
+ max11614, max11615, max11616, max11617, max11644, max11645,
+ max11646, max11647) Provides direct access via sysfs.
+
+endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
new file mode 100644
index 0000000..8d6a7f9
--- /dev/null
+++ b/drivers/iio/adc/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO ADCs
+#
+
+iio_max1363-y := max1363_core.o
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
diff --git a/drivers/iio/adc/max1363.h b/drivers/iio/adc/max1363.h
new file mode 100644
index 0000000..5135f15
--- /dev/null
+++ b/drivers/iio/adc/max1363.h
@@ -0,0 +1,148 @@
+ /*
+ * Copyright (C) 2008-2011 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.
+ */
+
+#include <linux/kernel.h>
+
+#ifndef _MAX1363_H_
+#define _MAX1363_H_
+
+#define MAX1363_MAX_CHANNELS 25
+#define MAX1363_SETUP_BYTE(a) ((a) | 0x80)
+
+/* There is a fair bit more defined here than currently
+ * used, but the intention is to support everything these
+ * chips do in the long run */
+
+/* see data sheets */
+/* max1363 and max1236, max1237, max1238, max1239 */
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00
+#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40
+#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60
+#define MAX1363_SETUP_POWER_UP_INT_REF 0x10
+#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00
+
+/* think about including max11600 etc - more settings */
+#define MAX1363_SETUP_EXT_CLOCK 0x08
+#define MAX1363_SETUP_INT_CLOCK 0x00
+#define MAX1363_SETUP_UNIPOLAR 0x00
+#define MAX1363_SETUP_BIPOLAR 0x04
+#define MAX1363_SETUP_RESET 0x00
+#define MAX1363_SETUP_NORESET 0x02
+/* max1363 only - though don't care on others.
+ * For now monitor modes are not implemented as the relevant
+ * line is not connected on my test board.
+ * The definitions are here as I intend to add this soon.
+ */
+#define MAX1363_SETUP_MONITOR_SETUP 0x01
+
+/* Specific to the max1363 */
+#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4))
+#define MAX1363_MON_INT_ENABLE 0x01
+
+/* defined for readability reasons */
+/* All chips */
+#define MAX1363_CONFIG_BYTE(a) ((a))
+
+#define MAX1363_CONFIG_SE 0x01
+#define MAX1363_CONFIG_DE 0x00
+#define MAX1363_CONFIG_SCAN_TO_CS 0x00
+#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20
+#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40
+#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60
+/* max123{6-9} only */
+#define MAX1236_SCAN_MID_TO_CHANNEL 0x40
+
+/* max1363 only - merely part of channel selects or don't care for others*/
+#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18
+
+#define MAX1363_CHANNEL_SEL(a) ((a) << 1)
+
+/* max1363 strictly 0x06 - but doesn't matter */
+#define MAX1363_CHANNEL_SEL_MASK 0x1E
+#define MAX1363_SCAN_MASK 0x60
+#define MAX1363_SE_DE_MASK 0x01
+
+/**
+ * struct max1363_mode - scan mode information
+ * @conf: The corresponding value of the configuration register
+ * @modemask: Bit mask corresponding to channels enabled in this mode
+ */
+struct max1363_mode {
+ int8_t conf;
+ DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS);
+};
+
+/* This must be maintained along side the max1363_mode_table in max1363_core */
+enum max1363_modes {
+ /* Single read of a single channel */
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ /* Differential single read */
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ /* Scan to channel and mid to channel where overlapping */
+ s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6,
+ s6to7, s0to7, s6to8, s0to8, s6to9,
+ s0to9, s6to10, s0to10, s6to11, s0to11,
+ /* Differential scan to channel and mid to channel where overlapping */
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9,
+ d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2,
+ d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8,
+ d7m6to11m10, d1m0to11m10,
+};
+
+struct iio_info;
+struct iio_chan_spec;
+
+/**
+ * struct max1363_chip_info - chip specifc information
+ * @name: indentification string for chip
+ * @bits: accuracy of the adc in bits
+ * @int_vref_mv: the internal reference voltage
+ * @info: iio core function callbacks structure
+ * @mode_list: array of available scan modes
+ * @num_modes: the number of scan modes available
+ * @default_mode: the scan mode in which the chip starts up
+ * @channels: channel specification
+ * @num_channels: number of channels
+ */
+struct max1363_chip_info {
+ const struct iio_info *info;
+ struct iio_chan_spec *channels;
+ int num_channels;
+ const enum max1363_modes *mode_list;
+ enum max1363_modes default_mode;
+ u16 int_vref_mv;
+ u8 num_modes;
+ u8 bits;
+};
+
+struct regulator;
+struct i2c_client;
+
+/**
+ * struct max1363_state - driver instance specific data
+ * @client: i2c_client
+ * @setupbyte: cache of current device setup byte
+ * @configbyte: cache of current device config byte
+ * @chip_info: chip model specific constants, available modes etc
+ * @current_mode: the scan mode of this chip
+ * @requestedmask: a valid requested set of channels
+ * @reg: supply regulator
+ */
+struct max1363_state {
+ struct i2c_client *client;
+ u8 setupbyte;
+ u8 configbyte;
+ const struct max1363_chip_info *chip_info;
+ const struct max1363_mode *current_mode;
+ u32 requestedmask;
+ struct regulator *reg;
+};
+
+#endif /* _MAX1363_H_ */
diff --git a/drivers/iio/adc/max1363_core.c b/drivers/iio/adc/max1363_core.c
new file mode 100644
index 0000000..cb51bfb
--- /dev/null
+++ b/drivers/iio/adc/max1363_core.c
@@ -0,0 +1,1000 @@
+/*
+ * iio/adc/max1363.c
+ *
+ * Copyright (C) 2008-2011 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.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "max1363.h"
+
+#define MAX1363_MODE_SINGLE(_num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask, \
+ }
+
+#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask, \
+ }
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask \
+}
+
+#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_nump) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_DE, \
+ .modemask[0] = _mask \
+ }
+
+/* Can't think how to automate naming so specify for now */
+#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_DE, \
+ .modemask[0] = _mask \
+ }
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask \
+}
+
+static const struct max1363_mode max1363_mode_table[] = {
+ /* All of the single channel options first */
+ MAX1363_MODE_SINGLE(0, 1 << 0),
+ MAX1363_MODE_SINGLE(1, 1 << 1),
+ MAX1363_MODE_SINGLE(2, 1 << 2),
+ MAX1363_MODE_SINGLE(3, 1 << 3),
+ MAX1363_MODE_SINGLE(4, 1 << 4),
+ MAX1363_MODE_SINGLE(5, 1 << 5),
+ MAX1363_MODE_SINGLE(6, 1 << 6),
+ MAX1363_MODE_SINGLE(7, 1 << 7),
+ MAX1363_MODE_SINGLE(8, 1 << 8),
+ MAX1363_MODE_SINGLE(9, 1 << 9),
+ MAX1363_MODE_SINGLE(10, 1 << 10),
+ MAX1363_MODE_SINGLE(11, 1 << 11),
+
+ MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12),
+ MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13),
+ MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14),
+ MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15),
+ MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16),
+ MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17),
+ MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18),
+ MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19),
+ MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20),
+ MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21),
+ MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22),
+ MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23),
+
+ /* The multichannel scans next */
+ MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003),
+ MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C),
+ MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF),
+
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000),
+};
+
+static int max1363_write_basic_config(struct i2c_client *client,
+ unsigned char d1,
+ unsigned char d2)
+{
+ u8 tx_buf[2] = {d1, d2};
+
+ return i2c_master_send(client, tx_buf, 2);
+}
+
+static int max1363_set_scan_mode(struct max1363_state *st)
+{
+ st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+ | MAX1363_SCAN_MASK
+ | MAX1363_SE_DE_MASK);
+ st->configbyte |= st->current_mode->conf;
+
+ return max1363_write_basic_config(st->client,
+ st->setupbyte,
+ st->configbyte);
+}
+
+static int max1363_read_single_chan(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ long m)
+{
+ int ret = 0;
+ s32 data;
+ char rxbuf[2];
+ struct max1363_state *st = iio_priv(indio_dev);
+ struct i2c_client *client = st->client;
+
+ mutex_lock(&indio_dev->mlock);
+
+ /* Check to see if current scan mode is correct */
+ if (st->current_mode != &max1363_mode_table[chan->address]) {
+ /* Update scan mode if needed */
+ st->current_mode = &max1363_mode_table[chan->address];
+ ret = max1363_set_scan_mode(st);
+ if (ret < 0)
+ goto error_ret;
+ }
+ if (st->chip_info->bits != 8) {
+ /* Get reading */
+ data = i2c_master_recv(client, rxbuf, 2);
+ if (data < 0) {
+ ret = data;
+ goto error_ret;
+ }
+ data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8;
+ } else {
+ /* Get reading */
+ data = i2c_master_recv(client, rxbuf, 1);
+ if (data < 0) {
+ ret = data;
+ goto error_ret;
+ }
+ data = rxbuf[0];
+ }
+
+ *val = data;
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+
+}
+
+static int max1363_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ int ret;
+ switch (m) {
+ case 0:
+ ret = max1363_read_single_chan(indio_dev, chan, val, m);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+ if ((1 << (st->chip_info->bits + 1)) >
+ st->chip_info->int_vref_mv) {
+ *val = 0;
+ *val2 = 500000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ } else {
+ *val = (st->chip_info->int_vref_mv)
+ >> st->chip_info->bits;
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Applies to max1363 */
+static const enum max1363_modes max1363_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+};
+
+/*
+ * Bits and scan inded not currently used but left here to
+ * reduce churn during merge.
+ */
+#define MAX1363_CHAN_U(num, addr, scan_index, bits) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .address = addr, \
+ .info_mask = MAX1363_INFO_MASK \
+ } \
+
+/* bipolar channel */
+#define MAX1363_CHAN_B(num, num2, addr, scan_index, bits) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .indexed = 1, \
+ .channel = num, \
+ .channel2 = num2, \
+ .address = addr, \
+ .info_mask = MAX1363_INFO_MASK, \
+ }
+#define MAX1363_INFO_MASK (1 << IIO_CHAN_INFO_SCALE_SHARED)
+
+#define MAX1363_CHAN_UNIPOLAR(index)
+#define MAX1363_4X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits), \
+ MAX1363_CHAN_U(1, _s1, 1, bits), \
+ MAX1363_CHAN_U(2, _s2, 2, bits), \
+ MAX1363_CHAN_U(3, _s3, 3, bits), \
+ MAX1363_CHAN_B(0, 1, d0m1, 4, bits), \
+ MAX1363_CHAN_B(2, 3, d2m3, 5, bits), \
+ MAX1363_CHAN_B(1, 0, d1m0, 6, bits), \
+ MAX1363_CHAN_B(3, 2, d3m2, 7, bits), \
+ }
+
+static struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8);
+static struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10);
+static struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12);
+
+/* Appies to max1236, max1237 */
+static const enum max1363_modes max1236_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+ s2to3,
+};
+
+/* Applies to max1238, max1239 */
+static const enum max1363_modes max1238_mode_list[] = {
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+ s0to7, s0to8, s0to9, s0to10, s0to11,
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+ s6to7, s6to8, s6to9, s6to10, s6to11,
+ d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10,
+};
+
+#define MAX1363_12X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits), \
+ MAX1363_CHAN_U(1, _s1, 1, bits), \
+ MAX1363_CHAN_U(2, _s2, 2, bits), \
+ MAX1363_CHAN_U(3, _s3, 3, bits), \
+ MAX1363_CHAN_U(4, _s4, 4, bits), \
+ MAX1363_CHAN_U(5, _s5, 5, bits), \
+ MAX1363_CHAN_U(6, _s6, 6, bits), \
+ MAX1363_CHAN_U(7, _s7, 7, bits), \
+ MAX1363_CHAN_U(8, _s8, 8, bits), \
+ MAX1363_CHAN_U(9, _s9, 9, bits), \
+ MAX1363_CHAN_U(10, _s10, 10, bits), \
+ MAX1363_CHAN_U(11, _s11, 11, bits), \
+ MAX1363_CHAN_B(0, 1, d0m1, 12, bits), \
+ MAX1363_CHAN_B(2, 3, d2m3, 13, bits), \
+ MAX1363_CHAN_B(4, 5, d4m5, 14, bits), \
+ MAX1363_CHAN_B(6, 7, d6m7, 15, bits), \
+ MAX1363_CHAN_B(8, 9, d8m9, 16, bits), \
+ MAX1363_CHAN_B(10, 11, d10m11, 17, bits), \
+ MAX1363_CHAN_B(1, 0, d1m0, 18, bits), \
+ MAX1363_CHAN_B(3, 2, d3m2, 19, bits), \
+ MAX1363_CHAN_B(5, 4, d5m4, 20, bits), \
+ MAX1363_CHAN_B(7, 6, d7m6, 21, bits), \
+ MAX1363_CHAN_B(9, 8, d9m8, 22, bits), \
+ MAX1363_CHAN_B(11, 10, d11m10, 23, bits), \
+ }
+
+static struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
+static struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
+static struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12);
+
+static const enum max1363_modes max11607_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ s2to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+};
+
+static const enum max1363_modes max11608_mode_list[] = {
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7,
+ s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7,
+ s6to7,
+ d0m1, d2m3, d4m5, d6m7,
+ d1m0, d3m2, d5m4, d7m6,
+ d0m1to2m3, d0m1to4m5, d0m1to6m7,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6,
+};
+
+#define MAX1363_8X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits), \
+ MAX1363_CHAN_U(1, _s1, 1, bits), \
+ MAX1363_CHAN_U(2, _s2, 2, bits), \
+ MAX1363_CHAN_U(3, _s3, 3, bits), \
+ MAX1363_CHAN_U(4, _s4, 4, bits), \
+ MAX1363_CHAN_U(5, _s5, 5, bits), \
+ MAX1363_CHAN_U(6, _s6, 6, bits), \
+ MAX1363_CHAN_U(7, _s7, 7, bits), \
+ MAX1363_CHAN_B(0, 1, d0m1, 8, bits), \
+ MAX1363_CHAN_B(2, 3, d2m3, 9, bits), \
+ MAX1363_CHAN_B(4, 5, d4m5, 10, bits), \
+ MAX1363_CHAN_B(6, 7, d6m7, 11, bits), \
+ MAX1363_CHAN_B(1, 0, d1m0, 12, bits), \
+ MAX1363_CHAN_B(3, 2, d3m2, 13, bits), \
+ MAX1363_CHAN_B(5, 4, d5m4, 14, bits), \
+ MAX1363_CHAN_B(7, 6, d7m6, 15, bits), \
+}
+static struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
+static struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10);
+static struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12);
+
+static const enum max1363_modes max11644_mode_list[] = {
+ _s0, _s1, s0to1, d0m1, d1m0,
+};
+
+#define MAX1363_2X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits), \
+ MAX1363_CHAN_U(1, _s1, 1, bits), \
+ MAX1363_CHAN_B(0, 1, d0m1, 2, bits), \
+ MAX1363_CHAN_B(1, 0, d1m0, 3, bits), \
+ }
+
+static struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10);
+static struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12);
+
+enum { max1361,
+ max1362,
+ max1363,
+ max1364,
+ max1036,
+ max1037,
+ max1038,
+ max1039,
+ max1136,
+ max1137,
+ max1138,
+ max1139,
+ max1236,
+ max1237,
+ max1238,
+ max1239,
+ max11600,
+ max11601,
+ max11602,
+ max11603,
+ max11604,
+ max11605,
+ max11606,
+ max11607,
+ max11608,
+ max11609,
+ max11610,
+ max11611,
+ max11612,
+ max11613,
+ max11614,
+ max11615,
+ max11616,
+ max11617,
+ max11644,
+ max11645,
+ max11646,
+ max11647
+};
+
+static const struct iio_info max1238_info = {
+ .read_raw = &max1363_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info max1363_info = {
+ .read_raw = &max1363_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+/* max1363 and max1368 tested - rest from data sheet */
+static const struct max1363_chip_info max1363_chip_info_tbl[] = {
+ [max1361] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ .info = &max1363_info,
+ },
+ [max1362] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ .info = &max1363_info,
+ },
+ [max1363] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ .info = &max1363_info,
+ },
+ [max1364] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ .info = &max1363_info,
+ },
+ [max1036] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max1037] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max1038] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max1039] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max1136] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max1137] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max1138] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max1139] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max1236] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max1237] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max1238] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max1239] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11600] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max11601] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max11602] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
+ },
+ [max11603] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
+ },
+ [max11604] = {
+ .bits = 8,
+ .int_vref_mv = 4098,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11605] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11606] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max11607] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max11608] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
+ },
+ [max11609] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
+ },
+ [max11610] = {
+ .bits = 10,
+ .int_vref_mv = 4098,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11611] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11612] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max11613] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max11614] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
+ },
+ [max11615] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
+ },
+ [max11616] = {
+ .bits = 12,
+ .int_vref_mv = 4098,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11617] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11644] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11644_channels,
+ .num_channels = ARRAY_SIZE(max11644_channels),
+ },
+ [max11645] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11644_channels,
+ .num_channels = ARRAY_SIZE(max11644_channels),
+ },
+ [max11646] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11646_channels,
+ .num_channels = ARRAY_SIZE(max11646_channels),
+ },
+ [max11647] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11646_channels,
+ .num_channels = ARRAY_SIZE(max11646_channels),
+ },
+};
+
+static int max1363_initial_setup(struct max1363_state *st)
+{
+ st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD
+ | MAX1363_SETUP_POWER_UP_INT_REF
+ | MAX1363_SETUP_INT_CLOCK
+ | MAX1363_SETUP_UNIPOLAR
+ | MAX1363_SETUP_NORESET;
+
+ /* Set scan mode writes the config anyway so wait until then*/
+ st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte);
+ st->current_mode = &max1363_mode_table[st->chip_info->default_mode];
+ st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte);
+
+ return max1363_set_scan_mode(st);
+}
+
+static int __devinit max1363_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret, i, regdone = 0;
+ struct max1363_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *reg;
+
+ reg = regulator_get(&client->dev, "vcc");
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ goto error_out;
+ }
+
+ ret = regulator_enable(reg);
+ if (ret)
+ goto error_put_reg;
+
+ indio_dev = iio_device_allocate(sizeof(struct max1363_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_disable_reg;
+ }
+ st = iio_priv(indio_dev);
+ st->reg = reg;
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ st->chip_info = &max1363_chip_info_tbl[id->driver_data];
+ st->client = client;
+
+ indio_dev->available_scan_masks
+ = kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*
+ (st->chip_info->num_modes + 1), GFP_KERNEL);
+ if (!indio_dev->available_scan_masks) {
+ ret = -ENOMEM;
+ goto error_free_device;
+ }
+
+ for (i = 0; i < st->chip_info->num_modes; i++)
+ bitmap_copy(indio_dev->available_scan_masks +
+ BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i,
+ max1363_mode_table[st->chip_info->mode_list[i]]
+ .modemask, MAX1363_MAX_CHANNELS);
+ /* Estabilish that the iio_dev is a child of the i2c device */
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+
+ indio_dev->info = st->chip_info->info;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ ret = max1363_initial_setup(st);
+ if (ret < 0)
+ goto error_free_available_scan_masks;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_available_scan_masks;
+ regdone = 1;
+
+ return 0;
+
+error_free_available_scan_masks:
+ kfree(indio_dev->available_scan_masks);
+error_free_device:
+ if (!regdone)
+ iio_device_free(indio_dev);
+ else
+ iio_device_unregister(indio_dev);
+error_disable_reg:
+ regulator_disable(reg);
+error_put_reg:
+ regulator_put(reg);
+error_out:
+ return ret;
+}
+
+static int max1363_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct max1363_state *st = iio_priv(indio_dev);
+ struct regulator *reg = st->reg;
+
+ kfree(indio_dev->available_scan_masks);
+ if (!IS_ERR(reg)) {
+ regulator_disable(reg);
+ regulator_put(reg);
+ }
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id max1363_id[] = {
+ { "max1361", max1361 },
+ { "max1362", max1362 },
+ { "max1363", max1363 },
+ { "max1364", max1364 },
+ { "max1036", max1036 },
+ { "max1037", max1037 },
+ { "max1038", max1038 },
+ { "max1039", max1039 },
+ { "max1136", max1136 },
+ { "max1137", max1137 },
+ { "max1138", max1138 },
+ { "max1139", max1139 },
+ { "max1236", max1236 },
+ { "max1237", max1237 },
+ { "max1238", max1238 },
+ { "max1239", max1239 },
+ { "max11600", max11600 },
+ { "max11601", max11601 },
+ { "max11602", max11602 },
+ { "max11603", max11603 },
+ { "max11604", max11604 },
+ { "max11605", max11605 },
+ { "max11606", max11606 },
+ { "max11607", max11607 },
+ { "max11608", max11608 },
+ { "max11609", max11609 },
+ { "max11610", max11610 },
+ { "max11611", max11611 },
+ { "max11612", max11612 },
+ { "max11613", max11613 },
+ { "max11614", max11614 },
+ { "max11615", max11615 },
+ { "max11616", max11616 },
+ { "max11617", max11617 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max1363_id);
+
+static struct i2c_driver max1363_driver = {
+ .driver = {
+ .name = "max1363",
+ },
+ .probe = max1363_probe,
+ .remove = max1363_remove,
+ .id_table = max1363_id,
+};
+
+static __init int max1363_init(void)
+{
+ return i2c_add_driver(&max1363_driver);
+}
+module_init(max1363_init);
+
+static __exit void max1363_exit(void)
+{
+ i2c_del_driver(&max1363_driver);
+}
+module_exit(max1363_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Maxim 1363 ADC");
+MODULE_LICENSE("GPL v2");
+
--
1.7.3.4
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 3/6] IIO:ADC:ad799x initial import.
2011-09-27 14:29 [RFC PATCH 0/6 V2] IIO: Out of staging step 1: The core Jonathan Cameron
2011-09-27 14:29 ` [PATCH 1/6] IIO: Core sysfs only support Jonathan Cameron
2011-09-27 14:29 ` [PATCH 2/6] IIO:ADC: max1363 initial import Jonathan Cameron
@ 2011-09-27 14:29 ` Jonathan Cameron
2011-09-28 7:10 ` Michael Hennerich
2011-09-27 14:29 ` [PATCH 4/6] IIO:light:tsl2563 initial move out of staging Jonathan Cameron
` (2 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Jonathan Cameron @ 2011-09-27 14:29 UTC (permalink / raw)
To: linux-iio; +Cc: dtor, khali, guenter.roeck, Michael Hennerich, Jonathan Cameron
From: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 3 +
drivers/iio/adc/ad799x_core.c | 722 +++++++++++++++++++++++++++++++++++++++++
include/linux/iio/ad799x.h | 12 +
4 files changed, 748 insertions(+), 0 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 0673d78..3d97b21 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -3,6 +3,17 @@
#
menu "Analog to digital convertors"
+config IIO_AD799X
+ tristate "Analog Devices AD799x ADC driver"
+ depends on I2C
+ select IIO_TRIGGER if IIO_BUFFER
+ select AD799X_RING_BUFFER
+ help
+ Say yes here to build support for Analog Devices:
+ ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998
+ i2c analog to digital convertors (ADC). Provides direct access
+ via sysfs.
+
config IIO_MAX1363
tristate "Maxim max1363 ADC driver"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8d6a7f9..c197334 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -2,5 +2,8 @@
# Makefile for IIO ADCs
#
+iio_ad799x-y := ad799x_core.o
+obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
+
iio_max1363-y := max1363_core.o
obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
diff --git a/drivers/iio/adc/ad799x_core.c b/drivers/iio/adc/ad799x_core.c
new file mode 100644
index 0000000..0833ee9
--- /dev/null
+++ b/drivers/iio/adc/ad799x_core.c
@@ -0,0 +1,722 @@
+/*
+ * iio/adc/ad799x.c
+ * Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc.
+ *
+ * based on iio/adc/max1363
+ * Copyright (C) 2008-2010 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ad799x.c
+ *
+ * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
+ * ad7998 and similar chips.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/ad799x.h>
+
+#define AD799X_CHANNEL_SHIFT 4
+/*
+ * AD7991, AD7995 and AD7999 defines
+ */
+
+#define AD7991_REF_SEL 0x08
+#define AD7991_FLTR 0x04
+#define AD7991_BIT_TRIAL_DELAY 0x02
+#define AD7991_SAMPLE_DELAY 0x01
+
+/*
+ * AD7992, AD7993, AD7994, AD7997 and AD7998 defines
+ */
+
+#define AD7998_FLTR 0x08
+#define AD7998_ALERT_EN 0x04
+#define AD7998_BUSY_ALERT 0x02
+#define AD7998_BUSY_ALERT_POL 0x01
+
+#define AD7998_CONV_RES_REG 0x0
+#define AD7998_ALERT_STAT_REG 0x1
+#define AD7998_CONF_REG 0x2
+#define AD7998_CYCLE_TMR_REG 0x3
+#define AD7998_DATALOW_CH1_REG 0x4
+#define AD7998_DATAHIGH_CH1_REG 0x5
+#define AD7998_HYST_CH1_REG 0x6
+#define AD7998_DATALOW_CH2_REG 0x7
+#define AD7998_DATAHIGH_CH2_REG 0x8
+#define AD7998_HYST_CH2_REG 0x9
+#define AD7998_DATALOW_CH3_REG 0xA
+#define AD7998_DATAHIGH_CH3_REG 0xB
+#define AD7998_HYST_CH3_REG 0xC
+#define AD7998_DATALOW_CH4_REG 0xD
+#define AD7998_DATAHIGH_CH4_REG 0xE
+#define AD7998_HYST_CH4_REG 0xF
+
+#define AD7998_CYC_MASK 0x7
+#define AD7998_CYC_DIS 0x0
+#define AD7998_CYC_TCONF_32 0x1
+#define AD7998_CYC_TCONF_64 0x2
+#define AD7998_CYC_TCONF_128 0x3
+#define AD7998_CYC_TCONF_256 0x4
+#define AD7998_CYC_TCONF_512 0x5
+#define AD7998_CYC_TCONF_1024 0x6
+#define AD7998_CYC_TCONF_2048 0x7
+
+#define AD7998_ALERT_STAT_CLEAR 0xFF
+
+/*
+ * AD7997 and AD7997 defines
+ */
+
+#define AD7997_8_READ_SINGLE 0x80
+#define AD7997_8_READ_SEQUENCE 0x70
+/* TODO: move this into a common header */
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+enum {
+ ad7991,
+ ad7995,
+ ad7999,
+ ad7992,
+ ad7993,
+ ad7994,
+ ad7997,
+ ad7998
+};
+
+struct ad799x_state;
+
+/**
+ * struct ad799x_chip_info - chip specifc information
+ * @channel: channel specification
+ * @num_channels: number of channels
+ * @int_vref_mv: the internal reference voltage
+ */
+struct ad799x_chip_info {
+ struct iio_chan_spec channel[9];
+ int num_channels;
+ u16 int_vref_mv;
+};
+
+struct ad799x_state {
+ struct i2c_client *client;
+ const struct ad799x_chip_info *chip_info;
+ struct regulator *reg;
+ u16 int_vref_mv;
+ unsigned id;
+ char *name;
+ u16 config;
+};
+
+/*
+ * ad799x register access by I2C
+ */
+static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data)
+{
+ struct i2c_client *client = st->client;
+ int ret = 0;
+
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C read error\n");
+ return ret;
+ }
+
+ *data = swab16((u16)ret);
+
+ return 0;
+}
+
+static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
+{
+ u16 rxbuf;
+ u8 cmd;
+ int ret;
+
+ switch (st->id) {
+ case ad7991:
+ case ad7995:
+ case ad7999:
+ cmd = st->config | ((1 << ch) << AD799X_CHANNEL_SHIFT);
+ break;
+ case ad7992:
+ case ad7993:
+ case ad7994:
+ cmd = (1 << ch) << AD799X_CHANNEL_SHIFT;
+ break;
+ case ad7997:
+ case ad7998:
+ cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = ad799x_i2c_read16(st, cmd, &rxbuf);
+ if (ret < 0)
+ return ret;
+
+ return rxbuf;
+}
+
+static int ad799x_read_raw(struct iio_dev *dev_info,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad799x_state *st = iio_priv(dev_info);
+ unsigned int scale_uv;
+
+ switch (m) {
+ case 0:
+ mutex_lock(&dev_info->mlock);
+ ret = ad799x_scan_direct(st, chan->channel);
+ mutex_unlock(&dev_info->mlock);
+
+ if (ret < 0)
+ return ret;
+ *val = (ret >> chan->scan_type.shift) &
+ RES_MASK(chan->scan_type.realbits);
+ return IIO_VAL_INT;
+ case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+ scale_uv = (st->int_vref_mv * 1000) >> chan->scan_type.realbits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad799X_info = {
+ .read_raw = &ad799x_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+
+static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
+ [ad7991] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ },
+ .num_channels = 5,
+ .int_vref_mv = 4096,
+ },
+ [ad7995] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ },
+ .num_channels = 5,
+ .int_vref_mv = 1024,
+ },
+ [ad7999] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 8,
+ .shift = 4,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 8,
+ .shift = 4,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 8,
+ .shift = 4,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 8,
+ .shift = 4,
+ },
+ },
+ },
+ .num_channels = 5,
+ .int_vref_mv = 1024,
+ },
+ [ad7992] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ },
+ .num_channels = 3,
+ .int_vref_mv = 4096,
+ },
+ [ad7993] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ },
+ .num_channels = 5,
+ .int_vref_mv = 1024,
+ },
+ [ad7994] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ },
+ .num_channels = 5,
+ .int_vref_mv = 4096,
+ },
+ [ad7997] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [4] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 4,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [5] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 5,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [6] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 6,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ [7] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 7,
+ .scan_type = {
+ .realbits = 10,
+ .shift = 2,
+ },
+ },
+ },
+ .num_channels = 9,
+ .int_vref_mv = 1024,
+ },
+ [ad7998] = {
+ .channel = {
+ [0] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [1] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [2] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [3] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 3,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [4] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 4,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [5] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 5,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [6] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 6,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ [7] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 7,
+ .scan_type = {
+ .realbits = 12,
+ .shift = 0,
+ },
+ },
+ },
+ .num_channels = 9,
+ .int_vref_mv = 4096,
+ },
+};
+
+static int __devinit ad799x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct ad799x_platform_data *pdata = client->dev.platform_data;
+ struct ad799x_state *st;
+ struct iio_dev *indio_dev = iio_device_allocate(sizeof(*st));
+
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ st->id = id->driver_data;
+ st->chip_info = &ad799x_chip_info_tbl[st->id];
+
+ /* TODO: Add pdata options for filtering and bit delay */
+
+ if (pdata)
+ st->int_vref_mv = pdata->vref_mv;
+ else
+ st->int_vref_mv = st->chip_info->int_vref_mv;
+
+ st->reg = regulator_get(&client->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+ }
+ st->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->info = &ad799X_info;
+
+ indio_dev->channels = st->chip_info->channel;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+error_put_reg:
+ if (!IS_ERR(st->reg))
+ regulator_put(st->reg);
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static __devexit int ad799x_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ if (!IS_ERR(st->reg)) {
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+ }
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad799x_id[] = {
+ { "ad7991", ad7991 },
+ { "ad7995", ad7995 },
+ { "ad7999", ad7999 },
+ { "ad7992", ad7992 },
+ { "ad7993", ad7993 },
+ { "ad7994", ad7994 },
+ { "ad7997", ad7997 },
+ { "ad7998", ad7998 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad799x_id);
+
+static struct i2c_driver ad799x_driver = {
+ .driver = {
+ .name = "ad799x",
+ },
+ .probe = ad799x_probe,
+ .remove = __devexit_p(ad799x_remove),
+ .id_table = ad799x_id,
+};
+
+static __init int ad799x_init(void)
+{
+ return i2c_add_driver(&ad799x_driver);
+}
+
+static __exit void ad799x_exit(void)
+{
+ i2c_del_driver(&ad799x_driver);
+}
+
+MODULE_AUTHOR("Michael Hennerich <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/include/linux/iio/ad799x.h b/include/linux/iio/ad799x.h
new file mode 100644
index 0000000..38517be
--- /dev/null
+++ b/include/linux/iio/ad799x.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
+ * Copyright (C) 2008-2010 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+struct ad799x_platform_data {
+ u16 vref_mv;
+};
--
1.7.3.4
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH 3/6] IIO:ADC:ad799x initial import.
2011-09-27 14:29 ` [PATCH 3/6] IIO:ADC:ad799x " Jonathan Cameron
@ 2011-09-28 7:10 ` Michael Hennerich
0 siblings, 0 replies; 8+ messages in thread
From: Michael Hennerich @ 2011-09-28 7:10 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-iio@vger.kernel.org, dtor@mail.ru, khali@linux-fr.org,
guenter.roeck@ericsson.com
On 09/27/2011 04:29 PM, Jonathan Cameron wrote:
> From: Michael Hennerich<michael.hennerich@analog.com>
>
> Signed-off-by: Jonathan Cameron<jic23@cam.ac.uk>
Signed-off-by: Michael Hennerich<michael.hennerich@analog.com>
> ---
> drivers/iio/adc/Kconfig | 11 +
> drivers/iio/adc/Makefile | 3 +
> drivers/iio/adc/ad799x_core.c | 722 +++++++++++++++++++++++++++++++++++++++++
> include/linux/iio/ad799x.h | 12 +
> 4 files changed, 748 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 0673d78..3d97b21 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -3,6 +3,17 @@
> #
> menu "Analog to digital convertors"
>
> +config IIO_AD799X
> + tristate "Analog Devices AD799x ADC driver"
> + depends on I2C
> + select IIO_TRIGGER if IIO_BUFFER
> + select AD799X_RING_BUFFER
> + help
> + Say yes here to build support for Analog Devices:
> + ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998
> + i2c analog to digital convertors (ADC). Provides direct access
> + via sysfs.
> +
> config IIO_MAX1363
> tristate "Maxim max1363 ADC driver"
> depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 8d6a7f9..c197334 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -2,5 +2,8 @@
> # Makefile for IIO ADCs
> #
>
> +iio_ad799x-y := ad799x_core.o
> +obj-$(CONFIG_IIO_AD799X) += iio_ad799x.o
> +
> iio_max1363-y := max1363_core.o
> obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
> \ No newline at end of file
> diff --git a/drivers/iio/adc/ad799x_core.c b/drivers/iio/adc/ad799x_core.c
> new file mode 100644
> index 0000000..0833ee9
> --- /dev/null
> +++ b/drivers/iio/adc/ad799x_core.c
> @@ -0,0 +1,722 @@
> +/*
> + * iio/adc/ad799x.c
> + * Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc.
> + *
> + * based on iio/adc/max1363
> + * Copyright (C) 2008-2010 Jonathan Cameron
> + *
> + * based on linux/drivers/i2c/chips/max123x
> + * Copyright (C) 2002-2004 Stefan Eletzhofer
> + *
> + * based on linux/drivers/acron/char/pcf8583.c
> + * Copyright (C) 2000 Russell King
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * ad799x.c
> + *
> + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
> + * ad7998 and similar chips.
> + *
> + */
> +
> +#include<linux/interrupt.h>
> +#include<linux/device.h>
> +#include<linux/kernel.h>
> +#include<linux/sysfs.h>
> +#include<linux/i2c.h>
> +#include<linux/regulator/consumer.h>
> +#include<linux/slab.h>
> +#include<linux/types.h>
> +#include<linux/err.h>
> +#include<linux/module.h>
> +
> +#include<linux/iio/iio.h>
> +#include<linux/iio/sysfs.h>
> +#include<linux/iio/ad799x.h>
> +
> +#define AD799X_CHANNEL_SHIFT 4
> +/*
> + * AD7991, AD7995 and AD7999 defines
> + */
> +
> +#define AD7991_REF_SEL 0x08
> +#define AD7991_FLTR 0x04
> +#define AD7991_BIT_TRIAL_DELAY 0x02
> +#define AD7991_SAMPLE_DELAY 0x01
> +
> +/*
> + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines
> + */
> +
> +#define AD7998_FLTR 0x08
> +#define AD7998_ALERT_EN 0x04
> +#define AD7998_BUSY_ALERT 0x02
> +#define AD7998_BUSY_ALERT_POL 0x01
> +
> +#define AD7998_CONV_RES_REG 0x0
> +#define AD7998_ALERT_STAT_REG 0x1
> +#define AD7998_CONF_REG 0x2
> +#define AD7998_CYCLE_TMR_REG 0x3
> +#define AD7998_DATALOW_CH1_REG 0x4
> +#define AD7998_DATAHIGH_CH1_REG 0x5
> +#define AD7998_HYST_CH1_REG 0x6
> +#define AD7998_DATALOW_CH2_REG 0x7
> +#define AD7998_DATAHIGH_CH2_REG 0x8
> +#define AD7998_HYST_CH2_REG 0x9
> +#define AD7998_DATALOW_CH3_REG 0xA
> +#define AD7998_DATAHIGH_CH3_REG 0xB
> +#define AD7998_HYST_CH3_REG 0xC
> +#define AD7998_DATALOW_CH4_REG 0xD
> +#define AD7998_DATAHIGH_CH4_REG 0xE
> +#define AD7998_HYST_CH4_REG 0xF
> +
> +#define AD7998_CYC_MASK 0x7
> +#define AD7998_CYC_DIS 0x0
> +#define AD7998_CYC_TCONF_32 0x1
> +#define AD7998_CYC_TCONF_64 0x2
> +#define AD7998_CYC_TCONF_128 0x3
> +#define AD7998_CYC_TCONF_256 0x4
> +#define AD7998_CYC_TCONF_512 0x5
> +#define AD7998_CYC_TCONF_1024 0x6
> +#define AD7998_CYC_TCONF_2048 0x7
> +
> +#define AD7998_ALERT_STAT_CLEAR 0xFF
> +
> +/*
> + * AD7997 and AD7997 defines
> + */
> +
> +#define AD7997_8_READ_SINGLE 0x80
> +#define AD7997_8_READ_SEQUENCE 0x70
> +/* TODO: move this into a common header */
> +#define RES_MASK(bits) ((1<< (bits)) - 1)
> +
> +enum {
> + ad7991,
> + ad7995,
> + ad7999,
> + ad7992,
> + ad7993,
> + ad7994,
> + ad7997,
> + ad7998
> +};
> +
> +struct ad799x_state;
> +
> +/**
> + * struct ad799x_chip_info - chip specifc information
> + * @channel: channel specification
> + * @num_channels: number of channels
> + * @int_vref_mv: the internal reference voltage
> + */
> +struct ad799x_chip_info {
> + struct iio_chan_spec channel[9];
> + int num_channels;
> + u16 int_vref_mv;
> +};
> +
> +struct ad799x_state {
> + struct i2c_client *client;
> + const struct ad799x_chip_info *chip_info;
> + struct regulator *reg;
> + u16 int_vref_mv;
> + unsigned id;
> + char *name;
> + u16 config;
> +};
> +
> +/*
> + * ad799x register access by I2C
> + */
> +static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data)
> +{
> + struct i2c_client *client = st->client;
> + int ret = 0;
> +
> + ret = i2c_smbus_read_word_data(client, reg);
> + if (ret< 0) {
> + dev_err(&client->dev, "I2C read error\n");
> + return ret;
> + }
> +
> + *data = swab16((u16)ret);
> +
> + return 0;
> +}
> +
> +static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
> +{
> + u16 rxbuf;
> + u8 cmd;
> + int ret;
> +
> + switch (st->id) {
> + case ad7991:
> + case ad7995:
> + case ad7999:
> + cmd = st->config | ((1<< ch)<< AD799X_CHANNEL_SHIFT);
> + break;
> + case ad7992:
> + case ad7993:
> + case ad7994:
> + cmd = (1<< ch)<< AD799X_CHANNEL_SHIFT;
> + break;
> + case ad7997:
> + case ad7998:
> + cmd = (ch<< AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + ret = ad799x_i2c_read16(st, cmd,&rxbuf);
> + if (ret< 0)
> + return ret;
> +
> + return rxbuf;
> +}
> +
> +static int ad799x_read_raw(struct iio_dev *dev_info,
> + struct iio_chan_spec const *chan,
> + int *val,
> + int *val2,
> + long m)
> +{
> + int ret;
> + struct ad799x_state *st = iio_priv(dev_info);
> + unsigned int scale_uv;
> +
> + switch (m) {
> + case 0:
> + mutex_lock(&dev_info->mlock);
> + ret = ad799x_scan_direct(st, chan->channel);
> + mutex_unlock(&dev_info->mlock);
> +
> + if (ret< 0)
> + return ret;
> + *val = (ret>> chan->scan_type.shift)&
> + RES_MASK(chan->scan_type.realbits);
> + return IIO_VAL_INT;
> + case (1<< IIO_CHAN_INFO_SCALE_SHARED):
> + scale_uv = (st->int_vref_mv * 1000)>> chan->scan_type.realbits;
> + *val = scale_uv / 1000;
> + *val2 = (scale_uv % 1000) * 1000;
> + return IIO_VAL_INT_PLUS_MICRO;
> + }
> + return -EINVAL;
> +}
> +
> +static const struct iio_info ad799X_info = {
> + .read_raw =&ad799x_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +
> +static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
> + [ad7991] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + },
> + .num_channels = 5,
> + .int_vref_mv = 4096,
> + },
> + [ad7995] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + },
> + .num_channels = 5,
> + .int_vref_mv = 1024,
> + },
> + [ad7999] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 8,
> + .shift = 4,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 8,
> + .shift = 4,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 8,
> + .shift = 4,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 8,
> + .shift = 4,
> + },
> + },
> + },
> + .num_channels = 5,
> + .int_vref_mv = 1024,
> + },
> + [ad7992] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + },
> + .num_channels = 3,
> + .int_vref_mv = 4096,
> + },
> + [ad7993] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + },
> + .num_channels = 5,
> + .int_vref_mv = 1024,
> + },
> + [ad7994] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + },
> + .num_channels = 5,
> + .int_vref_mv = 4096,
> + },
> + [ad7997] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [4] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 4,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [5] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 5,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [6] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 6,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + [7] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 7,
> + .scan_type = {
> + .realbits = 10,
> + .shift = 2,
> + },
> + },
> + },
> + .num_channels = 9,
> + .int_vref_mv = 1024,
> + },
> + [ad7998] = {
> + .channel = {
> + [0] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 0,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [1] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 1,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [2] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 2,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [3] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 3,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [4] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 4,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [5] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 5,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [6] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 6,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + [7] = {
> + .type = IIO_VOLTAGE,
> + .indexed = 1,
> + .channel = 7,
> + .scan_type = {
> + .realbits = 12,
> + .shift = 0,
> + },
> + },
> + },
> + .num_channels = 9,
> + .int_vref_mv = 4096,
> + },
> +};
> +
> +static int __devinit ad799x_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + int ret;
> + struct ad799x_platform_data *pdata = client->dev.platform_data;
> + struct ad799x_state *st;
> + struct iio_dev *indio_dev = iio_device_allocate(sizeof(*st));
> +
> + if (indio_dev == NULL)
> + return -ENOMEM;
> +
> + st = iio_priv(indio_dev);
> + /* this is only used for device removal purposes */
> + i2c_set_clientdata(client, indio_dev);
> +
> + st->id = id->driver_data;
> + st->chip_info =&ad799x_chip_info_tbl[st->id];
> +
> + /* TODO: Add pdata options for filtering and bit delay */
> +
> + if (pdata)
> + st->int_vref_mv = pdata->vref_mv;
> + else
> + st->int_vref_mv = st->chip_info->int_vref_mv;
> +
> + st->reg = regulator_get(&client->dev, "vcc");
> + if (!IS_ERR(st->reg)) {
> + ret = regulator_enable(st->reg);
> + if (ret)
> + goto error_put_reg;
> + }
> + st->client = client;
> +
> + indio_dev->dev.parent =&client->dev;
> + indio_dev->name = id->name;
> + indio_dev->info =&ad799X_info;
> +
> + indio_dev->channels = st->chip_info->channel;
> + indio_dev->num_channels = st->chip_info->num_channels;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret)
> + goto error_disable_reg;
> +
> + return 0;
> +
> +error_disable_reg:
> + if (!IS_ERR(st->reg))
> + regulator_disable(st->reg);
> +error_put_reg:
> + if (!IS_ERR(st->reg))
> + regulator_put(st->reg);
> + iio_device_free(indio_dev);
> +
> + return ret;
> +}
> +
> +static __devexit int ad799x_remove(struct i2c_client *client)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(client);
> + struct ad799x_state *st = iio_priv(indio_dev);
> +
> + if (!IS_ERR(st->reg)) {
> + regulator_disable(st->reg);
> + regulator_put(st->reg);
> + }
> + iio_device_unregister(indio_dev);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id ad799x_id[] = {
> + { "ad7991", ad7991 },
> + { "ad7995", ad7995 },
> + { "ad7999", ad7999 },
> + { "ad7992", ad7992 },
> + { "ad7993", ad7993 },
> + { "ad7994", ad7994 },
> + { "ad7997", ad7997 },
> + { "ad7998", ad7998 },
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ad799x_id);
> +
> +static struct i2c_driver ad799x_driver = {
> + .driver = {
> + .name = "ad799x",
> + },
> + .probe = ad799x_probe,
> + .remove = __devexit_p(ad799x_remove),
> + .id_table = ad799x_id,
> +};
> +
> +static __init int ad799x_init(void)
> +{
> + return i2c_add_driver(&ad799x_driver);
> +}
> +
> +static __exit void ad799x_exit(void)
> +{
> + i2c_del_driver(&ad799x_driver);
> +}
> +
> +MODULE_AUTHOR("Michael Hennerich<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/include/linux/iio/ad799x.h b/include/linux/iio/ad799x.h
> new file mode 100644
> index 0000000..38517be
> --- /dev/null
> +++ b/include/linux/iio/ad799x.h
> @@ -0,0 +1,12 @@
> +/*
> + * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
> + * Copyright (C) 2008-2010 Jonathan Cameron
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +struct ad799x_platform_data {
> + u16 vref_mv;
> +};
> --
> 1.7.3.4
>
>
--
Greetings,
Michael
--
Analog Devices GmbH Wilhelm-Wagenfeld-Str. 6 80807 Muenchen
Sitz der Gesellschaft: Muenchen; Registergericht: Muenchen HRB 40368;
Geschaeftsfuehrer:Dr.Carsten Suckrow, Thomas Wessel, William A. Martin,
Margaret Seif
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/6] IIO:light:tsl2563 initial move out of staging.
2011-09-27 14:29 [RFC PATCH 0/6 V2] IIO: Out of staging step 1: The core Jonathan Cameron
` (2 preceding siblings ...)
2011-09-27 14:29 ` [PATCH 3/6] IIO:ADC:ad799x " Jonathan Cameron
@ 2011-09-27 14:29 ` Jonathan Cameron
2011-09-27 14:29 ` [PATCH 5/6] IIO:imu:adis16400 partial move from staging Jonathan Cameron
2011-09-27 14:29 ` [PATCH 6/6] IIO: ABI documetation Jonathan Cameron
5 siblings, 0 replies; 8+ messages in thread
From: Jonathan Cameron @ 2011-09-27 14:29 UTC (permalink / raw)
To: linux-iio; +Cc: dtor, khali, guenter.roeck, Amit Kucheria, Jonathan Cameron
From: Amit Kucheria <amit.kucheria@verdurent.com>
Driver has had all event related elements stripped out leaving just
the raw sysfs reads for now.
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/light/Kconfig | 16 +
drivers/iio/light/Makefile | 6 +
drivers/iio/light/tsl2563.c | 653 +++++++++++++++++++++++++++++++++++++++++++
include/linux/iio/tsl2563.h | 9 +
6 files changed, 686 insertions(+), 0 deletions(-)
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index cebe21f..c5613b6 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -13,5 +13,6 @@ menuconfig IIO
if IIO
source "drivers/iio/adc/Kconfig"
+source "drivers/iio/light/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 42d6d12..0e59946 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_IIO) += iio.o
industrialio-y := core.o
obj-y += adc/
+obj-y += light/
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
new file mode 100644
index 0000000..6282ce0
--- /dev/null
+++ b/drivers/iio/light/Kconfig
@@ -0,0 +1,16 @@
+#
+# IIO Light Sensor Drivers
+#
+menu "Light Sensors"
+
+config IIO_TSL2563
+tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
+ depends on I2C
+ help
+ If you say yes here you get support for the Taos TSL2560,
+ TSL2561, TSL2562 and TSL2563 ambient light sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called iio_tsl2563.
+
+endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
new file mode 100644
index 0000000..f7fe4fd
--- /dev/null
+++ b/drivers/iio/light/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO Light Sensors
+#
+
+iio_tsl2563-y := tsl2563.o
+obj-$(CONFIG_IIO_MAX1363) += iio_tsl2563.o
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
new file mode 100644
index 0000000..f262e49
--- /dev/null
+++ b/drivers/iio/light/tsl2563.c
@@ -0,0 +1,653 @@
+/*
+ * drivers/i2c/chips/tsl2563.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ * Contact: Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * Converted to IIO driver
+ * Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/tsl2563.h>
+
+/* Use this many bits for fraction part. */
+#define ADC_FRAC_BITS (14)
+
+/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
+#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
+
+/* Bits used for fraction in calibration coefficients.*/
+#define CALIB_FRAC_BITS (10)
+/* 0.5 in CALIB_FRAC_BITS precision */
+#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1))
+/* Make a fraction from a number n that was multiplied with b. */
+#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b))
+/* Decimal 10^(digits in sysfs presentation) */
+#define CALIB_BASE_SYSFS (1000)
+
+#define TSL2563_CMD (0x80)
+#define TSL2563_CLEARINT (0x40)
+
+#define TSL2563_REG_CTRL (0x00)
+#define TSL2563_REG_TIMING (0x01)
+#define TSL2563_REG_LOWLOW (0x02) /* data0 low threshold, 2 bytes */
+#define TSL2563_REG_LOWHIGH (0x03)
+#define TSL2563_REG_HIGHLOW (0x04) /* data0 high threshold, 2 bytes */
+#define TSL2563_REG_HIGHHIGH (0x05)
+#define TSL2563_REG_INT (0x06)
+#define TSL2563_REG_ID (0x0a)
+#define TSL2563_REG_DATA0LOW (0x0c) /* broadband sensor value, 2 bytes */
+#define TSL2563_REG_DATA0HIGH (0x0d)
+#define TSL2563_REG_DATA1LOW (0x0e) /* infrared sensor value, 2 bytes */
+#define TSL2563_REG_DATA1HIGH (0x0f)
+
+#define TSL2563_CMD_POWER_ON (0x03)
+#define TSL2563_CMD_POWER_OFF (0x00)
+#define TSL2563_CTRL_POWER_MASK (0x03)
+
+#define TSL2563_TIMING_13MS (0x00)
+#define TSL2563_TIMING_100MS (0x01)
+#define TSL2563_TIMING_400MS (0x02)
+#define TSL2563_TIMING_MASK (0x03)
+#define TSL2563_TIMING_GAIN16 (0x10)
+#define TSL2563_TIMING_GAIN1 (0x00)
+
+#define TSL2563_INT_DISBLED (0x00)
+#define TSL2563_INT_LEVEL (0x10)
+#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
+
+struct tsl2563_gainlevel_coeff {
+ u8 gaintime;
+ u16 min;
+ u16 max;
+};
+
+static const struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = {
+ {
+ .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16,
+ .min = 0,
+ .max = 65534,
+ }, {
+ .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1,
+ .min = 2048,
+ .max = 65534,
+ }, {
+ .gaintime = TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1,
+ .min = 4095,
+ .max = 37177,
+ }, {
+ .gaintime = TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1,
+ .min = 3000,
+ .max = 65535,
+ },
+};
+
+struct tsl2563_chip {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct delayed_work poweroff_work;
+
+ /* Remember state for suspend and resume functions */
+ pm_message_t state;
+
+ struct tsl2563_gainlevel_coeff const *gainlevel;
+
+ /* Calibration coefficients */
+ u32 calib0;
+ u32 calib1;
+ int cover_comp_gain;
+
+ /* Cache current values, to be returned while suspended */
+ u32 data0;
+ u32 data1;
+};
+
+static int tsl2563_set_power(struct tsl2563_chip *chip, int on)
+{
+ return i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_CTRL,
+ on ?
+ TSL2563_CMD_POWER_ON :
+ TSL2563_CMD_POWER_OFF);
+}
+
+/*
+ * Return value is 0 for off, 1 for on, or a negative error
+ * code if reading failed.
+ */
+static int tsl2563_get_power(struct tsl2563_chip *chip)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_CTRL);
+ if (ret < 0)
+ return ret;
+
+ return (ret & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON;
+}
+
+static int tsl2563_configure(struct tsl2563_chip *chip)
+{
+ return i2c_smbus_write_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_TIMING,
+ chip->gainlevel->gaintime);
+}
+
+static void tsl2563_poweroff_work(struct work_struct *work)
+{
+ struct tsl2563_chip *chip =
+ container_of(work, struct tsl2563_chip, poweroff_work.work);
+ tsl2563_set_power(chip, 0);
+}
+
+static int tsl2563_detect(struct tsl2563_chip *chip)
+{
+ int ret;
+
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ return ret;
+
+ ret = tsl2563_get_power(chip);
+ if (ret < 0)
+ return ret;
+
+ return ret ? 0 : -ENODEV;
+}
+
+/*
+ * "Normalized" ADC value is one obtained with 400ms of integration time and
+ * 16x gain. This function returns the number of bits of shift needed to
+ * convert between normalized values and HW values obtained using given
+ * timing and gain settings.
+ */
+static int adc_shiftbits(u8 timing)
+{
+ int shift = 0;
+
+ switch (timing & TSL2563_TIMING_MASK) {
+ case TSL2563_TIMING_13MS:
+ shift += 5;
+ break;
+ case TSL2563_TIMING_100MS:
+ shift += 2;
+ break;
+ case TSL2563_TIMING_400MS:
+ /* no-op */
+ break;
+ }
+
+ if (!(timing & TSL2563_TIMING_GAIN16))
+ shift += 4;
+
+ return shift;
+}
+
+/* Convert a HW ADC value to normalized scale. */
+static u32 normalize_adc(u16 adc, u8 timing)
+{
+ return adc << adc_shiftbits(timing);
+}
+
+static void tsl2563_wait_adc(struct tsl2563_chip *chip)
+{
+ unsigned int delay;
+
+ switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) {
+ case TSL2563_TIMING_13MS:
+ delay = 14;
+ break;
+ case TSL2563_TIMING_100MS:
+ delay = 101;
+ break;
+ default:
+ delay = 402;
+ }
+ /*
+ * TODO: Make sure that we wait at least required delay but why we
+ * have to extend it one tick more?
+ */
+ schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2);
+}
+
+static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc)
+{
+ struct i2c_client *client = chip->client;
+
+ if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) {
+
+ (adc > chip->gainlevel->max) ?
+ chip->gainlevel++ : chip->gainlevel--;
+
+ i2c_smbus_write_byte_data(client,
+ TSL2563_CMD | TSL2563_REG_TIMING,
+ chip->gainlevel->gaintime);
+
+ tsl2563_wait_adc(chip);
+ tsl2563_wait_adc(chip);
+
+ return 1;
+ } else
+ return 0;
+}
+
+static int tsl2563_get_adc(struct tsl2563_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ u16 adc0, adc1;
+ int retry = 1;
+ int ret = 0;
+
+ if (chip->state.event != PM_EVENT_ON)
+ goto out;
+
+ cancel_delayed_work(&chip->poweroff_work);
+
+ if (!tsl2563_get_power(chip)) {
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ goto out;
+ ret = tsl2563_configure(chip);
+ if (ret)
+ goto out;
+ tsl2563_wait_adc(chip);
+ }
+
+ while (retry) {
+ ret = i2c_smbus_read_word_data(client,
+ TSL2563_CMD | TSL2563_REG_DATA0LOW);
+ if (ret < 0)
+ goto out;
+ adc0 = ret;
+
+ ret = i2c_smbus_read_word_data(client,
+ TSL2563_CMD | TSL2563_REG_DATA1LOW);
+ if (ret < 0)
+ goto out;
+ adc1 = ret;
+
+ retry = tsl2563_adjust_gainlevel(chip, adc0);
+ }
+
+ chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
+ chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static inline int calib_to_sysfs(u32 calib)
+{
+ return (int) (((calib * CALIB_BASE_SYSFS) +
+ CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
+}
+
+static inline u32 calib_from_sysfs(int value)
+{
+ return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
+}
+
+/*
+ * Conversions between lux and ADC values.
+ *
+ * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are
+ * appropriate constants. Different constants are needed for different
+ * kinds of light, determined by the ratio adc1/adc0 (basically the ratio
+ * of the intensities in infrared and visible wavelengths). lux_table below
+ * lists the upper threshold of the adc1/adc0 ratio and the corresponding
+ * constants.
+ */
+
+struct tsl2563_lux_coeff {
+ unsigned long ch_ratio;
+ unsigned long ch0_coeff;
+ unsigned long ch1_coeff;
+};
+
+static const struct tsl2563_lux_coeff lux_table[] = {
+ {
+ .ch_ratio = FRAC10K(1300),
+ .ch0_coeff = FRAC10K(315),
+ .ch1_coeff = FRAC10K(262),
+ }, {
+ .ch_ratio = FRAC10K(2600),
+ .ch0_coeff = FRAC10K(337),
+ .ch1_coeff = FRAC10K(430),
+ }, {
+ .ch_ratio = FRAC10K(3900),
+ .ch0_coeff = FRAC10K(363),
+ .ch1_coeff = FRAC10K(529),
+ }, {
+ .ch_ratio = FRAC10K(5200),
+ .ch0_coeff = FRAC10K(392),
+ .ch1_coeff = FRAC10K(605),
+ }, {
+ .ch_ratio = FRAC10K(6500),
+ .ch0_coeff = FRAC10K(229),
+ .ch1_coeff = FRAC10K(291),
+ }, {
+ .ch_ratio = FRAC10K(8000),
+ .ch0_coeff = FRAC10K(157),
+ .ch1_coeff = FRAC10K(180),
+ }, {
+ .ch_ratio = FRAC10K(13000),
+ .ch0_coeff = FRAC10K(34),
+ .ch1_coeff = FRAC10K(26),
+ }, {
+ .ch_ratio = ULONG_MAX,
+ .ch0_coeff = 0,
+ .ch1_coeff = 0,
+ },
+};
+
+/*
+ * Convert normalized, scaled ADC values to lux.
+ */
+static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+{
+ const struct tsl2563_lux_coeff *lp = lux_table;
+ unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
+
+ ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX;
+
+ while (lp->ch_ratio < ratio)
+ lp++;
+
+ lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff;
+
+ return (unsigned int) (lux >> ADC_FRAC_BITS);
+}
+
+/* Apply calibration coefficient to ADC count. */
+static u32 calib_adc(u32 adc, u32 calib)
+{
+ unsigned long scaled = adc;
+
+ scaled *= calib;
+ scaled >>= CALIB_FRAC_BITS;
+
+ return (u32) scaled;
+}
+
+static int tsl2563_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+ if (chan->channel == 0)
+ chip->calib0 = calib_from_sysfs(val);
+ else
+ chip->calib1 = calib_from_sysfs(val);
+
+ return 0;
+}
+
+static int tsl2563_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret = -EINVAL;
+ u32 calib0, calib1;
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+ mutex_lock(&chip->lock);
+ switch (m) {
+ case 0:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ goto error_ret;
+ calib0 = calib_adc(chip->data0, chip->calib0) *
+ chip->cover_comp_gain;
+ calib1 = calib_adc(chip->data1, chip->calib1) *
+ chip->cover_comp_gain;
+ *val = adc_to_lux(calib0, calib1);
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_INTENSITY:
+ ret = tsl2563_get_adc(chip);
+ if (ret)
+ goto error_ret;
+ if (chan->channel == 0)
+ *val = chip->data0;
+ else
+ *val = chip->data1;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
+ if (chan->channel == 0)
+ *val = calib_to_sysfs(chip->calib0);
+ else
+ *val = calib_to_sysfs(chip->calib1);
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+error_ret:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static const struct iio_chan_spec tsl2563_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ }, {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE),
+ }, {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_INFRARED,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE),
+ }
+};
+
+static const struct iio_info tsl2563_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2563_read_raw,
+ .write_raw = &tsl2563_write_raw,
+};
+
+static int __devinit tsl2563_probe(struct i2c_client *client,
+ const struct i2c_device_id *device_id)
+{
+ struct iio_dev *indio_dev;
+ struct tsl2563_chip *chip;
+ struct tsl2563_platform_data *pdata = client->dev.platform_data;
+ int err = 0;
+ int ret;
+ u8 id = 0;
+
+ indio_dev = iio_device_allocate(sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+ chip->client = client;
+
+ err = tsl2563_detect(chip);
+ if (err) {
+ dev_err(&client->dev, "device not found, error %d\n", -err);
+ goto fail1;
+ }
+
+ /* Check we can read the id */
+ err = i2c_smbus_read_byte_data(chip->client,
+ TSL2563_CMD | TSL2563_REG_ID);
+ if (err < 0)
+ goto fail1;
+
+ mutex_init(&chip->lock);
+
+ /* Default values used until userspace says otherwise */
+ chip->gainlevel = tsl2563_gainlevel_table;
+ chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
+ chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+
+ if (pdata)
+ chip->cover_comp_gain = pdata->cover_comp_gain;
+ else
+ chip->cover_comp_gain = 1;
+
+ dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
+ indio_dev->name = client->name;
+ indio_dev->channels = tsl2563_channels;
+ indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &tsl2563_info;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto fail1;
+ err = tsl2563_configure(chip);
+ if (err)
+ goto fail2;
+
+ INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work);
+
+ schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
+
+ return 0;
+fail2:
+ iio_device_unregister(indio_dev);
+fail1:
+ kfree(chip);
+ return err;
+}
+
+static int tsl2563_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+ tsl2563_set_power(chip, 0);
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static int tsl2563_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_set_power(chip, 0);
+ if (ret)
+ goto out;
+
+ chip->state = state;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int tsl2563_resume(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct tsl2563_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&chip->lock);
+
+ ret = tsl2563_set_power(chip, 1);
+ if (ret)
+ goto out;
+
+ ret = tsl2563_configure(chip);
+ if (ret)
+ goto out;
+
+ chip->state.event = PM_EVENT_ON;
+
+out:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static const struct i2c_device_id tsl2563_id[] = {
+ { "tsl2560", 0 },
+ { "tsl2561", 0 },
+ { "tsl2562", 0 },
+ { "tsl2563", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tsl2563_id);
+
+static struct i2c_driver tsl2563_i2c_driver = {
+ .driver = {
+ .name = "tsl2563",
+ },
+ .suspend = tsl2563_suspend,
+ .resume = tsl2563_resume,
+ .probe = tsl2563_probe,
+ .remove = __devexit_p(tsl2563_remove),
+ .id_table = tsl2563_id,
+};
+
+static int __init tsl2563_init(void)
+{
+ return i2c_add_driver(&tsl2563_i2c_driver);
+}
+module_init(tsl2563_init);
+
+static void __exit tsl2563_exit(void)
+{
+ i2c_del_driver(&tsl2563_i2c_driver);
+}
+module_exit(tsl2563_exit);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("tsl2563 light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/tsl2563.h b/include/linux/iio/tsl2563.h
new file mode 100644
index 0000000..e0bc31c
--- /dev/null
+++ b/include/linux/iio/tsl2563.h
@@ -0,0 +1,9 @@
+#ifndef _TSL2563_H_
+#define _TSL2563_H_
+
+struct tsl2563_platform_data {
+ int cover_comp_gain;
+};
+
+#endif /* _TSL2563_H_ */
+
--
1.7.3.4
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 5/6] IIO:imu:adis16400 partial move from staging.
2011-09-27 14:29 [RFC PATCH 0/6 V2] IIO: Out of staging step 1: The core Jonathan Cameron
` (3 preceding siblings ...)
2011-09-27 14:29 ` [PATCH 4/6] IIO:light:tsl2563 initial move out of staging Jonathan Cameron
@ 2011-09-27 14:29 ` Jonathan Cameron
2011-09-27 14:29 ` [PATCH 6/6] IIO: ABI documetation Jonathan Cameron
5 siblings, 0 replies; 8+ messages in thread
From: Jonathan Cameron @ 2011-09-27 14:29 UTC (permalink / raw)
To: linux-iio; +Cc: dtor, khali, guenter.roeck, Manuel Stahl, Jonathan Cameron
From: Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
This driver made use of a few convenient corners of scan_type so
I have lifted those elements into iio_chan_spec structure for now
(in initial iio core support patch).
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
drivers/iio/Kconfig | 1 +
drivers/iio/Makefile | 1 +
drivers/iio/imu/Kconfig | 12 +
drivers/iio/imu/Makefile | 6 +
drivers/iio/imu/adis16400.h | 180 ++++++
drivers/iio/imu/adis16400_core.c | 1142 ++++++++++++++++++++++++++++++++++++++
6 files changed, 1342 insertions(+), 0 deletions(-)
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index c5613b6..dd2daa1 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -13,6 +13,7 @@ menuconfig IIO
if IIO
source "drivers/iio/adc/Kconfig"
+source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 0e59946..db3c426 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_IIO) += iio.o
industrialio-y := core.o
obj-y += adc/
+obj-y += imu/
obj-y += light/
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
new file mode 100644
index 0000000..fe3c28c
--- /dev/null
+++ b/drivers/iio/imu/Kconfig
@@ -0,0 +1,12 @@
+menu "Inertial measurement units (IMUs)"
+
+config IIO_ADIS16400
+ tristate "Analog Devices ADIS16400 and similar IMU SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices adis16300, adis16350,
+ adis16354, adis16355, adis16360, adis16362, adis16364, adis16365,
+ adis16400 and adis16405 triaxial inertial sensors (adis16400 series
+ also have magnetometers).
+
+endmenu
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
new file mode 100644
index 0000000..eb4d31e
--- /dev/null
+++ b/drivers/iio/imu/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO IMUs
+#
+
+iio_adis16400-y := adis16400_core.o
+obj-$(CONFIG_IIO_ADIS16400) += iio_adis16400.o
\ No newline at end of file
diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h
new file mode 100644
index 0000000..78377ea
--- /dev/null
+++ b/drivers/iio/imu/adis16400.h
@@ -0,0 +1,180 @@
+/*
+ * adis16400.h support Analog Devices ADIS16400
+ * 3d 18g accelerometers,
+ * 3d gyroscopes,
+ * 3d 2.5gauss magnetometers via SPI
+ *
+ * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ *
+ * Loosely based upon lis3l02dq.h
+ *
+ * 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.
+ */
+
+#ifndef SPI_ADIS16400_H_
+#define SPI_ADIS16400_H_
+
+#define ADIS16400_STARTUP_DELAY 290 /* ms */
+#define ADIS16400_MTEST_DELAY 90 /* ms */
+
+#define ADIS16400_READ_REG(a) a
+#define ADIS16400_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
+#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
+#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */
+#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */
+#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */
+#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */
+#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */
+#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */
+#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */
+#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */
+#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */
+#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */
+
+#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */
+#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */
+#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */
+
+#define ADIS16300_PITCH_OUT 0x12 /* X axis inclinometer output measurement */
+#define ADIS16300_ROLL_OUT 0x12 /* Y axis inclinometer output measurement */
+
+/* Calibration parameters */
+#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
+#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
+#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */
+#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */
+#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */
+#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */
+#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */
+#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */
+#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */
+#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */
+#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */
+#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */
+
+#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */
+#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */
+#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */
+#define ADIS16400_DIAG_STAT 0x3C /* System status */
+
+/* Alarm functions */
+#define ADIS16400_GLOB_CMD 0x3E /* System command */
+#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */
+#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */
+#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */
+#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */
+#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */
+#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */
+
+#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
+
+#define ADIS16400_ERROR_ACTIVE (1<<14)
+#define ADIS16400_NEW_DATA (1<<14)
+
+/* MSC_CTRL */
+#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11)
+#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10)
+#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9)
+#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8)
+#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7)
+#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6)
+#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2)
+#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1)
+#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0)
+
+/* SMPL_PRD */
+#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7)
+#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F
+
+/* DIAG_STAT */
+#define ADIS16400_DIAG_STAT_ZACCL_FAIL (1<<15)
+#define ADIS16400_DIAG_STAT_YACCL_FAIL (1<<14)
+#define ADIS16400_DIAG_STAT_XACCL_FAIL (1<<13)
+#define ADIS16400_DIAG_STAT_XGYRO_FAIL (1<<12)
+#define ADIS16400_DIAG_STAT_YGYRO_FAIL (1<<11)
+#define ADIS16400_DIAG_STAT_ZGYRO_FAIL (1<<10)
+#define ADIS16400_DIAG_STAT_ALARM2 (1<<9)
+#define ADIS16400_DIAG_STAT_ALARM1 (1<<8)
+#define ADIS16400_DIAG_STAT_FLASH_CHK (1<<6)
+#define ADIS16400_DIAG_STAT_SELF_TEST (1<<5)
+#define ADIS16400_DIAG_STAT_OVERFLOW (1<<4)
+#define ADIS16400_DIAG_STAT_SPI_FAIL (1<<3)
+#define ADIS16400_DIAG_STAT_FLASH_UPT (1<<2)
+#define ADIS16400_DIAG_STAT_POWER_HIGH (1<<1)
+#define ADIS16400_DIAG_STAT_POWER_LOW (1<<0)
+
+/* GLOB_CMD */
+#define ADIS16400_GLOB_CMD_SW_RESET (1<<7)
+#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4)
+#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3)
+#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2)
+#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1)
+#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0)
+
+/* SLP_CNT */
+#define ADIS16400_SLP_CNT_POWER_OFF (1<<8)
+
+#define ADIS16400_MAX_TX 24
+#define ADIS16400_MAX_RX 24
+
+#define ADIS16400_SPI_SLOW (u32)(300 * 1000)
+#define ADIS16400_SPI_BURST (u32)(1000 * 1000)
+#define ADIS16400_SPI_FAST (u32)(2000 * 1000)
+
+#define ADIS16400_HAS_PROD_ID 1
+
+struct adis16400_chip_info {
+ const struct iio_chan_spec *channels;
+ const int num_channels;
+ const int product_id;
+ const long flags;
+ unsigned int gyro_scale_micro;
+ unsigned int accel_scale_micro;
+};
+
+/**
+ * struct adis16400_state - device instance specific data
+ * @us: actual spi_device
+ * @trig: data ready trigger registered with iio
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ * @buf_lock: mutex to protect tx and rx
+ **/
+struct adis16400_state {
+ struct spi_device *us;
+ struct iio_trigger *trig;
+ struct mutex buf_lock;
+ struct adis16400_chip_info *variant;
+
+ u8 tx[ADIS16400_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADIS16400_MAX_RX] ____cacheline_aligned;
+};
+
+#define ADIS16400_SCAN_SUPPLY 0
+#define ADIS16400_SCAN_GYRO_X 1
+#define ADIS16400_SCAN_GYRO_Y 2
+#define ADIS16400_SCAN_GYRO_Z 3
+#define ADIS16400_SCAN_ACC_X 4
+#define ADIS16400_SCAN_ACC_Y 5
+#define ADIS16400_SCAN_ACC_Z 6
+#define ADIS16400_SCAN_MAGN_X 7
+#define ADIS16350_SCAN_TEMP_X 7
+#define ADIS16400_SCAN_MAGN_Y 8
+#define ADIS16350_SCAN_TEMP_Y 8
+#define ADIS16400_SCAN_MAGN_Z 9
+#define ADIS16350_SCAN_TEMP_Z 9
+#define ADIS16400_SCAN_TEMP 10
+#define ADIS16350_SCAN_ADC_0 10
+#define ADIS16400_SCAN_ADC_0 11
+#define ADIS16300_SCAN_INCLI_X 12
+#define ADIS16300_SCAN_INCLI_Y 13
+
+#endif /* SPI_ADIS16400_H_ */
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c
new file mode 100644
index 0000000..b957bb4
--- /dev/null
+++ b/drivers/iio/imu/adis16400_core.c
@@ -0,0 +1,1142 @@
+/*
+ * adis16400.c support a number of ADI IMUs
+ *
+ * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2007-2011 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * 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.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "adis16400.h"
+
+enum adis16400_chip_variant {
+ ADIS16300,
+ ADIS16334,
+ ADIS16350,
+ ADIS16360,
+ ADIS16362,
+ ADIS16364,
+ ADIS16365,
+ ADIS16400,
+};
+
+/**
+ * adis16400_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ */
+static int adis16400_spi_write_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct adis16400_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16400_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * adis16400_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ * is assumed to have address one greater.
+ * @val: value to be written
+ *
+ * At the moment the spi framework doesn't allow global setting of cs_change.
+ * This means that use cannot be made of spi_write.
+ */
+static int adis16400_spi_write_reg_16(struct iio_dev *indio_dev,
+ u8 lower_reg_address,
+ u16 value)
+{
+ int ret;
+ struct spi_message msg;
+ struct adis16400_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx + 2,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16400_WRITE_REG(lower_reg_address);
+ st->tx[1] = value & 0xFF;
+ st->tx[2] = ADIS16400_WRITE_REG(lower_reg_address + 1);
+ st->tx[3] = (value >> 8) & 0xFF;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ ret = spi_sync(st->us, &msg);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * adis16400_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @indio_dev: iio device
+ * @reg_address: the address of the lower of the two registers. Second register
+ * is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ *
+ * At the moment the spi framework doesn't allow global setting of cs_change.
+ * This means that use cannot be made of spi_read.
+ **/
+static int adis16400_spi_read_reg_16(struct iio_dev *indio_dev,
+ u8 lower_reg_address,
+ u16 *val)
+{
+ struct spi_message msg;
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16400_READ_REG(lower_reg_address);
+ st->tx[1] = 0;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ ret = spi_sync(st->us, &msg);
+ if (ret) {
+ dev_err(&st->us->dev,
+ "problem when reading 16 bit register 0x%02X",
+ lower_reg_address);
+ goto error_ret;
+ }
+ *val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static ssize_t adis16400_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int ret, len = 0;
+ u16 t;
+ int sps;
+ ret = adis16400_spi_read_reg_16(indio_dev,
+ ADIS16400_SMPL_PRD,
+ &t);
+ if (ret)
+ return ret;
+ sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 53 : 1638;
+ sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1;
+ len = sprintf(buf, "%d SPS\n", sps);
+ return len;
+}
+
+static ssize_t adis16400_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct adis16400_state *st = iio_priv(indio_dev);
+ long val;
+ int ret;
+ u8 t;
+
+ ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ t = (1638 / val);
+ if (t > 0)
+ t--;
+ t &= ADIS16400_SMPL_PRD_DIV_MASK;
+ if ((t & ADIS16400_SMPL_PRD_DIV_MASK) >= 0x0A)
+ st->us->max_speed_hz = ADIS16400_SPI_SLOW;
+ else
+ st->us->max_speed_hz = ADIS16400_SPI_FAST;
+
+ ret = adis16400_spi_write_reg_8(indio_dev,
+ ADIS16400_SMPL_PRD,
+ t);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static int adis16400_reset(struct iio_dev *indio_dev)
+{
+ int ret;
+ ret = adis16400_spi_write_reg_8(indio_dev,
+ ADIS16400_GLOB_CMD,
+ ADIS16400_GLOB_CMD_SW_RESET);
+ if (ret)
+ dev_err(&indio_dev->dev, "problem resetting device");
+
+ return ret;
+}
+
+static ssize_t adis16400_write_reset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ bool val;
+ int ret;
+
+ ret = strtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+ if (val) {
+ ret = adis16400_reset(dev_get_drvdata(dev));
+ if (ret < 0)
+ return ret;
+ }
+
+ return len;
+}
+
+/* Power down the device */
+static int adis16400_stop_device(struct iio_dev *indio_dev)
+{
+ int ret;
+ u16 val = ADIS16400_SLP_CNT_POWER_OFF;
+
+ ret = adis16400_spi_write_reg_16(indio_dev, ADIS16400_SLP_CNT, val);
+ if (ret)
+ dev_err(&indio_dev->dev,
+ "problem with turning device off: SLP_CNT");
+
+ return ret;
+}
+
+static int adis16400_check_status(struct iio_dev *indio_dev)
+{
+ u16 status;
+ int ret;
+ struct device *dev = &indio_dev->dev;
+
+ ret = adis16400_spi_read_reg_16(indio_dev,
+ ADIS16400_DIAG_STAT, &status);
+
+ if (ret < 0) {
+ dev_err(dev, "Reading status failed\n");
+ goto error_ret;
+ }
+ ret = status;
+ if (status & ADIS16400_DIAG_STAT_ZACCL_FAIL)
+ dev_err(dev, "Z-axis accelerometer self-test failure\n");
+ if (status & ADIS16400_DIAG_STAT_YACCL_FAIL)
+ dev_err(dev, "Y-axis accelerometer self-test failure\n");
+ if (status & ADIS16400_DIAG_STAT_XACCL_FAIL)
+ dev_err(dev, "X-axis accelerometer self-test failure\n");
+ if (status & ADIS16400_DIAG_STAT_XGYRO_FAIL)
+ dev_err(dev, "X-axis gyroscope self-test failure\n");
+ if (status & ADIS16400_DIAG_STAT_YGYRO_FAIL)
+ dev_err(dev, "Y-axis gyroscope self-test failure\n");
+ if (status & ADIS16400_DIAG_STAT_ZGYRO_FAIL)
+ dev_err(dev, "Z-axis gyroscope self-test failure\n");
+ if (status & ADIS16400_DIAG_STAT_ALARM2)
+ dev_err(dev, "Alarm 2 active\n");
+ if (status & ADIS16400_DIAG_STAT_ALARM1)
+ dev_err(dev, "Alarm 1 active\n");
+ if (status & ADIS16400_DIAG_STAT_FLASH_CHK)
+ dev_err(dev, "Flash checksum error\n");
+ if (status & ADIS16400_DIAG_STAT_SELF_TEST)
+ dev_err(dev, "Self test error\n");
+ if (status & ADIS16400_DIAG_STAT_OVERFLOW)
+ dev_err(dev, "Sensor overrange\n");
+ if (status & ADIS16400_DIAG_STAT_SPI_FAIL)
+ dev_err(dev, "SPI failure\n");
+ if (status & ADIS16400_DIAG_STAT_FLASH_UPT)
+ dev_err(dev, "Flash update failed\n");
+ if (status & ADIS16400_DIAG_STAT_POWER_HIGH)
+ dev_err(dev, "Power supply above 5.25V\n");
+ if (status & ADIS16400_DIAG_STAT_POWER_LOW)
+ dev_err(dev, "Power supply below 4.75V\n");
+
+error_ret:
+ return ret;
+}
+
+static int adis16400_self_test(struct iio_dev *indio_dev)
+{
+ int ret;
+ ret = adis16400_spi_write_reg_16(indio_dev,
+ ADIS16400_MSC_CTRL,
+ ADIS16400_MSC_CTRL_MEM_TEST);
+ if (ret) {
+ dev_err(&indio_dev->dev, "problem starting self test");
+ goto err_ret;
+ }
+
+ msleep(ADIS16400_MTEST_DELAY);
+ adis16400_check_status(indio_dev);
+
+err_ret:
+ return ret;
+}
+
+static int adis16400_initial_setup(struct iio_dev *indio_dev)
+{
+ int ret;
+ u16 prod_id, smp_prd;
+ struct adis16400_state *st = iio_priv(indio_dev);
+
+ /* use low spi speed for init */
+ st->us->max_speed_hz = ADIS16400_SPI_SLOW;
+ st->us->mode = SPI_MODE_3;
+ spi_setup(st->us);
+
+ ret = adis16400_self_test(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "self test failure");
+ goto err_ret;
+ }
+
+ ret = adis16400_check_status(indio_dev);
+ if (ret) {
+ adis16400_reset(indio_dev);
+ dev_err(&indio_dev->dev, "device not playing ball -> reset");
+ msleep(ADIS16400_STARTUP_DELAY);
+ ret = adis16400_check_status(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "giving up");
+ goto err_ret;
+ }
+ }
+ if (st->variant->flags & ADIS16400_HAS_PROD_ID) {
+ ret = adis16400_spi_read_reg_16(indio_dev,
+ ADIS16400_PRODUCT_ID, &prod_id);
+ if (ret)
+ goto err_ret;
+
+ if ((prod_id & 0xF000) != st->variant->product_id)
+ dev_warn(&indio_dev->dev, "incorrect id");
+
+ dev_info(&indio_dev->dev,
+ "%s: prod_id 0x%04x at CS%d (irq %d)\n",
+ indio_dev->name, prod_id,
+ st->us->chip_select, st->us->irq);
+ }
+ /* use high spi speed if possible */
+ ret = adis16400_spi_read_reg_16(indio_dev,
+ ADIS16400_SMPL_PRD, &smp_prd);
+ if (!ret && (smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) {
+ st->us->max_speed_hz = ADIS16400_SPI_SLOW;
+ spi_setup(st->us);
+ }
+
+err_ret:
+ return ret;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency,
+ S_IWUSR | S_IRUGO,
+ adis16400_read_frequency,
+ adis16400_write_frequency,
+ 0);
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16400_write_reset, 0);
+
+static IIO_CONST_ATTR(sampling_frequency_available, "409 546 819 1638");
+
+enum adis16400_chan {
+ in_supply,
+ gyro_x,
+ gyro_y,
+ gyro_z,
+ accel_x,
+ accel_y,
+ accel_z,
+ magn_x,
+ magn_y,
+ magn_z,
+ temp,
+ temp0, temp1, temp2,
+ in1,
+ incli_x,
+ incli_y,
+};
+
+static u8 adis16400_addresses[17][2] = {
+ [in_supply] = { ADIS16400_SUPPLY_OUT },
+ [gyro_x] = { ADIS16400_XGYRO_OUT, ADIS16400_XGYRO_OFF },
+ [gyro_y] = { ADIS16400_YGYRO_OUT, ADIS16400_YGYRO_OFF },
+ [gyro_z] = { ADIS16400_ZGYRO_OUT, ADIS16400_ZGYRO_OFF },
+ [accel_x] = { ADIS16400_XACCL_OUT, ADIS16400_XACCL_OFF },
+ [accel_y] = { ADIS16400_YACCL_OUT, ADIS16400_YACCL_OFF },
+ [accel_z] = { ADIS16400_ZACCL_OUT, ADIS16400_ZACCL_OFF },
+ [magn_x] = { ADIS16400_XMAGN_OUT },
+ [magn_y] = { ADIS16400_YMAGN_OUT },
+ [magn_z] = { ADIS16400_ZMAGN_OUT },
+ [temp] = { ADIS16400_TEMP_OUT },
+ [temp0] = { ADIS16350_XTEMP_OUT },
+ [temp1] = { ADIS16350_YTEMP_OUT },
+ [temp2] = { ADIS16350_ZTEMP_OUT },
+ [in1] = { ADIS16400_AUX_ADC },
+ [incli_x] = { ADIS16300_PITCH_OUT },
+ [incli_y] = { ADIS16300_ROLL_OUT }
+};
+
+static int adis16400_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16400_spi_write_reg_16(indio_dev,
+ adis16400_addresses[chan->address][1],
+ val);
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adis16400_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct adis16400_state *st = iio_priv(indio_dev);
+ int ret, shift;
+ s16 val16;
+
+ switch (mask) {
+ case 0:
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16400_spi_read_reg_16(indio_dev,
+ adis16400_addresses[chan->address][0],
+ &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << chan->scan_type.realbits) - 1;
+ if (chan->scan_type.sign == 's') {
+ shift = 16 - chan->scan_type.realbits;
+ val16 = (s16)(val16 << shift) >> shift;
+ }
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+ case (1 << IIO_CHAN_INFO_SCALE_SEPARATE):
+ switch (chan->type) {
+ case IIO_GYRO:
+ *val = 0;
+ *val2 = st->variant->gyro_scale_micro;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_IN:
+ *val = 0;
+ if (chan->channel == 0)
+ *val2 = 2418;
+ else
+ *val2 = 806;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = st->variant->accel_scale_micro;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_MAGN:
+ *val = 0;
+ *val2 = 500;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 140000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16400_spi_read_reg_16(indio_dev,
+ adis16400_addresses[chan->address][1],
+ &val16);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret)
+ return ret;
+ val16 = ((val16 & 0xFFF) << 4) >> 4;
+ *val = val16;
+ return IIO_VAL_INT;
+ case (1 << IIO_CHAN_INFO_OFFSET_SEPARATE):
+ /* currently only temperature */
+ *val = 198;
+ *val2 = 160000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct iio_chan_spec adis16400_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "supply",
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = in_supply,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 'u',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = magn_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = magn_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = magn_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = temp,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_IN,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = in1,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 'u',
+ },
+ },
+};
+
+static struct iio_chan_spec adis16350_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "supply",
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = in_supply,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 'u',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "x",
+ .info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = temp0,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 1,
+ .extend_name = "y",
+ .info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = temp1,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 2,
+ .extend_name = "z",
+ .info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = temp2,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_IN,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = in1,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 'u',
+ },
+ },
+};
+
+static struct iio_chan_spec adis16300_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "supply",
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = in_supply,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 'u',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = temp,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_IN,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+ .address = in1,
+ .scan_type = {
+ .realbits = 12,
+ .sign = 'u',
+ },
+ }, {
+ .type = IIO_INCLI,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = incli_x,
+ .scan_type = {
+ .realbits = 13,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_INCLI,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = incli_y,
+ .scan_type = {
+ .realbits = 13,
+ .sign = 's',
+ },
+ },
+};
+
+static const struct iio_chan_spec adis16334_channels[] = {
+ {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_GYRO,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = gyro_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_x,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_y,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+ (1 << IIO_CHAN_INFO_SCALE_SHARED),
+ .address = accel_z,
+ .scan_type = {
+ .realbits = 14,
+ .sign = 's',
+ },
+ },
+};
+
+static struct attribute *adis16400_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_reset.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16400_attribute_group = {
+ .attrs = adis16400_attributes,
+};
+
+static struct adis16400_chip_info adis16400_chips[] = {
+ [ADIS16300] = {
+ .channels = adis16300_channels,
+ .num_channels = ARRAY_SIZE(adis16300_channels),
+ .gyro_scale_micro = 873,
+ .accel_scale_micro = 5884,
+ },
+ [ADIS16334] = {
+ .channels = adis16334_channels,
+ .num_channels = ARRAY_SIZE(adis16334_channels),
+ .gyro_scale_micro = 873,
+ .accel_scale_micro = 981,
+ },
+ [ADIS16350] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .gyro_scale_micro = 872664,
+ .accel_scale_micro = 24732,
+ },
+ [ADIS16360] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID,
+ .product_id = 0x3FE8,
+ .gyro_scale_micro = 1279,
+ .accel_scale_micro = 24732,
+ },
+ [ADIS16362] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID,
+ .product_id = 0x3FEA,
+ .gyro_scale_micro = 1279,
+ .accel_scale_micro = 24732,
+ },
+ [ADIS16364] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID,
+ .product_id = 0x3FEC,
+ .gyro_scale_micro = 1279,
+ .accel_scale_micro = 24732,
+ },
+ [ADIS16365] = {
+ .channels = adis16350_channels,
+ .num_channels = ARRAY_SIZE(adis16350_channels),
+ .flags = ADIS16400_HAS_PROD_ID,
+ .product_id = 0x3FED,
+ .gyro_scale_micro = 1279,
+ .accel_scale_micro = 24732,
+ },
+ [ADIS16400] = {
+ .channels = adis16400_channels,
+ .num_channels = ARRAY_SIZE(adis16400_channels),
+ .flags = ADIS16400_HAS_PROD_ID,
+ .product_id = 0x4015,
+ .gyro_scale_micro = 873,
+ .accel_scale_micro = 32656,
+ }
+};
+
+static const struct iio_info adis16400_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &adis16400_read_raw,
+ .write_raw = &adis16400_write_raw,
+ .attrs = &adis16400_attribute_group,
+};
+
+static int __devinit adis16400_probe(struct spi_device *spi)
+{
+ int ret, regdone = 0;
+ struct adis16400_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = iio_device_allocate(sizeof(*st));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+
+ /* setup the industrialio driver allocated elements */
+ st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data];
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = st->variant->channels;
+ indio_dev->num_channels = st->variant->num_channels;
+ indio_dev->info = &adis16400_info;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_dev;
+ regdone = 1;
+
+ /* Get the device into a sane initial state */
+ ret = adis16400_initial_setup(indio_dev);
+ if (ret)
+ goto error_free_dev;
+ return 0;
+
+error_free_dev:
+ if (regdone)
+ iio_device_unregister(indio_dev);
+ else
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int adis16400_remove(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ ret = adis16400_stop_device(indio_dev);
+ if (ret)
+ return ret;
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id adis16400_id[] = {
+ {"adis16300", ADIS16300},
+ {"adis16334", ADIS16334},
+ {"adis16350", ADIS16350},
+ {"adis16354", ADIS16350},
+ {"adis16355", ADIS16350},
+ {"adis16360", ADIS16360},
+ {"adis16362", ADIS16362},
+ {"adis16364", ADIS16364},
+ {"adis16365", ADIS16365},
+ {"adis16400", ADIS16400},
+ {"adis16405", ADIS16400},
+ {}
+};
+
+static struct spi_driver adis16400_driver = {
+ .driver = {
+ .name = "adis16400",
+ .owner = THIS_MODULE,
+ },
+ .id_table = adis16400_id,
+ .probe = adis16400_probe,
+ .remove = __devexit_p(adis16400_remove),
+};
+
+static __init int adis16400_init(void)
+{
+ return spi_register_driver(&adis16400_driver);
+}
+module_init(adis16400_init);
+
+static __exit void adis16400_exit(void)
+{
+ spi_unregister_driver(&adis16400_driver);
+}
+module_exit(adis16400_exit);
+
+MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
+MODULE_LICENSE("GPL v2");
--
1.7.3.4
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 6/6] IIO: ABI documetation.
2011-09-27 14:29 [RFC PATCH 0/6 V2] IIO: Out of staging step 1: The core Jonathan Cameron
` (4 preceding siblings ...)
2011-09-27 14:29 ` [PATCH 5/6] IIO:imu:adis16400 partial move from staging Jonathan Cameron
@ 2011-09-27 14:29 ` Jonathan Cameron
5 siblings, 0 replies; 8+ messages in thread
From: Jonathan Cameron @ 2011-09-27 14:29 UTC (permalink / raw)
To: linux-iio; +Cc: dtor, khali, guenter.roeck, Jonathan Cameron
Elements lifted from staging documentation,
drivers/staging/iio/Documentation/sysfs-bus-iio
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
Documentation/ABI/testing/sysfs-bus-iio | 223 +++++++++++++++++++++++++++++++
1 files changed, 223 insertions(+), 0 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
new file mode 100644
index 0000000..f271509
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -0,0 +1,223 @@
+What: /sys/bus/iio/devices/iio:deviceX
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware chip or device accessed by one communication port.
+ Corresponds to a grouping of sensor channels. X is the IIO
+ index of the device.
+
+What: /sys/bus/iio/devices/iio:deviceX/name
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Description of the physical chip / device for device X.
+ Typically a part number.
+
+What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Some devices have internal clocks. This parameter sets the
+ resulting sampling frequency.
+
+What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency_available
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ When the internal sampling clock can only take a small
+ discrete set of values, this file lists those available.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw (unscaled no bias removal etc) voltage measurement from
+ channel Y. In special cases where the channel does not
+ correspond to externally available input one of the named
+ versions may be used. The number must always be specified and
+ unique to allow association with event codes. Units after
+ application of scale and offset are microvolts.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw (unscaled) differential voltage measurement equivalent to
+ channel Y - channel Z where these channel numbers apply to the
+ physically equivalent inputs when non differential readings are
+ separately available. In differential only parts, then all that
+ is required is a consistent labeling. Units after application
+ of scale and offset are microvolts.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_tempX_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_x_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_y_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_z_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Raw (unscaled no bias removal etc) temperature measurement.
+ It an axis is specified it generally means that the temperature
+ sensor is associated with one part of a compound device (e.g.
+ a gyroscope axis). Units after application of scale and offset
+ are milli degrees Celsuis.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Acceleration in direction x, y or z (may be arbitrarily assigned
+ but should match other such assignments on device).
+ Has all of the equivalent parameters as per voltageY. Units
+ after application of scale and offset are m/s^2.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_x_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_y_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_z_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Angular velocity about axis x, y or z (may be arbitrarily
+ assigned) Data converted by application of offset then scale to
+ radians per second. Has all the equivalent parameters as
+ per voltageY. Units after application of scale and offset are
+ radians per second.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_incli_x_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_incli_y_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_incli_z_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Inclination raw reading about axis x, y or z (may be
+ arbitrarily assigned). Data converted by application of offset
+ and scale to Degrees.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_raw
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_raw
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Magnetic field along axis x, y or z (may be arbitrarily
+ assigned). Data converted by application of offset
+ then scale to Gauss.
+
+What: /sys/bus/iio/devices/device[n]/illuminance0_input
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ This should return the calculated lux from the light sensor.
+ Units are lumens.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_infrared_input
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property is supported by sensors that have an infrared
+ sensitive element. Unfortunately this reading is very
+ frequency range dependent and hence tends not to have well
+ defined units.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_both_input
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property is supported by sensors that have an infrared
+ and visible light sensing element. The visible portion is
+ usually extracted by subtracting the value from a separate
+ infrared only sensor. Unfortunately this reading is very
+ frequency range dependent and hence tends not to have well
+ defined units.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ If known for a device, offset to be added to <type>[Y]_raw prior
+ to scaling by <type>[Y]_scale in order to obtain value in the
+ <type> units as specified in <type>[y]_raw documentation.
+ Not present if the offset is always 0 or unknown. If Y or
+ axis <x|y|z> is not present, then the offset applies to all
+ in channels of <type>.
+ May be writable if a variable offset can be applied on the
+ device. Note that this is different to calibbias which
+ is for devices (or drivers) that apply offsets to compensate
+ for variation between different instances of the part, typically
+ adjusted by using some hardware supported calibration procedure.
+ Calibbias is applied internally, offset is applied in userspace
+ to the _raw output.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_x_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_y_scale
+What: /sys/bus/iio/devices/iio:deviceX/in_magn_z_scale
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ If known for a device, scale to be applied to <type>Y[_name]_raw
+ post addition of <type>[Y][_name]_offset in order to obtain the
+ measured value in <type> units as specified in
+ <type>[Y][_name]_raw documentation.. If shared across all in
+ channels then Y and <x|y|z> are not present and the value is
+ called <type>[Y][_name]_scale. The peak modifier means this
+ value is applied to <type>Y[_name]_peak_raw values.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available
+What: /sys/.../iio:deviceX/in_voltageX_scale_available
+What: /sys/.../iio:deviceX/in_voltage-voltage_scale_available
+What: /sys/.../iio:deviceX/out_voltageX_scale_available
+What: /sys/.../iio:deviceX/in_capacitance_scale_available
+KernelVersion: 2.635
+Contact: linux-iio@vger.kernel.org
+Description:
+ If a discrete set of scale values are available, they
+ are listed in this attribute.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibbias
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibbias
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibbias
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_x_calibbias
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_y_calibbias
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_z_calibbias
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware applied calibration offset. (assumed to fix production
+ inaccuracies).
+
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_x_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_y_calibscale
+What: /sys/bus/iio/devices/iio:deviceX/in_gyro_z_calibscale
+What: /sys/.../iio:deviceX/in_intensityY_infrared_calibscale
+What: /sys/.../iio:deviceX/in_intensityY_both_calibscale
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware applied calibration scale factor. (assumed to fix
+ production inaccuracies). If shared across all channels,
+ <type>_calibscale is used.
\ No newline at end of file
--
1.7.3.4
^ permalink raw reply related [flat|nested] 8+ messages in thread