linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6 V2] IIO: Out of staging step 1: The core
@ 2011-11-07 14:52 jic23
  2011-11-07 14:52 ` [PATCH 1/6] IIO: Core sysfs only support jic23
                   ` (6 more replies)
  0 siblings, 7 replies; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, Jonathan Cameron

From: Jonathan Cameron <jic23@kernel.org>

Dear All,

The cc list for this set is rather arbitary so please do forward to anyone
else who would be interested.

Curiously the vast majority of the changes in here are actually
from work done in the staging tree rather than in this much simpler
to review code.  Oh well, such is life.

Anyhow main changes are one from Lars-Peter and one from me.

Lars-Peter and Dan Carpenter both noted the rather strange use of a
bitmask in which only one bit was ever set and so suggested just using
the underlying enum rather than messing around with shifting it.  An
excellent simple clean up. Thanks guys.

Having done this I pointed out that the difference between shared and
separate channel info elements should only matter up to the point where
we have registered the relevant sysfs attributes.  Having fixed the
drivers where this was not the case, this observation allowed us to
define some magic macros allowing us to halve the length of the
chan_info enum.  Had the nice side effect of simplifying in kernel
interfaces to these elements as they no longer have to care if they
are getting a value shared across multiple channels or not.  This
concept was only ever meant as a convenience for userspace interfaces.
(we can not reasonably cover all interactions anyway so it doesn't
really give much information about the underlying interaction between
the different channels!)

Updated in kernel interface patches to follow.  They are all in of
git tree given below.

Anyhow, the original cover letter was (with links updated to reflect
the fact that our dev tree is back on kernel.org):

Dear All,

Firstly note that I have pushed ahead of this alongside the ongoing
discussions on how to handle in kernel interfaces for the devices
covered by IIO.  I propose to build those on top of this patch
set and will be working on that support whilst this set is
under review.

Secondly, this code has some namespace clashes with the staging
IIO code, so you will need a couple of patches that can be found
in https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git

This is our first attempt to propose moving 'some' of the
Industrial I/O subsystem out of staging.  This cover letter
attempts to explain what IIO is and why it is needed.
All comments welcome on this as well as the code!

So the big question is 'Why is there a need for a new subsystem?'

To answer this we need two things, a list or requirements and analysis
of why current subsystems (hwmon and input) do not meet them. Focused
subsystems targeting just one type of sensor have been rejected as
a concept (see ALS proposal).  Note that there is a certain amount of
overlap with Comedi.  At the moment we are proposing that any attempt
to provide compatible interfaces there occurs in a suitable user-space
library.

Requirements of IIO (note here we are covering just user-space usage.
In kernel interfaces will be dealt with separately once we have
pinned down how to do them cleanly).

1) Simple interface for simple use cases.
2) High performance data route.
3) Generic and consistent 'event' handling.
4) Wide device type coverage with consistent interface.
5) Complex triggering setups - multiple devices capture 'simultaneously'.
6) Support output devices.

Lets take these in turn:

1 - Simple interface for simple use cases:

The requirement boils down to an easy way to get a reading from a device or set
an output without much overhead in terms of user-space code complexity.

This is an area in which hwmon excels. Everything is available via sysfs
and that's exactly what we allow drivers to provide.  Some discussion has
taken place on allowing input devices to export some elements via sysfs
but at least with accelerometers it has floundered on the lack of a
consistent interface.

2 - High performance data route:

We want a path via which data can get to user-space without much overhead,
thus allowing high speed data capture. (Note I'm not arguing we have this
right yet - but the potential is there).

Input comes closest to this, but its approach of treating all data as events
and providing significant filtering options (obviously tailored to human input)
means that it isn't light weight.

The approach taken in IIO is to provide a 'buffered' interface.

The buffer may be in hardware or in software. The 'data' outputs of the
devices being considered can be mapped onto the concept of 'scans'.  In ADCs it
is common to have a single actual converter connected via a multiplexer to a
much larger set of inputs. The multiplexer will select the channels in turn in a
repeating sequence.  Each repeat is termed a scan.  The whole scan clearly
doesn't always occur simultaneously, but can be considered as a single set
of associated data none the less. Typically it is captured considerably faster
than it can be read out over the bus.  Thus we have repeating sequences of data.
This means that the meta data about what we are reading can be read
'out of band' from the data stream itself.

In IIO the buffers consist of a sequence of scans. The contents of each scan
data set (laid out however a driver wants to do it) is provided via a set of
sysfs attributes in the scan_elements directory.

The reason we can do it this way is that unlike input we pass our 'unusual'
events via a different path.
(see point 3).

We also allow for the minimum possible amount of processing of data to occur
in the kernel. Its often best to leave this to user-space and in a number
of important use cases doesn't need doing at all, either because it can be
done offline (logging) or because the reverse transform can be applied
to a much smaller set of data (e.g. software threshold comparison).  Note
that the raw value off the sensor is often considerably more compact to store
than a value in some set of consistent units.

3 - Generic and consistent 'event' handling.

Numerous sensing devices provide threshold type detectors designed to do
simple comparisons on the data without involving the host device.

In input, these are passed within the main event stream (we don't as that
would mean all data would need to carry identifying meta-data along).  Where
these events map to typical human input device elements (double click etc)
these are well handled. Hwmon supports a small set of such events via its alarm
attributes and recently has started using uevents.

For IIO we have decided to try and create an event code (literally one number)
to describe all types of events that these devices can detect and pass on to
the host device. This is coupled with appropriate sysfs controls to configure
the event.  The event code consists of:

1) What type of channel

2) Which channel (either axis / index or just index) Note this can be compound
if modified. (e.g. free fall is typically X&Y&Z all less than alpha). Also there
may be two channel numbers for differential signals.

3) What type of processed data it is applied to (raw value, abs value,
rate of change, average shifted value etc)

4) Which direction the threshold is crossed in.

This simple set of data has covered all events that we may term 'non-error'
events.  Discussions are underway about how to handle true error events
(input is disconnected, loss of tracking etc) but it won't be through this
path.

4 - Device coverage with a consistent interface

Hwmon and input have consistent interfaces that cover their chosen target well.
When considering a wider range of sensors they simply don't generalize well
enough. After all why should they?

Initially we matched hwmon where ever possible. In the end we broke away for a
number of reasons. The in / out naming for voltage is a nasty bit of legacy for
them which we don't want to carry. There is no general way of specifying whether
a channel is an input or an output.  Having said that there is an easy mapping
from IIO to hwmon.

Input is only interested in a narrow set of devices (those for human input).

5) Complex triggering setups

This is probably more of a niche requirement but the generalizations required
for it does simplify simple cases as well.

In a traditional data capturing environment (picture oscilloscopes / data
loggers), the concept of a trigger is vital.  That is, some event on which
data is captured.

In IIO this point is stretched somewhat.  For some devices it is indeed the
point of capture (those with explicit 'capture now' pins, or those that
capture when asked to by a bus transaction).  For other devices that run on
their own internal clocks this is actually a 'data has been captured now'
signal (data-ready).

What we want to allow (where possible) is for any device to be triggered by
any other and crucially for any number of devices to be triggered by a single
source.  As Thomas Gleixner observed, this is pretty much an irq cascade
(and hence that's how it is now implemented - a very tidy approach!).

Some of the types of trigger we have are:

a) Data ready signals
   These usually need to be used to trigger reading from the device producing
   them but can be used to get only 'slightly' late readings from other devices.
b) Software triggers
   These allow user-space to trigger simultaneous capture from a number of
   devices.
c) Standalone triggers.
   General interrupts from where-ever can be used to cause a capture to occur
   - typically a gpio.
d) Hardware timers
   (could probably do with a more consistent interface outside of IIO for
   these).

6) Support output devices.

This is a relatively recent addition to IIO (despite us planning on it when
the name was picked!).  Right now the support is very rudimentary -
corresponding to the most basic sysfs only device interfaces.

It becomes more interesting when buffered outputs are considered, but that
has not yet been implemented.

There is rudimentary support for output in hwmon (as is appropriate for its
use domain) and indeed some of this could map to the regulator api, but
neither is adaptable or suitable for more complex data flows.  Recently
the question of the divide between pwm and dds devices was raised and
it is clearly something that will need pinning down at a later date.

To get an appreciation for the whole subsystem it is necessary to look at
what is still in staging as we aren't attempting to move the whole core
out in one go.  Naturally if anyone fancies diving deeper into that tree
and commenting on stuff in there it would also be welcomed.

Note however that being in staging some of the drivers are in 'interesting'
shape.

'Good' drivers include (and this is far from a complete list)

adc/
	ad7192
	ad7780
	ad7792
	ad799x
	max1363
accel/
	lis3l02dq
gyro/
	adis16260
imu/
	adis16400
light/
	tsl2563


Amit Kucheria (1):
  IIO:light:tsl2563 initial move out of staging.

Jonathan Cameron (3):
  IIO: Core sysfs only support.
  IIO:ADC: max1363 initial import.
  IIO: ABI documetation.

Manuel Stahl (1):
  IIO:imu:adis16400 partial move from staging.

Michael Hennerich (1):
  IIO:ADC:ad799x initial import.

 Documentation/ABI/testing/sysfs-bus-iio |  223 ++++++
 drivers/Kconfig                         |    2 +
 drivers/Makefile                        |    3 +
 drivers/iio/Kconfig                     |   18 +
 drivers/iio/Makefile                    |   10 +
 drivers/iio/adc/Kconfig                 |   30 +
 drivers/iio/adc/Makefile                |    9 +
 drivers/iio/adc/ad799x_core.c           |  761 ++++++++++++++++++++
 drivers/iio/adc/max1363.h               |  148 ++++
 drivers/iio/adc/max1363_core.c          |  997 +++++++++++++++++++++++++++
 drivers/iio/iio.c                       |  591 ++++++++++++++++
 drivers/iio/imu/Kconfig                 |   12 +
 drivers/iio/imu/Makefile                |    6 +
 drivers/iio/imu/adis16400.h             |  180 +++++
 drivers/iio/imu/adis16400_core.c        | 1142 +++++++++++++++++++++++++++++++
 drivers/iio/light/Kconfig               |   16 +
 drivers/iio/light/Makefile              |    6 +
 drivers/iio/light/tsl2563.c             |  654 ++++++++++++++++++
 include/linux/iio/ad799x.h              |   12 +
 include/linux/iio/iio.h                 |  244 +++++++
 include/linux/iio/sysfs.h               |   68 ++
 include/linux/iio/tsl2563.h             |    8 +
 include/linux/iio/types.h               |   52 ++
 23 files changed, 5192 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-iio
 create mode 100644 drivers/iio/Kconfig
 create mode 100644 drivers/iio/Makefile
 create mode 100644 drivers/iio/adc/Kconfig
 create mode 100644 drivers/iio/adc/Makefile
 create mode 100644 drivers/iio/adc/ad799x_core.c
 create mode 100644 drivers/iio/adc/max1363.h
 create mode 100644 drivers/iio/adc/max1363_core.c
 create mode 100644 drivers/iio/iio.c
 create mode 100644 drivers/iio/imu/Kconfig
 create mode 100644 drivers/iio/imu/Makefile
 create mode 100644 drivers/iio/imu/adis16400.h
 create mode 100644 drivers/iio/imu/adis16400_core.c
 create mode 100644 drivers/iio/light/Kconfig
 create mode 100644 drivers/iio/light/Makefile
 create mode 100644 drivers/iio/light/tsl2563.c
 create mode 100644 include/linux/iio/ad799x.h
 create mode 100644 include/linux/iio/iio.h
 create mode 100644 include/linux/iio/sysfs.h
 create mode 100644 include/linux/iio/tsl2563.h
 create mode 100644 include/linux/iio/types.h

-- 
1.7.7.2


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

* [PATCH 1/6] IIO: Core sysfs only support.
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
@ 2011-11-07 14:52 ` jic23
  2011-11-11 10:40   ` Michael Hennerich
  2012-02-01 15:20   ` Maxime Ripard
  2011-11-07 14:52 ` [PATCH 2/6] IIO:ADC: max1363 initial import jic23
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, Jonathan Cameron

From: Jonathan Cameron <jic23@cam.ac.uk>

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          |    3 +
 drivers/iio/Kconfig       |   11 +
 drivers/iio/Makefile      |    6 +
 drivers/iio/iio.c         |  591 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/iio.h   |  244 +++++++++++++++++++
 include/linux/iio/sysfs.h |   68 +++++
 include/linux/iio/types.h |   52 ++++
 8 files changed, 977 insertions(+), 0 deletions(-)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index b5e6f24..7410537 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -136,4 +136,6 @@ source "drivers/hv/Kconfig"
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/iio/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 1b31421..216bba4 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -129,6 +129,9 @@ obj-$(CONFIG_IOMMU_SUPPORT)	+= iommu/
 
 # Virtualization drivers
 obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
+
 obj-$(CONFIG_HYPERV)		+= hv/
 
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
+
+obj-$(CONFIG_IIO)		+= iio/
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
new file mode 100644
index 0000000..012ebb0
--- /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.
+
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..9a98f5f
--- /dev/null
+++ b/drivers/iio/iio.c
@@ -0,0 +1,591 @@
+/* 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_ANGL_VEL] = "anglvel",
+	[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_INFRARED] = "ir",
+};
+
+static const char * const iio_chan_info_postfix[] = {
+	[IIO_CHAN_INFO_SCALE] = "scale",
+	[IIO_CHAN_INFO_OFFSET] = "offset",
+	[IIO_CHAN_INFO_CALIBSCALE] = "calibscale",
+	[IIO_CHAN_INFO_CALIBBIAS] = "calibbias",
+	[IIO_CHAN_INFO_PEAK] = "peak_raw",
+	[IIO_CHAN_INFO_PEAK_SCALE] = "peak_scale",
+	[IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW] = "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);
+}
+
+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.groups = dev->groups;
+		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 && !generic) {
+		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;
+	attrcount++;
+
+	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,
+					     i/2,
+					     !(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..4367d82
--- /dev/null
+++ b/include/linux/iio/iio.h
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+#ifndef _IIO_H_
+#define _IIO_H_
+
+#include <linux/klist.h>
+#include <linux/device.h>
+#include <linux/iio/types.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_info_enum {
+	IIO_CHAN_INFO_SCALE = 1,
+	IIO_CHAN_INFO_OFFSET,
+	IIO_CHAN_INFO_CALIBSCALE,
+	IIO_CHAN_INFO_CALIBBIAS,
+	IIO_CHAN_INFO_PEAK,
+	IIO_CHAN_INFO_PEAK_SCALE,
+	IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW,
+};
+
+#define IIO_CHAN_INFO_SHARED_BIT(type) BIT(type*2)
+#define IIO_CHAN_INFO_SEPARATE_BIT(type) BIT(type*2 + 1)
+
+#define IIO_CHAN_INFO_SCALE_SEPARATE_BIT		\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_SCALE)
+#define IIO_CHAN_INFO_SCALE_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_SCALE)
+#define IIO_CHAN_INFO_OFFSET_SEPARATE_BIT			\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_OFFSET)
+#define IIO_CHAN_INFO_OFFSET_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_OFFSET)
+#define IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT			\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_CALIBSCALE)
+#define IIO_CHAN_INFO_CALIBSCALE_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_CALIBSCALE)
+#define IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT			\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_CALIBBIAS)
+#define IIO_CHAN_INFO_CALIBBIAS_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_CALIBBIAS)
+#define IIO_CHAN_INFO_PEAK_SEPARATE_BIT			\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PEAK)
+#define IIO_CHAN_INFO_PEAK_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PEAK)
+#define IIO_CHAN_INFO_PEAKSCALE_SEPARATE_BIT			\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PEAKSCALE)
+#define IIO_CHAN_INFO_PEAKSCALE_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PEAKSCALE)
+#define IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE_BIT	\
+	IIO_CHAN_INFO_SEPARATE_BIT(				\
+		IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW)
+#define IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED_BIT	\
+	IIO_CHAN_INFO_SHARED_BIT(				\
+		IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW)
+#define IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT			\
+	IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_AVERAGE_RAW)
+#define IIO_CHAN_INFO_AVERAGE_RAW_SHARED_BIT			\
+	IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_AVERAGE_RAW)
+
+
+enum iio_direction {
+	IIO_IN,
+	IIO_OUT,
+};
+
+/**
+ * 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:		Description of data format.
+ * @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 scan_type - description of the data format
+	 * @sign:	Set if signed value
+	 * @realbits:	Number of valid bits of data
+	 * @shift:	Shift right by this before masking out realbits.
+	 */
+	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;
+};
+
+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
+ * @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
+ * @indio_dev:		Device structure filled by the device driver
+ **/
+int iio_device_register(struct iio_dev *indio_dev);
+
+/**
+ * iio_device_unregister() - unregister a device from the IIO subsystem
+ * @indio_dev:		Device structure representing the device.
+ **/
+void iio_device_unregister(struct iio_dev *indio_dev);
+
+/**
+ * iio_put_device() - reference counted deallocation of struct device
+ * @indio_dev: the iio_device containing the device
+ **/
+static inline void iio_put_device(struct iio_dev *indio_dev)
+{
+	if (indio_dev)
+		put_device(&indio_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_ */
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
new file mode 100644
index 0000000..4cb24aa
--- /dev/null
+++ b/include/linux/iio/types.h
@@ -0,0 +1,52 @@
+/* industrial I/O data types needed both in and out of kernel
+ *
+ * Copyright (c) 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.
+ */
+
+#ifndef _IIO_TYPES_H_
+#define _IIO_TYPES_H_
+
+enum iio_chan_type {
+	IIO_VOLTAGE,
+	IIO_CURRENT,
+	IIO_POWER,
+	IIO_CAPACITANCE,
+	IIO_ACCEL,
+	IIO_ANGL_VEL,
+	IIO_MAGN,
+	IIO_LIGHT,
+	IIO_INTENSITY,
+	IIO_PROXIMITY,
+	IIO_TEMP,
+	IIO_INCLI,
+	IIO_ROT,
+	IIO_ANGL,
+	IIO_TIMESTAMP,
+};
+
+enum iio_modifier {
+	IIO_NO_MOD,
+	IIO_MOD_X,
+	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_INFRARED,
+};
+
+#define IIO_VAL_INT 1
+#define IIO_VAL_INT_PLUS_MICRO 2
+#define IIO_VAL_INT_PLUS_NANO 3
+
+#endif /* _IIO_TYPES_H_ */
-- 
1.7.7.2


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

* [PATCH 2/6] IIO:ADC: max1363 initial import.
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
  2011-11-07 14:52 ` [PATCH 1/6] IIO: Core sysfs only support jic23
@ 2011-11-07 14:52 ` jic23
  2011-11-07 14:52 ` [PATCH 3/6] IIO:ADC:ad799x " jic23
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, Jonathan Cameron

From: Jonathan Cameron <jic23@cam.ac.uk>

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            |    5 +
 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 |  997 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1177 insertions(+), 0 deletions(-)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 012ebb0..f1e8a22 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -9,3 +9,8 @@ menuconfig IIO
 	  The Industrial input / output subsystem provides a unified
 	  framework for many different types of embedded sensor.
 
+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..6064fff
--- /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
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..f21ec63
--- /dev/null
+++ b/drivers/iio/adc/max1363_core.c
@@ -0,0 +1,997 @@
+/*
+ * 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 IIO_CHAN_INFO_SCALE:
+		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 = IIO_CHAN_INFO_SCALE_SHARED_BIT,		\
+	}								\
+
+/* 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 = IIO_CHAN_INFO_SCALE_SHARED_BIT,		\
+	}
+
+
+#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;
+	struct max1363_state *st;
+	struct iio_dev *indio_dev;
+
+	indio_dev = iio_device_allocate(sizeof(struct max1363_state));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	st = iio_priv(indio_dev);
+	st->reg = regulator_get(&client->dev, "vcc");
+	if (IS_ERR(st->reg)) {
+		ret = PTR_ERR(st->reg);
+		goto error_free_device;
+	}
+
+	ret = regulator_enable(st->reg);
+	if (ret < 0)
+		goto error_put_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)*sizeof(long)*
+			  (st->chip_info->num_modes + 1), GFP_KERNEL);
+	if (!indio_dev->available_scan_masks) {
+		ret = -ENOMEM;
+		goto error_disable_reg;
+	}
+
+	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;
+
+	return 0;
+
+error_free_available_scan_masks:
+	kfree(indio_dev->available_scan_masks);
+error_disable_reg:
+	if (!IS_ERR(st->reg))
+		regulator_disable(st->reg);
+error_put_reg:
+	if (!IS_ERR(st->reg))
+		regulator_put(st->reg);
+error_free_device:
+	iio_device_free(indio_dev);
+error_ret:
+	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);
+
+	iio_device_unregister(indio_dev);
+	
+	kfree(indio_dev->available_scan_masks);
+	if (!IS_ERR(st->reg)) {
+		regulator_disable(st->reg);
+		regulator_put(st->reg);
+	}
+
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id 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.7.2


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

* [PATCH 3/6] IIO:ADC:ad799x initial import.
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
  2011-11-07 14:52 ` [PATCH 1/6] IIO: Core sysfs only support jic23
  2011-11-07 14:52 ` [PATCH 2/6] IIO:ADC: max1363 initial import jic23
@ 2011-11-07 14:52 ` jic23
  2011-11-08 13:07   ` Lars-Peter Clausen
  2011-11-07 14:52 ` [PATCH 4/6] IIO:light:tsl2563 initial move out of staging jic23
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, Michael Hennerich,
	Jonathan Cameron

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      |    5 +-
 drivers/iio/adc/ad799x_core.c |  761 +++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/ad799x.h    |   12 +
 4 files changed, 788 insertions(+), 1 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 6064fff..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
+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..a9e8f5f
--- /dev/null
+++ b/drivers/iio/adc/ad799x_core.c
@@ -0,0 +1,761 @@
+/*
+ * iio/adc/ad799x.c
+ * Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc.
+ *
+ * based on iio/adc/max1363
+ * Copyright (C) 2008-2010 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ad799x.c
+ *
+ * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
+ * ad7998 and similar chips.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/ad799x.h>
+
+#define AD799X_CHANNEL_SHIFT			4
+/*
+ * AD7991, AD7995 and AD7999 defines
+ */
+
+#define AD7991_REF_SEL				0x08
+#define AD7991_FLTR				0x04
+#define AD7991_BIT_TRIAL_DELAY			0x02
+#define AD7991_SAMPLE_DELAY			0x01
+
+/*
+ * AD7992, AD7993, AD7994, AD7997 and AD7998 defines
+ */
+
+#define AD7998_FLTR				0x08
+#define AD7998_ALERT_EN				0x04
+#define AD7998_BUSY_ALERT			0x02
+#define AD7998_BUSY_ALERT_POL			0x01
+
+#define AD7998_CONV_RES_REG			0x0
+#define AD7998_ALERT_STAT_REG			0x1
+#define AD7998_CONF_REG				0x2
+#define AD7998_CYCLE_TMR_REG			0x3
+#define AD7998_DATALOW_CH1_REG			0x4
+#define AD7998_DATAHIGH_CH1_REG			0x5
+#define AD7998_HYST_CH1_REG			0x6
+#define AD7998_DATALOW_CH2_REG			0x7
+#define AD7998_DATAHIGH_CH2_REG			0x8
+#define AD7998_HYST_CH2_REG			0x9
+#define AD7998_DATALOW_CH3_REG			0xA
+#define AD7998_DATAHIGH_CH3_REG			0xB
+#define AD7998_HYST_CH3_REG			0xC
+#define AD7998_DATALOW_CH4_REG			0xD
+#define AD7998_DATAHIGH_CH4_REG			0xE
+#define AD7998_HYST_CH4_REG			0xF
+
+#define AD7998_CYC_MASK				0x7
+#define AD7998_CYC_DIS				0x0
+#define AD7998_CYC_TCONF_32			0x1
+#define AD7998_CYC_TCONF_64			0x2
+#define AD7998_CYC_TCONF_128			0x3
+#define AD7998_CYC_TCONF_256			0x4
+#define AD7998_CYC_TCONF_512			0x5
+#define AD7998_CYC_TCONF_1024			0x6
+#define AD7998_CYC_TCONF_2048			0x7
+
+#define AD7998_ALERT_STAT_CLEAR			0xFF
+
+/*
+ * AD7997 and AD7997 defines
+ */
+
+#define AD7997_8_READ_SINGLE			0x80
+#define AD7997_8_READ_SEQUENCE			0x70
+/* TODO: move this into a common header */
+#define RES_MASK(bits)	((1 << (bits)) - 1)
+
+enum {
+	ad7991,
+	ad7995,
+	ad7999,
+	ad7992,
+	ad7993,
+	ad7994,
+	ad7997,
+	ad7998
+};
+
+struct ad799x_state;
+
+/**
+ * struct ad799x_chip_info - chip specifc information
+ * @channel:		channel specification
+ * @num_channels:	number of channels
+ * @int_vref_mv:	the internal reference voltage
+ */
+struct ad799x_chip_info {
+	struct iio_chan_spec		channel[9];
+	int				num_channels;
+	u16				int_vref_mv;
+};
+
+struct ad799x_state {
+	struct i2c_client		*client;
+	const struct ad799x_chip_info	*chip_info;
+	struct regulator		*reg;
+	u16				int_vref_mv;
+	unsigned			id;
+	char				*name;
+	u16				config;
+};
+
+/*
+ * ad799x register access by I2C
+ */
+static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data)
+{
+	struct i2c_client *client = st->client;
+	int ret = 0;
+
+	ret = i2c_smbus_read_word_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "I2C read error\n");
+		return ret;
+	}
+
+	*data = swab16((u16)ret);
+
+	return 0;
+}
+
+static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
+{
+	u16 rxbuf;
+	u8 cmd;
+	int ret;
+
+	switch (st->id) {
+	case ad7991:
+	case ad7995:
+	case ad7999:
+		cmd = st->config | ((1 << ch) << AD799X_CHANNEL_SHIFT);
+		break;
+	case ad7992:
+	case ad7993:
+	case ad7994:
+		cmd = (1 << ch) << AD799X_CHANNEL_SHIFT;
+		break;
+	case ad7997:
+	case ad7998:
+		cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = ad799x_i2c_read16(st, cmd, &rxbuf);
+	if (ret < 0)
+		return ret;
+
+	return rxbuf;
+}
+
+static int ad799x_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val,
+			   int *val2,
+			   long m)
+{
+	int ret;
+	struct ad799x_state *st = iio_priv(indio_dev);
+	unsigned int scale_uv;
+
+	switch (m) {
+	case 0:
+		mutex_lock(&indio_dev->mlock);
+		ret = ad799x_scan_direct(st, chan->channel);
+		mutex_unlock(&indio_dev->mlock);
+
+		if (ret < 0)
+			return ret;
+		*val = (ret >> chan->scan_type.shift) &
+			RES_MASK(chan->scan_type.realbits);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		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,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type =  {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+		},
+		.num_channels = 5,
+		.int_vref_mv = 4096,
+	},
+	[ad7995] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+		},
+		.num_channels = 5,
+		.int_vref_mv = 1024,
+	},
+	[ad7999] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 8,
+					.shift = 4,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 8,
+					.shift = 4,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 8,
+					.shift = 4,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 8,
+					.shift = 4,
+				},
+			},
+		},
+		.num_channels = 5,
+		.int_vref_mv = 1024,
+	},
+	[ad7992] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+		},
+		.num_channels = 3,
+		.int_vref_mv = 4096,
+	},
+	[ad7993] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+		},
+		.num_channels = 5,
+		.int_vref_mv = 1024,
+	},
+	[ad7994] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+		},
+		.num_channels = 5,
+		.int_vref_mv = 4096,
+	},
+	[ad7997] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[4] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 4,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[5] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 5,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[6] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 6,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+			[7] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 7,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 10,
+					.shift = 2,
+				},
+			},
+		},
+		.num_channels = 9,
+		.int_vref_mv = 1024,
+	},
+	[ad7998] = {
+		.channel = {
+			[0] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[1] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 1,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[2] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 2,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[3] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 3,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[4] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 4,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[5] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 5,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[6] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 6,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+			[7] = {
+				.type = IIO_VOLTAGE,
+				.indexed = 1,
+				.channel = 7,
+				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+				.scan_type = {
+					.realbits = 12,
+					.shift = 0,
+				},
+			},
+		},
+		.num_channels = 9,
+		.int_vref_mv = 4096,
+	},
+};
+
+static int __devinit ad799x_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	int ret;
+	struct ad799x_platform_data *pdata = client->dev.platform_data;
+	struct ad799x_state *st;
+	struct iio_dev *indio_dev = iio_device_allocate(sizeof(*st));
+
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, indio_dev);
+
+	st->id = id->driver_data;
+	st->chip_info = &ad799x_chip_info_tbl[st->id];
+
+	/* TODO: Add pdata options for filtering and bit delay */
+
+	if (pdata)
+		st->int_vref_mv = pdata->vref_mv;
+	else
+		st->int_vref_mv = st->chip_info->int_vref_mv;
+
+	st->reg = regulator_get(&client->dev, "vcc");
+	if (!IS_ERR(st->reg)) {
+		ret = regulator_enable(st->reg);
+		if (ret)
+			goto error_put_reg;
+	}
+	st->client = client;
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = id->name;
+	indio_dev->info = &ad799X_info;
+
+	indio_dev->channels = st->chip_info->channel;
+	indio_dev->num_channels = st->chip_info->num_channels;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_disable_reg;
+
+	return 0;
+
+error_disable_reg:
+	if (!IS_ERR(st->reg))
+		regulator_disable(st->reg);
+error_put_reg:
+	if (!IS_ERR(st->reg))
+		regulator_put(st->reg);
+	iio_device_free(indio_dev);
+
+	return ret;
+}
+
+static __devexit int ad799x_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct ad799x_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	if (!IS_ERR(st->reg)) {
+		regulator_disable(st->reg);
+		regulator_put(st->reg);
+	}
+	iio_device_free(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id ad799x_id[] = {
+	{ "ad7991", ad7991 },
+	{ "ad7995", ad7995 },
+	{ "ad7999", ad7999 },
+	{ "ad7992", ad7992 },
+	{ "ad7993", ad7993 },
+	{ "ad7994", ad7994 },
+	{ "ad7997", ad7997 },
+	{ "ad7998", ad7998 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad799x_id);
+
+static struct i2c_driver ad799x_driver = {
+	.driver = {
+		.name = "ad799x",
+	},
+	.probe = ad799x_probe,
+	.remove = __devexit_p(ad799x_remove),
+	.id_table = ad799x_id,
+};
+
+static __init int ad799x_init(void)
+{
+	return i2c_add_driver(&ad799x_driver);
+}
+
+static __exit void ad799x_exit(void)
+{
+	i2c_del_driver(&ad799x_driver);
+}
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@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.7.2


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

* [PATCH 4/6] IIO:light:tsl2563 initial move out of staging.
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
                   ` (2 preceding siblings ...)
  2011-11-07 14:52 ` [PATCH 3/6] IIO:ADC:ad799x " jic23
@ 2011-11-07 14:52 ` jic23
  2011-11-07 14:52 ` [PATCH 5/6] IIO:imu:adis16400 partial move from staging jic23
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, 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 |  654 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/tsl2563.h |    8 +
 6 files changed, 686 insertions(+), 0 deletions(-)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index f1e8a22..5dcd9e5 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -12,5 +12,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..c4abe07
--- /dev/null
+++ b/drivers/iio/light/tsl2563.c
@@ -0,0 +1,654 @@
+/*
+ * 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 IIO_CHAN_INFO_CALIBSCALE:
+		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 = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+	}, {
+		.type = IIO_INTENSITY,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_INFRARED,
+		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+	}
+};
+
+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);
+
+	iio_device_unregister(indio_dev);
+	tsl2563_set_power(chip, 0);
+	iio_device_free(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..3e503cd
--- /dev/null
+++ b/include/linux/iio/tsl2563.h
@@ -0,0 +1,8 @@
+#ifndef _TSL2563_H_
+#define _TSL2563_H_
+
+struct tsl2563_platform_data {
+	int cover_comp_gain;
+};
+
+#endif /* _TSL2563_H_ */
-- 
1.7.7.2


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

* [PATCH 5/6] IIO:imu:adis16400 partial move from staging.
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
                   ` (3 preceding siblings ...)
  2011-11-07 14:52 ` [PATCH 4/6] IIO:light:tsl2563 initial move out of staging jic23
@ 2011-11-07 14:52 ` jic23
  2011-11-11 10:41   ` Michael Hennerich
  2011-11-07 14:52 ` [PATCH 6/6] IIO: ABI documetation jic23
  2011-11-08 13:32 ` [PATCH 0/6 V2] IIO: Out of staging step 1: The core Lars-Peter Clausen
  6 siblings, 1 reply; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, 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 5dcd9e5..308bc97 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -12,6 +12,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..5e49742
--- /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 IIO_CHAN_INFO_CALIBBIAS:
+		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 IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*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 IIO_CHAN_INFO_CALIBBIAS:
+		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 IIO_CHAN_INFO_OFFSET:
+		/* 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 = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = in_supply,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 'u',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = magn_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = magn_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = magn_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = temp,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_IN,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.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 = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = in_supply,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 'u',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.extend_name = "x",
+		.info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = temp0,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 1,
+		.extend_name = "y",
+		.info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = temp1,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 2,
+		.extend_name = "z",
+		.info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = temp2,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_IN,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.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 = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = in_supply,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 'u',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = temp,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_IN,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+		.address = in1,
+		.scan_type = {
+			.realbits = 12,
+			.sign = 'u',
+		},
+	}, {
+		.type = IIO_INCLI,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = incli_x,
+		.scan_type = {
+			.realbits = 13,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_INCLI,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = incli_y,
+		.scan_type = {
+			.realbits = 13,
+			.sign = 's',
+		},
+	},
+};
+
+static const struct iio_chan_spec adis16334_channels[] = {
+	{
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ANGL_VEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = gyro_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_x,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_y,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.address = accel_z,
+		.scan_type = {
+			.realbits = 14,
+			.sign = 's',
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
+		IIO_CHAN_INFO_SCALE_SHARED_BIT,
+		.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;
+	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;
+
+	/* Get the device into a sane initial state */
+	ret = adis16400_initial_setup(indio_dev);
+	if (ret)
+		goto error_unregister_dev;
+
+	return 0;
+
+error_unregister_dev:
+	iio_device_unregister(indio_dev);
+error_free_dev:
+	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);
+
+	iio_device_unregister(indio_dev);
+
+	ret = adis16400_stop_device(indio_dev);
+	if (ret)
+		return ret;
+
+	iio_device_free(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.7.2


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

* [PATCH 6/6] IIO: ABI documetation.
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
                   ` (4 preceding siblings ...)
  2011-11-07 14:52 ` [PATCH 5/6] IIO:imu:adis16400 partial move from staging jic23
@ 2011-11-07 14:52 ` jic23
  2011-11-11 10:41   ` Michael Hennerich
  2011-11-08 13:32 ` [PATCH 0/6 V2] IIO: Out of staging step 1: The core Lars-Peter Clausen
  6 siblings, 1 reply; 21+ messages in thread
From: jic23 @ 2011-11-07 14:52 UTC (permalink / raw)
  To: linux-iio, linux-kernel
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, linus.walleij, lars, maxime.ripard, Jonathan Cameron

From: Jonathan Cameron <jic23@cam.ac.uk>

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..965c7cf
--- /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_anglvel_x_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_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_anglvel_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_anglvel_x_calibbias
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_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_anglvel_x_calibscale
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
+What:		/sys/bus/iio/devices/iio:deviceX/in_anglvel_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.7.2


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

* Re: [PATCH 3/6] IIO:ADC:ad799x initial import.
  2011-11-07 14:52 ` [PATCH 3/6] IIO:ADC:ad799x " jic23
@ 2011-11-08 13:07   ` Lars-Peter Clausen
  2011-11-08 13:35     ` Jonathan Cameron
  0 siblings, 1 reply; 21+ messages in thread
From: Lars-Peter Clausen @ 2011-11-08 13:07 UTC (permalink / raw)
  To: jic23
  Cc: linux-iio, linux-kernel, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd, linus.walleij, maxime.ripard,
	Michael Hennerich

On 11/07/2011 03:52 PM, jic23@cam.ac.uk 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      |    5 +-
>  drivers/iio/adc/ad799x_core.c |  761 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/ad799x.h    |   12 +
>  4 files changed, 788 insertions(+), 1 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

ringbuffer and trigger support is still in staging.

Also we have another problem here, we now have to drivers for the same
device, both with the same symbol name. This won't really work.


> +	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 6064fff..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
> +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..a9e8f5f
> --- /dev/null
> +++ b/drivers/iio/adc/ad799x_core.c
> @@ -0,0 +1,761 @@
> +/*
> + * iio/adc/ad799x.c
> + * Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc.
> + *
> + * based on iio/adc/max1363
> + * Copyright (C) 2008-2010 Jonathan Cameron
> + *
> + * based on linux/drivers/i2c/chips/max123x
> + * Copyright (C) 2002-2004 Stefan Eletzhofer
> + *
> + * based on linux/drivers/acron/char/pcf8583.c
> + * Copyright (C) 2000 Russell King
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * ad799x.c
> + *
> + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
> + * ad7998 and similar chips.
> + *
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/sysfs.h>
> +#include <linux/i2c.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/err.h>
> +#include <linux/module.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/ad799x.h>
> +
> +#define AD799X_CHANNEL_SHIFT			4
> +/*
> + * AD7991, AD7995 and AD7999 defines
> + */
> +
> +#define AD7991_REF_SEL				0x08
> +#define AD7991_FLTR				0x04
> +#define AD7991_BIT_TRIAL_DELAY			0x02
> +#define AD7991_SAMPLE_DELAY			0x01
> +
> +/*
> + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines
> + */
> +
> +#define AD7998_FLTR				0x08
> +#define AD7998_ALERT_EN				0x04
> +#define AD7998_BUSY_ALERT			0x02
> +#define AD7998_BUSY_ALERT_POL			0x01
> +
> +#define AD7998_CONV_RES_REG			0x0
> +#define AD7998_ALERT_STAT_REG			0x1
> +#define AD7998_CONF_REG				0x2
> +#define AD7998_CYCLE_TMR_REG			0x3
> +#define AD7998_DATALOW_CH1_REG			0x4
> +#define AD7998_DATAHIGH_CH1_REG			0x5
> +#define AD7998_HYST_CH1_REG			0x6
> +#define AD7998_DATALOW_CH2_REG			0x7
> +#define AD7998_DATAHIGH_CH2_REG			0x8
> +#define AD7998_HYST_CH2_REG			0x9
> +#define AD7998_DATALOW_CH3_REG			0xA
> +#define AD7998_DATAHIGH_CH3_REG			0xB
> +#define AD7998_HYST_CH3_REG			0xC
> +#define AD7998_DATALOW_CH4_REG			0xD
> +#define AD7998_DATAHIGH_CH4_REG			0xE
> +#define AD7998_HYST_CH4_REG			0xF
> +
> +#define AD7998_CYC_MASK				0x7
> +#define AD7998_CYC_DIS				0x0
> +#define AD7998_CYC_TCONF_32			0x1
> +#define AD7998_CYC_TCONF_64			0x2
> +#define AD7998_CYC_TCONF_128			0x3
> +#define AD7998_CYC_TCONF_256			0x4
> +#define AD7998_CYC_TCONF_512			0x5
> +#define AD7998_CYC_TCONF_1024			0x6
> +#define AD7998_CYC_TCONF_2048			0x7
> +
> +#define AD7998_ALERT_STAT_CLEAR			0xFF
> +
> +/*
> + * AD7997 and AD7997 defines
> + */
> +
> +#define AD7997_8_READ_SINGLE			0x80
> +#define AD7997_8_READ_SEQUENCE			0x70
> +/* TODO: move this into a common header */
> +#define RES_MASK(bits)	((1 << (bits)) - 1)
> +
> +enum {
> +	ad7991,
> +	ad7995,
> +	ad7999,
> +	ad7992,
> +	ad7993,
> +	ad7994,
> +	ad7997,
> +	ad7998
> +};
> +
> +struct ad799x_state;
> +
> +/**
> + * struct ad799x_chip_info - chip specifc information
> + * @channel:		channel specification
> + * @num_channels:	number of channels
> + * @int_vref_mv:	the internal reference voltage
> + */
> +struct ad799x_chip_info {
> +	struct iio_chan_spec		channel[9];
> +	int				num_channels;
> +	u16				int_vref_mv;
> +};
> +
> +struct ad799x_state {
> +	struct i2c_client		*client;
> +	const struct ad799x_chip_info	*chip_info;
> +	struct regulator		*reg;
> +	u16				int_vref_mv;
> +	unsigned			id;
> +	char				*name;
> +	u16				config;
> +};
> +
> +/*
> + * ad799x register access by I2C
> + */
> +static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data)
> +{
> +	struct i2c_client *client = st->client;
> +	int ret = 0;
> +
> +	ret = i2c_smbus_read_word_data(client, reg);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "I2C read error\n");
> +		return ret;
> +	}
> +
> +	*data = swab16((u16)ret);
> +
> +	return 0;
> +}
> +
> +static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
> +{
> +	u16 rxbuf;
> +	u8 cmd;
> +	int ret;
> +
> +	switch (st->id) {
> +	case ad7991:
> +	case ad7995:
> +	case ad7999:
> +		cmd = st->config | ((1 << ch) << AD799X_CHANNEL_SHIFT);
> +		break;
> +	case ad7992:
> +	case ad7993:
> +	case ad7994:
> +		cmd = (1 << ch) << AD799X_CHANNEL_SHIFT;
> +		break;
> +	case ad7997:
> +	case ad7998:
> +		cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = ad799x_i2c_read16(st, cmd, &rxbuf);
> +	if (ret < 0)
> +		return ret;
> +
> +	return rxbuf;
> +}
> +
> +static int ad799x_read_raw(struct iio_dev *indio_dev,
> +			   struct iio_chan_spec const *chan,
> +			   int *val,
> +			   int *val2,
> +			   long m)
> +{
> +	int ret;
> +	struct ad799x_state *st = iio_priv(indio_dev);
> +	unsigned int scale_uv;
> +
> +	switch (m) {
> +	case 0:
> +		mutex_lock(&indio_dev->mlock);
> +		ret = ad799x_scan_direct(st, chan->channel);
> +		mutex_unlock(&indio_dev->mlock);
> +
> +		if (ret < 0)
> +			return ret;
> +		*val = (ret >> chan->scan_type.shift) &
> +			RES_MASK(chan->scan_type.realbits);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		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,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type =  {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +		},
> +		.num_channels = 5,
> +		.int_vref_mv = 4096,
> +	},
> +	[ad7995] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +		},
> +		.num_channels = 5,
> +		.int_vref_mv = 1024,
> +	},
> +	[ad7999] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 8,
> +					.shift = 4,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 8,
> +					.shift = 4,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 8,
> +					.shift = 4,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 8,
> +					.shift = 4,
> +				},
> +			},
> +		},
> +		.num_channels = 5,
> +		.int_vref_mv = 1024,
> +	},
> +	[ad7992] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +		},
> +		.num_channels = 3,
> +		.int_vref_mv = 4096,
> +	},
> +	[ad7993] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +		},
> +		.num_channels = 5,
> +		.int_vref_mv = 1024,
> +	},
> +	[ad7994] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +		},
> +		.num_channels = 5,
> +		.int_vref_mv = 4096,
> +	},
> +	[ad7997] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[4] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 4,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[5] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 5,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[6] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 6,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +			[7] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 7,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 10,
> +					.shift = 2,
> +				},
> +			},
> +		},
> +		.num_channels = 9,
> +		.int_vref_mv = 1024,
> +	},
> +	[ad7998] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[1] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 1,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[2] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 2,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[3] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 3,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[4] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 4,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[5] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 5,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[6] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 6,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +			[7] = {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.channel = 7,
> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +				.scan_type = {
> +					.realbits = 12,
> +					.shift = 0,
> +				},
> +			},
> +		},
> +		.num_channels = 9,
> +		.int_vref_mv = 4096,
> +	},
> +};

All 'num_channels' fields needs to be decremented by one since the
IIO_CHAN_SOFT_TIMESTAMP channel was removed.

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

* Re: [PATCH 0/6 V2] IIO: Out of staging step 1: The core
  2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
                   ` (5 preceding siblings ...)
  2011-11-07 14:52 ` [PATCH 6/6] IIO: ABI documetation jic23
@ 2011-11-08 13:32 ` Lars-Peter Clausen
  2011-11-08 14:23   ` Jonathan Cameron
  6 siblings, 1 reply; 21+ messages in thread
From: Lars-Peter Clausen @ 2011-11-08 13:32 UTC (permalink / raw)
  To: jic23
  Cc: linux-iio, linux-kernel, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd, linus.walleij, maxime.ripard,
	Jonathan Cameron

On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
> From: Jonathan Cameron <jic23@kernel.org>
> 
> [...]
> Dear All,
> 
> Firstly note that I have pushed ahead of this alongside the ongoing
> discussions on how to handle in kernel interfaces for the devices
> covered by IIO.  I propose to build those on top of this patch
> set and will be working on that support whilst this set is
> under review.
> 
> Secondly, this code has some namespace clashes with the staging
> IIO code, so you will need a couple of patches that can be found
> in https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
> 
> This is our first attempt to propose moving 'some' of the
> Industrial I/O subsystem out of staging.  This cover letter
> attempts to explain what IIO is and why it is needed.
> All comments welcome on this as well as the code!


I don't think moving just part of the IIO core out of staging will work. We
now end up with two competing frameworks for the same purpose which mostly
have the same API. If I for example enable both ST_IIO and IIO at the same
time everything will explode, since both want to register the same device class.

In my opinion we should move all of the core interface including events and
buffer support at once. Drivers of course can stay in staging. I guess the
main reason why this code is still in staging is that we don't fell
confident enough about the user-space ABI yet. The overall code quality is
ok and there are no major problems with the internal API.

With the new in-kernel interface the user-space buffer support becomes just
another consumer anyway. So we could keep it in staging for now. Something
similar is probably possible for event support. Provide the in-kernel
interfaces out of staging, but keep the user-space delivery mechanism in
staging for now.

I'll try to come up with some patches which allow coexistence of the
in-staging and out-of-staging IIO parts.

- Lars

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

* Re: [PATCH 3/6] IIO:ADC:ad799x initial import.
  2011-11-08 13:07   ` Lars-Peter Clausen
@ 2011-11-08 13:35     ` Jonathan Cameron
  0 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2011-11-08 13:35 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: jic23, linux-iio, linux-kernel, guenter.roeck, khali,
	dmitry.torokhov, broonie, gregkh, alan, arnd, linus.walleij,
	maxime.ripard, Michael Hennerich

On 11/08/2011 01:07 PM, Lars-Peter Clausen wrote:
> On 11/07/2011 03:52 PM, jic23@cam.ac.uk 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      |    5 +-
>>  drivers/iio/adc/ad799x_core.c |  761 +++++++++++++++++++++++++++++++++++++++++
>>  include/linux/iio/ad799x.h    |   12 +
>>  4 files changed, 788 insertions(+), 1 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
> 
> ringbuffer and trigger support is still in staging.
> 
Good point.
> Also we have another problem here, we now have to drivers for the same
> device, both with the same symbol name. This won't really work.
which name?  iio_ prefix deals with everything other than udev getting
confused about which module to load.
> 
> 
>> +	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 6064fff..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
>> +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..a9e8f5f
>> --- /dev/null
>> +++ b/drivers/iio/adc/ad799x_core.c
>> @@ -0,0 +1,761 @@
>> +/*
>> + * iio/adc/ad799x.c
>> + * Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc.
>> + *
>> + * based on iio/adc/max1363
>> + * Copyright (C) 2008-2010 Jonathan Cameron
>> + *
>> + * based on linux/drivers/i2c/chips/max123x
>> + * Copyright (C) 2002-2004 Stefan Eletzhofer
>> + *
>> + * based on linux/drivers/acron/char/pcf8583.c
>> + * Copyright (C) 2000 Russell King
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * ad799x.c
>> + *
>> + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
>> + * ad7998 and similar chips.
>> + *
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/i2c.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <linux/types.h>
>> +#include <linux/err.h>
>> +#include <linux/module.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/ad799x.h>
>> +
>> +#define AD799X_CHANNEL_SHIFT			4
>> +/*
>> + * AD7991, AD7995 and AD7999 defines
>> + */
>> +
>> +#define AD7991_REF_SEL				0x08
>> +#define AD7991_FLTR				0x04
>> +#define AD7991_BIT_TRIAL_DELAY			0x02
>> +#define AD7991_SAMPLE_DELAY			0x01
>> +
>> +/*
>> + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines
>> + */
>> +
>> +#define AD7998_FLTR				0x08
>> +#define AD7998_ALERT_EN				0x04
>> +#define AD7998_BUSY_ALERT			0x02
>> +#define AD7998_BUSY_ALERT_POL			0x01
>> +
>> +#define AD7998_CONV_RES_REG			0x0
>> +#define AD7998_ALERT_STAT_REG			0x1
>> +#define AD7998_CONF_REG				0x2
>> +#define AD7998_CYCLE_TMR_REG			0x3
>> +#define AD7998_DATALOW_CH1_REG			0x4
>> +#define AD7998_DATAHIGH_CH1_REG			0x5
>> +#define AD7998_HYST_CH1_REG			0x6
>> +#define AD7998_DATALOW_CH2_REG			0x7
>> +#define AD7998_DATAHIGH_CH2_REG			0x8
>> +#define AD7998_HYST_CH2_REG			0x9
>> +#define AD7998_DATALOW_CH3_REG			0xA
>> +#define AD7998_DATAHIGH_CH3_REG			0xB
>> +#define AD7998_HYST_CH3_REG			0xC
>> +#define AD7998_DATALOW_CH4_REG			0xD
>> +#define AD7998_DATAHIGH_CH4_REG			0xE
>> +#define AD7998_HYST_CH4_REG			0xF
>> +
>> +#define AD7998_CYC_MASK				0x7
>> +#define AD7998_CYC_DIS				0x0
>> +#define AD7998_CYC_TCONF_32			0x1
>> +#define AD7998_CYC_TCONF_64			0x2
>> +#define AD7998_CYC_TCONF_128			0x3
>> +#define AD7998_CYC_TCONF_256			0x4
>> +#define AD7998_CYC_TCONF_512			0x5
>> +#define AD7998_CYC_TCONF_1024			0x6
>> +#define AD7998_CYC_TCONF_2048			0x7
>> +
>> +#define AD7998_ALERT_STAT_CLEAR			0xFF
>> +
>> +/*
>> + * AD7997 and AD7997 defines
>> + */
>> +
>> +#define AD7997_8_READ_SINGLE			0x80
>> +#define AD7997_8_READ_SEQUENCE			0x70
>> +/* TODO: move this into a common header */
>> +#define RES_MASK(bits)	((1 << (bits)) - 1)
>> +
>> +enum {
>> +	ad7991,
>> +	ad7995,
>> +	ad7999,
>> +	ad7992,
>> +	ad7993,
>> +	ad7994,
>> +	ad7997,
>> +	ad7998
>> +};
>> +
>> +struct ad799x_state;
>> +
>> +/**
>> + * struct ad799x_chip_info - chip specifc information
>> + * @channel:		channel specification
>> + * @num_channels:	number of channels
>> + * @int_vref_mv:	the internal reference voltage
>> + */
>> +struct ad799x_chip_info {
>> +	struct iio_chan_spec		channel[9];
>> +	int				num_channels;
>> +	u16				int_vref_mv;
>> +};
>> +
>> +struct ad799x_state {
>> +	struct i2c_client		*client;
>> +	const struct ad799x_chip_info	*chip_info;
>> +	struct regulator		*reg;
>> +	u16				int_vref_mv;
>> +	unsigned			id;
>> +	char				*name;
>> +	u16				config;
>> +};
>> +
>> +/*
>> + * ad799x register access by I2C
>> + */
>> +static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data)
>> +{
>> +	struct i2c_client *client = st->client;
>> +	int ret = 0;
>> +
>> +	ret = i2c_smbus_read_word_data(client, reg);
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "I2C read error\n");
>> +		return ret;
>> +	}
>> +
>> +	*data = swab16((u16)ret);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
>> +{
>> +	u16 rxbuf;
>> +	u8 cmd;
>> +	int ret;
>> +
>> +	switch (st->id) {
>> +	case ad7991:
>> +	case ad7995:
>> +	case ad7999:
>> +		cmd = st->config | ((1 << ch) << AD799X_CHANNEL_SHIFT);
>> +		break;
>> +	case ad7992:
>> +	case ad7993:
>> +	case ad7994:
>> +		cmd = (1 << ch) << AD799X_CHANNEL_SHIFT;
>> +		break;
>> +	case ad7997:
>> +	case ad7998:
>> +		cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = ad799x_i2c_read16(st, cmd, &rxbuf);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return rxbuf;
>> +}
>> +
>> +static int ad799x_read_raw(struct iio_dev *indio_dev,
>> +			   struct iio_chan_spec const *chan,
>> +			   int *val,
>> +			   int *val2,
>> +			   long m)
>> +{
>> +	int ret;
>> +	struct ad799x_state *st = iio_priv(indio_dev);
>> +	unsigned int scale_uv;
>> +
>> +	switch (m) {
>> +	case 0:
>> +		mutex_lock(&indio_dev->mlock);
>> +		ret = ad799x_scan_direct(st, chan->channel);
>> +		mutex_unlock(&indio_dev->mlock);
>> +
>> +		if (ret < 0)
>> +			return ret;
>> +		*val = (ret >> chan->scan_type.shift) &
>> +			RES_MASK(chan->scan_type.realbits);
>> +		return IIO_VAL_INT;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		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,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type =  {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 5,
>> +		.int_vref_mv = 4096,
>> +	},
>> +	[ad7995] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 5,
>> +		.int_vref_mv = 1024,
>> +	},
>> +	[ad7999] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 8,
>> +					.shift = 4,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 8,
>> +					.shift = 4,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 8,
>> +					.shift = 4,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 8,
>> +					.shift = 4,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 5,
>> +		.int_vref_mv = 1024,
>> +	},
>> +	[ad7992] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 3,
>> +		.int_vref_mv = 4096,
>> +	},
>> +	[ad7993] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 5,
>> +		.int_vref_mv = 1024,
>> +	},
>> +	[ad7994] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 5,
>> +		.int_vref_mv = 4096,
>> +	},
>> +	[ad7997] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[4] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 4,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[5] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 5,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[6] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 6,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +			[7] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 7,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 10,
>> +					.shift = 2,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 9,
>> +		.int_vref_mv = 1024,
>> +	},
>> +	[ad7998] = {
>> +		.channel = {
>> +			[0] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 0,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[1] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 1,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[2] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 2,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[3] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 3,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[4] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 4,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[5] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 5,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[6] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 6,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +			[7] = {
>> +				.type = IIO_VOLTAGE,
>> +				.indexed = 1,
>> +				.channel = 7,
>> +				.info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
>> +				.scan_type = {
>> +					.realbits = 12,
>> +					.shift = 0,
>> +				},
>> +			},
>> +		},
>> +		.num_channels = 9,
>> +		.int_vref_mv = 4096,
>> +	},
>> +};
> 
> All 'num_channels' fields needs to be decremented by one since the
> IIO_CHAN_SOFT_TIMESTAMP channel was removed.
Excellent point.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [PATCH 0/6 V2] IIO: Out of staging step 1: The core
  2011-11-08 13:32 ` [PATCH 0/6 V2] IIO: Out of staging step 1: The core Lars-Peter Clausen
@ 2011-11-08 14:23   ` Jonathan Cameron
  2011-11-08 14:53     ` Lars-Peter Clausen
  0 siblings, 1 reply; 21+ messages in thread
From: Jonathan Cameron @ 2011-11-08 14:23 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: jic23, linux-iio, linux-kernel, guenter.roeck, khali,
	dmitry.torokhov, broonie, gregkh, alan, arnd, linus.walleij,
	maxime.ripard

On 11/08/2011 01:32 PM, Lars-Peter Clausen wrote:
> On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
>> From: Jonathan Cameron <jic23@kernel.org>
>>
>> [...]
>> Dear All,
>>
>> Firstly note that I have pushed ahead of this alongside the ongoing
>> discussions on how to handle in kernel interfaces for the devices
>> covered by IIO.  I propose to build those on top of this patch
>> set and will be working on that support whilst this set is
>> under review.
>>
>> Secondly, this code has some namespace clashes with the staging
>> IIO code, so you will need a couple of patches that can be found
>> in https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
>>
>> This is our first attempt to propose moving 'some' of the
>> Industrial I/O subsystem out of staging.  This cover letter
>> attempts to explain what IIO is and why it is needed.
>> All comments welcome on this as well as the code!
> 
> 
> I don't think moving just part of the IIO core out of staging will work.
It's the only option that looks plausible.  We just aren't going to get
anyone to review all the code in one go.  The original move into staging
was entirely about exposure, rather than code quality (not to say we
haven't improved that as well!)  The other thing is that the
simple stuff is mature and useful.  The buffering and event side of
things is still evolving and hence it may be a while yet before it is
stable enough.  (It was mature until the whole in kernel interface stuff
came up and lead to a substantial rewrite!)
 We
> now end up with two competing frameworks for the same purpose which mostly
> have the same API. If I for example enable both ST_IIO and IIO at the same
> time everything will explode, since both want to register the same device class.
True. That would be fixed by a simple namespace move though.  Annoying,
but plausible.
> 
> In my opinion we should move all of the core interface including events and
> buffer support at once. Drivers of course can stay in staging. I guess the
> main reason why this code is still in staging is that we don't fell
> confident enough about the user-space ABI yet. The overall code quality is
> ok and there are no major problems with the internal API.
Partly that, and partly that and partly there are controversial elements
to be discussed in each of the major parts.  There's a lot of pressure
to get 'something' out for the simple drivers now even if we take a
while to 'discuss' the other elements.  Hence it needs to happen in
chunks from the point of view of review, even if the final pull request
will bring over the whole core.

All in all, there are parts of our userspace interface that are still
'flexible' but they are typically for devices types we haven't yet put
much effort into.  The main types are all well covered with consistent
interfaces now.
> 
> With the new in-kernel interface the user-space buffer support becomes just
> another consumer anyway.
That would perhaps 'ideally' be true, but we really don't have that
level of separation at the moment.  The in kernel buffer access route
'works' but that's as far as I would go right now.

> So we could keep it in staging for now. Something
> similar is probably possible for event support.
True, but we haven't even started on inkernel interfaces for that path
yet.  They are probably going to be 'fairly' simple, but it needs doing.
> Provide the in-kernel
> interfaces out of staging, but keep the user-space delivery mechanism in
> staging for now.
> 
> I'll try to come up with some patches which allow coexistence of the
> in-staging and out-of-staging IIO parts.
Easy - move the name space.  Nothing else will work without adding code
to the out of staging part which isn't going to be acceptable.

Overall, I understand your point, but I'm far from convinced we can do
enough of the core in one go to be of practical use.

To do what you suggest we need complete separation between the core and
the userspace parts of IIO.  That is a far from trivial change.  I am
happy to spend some time investigating the possiblity of doing this, but
a few issues come to mind.

1) Elements that are not covered by the chan_spec. Some are fairly
standard, many just aren't.

2) Do we really want a map to do channel association?  Just imagine what
it will look like for some of the bigger devices with hundreds
of channels...

Anyhow, even if we do this. I still think the core move needs to occur
in a couple of stages.  The eventual pull to Linus may include all of
them, but it may well not if we don't get it all sorted in one cycle.

Jonathan

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

* Re: [PATCH 0/6 V2] IIO: Out of staging step 1: The core
  2011-11-08 14:23   ` Jonathan Cameron
@ 2011-11-08 14:53     ` Lars-Peter Clausen
  2011-11-08 15:29       ` Jonathan Cameron
  0 siblings, 1 reply; 21+ messages in thread
From: Lars-Peter Clausen @ 2011-11-08 14:53 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: jic23, linux-iio, linux-kernel, guenter.roeck, khali,
	dmitry.torokhov, broonie, gregkh, alan, arnd, linus.walleij,
	maxime.ripard

On 11/08/2011 03:23 PM, Jonathan Cameron wrote:
> On 11/08/2011 01:32 PM, Lars-Peter Clausen wrote:
>> On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
>>> From: Jonathan Cameron <jic23@kernel.org>
>>>
>>> [...]
>>> Dear All,
>>>
>>> Firstly note that I have pushed ahead of this alongside the ongoing
>>> discussions on how to handle in kernel interfaces for the devices
>>> covered by IIO.  I propose to build those on top of this patch
>>> set and will be working on that support whilst this set is
>>> under review.
>>>
>>> Secondly, this code has some namespace clashes with the staging
>>> IIO code, so you will need a couple of patches that can be found
>>> in https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
>>>
>>> This is our first attempt to propose moving 'some' of the
>>> Industrial I/O subsystem out of staging.  This cover letter
>>> attempts to explain what IIO is and why it is needed.
>>> All comments welcome on this as well as the code!
>>
>>
>> I don't think moving just part of the IIO core out of staging will work.
> It's the only option that looks plausible.  We just aren't going to get
> anyone to review all the code in one go.  The original move into staging
> was entirely about exposure, rather than code quality (not to say we
> haven't improved that as well!)  The other thing is that the
> simple stuff is mature and useful.  The buffering and event side of
> things is still evolving and hence it may be a while yet before it is
> stable enough.  (It was mature until the whole in kernel interface stuff
> came up and lead to a substantial rewrite!)
>  We
>> now end up with two competing frameworks for the same purpose which mostly
>> have the same API. If I for example enable both ST_IIO and IIO at the same
>> time everything will explode, since both want to register the same device class.
> True. That would be fixed by a simple namespace move though.  Annoying,
> but plausible.

Still two almost identical frameworks for the same purpose. The code for the
out-of-staging and still-in-staging branches have already started to divert.
Having both in the mainline kernel is going to be maintenance hell. People
will start sending patches for one, but not the other. I just don't think
this will workout well.

>>
>> In my opinion we should move all of the core interface including events and
>> buffer support at once. Drivers of course can stay in staging. I guess the
>> main reason why this code is still in staging is that we don't fell
>> confident enough about the user-space ABI yet. The overall code quality is
>> ok and there are no major problems with the internal API.
> Partly that, and partly that and partly there are controversial elements
> to be discussed in each of the major parts.  There's a lot of pressure
> to get 'something' out for the simple drivers now even if we take a
> while to 'discuss' the other elements.  Hence it needs to happen in
> chunks from the point of view of review, even if the final pull request
> will bring over the whole core.
> 

If the core split-up is just for review and is not intended to be merged
part-by-part over several kernel releases I don't see a problem.

- Lars


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

* Re: [PATCH 0/6 V2] IIO: Out of staging step 1: The core
  2011-11-08 14:53     ` Lars-Peter Clausen
@ 2011-11-08 15:29       ` Jonathan Cameron
  0 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2011-11-08 15:29 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: jic23, linux-iio, linux-kernel, guenter.roeck, khali,
	dmitry.torokhov, broonie, gregkh, alan, arnd, linus.walleij,
	maxime.ripard

On 11/08/2011 02:53 PM, Lars-Peter Clausen wrote:
> On 11/08/2011 03:23 PM, Jonathan Cameron wrote:
>> On 11/08/2011 01:32 PM, Lars-Peter Clausen wrote:
>>> On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
>>>> From: Jonathan Cameron <jic23@kernel.org>
>>>>
>>>> [...]
>>>> Dear All,
>>>>
>>>> Firstly note that I have pushed ahead of this alongside the ongoing
>>>> discussions on how to handle in kernel interfaces for the devices
>>>> covered by IIO.  I propose to build those on top of this patch
>>>> set and will be working on that support whilst this set is
>>>> under review.
>>>>
>>>> Secondly, this code has some namespace clashes with the staging
>>>> IIO code, so you will need a couple of patches that can be found
>>>> in https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
>>>>
>>>> This is our first attempt to propose moving 'some' of the
>>>> Industrial I/O subsystem out of staging.  This cover letter
>>>> attempts to explain what IIO is and why it is needed.
>>>> All comments welcome on this as well as the code!
>>>
>>>
>>> I don't think moving just part of the IIO core out of staging will work.
>> It's the only option that looks plausible.  We just aren't going to get
>> anyone to review all the code in one go.  The original move into staging
>> was entirely about exposure, rather than code quality (not to say we
>> haven't improved that as well!)  The other thing is that the
>> simple stuff is mature and useful.  The buffering and event side of
>> things is still evolving and hence it may be a while yet before it is
>> stable enough.  (It was mature until the whole in kernel interface stuff
>> came up and lead to a substantial rewrite!)
>>  We
>>> now end up with two competing frameworks for the same purpose which mostly
>>> have the same API. If I for example enable both ST_IIO and IIO at the same
>>> time everything will explode, since both want to register the same device class.
>> True. That would be fixed by a simple namespace move though.  Annoying,
>> but plausible.
> 
> Still two almost identical frameworks for the same purpose. The code for the
> out-of-staging and still-in-staging branches have already started to divert.
> Having both in the mainline kernel is going to be maintenance hell. People
> will start sending patches for one, but not the other. I just don't think
> this will workout well.
Yes, I agree entirely. It already isn't working that well as I keep
porting patches between the two.  Intent is to get over this as quickly
as possible by getting stuff out of staging!  If people add new stuff to
the staging tree, that is applicable to the non staging one I will be
shouting at them to do both.  The intent is to keep the two trees close
enough that driver moves are trivial (when the support is there in the
non staging tree).
> 
>>>
>>> In my opinion we should move all of the core interface including events and
>>> buffer support at once. Drivers of course can stay in staging. I guess the
>>> main reason why this code is still in staging is that we don't fell
>>> confident enough about the user-space ABI yet. The overall code quality is
>>> ok and there are no major problems with the internal API.
>> Partly that, and partly that and partly there are controversial elements
>> to be discussed in each of the major parts.  There's a lot of pressure
>> to get 'something' out for the simple drivers now even if we take a
>> while to 'discuss' the other elements.  Hence it needs to happen in
>> chunks from the point of view of review, even if the final pull request
>> will bring over the whole core.
>>
> 
> If the core split-up is just for review and is not intended to be merged
> part-by-part over several kernel releases I don't see a problem.
The intent is that it should be 'possible' to merge it part by part.
I'm not against that happening if the more complex stuff doesn't happen
this cycle.  I'd love it to go quickly, but time as ever is a precious
resource.

Jonathan

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

* Re: [PATCH 1/6] IIO: Core sysfs only support.
  2011-11-07 14:52 ` [PATCH 1/6] IIO: Core sysfs only support jic23
@ 2011-11-11 10:40   ` Michael Hennerich
  2012-02-01 15:20   ` Maxime Ripard
  1 sibling, 0 replies; 21+ messages in thread
From: Michael Hennerich @ 2011-11-11 10:40 UTC (permalink / raw)
  To: jic23
  Cc: linux-iio, linux-kernel, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd, linus.walleij, lars, maxime.ripard

On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
> From: Jonathan Cameron<jic23@cam.ac.uk>
>
> 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>
Since this is a subset of what we already reviewed and tested while in 
staging -
I'm happy to add my ACK.

Acked-by: Michael Hennerich <michael.hennerich@analog.com>

> ---
>   drivers/Kconfig           |    2 +
>   drivers/Makefile          |    3 +
>   drivers/iio/Kconfig       |   11 +
>   drivers/iio/Makefile      |    6 +
>   drivers/iio/iio.c         |  591 +++++++++++++++++++++++++++++++++++++++++++++
>   include/linux/iio/iio.h   |  244 +++++++++++++++++++
>   include/linux/iio/sysfs.h |   68 +++++
>   include/linux/iio/types.h |   52 ++++
>   8 files changed, 977 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index b5e6f24..7410537 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -136,4 +136,6 @@ source "drivers/hv/Kconfig"
>
>   source "drivers/devfreq/Kconfig"
>
> +source "drivers/iio/Kconfig"
> +
>   endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 1b31421..216bba4 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -129,6 +129,9 @@ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
>
>   # Virtualization drivers
>   obj-$(CONFIG_VIRT_DRIVERS)     += virt/
> +
>   obj-$(CONFIG_HYPERV)           += hv/
>
>   obj-$(CONFIG_PM_DEVFREQ)       += devfreq/
> +
> +obj-$(CONFIG_IIO)              += iio/
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> new file mode 100644
> index 0000000..012ebb0
> --- /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.
> +
> 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..9a98f5f
> --- /dev/null
> +++ b/drivers/iio/iio.c
> @@ -0,0 +1,591 @@
> +/* 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_ANGL_VEL] = "anglvel",
> +       [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_INFRARED] = "ir",
> +};
> +
> +static const char * const iio_chan_info_postfix[] = {
> +       [IIO_CHAN_INFO_SCALE] = "scale",
> +       [IIO_CHAN_INFO_OFFSET] = "offset",
> +       [IIO_CHAN_INFO_CALIBSCALE] = "calibscale",
> +       [IIO_CHAN_INFO_CALIBBIAS] = "calibbias",
> +       [IIO_CHAN_INFO_PEAK] = "peak_raw",
> +       [IIO_CHAN_INFO_PEAK_SCALE] = "peak_scale",
> +       [IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW] = "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);
> +}
> +
> +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.groups = dev->groups;
> +               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&&  !generic) {
> +               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;
> +       attrcount++;
> +
> +       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,
> +                                            i/2,
> +                                            !(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..4367d82
> --- /dev/null
> +++ b/include/linux/iio/iio.h
> @@ -0,0 +1,244 @@
> +/*
> + * 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.
> + */
> +#ifndef _IIO_H_
> +#define _IIO_H_
> +
> +#include<linux/klist.h>
> +#include<linux/device.h>
> +#include<linux/iio/types.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_info_enum {
> +       IIO_CHAN_INFO_SCALE = 1,
> +       IIO_CHAN_INFO_OFFSET,
> +       IIO_CHAN_INFO_CALIBSCALE,
> +       IIO_CHAN_INFO_CALIBBIAS,
> +       IIO_CHAN_INFO_PEAK,
> +       IIO_CHAN_INFO_PEAK_SCALE,
> +       IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW,
> +};
> +
> +#define IIO_CHAN_INFO_SHARED_BIT(type) BIT(type*2)
> +#define IIO_CHAN_INFO_SEPARATE_BIT(type) BIT(type*2 + 1)
> +
> +#define IIO_CHAN_INFO_SCALE_SEPARATE_BIT               \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_SCALE)
> +#define IIO_CHAN_INFO_SCALE_SHARED_BIT                 \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_SCALE)
> +#define IIO_CHAN_INFO_OFFSET_SEPARATE_BIT                      \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_OFFSET)
> +#define IIO_CHAN_INFO_OFFSET_SHARED_BIT                        \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_OFFSET)
> +#define IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT                  \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_CALIBSCALE)
> +#define IIO_CHAN_INFO_CALIBSCALE_SHARED_BIT                    \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_CALIBSCALE)
> +#define IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT                   \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_CALIBBIAS)
> +#define IIO_CHAN_INFO_CALIBBIAS_SHARED_BIT                     \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_CALIBBIAS)
> +#define IIO_CHAN_INFO_PEAK_SEPARATE_BIT                        \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PEAK)
> +#define IIO_CHAN_INFO_PEAK_SHARED_BIT                  \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PEAK)
> +#define IIO_CHAN_INFO_PEAKSCALE_SEPARATE_BIT                   \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_PEAKSCALE)
> +#define IIO_CHAN_INFO_PEAKSCALE_SHARED_BIT                     \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_PEAKSCALE)
> +#define IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE_BIT   \
> +       IIO_CHAN_INFO_SEPARATE_BIT(                             \
> +               IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW)
> +#define IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED_BIT     \
> +       IIO_CHAN_INFO_SHARED_BIT(                               \
> +               IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW)
> +#define IIO_CHAN_INFO_AVERAGE_RAW_SEPARATE_BIT                 \
> +       IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_AVERAGE_RAW)
> +#define IIO_CHAN_INFO_AVERAGE_RAW_SHARED_BIT                   \
> +       IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_AVERAGE_RAW)
> +
> +
> +enum iio_direction {
> +       IIO_IN,
> +       IIO_OUT,
> +};
> +
> +/**
> + * 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:         Description of data format.
> + * @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 scan_type - description of the data format
> +        * @sign:       Set if signed value
> +        * @realbits:   Number of valid bits of data
> +        * @shift:      Shift right by this before masking out realbits.
> +        */
> +       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;
> +};
> +
> +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
> + * @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
> + * @indio_dev:         Device structure filled by the device driver
> + **/
> +int iio_device_register(struct iio_dev *indio_dev);
> +
> +/**
> + * iio_device_unregister() - unregister a device from the IIO subsystem
> + * @indio_dev:         Device structure representing the device.
> + **/
> +void iio_device_unregister(struct iio_dev *indio_dev);
> +
> +/**
> + * iio_put_device() - reference counted deallocation of struct device
> + * @indio_dev: the iio_device containing the device
> + **/
> +static inline void iio_put_device(struct iio_dev *indio_dev)
> +{
> +       if (indio_dev)
> +               put_device(&indio_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_ */
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> new file mode 100644
> index 0000000..4cb24aa
> --- /dev/null
> +++ b/include/linux/iio/types.h
> @@ -0,0 +1,52 @@
> +/* industrial I/O data types needed both in and out of kernel
> + *
> + * Copyright (c) 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.
> + */
> +
> +#ifndef _IIO_TYPES_H_
> +#define _IIO_TYPES_H_
> +
> +enum iio_chan_type {
> +       IIO_VOLTAGE,
> +       IIO_CURRENT,
> +       IIO_POWER,
> +       IIO_CAPACITANCE,
> +       IIO_ACCEL,
> +       IIO_ANGL_VEL,
> +       IIO_MAGN,
> +       IIO_LIGHT,
> +       IIO_INTENSITY,
> +       IIO_PROXIMITY,
> +       IIO_TEMP,
> +       IIO_INCLI,
> +       IIO_ROT,
> +       IIO_ANGL,
> +       IIO_TIMESTAMP,
> +};
> +
> +enum iio_modifier {
> +       IIO_NO_MOD,
> +       IIO_MOD_X,
> +       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_INFRARED,
> +};
> +
> +#define IIO_VAL_INT 1
> +#define IIO_VAL_INT_PLUS_MICRO 2
> +#define IIO_VAL_INT_PLUS_NANO 3
> +
> +#endif /* _IIO_TYPES_H_ */
> --
> 1.7.7.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


-- 
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] 21+ messages in thread

* Re: [PATCH 6/6] IIO: ABI documetation.
  2011-11-07 14:52 ` [PATCH 6/6] IIO: ABI documetation jic23
@ 2011-11-11 10:41   ` Michael Hennerich
  0 siblings, 0 replies; 21+ messages in thread
From: Michael Hennerich @ 2011-11-11 10:41 UTC (permalink / raw)
  To: jic23
  Cc: linux-iio, linux-kernel, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd, linus.walleij, lars, maxime.ripard

On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
> From: Jonathan Cameron<jic23@cam.ac.uk>
>
> Elements lifted from staging documentation,
> drivers/staging/iio/Documentation/sysfs-bus-iio
>
> Signed-off-by: Jonathan Cameron<jic23@cam.ac.uk>
Acked-by: Michael Hennerich <michael.hennerich@analog.com>
> ---
>   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..965c7cf
> --- /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_anglvel_x_raw
> +What:          /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw
> +What:          /sys/bus/iio/devices/iio:deviceX/in_anglvel_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_anglvel_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_anglvel_x_calibbias
> +What:          /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibbias
> +What:          /sys/bus/iio/devices/iio:deviceX/in_anglvel_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_anglvel_x_calibscale
> +What:          /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
> +What:          /sys/bus/iio/devices/iio:deviceX/in_anglvel_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.7.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


-- 
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] 21+ messages in thread

* Re: [PATCH 5/6] IIO:imu:adis16400 partial move from staging.
  2011-11-07 14:52 ` [PATCH 5/6] IIO:imu:adis16400 partial move from staging jic23
@ 2011-11-11 10:41   ` Michael Hennerich
  0 siblings, 0 replies; 21+ messages in thread
From: Michael Hennerich @ 2011-11-11 10:41 UTC (permalink / raw)
  To: jic23
  Cc: linux-iio, linux-kernel, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd, linus.walleij, lars, maxime.ripard,
	Manuel Stahl

On 11/07/2011 03:52 PM, jic23@cam.ac.uk wrote:
> 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>
Acked-by: Michael Hennerich <michael.hennerich@analog.com>
> ---
>   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 5dcd9e5..308bc97 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -12,6 +12,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..5e49742
> --- /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 IIO_CHAN_INFO_CALIBBIAS:
> +               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 IIO_CHAN_INFO_SCALE:
> +               switch (chan->type) {
> +               case IIO_ANGL_VEL:
> +                       *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 IIO_CHAN_INFO_CALIBBIAS:
> +               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 IIO_CHAN_INFO_OFFSET:
> +               /* 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 = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = in_supply,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 'u',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_MAGN,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = magn_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_MAGN,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = magn_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_MAGN,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = magn_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_TEMP,
> +               .indexed = 1,
> +               .channel = 0,
> +               .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = temp,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_IN,
> +               .indexed = 1,
> +               .channel = 1,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .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 = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = in_supply,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 'u',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_TEMP,
> +               .indexed = 1,
> +               .channel = 0,
> +               .extend_name = "x",
> +               .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = temp0,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_TEMP,
> +               .indexed = 1,
> +               .channel = 1,
> +               .extend_name = "y",
> +               .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = temp1,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_TEMP,
> +               .indexed = 1,
> +               .channel = 2,
> +               .extend_name = "z",
> +               .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = temp2,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_IN,
> +               .indexed = 1,
> +               .channel = 1,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .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 = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = in_supply,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 'u',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_TEMP,
> +               .indexed = 1,
> +               .channel = 0,
> +               .info_mask = IIO_CHAN_INFO_OFFSET_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = temp,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_IN,
> +               .indexed = 1,
> +               .channel = 1,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
> +               .address = in1,
> +               .scan_type = {
> +                       .realbits = 12,
> +                       .sign = 'u',
> +               },
> +       }, {
> +               .type = IIO_INCLI,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = incli_x,
> +               .scan_type = {
> +                       .realbits = 13,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_INCLI,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = incli_y,
> +               .scan_type = {
> +                       .realbits = 13,
> +                       .sign = 's',
> +               },
> +       },
> +};
> +
> +static const struct iio_chan_spec adis16334_channels[] = {
> +       {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ANGL_VEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = gyro_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_X,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_x,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Y,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_y,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_ACCEL,
> +               .modified = 1,
> +               .channel2 = IIO_MOD_Z,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .address = accel_z,
> +               .scan_type = {
> +                       .realbits = 14,
> +                       .sign = 's',
> +               },
> +       }, {
> +               .type = IIO_TEMP,
> +               .indexed = 1,
> +               .channel = 0,
> +               .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT |
> +               IIO_CHAN_INFO_SCALE_SHARED_BIT,
> +               .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;
> +       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;
> +
> +       /* Get the device into a sane initial state */
> +       ret = adis16400_initial_setup(indio_dev);
> +       if (ret)
> +               goto error_unregister_dev;
> +
> +       return 0;
> +
> +error_unregister_dev:
> +       iio_device_unregister(indio_dev);
> +error_free_dev:
> +       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);
> +
> +       iio_device_unregister(indio_dev);
> +
> +       ret = adis16400_stop_device(indio_dev);
> +       if (ret)
> +               return ret;
> +
> +       iio_device_free(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.7.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>


-- 
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] 21+ messages in thread

* Re: [PATCH 1/6] IIO: Core sysfs only support.
  2011-11-07 14:52 ` [PATCH 1/6] IIO: Core sysfs only support jic23
  2011-11-11 10:40   ` Michael Hennerich
@ 2012-02-01 15:20   ` Maxime Ripard
  1 sibling, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2012-02-01 15:20 UTC (permalink / raw)
  To: jic23
  Cc: linux-iio, linux-kernel, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd, linus.walleij, lars

Hi,
I know that this patchset is probably a bit outdated but still...

On 07/11/2011 15:52, jic23@cam.ac.uk wrote:
> From: Jonathan Cameron <jic23@cam.ac.uk>
> 
> 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>
First, you can add my
Tested-by: Maxime Ripard <maxime.ripard@free-electrons.com>

I have encountered a bug during my tests though, see below

> +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.groups = dev->groups;
> +		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);

Here, the iio_put_device is called in free, but get_device is never
called, but seem to be called in iio_device_register, which, if you ever
encounter an error in the probe function, you will never have called the
iio_device_register function, before calling the iio_device_free
function, which leads to a segfault.

Maxime

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

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

* Re: [PATCH 1/6] IIO: Core sysfs only support.
  2011-10-17 13:16 ` [PATCH 1/6] IIO: Core sysfs only support Jonathan Cameron
  2011-10-17 13:42   ` Alexander Stein
@ 2011-10-17 16:27   ` Jonathan Cameron
  1 sibling, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2011-10-17 16:27 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel, linux-iio, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd

On 10/17/11 14:16, Jonathan Cameron wrote:
> 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.
Sorry all, one little rather debilitating buglet in here.
Means no attrs actually get registered.
...
> +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) {
Following line was missing for some reason.

		dev->dev.groups = dev->groups;
> +		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);
..

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

* Re: [PATCH 1/6] IIO: Core sysfs only support.
  2011-10-17 13:42   ` Alexander Stein
@ 2011-10-17 13:48     ` Jonathan Cameron
  0 siblings, 0 replies; 21+ messages in thread
From: Jonathan Cameron @ 2011-10-17 13:48 UTC (permalink / raw)
  To: Alexander Stein
  Cc: linux-kernel, linux-iio, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd

On 10/17/11 14:42, Alexander Stein wrote:
> Hello Jonathan,
> 
> On Monday 17 October 2011 15:16:16 you wrote:
>> 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          |    3 +
>>  drivers/iio/Kconfig       |   11 +
>>  drivers/iio/Makefile      |    6 +
>>  drivers/iio/iio.c         |  591
>> +++++++++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h   | 
>> 250 +++++++++++++++++++
>>  include/linux/iio/sysfs.h |   68 +++++
>>  7 files changed, 931 insertions(+), 0 deletions(-)
>>
>> [...]
>> 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.
> 
> I don't know much about iio so I want to read about it. But where does 
> Documentation/iio come from?
An excellent point.  First fix for V2 ;)

It is in drivers/staging/iio/  I haven't lifted most of it over yet.

Sadly some of it is somewhat bit rotted. Best bet is to read
sysfs-bus-iio to see sysfs interface and we do have an example driver
but it's not quite merged yet (working its way to staging-next).
So see

https://github.com/jic23/linux-iio/blob/master/drivers/staging/iio/iio_simple_dummy.c

As someone new to IIO I'd particularly like to hear you
opinions on whether the dummy driver helped you to figure
out what is going on.   Any other comments of course would
be most welcome.

Perhaps I'll pull the dummy driver into V2 of this patch set.

Thanks,

Jonathan

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

* Re: [PATCH 1/6] IIO: Core sysfs only support.
  2011-10-17 13:16 ` [PATCH 1/6] IIO: Core sysfs only support Jonathan Cameron
@ 2011-10-17 13:42   ` Alexander Stein
  2011-10-17 13:48     ` Jonathan Cameron
  2011-10-17 16:27   ` Jonathan Cameron
  1 sibling, 1 reply; 21+ messages in thread
From: Alexander Stein @ 2011-10-17 13:42 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: linux-kernel, linux-iio, guenter.roeck, khali, dmitry.torokhov,
	broonie, gregkh, alan, arnd

Hello Jonathan,

On Monday 17 October 2011 15:16:16 you wrote:
> 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          |    3 +
>  drivers/iio/Kconfig       |   11 +
>  drivers/iio/Makefile      |    6 +
>  drivers/iio/iio.c         |  591
> +++++++++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h   | 
> 250 +++++++++++++++++++
>  include/linux/iio/sysfs.h |   68 +++++
>  7 files changed, 931 insertions(+), 0 deletions(-)
> 
> [...]
> 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.

I don't know much about iio so I want to read about it. But where does 
Documentation/iio come from?

Regards,
Alexander

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

* [PATCH 1/6] IIO: Core sysfs only support.
  2011-10-17 13:16 [PATCH 0/6] " Jonathan Cameron
@ 2011-10-17 13:16 ` Jonathan Cameron
  2011-10-17 13:42   ` Alexander Stein
  2011-10-17 16:27   ` Jonathan Cameron
  0 siblings, 2 replies; 21+ messages in thread
From: Jonathan Cameron @ 2011-10-17 13:16 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: guenter.roeck, khali, dmitry.torokhov, broonie, gregkh, alan,
	arnd, 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          |    3 +
 drivers/iio/Kconfig       |   11 +
 drivers/iio/Makefile      |    6 +
 drivers/iio/iio.c         |  591 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/iio.h   |  250 +++++++++++++++++++
 include/linux/iio/sysfs.h |   68 +++++
 7 files changed, 931 insertions(+), 0 deletions(-)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index ce3c35f..f90101e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -132,4 +132,6 @@ source "drivers/virt/Kconfig"
 
 source "drivers/hv/Kconfig"
 
+source "drivers/iio/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index ef693cf..df39628 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -127,5 +127,8 @@ obj-$(CONFIG_IOMMU_SUPPORT)	+= iommu/
 
 # Virtualization drivers
 obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
+
 obj-$(CONFIG_HYPERV)		+= hv/
 
+obj-$(CONFIG_IIO)		+= iio/
+
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..326b09b
--- /dev/null
+++ b/drivers/iio/iio.c
@@ -0,0 +1,591 @@
+/* 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_ANGL_VEL] = "anglvel",
+	[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_INFRARED] = "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);
+}
+
+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..8066c8a
--- /dev/null
+++ b/include/linux/iio/iio.h
@@ -0,0 +1,250 @@
+/*
+ * 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_ANGL_VEL,
+	IIO_MAGN,
+	IIO_LIGHT,
+	IIO_INTENSITY,
+	IIO_PROXIMITY,
+	IIO_TEMP,
+	IIO_INCLI,
+	IIO_ROT,
+	IIO_ANGL,
+	IIO_TIMESTAMP,
+};
+
+enum iio_modifier {
+	IIO_NO_MOD,
+	IIO_MOD_X,
+	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_INFRARED,
+};
+
+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:		Description of data format.
+ * @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 scan_type - description of the data format
+	 * @sign:	Set if signed value
+	 * @realbits:	Number of valid bits of data
+	 * @shift:	Shift right by this before masking out realbits.
+	 */
+	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;
+};
+
+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
+ * @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
+ * @indio_dev:		Device structure filled by the device driver
+ **/
+int iio_device_register(struct iio_dev *indio_dev);
+
+/**
+ * iio_device_unregister() - unregister a device from the IIO subsystem
+ * @indio_dev:		Device structure representing the device.
+ **/
+void iio_device_unregister(struct iio_dev *indio_dev);
+
+/**
+ * iio_put_device() - reference counted deallocation of struct device
+ * @indio_dev: the iio_device containing the device
+ **/
+static inline void iio_put_device(struct iio_dev *indio_dev)
+{
+	if (indio_dev)
+		put_device(&indio_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.7


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

end of thread, other threads:[~2012-02-01 15:21 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-11-07 14:52 [PATCH 0/6 V2] IIO: Out of staging step 1: The core jic23
2011-11-07 14:52 ` [PATCH 1/6] IIO: Core sysfs only support jic23
2011-11-11 10:40   ` Michael Hennerich
2012-02-01 15:20   ` Maxime Ripard
2011-11-07 14:52 ` [PATCH 2/6] IIO:ADC: max1363 initial import jic23
2011-11-07 14:52 ` [PATCH 3/6] IIO:ADC:ad799x " jic23
2011-11-08 13:07   ` Lars-Peter Clausen
2011-11-08 13:35     ` Jonathan Cameron
2011-11-07 14:52 ` [PATCH 4/6] IIO:light:tsl2563 initial move out of staging jic23
2011-11-07 14:52 ` [PATCH 5/6] IIO:imu:adis16400 partial move from staging jic23
2011-11-11 10:41   ` Michael Hennerich
2011-11-07 14:52 ` [PATCH 6/6] IIO: ABI documetation jic23
2011-11-11 10:41   ` Michael Hennerich
2011-11-08 13:32 ` [PATCH 0/6 V2] IIO: Out of staging step 1: The core Lars-Peter Clausen
2011-11-08 14:23   ` Jonathan Cameron
2011-11-08 14:53     ` Lars-Peter Clausen
2011-11-08 15:29       ` Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2011-10-17 13:16 [PATCH 0/6] " Jonathan Cameron
2011-10-17 13:16 ` [PATCH 1/6] IIO: Core sysfs only support Jonathan Cameron
2011-10-17 13:42   ` Alexander Stein
2011-10-17 13:48     ` Jonathan Cameron
2011-10-17 16:27   ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).