All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] IIO: Out of staging step 1. The core.
@ 2011-08-30 16:18 Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 1/4] IIO: Core sysfs only support Jonathan Cameron
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Jonathan Cameron @ 2011-08-30 16:18 UTC (permalink / raw)
  To: linux-iio; +Cc: Michael.Hennerich, manuel.stahl, Jonathan Cameron

Hi All,

I'd like to make a good impression when posting this more widely,
hence I'm posting it here first. Please comment on both the code
and the text of this explanatory email!  Are there other things
I should put in here?  Clearly the set is currently missing
documentation which will need doing before this goes to lkml etc.

Also note that the code has to sit on a couple of namespace moves
to avoid a clash with the staging tree.  These are in the
outofstaging branch of iio-blue.git.

Dear All,

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 subystems (hwmon and input) do not meet them. Focused
subsytems targetting just one type of sensor have been rejected as
a concept (see ALS proposal).

Requirements of IIO.

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.

This is an area in which hwmon excels. Everything is available via sysfs
and thats exactly what we allow drivers to provide.

2 - High performance data route:

We want a path via which data can get to userspace 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 (obvously 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 actually convertor 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.  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 amount of processing of data to occur in the
kernel possible. Its often best to leave this to userspace 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 metadata 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.

For IIO we have decided to try and create an event code (litterally 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. freefall is typically X&Y&Z all less than alpha)

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

4) Which direction the threshold is crossed in.

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. Afterall 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 (see possible future work).

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 do 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 actualy a 'data has been captured now'
signal (dataready).

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 userspace to trigger simultaneous capture from a number of
   devices.
c) Standalone triggers.
   General interrupts from whereever 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.




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


So what do we have here?

Our merge plan (order may change) has the following elements

1) Registration core and sysfs interface
2) Some stripped down drivers using the above.

3) Event interface
4) Extend drivers in 2 to provide event interfaces.

5) Buffered reading interface
6) Extend drivers from 4 to provide buffered reading.
7) Pull in hardware ring buffer device examples

8) Futher sets pulling across a few drivers at time (keeping review
manageable!)

Drivers may be added to the subsystem at any stage (once support for
what they use is available).

This patchset (1-2) only covers the simplest type of driver supported
by IIO, that with only a sysfs based interface.  Please review it
with that in mind!

Acknowledgements

A lot of thanks is due to various people during the IIO development.
Most of it is born out by commit messages but I'd particularly like to
thank Michael Hennerich for massive contributions and Arnd Bergmann for
taking the time to not only suggest what we should do differently but
to explain it to me and look at the results as they occured.

Jonathan Cameron (4):
  IIO: Core sysfs only support.
  IIO:ADC: max1363 initial import.
  IIO:light:tsl2563 initial move out of staging.
  IIO:imu:adis16400 partial move from staging.

 drivers/Kconfig                  |    2 +
 drivers/Makefile                 |    2 +
 drivers/iio/Kconfig              |   19 +
 drivers/iio/Makefile             |   10 +
 drivers/iio/adc/Kconfig          |   17 +
 drivers/iio/adc/Makefile         |    6 +
 drivers/iio/adc/max1363.h        |  148 ++++++
 drivers/iio/adc/max1363_core.c   | 1000 ++++++++++++++++++++++++++++++++++++
 drivers/iio/iio.c                |  593 +++++++++++++++++++++
 drivers/iio/imu/Kconfig          |   10 +
 drivers/iio/imu/Makefile         |    6 +
 drivers/iio/imu/adis16400.h      |  180 +++++++
 drivers/iio/imu/adis16400_core.c | 1057 ++++++++++++++++++++++++++++++++++++++
 drivers/iio/light/Kconfig        |   14 +
 drivers/iio/light/Makefile       |    6 +
 drivers/iio/light/tsl2563.c      |  653 +++++++++++++++++++++++
 include/linux/iio/iio.h          |  243 +++++++++
 include/linux/iio/sysfs.h        |   68 +++
 include/linux/iio/tsl2563.h      |    9 +
 19 files changed, 4043 insertions(+), 0 deletions(-)
 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/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/iio.h
 create mode 100644 include/linux/iio/sysfs.h
 create mode 100644 include/linux/iio/tsl2563.h

-- 
1.7.3.4


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

* [PATCH 1/4] IIO: Core sysfs only support.
  2011-08-30 16:18 [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
@ 2011-08-30 16:18 ` Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 2/4] IIO:ADC: max1363 initial import Jonathan Cameron
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2011-08-30 16:18 UTC (permalink / raw)
  To: linux-iio; +Cc: Michael.Hennerich, manuel.stahl, Jonathan Cameron

Add support for simple sysfs only interfaces.

Bulk of patch is concerned with taking struct iio_chan_spec
arrays and generating all the relevant interfaces from them.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/Kconfig           |    2 +
 drivers/Makefile          |    2 +
 drivers/iio/Kconfig       |   11 +
 drivers/iio/Makefile      |    6 +
 drivers/iio/iio.c         |  593 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/iio.h   |  243 ++++++++++++++++++
 include/linux/iio/sysfs.h |   68 +++++
 7 files changed, 925 insertions(+), 0 deletions(-)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 95b9e7e..331b3d5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -130,4 +130,6 @@ source "drivers/iommu/Kconfig"
 
 source "drivers/virt/Kconfig"
 
+source "drivers/iio/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7fa433a..03c38ed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -127,3 +127,5 @@ obj-$(CONFIG_IOMMU_SUPPORT)	+= iommu/
 
 # Virtualization drivers
 obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
+
+obj-$(CONFIG_IIO)		+= iio/
\ No newline at end of file
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
new file mode 100644
index 0000000..5d9a97d
--- /dev/null
+++ b/drivers/iio/Kconfig
@@ -0,0 +1,11 @@
+#
+# Industrial I/O subsystem
+#
+
+menuconfig IIO
+	tristate "Industrial I/O support"
+	depends on GENERIC_HARDIRQS
+	help
+	  The Industrial input / output subsystem provides a unified
+	  framework for many different types of embedded sensor.
+	  See Documentation/iio for more information.
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
new file mode 100644
index 0000000..733846b
--- /dev/null
+++ b/drivers/iio/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Industrial I/O subsystem
+#
+
+obj-$(CONFIG_IIO) += iio.o
+industrialio-y := core.o
diff --git a/drivers/iio/iio.c b/drivers/iio/iio.c
new file mode 100644
index 0000000..ec046d8
--- /dev/null
+++ b/drivers/iio/iio.c
@@ -0,0 +1,593 @@
+/* The industrial I/O core
+ *
+ * Copyright (c) 2008-2011 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Based on elements of hwmon and input subsystems.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+static DEFINE_IDA(iio_ida);
+
+static struct bus_type iio_bus_type = {
+	.name = "iio",
+};
+
+static const char * const iio_data_type_name[] = {
+	[IIO_RAW] = "raw",
+	[IIO_PROCESSED] = "input",
+};
+
+static const char * const iio_chan_type_name_spec[] = {
+	[IIO_VOLTAGE] = "voltage",
+	[IIO_CURRENT] = "current",
+	[IIO_POWER] = "power",
+	[IIO_ACCEL] = "accel",
+	[IIO_GYRO] = "gyro",
+	[IIO_MAGN] = "magn",
+	[IIO_LIGHT] = "illuminance",
+	[IIO_INTENSITY] = "intensity",
+	[IIO_PROXIMITY] = "proximity",
+	[IIO_TEMP] = "temp",
+	[IIO_INCLI] = "incli",
+	[IIO_ROT] = "rot",
+	[IIO_ANGL] = "angl",
+	[IIO_TIMESTAMP] = "timestamp",
+};
+
+static const char * const iio_direction_name[] = {
+	[IIO_IN] = "in",
+	[IIO_OUT] = "out",
+};
+
+static const char * const iio_modifier_names[] = {
+	[IIO_MOD_X] = "x",
+	[IIO_MOD_Y] = "y",
+	[IIO_MOD_Z] = "z",
+	[IIO_MOD_LIGHT_BOTH] = "both",
+	[IIO_MOD_LIGHT_IR] = "ir",
+};
+
+/* relies on pairs of these shared then separate */
+static const char * const iio_chan_info_postfix[] = {
+	[IIO_CHAN_INFO_SCALE_SHARED/2] = "scale",
+	[IIO_CHAN_INFO_OFFSET_SHARED/2] = "offset",
+	[IIO_CHAN_INFO_CALIBSCALE_SHARED/2] = "calibscale",
+	[IIO_CHAN_INFO_CALIBBIAS_SHARED/2] = "calibbias",
+	[IIO_CHAN_INFO_PEAK_SHARED/2] = "peak_raw",
+	[IIO_CHAN_INFO_PEAK_SCALE_SHARED/2] = "peak_scale",
+	[IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED/2]
+	= "quadrature_correction_raw",
+};
+
+static void iio_device_free_read_attr(struct iio_dev *indio_dev,
+						 struct iio_dev_attr *p)
+{
+	kfree(p->dev_attr.attr.name);
+	kfree(p);
+}
+
+static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
+{
+
+	struct iio_dev_attr *p, *n;
+
+	list_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) {
+		list_del(&p->l);
+		iio_device_free_read_attr(indio_dev, p);
+	}
+	kfree(indio_dev->chan_attr_group.attrs);
+}
+
+static void iio_dev_release(struct device *device)
+{
+	struct iio_dev *indio_dev = container_of(device, struct iio_dev, dev);
+	iio_device_unregister_sysfs(indio_dev);
+	ida_simple_remove(&iio_ida, indio_dev->id);
+	kfree(indio_dev);
+}
+
+static struct device_type iio_dev_type = {
+	.name = "iio_device",
+	.release = iio_dev_release,
+};
+
+struct iio_dev *iio_device_allocate(int sizeof_priv)
+{
+	struct iio_dev *dev;
+	size_t alloc_size;
+
+	alloc_size = sizeof(struct iio_dev);
+	if (sizeof_priv) {
+		alloc_size = ALIGN(alloc_size, IIO_ALIGN);
+		alloc_size += sizeof_priv;
+	}
+	/* ensure cacheline alignment of whole construct */
+	alloc_size += IIO_ALIGN - 1;
+
+	dev = kzalloc(alloc_size, GFP_KERNEL);
+
+	if (dev) {
+		dev->dev.type = &iio_dev_type;
+		dev->dev.bus = &iio_bus_type;
+		device_initialize(&dev->dev);
+		dev_set_drvdata(&dev->dev, (void *)dev);
+		mutex_init(&dev->mlock);
+	}
+
+	return dev;
+}
+EXPORT_SYMBOL_GPL(iio_device_allocate);
+
+void iio_device_free(struct iio_dev *dev)
+{
+	if (dev)
+		iio_put_device(dev);
+}
+EXPORT_SYMBOL_GPL(iio_device_free);
+
+ssize_t __iio_read_const_attr(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return sprintf(buf, "%s\n",
+		       container_of(attr,
+				    struct iio_const_attr,
+				    dev_attr)->string);
+}
+EXPORT_SYMBOL_GPL(__iio_read_const_attr);
+
+static ssize_t iio_read_channel_info(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int val, val2;
+	int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
+					    &val, &val2, this_attr->address);
+
+	if (ret < 0)
+		return ret;
+
+	if (ret == IIO_VAL_INT)
+		return sprintf(buf, "%d\n", val);
+	else if (ret == IIO_VAL_INT_PLUS_MICRO) {
+		if (val2 < 0)
+			return sprintf(buf, "-%d.%06u\n", val, -val2);
+		else
+			return sprintf(buf, "%d.%06u\n", val, val2);
+	} else if (ret == IIO_VAL_INT_PLUS_NANO) {
+		if (val2 < 0)
+			return sprintf(buf, "-%d.%09u\n", val, -val2);
+		else
+			return sprintf(buf, "%d.%09u\n", val, val2);
+	} else
+		return 0;
+}
+
+static ssize_t iio_write_channel_info(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret, integer = 0, fract = 0, fract_mult = 100000;
+	bool integer_part = true, negative = false;
+
+	/* Assumes decimal - precision based on number of digits */
+	if (!indio_dev->info->write_raw)
+		return -EINVAL;
+
+	if (indio_dev->info->write_raw_get_fmt)
+		switch (indio_dev->info->write_raw_get_fmt(indio_dev,
+			this_attr->c, this_attr->address)) {
+		case IIO_VAL_INT_PLUS_MICRO:
+			fract_mult = 100000;
+			break;
+		case IIO_VAL_INT_PLUS_NANO:
+			fract_mult = 100000000;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+	if (buf[0] == '-') {
+		negative = true;
+		buf++;
+	}
+
+	while (*buf) {
+		if ('0' <= *buf && *buf <= '9') {
+			if (integer_part)
+				integer = integer*10 + *buf - '0';
+			else {
+				fract += fract_mult*(*buf - '0');
+				if (fract_mult == 1)
+					break;
+				fract_mult /= 10;
+			}
+		} else if (*buf == '\n') {
+			if (*(buf + 1) == '\0')
+				break;
+			else
+				return -EINVAL;
+		} else if (*buf == '.') {
+			integer_part = false;
+		} else {
+			return -EINVAL;
+		}
+		buf++;
+	}
+	if (negative) {
+		if (integer)
+			integer = -integer;
+		else
+			fract = -fract;
+	}
+
+	ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
+					 integer, fract, this_attr->address);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static
+int __iio_device_attr_init(struct device_attribute *dev_attr,
+			   const char *postfix,
+			   struct iio_chan_spec const *chan,
+			   ssize_t (*readfunc)(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf),
+			   ssize_t (*writefunc)(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf,
+						size_t len),
+			   bool generic)
+{
+	int ret;
+	char *name_format, *full_postfix;
+	sysfs_attr_init(&dev_attr->attr);
+
+	/* Build up postfix of <extend_name>_<modifier>_postfix */
+	if (chan->modified) {
+		if (chan->extend_name)
+			full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
+						 iio_modifier_names[chan
+								    ->channel2],
+						 chan->extend_name,
+						 postfix);
+		else
+			full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
+						 iio_modifier_names[chan
+								    ->channel2],
+						 postfix);
+	} else {
+		if (chan->extend_name == NULL)
+			full_postfix = kstrdup(postfix, GFP_KERNEL);
+		else
+			full_postfix = kasprintf(GFP_KERNEL,
+						 "%s_%s",
+						 chan->extend_name,
+						 postfix);
+	}
+	if (full_postfix == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	if (chan->differential) { /* Differential  can not have modifier */
+		if (generic)
+			name_format
+				= kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
+					    iio_direction_name[chan->output],
+					    iio_chan_type_name_spec[chan->type],
+					    iio_chan_type_name_spec[chan->type],
+					    full_postfix);
+		else if (chan->indexed)
+			name_format
+				= kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s",
+					    iio_direction_name[chan->output],
+					    iio_chan_type_name_spec[chan->type],
+					    chan->channel,
+					    iio_chan_type_name_spec[chan->type],
+					    chan->channel2,
+					    full_postfix);
+		else {
+			WARN_ON("Differential channels must be indexed\n");
+			ret = -EINVAL;
+			goto error_free_full_postfix;
+		}
+	} else { /* Single ended */
+		if (generic)
+			name_format
+				= kasprintf(GFP_KERNEL, "%s_%s_%s",
+					    iio_direction_name[chan->output],
+					    iio_chan_type_name_spec[chan->type],
+					    full_postfix);
+		else if (chan->indexed)
+			name_format
+				= kasprintf(GFP_KERNEL, "%s_%s%d_%s",
+					    iio_direction_name[chan->output],
+					    iio_chan_type_name_spec[chan->type],
+					    chan->channel,
+					    full_postfix);
+		else
+			name_format
+				= kasprintf(GFP_KERNEL, "%s_%s_%s",
+					    iio_direction_name[chan->output],
+					    iio_chan_type_name_spec[chan->type],
+					    full_postfix);
+	}
+	if (name_format == NULL) {
+		ret = -ENOMEM;
+		goto error_free_full_postfix;
+	}
+	dev_attr->attr.name = kasprintf(GFP_KERNEL,
+					name_format,
+					chan->channel,
+					chan->channel2);
+	if (dev_attr->attr.name == NULL) {
+		ret = -ENOMEM;
+		goto error_free_name_format;
+	}
+
+	if (readfunc) {
+		dev_attr->attr.mode |= S_IRUGO;
+		dev_attr->show = readfunc;
+	}
+
+	if (writefunc) {
+		dev_attr->attr.mode |= S_IWUSR;
+		dev_attr->store = writefunc;
+	}
+	kfree(name_format);
+	kfree(full_postfix);
+
+	return 0;
+
+error_free_name_format:
+	kfree(name_format);
+error_free_full_postfix:
+	kfree(full_postfix);
+error_ret:
+	return ret;
+}
+
+static void __iio_device_attr_deinit(struct device_attribute *dev_attr)
+{
+	kfree(dev_attr->attr.name);
+}
+
+static
+int __iio_add_chan_devattr(const char *postfix,
+			   struct iio_chan_spec const *chan,
+			   ssize_t (*readfunc)(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf),
+			   ssize_t (*writefunc)(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf,
+						size_t len),
+			   u64 mask,
+			   bool generic,
+			   struct device *dev,
+			   struct list_head *attr_list)
+{
+	int ret;
+	struct iio_dev_attr *iio_attr, *t;
+
+	iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL);
+	if (iio_attr == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	ret = __iio_device_attr_init(&iio_attr->dev_attr,
+				     postfix, chan,
+				     readfunc, writefunc, generic);
+	if (ret)
+		goto error_iio_dev_attr_free;
+	iio_attr->c = chan;
+	iio_attr->address = mask;
+	list_for_each_entry(t, attr_list, l)
+		if (strcmp(t->dev_attr.attr.name,
+			   iio_attr->dev_attr.attr.name) == 0) {
+			if (!generic)
+				dev_err(dev, "tried to double register : %s\n",
+					t->dev_attr.attr.name);
+			ret = -EBUSY;
+			goto error_device_attr_deinit;
+		}
+
+	list_add(&iio_attr->l, attr_list);
+
+	return 0;
+
+error_device_attr_deinit:
+	__iio_device_attr_deinit(&iio_attr->dev_attr);
+error_iio_dev_attr_free:
+	kfree(iio_attr);
+error_ret:
+	return ret;
+}
+
+static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
+					struct iio_chan_spec const *chan)
+{
+	int ret, i, attrcount = 0;
+
+	if (chan->channel < 0)
+		return 0;
+	ret = __iio_add_chan_devattr(iio_data_type_name[chan->processed_val],
+				     chan,
+				     &iio_read_channel_info,
+				     (chan->output ?
+				      &iio_write_channel_info : NULL),
+				     0,
+				     0,
+				     &indio_dev->dev,
+				     &indio_dev->channel_attr_list);
+	if (ret)
+		goto error_ret;
+
+	for_each_set_bit(i, &chan->info_mask,
+			 ARRAY_SIZE(iio_chan_info_postfix)*2) {
+		ret = __iio_add_chan_devattr(iio_chan_info_postfix[i/2],
+					     chan,
+					     &iio_read_channel_info,
+					     &iio_write_channel_info,
+					     (1 << i),
+					     !(i%2),
+					     &indio_dev->dev,
+					     &indio_dev->channel_attr_list);
+		if (ret == -EBUSY && (i%2 == 0)) {
+			ret = 0;
+			continue;
+		}
+		if (ret < 0)
+			goto error_ret;
+		attrcount++;
+	}
+	ret = attrcount;
+error_ret:
+	return ret;
+}
+
+static ssize_t iio_show_dev_name(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	return sprintf(buf, "%s\n", indio_dev->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
+
+static int iio_device_register_sysfs(struct iio_dev *indio_dev)
+{
+	int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
+	struct iio_dev_attr *p, *n;
+	struct attribute **attr;
+
+	/* First count elements in any existing group */
+	if (indio_dev->info->attrs) {
+		attr = indio_dev->info->attrs->attrs;
+		while (*attr++ != NULL)
+			attrcount_orig++;
+	}
+	attrcount = attrcount_orig;
+
+	INIT_LIST_HEAD(&indio_dev->channel_attr_list);
+	if (indio_dev->channels)
+		for (i = 0; i < indio_dev->num_channels; i++) {
+			ret = iio_device_add_channel_sysfs(indio_dev,
+							   &indio_dev
+							   ->channels[i]);
+			if (ret < 0)
+				goto error_clear_attrs;
+			attrcount += ret;
+		}
+	if (indio_dev->name)
+		attrcount++;
+
+	indio_dev->chan_attr_group.attrs
+		= kzalloc(sizeof(indio_dev->chan_attr_group.attrs[0])*
+			  (attrcount + 1),
+			  GFP_KERNEL);
+	if (indio_dev->chan_attr_group.attrs == NULL) {
+		ret = -ENOMEM;
+		goto error_clear_attrs;
+	}
+	/* Copy across original attributes */
+	if (indio_dev->info->attrs)
+		memcpy(indio_dev->chan_attr_group.attrs,
+		       indio_dev->info->attrs->attrs,
+		       sizeof(indio_dev->chan_attr_group.attrs[0])
+		       *attrcount_orig);
+	attrn = attrcount_orig;
+	/* Add all elements from the list. */
+	list_for_each_entry(p, &indio_dev->channel_attr_list, l)
+		indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
+	if (indio_dev->name)
+		indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
+
+	indio_dev->groups[indio_dev->groupcounter++] =
+		&indio_dev->chan_attr_group;
+
+	return 0;
+
+error_clear_attrs:
+	list_for_each_entry_safe(p, n,
+				 &indio_dev->channel_attr_list, l) {
+		list_del(&p->l);
+		iio_device_free_read_attr(indio_dev, p);
+	}
+
+	return ret;
+}
+
+int iio_device_register(struct iio_dev *indio_dev)
+{
+	int ret;
+
+	indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
+	if (indio_dev->id < 0) {
+		ret = indio_dev->id;
+		goto error_ret;
+	}
+
+	dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id);
+
+	ret = iio_device_register_sysfs(indio_dev);
+	if (ret) {
+		dev_err(indio_dev->dev.parent,
+			"Failed to register sysfs interfaces\n");
+		goto error_free_ida;
+	}
+	ret = device_add(&indio_dev->dev);
+	if (ret)
+		goto error_free_sysfs;
+
+	return 0;
+
+error_free_sysfs:
+	iio_device_unregister_sysfs(indio_dev);
+error_free_ida:
+	ida_simple_remove(&iio_ida, indio_dev->id);
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *indio_dev)
+{
+	device_unregister(&indio_dev->dev);
+}
+EXPORT_SYMBOL(iio_device_unregister);
+
+static int __init iio_init(void)
+{
+	return bus_register(&iio_bus_type);
+}
+subsys_initcall(iio_init);
+
+static void __exit iio_exit(void)
+{
+	bus_unregister(&iio_bus_type);
+}
+module_exit(iio_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Industrial I/O core");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
new file mode 100644
index 0000000..8b94db6
--- /dev/null
+++ b/include/linux/iio/iio.h
@@ -0,0 +1,243 @@
+/*
+ * The industrial I/O core
+ *
+ * Copyright (c) 2008-2011 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/klist.h>
+#include <linux/device.h>
+
+#ifndef _IIO_H_
+#define _IIO_H_
+
+/* Minimum alignment of priv within iio_dev */
+#define IIO_ALIGN L1_CACHE_BYTES
+
+enum iio_data_type {
+	IIO_RAW,
+	IIO_PROCESSED,
+};
+
+enum iio_chan_type {
+	IIO_VOLTAGE,
+	IIO_CURRENT,
+	IIO_POWER,
+	IIO_CAPACITANCE,
+	IIO_ACCEL,
+	IIO_GYRO,
+	IIO_MAGN,
+	IIO_LIGHT,
+	IIO_INTENSITY,
+	IIO_PROXIMITY,
+	IIO_TEMP,
+	IIO_INCLI,
+	IIO_ROT,
+	IIO_ANGL,
+	IIO_TIMESTAMP,
+};
+
+enum iio_modifier {
+	IIO_MOD_X = 1,
+	IIO_MOD_Y,
+	IIO_MOD_Z,
+	IIO_MOD_X_AND_Y,
+	IIO_MOD_X_ANX_Z,
+	IIO_MOD_Y_AND_Z,
+	IIO_MOD_X_AND_Y_AND_Z,
+	IIO_MOD_X_OR_Y,
+	IIO_MOD_X_OR_Z,
+	IIO_MOD_Y_OR_Z,
+	IIO_MOD_X_OR_Y_OR_Z,
+	IIO_MOD_LIGHT_BOTH,
+	IIO_MOD_LIGHT_IR,
+};
+
+enum iio_chan_info_enum {
+	IIO_CHAN_INFO_SCALE_SHARED,
+	IIO_CHAN_INFO_SCALE_SEPARATE,
+	IIO_CHAN_INFO_OFFSET_SHARED,
+	IIO_CHAN_INFO_OFFSET_SEPARATE,
+	IIO_CHAN_INFO_CALIBSCALE_SHARED,
+	IIO_CHAN_INFO_CALIBSCALE_SEPARATE,
+	IIO_CHAN_INFO_CALIBBIAS_SHARED,
+	IIO_CHAN_INFO_CALIBBIAS_SEPARATE,
+	IIO_CHAN_INFO_PEAK_SHARED,
+	IIO_CHAN_INFO_PEAK_SEPARATE,
+	IIO_CHAN_INFO_PEAK_SCALE_SHARED,
+	IIO_CHAN_INFO_PEAK_SCALE_SEPARATE,
+	IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SHARED,
+	IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW_SEPARATE,
+};
+
+enum iio_direction {
+	IIO_IN,
+	IIO_OUT,
+};
+
+#define IIO_VAL_INT 1
+#define IIO_VAL_INT_PLUS_MICRO 2
+#define IIO_VAL_INT_PLUS_NANO 3
+
+/**
+ * struct iio_chan_spec - specification of a single channel
+ * @type:		What type of measurement is the channel making.
+ * @channel:		What number or name do we wish to asign the channel.
+ * @channel2:		If there is a second number for a differential
+ *			channel then this is it. If modified is set then the
+ *			value here specifies the modifier.
+ * @address:		Driver specific identifier.
+ * @realbits:		Number of valid bits of data
+ * @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.
+ * @sign:		Is channel signed data
+ */
+struct iio_chan_spec {
+	enum iio_chan_type	type;
+	int			channel;
+	int			channel2;
+	unsigned long		address;
+	u8			realbits;
+	long			info_mask;
+	char			*extend_name;
+	unsigned		processed_val:1;
+	unsigned		modified:1;
+	unsigned		indexed:1;
+	unsigned		output:1;
+	unsigned		differential:1;
+	unsigned		sign:1;
+};
+
+struct iio_dev;
+
+/**
+ * struct iio_info - constant information about device
+ * @driver_module:	module structure used to ensure correct
+ *			ownership of chrdevs etc
+ * @attrs:		general purpose device attributes
+ * @read_raw:		function to request a value from the device.
+ *			mask specifies which value. Note 0 means a reading of
+ *			the channel in question.  Return value will specify the
+ *			type of value returned by the device. val and val2 will
+ *			contain the elements making up the returned value.
+ * @write_raw:		function to write a value to the device.
+ *			Parameters are the same as for read_raw.
+ * @write_raw_get_fmt:	callback function to query the expected
+ *			format/precision. If not set by the driver, write_raw
+ *			returns IIO_VAL_INT_PLUS_MICRO.
+ **/
+struct iio_info {
+	struct module			*driver_module;
+	const struct attribute_group	*attrs;
+
+	int (*read_raw)(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *chan,
+			int *val,
+			int *val2,
+			long mask);
+
+	int (*write_raw)(struct iio_dev *indio_dev,
+			 struct iio_chan_spec const *chan,
+			 int val,
+			 int val2,
+			 long mask);
+
+	int (*write_raw_get_fmt)(struct iio_dev *indio_dev,
+			 struct iio_chan_spec const *chan,
+			 long mask);
+};
+
+/**
+ * struct iio_dev - industrial I/O device
+ * @id:			[INTERN] used to identify device internally
+ * @dev:		[DRIVER] device structure, should be assigned a parent
+ *			and owner
+ * @mlock:		[INTERN] lock used to prevent simultaneous device state
+ *			changes
+ * @available_scan_masks: [DRIVER] optional array of allowed bitmasks
+ * @trig:		[INTERN] current device trigger (ring buffer modes)
+ * @channels:		[DRIVER] channel specification structure table
+ * @num_channels:	[DRIVER] number of chanels specified in @channels
+ * @channel_attr_list:	[INTERN] keep track of automatically created channel
+ *			attributes
+ * @chan_attr_group:	[INTERN] group for all attrs in base directory
+ * @name:		[DRIVER] name of the device
+ * @info:		[DRIVER] callbacks and constant info from driver
+ * @groups:		[INTERN] attribute groups
+ * @groupcounter:	[INTERN] index of next attribute group
+ **/
+struct iio_dev {
+	int				id;
+	struct device			dev;
+	struct mutex			mlock;
+	unsigned long			*available_scan_masks;
+	struct iio_chan_spec const	*channels;
+	int				num_channels;
+
+	struct list_head		channel_attr_list;
+	struct attribute_group		chan_attr_group;
+	const char			*name;
+	const struct iio_info		*info;
+#define IIO_MAX_GROUPS 1
+	const struct attribute_group	*groups[IIO_MAX_GROUPS + 1];
+	int				groupcounter;
+};
+
+/**
+ * iio_device_allocate() - allocate an iio_dev from a driver
+ * @sizeof_priv:	Space to allocate for private structure.
+ **/
+struct iio_dev *iio_device_allocate(int sizeof_priv);
+
+static inline void *iio_priv(const struct iio_dev *dev)
+{
+	return (char *)dev + ALIGN(sizeof(struct iio_dev), IIO_ALIGN);
+}
+
+/**
+ * iio_device_free() - free an iio_dev from a driver
+ * @dev: the iio_dev associated with the device
+ **/
+void iio_device_free(struct iio_dev *dev);
+
+/**
+ * iio_device_register() - register a device with the IIO subsystem
+ * @dev_info:		Device structure filled by the device driver
+ **/
+int iio_device_register(struct iio_dev *dev_info);
+
+/**
+ * iio_device_unregister() - unregister a device from the IIO subsystem
+ * @dev_info:		Device structure representing the device.
+ **/
+void iio_device_unregister(struct iio_dev *dev_info);
+
+/**
+ * iio_put_device() - reference counted deallocation of struct device
+ * @dev: the iio_device containing the device
+ **/
+static inline void iio_put_device(struct iio_dev *dev)
+{
+	if (dev)
+		put_device(&dev->dev);
+};
+
+#endif /* _IIO_H_ */
diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h
new file mode 100644
index 0000000..c6735bf
--- /dev/null
+++ b/include/linux/iio/sysfs.h
@@ -0,0 +1,68 @@
+/*
+ * The industrial I/O core
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * General attributes
+ */
+
+#include <linux/klist.h>
+#include <linux/device.h>
+
+#ifndef _IIO_SYSFS_H_
+#define _IIO_SYSFS_H_
+
+struct iio_chan_spec;
+
+/**
+ * struct iio_dev_attr - iio specific device attribute
+ * @dev_attr:	underlying device attribute
+ * @address:	associated register address
+ * @l:		list head for maintaining list of dynamically created attrs
+ * @c:		channel spec for channel with which attr is associated if any
+ */
+struct iio_dev_attr {
+	struct device_attribute dev_attr;
+	int address;
+	struct list_head l;
+	struct iio_chan_spec const *c;
+};
+
+#define to_iio_dev_attr(_dev_attr)				\
+	container_of(_dev_attr, struct iio_dev_attr, dev_attr)
+
+#define IIO_ATTR(_name, _mode, _show, _store, _addr)		\
+	{ .dev_attr = __ATTR(_name, _mode, _show, _store),	\
+	  .address = _addr }
+
+#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr)	\
+	struct iio_dev_attr iio_dev_attr_##_name		\
+	= IIO_ATTR(_name, _mode, _show, _store, _addr)
+
+/**
+ * struct iio_const_attr - constant device specific attribute
+ *                         often used for things like available modes
+ * @string:	attribute string
+ * @dev_attr:	underlying device attribute
+ */
+struct iio_const_attr {
+	const char *string;
+	struct device_attribute dev_attr;
+};
+
+#define to_iio_const_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_const_attr, dev_attr)
+
+ssize_t __iio_read_const_attr(struct device *dev,
+			      struct device_attribute *attr,
+			      char *len);
+
+#define IIO_CONST_ATTR(_name, _string)					\
+	struct iio_const_attr iio_const_attr_##_name			\
+	= { .string = _string,						\
+	    .dev_attr = __ATTR(_name, S_IRUGO, __iio_read_const_attr, NULL) }
+#endif /* _IIO_SYSFS_H_ */
-- 
1.7.3.4

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

* [PATCH 2/4] IIO:ADC: max1363 initial import.
  2011-08-30 16:18 [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 1/4] IIO: Core sysfs only support Jonathan Cameron
@ 2011-08-30 16:18 ` Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 3/4] IIO:light:tsl2563 initial move out of staging Jonathan Cameron
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2011-08-30 16:18 UTC (permalink / raw)
  To: linux-iio; +Cc: Michael.Hennerich, manuel.stahl, Jonathan Cameron

Provides basic channel reading via sysfs.
For what is coming in this driver see the version still
in staging.

A couple of macros take more parameters than they use to reduce churn when
driver is updated to include events and buffer support.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
 drivers/iio/Kconfig            |    6 +
 drivers/iio/Makefile           |    2 +
 drivers/iio/adc/Kconfig        |   17 +
 drivers/iio/adc/Makefile       |    6 +
 drivers/iio/adc/max1363.h      |  148 ++++++
 drivers/iio/adc/max1363_core.c | 1000 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1179 insertions(+), 0 deletions(-)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 5d9a97d..cebe21f 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -9,3 +9,9 @@ menuconfig IIO
 	  The Industrial input / output subsystem provides a unified
 	  framework for many different types of embedded sensor.
 	  See Documentation/iio for more information.
+
+if IIO
+
+source "drivers/iio/adc/Kconfig"
+
+endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 733846b..42d6d12 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -4,3 +4,5 @@
 
 obj-$(CONFIG_IIO) += iio.o
 industrialio-y := core.o
+
+obj-y += adc/
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
new file mode 100644
index 0000000..24d08d5
--- /dev/null
+++ b/drivers/iio/adc/Kconfig
@@ -0,0 +1,17 @@
+#
+# IIO ADC drivers
+#
+comment "Analog to digital convertors (ADCs)"
+
+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.
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
new file mode 100644
index 0000000..8d6a7f9
--- /dev/null
+++ b/drivers/iio/adc/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO ADCs
+#
+
+iio_max1363-y := max1363_core.o
+obj-$(CONFIG_IIO_MAX1363) += iio_max1363.o
\ No newline at end of file
diff --git a/drivers/iio/adc/max1363.h b/drivers/iio/adc/max1363.h
new file mode 100644
index 0000000..5135f15
--- /dev/null
+++ b/drivers/iio/adc/max1363.h
@@ -0,0 +1,148 @@
+ /*
+  * Copyright (C) 2008-2011 Jonathan Cameron
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  */
+
+#include <linux/kernel.h>
+
+#ifndef _MAX1363_H_
+#define  _MAX1363_H_
+
+#define MAX1363_MAX_CHANNELS 25
+#define MAX1363_SETUP_BYTE(a) ((a) | 0x80)
+
+/* There is a fair bit more defined here than currently
+ * used, but the intention is to support everything these
+ * chips do in the long run */
+
+/* see data sheets */
+/* max1363 and max1236, max1237, max1238, max1239 */
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD	0x00
+#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF	0x20
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT	0x40
+#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT	0x60
+#define MAX1363_SETUP_POWER_UP_INT_REF		0x10
+#define MAX1363_SETUP_POWER_DOWN_INT_REF	0x00
+
+/* think about including max11600 etc - more settings */
+#define MAX1363_SETUP_EXT_CLOCK			0x08
+#define MAX1363_SETUP_INT_CLOCK			0x00
+#define MAX1363_SETUP_UNIPOLAR			0x00
+#define MAX1363_SETUP_BIPOLAR			0x04
+#define MAX1363_SETUP_RESET			0x00
+#define MAX1363_SETUP_NORESET			0x02
+/* max1363 only - though don't care on others.
+ * For now monitor modes are not implemented as the relevant
+ * line is not connected on my test board.
+ * The definitions are here as I intend to add this soon.
+ */
+#define MAX1363_SETUP_MONITOR_SETUP		0x01
+
+/* Specific to the max1363 */
+#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4))
+#define MAX1363_MON_INT_ENABLE			0x01
+
+/* defined for readability reasons */
+/* All chips */
+#define MAX1363_CONFIG_BYTE(a) ((a))
+
+#define MAX1363_CONFIG_SE			0x01
+#define MAX1363_CONFIG_DE			0x00
+#define MAX1363_CONFIG_SCAN_TO_CS		0x00
+#define MAX1363_CONFIG_SCAN_SINGLE_8		0x20
+#define MAX1363_CONFIG_SCAN_MONITOR_MODE	0x40
+#define MAX1363_CONFIG_SCAN_SINGLE_1		0x60
+/* max123{6-9} only */
+#define MAX1236_SCAN_MID_TO_CHANNEL		0x40
+
+/* max1363 only - merely part of channel selects or don't care for others*/
+#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18
+
+#define MAX1363_CHANNEL_SEL(a) ((a) << 1)
+
+/* max1363 strictly 0x06 - but doesn't matter */
+#define MAX1363_CHANNEL_SEL_MASK		0x1E
+#define MAX1363_SCAN_MASK			0x60
+#define MAX1363_SE_DE_MASK			0x01
+
+/**
+ * struct max1363_mode - scan mode information
+ * @conf:	The corresponding value of the configuration register
+ * @modemask:	Bit mask corresponding to channels enabled in this mode
+ */
+struct max1363_mode {
+	int8_t		conf;
+	DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS);
+};
+
+/* This must be maintained along side the max1363_mode_table in max1363_core */
+enum max1363_modes {
+	/* Single read of a single channel */
+	_s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+	/* Differential single read */
+	d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+	d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+	/* Scan to channel and mid to channel where overlapping */
+	s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6,
+	s6to7, s0to7, s6to8, s0to8, s6to9,
+	s0to9, s6to10, s0to10, s6to11, s0to11,
+	/* Differential scan to channel and mid to channel where overlapping */
+	d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9,
+	d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2,
+	d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8,
+	d7m6to11m10, d1m0to11m10,
+};
+
+struct iio_info;
+struct iio_chan_spec;
+
+/**
+ * struct max1363_chip_info - chip specifc information
+ * @name:		indentification string for chip
+ * @bits:		accuracy of the adc in bits
+ * @int_vref_mv:	the internal reference voltage
+ * @info:		iio core function callbacks structure
+ * @mode_list:		array of available scan modes
+ * @num_modes:		the number of scan modes available
+ * @default_mode:	the scan mode in which the chip starts up
+ * @channels:		channel specification
+ * @num_channels:	number of channels
+ */
+struct max1363_chip_info {
+	const struct iio_info		*info;
+	struct iio_chan_spec		*channels;
+	int				num_channels;
+	const enum max1363_modes	*mode_list;
+	enum max1363_modes		default_mode;
+	u16				int_vref_mv;
+	u8				num_modes;
+	u8				bits;
+};
+
+struct regulator;
+struct i2c_client;
+
+/**
+ * struct max1363_state - driver instance specific data
+ * @client:		i2c_client
+ * @setupbyte:		cache of current device setup byte
+ * @configbyte:		cache of current device config byte
+ * @chip_info:		chip model specific constants, available modes etc
+ * @current_mode:	the scan mode of this chip
+ * @requestedmask:	a valid requested set of channels
+ * @reg:		supply regulator
+ */
+struct max1363_state {
+	struct i2c_client		*client;
+	u8				setupbyte;
+	u8				configbyte;
+	const struct max1363_chip_info	*chip_info;
+	const struct max1363_mode	*current_mode;
+	u32				requestedmask;
+	struct regulator		*reg;
+};
+
+#endif /* _MAX1363_H_ */
diff --git a/drivers/iio/adc/max1363_core.c b/drivers/iio/adc/max1363_core.c
new file mode 100644
index 0000000..cb51bfb
--- /dev/null
+++ b/drivers/iio/adc/max1363_core.c
@@ -0,0 +1,1000 @@
+/*
+ * iio/adc/max1363.c
+ *
+ * Copyright (C) 2008-2011 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "max1363.h"
+
+#define MAX1363_MODE_SINGLE(_num, _mask) {				\
+		.conf = MAX1363_CHANNEL_SEL(_num)			\
+			| MAX1363_CONFIG_SCAN_SINGLE_1			\
+			| MAX1363_CONFIG_SE,				\
+			.modemask[0] = _mask,				\
+			}
+
+#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) {			\
+		.conf = MAX1363_CHANNEL_SEL(_num)			\
+			| MAX1363_CONFIG_SCAN_TO_CS			\
+			| MAX1363_CONFIG_SE,				\
+			.modemask[0] = _mask,				\
+			}
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) {		\
+		.conf = MAX1363_CHANNEL_SEL(_num)			\
+			| MAX1236_SCAN_MID_TO_CHANNEL			\
+			| MAX1363_CONFIG_SE,				\
+			.modemask[0] = _mask				\
+}
+
+#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) {			\
+		.conf = MAX1363_CHANNEL_SEL(_nump)			\
+			| MAX1363_CONFIG_SCAN_SINGLE_1			\
+			| MAX1363_CONFIG_DE,				\
+			.modemask[0] = _mask				\
+			}
+
+/* Can't think how to automate naming so specify for now */
+#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) {	\
+		.conf = MAX1363_CHANNEL_SEL(_num)			\
+			| MAX1363_CONFIG_SCAN_TO_CS			\
+			| MAX1363_CONFIG_DE,				\
+			.modemask[0] = _mask				\
+			}
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) {	\
+		.conf = MAX1363_CHANNEL_SEL(_num)			\
+			| MAX1236_SCAN_MID_TO_CHANNEL			\
+			| MAX1363_CONFIG_SE,				\
+			.modemask[0] = _mask				\
+}
+
+static const struct max1363_mode max1363_mode_table[] = {
+	/* All of the single channel options first */
+	MAX1363_MODE_SINGLE(0, 1 << 0),
+	MAX1363_MODE_SINGLE(1, 1 << 1),
+	MAX1363_MODE_SINGLE(2, 1 << 2),
+	MAX1363_MODE_SINGLE(3, 1 << 3),
+	MAX1363_MODE_SINGLE(4, 1 << 4),
+	MAX1363_MODE_SINGLE(5, 1 << 5),
+	MAX1363_MODE_SINGLE(6, 1 << 6),
+	MAX1363_MODE_SINGLE(7, 1 << 7),
+	MAX1363_MODE_SINGLE(8, 1 << 8),
+	MAX1363_MODE_SINGLE(9, 1 << 9),
+	MAX1363_MODE_SINGLE(10, 1 << 10),
+	MAX1363_MODE_SINGLE(11, 1 << 11),
+
+	MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12),
+	MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13),
+	MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14),
+	MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15),
+	MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16),
+	MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17),
+	MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18),
+	MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19),
+	MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20),
+	MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21),
+	MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22),
+	MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23),
+
+	/* The multichannel scans next */
+	MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003),
+	MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C),
+	MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F),
+	MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F),
+	MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F),
+	MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0),
+	MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0),
+	MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0),
+	MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0),
+	MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0),
+	MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF),
+
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000),
+};
+
+static int max1363_write_basic_config(struct i2c_client *client,
+				      unsigned char d1,
+				      unsigned char d2)
+{
+	u8 tx_buf[2] = {d1, d2};
+
+	return i2c_master_send(client, tx_buf, 2);
+}
+
+static int max1363_set_scan_mode(struct max1363_state *st)
+{
+	st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+			    | MAX1363_SCAN_MASK
+			    | MAX1363_SE_DE_MASK);
+	st->configbyte |= st->current_mode->conf;
+
+	return max1363_write_basic_config(st->client,
+					  st->setupbyte,
+					  st->configbyte);
+}
+
+static int max1363_read_single_chan(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan,
+				    int *val,
+				    long m)
+{
+	int ret = 0;
+	s32 data;
+	char rxbuf[2];
+	struct max1363_state *st = iio_priv(indio_dev);
+	struct i2c_client *client = st->client;
+
+	mutex_lock(&indio_dev->mlock);
+
+	/* Check to see if current scan mode is correct */
+	if (st->current_mode != &max1363_mode_table[chan->address]) {
+		/* Update scan mode if needed */
+		st->current_mode = &max1363_mode_table[chan->address];
+		ret = max1363_set_scan_mode(st);
+		if (ret < 0)
+			goto error_ret;
+	}
+	if (st->chip_info->bits != 8) {
+		/* Get reading */
+		data = i2c_master_recv(client, rxbuf, 2);
+		if (data < 0) {
+			ret = data;
+			goto error_ret;
+		}
+		data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8;
+	} else {
+		/* Get reading */
+		data = i2c_master_recv(client, rxbuf, 1);
+		if (data < 0) {
+			ret = data;
+			goto error_ret;
+		}
+		data = rxbuf[0];
+	}
+
+	*val = data;
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+
+}
+
+static int max1363_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long m)
+{
+	struct max1363_state *st = iio_priv(indio_dev);
+	int ret;
+	switch (m) {
+	case 0:
+		ret = max1363_read_single_chan(indio_dev, chan, val, m);
+		if (ret < 0)
+			return ret;
+		return IIO_VAL_INT;
+	case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+		if ((1 << (st->chip_info->bits + 1)) >
+		    st->chip_info->int_vref_mv) {
+			*val = 0;
+			*val2 = 500000;
+			return IIO_VAL_INT_PLUS_MICRO;
+		} else {
+			*val = (st->chip_info->int_vref_mv)
+				>> st->chip_info->bits;
+			return IIO_VAL_INT;
+		}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* Applies to max1363 */
+static const enum max1363_modes max1363_mode_list[] = {
+	_s0, _s1, _s2, _s3,
+	s0to1, s0to2, s0to3,
+	d0m1, d2m3, d1m0, d3m2,
+	d0m1to2m3, d1m0to3m2,
+};
+
+/*
+ * Bits and scan inded not currently used but left here to
+ * reduce churn during merge.
+ */
+#define MAX1363_CHAN_U(num, addr, scan_index, bits)			\
+	{								\
+		.type = IIO_VOLTAGE,					\
+		.indexed = 1,						\
+		.channel = num,						\
+		.address = addr,					\
+		.info_mask = MAX1363_INFO_MASK				\
+	}								\
+
+/* bipolar channel */
+#define MAX1363_CHAN_B(num, num2, addr, scan_index, bits)		\
+	{								\
+		.type = IIO_VOLTAGE,					\
+		.differential = 1,					\
+		.indexed = 1,						\
+		.channel = num,						\
+		.channel2 = num2,					\
+		.address = addr,					\
+		.info_mask = MAX1363_INFO_MASK,				\
+	}
+#define MAX1363_INFO_MASK (1 << IIO_CHAN_INFO_SCALE_SHARED)
+
+#define MAX1363_CHAN_UNIPOLAR(index)
+#define MAX1363_4X_CHANS(bits) {		\
+	MAX1363_CHAN_U(0, _s0, 0, bits),	\
+	MAX1363_CHAN_U(1, _s1, 1, bits),	\
+	MAX1363_CHAN_U(2, _s2, 2, bits),	\
+	MAX1363_CHAN_U(3, _s3, 3, bits),	\
+	MAX1363_CHAN_B(0, 1, d0m1, 4, bits),	\
+	MAX1363_CHAN_B(2, 3, d2m3, 5, bits),	\
+	MAX1363_CHAN_B(1, 0, d1m0, 6, bits),	\
+	MAX1363_CHAN_B(3, 2, d3m2, 7, bits),	\
+	}
+
+static struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8);
+static struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10);
+static struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12);
+
+/* Appies to max1236, max1237 */
+static const enum max1363_modes max1236_mode_list[] = {
+	_s0, _s1, _s2, _s3,
+	s0to1, s0to2, s0to3,
+	d0m1, d2m3, d1m0, d3m2,
+	d0m1to2m3, d1m0to3m2,
+	s2to3,
+};
+
+/* Applies to max1238, max1239 */
+static const enum max1363_modes max1238_mode_list[] = {
+	_s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+	s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+	s0to7, s0to8, s0to9, s0to10, s0to11,
+	d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+	d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+	d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+	d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+	s6to7, s6to8, s6to9, s6to10, s6to11,
+	d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10,
+};
+
+#define MAX1363_12X_CHANS(bits) {			\
+	MAX1363_CHAN_U(0, _s0, 0, bits),		\
+	MAX1363_CHAN_U(1, _s1, 1, bits),		\
+	MAX1363_CHAN_U(2, _s2, 2, bits),		\
+	MAX1363_CHAN_U(3, _s3, 3, bits),		\
+	MAX1363_CHAN_U(4, _s4, 4, bits),		\
+	MAX1363_CHAN_U(5, _s5, 5, bits),		\
+	MAX1363_CHAN_U(6, _s6, 6, bits),		\
+	MAX1363_CHAN_U(7, _s7, 7, bits),		\
+	MAX1363_CHAN_U(8, _s8, 8, bits),		\
+	MAX1363_CHAN_U(9, _s9, 9, bits),		\
+	MAX1363_CHAN_U(10, _s10, 10, bits),		\
+	MAX1363_CHAN_U(11, _s11, 11, bits),		\
+	MAX1363_CHAN_B(0, 1, d0m1, 12, bits),		\
+	MAX1363_CHAN_B(2, 3, d2m3, 13, bits),		\
+	MAX1363_CHAN_B(4, 5, d4m5, 14, bits),		\
+	MAX1363_CHAN_B(6, 7, d6m7, 15, bits),		\
+	MAX1363_CHAN_B(8, 9, d8m9, 16, bits),		\
+	MAX1363_CHAN_B(10, 11, d10m11, 17, bits),	\
+	MAX1363_CHAN_B(1, 0, d1m0, 18, bits),		\
+	MAX1363_CHAN_B(3, 2, d3m2, 19, bits),		\
+	MAX1363_CHAN_B(5, 4, d5m4, 20, bits),		\
+	MAX1363_CHAN_B(7, 6, d7m6, 21, bits),		\
+	MAX1363_CHAN_B(9, 8, d9m8, 22, bits),		\
+	MAX1363_CHAN_B(11, 10, d11m10, 23, bits),	\
+	}
+
+static struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
+static struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
+static struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12);
+
+static const enum max1363_modes max11607_mode_list[] = {
+	_s0, _s1, _s2, _s3,
+	s0to1, s0to2, s0to3,
+	s2to3,
+	d0m1, d2m3, d1m0, d3m2,
+	d0m1to2m3, d1m0to3m2,
+};
+
+static const enum max1363_modes max11608_mode_list[] = {
+	_s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7,
+	s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7,
+	s6to7,
+	d0m1, d2m3, d4m5, d6m7,
+	d1m0, d3m2, d5m4, d7m6,
+	d0m1to2m3, d0m1to4m5, d0m1to6m7,
+	d1m0to3m2, d1m0to5m4, d1m0to7m6,
+};
+
+#define MAX1363_8X_CHANS(bits) {		\
+	MAX1363_CHAN_U(0, _s0, 0, bits),	\
+	MAX1363_CHAN_U(1, _s1, 1, bits),	\
+	MAX1363_CHAN_U(2, _s2, 2, bits),	\
+	MAX1363_CHAN_U(3, _s3, 3, bits),	\
+	MAX1363_CHAN_U(4, _s4, 4, bits),	\
+	MAX1363_CHAN_U(5, _s5, 5, bits),	\
+	MAX1363_CHAN_U(6, _s6, 6, bits),	\
+	MAX1363_CHAN_U(7, _s7, 7, bits),	\
+	MAX1363_CHAN_B(0, 1, d0m1, 8, bits),	\
+	MAX1363_CHAN_B(2, 3, d2m3, 9, bits),	\
+	MAX1363_CHAN_B(4, 5, d4m5, 10, bits),	\
+	MAX1363_CHAN_B(6, 7, d6m7, 11, bits),	\
+	MAX1363_CHAN_B(1, 0, d1m0, 12, bits),	\
+	MAX1363_CHAN_B(3, 2, d3m2, 13, bits),	\
+	MAX1363_CHAN_B(5, 4, d5m4, 14, bits),	\
+	MAX1363_CHAN_B(7, 6, d7m6, 15, bits),	\
+}
+static struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
+static struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10);
+static struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12);
+
+static const enum max1363_modes max11644_mode_list[] = {
+	_s0, _s1, s0to1, d0m1, d1m0,
+};
+
+#define MAX1363_2X_CHANS(bits) {			\
+	MAX1363_CHAN_U(0, _s0, 0, bits),		\
+	MAX1363_CHAN_U(1, _s1, 1, bits),		\
+	MAX1363_CHAN_B(0, 1, d0m1, 2, bits),		\
+	MAX1363_CHAN_B(1, 0, d1m0, 3, bits),		\
+	}
+
+static struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10);
+static struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12);
+
+enum { max1361,
+       max1362,
+       max1363,
+       max1364,
+       max1036,
+       max1037,
+       max1038,
+       max1039,
+       max1136,
+       max1137,
+       max1138,
+       max1139,
+       max1236,
+       max1237,
+       max1238,
+       max1239,
+       max11600,
+       max11601,
+       max11602,
+       max11603,
+       max11604,
+       max11605,
+       max11606,
+       max11607,
+       max11608,
+       max11609,
+       max11610,
+       max11611,
+       max11612,
+       max11613,
+       max11614,
+       max11615,
+       max11616,
+       max11617,
+       max11644,
+       max11645,
+       max11646,
+       max11647
+};
+
+static const struct iio_info max1238_info = {
+	.read_raw = &max1363_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const struct iio_info max1363_info = {
+	.read_raw = &max1363_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+/* max1363 and max1368 tested - rest from data sheet */
+static const struct max1363_chip_info max1363_chip_info_tbl[] = {
+	[max1361] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+		.channels = max1136_channels,
+		.num_channels = ARRAY_SIZE(max1136_channels),
+		.info = &max1363_info,
+	},
+	[max1362] = {
+		.bits = 10,
+		.int_vref_mv = 4096,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+		.channels = max1136_channels,
+		.num_channels = ARRAY_SIZE(max1136_channels),
+		.info = &max1363_info,
+	},
+	[max1363] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+		.channels = max1236_channels,
+		.num_channels = ARRAY_SIZE(max1236_channels),
+		.info = &max1363_info,
+	},
+	[max1364] = {
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+		.channels = max1236_channels,
+		.num_channels = ARRAY_SIZE(max1236_channels),
+		.info = &max1363_info,
+	},
+	[max1036] = {
+		.bits = 8,
+		.int_vref_mv = 4096,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1036_channels,
+		.num_channels = ARRAY_SIZE(max1036_channels),
+	},
+	[max1037] = {
+		.bits = 8,
+		.int_vref_mv = 2048,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1036_channels,
+		.num_channels = ARRAY_SIZE(max1036_channels),
+	},
+	[max1038] = {
+		.bits = 8,
+		.int_vref_mv = 4096,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1038_channels,
+		.num_channels = ARRAY_SIZE(max1038_channels),
+	},
+	[max1039] = {
+		.bits = 8,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1038_channels,
+		.num_channels = ARRAY_SIZE(max1038_channels),
+	},
+	[max1136] = {
+		.bits = 10,
+		.int_vref_mv = 4096,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1136_channels,
+		.num_channels = ARRAY_SIZE(max1136_channels),
+	},
+	[max1137] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1136_channels,
+		.num_channels = ARRAY_SIZE(max1136_channels),
+	},
+	[max1138] = {
+		.bits = 10,
+		.int_vref_mv = 4096,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1138_channels,
+		.num_channels = ARRAY_SIZE(max1138_channels),
+	},
+	[max1139] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1138_channels,
+		.num_channels = ARRAY_SIZE(max1138_channels),
+	},
+	[max1236] = {
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1236_channels,
+		.num_channels = ARRAY_SIZE(max1236_channels),
+	},
+	[max1237] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1236_channels,
+		.num_channels = ARRAY_SIZE(max1236_channels),
+	},
+	[max1238] = {
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max1239] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11600] = {
+		.bits = 8,
+		.int_vref_mv = 4096,
+		.mode_list = max11607_mode_list,
+		.num_modes = ARRAY_SIZE(max11607_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1036_channels,
+		.num_channels = ARRAY_SIZE(max1036_channels),
+	},
+	[max11601] = {
+		.bits = 8,
+		.int_vref_mv = 2048,
+		.mode_list = max11607_mode_list,
+		.num_modes = ARRAY_SIZE(max11607_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1036_channels,
+		.num_channels = ARRAY_SIZE(max1036_channels),
+	},
+	[max11602] = {
+		.bits = 8,
+		.int_vref_mv = 4096,
+		.mode_list = max11608_mode_list,
+		.num_modes = ARRAY_SIZE(max11608_mode_list),
+		.default_mode = s0to7,
+		.info = &max1238_info,
+		.channels = max11602_channels,
+		.num_channels = ARRAY_SIZE(max11602_channels),
+	},
+	[max11603] = {
+		.bits = 8,
+		.int_vref_mv = 2048,
+		.mode_list = max11608_mode_list,
+		.num_modes = ARRAY_SIZE(max11608_mode_list),
+		.default_mode = s0to7,
+		.info = &max1238_info,
+		.channels = max11602_channels,
+		.num_channels = ARRAY_SIZE(max11602_channels),
+	},
+	[max11604] = {
+		.bits = 8,
+		.int_vref_mv = 4098,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11605] = {
+		.bits = 8,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11606] = {
+		.bits = 10,
+		.int_vref_mv = 4096,
+		.mode_list = max11607_mode_list,
+		.num_modes = ARRAY_SIZE(max11607_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1136_channels,
+		.num_channels = ARRAY_SIZE(max1136_channels),
+	},
+	[max11607] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max11607_mode_list,
+		.num_modes = ARRAY_SIZE(max11607_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1136_channels,
+		.num_channels = ARRAY_SIZE(max1136_channels),
+	},
+	[max11608] = {
+		.bits = 10,
+		.int_vref_mv = 4096,
+		.mode_list = max11608_mode_list,
+		.num_modes = ARRAY_SIZE(max11608_mode_list),
+		.default_mode = s0to7,
+		.info = &max1238_info,
+		.channels = max11608_channels,
+		.num_channels = ARRAY_SIZE(max11608_channels),
+	},
+	[max11609] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max11608_mode_list,
+		.num_modes = ARRAY_SIZE(max11608_mode_list),
+		.default_mode = s0to7,
+		.info = &max1238_info,
+		.channels = max11608_channels,
+		.num_channels = ARRAY_SIZE(max11608_channels),
+	},
+	[max11610] = {
+		.bits = 10,
+		.int_vref_mv = 4098,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11611] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11612] = {
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max11607_mode_list,
+		.num_modes = ARRAY_SIZE(max11607_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1236_channels,
+		.num_channels = ARRAY_SIZE(max1236_channels),
+	},
+	[max11613] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max11607_mode_list,
+		.num_modes = ARRAY_SIZE(max11607_mode_list),
+		.default_mode = s0to3,
+		.info = &max1238_info,
+		.channels = max1236_channels,
+		.num_channels = ARRAY_SIZE(max1236_channels),
+	},
+	[max11614] = {
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max11608_mode_list,
+		.num_modes = ARRAY_SIZE(max11608_mode_list),
+		.default_mode = s0to7,
+		.info = &max1238_info,
+		.channels = max11614_channels,
+		.num_channels = ARRAY_SIZE(max11614_channels),
+	},
+	[max11615] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max11608_mode_list,
+		.num_modes = ARRAY_SIZE(max11608_mode_list),
+		.default_mode = s0to7,
+		.info = &max1238_info,
+		.channels = max11614_channels,
+		.num_channels = ARRAY_SIZE(max11614_channels),
+	},
+	[max11616] = {
+		.bits = 12,
+		.int_vref_mv = 4098,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11617] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+		.info = &max1238_info,
+		.channels = max1238_channels,
+		.num_channels = ARRAY_SIZE(max1238_channels),
+	},
+	[max11644] = {
+		.bits = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max11644_mode_list,
+		.num_modes = ARRAY_SIZE(max11644_mode_list),
+		.default_mode = s0to1,
+		.info = &max1238_info,
+		.channels = max11644_channels,
+		.num_channels = ARRAY_SIZE(max11644_channels),
+	},
+	[max11645] = {
+		.bits = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max11644_mode_list,
+		.num_modes = ARRAY_SIZE(max11644_mode_list),
+		.default_mode = s0to1,
+		.info = &max1238_info,
+		.channels = max11644_channels,
+		.num_channels = ARRAY_SIZE(max11644_channels),
+	},
+	[max11646] = {
+		.bits = 10,
+		.int_vref_mv = 2048,
+		.mode_list = max11644_mode_list,
+		.num_modes = ARRAY_SIZE(max11644_mode_list),
+		.default_mode = s0to1,
+		.info = &max1238_info,
+		.channels = max11646_channels,
+		.num_channels = ARRAY_SIZE(max11646_channels),
+	},
+	[max11647] = {
+		.bits = 10,
+		.int_vref_mv = 4096,
+		.mode_list = max11644_mode_list,
+		.num_modes = ARRAY_SIZE(max11644_mode_list),
+		.default_mode = s0to1,
+		.info = &max1238_info,
+		.channels = max11646_channels,
+		.num_channels = ARRAY_SIZE(max11646_channels),
+	},
+};
+
+static int max1363_initial_setup(struct max1363_state *st)
+{
+	st->setupbyte = MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD
+		| MAX1363_SETUP_POWER_UP_INT_REF
+		| MAX1363_SETUP_INT_CLOCK
+		| MAX1363_SETUP_UNIPOLAR
+		| MAX1363_SETUP_NORESET;
+
+	/* Set scan mode writes the config anyway so wait until then*/
+	st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte);
+	st->current_mode = &max1363_mode_table[st->chip_info->default_mode];
+	st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte);
+
+	return max1363_set_scan_mode(st);
+}
+
+static int __devinit max1363_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	int ret, i, regdone = 0;
+	struct max1363_state *st;
+	struct iio_dev *indio_dev;
+	struct regulator *reg;
+
+	reg = regulator_get(&client->dev, "vcc");
+	if (IS_ERR(reg)) {
+		ret = PTR_ERR(reg);
+		goto error_out;
+	}
+
+	ret = regulator_enable(reg);
+	if (ret)
+		goto error_put_reg;
+
+	indio_dev = iio_device_allocate(sizeof(struct max1363_state));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_disable_reg;
+	}
+	st = iio_priv(indio_dev);
+	st->reg = reg;
+	/* this is only used for device removal purposes */
+	i2c_set_clientdata(client, indio_dev);
+
+	st->chip_info = &max1363_chip_info_tbl[id->driver_data];
+	st->client = client;
+
+	indio_dev->available_scan_masks
+		= kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*
+			  (st->chip_info->num_modes + 1), GFP_KERNEL);
+	if (!indio_dev->available_scan_masks) {
+		ret = -ENOMEM;
+		goto error_free_device;
+	}
+
+	for (i = 0; i < st->chip_info->num_modes; i++)
+		bitmap_copy(indio_dev->available_scan_masks +
+			    BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i,
+			    max1363_mode_table[st->chip_info->mode_list[i]]
+			    .modemask, MAX1363_MAX_CHANNELS);
+	/* Estabilish that the iio_dev is a child of the i2c device */
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->name = id->name;
+
+	indio_dev->info = st->chip_info->info;
+	indio_dev->channels = st->chip_info->channels;
+	indio_dev->num_channels = st->chip_info->num_channels;
+	ret = max1363_initial_setup(st);
+	if (ret < 0)
+		goto error_free_available_scan_masks;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_free_available_scan_masks;
+	regdone = 1;
+
+	return 0;
+
+error_free_available_scan_masks:
+	kfree(indio_dev->available_scan_masks);
+error_free_device:
+	if (!regdone)
+		iio_device_free(indio_dev);
+	else
+		iio_device_unregister(indio_dev);
+error_disable_reg:
+	regulator_disable(reg);
+error_put_reg:
+	regulator_put(reg);
+error_out:
+	return ret;
+}
+
+static int max1363_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct max1363_state *st = iio_priv(indio_dev);
+	struct regulator *reg = st->reg;
+
+	kfree(indio_dev->available_scan_masks);
+	if (!IS_ERR(reg)) {
+		regulator_disable(reg);
+		regulator_put(reg);
+	}
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id max1363_id[] = {
+	{ "max1361", max1361 },
+	{ "max1362", max1362 },
+	{ "max1363", max1363 },
+	{ "max1364", max1364 },
+	{ "max1036", max1036 },
+	{ "max1037", max1037 },
+	{ "max1038", max1038 },
+	{ "max1039", max1039 },
+	{ "max1136", max1136 },
+	{ "max1137", max1137 },
+	{ "max1138", max1138 },
+	{ "max1139", max1139 },
+	{ "max1236", max1236 },
+	{ "max1237", max1237 },
+	{ "max1238", max1238 },
+	{ "max1239", max1239 },
+	{ "max11600", max11600 },
+	{ "max11601", max11601 },
+	{ "max11602", max11602 },
+	{ "max11603", max11603 },
+	{ "max11604", max11604 },
+	{ "max11605", max11605 },
+	{ "max11606", max11606 },
+	{ "max11607", max11607 },
+	{ "max11608", max11608 },
+	{ "max11609", max11609 },
+	{ "max11610", max11610 },
+	{ "max11611", max11611 },
+	{ "max11612", max11612 },
+	{ "max11613", max11613 },
+	{ "max11614", max11614 },
+	{ "max11615", max11615 },
+	{ "max11616", max11616 },
+	{ "max11617", max11617 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, max1363_id);
+
+static struct i2c_driver max1363_driver = {
+	.driver = {
+		.name = "max1363",
+	},
+	.probe = max1363_probe,
+	.remove = max1363_remove,
+	.id_table = max1363_id,
+};
+
+static __init int max1363_init(void)
+{
+	return i2c_add_driver(&max1363_driver);
+}
+module_init(max1363_init);
+
+static __exit void max1363_exit(void)
+{
+	i2c_del_driver(&max1363_driver);
+}
+module_exit(max1363_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Maxim 1363 ADC");
+MODULE_LICENSE("GPL v2");
+
-- 
1.7.3.4


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

* [PATCH 3/4] IIO:light:tsl2563 initial move out of staging.
  2011-08-30 16:18 [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 1/4] IIO: Core sysfs only support Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 2/4] IIO:ADC: max1363 initial import Jonathan Cameron
@ 2011-08-30 16:18 ` Jonathan Cameron
  2011-08-30 16:18 ` [PATCH 4/4] IIO:imu:adis16400 partial move from staging Jonathan Cameron
  2011-09-21  9:54 ` [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
  4 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2011-08-30 16:18 UTC (permalink / raw)
  To: linux-iio; +Cc: Michael.Hennerich, manuel.stahl, Jonathan Cameron

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>

Conflicts:

	drivers/iio/Makefile
---
 drivers/iio/Kconfig         |    1 +
 drivers/iio/Makefile        |    1 +
 drivers/iio/light/Kconfig   |   14 +
 drivers/iio/light/Makefile  |    6 +
 drivers/iio/light/tsl2563.c |  653 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/tsl2563.h |    9 +
 6 files changed, 684 insertions(+), 0 deletions(-)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index cebe21f..c5613b6 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -13,5 +13,6 @@ menuconfig IIO
 if IIO
 
 source "drivers/iio/adc/Kconfig"
+source "drivers/iio/light/Kconfig"
 
 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 42d6d12..0e59946 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_IIO) += iio.o
 industrialio-y := core.o
 
 obj-y += adc/
+obj-y += light/
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
new file mode 100644
index 0000000..16ad7ce
--- /dev/null
+++ b/drivers/iio/light/Kconfig
@@ -0,0 +1,14 @@
+#
+# IIO Light Sensor Drivers
+#
+comment "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.
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..6884ad5
--- /dev/null
+++ b/drivers/iio/light/tsl2563.c
@@ -0,0 +1,653 @@
+/*
+ * drivers/i2c/chips/tsl2563.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ * Contact: Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * Converted to IIO driver
+ * Amit Kucheria <amit.kucheria@verdurent.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/tsl2563.h>
+
+/* Use this many bits for fraction part. */
+#define ADC_FRAC_BITS		(14)
+
+/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
+#define FRAC10K(f)		(((f) * (1L << (ADC_FRAC_BITS))) / (10000))
+
+/* Bits used for fraction in calibration coefficients.*/
+#define CALIB_FRAC_BITS		(10)
+/* 0.5 in CALIB_FRAC_BITS precision */
+#define CALIB_FRAC_HALF		(1 << (CALIB_FRAC_BITS - 1))
+/* Make a fraction from a number n that was multiplied with b. */
+#define CALIB_FRAC(n, b)	(((n) << CALIB_FRAC_BITS) / (b))
+/* Decimal 10^(digits in sysfs presentation) */
+#define CALIB_BASE_SYSFS	(1000)
+
+#define TSL2563_CMD		(0x80)
+#define TSL2563_CLEARINT	(0x40)
+
+#define TSL2563_REG_CTRL	(0x00)
+#define TSL2563_REG_TIMING	(0x01)
+#define TSL2563_REG_LOWLOW	(0x02) /* data0 low threshold, 2 bytes */
+#define TSL2563_REG_LOWHIGH	(0x03)
+#define TSL2563_REG_HIGHLOW	(0x04) /* data0 high threshold, 2 bytes */
+#define TSL2563_REG_HIGHHIGH	(0x05)
+#define TSL2563_REG_INT		(0x06)
+#define TSL2563_REG_ID		(0x0a)
+#define TSL2563_REG_DATA0LOW	(0x0c) /* broadband sensor value, 2 bytes */
+#define TSL2563_REG_DATA0HIGH	(0x0d)
+#define TSL2563_REG_DATA1LOW	(0x0e) /* infrared sensor value, 2 bytes */
+#define TSL2563_REG_DATA1HIGH	(0x0f)
+
+#define TSL2563_CMD_POWER_ON	(0x03)
+#define TSL2563_CMD_POWER_OFF	(0x00)
+#define TSL2563_CTRL_POWER_MASK	(0x03)
+
+#define TSL2563_TIMING_13MS	(0x00)
+#define TSL2563_TIMING_100MS	(0x01)
+#define TSL2563_TIMING_400MS	(0x02)
+#define TSL2563_TIMING_MASK	(0x03)
+#define TSL2563_TIMING_GAIN16	(0x10)
+#define TSL2563_TIMING_GAIN1	(0x00)
+
+#define TSL2563_INT_DISBLED	(0x00)
+#define TSL2563_INT_LEVEL	(0x10)
+#define TSL2563_INT_PERSIST(n)	((n) & 0x0F)
+
+struct tsl2563_gainlevel_coeff {
+	u8 gaintime;
+	u16 min;
+	u16 max;
+};
+
+static const struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = {
+	{
+		.gaintime	= TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16,
+		.min		= 0,
+		.max		= 65534,
+	}, {
+		.gaintime	= TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1,
+		.min		= 2048,
+		.max		= 65534,
+	}, {
+		.gaintime	= TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1,
+		.min		= 4095,
+		.max		= 37177,
+	}, {
+		.gaintime	= TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1,
+		.min		= 3000,
+		.max		= 65535,
+	},
+};
+
+struct tsl2563_chip {
+	struct mutex		lock;
+	struct i2c_client	*client;
+	struct delayed_work	poweroff_work;
+
+	/* Remember state for suspend and resume functions */
+	pm_message_t		state;
+
+	struct tsl2563_gainlevel_coeff const *gainlevel;
+
+	/* Calibration coefficients */
+	u32			calib0;
+	u32			calib1;
+	int			cover_comp_gain;
+
+	/* Cache current values, to be returned while suspended */
+	u32			data0;
+	u32			data1;
+};
+
+static int tsl2563_set_power(struct tsl2563_chip *chip, int on)
+{
+	return i2c_smbus_write_byte_data(chip->client,
+					 TSL2563_CMD | TSL2563_REG_CTRL,
+					 on ?
+					 TSL2563_CMD_POWER_ON :
+					 TSL2563_CMD_POWER_OFF);
+}
+
+/*
+ * Return value is 0 for off, 1 for on, or a negative error
+ * code if reading failed.
+ */
+static int tsl2563_get_power(struct tsl2563_chip *chip)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(chip->client,
+				       TSL2563_CMD | TSL2563_REG_CTRL);
+	if (ret < 0)
+		return ret;
+
+	return (ret & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON;
+}
+
+static int tsl2563_configure(struct tsl2563_chip *chip)
+{
+	return i2c_smbus_write_byte_data(chip->client,
+					 TSL2563_CMD | TSL2563_REG_TIMING,
+					 chip->gainlevel->gaintime);
+}
+
+static void tsl2563_poweroff_work(struct work_struct *work)
+{
+	struct tsl2563_chip *chip =
+		container_of(work, struct tsl2563_chip, poweroff_work.work);
+	tsl2563_set_power(chip, 0);
+}
+
+static int tsl2563_detect(struct tsl2563_chip *chip)
+{
+	int ret;
+
+	ret = tsl2563_set_power(chip, 1);
+	if (ret)
+		return ret;
+
+	ret = tsl2563_get_power(chip);
+	if (ret < 0)
+		return ret;
+
+	return ret ? 0 : -ENODEV;
+}
+
+/*
+ * "Normalized" ADC value is one obtained with 400ms of integration time and
+ * 16x gain. This function returns the number of bits of shift needed to
+ * convert between normalized values and HW values obtained using given
+ * timing and gain settings.
+ */
+static int adc_shiftbits(u8 timing)
+{
+	int shift = 0;
+
+	switch (timing & TSL2563_TIMING_MASK) {
+	case TSL2563_TIMING_13MS:
+		shift += 5;
+		break;
+	case TSL2563_TIMING_100MS:
+		shift += 2;
+		break;
+	case TSL2563_TIMING_400MS:
+		/* no-op */
+		break;
+	}
+
+	if (!(timing & TSL2563_TIMING_GAIN16))
+		shift += 4;
+
+	return shift;
+}
+
+/* Convert a HW ADC value to normalized scale. */
+static u32 normalize_adc(u16 adc, u8 timing)
+{
+	return adc << adc_shiftbits(timing);
+}
+
+static void tsl2563_wait_adc(struct tsl2563_chip *chip)
+{
+	unsigned int delay;
+
+	switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) {
+	case TSL2563_TIMING_13MS:
+		delay = 14;
+		break;
+	case TSL2563_TIMING_100MS:
+		delay = 101;
+		break;
+	default:
+		delay = 402;
+	}
+	/*
+	 * TODO: Make sure that we wait at least required delay but why we
+	 * have to extend it one tick more?
+	 */
+	schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2);
+}
+
+static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc)
+{
+	struct i2c_client *client = chip->client;
+
+	if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) {
+
+		(adc > chip->gainlevel->max) ?
+			chip->gainlevel++ : chip->gainlevel--;
+
+		i2c_smbus_write_byte_data(client,
+					  TSL2563_CMD | TSL2563_REG_TIMING,
+					  chip->gainlevel->gaintime);
+
+		tsl2563_wait_adc(chip);
+		tsl2563_wait_adc(chip);
+
+		return 1;
+	} else
+		return 0;
+}
+
+static int tsl2563_get_adc(struct tsl2563_chip *chip)
+{
+	struct i2c_client *client = chip->client;
+	u16 adc0, adc1;
+	int retry = 1;
+	int ret = 0;
+
+	if (chip->state.event != PM_EVENT_ON)
+		goto out;
+
+	cancel_delayed_work(&chip->poweroff_work);
+
+	if (!tsl2563_get_power(chip)) {
+		ret = tsl2563_set_power(chip, 1);
+		if (ret)
+			goto out;
+		ret = tsl2563_configure(chip);
+		if (ret)
+			goto out;
+		tsl2563_wait_adc(chip);
+	}
+
+	while (retry) {
+		ret = i2c_smbus_read_word_data(client,
+				TSL2563_CMD | TSL2563_REG_DATA0LOW);
+		if (ret < 0)
+			goto out;
+		adc0 = ret;
+
+		ret = i2c_smbus_read_word_data(client,
+				TSL2563_CMD | TSL2563_REG_DATA1LOW);
+		if (ret < 0)
+			goto out;
+		adc1 = ret;
+
+		retry = tsl2563_adjust_gainlevel(chip, adc0);
+	}
+
+	chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
+	chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+
+
+	ret = 0;
+out:
+	return ret;
+}
+
+static inline int calib_to_sysfs(u32 calib)
+{
+	return (int) (((calib * CALIB_BASE_SYSFS) +
+		       CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
+}
+
+static inline u32 calib_from_sysfs(int value)
+{
+	return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
+}
+
+/*
+ * Conversions between lux and ADC values.
+ *
+ * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are
+ * appropriate constants. Different constants are needed for different
+ * kinds of light, determined by the ratio adc1/adc0 (basically the ratio
+ * of the intensities in infrared and visible wavelengths). lux_table below
+ * lists the upper threshold of the adc1/adc0 ratio and the corresponding
+ * constants.
+ */
+
+struct tsl2563_lux_coeff {
+	unsigned long ch_ratio;
+	unsigned long ch0_coeff;
+	unsigned long ch1_coeff;
+};
+
+static const struct tsl2563_lux_coeff lux_table[] = {
+	{
+		.ch_ratio	= FRAC10K(1300),
+		.ch0_coeff	= FRAC10K(315),
+		.ch1_coeff	= FRAC10K(262),
+	}, {
+		.ch_ratio	= FRAC10K(2600),
+		.ch0_coeff	= FRAC10K(337),
+		.ch1_coeff	= FRAC10K(430),
+	}, {
+		.ch_ratio	= FRAC10K(3900),
+		.ch0_coeff	= FRAC10K(363),
+		.ch1_coeff	= FRAC10K(529),
+	}, {
+		.ch_ratio	= FRAC10K(5200),
+		.ch0_coeff	= FRAC10K(392),
+		.ch1_coeff	= FRAC10K(605),
+	}, {
+		.ch_ratio	= FRAC10K(6500),
+		.ch0_coeff	= FRAC10K(229),
+		.ch1_coeff	= FRAC10K(291),
+	}, {
+		.ch_ratio	= FRAC10K(8000),
+		.ch0_coeff	= FRAC10K(157),
+		.ch1_coeff	= FRAC10K(180),
+	}, {
+		.ch_ratio	= FRAC10K(13000),
+		.ch0_coeff	= FRAC10K(34),
+		.ch1_coeff	= FRAC10K(26),
+	}, {
+		.ch_ratio	= ULONG_MAX,
+		.ch0_coeff	= 0,
+		.ch1_coeff	= 0,
+	},
+};
+
+/*
+ * Convert normalized, scaled ADC values to lux.
+ */
+static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+{
+	const struct tsl2563_lux_coeff *lp = lux_table;
+	unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
+
+	ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX;
+
+	while (lp->ch_ratio < ratio)
+		lp++;
+
+	lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff;
+
+	return (unsigned int) (lux >> ADC_FRAC_BITS);
+}
+
+/* Apply calibration coefficient to ADC count. */
+static u32 calib_adc(u32 adc, u32 calib)
+{
+	unsigned long scaled = adc;
+
+	scaled *= calib;
+	scaled >>= CALIB_FRAC_BITS;
+
+	return (u32) scaled;
+}
+
+static int tsl2563_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+	if (chan->channel == 0)
+		chip->calib0 = calib_from_sysfs(val);
+	else
+		chip->calib1 = calib_from_sysfs(val);
+
+	return 0;
+}
+
+static int tsl2563_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long m)
+{
+	int ret = -EINVAL;
+	u32 calib0, calib1;
+	struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+	mutex_lock(&chip->lock);
+	switch (m) {
+	case 0:
+		switch (chan->type) {
+		case IIO_LIGHT:
+			ret = tsl2563_get_adc(chip);
+			if (ret)
+				goto error_ret;
+			calib0 = calib_adc(chip->data0, chip->calib0) *
+				chip->cover_comp_gain;
+			calib1 = calib_adc(chip->data1, chip->calib1) *
+				chip->cover_comp_gain;
+			*val = adc_to_lux(calib0, calib1);
+			ret = IIO_VAL_INT;
+			break;
+		case IIO_INTENSITY:
+			ret = tsl2563_get_adc(chip);
+			if (ret)
+				goto error_ret;
+			if (chan->channel == 0)
+				*val = chip->data0;
+			else
+				*val = chip->data1;
+			ret = IIO_VAL_INT;
+			break;
+		default:
+			break;
+		}
+		break;
+
+	case (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE):
+		if (chan->channel == 0)
+			*val = calib_to_sysfs(chip->calib0);
+		else
+			*val = calib_to_sysfs(chip->calib1);
+		ret = IIO_VAL_INT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+error_ret:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static const struct iio_chan_spec tsl2563_channels[] = {
+	{
+		.type = IIO_LIGHT,
+		.indexed = 1,
+		.channel = 0,
+	}, {
+		.type = IIO_INTENSITY,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE),
+	}, {
+		.type = IIO_INTENSITY,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBSCALE_SEPARATE),
+	}
+};
+
+static const struct iio_info tsl2563_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &tsl2563_read_raw,
+	.write_raw = &tsl2563_write_raw,
+};
+
+static int __devinit tsl2563_probe(struct i2c_client *client,
+				const struct i2c_device_id *device_id)
+{
+	struct iio_dev *indio_dev;
+	struct tsl2563_chip *chip;
+	struct tsl2563_platform_data *pdata = client->dev.platform_data;
+	int err = 0;
+	int ret;
+	u8 id = 0;
+
+	indio_dev = iio_device_allocate(sizeof(*chip));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	chip = iio_priv(indio_dev);
+
+	i2c_set_clientdata(client, indio_dev);
+	chip->client = client;
+
+	err = tsl2563_detect(chip);
+	if (err) {
+		dev_err(&client->dev, "device not found, error %d\n", -err);
+		goto fail1;
+	}
+
+	/* Check we can read the id */
+	err = i2c_smbus_read_byte_data(chip->client,
+				       TSL2563_CMD | TSL2563_REG_ID);
+	if (err < 0)
+		goto fail1;
+
+	mutex_init(&chip->lock);
+
+	/* Default values used until userspace says otherwise */
+	chip->gainlevel = tsl2563_gainlevel_table;
+	chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
+	chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+
+	if (pdata)
+		chip->cover_comp_gain = pdata->cover_comp_gain;
+	else
+		chip->cover_comp_gain = 1;
+
+	dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
+	indio_dev->name = client->name;
+	indio_dev->channels = tsl2563_channels;
+	indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &tsl2563_info;
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto fail1;
+	err = tsl2563_configure(chip);
+	if (err)
+		goto fail2;
+
+	INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work);
+
+	schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
+
+	return 0;
+fail2:
+	iio_device_unregister(indio_dev);
+fail1:
+	kfree(chip);
+	return err;
+}
+
+static int tsl2563_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct tsl2563_chip *chip = iio_priv(indio_dev);
+
+	tsl2563_set_power(chip, 0);
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static int tsl2563_suspend(struct i2c_client *client, pm_message_t state)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct tsl2563_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&chip->lock);
+
+	ret = tsl2563_set_power(chip, 0);
+	if (ret)
+		goto out;
+
+	chip->state = state;
+
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static int tsl2563_resume(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct tsl2563_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&chip->lock);
+
+	ret = tsl2563_set_power(chip, 1);
+	if (ret)
+		goto out;
+
+	ret = tsl2563_configure(chip);
+	if (ret)
+		goto out;
+
+	chip->state.event = PM_EVENT_ON;
+
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+static const struct i2c_device_id tsl2563_id[] = {
+	{ "tsl2560", 0 },
+	{ "tsl2561", 0 },
+	{ "tsl2562", 0 },
+	{ "tsl2563", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tsl2563_id);
+
+static struct i2c_driver tsl2563_i2c_driver = {
+	.driver = {
+		.name	 = "tsl2563",
+	},
+	.suspend	= tsl2563_suspend,
+	.resume		= tsl2563_resume,
+	.probe		= tsl2563_probe,
+	.remove		= __devexit_p(tsl2563_remove),
+	.id_table	= tsl2563_id,
+};
+
+static int __init tsl2563_init(void)
+{
+	return i2c_add_driver(&tsl2563_i2c_driver);
+}
+module_init(tsl2563_init);
+
+static void __exit tsl2563_exit(void)
+{
+	i2c_del_driver(&tsl2563_i2c_driver);
+}
+module_exit(tsl2563_exit);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("tsl2563 light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/tsl2563.h b/include/linux/iio/tsl2563.h
new file mode 100644
index 0000000..e0bc31c
--- /dev/null
+++ b/include/linux/iio/tsl2563.h
@@ -0,0 +1,9 @@
+#ifndef _TSL2563_H_
+#define _TSL2563_H_
+
+struct tsl2563_platform_data {
+	int cover_comp_gain;
+};
+
+#endif /* _TSL2563_H_ */
+
-- 
1.7.3.4

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

* [PATCH 4/4] IIO:imu:adis16400 partial move from staging.
  2011-08-30 16:18 [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
                   ` (2 preceding siblings ...)
  2011-08-30 16:18 ` [PATCH 3/4] IIO:light:tsl2563 initial move out of staging Jonathan Cameron
@ 2011-08-30 16:18 ` Jonathan Cameron
  2011-09-21  9:54 ` [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
  4 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2011-08-30 16:18 UTC (permalink / raw)
  To: linux-iio; +Cc: Michael.Hennerich, manuel.stahl, Jonathan Cameron

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          |   10 +
 drivers/iio/imu/Makefile         |    6 +
 drivers/iio/imu/adis16400.h      |  180 +++++++
 drivers/iio/imu/adis16400_core.c | 1057 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 1255 insertions(+), 0 deletions(-)

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index c5613b6..dd2daa1 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -13,6 +13,7 @@ menuconfig IIO
 if IIO
 
 source "drivers/iio/adc/Kconfig"
+source "drivers/iio/imu/Kconfig"
 source "drivers/iio/light/Kconfig"
 
 endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 0e59946..db3c426 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_IIO) += iio.o
 industrialio-y := core.o
 
 obj-y += adc/
+obj-y += imu/
 obj-y += light/
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
new file mode 100644
index 0000000..29283e1
--- /dev/null
+++ b/drivers/iio/imu/Kconfig
@@ -0,0 +1,10 @@
+comment "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).
\ No newline at end of file
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..62061d1
--- /dev/null
+++ b/drivers/iio/imu/adis16400_core.c
@@ -0,0 +1,1057 @@
+/*
+ * 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,
+	ADIS16344,
+	ADIS16350,
+	ADIS16360,
+	ADIS16362,
+	ADIS16364,
+	ADIS16365,
+	ADIS16400,
+};
+
+/**
+ * adis16400_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ */
+static int adis16400_spi_write_reg_8(struct iio_dev *indio_dev,
+				     u8 reg_address,
+				     u8 val)
+{
+	int ret;
+	struct adis16400_state *st = iio_priv(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16400_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16400_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ *
+ * At the moment the spi framework doesn't allow global setting of cs_change.
+ * This means that use cannot be made of spi_write.
+ */
+static int adis16400_spi_write_reg_16(struct iio_dev *indio_dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct adis16400_state *st = iio_priv(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16400_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16400_WRITE_REG(lower_reg_address + 1);
+	st->tx[3] = (value >> 8) & 0xFF;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16400_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @indio_dev: iio device
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ *
+ * At the moment the spi framework doesn't allow global setting of cs_change.
+ * This means that use cannot be made of spi_read.
+ **/
+static int adis16400_spi_read_reg_16(struct iio_dev *indio_dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct adis16400_state *st = iio_priv(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16400_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev,
+			"problem when reading 16 bit register 0x%02X",
+			lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16400_read_frequency(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	int ret, len = 0;
+	u16 t;
+	int sps;
+	ret = adis16400_spi_read_reg_16(indio_dev,
+			ADIS16400_SMPL_PRD,
+			&t);
+	if (ret)
+		return ret;
+	sps =  (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 53 : 1638;
+	sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1;
+	len = sprintf(buf, "%d SPS\n", sps);
+	return len;
+}
+
+static ssize_t adis16400_write_frequency(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf,
+		size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16400_state *st = iio_priv(indio_dev);
+	long val;
+	int ret;
+	u8 t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	t = (1638 / val);
+	if (t > 0)
+		t--;
+	t &= ADIS16400_SMPL_PRD_DIV_MASK;
+	if ((t & ADIS16400_SMPL_PRD_DIV_MASK) >= 0x0A)
+		st->us->max_speed_hz = ADIS16400_SPI_SLOW;
+	else
+		st->us->max_speed_hz = ADIS16400_SPI_FAST;
+
+	ret = adis16400_spi_write_reg_8(indio_dev,
+			ADIS16400_SMPL_PRD,
+			t);
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static int adis16400_reset(struct iio_dev *indio_dev)
+{
+	int ret;
+	ret = adis16400_spi_write_reg_8(indio_dev,
+			ADIS16400_GLOB_CMD,
+			ADIS16400_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(&indio_dev->dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16400_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	bool val;
+	int ret;
+
+	ret = strtobool(buf, &val);
+	if (ret < 0)
+		return ret;
+	if (val) {
+		ret = adis16400_reset(dev_get_drvdata(dev));
+		if (ret < 0)
+			return ret;
+	}
+
+	return len;
+}
+
+/* Power down the device */
+static int adis16400_stop_device(struct iio_dev *indio_dev)
+{
+	int ret;
+	u16 val = ADIS16400_SLP_CNT_POWER_OFF;
+
+	ret = adis16400_spi_write_reg_16(indio_dev, ADIS16400_SLP_CNT, val);
+	if (ret)
+		dev_err(&indio_dev->dev,
+			"problem with turning device off: SLP_CNT");
+
+	return ret;
+}
+
+static int adis16400_check_status(struct iio_dev *indio_dev)
+{
+	u16 status;
+	int ret;
+	struct device *dev = &indio_dev->dev;
+
+	ret = adis16400_spi_read_reg_16(indio_dev,
+					ADIS16400_DIAG_STAT, &status);
+
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status;
+	if (status & ADIS16400_DIAG_STAT_ZACCL_FAIL)
+		dev_err(dev, "Z-axis accelerometer self-test failure\n");
+	if (status & ADIS16400_DIAG_STAT_YACCL_FAIL)
+		dev_err(dev, "Y-axis accelerometer self-test failure\n");
+	if (status & ADIS16400_DIAG_STAT_XACCL_FAIL)
+		dev_err(dev, "X-axis accelerometer self-test failure\n");
+	if (status & ADIS16400_DIAG_STAT_XGYRO_FAIL)
+		dev_err(dev, "X-axis gyroscope self-test failure\n");
+	if (status & ADIS16400_DIAG_STAT_YGYRO_FAIL)
+		dev_err(dev, "Y-axis gyroscope self-test failure\n");
+	if (status & ADIS16400_DIAG_STAT_ZGYRO_FAIL)
+		dev_err(dev, "Z-axis gyroscope self-test failure\n");
+	if (status & ADIS16400_DIAG_STAT_ALARM2)
+		dev_err(dev, "Alarm 2 active\n");
+	if (status & ADIS16400_DIAG_STAT_ALARM1)
+		dev_err(dev, "Alarm 1 active\n");
+	if (status & ADIS16400_DIAG_STAT_FLASH_CHK)
+		dev_err(dev, "Flash checksum error\n");
+	if (status & ADIS16400_DIAG_STAT_SELF_TEST)
+		dev_err(dev, "Self test error\n");
+	if (status & ADIS16400_DIAG_STAT_OVERFLOW)
+		dev_err(dev, "Sensor overrange\n");
+	if (status & ADIS16400_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16400_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16400_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 5.25V\n");
+	if (status & ADIS16400_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 4.75V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16400_self_test(struct iio_dev *indio_dev)
+{
+	int ret;
+	ret = adis16400_spi_write_reg_16(indio_dev,
+			ADIS16400_MSC_CTRL,
+			ADIS16400_MSC_CTRL_MEM_TEST);
+	if (ret) {
+		dev_err(&indio_dev->dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	msleep(ADIS16400_MTEST_DELAY);
+	adis16400_check_status(indio_dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16400_initial_setup(struct iio_dev *indio_dev)
+{
+	int ret;
+	u16 prod_id, smp_prd;
+	struct adis16400_state *st = iio_priv(indio_dev);
+
+	/* use low spi speed for init */
+	st->us->max_speed_hz = ADIS16400_SPI_SLOW;
+	st->us->mode = SPI_MODE_3;
+	spi_setup(st->us);
+
+	ret = adis16400_self_test(indio_dev);
+	if (ret) {
+		dev_err(&indio_dev->dev, "self test failure");
+		goto err_ret;
+	}
+
+	ret = adis16400_check_status(indio_dev);
+	if (ret) {
+		adis16400_reset(indio_dev);
+		dev_err(&indio_dev->dev, "device not playing ball -> reset");
+		msleep(ADIS16400_STARTUP_DELAY);
+		ret = adis16400_check_status(indio_dev);
+		if (ret) {
+			dev_err(&indio_dev->dev, "giving up");
+			goto err_ret;
+		}
+	}
+	if (st->variant->flags & ADIS16400_HAS_PROD_ID) {
+		ret = adis16400_spi_read_reg_16(indio_dev,
+						ADIS16400_PRODUCT_ID, &prod_id);
+		if (ret)
+			goto err_ret;
+
+		if ((prod_id & 0xF000) != st->variant->product_id)
+			dev_warn(&indio_dev->dev, "incorrect id");
+
+		dev_info(&indio_dev->dev, "%s: prod_id 0x%04x at CS%d (irq %d)\n",
+ 		       indio_dev->name, prod_id,
+ 		       st->us->chip_select, st->us->irq);
+	}
+	/* use high spi speed if possible */
+	ret = adis16400_spi_read_reg_16(indio_dev,
+					ADIS16400_SMPL_PRD, &smp_prd);
+	if (!ret && (smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) {
+		st->us->max_speed_hz = ADIS16400_SPI_SLOW;
+		spi_setup(st->us);
+	}
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency,
+		       S_IWUSR | S_IRUGO,
+		       adis16400_read_frequency,
+		       adis16400_write_frequency,
+		       0);
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16400_write_reset, 0);
+
+static IIO_CONST_ATTR(sampling_frequency_available, "409 546 819 1638");
+
+enum adis16400_chan {
+	in_supply,
+	gyro_x,
+	gyro_y,
+	gyro_z,
+	accel_x,
+	accel_y,
+	accel_z,
+	magn_x,
+	magn_y,
+	magn_z,
+	temp,
+	temp0, temp1, temp2,
+	in1,
+	incli_x,
+	incli_y,
+};
+
+static u8 adis16400_addresses[17][2] = {
+	[in_supply] = { ADIS16400_SUPPLY_OUT },
+	[gyro_x] = { ADIS16400_XGYRO_OUT, ADIS16400_XGYRO_OFF },
+	[gyro_y] = { ADIS16400_YGYRO_OUT, ADIS16400_YGYRO_OFF },
+	[gyro_z] = { ADIS16400_ZGYRO_OUT, ADIS16400_ZGYRO_OFF },
+	[accel_x] = { ADIS16400_XACCL_OUT, ADIS16400_XACCL_OFF },
+	[accel_y] = { ADIS16400_YACCL_OUT, ADIS16400_YACCL_OFF },
+	[accel_z] = { ADIS16400_ZACCL_OUT, ADIS16400_ZACCL_OFF },
+	[magn_x] = { ADIS16400_XMAGN_OUT },
+	[magn_y] = { ADIS16400_YMAGN_OUT },
+	[magn_z] = { ADIS16400_ZMAGN_OUT },
+	[temp] = { ADIS16400_TEMP_OUT },
+	[temp0] = { ADIS16350_XTEMP_OUT },
+	[temp1] = { ADIS16350_YTEMP_OUT },
+	[temp2] = { ADIS16350_ZTEMP_OUT },
+	[in1] = { ADIS16400_AUX_ADC },
+	[incli_x] = { ADIS16300_PITCH_OUT },
+	[incli_y] = { ADIS16300_ROLL_OUT }
+};
+
+static int adis16400_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	int ret;
+
+	switch (mask) {
+	case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+		mutex_lock(&indio_dev->mlock);
+		ret = adis16400_spi_write_reg_16(indio_dev,
+				adis16400_addresses[chan->address][1],
+				val);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adis16400_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val,
+			      int *val2,
+			      long mask)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	int ret, shift;
+	s16 val16;
+
+	switch (mask) {
+	case 0:
+		mutex_lock(&indio_dev->mlock);
+		ret = adis16400_spi_read_reg_16(indio_dev,
+				adis16400_addresses[chan->address][0],
+				&val16);
+		if (ret) {
+			mutex_unlock(&indio_dev->mlock);
+			return ret;
+		}
+		val16 &= (1 << chan->realbits) - 1;
+		if (chan->sign) {
+			shift = 16 - chan->realbits;
+			val16 = (s16)(val16 << shift) >> shift;
+		}
+		*val = val16;
+		mutex_unlock(&indio_dev->mlock);
+		return IIO_VAL_INT;
+	case (1 << IIO_CHAN_INFO_SCALE_SHARED):
+	case (1 << IIO_CHAN_INFO_SCALE_SEPARATE):
+		switch (chan->type) {
+		case IIO_GYRO:
+			*val = 0;
+			*val2 = st->variant->gyro_scale_micro;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_IN:
+			*val = 0;
+			if (chan->channel == 0)
+				*val2 = 2418;
+			else
+				*val2 = 806;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_ACCEL:
+			*val = 0;
+			*val2 = st->variant->accel_scale_micro;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_MAGN:
+			*val = 0;
+			*val2 = 500;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_TEMP:
+			*val = 0;
+			*val2 = 140000;
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE):
+		mutex_lock(&indio_dev->mlock);
+		ret = adis16400_spi_read_reg_16(indio_dev,
+				adis16400_addresses[chan->address][1],
+				&val16);
+		mutex_unlock(&indio_dev->mlock);
+		if (ret)
+			return ret;
+		val16 = ((val16 & 0xFFF) << 4) >> 4;
+		*val = val16;
+		return IIO_VAL_INT;
+	case (1 << IIO_CHAN_INFO_OFFSET_SEPARATE):
+		/* currently only temperature */
+		*val = 198;
+		*val2 = 160000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct iio_chan_spec adis16400_channels[] = {
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.channel = 0,
+		.extend_name = "supply",
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = in_supply,
+		.realbits = 14,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = magn_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = magn_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = magn_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = temp,
+		.realbits = 12,
+		.sign = 1,
+	}, {
+		.type = IIO_IN,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = in1,
+		.realbits = 12,
+	},
+};
+
+static struct iio_chan_spec adis16350_channels[] = {
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.channel = 0,
+		.extend_name = "supply",
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = in_supply,
+		.realbits = 12,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.extend_name = "x",
+		.info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = temp0,
+		.realbits = 12,
+		.sign = 1,
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 1,
+		.extend_name = "y",
+		.info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = temp1,
+		.realbits = 12,
+		.sign = 1,
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 2,
+		.extend_name = "z",
+		.info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = temp2,
+		.realbits = 12,
+		.sign = 1,
+	}, {
+		.type = IIO_IN,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = in1,
+		.realbits = 12,
+	},
+};
+
+static struct iio_chan_spec adis16300_channels[] = {
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.channel = 0,
+		.extend_name = "supply",
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = in_supply,
+		.realbits = 14,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = (1 << IIO_CHAN_INFO_OFFSET_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = temp,
+		.realbits = 12,
+		.sign = 1,
+	}, {
+		.type = IIO_IN,
+		.indexed = 1,
+		.channel = 1,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SEPARATE),
+		.address = in1,
+		.realbits = 12,
+	}, {
+		.type = IIO_INCLI,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = incli_x,
+		.realbits = 13,
+		.sign = 1,
+	}, {
+		.type = IIO_INCLI,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = incli_y,
+		.realbits = 13,
+		.sign = 1,
+	},
+};
+
+static const struct iio_chan_spec adis16344_channels[] = {
+	{
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_GYRO,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = gyro_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_x,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_y,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_ACCEL,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_z,
+		.realbits = 14,
+		.sign = 1,
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.channel = 0,
+		.info_mask = (1 << IIO_CHAN_INFO_CALIBBIAS_SEPARATE) |
+		(1 << IIO_CHAN_INFO_SCALE_SHARED),
+		.address = accel_z,
+		.realbits = 14,
+		.sign = 1,
+	},
+};
+
+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,
+	},
+	[ADIS16344] = {
+		.channels = adis16344_channels,
+		.num_channels = ARRAY_SIZE(adis16344_channels),
+		.gyro_scale_micro = 873,
+		.accel_scale_micro = 981,
+	},
+	[ADIS16350] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.gyro_scale_micro = 872664,
+		.accel_scale_micro = 24732,
+	},
+	[ADIS16360] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID,
+		.product_id = 0x3FE8,
+		.gyro_scale_micro = 1279,
+		.accel_scale_micro = 24732,
+	},
+	[ADIS16362] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID,
+		.product_id = 0x3FEA,
+		.gyro_scale_micro = 1279,
+		.accel_scale_micro = 24732,
+	},
+	[ADIS16364] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID,
+		.product_id = 0x3FEC,
+		.gyro_scale_micro = 1279,
+		.accel_scale_micro = 24732,
+	},
+	[ADIS16365] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID,
+		.product_id = 0x3FED,
+		.gyro_scale_micro = 1279,
+		.accel_scale_micro = 24732,
+	},
+	[ADIS16400] = {
+		.channels = adis16400_channels,
+		.num_channels = ARRAY_SIZE(adis16400_channels),
+		.flags = ADIS16400_HAS_PROD_ID,
+		.product_id = 0x4015,
+		.gyro_scale_micro = 873,
+		.accel_scale_micro = 32656,
+	}
+};
+
+static const struct iio_info adis16400_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &adis16400_read_raw,
+	.write_raw = &adis16400_write_raw,
+	.attrs = &adis16400_attribute_group,
+};
+
+static int __devinit adis16400_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16400_state *st;
+	struct iio_dev *indio_dev;
+
+	indio_dev = iio_device_allocate(sizeof(*st));
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	st = iio_priv(indio_dev);
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, indio_dev);
+
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+
+	/* setup the industrialio driver allocated elements */
+	st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data];
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->channels = st->variant->channels;
+	indio_dev->num_channels = st->variant->num_channels;
+	indio_dev->info = &adis16400_info;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_free_dev;
+	regdone = 1;
+
+	/* Get the device into a sane initial state */
+	ret = adis16400_initial_setup(indio_dev);
+	if (ret)
+		goto error_free_dev;
+	return 0;
+
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(indio_dev);
+	else
+		iio_device_free(indio_dev);
+error_ret:
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int adis16400_remove(struct spi_device *spi)
+{
+	int ret;
+	struct iio_dev *indio_dev =  spi_get_drvdata(spi);
+
+	ret = adis16400_stop_device(indio_dev);
+	if (ret)
+		return ret;
+
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adis16400_id[] = {
+	{"adis16300", ADIS16300},
+	{"adis16344", ADIS16344},
+	{"adis16350", ADIS16350},
+	{"adis16354", ADIS16350},
+	{"adis16355", ADIS16350},
+	{"adis16360", ADIS16360},
+	{"adis16362", ADIS16362},
+	{"adis16364", ADIS16364},
+	{"adis16365", ADIS16365},
+	{"adis16400", ADIS16400},
+	{"adis16405", ADIS16400},
+	{}
+};
+
+static struct spi_driver adis16400_driver = {
+	.driver = {
+		.name = "adis16400",
+		.owner = THIS_MODULE,
+	},
+	.id_table = adis16400_id,
+	.probe = adis16400_probe,
+	.remove = __devexit_p(adis16400_remove),
+};
+
+static __init int adis16400_init(void)
+{
+	return spi_register_driver(&adis16400_driver);
+}
+module_init(adis16400_init);
+
+static __exit void adis16400_exit(void)
+{
+	spi_unregister_driver(&adis16400_driver);
+}
+module_exit(adis16400_exit);
+
+MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.4

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

* Re: [RFC PATCH 0/4] IIO: Out of staging step 1. The core.
  2011-08-30 16:18 [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
                   ` (3 preceding siblings ...)
  2011-08-30 16:18 ` [PATCH 4/4] IIO:imu:adis16400 partial move from staging Jonathan Cameron
@ 2011-09-21  9:54 ` Jonathan Cameron
  2011-09-27 10:56   ` Jonathan Cameron
  4 siblings, 1 reply; 7+ messages in thread
From: Jonathan Cameron @ 2011-09-21  9:54 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Michael.Hennerich, manuel.stahl

Bump. Anyone had a chance to look at the text in this cover letter?

I'm planning ideally to send this first series out shortly.

Jonathan
On 08/30/11 17:18, Jonathan Cameron wrote:
> Hi All,
> 
> I'd like to make a good impression when posting this more widely,
> hence I'm posting it here first. Please comment on both the code
> and the text of this explanatory email!  Are there other things
> I should put in here?  Clearly the set is currently missing
> documentation which will need doing before this goes to lkml etc.
> 
> Also note that the code has to sit on a couple of namespace moves
> to avoid a clash with the staging tree.  These are in the
> outofstaging branch of iio-blue.git.
> 
> Dear All,
> 
> 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 subystems (hwmon and input) do not meet them. Focused
> subsytems targetting just one type of sensor have been rejected as
> a concept (see ALS proposal).
> 
> Requirements of IIO.
> 
> 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.
> 
> This is an area in which hwmon excels. Everything is available via sysfs
> and thats exactly what we allow drivers to provide.
> 
> 2 - High performance data route:
> 
> We want a path via which data can get to userspace 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 (obvously 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 actually convertor 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.  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 amount of processing of data to occur in the
> kernel possible. Its often best to leave this to userspace 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 metadata 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.
> 
> For IIO we have decided to try and create an event code (litterally 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. freefall is typically X&Y&Z all less than alpha)
> 
> 3) What type of processed data it is applied to (raw value, abs value,
> rate of change, average shifted value)
> 
> 4) Which direction the threshold is crossed in.
> 
> 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. Afterall 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 (see possible future work).
> 
> 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 do 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 actualy a 'data has been captured now'
> signal (dataready).
> 
> 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 userspace to trigger simultaneous capture from a number of
>    devices.
> c) Standalone triggers.
>    General interrupts from whereever 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.
> 
> 
> 
> 
> 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
> 
> 
> So what do we have here?
> 
> Our merge plan (order may change) has the following elements
> 
> 1) Registration core and sysfs interface
> 2) Some stripped down drivers using the above.
> 
> 3) Event interface
> 4) Extend drivers in 2 to provide event interfaces.
> 
> 5) Buffered reading interface
> 6) Extend drivers from 4 to provide buffered reading.
> 7) Pull in hardware ring buffer device examples
> 
> 8) Futher sets pulling across a few drivers at time (keeping review
> manageable!)
> 
> Drivers may be added to the subsystem at any stage (once support for
> what they use is available).
> 
> This patchset (1-2) only covers the simplest type of driver supported
> by IIO, that with only a sysfs based interface.  Please review it
> with that in mind!
> 
> Acknowledgements
> 
> A lot of thanks is due to various people during the IIO development.
> Most of it is born out by commit messages but I'd particularly like to
> thank Michael Hennerich for massive contributions and Arnd Bergmann for
> taking the time to not only suggest what we should do differently but
> to explain it to me and look at the results as they occured.
> 
> Jonathan Cameron (4):
>   IIO: Core sysfs only support.
>   IIO:ADC: max1363 initial import.
>   IIO:light:tsl2563 initial move out of staging.
>   IIO:imu:adis16400 partial move from staging.
> 
>  drivers/Kconfig                  |    2 +
>  drivers/Makefile                 |    2 +
>  drivers/iio/Kconfig              |   19 +
>  drivers/iio/Makefile             |   10 +
>  drivers/iio/adc/Kconfig          |   17 +
>  drivers/iio/adc/Makefile         |    6 +
>  drivers/iio/adc/max1363.h        |  148 ++++++
>  drivers/iio/adc/max1363_core.c   | 1000 ++++++++++++++++++++++++++++++++++++
>  drivers/iio/iio.c                |  593 +++++++++++++++++++++
>  drivers/iio/imu/Kconfig          |   10 +
>  drivers/iio/imu/Makefile         |    6 +
>  drivers/iio/imu/adis16400.h      |  180 +++++++
>  drivers/iio/imu/adis16400_core.c | 1057 ++++++++++++++++++++++++++++++++++++++
>  drivers/iio/light/Kconfig        |   14 +
>  drivers/iio/light/Makefile       |    6 +
>  drivers/iio/light/tsl2563.c      |  653 +++++++++++++++++++++++
>  include/linux/iio/iio.h          |  243 +++++++++
>  include/linux/iio/sysfs.h        |   68 +++
>  include/linux/iio/tsl2563.h      |    9 +
>  19 files changed, 4043 insertions(+), 0 deletions(-)
>  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/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/iio.h
>  create mode 100644 include/linux/iio/sysfs.h
>  create mode 100644 include/linux/iio/tsl2563.h
> 

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

* Re: [RFC PATCH 0/4] IIO: Out of staging step 1. The core.
  2011-09-21  9:54 ` [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
@ 2011-09-27 10:56   ` Jonathan Cameron
  0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2011-09-27 10:56 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Michael.Hennerich, manuel.stahl

On 09/21/11 10:54, Jonathan Cameron wrote:
> Bump. Anyone had a chance to look at the text in this cover letter?
> 
> I'm planning ideally to send this first series out shortly.
Updated version pushed to github. Changes are trivial case of
updating the namespace shift of staging tree to cover changes in
drivers and using menus for the sub device types in Kconfig.

Not really worth reposting.  Any comments to this series will
apply equally to that.

Jonathan
> 
> Jonathan
> On 08/30/11 17:18, Jonathan Cameron wrote:
>> Hi All,
>>
>> I'd like to make a good impression when posting this more widely,
>> hence I'm posting it here first. Please comment on both the code
>> and the text of this explanatory email!  Are there other things
>> I should put in here?  Clearly the set is currently missing
>> documentation which will need doing before this goes to lkml etc.
>>
>> Also note that the code has to sit on a couple of namespace moves
>> to avoid a clash with the staging tree.  These are in the
>> outofstaging branch of iio-blue.git.
>>
>> Dear All,
>>
>> 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 subystems (hwmon and input) do not meet them. Focused
>> subsytems targetting just one type of sensor have been rejected as
>> a concept (see ALS proposal).
>>
>> Requirements of IIO.
>>
>> 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.
>>
>> This is an area in which hwmon excels. Everything is available via sysfs
>> and thats exactly what we allow drivers to provide.
>>
>> 2 - High performance data route:
>>
>> We want a path via which data can get to userspace 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 (obvously 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 actually convertor 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.  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 amount of processing of data to occur in the
>> kernel possible. Its often best to leave this to userspace 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 metadata 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.
>>
>> For IIO we have decided to try and create an event code (litterally 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. freefall is typically X&Y&Z all less than alpha)
>>
>> 3) What type of processed data it is applied to (raw value, abs value,
>> rate of change, average shifted value)
>>
>> 4) Which direction the threshold is crossed in.
>>
>> 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. Afterall 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 (see possible future work).
>>
>> 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 do 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 actualy a 'data has been captured now'
>> signal (dataready).
>>
>> 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 userspace to trigger simultaneous capture from a number of
>>    devices.
>> c) Standalone triggers.
>>    General interrupts from whereever 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.
>>
>>
>>
>>
>> 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
>>
>>
>> So what do we have here?
>>
>> Our merge plan (order may change) has the following elements
>>
>> 1) Registration core and sysfs interface
>> 2) Some stripped down drivers using the above.
>>
>> 3) Event interface
>> 4) Extend drivers in 2 to provide event interfaces.
>>
>> 5) Buffered reading interface
>> 6) Extend drivers from 4 to provide buffered reading.
>> 7) Pull in hardware ring buffer device examples
>>
>> 8) Futher sets pulling across a few drivers at time (keeping review
>> manageable!)
>>
>> Drivers may be added to the subsystem at any stage (once support for
>> what they use is available).
>>
>> This patchset (1-2) only covers the simplest type of driver supported
>> by IIO, that with only a sysfs based interface.  Please review it
>> with that in mind!
>>
>> Acknowledgements
>>
>> A lot of thanks is due to various people during the IIO development.
>> Most of it is born out by commit messages but I'd particularly like to
>> thank Michael Hennerich for massive contributions and Arnd Bergmann for
>> taking the time to not only suggest what we should do differently but
>> to explain it to me and look at the results as they occured.
>>
>> Jonathan Cameron (4):
>>   IIO: Core sysfs only support.
>>   IIO:ADC: max1363 initial import.
>>   IIO:light:tsl2563 initial move out of staging.
>>   IIO:imu:adis16400 partial move from staging.
>>
>>  drivers/Kconfig                  |    2 +
>>  drivers/Makefile                 |    2 +
>>  drivers/iio/Kconfig              |   19 +
>>  drivers/iio/Makefile             |   10 +
>>  drivers/iio/adc/Kconfig          |   17 +
>>  drivers/iio/adc/Makefile         |    6 +
>>  drivers/iio/adc/max1363.h        |  148 ++++++
>>  drivers/iio/adc/max1363_core.c   | 1000 ++++++++++++++++++++++++++++++++++++
>>  drivers/iio/iio.c                |  593 +++++++++++++++++++++
>>  drivers/iio/imu/Kconfig          |   10 +
>>  drivers/iio/imu/Makefile         |    6 +
>>  drivers/iio/imu/adis16400.h      |  180 +++++++
>>  drivers/iio/imu/adis16400_core.c | 1057 ++++++++++++++++++++++++++++++++++++++
>>  drivers/iio/light/Kconfig        |   14 +
>>  drivers/iio/light/Makefile       |    6 +
>>  drivers/iio/light/tsl2563.c      |  653 +++++++++++++++++++++++
>>  include/linux/iio/iio.h          |  243 +++++++++
>>  include/linux/iio/sysfs.h        |   68 +++
>>  include/linux/iio/tsl2563.h      |    9 +
>>  19 files changed, 4043 insertions(+), 0 deletions(-)
>>  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/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/iio.h
>>  create mode 100644 include/linux/iio/sysfs.h
>>  create mode 100644 include/linux/iio/tsl2563.h
>>
> 
> 

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

end of thread, other threads:[~2011-09-27 10:56 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-30 16:18 [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
2011-08-30 16:18 ` [PATCH 1/4] IIO: Core sysfs only support Jonathan Cameron
2011-08-30 16:18 ` [PATCH 2/4] IIO:ADC: max1363 initial import Jonathan Cameron
2011-08-30 16:18 ` [PATCH 3/4] IIO:light:tsl2563 initial move out of staging Jonathan Cameron
2011-08-30 16:18 ` [PATCH 4/4] IIO:imu:adis16400 partial move from staging Jonathan Cameron
2011-09-21  9:54 ` [RFC PATCH 0/4] IIO: Out of staging step 1. The core Jonathan Cameron
2011-09-27 10:56   ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.