linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [Industrial I/O] [0/13] RFC: IIO v3 patchset
@ 2008-12-01 14:20 Jonathan Cameron
  2008-12-01 14:23 ` [Industrial I/O] [1/13] RFC: IIO core functionality Jonathan Cameron
                   ` (11 more replies)
  0 siblings, 12 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:20 UTC (permalink / raw)
  To: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette, Jonat
  Cc: LM Sensors, spi-devel-general

Dear All,

Sorry this has been so long in coming, it has been a busy couple of
months.

Summary of what IIO is about:

The Industrial I/O (name open to suggestions) subsystem is the result
of discussions on LKML about suitable facilities in the kernel for
the handling of Analog to Digital Converters (ADCs) and the huge classes
of sensor that act in similar fashion (digital accelerometers etc). The
intention is that this may also provide a home for DACs as a lot of chips
have both functions. The sort of devices we are talking typically
communicate over I2C or SPI buses though drivers for rs232 devices etc are
definitely on the cards. Basically we are interested in devices where direct
memory mapped access is not possible.

My particular motivation was to produce an adaptable data capture platform
and, seeing as that's what is paying the bills, it has been the initial focus of
my efforts.  The RFCs below include the question of what other facilities should
be included as options in this system.

For discussion of why these don't fit within existing subsystems see 
http://lkml.org/lkml/2008/5/20/135 and the rest of the associated thread.

The design is intended to allow a disparate set of drivers providing whatever
subset of functionality people have uses for. Hence, the intention is that it
is not necessary or even desirable to support as much as the example drivers
provided here. The system is designed to allow everything from simple drivers
providing sysfs based reading of individual channels to the level of complexity
seen in the sca3000 accelerometer driver found in patch 11.
To this end any final submission will include at least one example of a minimal
driver.

I'm still fairly new to kernel coding so have doubtlessly made many mistakes
and non optimal decisions within this code. So, whilst this is intended
primarily to feed a discussion of the overall framework, I would be grateful
to receive any more specific comments on the code! Particularly welcome are
any suggestions that will result in reductions in code length without loss of
functionality as this is still rather larger than would be ideal!

As ever, thanks to the numerous people who commented on the last version posted
and those who have been testing the various intermediate versions (Marcelo Pias
in particular.)

Contents:

[1/13] The Industrial I/O core:
		The core registration and event handling functionality of IIO.

[2/13] The Industrial I/O core - ring buffer support:
		Adds ring buffer handling to the IIO core.

[3/13] IIO: Software ring buffer implementation.
		An example software ring buffer module.

[4/13] The Industrial I/O core - trigger support
		Adds trigger support to the IIO core.

[5/13] IIO: Periodic real time clock based trigger.
		An example of IIO trigger supplying driver.

[6/13] IIO: Proof of concept GPIO based IIO trigger
		A very simple GPIO based trigger suitable for external
		synchronization.

[7/13] IIO: Maxim MAX1363 driver
		Driver for the the MAX1363 ADC chip (core functionality)

[8/13] IIO: Maxim MAX1363 driver (ring buffer support)
		Add triggered software ring buffer support to the driver

[9/13] IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (core)
		Driver for the ST LIS3L02DQ accelerometer (SPI only)

[10/13] IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (ring)
		Add triggered software ring buffer support and a data
		ready based trigger to the device.

[11/13] IIO: VTI SCA3000 3d accelerometer driver.
		Driver for the VTI SCA3000 range of accelerometers.

[12/13] IIO: Documentation
		Whilst most documentation related to IIO is contained
		as kernel doc comments within the source code, a brief
		overview of what it does and discussions of elements
		of individual drivers are provided in
		Documentation/industrialio.

[13/13] IIO: Example user space ring buffer access.
		Simple example program to illustrate how a user space
		application can interact with the ring buffer
		functionality of IIO (this includes event handling).
		The example used uses the datardy trigger of the
		lis3l02dq to drive the filling of a software ring buffer
		from the lis3l02dq device.

Overview of changes:

* Introduction of trigger drivers to allow more flexible sampling
  strategies and separate out the periodic RTC element (as only
  one example of a trigger driver.) New triggers provided as example
  / proofs of concept are GPIO based for external sync and data ready
  on the lis3l02dq (to act as example of a unified iio:trigger /
  iio:device driver)

* Far more modular design:
  - The core now acts as a registration system providing sysfs class
    handling and registration of chrdevs. The registration is relatively
    heavy weight pulling in other core elements such as ring buffers
    as appropriate.
  - Ring buffers are now an optional element (compile to near zero)
    if not enabled. Intention is that it will be possible to select
    most appropriate implementation at run time though for now this
    is hard coded in the drivers (as there is only one software
    implementation)
  - Separate library module provides a fairly generic ring buffer
    implementation.
  - Drivers are now spit into core component (direct access to channels
    and event chrdevs) and ring buffer related code.
  - The intention is that, if appropriate, other additional optional
    elements will be provided via a similar approach.  This would include
    interfacing to hwmon and input subsystems.
  - Hopefully this will make code review a much simpler exercise than
    the behemoth patches sent last time!

Requests for comment:

Big general question - Is it sensible to try and have unified drivers
providing all functionality users may want from a given sensor chip?
This came up in the previous discussion but I'm still not sure we have
a definitive answer. Basically, is it sensible to have the iio-core
provide hwmon interfaces (for devices that are used as such complete
with caching etc) or input interfaces (for things like accelerometers
that are increasingly used as input devices.)
It would be difficult to say the least to run these at the same time as
for example the ring buffering elements.

Is it worthwhile allowing multiple triggers for single device?  This may
greatly increase the individual driver complexity and not actually be that
useful.

Devices supplied triggers.  These may be used to trigger other devices. Tricky
to set this up unless they are already set to trigger themselves. A clean
means of specifying restrictions on trigger / device associations is needed.

Consider optimal approach to configuring scan modes.
 * Somewhat clunky approach used with max1363 - basically associate formalized
   text label with each scan mode.  Somewhat chip specific approach.

 * Scan element selection as in lis3l02dq and sca3000 drivers.
   Selectable sysfs elements represent each signal that is available.
   Works well in this case where any combination is possible but complex in the
   cases with exclusion problems such as unipolar and bipolar for the max1363.
   Also nasty search functionality and masking of results would be needed to
   apply this to chips like the max1363 where not every combination is
   directly possible.

Whilst it would be nice to clean this up, perhaps enforcing two rigid a set
of rules here will just lead to fiddly unmaintainable code.  Maybe one to
reconsider down the line when many more device drivers are available to
consider.

There are numerous RFCs concerned with more specific questions within the
various patches.

TO DO:

There are still quite a few elements that I know need cleaning up in this
code some of which are listed here.

Protection on many of the control interfaces. In some cases this will require
input via board configs.  For example the maximum sampling rate of a sensor
is often limited by a complex interaction of bus speed and available
processing power. The easiest option would be to allow board configuration
data to specify limits that the developer has tested and verified to give
reasonable performance.

Provision of static sysfs elements to make things like conversion factors
available in as generic a form as possible.

Drivers are all currently missing things like power management support which
will ideally be in place before final submission.

Other drivers in development:

Analog Devices ADIS16350 inertial measurement unit.

Illustration of sysfs Interface:

This is just intended to give an idea of what is available for a typical
driver (lis3l02dq) under /sys/class/industrialio/ (There some RFCs after)

 ls -R industrialio/
industrialio/:
iio:device0   iio:trigger0

industrialio/iio:device0:
accel_x                       event_line0_sources
accel_x_gain                  name
accel_x_offset                power
accel_y                       ring_buf0_acc_minor
accel_y_gain                  ring_buf0_ev_minor
accel_y_offset                ring_buffer
accel_z                       sampling_frequency
accel_z_gain                  scan_elements
accel_z_offset                subsystem
available_sampling_frequency  thresh
device                        trigger
event_line0_minor             uevent

industrialio/iio:device0/event_line0_sources:
accel_x_high  accel_y_high  accel_z_high
accel_x_low   accel_y_low   accel_z_low

industrialio/iio:device0/power:
wakeup

industrialio/iio:device0/ring_buffer:
bps          length       ring_enable

industrialio/iio:device0/scan_elements:
scan_en_accel_x    scan_en_accel_y    scan_en_accel_z    scan_en_timestamp

industrialio/iio:device0/trigger:
current_trigger

industrialio/iio:trigger0:
device     name       power      subsystem  uevent

industrialio/iio:trigger0/power:
wakeup

RFC (on sysfs subsystem):

Add an interface under the trigger to list which devices have it set
as their 'current_trigger'? (useful for debugging if nothing else).
Clearly this particular trigger driver only provides a name element.

Directory structure: Basically any suggestions for a more coherent
and intuitive structure would be welcomed.

All other comments welcomed!

--
Jonathan Cameron

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

* [Industrial I/O] [1/13] RFC: IIO core functionality
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
@ 2008-12-01 14:23 ` Jonathan Cameron
  2008-12-01 14:25 ` [Industrial I/O] [2/13] RFC: IIO ring buffer support (core) Jonathan Cameron
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:23 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

The Industrial I/O core functionality.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--

This patch contains the core functionality of the industrial I/O (IIO)
subsystem. This handling of sysfs registration of devices and
allocation of such things as character devices + the handling of
any access to the major device number associated with IIO.

Principle changes since the last version are concerned with making
this considerably more modular so as to make the subsystem more
adaptable and hopefuly easier to review.

If this component is used with a driver then the available
functionality is limited to sysfs access to readings and control
along with chrdev based event interfaces to transmit hardware
events (typically interrupts) to userspace programs.

Whilst this may seem overly complex, it as pretty much as simple
as I can manage whilst keeping it fairly adaptable.  All
suggestions for simplifications particularly welcome.

 drivers/Kconfig                           |    2
 drivers/Makefile                          |    1
 drivers/industrialio/Kconfig              |   12
 drivers/industrialio/Makefile             |    6
 drivers/industrialio/industrialio-core.c  |  907 ++++++++++++++++++++++++++++++
 include/linux/industrialio/chrdev.h       |   91 +++
 include/linux/industrialio/iio.h          |  440 ++++++++++++++
 include/linux/industrialio/ring_generic.h |   24
 include/linux/industrialio/sysfs.h        |  284 +++++++++
 include/linux/industrialio/trigger.h      |   68 ++
 10 files changed, 1835 insertions(+)

diff --git a/drivers/industrialio/industrialio-core.c b/drivers/industrialio/industrialio-core.c
new file mode 100644
index 0000000..88ad39d
--- /dev/null
+++ b/drivers/industrialio/industrialio-core.c
@@ -0,0 +1,907 @@
+/* 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.
+ *
+ * Based on elements of hwmon and input subsystems.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/kdev_t.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/trigger.h>
+
+#define IIO_ID_PREFIX "iio:device"
+#define IIO_ID_FORMAT IIO_ID_PREFIX "%d"
+
+/* Temporary - I'll request one when we are near submission*/
+#define IIO_MAJOR 244
+
+/* Integer id  - used to assign each registered device a unique id*/
+static DEFINE_IDR(iio_idr);
+static DEFINE_SPINLOCK(iio_idr_lock);
+
+struct class iio_class = {
+	.name = "industrialio",
+};
+
+/**
+ * struct __iio_state - internal iio subsystem state information.
+ * @fhs:	file operations for the various chrdevs. These are
+ *		all intially set to null in init.
+ **/
+struct __iio_state {
+	struct iio_handler *fhs[256];
+};
+
+static struct __iio_state iio_state;
+static DEFINE_SPINLOCK(iio_state_lock);
+
+ssize_t iio_scan_el_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_scan_el *this_el = to_iio_scan_el(attr);
+
+	return sprintf(buf, "%d\n",
+		       !!(indio_dev->scan_mask & this_el->mask));
+}
+EXPORT_SYMBOL(iio_scan_el_show);
+
+ssize_t iio_scan_el_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *buf,
+			  size_t len)
+{
+	int ret = 0;
+	bool state;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_scan_el *this_el = to_iio_scan_el(attr);
+
+	state = !(buf[0] == '0');
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) {
+		ret = -EBUSY;
+		goto error_ret;
+	}
+
+	if (!state && (indio_dev->scan_mask & this_el->mask)) {
+		indio_dev->scan_mask &= ~this_el->mask;
+		indio_dev->scan_count--;
+	} else if (state && !(indio_dev->scan_mask & this_el->mask)) {
+		indio_dev->scan_mask |= this_el->mask;
+		indio_dev->scan_count++;
+	}
+	if (this_el->set_state)
+		ret = this_el->set_state(this_el, indio_dev, state);
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+
+}
+EXPORT_SYMBOL(iio_scan_el_store);
+
+ssize_t iio_scan_el_ts_show(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", indio_dev->scan_timestamp);
+}
+EXPORT_SYMBOL(iio_scan_el_ts_show);
+
+ssize_t iio_scan_el_ts_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf,
+			     size_t len)
+{
+	int ret = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	bool state;
+	state = !(buf[0] == '0');
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_RING_TRIGGERED) {
+		ret = -EBUSY;
+		goto error_ret;
+	}
+	indio_dev->scan_timestamp = state;
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+EXPORT_SYMBOL(iio_scan_el_ts_store);
+
+/* TODO Check if these need locking */
+void __iio_change_event(struct iio_detected_event_list *ev,
+			int ev_code,
+			s64 timestamp)
+{
+	ev->ev.id = ev_code;
+	ev->ev.timestamp = timestamp;
+}
+EXPORT_SYMBOL(__iio_change_event);
+
+/* Used both in the interrupt line put events and the ring buffer ones */
+
+/* Note that in it's current form someone has to be listening before events
+ * are queued. Hence a client MUST open the chrdev before the ring buffer is
+ * switched on.
+ */
+ int __iio_push_event(struct iio_event_interface *ev_int,
+		     int ev_code,
+		     s64 timestamp,
+		     struct iio_shared_ev_pointer*
+		     shared_pointer_p)
+{
+	struct iio_detected_event_list *ev;
+	int ret = 0;
+
+	/* Does anyone care? */
+	if (test_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags)) {
+		if (ev_int->current_events == ev_int->max_events)
+			return 0;
+		ev = kmalloc(sizeof(struct iio_detected_event_list),
+			     GFP_KERNEL);
+		if (ev == NULL) {
+			ret = -ENOMEM;
+			goto error_ret;
+		}
+		ev->ev.id = ev_code;
+		ev->ev.timestamp = timestamp;
+		if (shared_pointer_p != NULL) {
+			ev->shared_pointer = shared_pointer_p;
+			shared_pointer_p->ev_p = ev;
+		} else
+			ev->shared_pointer = NULL;
+
+		mutex_lock(&ev_int->event_list_lock);
+		list_add_tail(&ev->list, &ev_int->det_events.list);
+		mutex_unlock(&ev_int->event_list_lock);
+
+		ev_int->current_events++;
+		wake_up_interruptible(&ev_int->wait);
+	}
+
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(__iio_push_event);
+
+int iio_push_event(struct iio_dev *dev_info,
+		   int ev_line,
+		   int ev_code,
+		   s64 timestamp)
+{
+	return __iio_push_event(&dev_info->event_interfaces[ev_line],
+			       ev_code, timestamp, NULL);
+}
+EXPORT_SYMBOL(iio_push_event);
+
+/* Generic interrupt line interrupt handler */
+irqreturn_t iio_interrupt_handler(int irq, void *_int_info)
+{
+	struct iio_interrupt *int_info = _int_info;
+	struct iio_dev *dev_info = int_info->dev_info;
+	struct iio_event_handler_list *p, *q;
+	s64 time_ns;
+	unsigned long flags;
+	spin_lock_irqsave(&int_info->ev_list_lock, flags);
+	if (list_empty(&int_info->ev_list)) {
+		spin_unlock_irqrestore(&int_info->ev_list_lock, flags);
+		return IRQ_NONE;
+	}
+
+
+	time_ns = iio_get_time_ns();
+	/* detect single element list*/
+	if (list_is_singular(&int_info->ev_list)) {
+		disable_irq_nosync(irq);
+		p = list_first_entry(&int_info->ev_list,
+				     struct iio_event_handler_list,
+				     list);
+		/* single event handler - maybe shared */
+		p->handler(dev_info, 1, time_ns, !(p->refcount > 1));
+	} else /* safe against list deletion? */
+		list_for_each_entry_safe(p, q, &int_info->ev_list, list) {
+			disable_irq_nosync(irq);
+			p->handler(dev_info, 1, time_ns, 0);
+		}
+	spin_unlock_irqrestore(&int_info->ev_list_lock, flags);
+	return IRQ_HANDLED;
+}
+
+/* Confirming the validity of supplied irq is left to drivers.*/
+int iio_register_interrupt_line(unsigned int irq,
+				struct iio_dev *dev_info,
+				int line_number,
+				unsigned long type,
+				const char *name)
+{
+	int ret;
+
+	dev_info->interrupts[line_number] =
+		kmalloc(sizeof(struct iio_interrupt), GFP_KERNEL);
+	if (dev_info->interrupts[line_number] == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	printk(KERN_INFO  "initializing interrupt line %d\n", line_number);
+	spin_lock_init(&dev_info->interrupts[line_number]->ev_list_lock);
+	INIT_LIST_HEAD(&dev_info->interrupts[line_number]->ev_list);
+	dev_info->interrupts[line_number]->line_number = line_number;
+	dev_info->interrupts[line_number]->irq = irq;
+	dev_info->interrupts[line_number]->dev_info = dev_info;
+
+	/* Possibly only request on demand?
+	 * Can see this may complicate the handling of interrupts.
+	 * However, with this approach we might end up handling lots of
+	 * events no-one cares about.*/
+	ret = request_irq(irq,
+			  &iio_interrupt_handler,
+			  type,
+			  name,
+			  dev_info->interrupts[line_number]);
+	if (ret < 0)
+		goto error_ret;
+
+	return 0;
+
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(iio_register_interrupt_line);
+
+/* This turns up an awful lot */
+ssize_t iio_read_const_attr(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string);
+}
+EXPORT_SYMBOL(iio_read_const_attr);
+
+/* Before this runs the interrupt generator must have been disabled */
+void iio_unregister_interrupt_line(struct iio_dev *dev_info,
+					    int line_number)
+{
+	/* make sure the interrupt handlers are all done */
+	flush_scheduled_work();
+	free_irq(dev_info->interrupts[line_number]->irq,
+		 dev_info->interrupts[line_number]);
+	kfree(dev_info->interrupts[line_number]);
+}
+EXPORT_SYMBOL(iio_unregister_interrupt_line);
+
+/* Reference counted add and remove */
+int iio_add_event_to_list(struct iio_event_handler_list *el,
+			  struct list_head *head)
+{
+	struct iio_interrupt *inter
+		= container_of(head, struct iio_interrupt, ev_list);
+	unsigned long flags;
+	/* take mutex to protect this element */
+	mutex_lock(&el->exist_lock);
+	if (el->refcount == 0) {
+		spin_lock_irqsave(&inter->ev_list_lock, flags);
+		/*take spinlock*/
+		list_add(&el->list, head);
+		spin_unlock_irqrestore(&inter->ev_list_lock, flags);
+	}
+	el->refcount++;
+	mutex_unlock(&el->exist_lock);
+	return 0;
+}
+EXPORT_SYMBOL(iio_add_event_to_list);
+
+int iio_remove_event_from_list(struct iio_event_handler_list *el,
+			       struct list_head *head)
+{
+	struct iio_interrupt *inter
+		= container_of(head, struct iio_interrupt, ev_list);
+	unsigned long flags;
+	mutex_lock(&el->exist_lock);
+	el->refcount--;
+	if (el->refcount == 0) {
+		spin_lock_irqsave(&inter->ev_list_lock, flags);
+		/* take spinlock */
+		list_del_init(&el->list);
+		spin_unlock_irqrestore(&inter->ev_list_lock, flags);
+	}
+	mutex_unlock(&el->exist_lock);
+	return 0;
+}
+EXPORT_SYMBOL(iio_remove_event_from_list);
+
+int iio_allocate_chrdev(struct iio_handler *handler)
+{
+	int id;
+
+	spin_lock(&iio_state_lock);
+	for (id = 0; id <= 256; id++)
+		if (iio_state.fhs[id] == NULL)
+			break;
+	if (id == 256) {
+		spin_unlock(&iio_state_lock);
+		return -ENOMEM;
+	}
+	iio_state.fhs[id] = handler;
+	spin_unlock(&iio_state_lock);
+	handler->id = id;
+
+	return 0;
+}
+
+void iio_deallocate_chrdev(struct iio_handler *handler)
+{
+	spin_lock(&iio_state_lock);
+	iio_state.fhs[handler->id] = NULL;
+	spin_unlock(&iio_state_lock);
+}
+
+/* Upon open, switch in the correct file ops.
+ * Lifted directly from input subsystem (more or less)
+ */
+static int iio_open_file(struct inode *inode, struct file *file)
+{
+	struct iio_handler *handler;
+	const struct file_operations *old_fops, *new_fops = NULL;
+	int err;
+
+	/* This lock needed as  we are dynamically allocating chrdevs */
+	spin_lock(&iio_state_lock);
+	handler = iio_state.fhs[iminor(inode)];
+	spin_unlock(&iio_state_lock);
+
+	if (!handler) {
+		fops_put(file->f_op);
+		return -ENODEV;
+	}
+	new_fops = fops_get(handler->fops);
+	if (new_fops == NULL) {
+		fops_put(file->f_op);
+		return -ENODEV;
+	}
+
+	/* cribbed from lp.c */
+	if (test_and_set_bit(IIO_BUSY_BIT_POS, &handler->flags)) {
+		fops_put(file->f_op);
+		return -EBUSY;
+	}
+
+	if (!new_fops->open) {
+		fops_put(new_fops);
+		return -ENODEV;
+	}
+	old_fops = file->f_op;
+	file->f_op = new_fops;
+	/* use the private data pointer in file to give access to device
+	 * specific stuff */
+	file->private_data = handler->private;
+	err = new_fops->open(inode, file);
+
+	if (err) {
+		fops_put(file->f_op);
+		file->f_op = fops_get(old_fops);
+	}
+	fops_put(old_fops);
+
+	return err;
+}
+
+static const struct file_operations iio_fops = {
+	.owner = THIS_MODULE,
+	.open = iio_open_file,
+};
+
+/* File ops for event character device files */
+ssize_t iio_event_chrdev_read(struct file *filep,
+			      char *buf,
+			      size_t count,
+			      loff_t *f_ps)
+{
+	struct iio_event_interface *ev_int = filep->private_data;
+	struct iio_detected_event_list *el;
+	int ret;
+
+	mutex_lock(&ev_int->event_list_lock);
+	if (list_empty(&ev_int->det_events.list)) {
+		if (filep->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			goto error_mutex_unlock;
+		}
+		mutex_unlock(&ev_int->event_list_lock);
+		/* Blocking on device; waiting for something to be there */
+		ret = wait_event_interruptible(ev_int->wait,
+					       !list_empty(&ev_int
+							   ->det_events.list));
+		if (ret)
+			goto error_ret;
+		/* Single access device so noone else can get the data */
+		mutex_lock(&ev_int->event_list_lock);
+	}
+
+	el = list_first_entry(&ev_int->det_events.list,
+			      struct iio_detected_event_list,
+			      list);
+
+	if (copy_to_user(buf, &(el->ev),
+			 sizeof(struct iio_event_data))) {
+		ret = -EFAULT;
+		goto error_mutex_unlock;
+	}
+
+	list_del(&el->list);
+	ev_int->current_events--;
+	mutex_unlock(&ev_int->event_list_lock);
+
+	spin_lock(&el->shared_pointer->lock);
+	if (el->shared_pointer)
+		(el->shared_pointer->ev_p) = NULL;
+	spin_unlock(&el->shared_pointer->lock);
+
+	kfree(el);
+
+	return sizeof(struct iio_event_data);
+
+error_mutex_unlock:
+	mutex_unlock(&ev_int->event_list_lock);
+error_ret:
+
+	return ret;
+}
+
+int iio_event_chrdev_release(struct inode *inode, struct file *filep)
+{
+	struct iio_event_interface *ev_int = filep->private_data;
+
+	module_put(ev_int->owner);
+	clear_bit(IIO_BUSY_BIT_POS, &ev_int->handler.flags);
+
+	return 0;
+}
+
+int iio_event_chrdev_open(struct inode *inode, struct file *filep)
+{
+	struct iio_event_interface *ev_int = filep->private_data;
+
+	try_module_get(ev_int->owner);
+
+	return 0;
+}
+
+static const struct file_operations iio_event_chrdev_fileops = {
+	.read =  iio_event_chrdev_read,
+	.release = iio_event_chrdev_release,
+	.open = iio_event_chrdev_open,
+	.owner = THIS_MODULE,
+};
+
+ssize_t iio_show_attr_minor(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_chrdev_minor_attr *_attr
+		= to_iio_chrdev_minor_attr(attr);
+	return sprintf(buf, "%d\n", _attr->minor);
+}
+
+void __init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
+				const char *name,
+				struct module *owner,
+				int id)
+{
+	minor_attr->dev_attr.attr.name = name;
+	minor_attr->dev_attr.attr.owner = owner;
+	minor_attr->dev_attr.attr.mode = S_IRUGO;
+	minor_attr->minor = id;
+	minor_attr->dev_attr.show = &iio_show_attr_minor;
+}
+
+int iio_setup_ev_int(struct iio_event_interface *ev_int,
+		     const char *name,
+		     struct module *owner,
+		     struct device *dev)
+{
+	int ret;
+
+	mutex_init(&ev_int->event_list_lock);
+	/* discussion point - make this variable? */
+	ev_int->max_events = 10;
+	ev_int->current_events = 0;
+	INIT_LIST_HEAD(&ev_int->det_events.list);
+	init_waitqueue_head(&ev_int->wait);
+	ev_int->handler.fops = &iio_event_chrdev_fileops;
+	ev_int->handler.private = ev_int;
+	ev_int->handler.flags = 0;
+	ret = iio_allocate_chrdev(&ev_int->handler);
+	if (ret)
+		goto error_ret;
+	__init_iio_chrdev_minor_attr(&ev_int->attr,
+				     (const char *)(name),
+				     owner,
+				     ev_int->handler.id);
+
+	ret = sysfs_create_file(&dev->kobj, &ev_int->attr.dev_attr.attr);
+	if (ret)
+		goto error_deallocate_chrdev;
+
+	return 0;
+error_deallocate_chrdev:
+	iio_deallocate_chrdev(&ev_int->handler);
+error_ret:
+	return ret;
+}
+
+void iio_free_ev_int(struct iio_event_interface *ev_int, struct device *dev)
+{
+	sysfs_remove_file(&dev->kobj, &ev_int->attr.dev_attr.attr);
+	iio_deallocate_chrdev(&ev_int->handler);
+}
+
+static int __init iio_init(void)
+{
+	int ret;
+
+	memset(iio_state.fhs,
+	       sizeof(struct iio_handler *)*256,
+	       0);
+
+	/* Create sysfs class */
+	ret  = class_register(&iio_class);
+	if (ret < 0) {
+		printk(KERN_ERR
+		       "industrialio.c: could not create sysfs class\n");
+		goto error_nothing;
+	}
+
+	ret = register_chrdev(IIO_MAJOR, "industrialio", &iio_fops);
+	if (ret) {
+		printk(KERN_ERR
+		       "industrialio: unable to register a char major %d",
+		       IIO_MAJOR);
+		goto error_unregister_class;
+	}
+	return 0;
+
+error_unregister_class:
+	class_unregister(&iio_class);
+error_nothing:
+	return ret;
+}
+
+static void __exit iio_exit(void)
+{
+	unregister_chrdev(IIO_MAJOR, "industrialio");
+	class_unregister(&iio_class);
+}
+
+static int iio_device_register_sysfs(struct iio_dev *dev_info)
+{
+	int ret = 0;
+
+	dev_info->sysfs_dev = device_create(&iio_class,
+					    dev_info->dev,
+					    0,
+					    dev_info,
+					    IIO_ID_FORMAT,
+					    dev_info->id);
+	if (IS_ERR(dev_info->sysfs_dev)) {
+		/* what would correct error here be?*/
+		ret = -EINVAL;
+		dev_err(dev_info->dev, "Failed in device create \n");
+		goto error_ret;
+	}
+
+	/* register attributes */
+	ret = sysfs_create_group(&dev_info->sysfs_dev->kobj, dev_info->attrs);
+	if (ret) {
+		dev_err(dev_info->dev, "Failed to register sysfs hooks\n");
+		goto error_free_sysfs_device;
+	}
+
+	if (dev_info->scan_el_attrs) {
+		ret = sysfs_create_group(&dev_info->sysfs_dev->kobj,
+					 dev_info->scan_el_attrs);
+		if (ret)
+			dev_err(dev_info->dev,
+				"Failed to add sysfs scan els\n");
+	}
+	return ret;
+
+error_free_sysfs_device:
+	device_unregister(dev_info->sysfs_dev);
+error_ret:
+	return ret;
+}
+
+static void iio_device_unregister_sysfs(struct iio_dev *dev_info)
+{
+	if (dev_info->scan_el_attrs)
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   dev_info->scan_el_attrs);
+
+	sysfs_remove_group(&dev_info->sysfs_dev->kobj, dev_info->attrs);
+	device_unregister(dev_info->sysfs_dev);
+}
+
+static int iio_device_register_id(struct iio_dev *dev_info)
+{
+	int ret;
+
+idr_again:
+	if (unlikely(idr_pre_get(&iio_idr, GFP_KERNEL) == 0))
+		return -ENOMEM;
+
+	spin_lock(&iio_idr_lock);
+	ret = idr_get_new(&iio_idr, NULL, &dev_info->id);
+	spin_unlock(&iio_idr_lock);
+	if (unlikely(ret == -EAGAIN))
+		goto idr_again;
+	else if (unlikely(ret))
+		return ret;
+	dev_info->id = dev_info->id & MAX_ID_MASK;
+
+	return 0;
+}
+
+static void iio_device_unregister_id(struct iio_dev *dev_info)
+{
+	spin_lock(&iio_idr_lock);
+	idr_remove(&iio_idr, dev_info->id);
+	spin_unlock(&iio_idr_lock);
+}
+
+static inline int __iio_add_event_config_attrs(struct iio_dev *dev_info, int i)
+{
+	int ret;
+	/*p for adding, q for removing */
+	struct attribute **attrp, **attrq;
+
+	if (dev_info->event_conf_attrs && dev_info->event_conf_attrs[i].attrs) {
+		attrp = dev_info->event_conf_attrs[i].attrs;
+		while (*attrp) {
+			ret =  sysfs_add_file_to_group(&dev_info->sysfs_dev
+						       ->kobj,
+						       *attrp,
+						       dev_info
+						       ->event_attrs[i].name);
+			if (ret)
+				goto error_ret;
+			attrp++;
+		}
+	}
+	return 0;
+
+error_ret:
+	attrq = dev_info->event_conf_attrs[i].attrs;
+	while (attrq != attrp) {
+			sysfs_remove_file_from_group(&dev_info->sysfs_dev->kobj,
+					     *attrq,
+					     dev_info->event_attrs[i].name);
+		attrq++;
+	}
+
+	return ret;
+}
+
+static inline int __iio_remove_event_config_attrs(struct iio_dev *dev_info,
+						  int i)
+{
+	struct attribute **attrq;
+
+	if (dev_info->event_conf_attrs
+		&& dev_info->event_conf_attrs[i].attrs) {
+		attrq = dev_info->event_conf_attrs[i].attrs;
+		while (*attrq) {
+			sysfs_remove_file_from_group(&dev_info->sysfs_dev->kobj,
+						     *attrq,
+						     dev_info
+						     ->event_attrs[i].name);
+			attrq++;
+		}
+	}
+
+	return 0;
+}
+
+static int iio_device_register_eventset(struct iio_dev *dev_info)
+{
+	int ret = 0, i, j;
+
+	if (dev_info->num_interrupt_lines == 0)
+		return 0;
+
+	dev_info->event_interfaces = (struct iio_event_interface *)
+		(kzalloc(sizeof(struct iio_event_interface)
+			 *dev_info->num_interrupt_lines,
+			 GFP_KERNEL));
+	if (dev_info->event_interfaces == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	/* assign id's to the event_interface elements */
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+		dev_info->event_interfaces[i].id = i;
+		dev_info->event_interfaces[i].owner = dev_info->driver_module;
+	}
+	dev_info->interrupts
+		= kzalloc(sizeof(struct iio_interrupt *)
+			  *dev_info->num_interrupt_lines,
+			  GFP_KERNEL);
+	if (dev_info->interrupts == NULL) {
+		dev_err(dev_info->dev,
+			"Failed to register sysfs hooks for events attributes");
+		ret = -ENOMEM;
+		goto error_free_event_interfaces;
+	}
+
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+
+		snprintf(dev_info->event_interfaces[i]._name, 20,
+			"event_line%d_minor", i);
+		ret = iio_setup_ev_int(&dev_info->event_interfaces[i],
+				       (const char *)(dev_info
+						      ->event_interfaces[i]
+						      ._name),
+				       dev_info->driver_module,
+				       dev_info->sysfs_dev);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Could not get chrdev interface\n");
+			goto error_free_setup_ev_ints;
+		}
+	}
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+		snprintf(dev_info->event_interfaces[i]._attrname, 20,
+			"event_line%d_sources", i);
+		dev_info->event_attrs[i].name
+			= (const char *)
+			(dev_info->event_interfaces[i]._attrname);
+		ret = sysfs_create_group(&dev_info->sysfs_dev->kobj,
+					 &dev_info->event_attrs[i]);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Failed to register sysfs for event attrs");
+			goto error_remove_sysfs_interfaces;
+		}
+	}
+	for (i = 0; i < dev_info->num_interrupt_lines; i++) {
+		ret = __iio_add_event_config_attrs(dev_info, i);
+		if (ret)
+			goto error_unregister_config_attrs;
+	}
+
+
+
+	return 0;
+
+error_unregister_config_attrs:
+	for (j = 0; j < i; j++)
+		__iio_remove_event_config_attrs(dev_info, i);
+	i = dev_info->num_interrupt_lines - 1;
+error_remove_sysfs_interfaces:
+	for (j = 0; j < i; j++)
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   &dev_info->event_attrs[j]);
+
+	i = dev_info->num_interrupt_lines - 1;
+error_free_setup_ev_ints:
+	for (j = 0; j < i; j++)
+		iio_free_ev_int(&dev_info->event_interfaces[j],
+				dev_info->sysfs_dev);
+	kfree(dev_info->interrupts);
+error_free_event_interfaces:
+	kfree(dev_info->event_interfaces);
+error_ret:
+
+	return ret;
+}
+
+static void iio_device_unregister_eventset(struct iio_dev *dev_info)
+{
+	int i;
+	if (dev_info->num_interrupt_lines == 0)
+		return;
+	for (i = 0; i < dev_info->num_interrupt_lines; i++)
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   &dev_info->event_attrs[i]);
+
+	for (i = 0; i < dev_info->num_interrupt_lines; i++)
+		iio_free_ev_int(&dev_info->event_interfaces[i],
+					 dev_info->dev);
+	kfree(dev_info->event_interfaces);
+}
+
+
+int iio_device_register(struct iio_dev *dev_info)
+{
+	int ret;
+	mutex_init(&dev_info->mlock);
+	dev_set_drvdata(dev_info->dev, (void *)(dev_info));
+
+/*Get a unique id */
+	ret = iio_device_register_id(dev_info);
+	if (ret) {
+		dev_err(dev_info->dev, "Failed to get id\n");
+		goto error_nothing;
+	}
+
+/* Create sysfs device */
+	ret = iio_device_register_sysfs(dev_info);
+	if (ret) {
+		dev_err(dev_info->dev,
+			"Failed to register sysfs interfaces\n");
+		goto error_free_idr;
+	}
+
+/* Interrupt triggered events setup */
+	ret = iio_device_register_eventset(dev_info);
+	if (ret) {
+		dev_err(dev_info->dev,
+			"Failed to register event set \n");
+		goto error_free_sysfs;
+	}
+
+
+/* Ring buffer init if relevant */
+	if (dev_info->modes & (INDIO_RING_TRIGGERED
+			       | INDIO_RING_HARDWARE_BUFFER)) {
+		ret = iio_device_register_ring(dev_info, 0);
+		if (ret) {
+			dev_err(dev_info->dev,
+				"Failed to register ring");
+			goto error_free_eventset;
+		}
+	}
+
+/* Register ability to use triggers */
+	iio_device_register_trigger_consumer(dev_info);
+
+	return 0;
+
+error_free_eventset:
+	iio_device_unregister_eventset(dev_info);
+error_free_sysfs:
+	iio_device_unregister_sysfs(dev_info);
+error_free_idr:
+	iio_device_unregister_id(dev_info);
+error_nothing:
+
+	return ret;
+}
+EXPORT_SYMBOL(iio_device_register);
+
+void iio_device_unregister(struct iio_dev *dev_info)
+{
+	iio_device_unregister_trigger_consumer(dev_info);
+	if (dev_info->modes & (INDIO_RING_TRIGGERED
+			       | INDIO_RING_HARDWARE_BUFFER))
+		iio_device_unregister_ring(dev_info);
+	iio_device_unregister_eventset(dev_info);
+	iio_device_unregister_sysfs(dev_info);
+	iio_device_unregister_id(dev_info);
+}
+EXPORT_SYMBOL(iio_device_unregister);
+
+
+
+subsys_initcall(iio_init);
+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/industrialio/iio.h b/include/linux/industrialio/iio.h
new file mode 100644
index 0000000..f51f653
--- /dev/null
+++ b/include/linux/industrialio/iio.h
@@ -0,0 +1,440 @@
+/* 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.
+ */
+
+#ifndef _INDUSTRIAL_IO_H_
+#define _INDUSTRIAL_IO_H_
+
+#include <linux/device.h>
+#include <linux/industrialio/sysfs.h>
+#include <linux/industrialio/ring_generic.h>
+#include <linux/industrialio/chrdev.h>
+
+/* IIO TODO LIST */
+/* Static device specific elements (conversion factors etc)
+ * should be exported via sysfs
+ *
+ * Provide alternative to high resolution timers if not available.
+ */
+
+/* Event interface flags */
+#define IIO_BUSY_BIT_POS 1
+
+struct iio_dev;
+
+/* Requires high resolution timers */
+static inline s64 iio_get_time_ns(void)
+{
+	struct timespec ts;
+	ktime_get_ts(&ts);
+	return timespec_to_ns(&ts);
+}
+
+/**
+ * struct iio_event_handler_list - element in list handlers for events
+ * @list:		list header
+ * @handler:		event handler function - called on event if this
+ *			event_handler is enabled.
+ * @refcount:		as the handler may be shared between multiple device
+ *			side events, reference counting ensures clean removal
+ * @exist_lock:		prevents race conditions related to refcount useage.
+ *
+ * Each device has one list of these per interrupt line
+ **/
+struct iio_event_handler_list {
+	struct list_head list;
+	int (*handler)(struct iio_dev *dev_io, int index, s64 timestamp,
+		       int no_test);
+	/* This element may be shared */ /* NEEDS LOCK unfortunately */
+	int refcount;
+	struct mutex exist_lock;
+};
+
+/**
+ * iio_add_event_to_list() - Wraps adding to event lists
+ * @el:		the list element of the event to be handled.
+ * @head:	the list associated with the event handler being used.
+ *
+ * Does reference counting to allow shared handlers.
+ **/
+int iio_add_event_to_list(struct iio_event_handler_list *el,
+			   struct list_head *head);
+/**
+ * iio_remove_event_from_list() - Wraps removing from event list
+ * @el:		element to be removed
+ * @head:	associate list head for the interrupt handler.
+ *
+ * Does reference counting to allow shared handlers.  The awkward bit
+ * is access to the spinlock used to prevent the interrupt handler from
+ * running into problems with this list having changed.
+ **/
+int iio_remove_event_from_list(struct iio_event_handler_list *el,
+			       struct list_head *head);
+
+
+/* Device operating modes */
+#define INDIO_DIRECT_MODE		0x01
+#define INDIO_RING_TRIGGERED		0x02
+#define INDIO_RING_HARDWARE_BUFFER	0x08
+
+#define INDIO_ALL_RING_MODES (INDIO_RING_TRIGGERED | INDIO_RING_HARDWARE_BUFFER)
+
+/* Vast majority of this is set by the industrialio subsystem on a
+ * call to iio_device_register. */
+
+/**
+ * struct iio_dev - industrial I/O device
+ * @id:			[INTERN] used to identify device internally
+ * @dev_data:		[DRIVER] device specific data
+ * @modes:		[DRIVER] operating modes supported by device
+ * @currentmode:	[DRIVER] current operating mode
+ * @sysfs_dev:		[INTERN] device created by sysfs needed for management
+ * @dev:		[DRIVER] the actual device structure
+ * @attrs:		[DRIVER] device attributes
+ * @driver_module:	[DRIVER] module structure used to ensure correct
+ *			ownership of chrdevs etc
+ * @scan_el_attrs:	[DRIVER] control of scan elements if that scan mode
+ *			control method is used
+ * @num_interrupt_lines:[DRIVER] number of physical interrupt lines from device
+ * @interrupts:		[INTERN] interrupt line specific event lists etc
+ * @event_attrs:	[DRIVER] event control attributes
+ * @event_conf_attrs:	[DRIVER] event configuration attributes
+ * @event_interfaces:	[INTERN] event chrdevs associated with interrupt lines
+ * @ring_bytes_per_datum:[DRIVER] number of bytes per datum in ring buffer
+ * @ring_length:	[DRIVER] number of datums in ring buffer
+ * @ring_attrs:		[INTERN] ring related attributes
+ * @ring_prenable:	[DRIVER] function to run prior to marking ring enabled
+ * @ring_postenable:	[DRIVER] function to run after marking ring enabled
+ * @ring_predisable:	[DRIVER] function to run prior to marking ring disabled
+ * @ring_postdisable:	[DRIVER] function to run after marking ring disabled
+ * @mlock:		[INTERN] lock used to prevent simultaneous device state
+ *			changes
+ * @scan_count:	        [INTERN] the number of elements in the current scan mode
+ * @scan_mask:		[INTERN] bitmask used in masking scan mode elements
+ * @trig:		[INTERN] current device trigger (ring buffer modes)
+ * @pollfunc:		[DRIVER] function run on trigger being recieved
+ **/
+
+struct iio_dev {
+	int				id;
+	void				*dev_data;
+	int				modes;
+	int				currentmode;
+	struct device			*sysfs_dev;
+	struct device			*dev;
+	const struct attribute_group	*attrs;
+	struct module			*driver_module;
+
+	struct attribute_group		*scan_el_attrs;
+	int				num_interrupt_lines;
+	struct iio_interrupt		**interrupts;
+	struct attribute_group		*event_attrs;
+	struct attribute_group		*event_conf_attrs;
+
+	struct iio_event_interface	*event_interfaces;
+	struct iio_ring_access_funcs	ring_access;
+
+	void				*ring;
+	struct attribute_group		*ring_attrs_group;
+	struct iio_ring_attr		*ring_attrs;
+
+	int				(*ring_preenable)(struct iio_dev *);
+	int				(*ring_postenable)(struct iio_dev *);
+	int				(*ring_predisable)(struct iio_dev *);
+	int				(*ring_postdisable)(struct iio_dev *);
+
+	struct mutex			mlock;
+
+	int				scan_count;
+	u16				scan_mask;
+	bool				scan_timestamp;
+	struct iio_trigger		*trig;
+	struct iio_poll_func		*pollfunc;
+};
+
+/**
+ * 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);
+
+/**
+ * struct iio_interrupt - wrapper used to allow easy handling of multiple
+ *			physical interrupt lines
+ * @dev_info:		the iio device for which the is an interrupt line
+ * @linue_number:	associated line number
+ * @irq:		associate interrupt number
+ * @ev_list:		event handler list for associated events
+ **/
+struct iio_interrupt {
+	struct iio_dev			*dev_info;
+	int				line_number;
+	int				irq;
+	struct list_head		ev_list;
+	spinlock_t			ev_list_lock;
+};
+
+/**
+ * iio_register_interrupt_line() - Tell IIO about interrupt lines
+ *
+ * @irq:		Typically provided via platform data
+ * @dev_info:		IIO device info structure for device
+ * @line_number:	Which interrupt line of the device is this?
+ * @type:		Interrupt type (e.g. edge triggered etc)
+ * @name:		Identifying name.
+ **/
+int iio_register_interrupt_line(unsigned int			irq,
+				struct iio_dev			*dev_info,
+				int				line_number,
+				unsigned long			type,
+				const char			*name);
+
+void iio_unregister_interrupt_line(struct iio_dev *dev_info,
+				   int line_number);
+
+
+
+/**
+ * iio_push_event() - try to add event to the list for userspace reading
+ * @dev_info:		IIO device structure
+ * @ev_line:		Which event line (hardware interrupt)
+ * @ev_code:		What event
+ * @timestamp:		When the event occured
+ **/
+int iio_push_event(struct iio_dev *dev_info,
+		  int ev_line,
+		  int ev_code,
+		  s64 timestamp);
+
+/**
+ * struct iio_work_cont - container for case where singleton handler case matters
+ * @ws:			[DEVICE]work_struct when not only possible event
+ * @ws_nocheck:		[DEVICE]work_struct when only possible event
+ * @address:		[DEVICE]associated register address
+ * @mask:		[DEVICE]associated mask for identifying event source
+ * @st:			[DEVICE]device specific state information
+ **/
+struct iio_work_cont {
+	struct work_struct ws;
+	struct work_struct ws_nocheck;
+	int address;
+	int mask;
+	void *st;
+};
+
+#define to_iio_work_cont_check(_ws)			\
+	container_of(_ws, struct iio_work_cont, ws)
+
+#define to_iio_work_cont_no_check(_ws)				\
+	container_of(_ws, struct iio_work_cont, ws_nocheck)
+
+static inline void
+iio_init_work_cont(struct iio_work_cont *cont,
+		   void (*_checkfunc)(struct work_struct *),
+		   void (*_nocheckfunc)(struct work_struct *),
+		   int _add, int _mask, void *_st)
+{
+	INIT_WORK(&(cont)->ws, _checkfunc);
+	INIT_WORK(&(cont)->ws_nocheck, _nocheckfunc);
+	cont->address = _add;
+	cont->mask = _mask;
+	cont->st = _st;
+}
+/**
+ * __iio_push_event() tries to add an event to the list associated with a chrdev
+ * @ev_int:		the event interface to which we are pushing the event
+ * @ev_code:		the outgoing event code
+ * @timestamp:		timestamp of the event
+ * @shared_pointer_p:	the shared event pointer
+ **/
+int __iio_push_event(struct iio_event_interface *ev_int,
+		    int ev_code,
+		    s64 timestamp,
+		    struct iio_shared_ev_pointer*
+		    shared_pointer_p);
+/**
+ * __iio_change_event() change an event code in case of event escallation
+ * @ev:			the evnet to be changed
+ * @ev_code:		new event code
+ * @timestamp:		new timestamp
+ **/
+void __iio_change_event(struct iio_detected_event_list *ev,
+			int ev_code,
+			s64 timestamp);
+
+/**
+ * iio_setup_ev_int() Configure an event interface (chrdev)
+ *
+ * @ev_int:		Interface we are configuring
+ * @owner:		Module that is responsible for registering this ev_int
+ * @dev		Device whose ev_int this is
+ **/
+int iio_setup_ev_int(struct iio_event_interface *ev_int,
+		     const char *name,
+		     struct module *owner,
+		     struct device *dev);
+
+void iio_free_ev_int(struct iio_event_interface *ev_int,  struct device *dev);
+
+/**
+ * iio_allocate_chrdev() - Allocate a chrdev
+ *
+ * @hander:		Struct that contains relevant file handling for chrdev
+ **/
+int iio_allocate_chrdev(struct iio_handler *handler);
+void iio_deallocate_chrdev(struct iio_handler *handler);
+
+/**
+ * iio_show_attr_minor() sysfs read function to get the chrdev minor number
+ **/
+ssize_t iio_show_attr_minor(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf);
+
+/* Used to distinguish between bipolar and unipolar scan elemenents.
+ * Whilst this may seem obvious, we may well want to change the representation
+ * in the future!*/
+#define IIO_SIGNED(a) -(a)
+#define IIO_UNSIGNED(a) (a)
+struct iio_dev;
+
+/**
+ * struct iio_scan_el - an individual element of a scan
+ *
+ * @dev_attr:		control attribute (if directly controllable)
+ * @mask:		bitmask corresonding to this scan element
+ * @bit_count:		number of bits in scan element
+ * @label:		useful data for the scan el (often reg address)
+ * @set_state:		for some devices datardy signals are generated
+ *			for any enabled lines.  This allows unwanted lines
+ *			to be disabled and hence not get in the way.
+ **/
+struct iio_scan_el {
+	struct device_attribute		dev_attr;
+	unsigned int			mask;
+	int				bit_count;
+	unsigned int			label;
+
+	int (*set_state)(struct iio_scan_el *scanel,
+			 struct iio_dev *dev_info,
+			 bool state);
+};
+
+#define to_iio_scan_el(_dev_attr)				\
+	container_of(_dev_attr, struct iio_scan_el, dev_attr);
+
+/**
+ * iio_scan_el_store() - sysfs scan element selection interface.
+ *
+ * A generic function used to enable various scan elements.  In some
+ * devices explicit read commands for each channel mean this is merely
+ * a software switch.  In others this must actively disable the channel.
+ * Complexities occur when this interacts with data ready type triggers
+ * which may not reset unless every channel that is enabled is explicitly
+ * read.
+ **/
+ssize_t iio_scan_el_store(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t len);
+/**
+ * iio_scal_el_show() -	sysfs interface to query whether a scan element is
+ *			is enabled or not.
+ **/
+ssize_t iio_scan_el_show(struct device *dev, struct device_attribute *attr,
+			 char *buf);
+/**
+ * IIO_SCAN_EL: - declare and initialize a scan element without control func
+ * @_name:	identifying name. Resulting struct is iio_scan_el_##_name,
+ *		sysfs element, scan_en_##_name.
+ * @_number:	unique id number for the scan element.
+ * @_bits:	number of bits in the scan element result (used in mixed bit
+ *		length devices).
+ * @_label:	indentification variable used by drivers.  Often a reg address.
+ **/
+#define IIO_SCAN_EL(_name, _number, _bits, _label)			\
+	struct iio_scan_el iio_scan_el_##_name = {			\
+		.dev_attr = __ATTR(scan_en_##_name,			\
+				   S_IRUGO | S_IWUSR,			\
+				   iio_scan_el_show,			\
+				   iio_scan_el_store),			\
+		.mask = (1 << _number),					\
+		.bit_count = _bits,					\
+		.label = _label,					\
+	}
+
+ssize_t iio_scan_el_ts_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t len);
+
+ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr,
+			    char *buf);
+/**
+ * IIO_SCAN_EL_C: - declare and initialize a scan element with a control func
+ *
+ * @_controlfunc: function used to notify hardware of whether state changes
+ **/
+#define IIO_SCAN_EL_C(_name, _number, _bits, _label, _controlfunc)	\
+	struct iio_scan_el iio_scan_el_##_name = {			\
+		.dev_attr = __ATTR(scan_en_##_name,			\
+				   S_IRUGO | S_IWUSR,			\
+				   iio_scan_el_show,			\
+				   iio_scan_el_store),			\
+		.mask = (1 << _number),					\
+		.bit_count = _bits,					\
+		.label = _label,					\
+		.set_state = _controlfunc,				\
+	}
+/**
+ * IIO_SCAN_EL_TIMESTAMP: - declare a special scan element for timestamps
+ *
+ * Odd one out. Handled slightly differently from other scan elements.
+ **/
+#define IIO_SCAN_EL_TIMESTAMP					\
+	struct iio_scan_el iio_scan_el_timestamp = {		\
+		.dev_attr = __ATTR(scan_en_timestamp,		\
+				   S_IRUGO | S_IWUSR,		\
+				   iio_scan_el_ts_show,		\
+				   iio_scan_el_ts_store),	\
+	}
+
+extern struct class iio_class;
+
+/* Ring buffer related */
+#ifdef CONFIG_IIO_RING_BUFFER
+/**
+ * iio_ring_enabled() helper function to test if any form of ring enabled
+ **/
+static inline bool iio_ring_enabled(struct iio_dev *dev_info)
+{
+	return dev_info->currentmode
+		& (INDIO_RING_TRIGGERED
+		   | INDIO_RING_HARDWARE_BUFFER);
+};
+/**
+ * iio_device_register_ring() connect up all ring related elements
+ **/
+int iio_device_register_ring(struct iio_dev *dev_info, int id);
+void iio_device_unregister_ring(struct iio_dev *dev_info);
+
+#else
+static inline bool iio_ring_enabled(struct iio_dev *dev_info)
+{
+	return false;
+};
+static inline int iio_device_register_ring(struct iio_dev *devinfo, int id)
+{
+	return 0;
+};
+static inline void iio_device_unregister_ring(struct iio_dev *dev_info) {};
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* _INDUSTRIAL_IO_H_ */
diff --git a/include/linux/industrialio/sysfs.h b/include/linux/industrialio/sysfs.h
new file mode 100644
index 0000000..eba6439
--- /dev/null
+++ b/include/linux/industrialio/sysfs.h
@@ -0,0 +1,284 @@
+/* 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
+ */
+
+#ifndef _INDUSTRIAL_IO_SYSFS_H_
+#define _INDUSTRIAL_IO_SYSFS_H_
+
+#include <linux/industrialio/iio.h>
+
+/**
+ * struct iio_event_attribute - event control attribute
+ * @dev_attr:	underlying device attribute
+ * @mask:	mask for the event when detecting
+ * @listel:	list header to allow addition to list of event handlers
+*/
+struct iio_event_attr {
+	struct device_attribute dev_attr;
+	int mask;
+	struct iio_event_handler_list *listel;
+};
+
+#define to_iio_event_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_event_attr, dev_attr)
+
+/**
+ * struct iio_chrdev_minor_attr - simple attribute to allow reading of chrdev
+ *				minor number
+ * @dev_attr:	underlying device attribute
+ * @minor:	the minor number
+ */
+struct iio_chrdev_minor_attr {
+	struct device_attribute dev_attr;
+	int minor;
+};
+
+void
+__init_iio_chrdev_minor_attr(struct iio_chrdev_minor_attr *minor_attr,
+			   const char *name,
+			   struct module *owner,
+			   int id);
+
+
+#define to_iio_chrdev_minor_attr(_dev_attr) \
+	container_of(_dev_attr, struct iio_chrdev_minor_attr, dev_attr);
+
+/**
+ * struct iio_dev_attr - iio specific device attribute
+ * @dev_attr:	underlying device attribute
+ * @address:	associated register address
+ */
+struct iio_dev_attr {
+	struct device_attribute dev_attr;
+	int address;
+	int val2;
+};
+
+#define to_iio_dev_attr(_dev_attr)				\
+	container_of(_dev_attr, struct iio_dev_attr, dev_attr)
+
+ssize_t iio_read_const_attr(struct device *dev,
+			    struct device_attribute *attr,
+			    char *len);
+
+/**
+ * struct iio_const_attr - constant device specific attribute
+ *                         often used for things like available modes
+ */
+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)
+
+/* Some attributes will be hard coded (device dependant) and not require an
+   address, in these cases pass a negative */
+#define IIO_ATTR(_name, _mode, _show, _store, _addr)		\
+	{ .dev_attr = __ATTR(_name, _mode, _show, _store),	\
+	  .address = _addr }
+
+#define IIO_ATTR_2(_name, _mode, _show, _store, _addr, _val2)	\
+	{ .dev_attr = __ATTR(_name, _mode, _show, _store),	\
+			.address = _addr,			\
+			.val2 = _val2 }
+
+#define IIO_DEVICE_ATTR(_name, _mode, _show, _store, _addr)	\
+	struct iio_dev_attr iio_dev_attr_##_name		\
+	= IIO_ATTR(_name, _mode, _show, _store, _addr)
+
+
+#define IIO_DEVICE_ATTR_2(_name, _mode, _show, _store, _addr, _val2)	\
+	struct iio_dev_attr iio_dev_attr_##_name			\
+	= IIO_ATTR_2(_name, _mode, _show, _store, _addr, _val2)
+
+#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)}
+
+/* Generic attributes of onetype or another */
+
+/**
+ * IIO_DEV_ATTR_REG: revision number for the device
+ *
+ * Very much device dependent.
+ **/
+#define IIO_DEV_ATTR_REV(_show)			\
+	IIO_DEVICE_ATTR(revision, S_IRUGO, _show, NULL, 0)
+
+/**
+ * IIO_DEV_ATTR_SAMP_FREQ: sets any internal clock frequency
+ **/
+#define IIO_DEV_ATTR_SAMP_FREQ(_mode, _show, _store)			\
+	IIO_DEVICE_ATTR(sampling_frequency, _mode, _show, _store, 0)
+
+/**
+ * IIO_DEV_ATTR_AVAIL_SAMP_FREQ: list available sampling frequencies.
+ *
+ * May be mode dependant on some devices
+ **/
+#define IIO_DEV_ATTR_AVAIL_SAMP_FREQ(_show)				\
+	IIO_DEVICE_ATTR(available_sampling_frequency, S_IRUGO, _show, NULL, 0)
+
+/**
+ * IIO_DEV_ATTR_CONST_AVAIL_SAMP_FREQ: list available sampling frequencies.
+ *
+ * Constant version
+ **/
+#define IIO_CONST_ATTR_AVAIL_SAMP_FREQ(_string)	\
+	IIO_CONST_ATTR(available_sampling_frequency, _string)
+/**
+ * IIO_DEV_ATTR_SCAN_MODE: select a scan mode
+ *
+ * This is used when only certain combinations of inputs may be read in one
+ * scan.
+ **/
+#define IIO_DEV_ATTR_SCAN_MODE(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(scan_mode, _mode, _show, _store, 0)
+/**
+ * IIO_DEV_ATTR_AVAIL_SCAN_MODES: list available scan modes
+ **/
+#define IIO_DEV_ATTR_AVAIL_SCAN_MODES(_show)				\
+	IIO_DEVICE_ATTR(available_scan_modes, S_IRUGO, _show, NULL, 0)
+
+/**
+ * IIO_DEV_ATTR_SCAN: result of scan of multiple channels
+ **/
+#define IIO_DEV_ATTR_SCAN(_show)		\
+	IIO_DEVICE_ATTR(scan, S_IRUGO, _show, NULL, 0);
+
+/**
+ * IIO_DEV_ATTR_INPUT: direct read of a single input channel
+ **/
+#define IIO_DEV_ATTR_INPUT(_number, _show)				\
+	IIO_DEVICE_ATTR(in##_number, S_IRUGO, _show, NULL, _number)
+
+
+/**
+ * IIO_DEV_ATTR_SW_RING_ENABLE: enable software ring buffer
+ *
+ * Success may be dependant on attachment of trigger previously
+ **/
+#define IIO_DEV_ATTR_SW_RING_ENABLE(_show, _store)			\
+	IIO_DEVICE_ATTR(sw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
+
+/**
+ * IIO_DEV_ATTR_HW_RING_ENABLE: enable hardware ring buffer
+ *
+ * This is a different attribute from the software one as one can invision
+ * schemes where a combination of the two may be used.
+ **/
+#define IIO_DEV_ATTR_HW_RING_ENABLE(_show, _store)			\
+	IIO_DEVICE_ATTR(hw_ring_enable, S_IRUGO | S_IWUSR, _show, _store, 0)
+
+/**
+ * IIO_DEV_ATTR_RING_BPS: set number of bits per sample
+ **/
+#define IIO_DEV_ATTR_RING_BPS(_mode, _show, _store)		\
+	IIO_DEVICE_ATTR(ring_bps, _mode, _show, _store, 0)
+
+/**
+ * IIO_DEV_ATTR_RING_BPS_AVAILABLE: no of bits per sample supported
+ **/
+#define IIO_DEV_ATTR_RING_BPS_AVAILABLE(_show)				\
+	IIO_DEVICE_ATTR(ring_bps_available, S_IRUGO, _show, NULL, 0)
+
+/**
+ * IIO_DEV_ATTR_TEMP: many sensors have auxiliary temperature sensors
+ **/
+#define IIO_DEV_ATTR_TEMP(_show)			\
+	IIO_DEVICE_ATTR(temp, S_IRUGO, _show, NULL, 0)
+/**
+ * IIO_EVENT_SH: generic shared event handler
+ *
+ * This is used in cases where more than one event may result from a single
+ * handler.  Often the case that some alarm register must be read and multiple
+ * alarms may have been triggered.
+ **/
+#define IIO_EVENT_SH(_name, _handler)					\
+	static struct iio_event_handler_list				\
+	iio_event_##_name = {						\
+		.handler = _handler,					\
+		.refcount = 0,						\
+		.exist_lock = __MUTEX_INITIALIZER(iio_event_##_name	\
+						  .exist_lock),	\
+	};
+/**
+ * IIO_EVENT_ATTR_SH: generic shared event attribute
+ *
+ * An attribute with an associated IIO_EVENT_SH
+ **/
+#define IIO_EVENT_ATTR_SH(_name, _ev_list, _show, _store, _mask)	\
+	static struct iio_event_attr					\
+	iio_event_attr_##_name						\
+	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR,		\
+			       _show, _store),				\
+	    .mask = _mask,						\
+	    .listel = &_ev_list };
+
+/**
+ * IIO_EVENT_ATTR: non shared event attribute
+ **/
+#define IIO_EVENT_ATTR(_name, _show, _store, _mask, _handler)		\
+	static struct iio_event_handler_list				\
+	iio_event_##_name = {						\
+		.handler = _handler,					\
+	};								\
+	static struct							\
+	iio_event_attr							\
+	iio_event_attr_##_name						\
+	= { .dev_attr = __ATTR(_name, S_IRUGO | S_IWUSR,		\
+			       _show, _store),				\
+	    .mask = _mask,						\
+	    .listel = &iio_event_##_name };				\
+
+/**
+ * IIO_EVENT_ATTR_DATA_RDY: event driven by data ready signal
+ *
+ * Not typically implemented in devices where full triggering support
+ * has been implemented
+ **/
+#define IIO_EVENT_ATTR_DATA_RDY(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(data_rdy, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_CODE_DATA_RDY		100
+#define IIO_EVENT_CODE_RING_BASE	200
+#define IIO_EVENT_CODE_ACCEL_BASE	300
+#define IIO_EVENT_CODE_GYRO_BASE	400
+#define IIO_EVENT_CODE_ADC_BASE		500
+#define IIO_EVENT_CODE_MISC_BASE	600
+
+#define IIO_EVENT_CODE_DEVICE_SPECIFIC	1000
+
+/**
+ * IIO_EVENT_ATTR_RING_50_FULL: ring buffer event to indicate 50% full
+ **/
+#define IIO_EVENT_ATTR_RING_50_FULL(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(ring_50_full, _show, _store, _mask, _handler)
+
+/**
+ * IIO_EVENT_ATTR_RING_50_FULL_SH: shared ring event to indicate 50% full
+ **/
+#define IIO_EVENT_ATTR_RING_50_FULL_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ring_50_full, _evlist, _show, _store, _mask)
+
+/**
+ * IIO_EVENT_ATTR_RING_75_FULL_SH: shared ring event to indicate 75% full
+ **/
+#define IIO_EVENT_ATTR_RING_75_FULL_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(ring_75_full, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_RING_50_FULL	IIO_EVENT_CODE_RING_BASE
+#define IIO_EVENT_CODE_RING_75_FULL	(IIO_EVENT_CODE_RING_BASE + 1)
+#define IIO_EVENT_CODE_RING_100_FULL	(IIO_EVENT_CODE_RING_BASE + 2)
+
+#endif /* _INDUSTRIAL_IO_SYSFS_H_ */
diff --git a/include/linux/industrialio/chrdev.h b/include/linux/industrialio/chrdev.h
new file mode 100644
index 0000000..00fda2e
--- /dev/null
+++ b/include/linux/industrialio/chrdev.h
@@ -0,0 +1,91 @@
+/* The industrial I/O core - character device related
+ *
+ * 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.
+ */
+
+/* FIXME - inprecise name for this file */
+
+#ifndef _IIO_CHRDEV_H_
+#define _IIO_CHRDEV_H_
+/**
+ * struct iio_handler - Structure used to specify file operations
+ *			for a particular chrdev
+ * @fops:	file operations including read function
+ * @id:		the location in the handler table - used for deallocation.
+ * @flags:	file operations related flags including busy flag.
+ */
+struct iio_handler {
+	const struct file_operations *fops;
+	int id;
+	unsigned long flags;
+	void *private;
+};
+
+/**
+ * struct iio_event_data - The actual event being pushed to userspace
+ * @id:		event identifier
+ * @timestamp:	best estimate of time of event occurance (often from
+ *		the interrupt handler)
+ */
+struct iio_event_data {
+	int id;
+	s64 timestamp;
+};
+
+/**
+ * struct iio_detected_event_list - list element for events that have occured
+ * @list:		linked list header
+ * @ev:			the event itself
+ * @shared_pointer:	used when the event is shared - i.e. can be escallated
+ *			on demand (eg ring buffer 50%->100% full)
+ */
+struct iio_detected_event_list {
+	struct list_head list;
+	struct iio_event_data ev;
+	struct iio_shared_ev_pointer *shared_pointer;
+};
+/**
+ * struct iio_shared_ev_pointer - allows shared events to identify if currently
+ *				in the detected event list
+ * @ev_p:	pointer to detected event list element (null if not in list)
+ * @lock:	protect this element to prevent simultaneous edit and remove
+ */
+struct iio_shared_ev_pointer {
+	struct iio_detected_event_list	*ev_p;
+	spinlock_t lock;
+};
+
+/**
+ * struct iio_event_interface - chrdev interface for an event line
+ * @handler:		fileoperations and related control for the chrdev
+ * @wait:		wait queue to allow blocking reads of events
+ * @event_list_lock:	mutex to protect the list of detected events
+ * @det_events:		list of detected events
+ * @max_events:		maximum number of events before new ones are dropped
+ * @current_events:	number of events in detected list
+ * @id:			indentifier to allow the event interface to know which
+ *			physical line it corresponds to
+ * @owner:		ensure the driver module owns the file, not iio
+ * @private:		driver specific data
+ * @_name:		used internally to store the sysfs name for minor id
+ *			attribute
+ */
+struct iio_event_interface {
+	struct iio_handler			handler;
+	wait_queue_head_t			wait;
+	struct mutex				event_list_lock;
+	struct iio_detected_event_list		det_events;
+	int					max_events;
+	int					current_events;
+	int					id;
+	struct iio_chrdev_minor_attr		attr;
+	struct module				*owner;
+	void					*private;
+	char					_name[20];
+	char					_attrname[20];
+};
+#endif
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 2f557f5..ae35394 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -106,5 +106,7 @@ source "drivers/uio/Kconfig"
 
 source "drivers/xen/Kconfig"
 
+source "drivers/industrialio/Kconfig"
+
 source "drivers/staging/Kconfig"
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index fceb71a..41a7d1c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-y				+= i2c/
+obj-y				+= industrialio/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_POWER_SUPPLY)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
new file mode 100644
index 0000000..7be386c
--- /dev/null
+++ b/drivers/industrialio/Kconfig
@@ -0,0 +1,12 @@
+#
+# Industrial I/O subsytem configuration
+#
+
+menuconfig INDUSTRIALIO
+	tristate "Industrial I/O support"
+	---help---
+	  The industrial I/O subsystem provides a unified framework for
+	  drivers for many different types of embedded sensors using a
+	  number of different physical interfaces (i2c, spi etc). See
+	  Documentation/industrialio for more information.
+
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
new file mode 100644
index 0000000..579ac30
--- /dev/null
+++ b/drivers/industrialio/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the industrial I/O core.
+#
+
+obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
+industrialio-y := industrialio-core.o
diff --git a/include/linux/industrialio/trigger.h b/include/linux/industrialio/trigger.h
new file mode 100644
index 0000000..bca0678
--- /dev/null
+++ b/include/linux/industrialio/trigger.h
@@ -0,0 +1,68 @@
+/* The industrial I/O core, trigger handling functions
+ *
+ * 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.
+ */
+#ifndef _IIO_TRIGGER_H_
+#define _IIO_TRIGGER_H_
+#define IIO_TRIGGER_NAME_LENGTH 20
+#define IIO_TRIGGER_ID_PREFIX "iio:trigger"
+#define IIO_TRIGGER_ID_FORMAT IIO_TRIGGER_ID_PREFIX "%d"
+
+#ifdef CONFIG_IIO_TRIGGERS
+#else
+struct iio_trigger{};
+static inline int iio_device_register_trigger_consumer(struct iio_dev *dev_info)
+{
+	return 0;
+};
+
+static inline int
+iio_device_unregister_trigger_consumer(struct iio_dev *dev_info)
+{
+	return 0;
+}
+
+
+static inline int iio_trigger_attach_poll_func(struct iio_trigger *trig,
+					       struct iio_poll_func *pf)
+{
+	return 0;
+}
+
+static inline int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
+						struct iio_poll_func *pf)
+{
+	return 0;
+}
+#endif
+
+/**
+ * struct iio_poll_func - poll function pair
+ *
+ * @list:			associate this with a triggers pollfunc_list
+ * @private_data:		data specific to device (passed into poll func)
+ * @poll_func_immediate:	function in here is run first. They should be
+ *				extremely lightweight.  Typically used for latch
+ *				control on sensor supporting it.
+ * @poll_func_main:		function in here is run after all immediates.
+ *				Reading from sensor etc typically involves
+ *				scheduling
+ *				from here.
+ *
+ * The two stage approach used here only important when multiple sensors are
+ * being triggered by a single trigger. This really comes into it's own with
+ * simultaneous sampling devices where a simple latch command can be used to
+ * make the device store the values on all inputs.
+ **/
+struct iio_poll_func {
+	struct				list_head list;
+	void				*private_data;
+	void (*poll_func_immediate)(struct iio_dev *indio_dev);
+	void (*poll_func_main)(struct iio_dev  *private_data);
+
+};
+#endif /* _IIO_TRIGGER_H_ */
diff --git a/include/linux/industrialio/ring_generic.h b/include/linux/industrialio/ring_generic.h
new file mode 100644
index 0000000..c79e2dd
--- /dev/null
+++ b/include/linux/industrialio/ring_generic.h
@@ -0,0 +1,24 @@
+/* The industrial I/O core - generic ring buffer interfaces.
+ *
+ * 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.
+ */
+
+#ifndef _IIO_RING_GENERIC_H_
+#define _IIO_RING_GENERIC_H_
+
+#include <linux/industrialio/chrdev.h>
+
+struct iio_ring_buffer;
+
+#ifdef CONFIG_IIO_RING_BUFFER
+#else
+
+struct iio_ring_buffer {};
+struct iio_ring_access_funcs {};
+#endif /* CONFIG_IIO_RING_BUFFER */
+
+#endif /* _IIO_RING_GENERIC_H_ */

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

* [Industrial I/O] [2/13] RFC: IIO ring buffer support (core)
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
  2008-12-01 14:23 ` [Industrial I/O] [1/13] RFC: IIO core functionality Jonathan Cameron
@ 2008-12-01 14:25 ` Jonathan Cameron
  2008-12-01 14:27 ` [Industrial I/O] [3/13] RFC: IIO Software ring buffer implementation Jonathan Cameron
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:25 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

The Industrial I/O core - ring buffer support

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>

--
This patch adds ring buffer support to the industrial I/O subsystem
core. This functionality may be disabled when not desired.

The support is designed to be generic enough to allow multiple
software implementations. At the moment only one is available so
the functionality for run time or board conf based selection has
not yet been implemented.

The framework also allows for hardware ring buffers existing on the
actual chip. An example of this usage is the VTI sca3000 driver found
later in this patch set.

 drivers/industrialio/Kconfig              |    7
 drivers/industrialio/Makefile             |    1
 drivers/industrialio/industrialio-ring.c  |  491 ++++++++++++++++++++++++++++++
 include/linux/industrialio/ring_generic.h |  123 +++++++
 4 files changed, 622 insertions(+)


diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
index 433f777..d132ab6 100644
--- a/drivers/industrialio/Kconfig
+++ b/drivers/industrialio/Kconfig
@@ -10,3 +10,10 @@ menuconfig INDUSTRIALIO
 	  number of different physical interfaces (i2c, spi etc). See
 	  Documentation/industrialio for more information.
 
+config IIO_RING_BUFFER
+       bool "Enable ring buffer support within iio"
+	  depends on INDUSTRIALIO
+	  help
+	    Provide support for various ring buffer based data
+	    acquisition methods.
+
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
index 1acd8fa..1f47af3 100644
--- a/drivers/industrialio/Makefile
+++ b/drivers/industrialio/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
 industrialio-y := industrialio-core.o
+industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
diff --git a/drivers/industrialio/industrialio-ring.c b/drivers/industrialio/industrialio-ring.c
new file mode 100644
index 0000000..04a8cb1
--- /dev/null
+++ b/drivers/industrialio/industrialio-ring.c
@@ -0,0 +1,491 @@
+/* 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.
+ *
+ * Handling of ring allocation / resizing.
+ *
+ *
+ * Things to look at here.
+ * - Better memory allocation techniques?
+ * - Alternative access techniques?
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/ring_generic.h>
+
+/* Allow for 3 digit id */
+#define IIO_RING_AC_MINOR_N_LEN 22
+#define IIO_RING_AC_MINOR_N "ring_buf%d_acc_minor"
+#define IIO_RING_EV_MINOR_N_LEN 21
+#define IIO_RING_EV_MINOR_N "ring_buf%d_ev_minor"
+
+int iio_push_ring_event(struct iio_ring_buffer *ring_buf,
+		       int event_code,
+		       s64 timestamp)
+{
+	return __iio_push_event(&ring_buf->ev_int,
+			       event_code,
+			       timestamp,
+			       &ring_buf->shared_ev_pointer);
+}
+EXPORT_SYMBOL(iio_push_ring_event);
+
+int iio_push_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
+				    int event_code,
+				    s64 timestamp)
+{
+	if (ring_buf->shared_ev_pointer.ev_p)
+		__iio_change_event(ring_buf->shared_ev_pointer.ev_p,
+				   event_code,
+				   timestamp);
+	else
+		return iio_push_ring_event(ring_buf,
+					  event_code,
+					  timestamp);
+	return 0;
+}
+EXPORT_SYMBOL(iio_push_or_escallate_ring_event);
+
+/**
+ * iio_ring_open() chrdev file open for ring buffer access
+ *
+ * This function relies on all ring buffer implementations having an
+ * iio_ring_buffer as their first element.
+ **/
+int iio_ring_open(struct inode *inode, struct file *filp)
+{
+	void *r = filp->private_data;
+	struct iio_ring_buffer *rb = r;
+
+	if (rb->ring_access->mark_in_use)
+		rb->ring_access->mark_in_use(r);
+	try_module_get(rb->access_minor_attr.dev_attr.attr.owner);
+
+	return 0;
+}
+
+/**
+ * iio_ring_release() chrdev file close ring buffer access
+ *
+ * This function relies on all ring buffer implementations having an
+ * iio_ring_buffer as their first element.
+ **/
+int iio_ring_release(struct inode *inode, struct file *filp)
+{
+	void *r = filp->private_data;
+	struct iio_ring_buffer *rb = r;
+
+	module_put(rb->access_minor_attr.dev_attr.attr.owner);
+	clear_bit(IIO_BUSY_BIT_POS, &rb->access_handler.flags);
+	if (rb->ring_access->unmark_in_use)
+		rb->ring_access->unmark_in_use(r);
+
+	return 0;
+}
+
+/**
+ * iio_ring_rip_outer() chrdev read for ring buffer access
+ *
+ * This function relies on all ring buffer implementations having an
+ * iio_ring _bufer as their first element.
+ **/
+ssize_t iio_ring_rip_outer(struct file *filp,
+			   char *buf,
+			   size_t count,
+			   loff_t *f_ps)
+{
+	void *r = filp->private_data;
+	struct iio_ring_buffer *rb = r;
+	int ret, dead_offset, copied;
+	u8 *data;
+	/* rip lots must exist. */
+	if (!rb->ring_access->rip_lots)
+		return -EINVAL;
+	copied = rb->ring_access->rip_lots(r, count, &data, &dead_offset);
+	if (copied < 0) {
+		ret = copied;
+		goto error_ret;
+	}
+	if (copy_to_user(buf, data + dead_offset, copied))  {
+		ret =  -EFAULT;
+		goto error_free_data_cpy;
+	}
+	/* In clever ring buffer designs this may not need to be freed.
+	 * When such a design exists I'll add this to ring access funcs. */
+	kfree(data);
+
+	return copied;
+
+error_free_data_cpy:
+	kfree(data);
+error_ret:
+	return ret;
+}
+
+static const struct file_operations iio_ring_fileops = {
+	.read = iio_ring_rip_outer,
+	.release = iio_ring_release,
+	.open = iio_ring_open,
+	.owner = THIS_MODULE,
+};
+
+/**
+ * __iio_request_ring_buffer_event_chrdev() allocate ring event chrdev
+ * @buf:	ring buffer whose event chrdev we are allocating
+ * @owner:	the module who owns the ring buffer (for ref counting)
+ * @dev:	device with which the chrdev is associated
+ **/
+static inline int
+__iio_request_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
+				       int id,
+				       struct module *owner,
+				       struct device *dev)
+{
+	int ret;
+
+	buf->event_minor_name = kmalloc(IIO_RING_EV_MINOR_N_LEN, GFP_KERNEL);
+	if (buf->event_minor_name == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	sprintf(buf->event_minor_name, IIO_RING_EV_MINOR_N, id);
+	ret = iio_setup_ev_int(&(buf->ev_int),
+			       (const char *)(buf->event_minor_name),
+			       owner,
+			       dev);
+	if (ret)
+		goto error_free_event_minor_name;
+
+	return 0;
+
+error_free_event_minor_name:
+	kfree(buf->event_minor_name);
+error_ret:
+	return ret;
+}
+
+static inline void
+__iio_free_ring_buffer_event_chrdev(struct iio_ring_buffer *buf,
+				    struct device *dev)
+{
+	iio_free_ev_int(&(buf->ev_int), dev);
+	kfree(buf->event_minor_name);
+}
+
+static inline int
+__iio_request_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
+					int id,
+					struct module *owner,
+					struct device *dev,
+					const struct file_operations *fops)
+{
+	int ret;
+/* Create and register the access character device */
+	buf->access_minor_name = kmalloc(IIO_RING_AC_MINOR_N_LEN,
+					 GFP_KERNEL);
+	if (buf->access_minor_name == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	sprintf(buf->access_minor_name, IIO_RING_AC_MINOR_N, id);
+
+	ret = iio_allocate_chrdev(&buf->access_handler);
+	if (ret)
+		goto error_free_access_minor_name;
+	buf->access_handler.fops = fops;
+	buf->access_handler.flags = 0;
+
+	__init_iio_chrdev_minor_attr(&buf->access_minor_attr,
+				   (const char *)(buf->access_minor_name),
+				   owner,
+				   buf->access_handler.id);
+
+	ret = sysfs_create_file(&dev->kobj,
+				&(buf->access_minor_attr.dev_attr.attr));
+	if (ret)
+		goto error_deallocate_chrdev;
+	return 0;
+
+error_deallocate_chrdev:
+	iio_deallocate_chrdev(&buf->access_handler);
+error_free_access_minor_name:
+	kfree(buf->access_minor_name);
+error_ret:
+	return ret;
+}
+
+static inline void
+__iio_free_ring_buffer_access_chrdev(struct iio_ring_buffer *buf,
+				     struct device *dev)
+{
+	sysfs_remove_file(&dev->kobj,
+			  &buf->access_minor_attr.dev_attr.attr);
+	iio_deallocate_chrdev(&buf->access_handler);
+	kfree(buf->access_minor_name);
+}
+
+static int iio_request_ring_buffer(struct iio_dev *dev_info,
+				   int id,
+				   struct module *owner,
+				   struct device *dev)
+{
+	int ret;
+	struct iio_ring_buffer *rb;
+	ret = dev_info->ring_access._create(&dev_info->ring);
+	if (ret)
+		goto error_ret;
+	rb = dev_info->ring;
+	/* Not ideal */
+	rb->ring_access = &dev_info->ring_access;
+	/* Initially mark for update of ring parameters */
+	if (dev_info->ring_access.mark_param_change)
+		dev_info->ring_access.mark_param_change(dev_info->ring);
+
+	ret = __iio_request_ring_buffer_event_chrdev(rb,
+						     id,
+						     owner,
+						     dev_info->sysfs_dev);
+	if (ret)
+		goto error_free_ring;
+
+	ret = __iio_request_ring_buffer_access_chrdev(rb,
+						      id,
+						      owner,
+						      dev_info->sysfs_dev,
+						      &iio_ring_fileops);
+	if (ret)
+		goto error_free_ring_buffer_event_chrdev;
+
+	rb->ev_int.private = rb;
+	rb->access_handler.private = rb;
+
+	return 0;
+error_free_ring_buffer_event_chrdev:
+	__iio_free_ring_buffer_event_chrdev(rb, dev_info->sysfs_dev);
+error_free_ring:
+	if (dev_info->ring_access._free)
+		dev_info->ring_access._free(dev_info->ring);
+error_ret:
+	return ret;
+}
+
+static void iio_unrequest_ring_buffer(struct iio_dev *dev_info)
+{
+
+	__iio_free_ring_buffer_event_chrdev(dev_info->ring, dev_info->dev);
+	if (dev_info->ring_access._free)
+		dev_info->ring_access._free(dev_info->ring);
+}
+
+static ssize_t iio_read_ring_length(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	int len = 0;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+
+	if (dev_info->ring_access.get_length)
+		len = sprintf(buf, "%d\n",
+			      dev_info->ring_access.get_length(dev_info->ring));
+
+	return len;
+}
+
+static ssize_t iio_write_ring_length(struct device *dev,
+					      struct device_attribute *attr,
+					      const char *buf,
+					      size_t len)
+{
+	int ret;
+	ulong val;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct iio_ring_access_funcs *ra = &dev_info->ring_access;
+	ret = strict_strtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (dev_info->ring_access.get_length)
+		if (val == ra->get_length(dev_info->ring))
+			return len;
+
+	if (ra->set_length) {
+		ra->set_length(dev_info->ring, val);
+		if (ra->mark_param_change)
+			ra->mark_param_change(dev_info->ring);
+	}
+
+	return len;
+}
+
+static ssize_t iio_read_ring_bps(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	int len = 0;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct iio_ring_access_funcs *ra = &dev_info->ring_access;
+
+	if (ra->get_bpd)
+		len = sprintf(buf, "%d\n",
+			      ra->get_bpd(dev_info->ring));
+
+	return len;
+}
+
+
+static DEVICE_ATTR(length, S_IRUGO | S_IWUSR,
+	    iio_read_ring_length,
+	    iio_write_ring_length);
+/* The software ring buffers aren't currently capable of changing the
+ * storage accuracy so this is read only. Note bps can change due to
+ * scan mode changes.
+ */
+static DEVICE_ATTR(bps, S_IRUGO, iio_read_ring_bps, NULL);
+
+static ssize_t iio_store_ring_enable(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf,
+				     size_t len)
+{
+	int ret;
+	bool requested_state, current_state;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct iio_ring_access_funcs *ra = &dev_info->ring_access;
+
+	mutex_lock(&dev_info->mlock);
+	requested_state = (buf[0] == '0') ? 0 : 1;
+	current_state = !!(dev_info->currentmode & INDIO_ALL_RING_MODES);
+	if (current_state == requested_state)
+		goto done;
+	if (requested_state) {
+		if (dev_info->ring_preenable) {
+			ret = dev_info->ring_preenable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+		if (ra->request_update) {
+			ret = ra->request_update(dev_info->ring);
+			if (ret)
+				goto error_ret;
+		}
+		if (ra->mark_in_use)
+			ra->mark_in_use(dev_info->ring);
+		/* Definitely possible for devices to support both of these.*/
+		if (dev_info->modes & INDIO_RING_TRIGGERED)
+			dev_info->currentmode = INDIO_RING_TRIGGERED;
+		else if (dev_info->modes & INDIO_RING_HARDWARE_BUFFER)
+			dev_info->currentmode = INDIO_RING_HARDWARE_BUFFER;
+		else { /* should never be reached */
+			ret = -EINVAL;
+			goto error_ret;
+		}
+
+		if (dev_info->ring_postenable) {
+			ret = dev_info->ring_postenable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+	} else {
+		if (dev_info->ring_predisable) {
+			ret = dev_info->ring_predisable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+		if (ra->unmark_in_use)
+			ra->unmark_in_use(dev_info->ring);
+		dev_info->currentmode = INDIO_DIRECT_MODE;
+		if (dev_info->ring_postdisable) {
+			ret = dev_info->ring_postdisable(dev_info);
+			if (ret)
+				goto error_ret;
+		}
+	}
+done:
+	mutex_unlock(&dev_info->mlock);
+	return len;
+
+error_ret:
+	mutex_unlock(&dev_info->mlock);
+	return ret;
+}
+
+static ssize_t iio_show_ring_enable(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!(indio_dev->currentmode
+				       & INDIO_ALL_RING_MODES));
+}
+
+DEVICE_ATTR(ring_enable, S_IRUGO | S_IWUSR,
+	    iio_show_ring_enable,
+	    iio_store_ring_enable);
+
+/* Standard set of ring buffer attributes */
+static struct attribute *iio_ring_attributes[] = {
+	&dev_attr_length.attr,
+	&dev_attr_bps.attr,
+	&dev_attr_ring_enable.attr,
+	NULL,
+};
+
+static const struct attribute_group iio_ring_attribute_group = {
+	.name = "ring_buffer",
+	.attrs = iio_ring_attributes,
+};
+
+int iio_device_register_ring(struct iio_dev *dev_info, int id)
+{
+	int ret = 0;
+	struct iio_ring_access_funcs *ra = &dev_info->ring_access;
+
+	ret = iio_request_ring_buffer(dev_info, id, dev_info->driver_module,
+				      dev_info->dev);
+	if (ret)
+		goto error_ret;
+	if (ra->_init) {
+		ret = ra->_init(dev_info->ring, dev_info);
+		if (ret)
+			goto error_unrequest_ring;
+	}
+	ret = sysfs_create_group(&dev_info->sysfs_dev->kobj,
+				 &iio_ring_attribute_group);
+	if (ret)
+		goto error_exit_ring;
+
+	return 0;
+
+error_exit_ring:
+	if (ra->_exit)
+		ra->_exit(dev_info->ring);
+error_unrequest_ring:
+	iio_unrequest_ring_buffer(dev_info);
+error_ret:
+	return ret;
+}
+
+void iio_device_unregister_ring(struct iio_dev *dev_info)
+{
+	struct iio_ring_access_funcs *ra = &dev_info->ring_access;
+	struct iio_ring_buffer *rb = dev_info->ring;;
+	if (dev_info->ring) {
+		sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+				   &iio_ring_attribute_group);
+		__iio_free_ring_buffer_access_chrdev(rb, dev_info->sysfs_dev);
+		__iio_free_ring_buffer_event_chrdev(rb, dev_info->sysfs_dev);
+		if (ra->_exit)
+			ra->_exit(dev_info->ring);
+		iio_unrequest_ring_buffer(dev_info);
+	}
+}
diff --git a/include/linux/industrialio/ring_generic.h b/include/linux/industrialio/ring_generic.h
index 6daff14..cbbce58 100644
--- a/include/linux/industrialio/ring_generic.h
+++ b/include/linux/industrialio/ring_generic.h
@@ -15,6 +15,129 @@
 struct iio_ring_buffer;
 
 #ifdef CONFIG_IIO_RING_BUFFER
+
+/**
+ * iio_push_ring_event() - ring buffer specific push to event chrdev
+ * @ring_buf:		ring buffer that is the event source
+ * @event_code:		event indentification code
+ * @timestamp:		time of event
+ **/
+int iio_push_ring_event(struct iio_ring_buffer *ring_buf,
+			int event_code,
+			s64 timestamp);
+/**
+ * iio_push_or_escallate_ring_event() -	escallate or add as appropriate
+ *
+ * Typical usecase is to escallate a 50% ring full to 75% full if noone has yet
+ * read the first event. Clearly the 50% full is no longer of interest in
+ * typical use case.
+ **/
+int iio_push_or_escallate_ring_event(struct iio_ring_buffer *ring_buf,
+				     int event_code,
+				     s64 timestamp);
+
+/**
+ * struct iio_ring_access_funcs - access functions for ring buffers.
+ * @create:		perform allocation
+ * @init:		get ring buffer ready for use
+ * @_exit:		reverse steps in init
+ * @_free:		deallocate ring buffer
+ * @mark_in_use:	reference counting, typically to prevent module removal
+ * @unmark_in_use:	reduce reference count when no longer using ring buffer
+ * @store_to:		actually store stuff to the ring buffer
+ * @read_last:		get the last element stored
+ * @rip_lots:		try to get a specified number of elements (must exist)
+ * @mark_param_change:	notify ring that some relevant parameter has changed
+ *			Often this means the underlying storage may need to
+ *			change.
+ * @request_update:	if a parameter change has been marked, update underlying
+ *			storage.
+ * @get_bpd:		get current bytes per datum
+ * @set_bpd:		set number of bytes per datum
+ * @get_length:		get number of datums in ring
+ * @set_length:		set number of datums in ring
+ * @is_enabled:		query if ring is currently being used
+ * @enable:		enable the ring
+ *
+ * The purpose of this structure is to make the ring buffer element
+ * modular as event for a given driver, different usecases may require
+ * different ring designs (space efficiency vs speed for example.
+ *
+ * It is worth noting that a given ring implementation may only support a small
+ * proportion of these functions.  The core code 'should' cope fine with any of
+ * them not existing.
+ **/
+struct iio_ring_access_funcs {
+	int (*_create)(void **ring);
+	int (*_init)(void *ring, void *indio_dev);
+	void (*_exit)(void *ring);
+	void (*_free)(void *ring);
+
+	void (*mark_in_use)(void *ring);
+	void (*unmark_in_use)(void *ring);
+
+	int (*store_to)(void *ring, u8 *data, s64 timestamp);
+	int (*read_last)(void *ring, u8 *data);
+	int (*rip_lots)(void *ring, size_t count, u8 **data, int *dead_offset);
+
+	int (*mark_param_change)(void *ring);
+	int (*request_update)(void *ring);
+
+	int (*get_bpd)(void *ring);
+	int (*set_bpd)(void *ring, size_t bpd);
+	int (*get_length)(void *ring);
+	int (*set_length)(void *ring, int length);
+
+	int (*is_enabled)(void *ring);
+	int (*enable)(void *ring);
+};
+
+/**
+ * struct iio_ring_buffer - general ring buffer structure
+ * @length:		[DEVICE]number of datums in ring
+ * @bpd:		[DEVICE]size of individual datum including timestamp
+ * @loopcount:		[INTERN]number of times the ring has looped
+ * @access_minor_name:	[INTERN]store of name of the access chrdev minor number
+ *			sysfs attribute
+ * @access_handler:	[INTERN]chrdev access handling
+ * @event_minor_name:	[INTERN]store of name of the event chrdev minor number
+ *			sysfs attribute
+ * @ev_int:		[INTERN]chrdev interface for the event chrdev
+ * @shared_ev_pointer:	[INTERN]the shared event pointer to allow escalation of
+ *			events
+ * @ring_access:	[DRIVER]ring access functions associated with the
+ *			implementation.
+ *
+ * This structure must be the first element in any ring buffer implementation
+ * structure as they must be castable to this.
+ **/
+struct iio_ring_buffer {
+	int				length;
+	int				bpd;
+	int				loopcount;
+	char				*access_minor_name;
+	struct iio_chrdev_minor_attr	access_minor_attr;
+	struct iio_handler		access_handler;
+	char				*event_minor_name;
+	struct iio_event_interface	ev_int;
+	struct iio_shared_ev_pointer	shared_ev_pointer;
+	struct iio_ring_access_funcs    *ring_access;
+};
+
+/**
+ * __iio_init_ring_buffer() - initialize common elements of ring buffers.
+ **/
+static inline void __iio_init_ring_buffer(struct iio_ring_buffer *ring,
+				 int bytes_per_datum, int length)
+{
+	ring->bpd = bytes_per_datum;
+	ring->length = length;
+	ring->loopcount = 0;
+	ring->shared_ev_pointer.ev_p = 0;
+	ring->shared_ev_pointer.lock =
+		__SPIN_LOCK_UNLOCKED(ring->shared_ev_pointer->loc);
+}
+
 #else
 
 struct iio_ring_buffer {};

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

* [Industrial I/O] [3/13] RFC: IIO Software ring buffer implementation
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
  2008-12-01 14:23 ` [Industrial I/O] [1/13] RFC: IIO core functionality Jonathan Cameron
  2008-12-01 14:25 ` [Industrial I/O] [2/13] RFC: IIO ring buffer support (core) Jonathan Cameron
@ 2008-12-01 14:27 ` Jonathan Cameron
  2008-12-01 14:29 ` [Industrial I/O] [4/13] RFC: IIO trigger support Jonathan Cameron
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:27 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

IIO: Software ring buffer implementation.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>

--

Intended to act as an example of how a software ring buffer might
function rather than as a definitive solution. The particular
focus of this was an application that required relatively hight
sampling rates (couple of Ksamples) with low computational load
and few dropped samples.

To my mind this is the most flakey element of this patch set.
All suggestions on alternative approaches / simplifications
would be most welcome. My intention is to also provide a far
simple locked ring buffer (hence all accesses in bh of interrupt
handlers) to act as a very simple example for people coding up
there own but this is not yet in place.

 drivers/industrialio/Kconfig         |    9
 drivers/industrialio/Makefile        |    4
 drivers/industrialio/ring_sw.c       |  370 +++++++++++++++++++++++++++++++++++
 include/linux/industrialio/ring_sw.h |  183 +++++++++++++++++
 4 files changed, 565 insertions(+), 1 deletion(-)

diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
index d132ab6..73c5616 100644
--- a/drivers/industrialio/Kconfig
+++ b/drivers/industrialio/Kconfig
@@ -17,3 +17,12 @@ config IIO_RING_BUFFER
 	    Provide support for various ring buffer based data
 	    acquisition methods.
 
+config IIO_SW_RING
+       tristate "Industrial I/O lock free software ring"
+       depends on IIO_RING_BUFFER && INDUSTRIALIO
+
+       ---help---
+	example software ring buffer implementation.  The design aim
+	of this particular realization was to minize write locking
+	with the intention that some devices would be able to write
+	in interrupt context.
\ No newline at end of file
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
index 1f47af3..0fda138 100644
--- a/drivers/industrialio/Makefile
+++ b/drivers/industrialio/Makefile
@@ -3,5 +3,7 @@
 #
 
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
-industrialio-y := industrialio-core.o
+industrialio-y := industrialio-core.o 
 industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
+
+obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
diff --git a/drivers/industrialio/ring_sw.c b/drivers/industrialio/ring_sw.c
new file mode 100644
index 0000000..347ace1
--- /dev/null
+++ b/drivers/industrialio/ring_sw.c
@@ -0,0 +1,370 @@
+/* The industrial I/O simple minimally locked ring buffer.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/industrialio/ring_sw.h>
+
+static inline int __iio_init_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
+					    int bytes_per_datum, int length)
+{
+	__iio_init_ring_buffer(&ring->buf, bytes_per_datum, length);
+	ring->use_lock = __SPIN_LOCK_UNLOCKED((ring)->use_lock);
+	ring->data = kmalloc(length*ring->buf.bpd, GFP_KERNEL);
+
+	return ring->data ? 0 : -ENOMEM;
+}
+
+static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring)
+{
+	kfree(ring->data);
+}
+
+void iio_mark_sw_rb_in_use(void *r)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	spin_lock(&ring->use_lock);
+	ring->use_count++;
+	spin_unlock(&ring->use_lock);
+}
+EXPORT_SYMBOL(iio_mark_sw_rb_in_use);
+
+void iio_unmark_sw_rb_in_use(void *r)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	spin_lock(&ring->use_lock);
+	ring->use_count--;
+	spin_unlock(&ring->use_lock);
+}
+EXPORT_SYMBOL(iio_unmark_sw_rb_in_use);
+
+
+/* Ring buffer related functionality */
+/* Store to ring is typically called in the bh of a data ready interrupt handler
+ * in the device driver */
+/* Lock always held if their is a chance this may be called */
+int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
+			 unsigned char *data,
+			 s64 timestamp)
+{
+	bool init_read = true;
+	int ret;
+	int code;
+
+	/* initial store */
+	if (unlikely(ring->write_p == 0)) {
+		ring->write_p = ring->data;
+		/* doesn't actually matter if this is out of the set */
+		ring->half_p = ring->data - ring->buf.length*ring->buf.bpd/2;
+		init_read = false;
+	}
+	memcpy(ring->write_p, data, ring->buf.bpd);
+	barrier();
+	ring->last_written_p = ring->write_p;
+	barrier();
+	ring->write_p += ring->buf.bpd;
+	/* End of ring, back to the beginning */
+	if (ring->write_p == ring->data + ring->buf.length*ring->buf.bpd) {
+		ring->write_p = ring->data;
+		ring->buf.loopcount++;
+	}
+	if (ring->read_p == 0)
+		ring->read_p = ring->data;
+	/* Buffer full - move the read pointer and create / escalate
+	 * ring event */
+	else if (ring->write_p == ring->read_p) {
+		ring->read_p += ring->buf.bpd;
+		if (ring->read_p
+		    == ring->data + ring->buf.length*ring->buf.bpd)
+			ring->read_p = ring->data;
+
+		spin_lock(&ring->buf.shared_ev_pointer.lock);
+		if (ring->buf.shared_ev_pointer.ev_p) {
+			/* Event escalation - probably quicker to let this
+			   keep running than check if it is necessary */
+			code = IIO_EVENT_CODE_RING_100_FULL;
+			__iio_change_event(ring
+					   ->buf.shared_ev_pointer.ev_p,
+					   code,
+					   timestamp);
+		} else {
+			code = IIO_EVENT_CODE_RING_100_FULL;
+			ret = __iio_push_event(&ring->buf.ev_int,
+					       code,
+					       timestamp,
+					       &ring
+					       ->buf.shared_ev_pointer);
+			if (ret) {
+				spin_unlock(&ring->buf.shared_ev_pointer.lock);
+				goto error_ret;
+			}
+		}
+		spin_unlock(&ring->buf.shared_ev_pointer.lock);
+	}
+	/* investigate if our event barrier has been passed */
+	/* There are definite 'issues' with this and chances of
+	 * simultaneous read */
+	/* Also need to use loop count to ensure this only happens once */
+	ring->half_p += ring->buf.bpd;
+	if (ring->half_p == ring->data + ring->buf.length*ring->buf.bpd)
+		ring->half_p = ring->data;
+	if (ring->half_p == ring->read_p) {
+		spin_lock(&ring->buf.shared_ev_pointer.lock);
+		code = IIO_EVENT_CODE_RING_50_FULL;
+		ret = __iio_push_event(&ring->buf.ev_int,
+				       code,
+				       timestamp,
+				       &ring->buf.shared_ev_pointer);
+		spin_unlock(&ring->buf.shared_ev_pointer.lock);
+
+		if (ret)
+			goto error_ret;
+	}
+	return 0;
+error_ret:
+	return ret;
+}
+
+int iio_rip_sw_rb(void *r,  size_t count, u8 **data, int *dead_offset)
+{
+	struct iio_sw_ring_buffer *ring = r;
+
+	u8 *initial_read_p, *initial_write_p, *current_read_p, *end_read_p;
+
+	int ret, max_copied;
+	/* Round down to nearest datum boundary */
+	int bytes_to_rip = count - count % ring->buf.bpd;
+
+	/* Limit size to whole of ring buffer */
+	bytes_to_rip = min(ring->buf.bpd*ring->buf.length, bytes_to_rip);
+
+	*data = kmalloc(bytes_to_rip, GFP_KERNEL);
+	if (*data == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* build local copy */
+	initial_read_p = ring->read_p;
+	if (unlikely(initial_read_p == 0)) { /* No data here as yet */
+		ret = 0;
+		goto error_free_data_cpy;
+	}
+
+	initial_write_p = ring->write_p;
+
+	/* Need a consistent pair */
+	while ((initial_read_p != ring->read_p)
+	       || (initial_write_p != ring->write_p)) {
+		initial_read_p = ring->read_p;
+		initial_write_p = ring->write_p;
+	}
+	if (initial_write_p == initial_read_p) {
+		/* No new data available.*/
+		ret = 0;
+		goto error_free_data_cpy;
+	}
+
+	if (initial_write_p > initial_read_p + bytes_to_rip) {
+		/* write_p is greater than necessary, all is easy */
+		max_copied = bytes_to_rip;
+		memcpy(*data, initial_read_p, max_copied);
+		end_read_p = initial_read_p + max_copied;
+	} else if (initial_write_p > initial_read_p) {
+		/*not enough data to cpy */
+		max_copied = initial_write_p - initial_read_p;
+		memcpy(*data, initial_read_p, max_copied);
+		end_read_p = initial_write_p;
+	} else {
+		/* going through 'end' of ring buffer */
+		max_copied = ring->data
+			+ ring->buf.length*ring->buf.bpd - initial_read_p;
+		memcpy(*data, initial_read_p, max_copied);
+		if (initial_write_p > ring->data + bytes_to_rip - max_copied) {
+			/* enough data to finish */
+			memcpy(*data + max_copied, ring->data,
+			       bytes_to_rip - max_copied);
+			max_copied = bytes_to_rip;
+			end_read_p = ring->data + (bytes_to_rip - max_copied);
+		} else {  /* not enough data */
+			memcpy(*data + max_copied, ring->data,
+			       initial_write_p - ring->data);
+			max_copied += initial_write_p - ring->data;
+			end_read_p = initial_write_p;
+		}
+	}
+	/* Now to verify which section was cleanly copied - i.e. how far
+	 * read pointer has been pushed */
+	current_read_p = ring->read_p;
+
+	if (initial_read_p <= current_read_p)
+		*dead_offset = current_read_p - initial_read_p;
+	else
+		*dead_offset = ring->buf.length*ring->buf.bpd
+			- (initial_read_p - current_read_p);
+
+	/* possible issue if the initial write has been lapped or indeed
+	 * the point we were reading to has been passed */
+	/* No valid data read.
+	 * In this case the read pointer is already correct having been
+	 * pushed further than we would look. */
+	if (max_copied - *dead_offset < 0) {
+		ret = 0;
+		goto error_free_data_cpy;
+	}
+
+	/* setup the next read position */
+	ring->read_p = end_read_p;
+	return max_copied - *dead_offset;
+
+error_free_data_cpy:
+	kfree(data);
+error_ret:
+	return 0;
+}
+EXPORT_SYMBOL(iio_rip_sw_rb);
+//EXPORT_SYMBOL_GPL(iio_store_to_sw_ring);
+int iio_store_to_sw_rb(void *r, u8 *data, s64 timestamp)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	return iio_store_to_sw_ring(ring, data, timestamp);
+}
+EXPORT_SYMBOL(iio_store_to_sw_rb);
+
+int iio_read_last_from_sw_ring(struct iio_sw_ring_buffer *ring,
+			       unsigned char *data)
+{
+	int loopcount_copy;
+	unsigned char *last_written_p_copy;
+
+	iio_mark_sw_rb_in_use(ring);
+again:
+	loopcount_copy = ring->buf.loopcount;
+	barrier();
+	last_written_p_copy = ring->last_written_p;
+	barrier(); /*unnessecary? */
+	/* Check there is anything here */
+	if (last_written_p_copy == 0)
+		return -EAGAIN;
+	memcpy(data, last_written_p_copy, ring->buf.bpd);
+
+	if (unlikely(loopcount_copy != ring->buf.loopcount)) {
+		if (unlikely(ring->last_written_p >= last_written_p_copy))
+			goto again;
+	}
+	iio_unmark_sw_rb_in_use(ring);
+	return 0;
+}
+
+int iio_read_last_from_sw_rb(void *r,
+			     unsigned char *data)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	return iio_read_last_from_sw_ring(ring, data);
+}
+EXPORT_SYMBOL(iio_read_last_from_sw_rb);
+
+int iio_create_sw_rb(void **r)
+{
+	*r = kzalloc(sizeof(struct iio_sw_ring_buffer), GFP_KERNEL);
+	return *r ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL(iio_create_sw_rb);
+
+int iio_init_sw_rb(void *r, void *indio_dev)
+{
+	return 0;
+}
+EXPORT_SYMBOL(iio_init_sw_rb);
+
+void iio_exit_sw_rb(void *r)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	__iio_free_sw_ring_buffer(ring);
+
+}
+EXPORT_SYMBOL(iio_exit_sw_rb);
+
+
+void iio_free_sw_rb(void *r)
+{
+	kfree(r);
+}
+EXPORT_SYMBOL(iio_free_sw_rb);
+
+int iio_request_update_sw_rb(void *r)
+{
+	int ret;
+	struct iio_sw_ring_buffer *ring = r;
+
+	spin_lock(&ring->use_lock);
+	if (ring->use_count || !ring->update_needed) {
+		ret = -EAGAIN;
+		goto error_ret;
+	}
+	__iio_free_sw_ring_buffer(ring);
+	ret = __iio_init_sw_ring_buffer(ring, ring->buf.bpd, ring->buf.length);
+error_ret:
+	spin_unlock(&ring->use_lock);
+	return ret;
+}
+EXPORT_SYMBOL(iio_request_update_sw_rb);
+
+int iio_get_bpd_sw_rb(void *r)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	return ring->buf.bpd;
+}
+EXPORT_SYMBOL(iio_get_bpd_sw_rb);
+
+int iio_set_bpd_sw_rb(void *r, size_t bpd)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	if (ring->buf.bpd != bpd) {
+		ring->buf.bpd = bpd;
+		if(ring->buf.ring_access->mark_param_change)
+			ring->buf.ring_access->mark_param_change(r);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(iio_set_bpd_sw_rb);
+
+int iio_get_length_sw_rb(void *r)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	return ring->buf.length;
+}
+EXPORT_SYMBOL(iio_get_length_sw_rb);
+
+int iio_set_length_sw_rb(void *r, int length)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	if(ring->buf.length != length) {
+		ring->buf.length = length;
+		if(ring->buf.ring_access->mark_param_change)
+			ring->buf.ring_access->mark_param_change(r);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(iio_set_length_sw_rb);
+
+int iio_mark_update_needed_sw_rb(void *r)
+{
+	struct iio_sw_ring_buffer *ring = r;
+	ring->update_needed = true;
+	return 0;
+}
+EXPORT_SYMBOL(iio_mark_update_needed_sw_rb);
+
+/* no init or exit as this is basically a library module */
+
+MODULE_DESCRIPTION("Industrialio I/O software ring buffer");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/industrialio/ring_sw.h b/include/linux/industrialio/ring_sw.h
new file mode 100644
index 0000000..0b85b56
--- /dev/null
+++ b/include/linux/industrialio/ring_sw.h
@@ -0,0 +1,183 @@
+/* The industrial I/O simple minimally locked ring buffer.
+ *
+ * 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.
+ *
+ * This code is deliberately kept separate from the main industrialio I/O core
+ * as it is intended that in the future a number of different software ring
+ * buffer implementations will exist with different characteristics to suit
+ * different applications.
+ *
+ * This particular one was designed for a data capture application where it was
+ * particularly important that no userspace reads would interrupt the capture
+ * process. To this end the ring is not locked during a read.
+ *
+ * Comments on this buffer design welcomed. It's far from efficient and some of
+ * my understanding of the effects of scheduling on this are somewhat limited.
+ * Frankly, to my mind, this is the current weak point in the industrial I/O
+ * patch set.
+ */
+
+#ifndef _IIO_RING_SW_H_
+#define _IIO_RING_SW_H_
+/* NEEDS COMMENTS */
+/* The intention is that this should be a separate module from the iio core.
+ * This is a bit like supporting algorithms dependent on what the device
+ * driver requests - some may support multiple options */
+
+
+#include <linux/autoconf.h>
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/ring_generic.h>
+
+#if defined CONFIG_IIO_SW_RING || defined CONFIG_IIO_SW_RING_MODULE
+
+/**
+ * iio_create_sw_rb() software ring buffer allocation
+ * @r:		pointer to ring buffer pointer
+ **/
+int iio_create_sw_rb(void **r);
+
+/**
+ * iio_init_sw_rb() initialize the software ring buffer
+ * @r:		pointer to a software ring buffer created by an
+ *		iio_create_sw_rb call.
+ **/
+int iio_init_sw_rb(void *r, void *indio_dev);
+/**
+ * iio_exit_sw_rb() reverse what was done in iio_init_sw_rb
+ **/
+void iio_exit_sw_rb(void *r);
+
+/**
+ * iio_free_sw_rb() free memory occupied by the core ring buffer struct
+ **/
+void iio_free_sw_rb(void *r);
+
+/**
+ * iio_mark_sw_rb_in_use() reference counting to prevent incorrect chances
+ **/
+void iio_mark_sw_rb_in_use(void *r);
+
+/**
+ *  iio_unmark_sw_rb_in_use() notify the ring buffer that we don't care anymore
+ **/
+void iio_unmark_sw_rb_in_use(void *r);
+
+/**
+ * iio_read_last_from_sw_rb() attempt to read the last stored datum from the rb
+ **/
+int iio_read_last_from_sw_rb(void *r, u8 *data);
+
+/**
+ * iio_store_to_sw_rb() store a new datum to the ring buffer
+ * @rb:	pointer to ring buffer instance
+ * @data:	the datum to be stored including timestamp if relevant.
+ * @timestamp:	timestamp which will be attached to buffer events if relevant.
+ **/
+int iio_store_to_sw_rb(void *rb, u8 *data, s64 timestamp);
+
+/**
+ * iio_rip_sw_rb() attempt to read data from the ring buffer
+ * @r:			ring buffer instance
+ * @count:		number of datum's to try and read
+ * @data:		where the data will be stored.
+ * @dead_offset:	how much of the stored data was possibly invalidated by
+ *			the end of the copy.
+ **/
+int iio_rip_sw_rb(void *r, size_t count, u8 **data, int *dead_offset);
+
+/**
+ * iio_request_update_sw_rb() update params if update needed
+ **/
+int iio_request_update_sw_rb(void *r);
+
+/**
+ * iio_mark_update_needed_sw_rb() tell the ring buffer it needs a param update
+ **/
+int iio_mark_update_needed_sw_rb(void *r);
+
+
+/**
+ * iio_get_bpd_sw_rb() get the datum size in bytes
+ **/
+int iio_get_bpd_sw_rb(void *r);
+
+/**
+ * iio_set_bpd_sw_rb() set the datum size in bytes
+ **/
+int iio_set_bpd_sw_rb(void *r, size_t bpd);
+
+/**
+ * iio_get_length_sw_rb() get how many datums the rb may contain
+ **/
+int iio_get_length_sw_rb(void *r);
+
+/**
+ * iio_set_length_sw_rb() set how many datums the rb may contain
+ **/
+int iio_set_length_sw_rb(void *r, int length);
+
+/**
+ * iio_ring_sw_register_funcs() helper function to set up rb access
+ **/
+static inline void iio_ring_sw_register_funcs(struct iio_ring_access_funcs *ra)
+{
+	ra->_create = &iio_create_sw_rb;
+	ra->_free = &iio_free_sw_rb;
+	ra->_init = &iio_init_sw_rb;
+	ra->_exit = &iio_exit_sw_rb;
+
+	ra->mark_in_use = &iio_mark_sw_rb_in_use;
+	ra->unmark_in_use = &iio_unmark_sw_rb_in_use;
+
+	ra->store_to = &iio_store_to_sw_rb;
+	ra->read_last = &iio_read_last_from_sw_rb;
+	ra->rip_lots = &iio_rip_sw_rb;
+
+	ra->mark_param_change = &iio_mark_update_needed_sw_rb;
+	ra->request_update = &iio_request_update_sw_rb;
+
+	ra->get_bpd = &iio_get_bpd_sw_rb;
+	ra->set_bpd = &iio_set_bpd_sw_rb;
+
+	ra->get_length = &iio_get_length_sw_rb;
+	ra->set_length = &iio_set_length_sw_rb;
+};
+
+/**
+ * struct iio_sw_ring_buffer - software ring buffer
+ * @buf:		generic ring buffer elements
+ * @data:		the ring buffer memory
+ * @read_p:		read pointer (oldest available)
+ * @write_p:		write pointer
+ * @last_written_p:	read pointer (newest available)
+ * @half_p:		half buffer length behind write_p (event generation)
+ * @use_count:		reference count to prevent resizing when in use
+ * @update_needed:	flag to indicated change in size requested
+ * @use_lock:		lock to prevent change in size when in use
+ *
+ * Note that the first element of all ring buffers must be a
+ * struct iio_ring_buffer.
+**/
+
+struct iio_sw_ring_buffer {
+	struct iio_ring_buffer buf;
+	unsigned char		*data;
+	unsigned char		*read_p;
+	unsigned char		*write_p;
+	unsigned char		*last_written_p;
+	/* used to act as a point at which to signal an event */
+	unsigned char		*half_p;
+	int			use_count;
+	int			update_needed;
+	spinlock_t		use_lock;
+};
+#else /* CONFIG_IIO_RING_BUFFER*/
+static inline void iio_ring_sw_register_funcs(struct iio_ring_access_funcs *ra)
+{};
+#endif /* !CONFIG_IIO_RING_BUFFER */
+#endif /* _IIO_RING_SW_H_ */

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

* [Industrial I/O] [4/13] RFC: IIO trigger support
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (2 preceding siblings ...)
  2008-12-01 14:27 ` [Industrial I/O] [3/13] RFC: IIO Software ring buffer implementation Jonathan Cameron
@ 2008-12-01 14:29 ` Jonathan Cameron
  2008-12-01 14:30 ` [Industrial I/O] [5/13] RFC: IIO Periodic real time clock based trigger Jonathan Cameron
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:29 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

The Industrial I/O core - trigger support

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--
This patch adds supports for IIO triggers.  These are effectively
interrupt sources which are used to 'trigger' things to happen within
IIO device drivers. Currently this limited to sampling data and
pushing to a software ring buffers.

Current trigger drivers (found in next couple of patches) are
1) Periodic RTC  - this replacement for the timer elements of the IIO v2
2) GPIO - sample on raising an external pin (useful for synchronizing
   samples with external hardware over which you do not have full
   control) This one is very much a proof of concept.
3) Data ready signals from certain hardware (currently LIS3L02DQ
   accelerometer)

The various triggers highlight some of the complexities to be overcome
by any attempt to create a generic framework for handling these sort
of events.

 drivers/industrialio/Kconfig                |    9 +
 drivers/industrialio/Makefile               |    1 +
 drivers/industrialio/industrialio-trigger.c |  363 +++++++++++++++++++++++++++
 include/linux/industrialio/trigger.h        |  110 ++++++++
 4 files changed, 483 insertions(+), 0 deletions(-)

diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
index 73c5616..9bf6a36 100644
--- a/drivers/industrialio/Kconfig
+++ b/drivers/industrialio/Kconfig
@@ -9,6 +9,15 @@ menuconfig INDUSTRIALIO
 	  drivers for many different types of embedded sensors using a
 	  number of different physical interfaces (i2c, spi etc). See
 	  Documentation/industrialio for more information.
+config IIO_TRIGGERS
+       boolean "Enable triggered sampling support"
+       depends on INDUSTRIALIO
+       default y
+         help
+	   Provides IIO core support for triggers.  Currently these
+	   are used to initialize capture of samples to push into 
+	   ring buffers.  The triggers are effectively a 'capture
+	   data now' interrupt.
 
 config IIO_RING_BUFFER
        bool "Enable ring buffer support within iio"
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
index 0fda138..15ef75f 100644
--- a/drivers/industrialio/Makefile
+++ b/drivers/industrialio/Makefile
@@ -5,5 +5,6 @@
 obj-$(CONFIG_INDUSTRIALIO)	+= industrialio.o
 industrialio-y := industrialio-core.o 
 industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
+industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
diff --git a/drivers/industrialio/industrialio-trigger.c b/drivers/industrialio/industrialio-trigger.c
new file mode 100644
index 0000000..aa8dc01
--- /dev/null
+++ b/drivers/industrialio/industrialio-trigger.c
@@ -0,0 +1,363 @@
+/* The industrial I/O core, trigger handling functions
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/trigger.h>
+
+/* RFC - Question of approach
+ * Make the common case (single sensor single trigger)
+ * simple by starting trigger capture from when first sensors
+ * is added.
+ *
+ * Complex simultaneous start requires use of 'hold' functionality
+ * of the trigger. (not implemented)
+ *
+ * Any other suggestions?
+ */
+
+
+static DEFINE_IDR(iio_trigger_idr);
+static DEFINE_SPINLOCK(iio_trigger_idr_lock);
+static LIST_HEAD(iio_trigger_list);
+static DEFINE_MUTEX(iio_trigger_list_lock);
+
+/**
+ * iio_trigger_register_sysfs() create a device for this trigger
+ * @trig_info:	the trigger
+ *
+ * Also adds any control attribute registered by the trigger driver
+ **/
+static int iio_trigger_register_sysfs(struct iio_trigger *trig_info)
+{
+	int ret = 0;
+
+	trig_info->sysfs_dev = device_create(&iio_class,
+					     trig_info->dev,
+					     0,
+					     trig_info,
+					     IIO_TRIGGER_ID_FORMAT,
+					     trig_info->id);
+	if (IS_ERR(trig_info->sysfs_dev)) {
+		ret = PTR_ERR(trig_info->sysfs_dev);
+		goto error_ret;
+	}
+
+	if (trig_info->control_attrs)
+		ret = sysfs_create_group(&trig_info->sysfs_dev->kobj,
+					 trig_info->control_attrs);
+
+error_ret:
+	return ret;
+}
+
+static void iio_trigger_unregister_sysfs(struct iio_trigger *trig_info)
+{
+	if (trig_info->control_attrs)
+		sysfs_remove_group(&trig_info->sysfs_dev->kobj,
+				   trig_info->control_attrs);
+	device_unregister(trig_info->sysfs_dev);
+}
+
+/**
+ * iio_trigger_register_id() get a unique id for this trigger
+ * @trig_info:	the trigger
+ **/
+static int iio_trigger_register_id(struct iio_trigger *trig_info)
+{
+	int ret = 0;
+
+idr_again:
+	if (unlikely(idr_pre_get(&iio_trigger_idr, GFP_KERNEL) == 0))
+		return -ENOMEM;
+
+	spin_lock(&iio_trigger_idr_lock);
+	ret = idr_get_new(&iio_trigger_idr, NULL, &trig_info->id);
+	spin_unlock(&iio_trigger_idr_lock);
+	if (unlikely(ret == -EAGAIN))
+		goto idr_again;
+	else if (likely(!ret))
+		trig_info->id = trig_info->id & MAX_ID_MASK;
+
+	return ret;
+}
+/**
+ * iio_trigger_unregister_id() free up unique id for use by another trigger
+ **/
+static void iio_trigger_unregister_id(struct iio_trigger *trig_info)
+{
+		spin_lock(&iio_trigger_idr_lock);
+		idr_remove(&iio_trigger_idr, trig_info->id);
+		spin_unlock(&iio_trigger_idr_lock);
+}
+
+int iio_trigger_register(struct iio_trigger *trig_info,
+			 struct device *parent,
+			 int id)
+{
+	int ret;
+	struct platform_device *subdev;
+
+	subdev = platform_device_alloc(trig_info->name, id);
+	if (!subdev) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	subdev->dev.parent = parent;
+	ret = platform_device_add(subdev);
+	if (ret)
+		goto error_free_platform_device;
+	trig_info->dev = &subdev->dev;
+	dev_set_drvdata(trig_info->dev, (void *)(trig_info));
+	ret = iio_trigger_register_id(trig_info);
+	if (ret)
+		goto error_free_platform_device;
+	ret = iio_trigger_register_sysfs(trig_info);
+	if (ret)
+		goto error_unregister_id;
+
+	mutex_lock(&iio_trigger_list_lock);
+	list_add_tail(&trig_info->list, &iio_trigger_list);
+	mutex_unlock(&iio_trigger_list_lock);
+
+	return 0;
+
+error_unregister_id:
+	iio_trigger_unregister_id(trig_info);
+error_free_platform_device:
+	platform_device_put(subdev);
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(iio_trigger_register);
+
+void iio_trigger_unregister(struct iio_trigger *trig_info)
+{
+	struct iio_trigger *cursor;
+	struct platform_device *subdev;
+
+	mutex_lock(&iio_trigger_list_lock);
+	list_for_each_entry(cursor, &iio_trigger_list, list)
+		if (cursor == trig_info) {
+			list_del(&cursor->list);
+			break;
+		}
+	mutex_unlock(&iio_trigger_list_lock);
+
+	iio_trigger_unregister_sysfs(trig_info);
+	iio_trigger_unregister_id(trig_info);
+	subdev = to_platform_device(trig_info->dev);
+	platform_device_unregister(subdev);
+}
+EXPORT_SYMBOL(iio_trigger_unregister);
+
+struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len)
+{
+	struct iio_trigger *trig;
+	bool found = false;
+
+	mutex_lock(&iio_trigger_list_lock);
+	list_for_each_entry(trig, &iio_trigger_list, list) {
+		if (strncmp(trig->name, name, len) == 0) {
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&iio_trigger_list_lock);
+
+	return found ? trig : NULL;
+};
+EXPORT_SYMBOL(iio_trigger_find_by_name);
+
+void iio_trigger_poll(struct iio_trigger *trig)
+{
+	struct iio_poll_func *pf_cursor;
+
+	list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) {
+		if (pf_cursor->poll_func_immediate) {
+			pf_cursor->poll_func_immediate(pf_cursor->private_data);
+			trig->use_count++;
+		}
+	}
+	list_for_each_entry(pf_cursor, &trig->pollfunc_list, list) {
+		if (pf_cursor->poll_func_main) {
+			pf_cursor->poll_func_main(pf_cursor->private_data);
+			trig->use_count++;
+		}
+	}
+}
+EXPORT_SYMBOL(iio_trigger_poll);
+
+void iio_trigger_notify_done(struct iio_trigger *trig)
+{
+	trig->use_count--;
+	if (trig->use_count == 0 && trig->try_reenable)
+		if (trig->try_reenable(trig)) {
+			/* Missed and interrupt so launch new poll now */
+			trig->timestamp = 0;
+			iio_trigger_poll(trig);
+		}
+}
+EXPORT_SYMBOL(iio_trigger_notify_done);
+
+ssize_t iio_trigger_read_name(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct iio_trigger *trig = dev_get_drvdata(dev);
+	return sprintf(buf, "%s\n", trig->name);
+}
+EXPORT_SYMBOL(iio_trigger_read_name);
+
+/* Trigger Consumer related functions */
+
+/* Complexity in here.  With certain triggers (datardy) an acknowledgement
+ * may be needed if the pollfuncs do not include the data read for the
+ * triggering device.
+ * This is not currently handled.  Alternative of not enabling trigger unless
+ * the relevant function is in there may be the best option.
+ */
+/* Worth protecting against double additions?*/
+int iio_trigger_attach_poll_func(struct iio_trigger *trig,
+				 struct iio_poll_func *pf)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
+	list_add_tail(&pf->list, &trig->pollfunc_list);
+	spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
+
+	if (trig->set_trigger_state)
+		ret = trig->set_trigger_state(trig, true);
+
+	return ret;
+}
+EXPORT_SYMBOL(iio_trigger_attach_poll_func);
+
+int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
+				  struct iio_poll_func *pf)
+{
+	struct iio_poll_func *pf_cursor;
+	unsigned long flags;
+	int succeeded = 0;
+
+	spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
+	list_for_each_entry(pf_cursor, &trig->pollfunc_list, list)
+		if (pf_cursor == pf) {
+			succeeded = 1;
+			break;
+		}
+	if (list_is_singular(&trig->pollfunc_list) && trig->set_trigger_state) {
+		spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
+		/* May require sleeping */
+		trig->set_trigger_state(trig, false);
+		spin_lock_irqsave(&trig->pollfunc_list_lock, flags);
+	}
+	/* Now we can delete safe in the knowledge that no interrupts
+	 * can be generated if nothing is there to handle them. */
+	if (succeeded)
+		list_del(&pf_cursor->list);
+	spin_unlock_irqrestore(&trig->pollfunc_list_lock, flags);
+
+	return succeeded ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL(iio_trigger_dettach_poll_func);
+
+/**
+ * iio_trigger_read_currrent() trigger consumer sysfs query which trigger
+ *
+ * For trigger consumers the current_trigger interface allows the trigger
+ * used by the device to be queried.
+ **/
+static ssize_t iio_trigger_read_current(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	int len = 0;
+	if (dev_info->trig)
+		len = snprintf(buf,
+			       IIO_TRIGGER_NAME_LENGTH,
+			       "%s\n",
+			       dev_info->trig->name);
+	return len;
+}
+
+/**
+ * iio_trigger_write_current() trigger consumer sysfs set current trigger
+ *
+ * For trigger consumers the current_trigger interface allows the trigger
+ * used for this device to be specified at run time based on the triggers
+ * name.
+ * Latter part of this function is concerned with maintaining use counts
+ * for any modules providing triggers to prevent removal when in use.
+ **/
+static ssize_t iio_trigger_write_current(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct iio_trigger *oldtrig = dev_info->trig;
+	mutex_lock(&dev_info->mlock);
+	if (dev_info->currentmode == INDIO_RING_TRIGGERED) {
+		mutex_unlock(&dev_info->mlock);
+		return -EBUSY;
+	}
+	mutex_unlock(&dev_info->mlock);
+
+	len = len < IIO_TRIGGER_NAME_LENGTH ? len : IIO_TRIGGER_NAME_LENGTH;
+
+	dev_info->trig = iio_trigger_find_by_name(buf, len);
+	if (dev_info->trig != oldtrig)
+		module_put(oldtrig->owner);
+	else if (dev_info->trig)
+		try_module_get(dev_info->trig->owner);
+
+	return len;
+}
+
+DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR,
+	    iio_trigger_read_current,
+	    iio_trigger_write_current);
+
+static struct attribute *iio_trigger_consumer_attrs[] = {
+	&dev_attr_current_trigger.attr,
+	NULL,
+};
+
+static const struct attribute_group iio_trigger_consumer_attr_group = {
+	.name = "trigger",
+	.attrs = iio_trigger_consumer_attrs,
+};
+
+int iio_device_register_trigger_consumer(struct iio_dev *dev_info)
+{
+	int ret;
+	ret = sysfs_create_group(&dev_info->sysfs_dev->kobj,
+				 &iio_trigger_consumer_attr_group);
+	return ret;
+}
+EXPORT_SYMBOL(iio_device_register_trigger_consumer);
+
+int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info)
+{
+	sysfs_remove_group(&dev_info->sysfs_dev->kobj,
+			   &iio_trigger_consumer_attr_group);
+	return 0;
+}
+EXPORT_SYMBOL(iio_device_unregister_trigger_consumer);
diff --git a/include/linux/industrialio/trigger.h b/include/linux/industrialio/trigger.h
index 517213d..bca0678 100644
--- a/include/linux/industrialio/trigger.h
+++ b/include/linux/industrialio/trigger.h
@@ -13,6 +13,116 @@
 #define IIO_TRIGGER_ID_FORMAT IIO_TRIGGER_ID_PREFIX "%d"
 
 #ifdef CONFIG_IIO_TRIGGERS
+
+/**
+ * struct iio_trigger - industrial I/O trigger device
+ *
+ * @id:			[INTERN] unique id number
+ * @name:		[DRIVER] unique name
+ * @dev:		[DRIVER] associated device (if relevant)
+ * @sysfs_dev:		[INTERN] sysfs relevant device
+ * @private_data:	[DRIVER] device specific data
+ * @list:		[INTERN] used in maintenance of global trigger list
+ * @alloc_list:		[DRIVER] used for driver specific trigger list
+ * @poll_func_list_lock:[INTERN] protection of the polling function list
+ * @pollfunc_list:	[INTERN] list of functions to run on trigger.
+ * @control_attrs:	[DRIVER] sysfs attributes relevant to trigger type
+ * @set_trigger_state:	[DRIVER] switch on/off the trigger on demand
+ * @timestamp:		[INTERN] timestamp usesd by some trigs (e.g. datardy)
+ * @owner:		[DRIVER] used to monitor usage count of the trigger.
+ **/
+struct iio_trigger {
+	int				id;
+	const char			*name;
+	struct device			*dev;
+	struct device			*sysfs_dev;
+	void				*private_data;
+	struct list_head		list;
+	struct list_head		alloc_list;
+	spinlock_t			pollfunc_list_lock;
+	struct list_head		pollfunc_list;
+	const struct attribute_group	*control_attrs;
+	s64				timestamp;
+	struct module			*owner;
+	int use_count;
+
+	int (*set_trigger_state)(struct iio_trigger *trig, bool state);
+	int (*try_reenable)(struct iio_trigger *trig);
+};
+
+/**
+ * iio_trigger_read_name() - sysfs access function to get the trigger name
+ **/
+ssize_t iio_trigger_read_name(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf);
+
+#define IIO_TRIGGER_NAME_ATTR DEVICE_ATTR(name, S_IRUGO,		\
+					  iio_trigger_read_name,	\
+					  NULL);
+
+/**
+ * iio_trigger_find_by_name() - search global trigger list
+ **/
+struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len);
+
+/**
+ * iio_trigger_init() - initialize lists and spinlocks in iio_trigger struct
+ **/
+static inline void iio_trigger_init(struct iio_trigger *trig)
+{
+	spin_lock_init(&trig->pollfunc_list_lock);
+	INIT_LIST_HEAD(&trig->list);
+	INIT_LIST_HEAD(&trig->pollfunc_list);
+};
+
+/**
+ * iio_trigger_register() - register a trigger with the IIO core
+ * @trig_info:	trigger to be registered
+ * @parent:	the device with which the trigger is associated
+ * @id:		unique indentification
+ **/
+int iio_trigger_register(struct iio_trigger *trig_info,
+			 struct device *parent,
+			 int id);
+
+/**
+ * iio_trigger_unregister() - unregister a trigger from the core
+ **/
+void iio_trigger_unregister(struct iio_trigger *trig_info);
+
+/**
+ * iio_trigger_attach_poll_func() - add a function pair to be run on trigger
+ * @trig:	trigger to which the function pair are being added
+ * @pf:	poll function pair
+ **/
+int iio_trigger_attach_poll_func(struct iio_trigger *trig,
+				 struct iio_poll_func *pf);
+
+/**
+ * iio_trigger_dettach_poll_func() -	remove function pair from those to be
+ *					run on trigger.
+ * @trig:	trigger from which the function is being removed.
+ * @pf:		poll function pair
+ **/
+int iio_trigger_dettach_poll_func(struct iio_trigger *trig,
+				  struct iio_poll_func *pf);
+
+/**
+ * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers.
+ **/
+int iio_device_register_trigger_consumer(struct iio_dev *dev_info);
+/**
+ * iio_device_unregister_trigger_consumer() - reverse the registration process.
+ **/
+int iio_device_unregister_trigger_consumer(struct iio_dev *dev_info);
+
+/**
+ * iio_trigger_poll() - called on a trigger occuring
+ * Typically called in relevant hardware interrupt handler.
+ **/
+void iio_trigger_poll(struct iio_trigger *);
+void iio_trigger_notify_done(struct iio_trigger *);
 #else
 struct iio_trigger{};
 static inline int iio_device_register_trigger_consumer(struct iio_dev *dev_info)

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

* [Industrial I/O] [5/13] RFC: IIO Periodic real time clock based trigger.
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (3 preceding siblings ...)
  2008-12-01 14:29 ` [Industrial I/O] [4/13] RFC: IIO trigger support Jonathan Cameron
@ 2008-12-01 14:30 ` Jonathan Cameron
  2008-12-01 14:32 ` [Industrial I/O] [6/13] RFC: Proof of concept GPIO based IIO trigger Jonathan Cameron
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:30 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

IIO: Periodic real time clock based trigger.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--

Quite a few real time clock implementations provide configurable
periodic interrupts.  This module allows these to be used as triggers
for filling IIO ring buffers at regular intervals.

As has been discussed a number of times on LKML and elsewhere, it
would be nice to have a generic timer system to handle the many
timers that are available and make use of their periodic capabilities.
Until this is available the RTC subsystem provides the easiest way
of doing this.

Whilst this is in of itself a useful implementation, the principal
reason for including it here is to illustrate how IIO trigger drivers
work.

 drivers/industrialio/Kconfig                       |    2 +
 drivers/industrialio/Makefile                      |    2 +
 drivers/industrialio/triggers/Kconfig              |    6 +
 drivers/industrialio/triggers/Makefile             |    5 +
 .../industrialio/triggers/iio-trig-periodic-rtc.c  |  197 ++++++++++++++++++++
 include/linux/industrialio/prtc_trigger.h          |    7 +
 6 files changed, 219 insertions(+), 0 deletions(-)

diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
index 9bf6a36..c20ca2f 100644
--- a/drivers/industrialio/Kconfig
+++ b/drivers/industrialio/Kconfig
@@ -19,6 +19,8 @@ config IIO_TRIGGERS
 	   ring buffers.  The triggers are effectively a 'capture
 	   data now' interrupt.
 
+source drivers/industrialio/triggers/Kconfig
+
 config IIO_RING_BUFFER
        bool "Enable ring buffer support within iio"
 	  depends on INDUSTRIALIO
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
index 15ef75f..518849a 100644
--- a/drivers/industrialio/Makefile
+++ b/drivers/industrialio/Makefile
@@ -8,3 +8,5 @@ industrialio-$(CONFIG_IIO_RING_BUFFER) += industrialio-ring.o
 industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
+
+obj-y += triggers/
\ No newline at end of file
diff --git a/drivers/industrialio/triggers/Kconfig b/drivers/industrialio/triggers/Kconfig
new file mode 100644
index 0000000..20e6498
--- /dev/null
+++ b/drivers/industrialio/triggers/Kconfig
@@ -0,0 +1,6 @@
+config IIO_PERIODIC_RTC_TRIGGER
+       tristate "Periodic RTC triggers"
+          depends on INDUSTRIALIO && IIO_TRIGGERS && RTC_CLASS
+	  help
+	    Provides support for using periodic capable real time
+	    clocks as IIO triggers.
diff --git a/drivers/industrialio/triggers/Makefile b/drivers/industrialio/triggers/Makefile
new file mode 100644
index 0000000..4ae55b9
--- /dev/null
+++ b/drivers/industrialio/triggers/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for triggers not associated with iio-devices
+#
+obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
+
diff --git a/drivers/industrialio/triggers/iio-trig-periodic-rtc.c b/drivers/industrialio/triggers/iio-trig-periodic-rtc.c
new file mode 100644
index 0000000..c395722
--- /dev/null
+++ b/drivers/industrialio/triggers/iio-trig-periodic-rtc.c
@@ -0,0 +1,197 @@
+/* The industrial I/O periodic RTC trigger driver
+ *
+ * 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.
+ *
+ * This is a heavily rewritten version of the periodic timer system in
+ * earlier version of industrialio.  It supplies the same functionality
+ * but via a trigger rather than a specific periodic timer system.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/trigger.h>
+#include <linux/industrialio/prtc_trigger.h>
+
+LIST_HEAD(iio_prtc_trigger_list);
+DEFINE_MUTEX(iio_prtc_trigger_list_lock);
+
+struct iio_prtc_trigger_info {
+	struct rtc_device *rtc;
+	int frequency;
+	struct rtc_task task;
+};
+
+static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_prtc_trigger_info *trig_info = trig->private_data;
+	return rtc_irq_set_state(trig_info->rtc, &trig_info->task, state);
+}
+
+static ssize_t iio_trig_periodic_read_freq(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct iio_trigger *trig = dev_get_drvdata(dev);
+	struct iio_prtc_trigger_info *trig_info = trig->private_data;
+	return sprintf(buf, "%u\n", trig_info->frequency);
+}
+
+static ssize_t iio_trig_periodic_write_freq(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t len)
+{
+	struct iio_trigger *trig = dev_get_drvdata(dev);
+	struct iio_prtc_trigger_info *trig_info = trig->private_data;
+	unsigned long val;
+	int ret;
+
+	ret = strict_strtoul(buf,10, &val);
+	if (ret)
+		goto error_ret;
+
+	ret = rtc_irq_set_freq(trig_info->rtc, &trig_info->task, val);
+	if (ret)
+		goto error_ret;
+
+	trig_info->frequency = val;
+
+	return len;
+
+error_ret:
+	return ret;
+}
+
+DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR,
+	    iio_trig_periodic_read_freq,
+	    iio_trig_periodic_write_freq);
+
+static struct attribute *iio_trig_prtc_attrs[] = {
+	&dev_attr_frequency.attr,
+	NULL,
+};
+static const struct attribute_group iio_trig_prtc_attr_group = {
+	.attrs = iio_trig_prtc_attrs,
+};
+
+static void iio_prtc_trigger_poll(void *private_data)
+{
+	iio_trigger_poll(private_data);
+}
+
+
+
+static int iio_trig_periodic_rtc_probe(struct platform_device *dev)
+{
+	struct iio_prtc_trigger_group *pdata = dev->dev.platform_data;
+	struct iio_prtc_trigger_info *trig_info;
+	struct iio_trigger *trig, *trig2;
+
+	int i, ret;
+	for (i = 0; i < pdata->num_triggers; i++) {
+		trig = kzalloc(sizeof(*trig), GFP_KERNEL);
+		if (!trig) {
+			ret = -ENOMEM;
+			goto error_free_trigs_1;
+		}
+		iio_trigger_init(trig);
+		trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+		if (!trig_info) {
+			ret = -ENOMEM;
+			goto error_free_trigs_2;
+		}
+		trig->private_data = trig_info;
+		trig->owner = THIS_MODULE;
+		trig->set_trigger_state = &iio_trig_periodic_rtc_set_state;
+		trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL);
+		if (trig->name == NULL) {
+			ret = -ENOMEM;
+			goto error_free_trigs_3;
+		}
+		snprintf((char *)trig->name, IIO_TRIGGER_NAME_LENGTH, "periodic%s", pdata->triggers[i].name);
+
+		/* RTC access */
+		trig_info->rtc = rtc_class_open((char *)(pdata->triggers[i].name));
+		if (trig_info->rtc == NULL) {
+			ret = -EINVAL;
+			goto error_free_trigs_4;
+		}
+		trig_info->task.func = iio_prtc_trigger_poll;
+		trig_info->task.private_data = trig;
+		ret = rtc_irq_register(trig_info->rtc, &trig_info->task);
+		if (ret)
+			printk("failed on reg\n");
+		trig->control_attrs = &iio_trig_prtc_attr_group;
+		ret = iio_trigger_register(trig, &dev->dev, i);
+		if (ret)
+			goto error_free_trigs_5;
+	}
+	return 0;
+error_free_trigs_5:
+	rtc_class_close(trig_info->rtc);
+error_free_trigs_4:
+	kfree(trig->name);
+error_free_trigs_3:
+	kfree(trig_info);
+error_free_trigs_2:
+	kfree(trig);
+error_free_trigs_1:
+	list_for_each_entry_safe(trig, trig2, &iio_prtc_trigger_list, alloc_list) {
+		trig_info = trig->private_data;
+		iio_trigger_unregister(trig);
+		//FREE RTC STUFF
+		kfree(trig->name);
+		kfree(trig_info);
+		kfree(trig);
+	}
+	return ret;
+}
+
+static int iio_trig_periodic_rtc_remove(struct platform_device *dev)
+{
+	struct iio_trigger *trig, *trig2;
+	struct iio_prtc_trigger_info *trig_info;
+	mutex_lock(&iio_prtc_trigger_list_lock);
+	list_for_each_entry_safe(trig, trig2, &iio_prtc_trigger_list, alloc_list) {
+		trig_info = trig->private_data;
+		iio_trigger_unregister(trig);
+		//FREE RTC STUFF
+		kfree(trig->name);
+		kfree(trig_info);
+		kfree(trig);
+	}
+	mutex_unlock(&iio_prtc_trigger_list_lock);
+	return 0;
+}
+
+static struct platform_driver iio_trig_periodic_rtc_driver = {
+	.probe = iio_trig_periodic_rtc_probe,
+	.remove = iio_trig_periodic_rtc_remove,
+	.driver = {
+		.name = "iio_prtc_trigger",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init iio_trig_periodic_rtc_init(void)
+{
+	return platform_driver_register(&iio_trig_periodic_rtc_driver);
+}
+
+static void __exit iio_trig_periodic_rtc_exit(void)
+{
+	return platform_driver_unregister(&iio_trig_periodic_rtc_driver);
+}
+
+module_init(iio_trig_periodic_rtc_init);
+module_exit(iio_trig_periodic_rtc_exit);
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Periodic realtime clock  trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/industrialio/prtc_trigger.h b/include/linux/industrialio/prtc_trigger.h
new file mode 100644
index 0000000..9dabc19
--- /dev/null
+++ b/include/linux/industrialio/prtc_trigger.h
@@ -0,0 +1,7 @@
+struct iio_prtc_trigger {
+	const char *name;
+};
+struct iio_prtc_trigger_group {
+	int num_triggers;
+	struct iio_prtc_trigger *triggers;
+};

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

* [Industrial I/O] [6/13] RFC: Proof of concept GPIO based IIO trigger
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (4 preceding siblings ...)
  2008-12-01 14:30 ` [Industrial I/O] [5/13] RFC: IIO Periodic real time clock based trigger Jonathan Cameron
@ 2008-12-01 14:32 ` Jonathan Cameron
       [not found] ` <4933F291.4020001-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:32 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

IIO: Proof of concept GPIO based IIO trigger

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--

A very limited trigger driver using a GPIO to provide synchronization
with external devices. Things like setting polarity and interrupt
types will need to be added to make this more useful.

 drivers/industrialio/triggers/Kconfig         |   12 ++
 drivers/industrialio/triggers/Makefile        |    5 +-
 drivers/industrialio/triggers/iio-trig-gpio.c |  193 +++++++++++++++++++++++++
 include/linux/industrialio/gpio_trigger.h     |    6 +
 4 files changed, 215 insertions(+), 1 deletions(-)

diff --git a/drivers/industrialio/triggers/Kconfig b/drivers/industrialio/triggers/Kconfig
index 20e6498..e100098 100644
--- a/drivers/industrialio/triggers/Kconfig
+++ b/drivers/industrialio/triggers/Kconfig
@@ -1,6 +1,18 @@
+config IIO_GPIO_TRIGGER
+       tristate "GPIO triggers"
+          depends on INDUSTRIALIO && IIO_TRIGGERS && GENERIC_GPIO
+	  help
+	    Driver provides support for using GPIOs as the IIO
+	    triggers.
+
 config IIO_PERIODIC_RTC_TRIGGER
        tristate "Periodic RTC triggers"
           depends on INDUSTRIALIO && IIO_TRIGGERS && RTC_CLASS
 	  help
 	    Provides support for using periodic capable real time
 	    clocks as IIO triggers.

diff --git a/drivers/industrialio/triggers/Makefile b/drivers/industrialio/triggers/Makefile
index 4ae55b9..767d2cd 100644
--- a/drivers/industrialio/triggers/Makefile
+++ b/drivers/industrialio/triggers/Makefile
@@ -1,5 +1,8 @@
 #
 # Makefile for triggers not associated with iio-devices
 #
-obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
 
+obj-$(CONFIG_IIO_GPIO_TRIGGER) += iio-trig-gpio.o
+
+obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
+#obj-$(CONFIG_IIO_PTIMER_BOARDINFO) += iio-ptimer-boardinfo.o
diff --git a/drivers/industrialio/triggers/iio-trig-gpio.c b/drivers/industrialio/triggers/iio-trig-gpio.c
new file mode 100644
index 0000000..9718779
--- /dev/null
+++ b/drivers/industrialio/triggers/iio-trig-gpio.c
@@ -0,0 +1,193 @@
+/*
+ * Industrial I/O - gpio based trigger support
+ *
+ * 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.
+ *
+ * Currently this is more of a functioning proof of concept that a fully
+ * fledged trigger driver.
+ *
+ * TODO:
+ *
+ * Add board config elements to allow specification of startup settings.
+ * Add configuration settings (irq type etc)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/trigger.h>
+#include <linux/industrialio/gpio_trigger.h>
+
+LIST_HEAD(iio_gpio_trigger_list);
+DEFINE_MUTEX(iio_gpio_trigger_list_lock);
+
+struct iio_gpio_trigger_info {
+	struct mutex in_use;
+	int gpio;
+};
+/* Need to reference count these triggers and only enable gpio interrupts as appropriate.*/
+
+/* So what functionality do we want in here?... */
+/* set high / low as interrupt type? */
+
+static irqreturn_t iio_gpio_trigger_poll(int irq, void *private)
+{
+	//private at this stage is the iio_trig.
+	iio_trigger_poll(private);
+	return IRQ_HANDLED;
+}
+
+DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *iio_gpio_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group iio_gpio_trigger_attr_group = {
+	.attrs = iio_gpio_trigger_attrs,
+};
+
+static int iio_gpio_trigger_probe(struct platform_device *dev)
+{
+	struct iio_gpio_trigger_pdata *pdata = dev->dev.platform_data;
+	struct iio_gpio_trigger_info *trig_info;
+	struct iio_trigger *trig, *trig2;
+	int i, irq, ret = 0;
+	if (!pdata) {
+		printk(KERN_ERR "No IIO gpio trigger platform data found\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < pdata->num_gpios; i++) {
+
+		trig = kzalloc(sizeof(*trig), GFP_KERNEL);
+		if (!trig) {
+			ret = -ENOMEM;
+			goto error_free_trigs_1;
+		}
+		iio_trigger_init(trig);
+
+		trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+		if (!trig_info) {
+			ret = -ENOMEM;
+			goto error_free_trigs_2;
+		}
+		trig->control_attrs = &iio_gpio_trigger_attr_group;
+		trig->private_data = trig_info;
+		trig_info->gpio = pdata->gpios[i];
+		trig->owner = THIS_MODULE;
+		trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL);
+		snprintf((char *)trig->name, IIO_TRIGGER_NAME_LENGTH, "gpiotrig%d", pdata->gpios[i]);
+
+
+		ret = gpio_request(trig_info->gpio, trig->name);
+		if (ret)
+			goto error_free_trigs_3;
+
+		ret = gpio_direction_input(trig_info->gpio);
+		if (ret)
+			goto error_free_trigs_4;
+
+		irq = gpio_to_irq(trig_info->gpio);
+		if (irq < 0)
+			goto error_free_trigs_4;
+
+		ret = request_irq(irq, iio_gpio_trigger_poll,
+				  IRQF_TRIGGER_RISING,
+				  trig->name,
+				  trig);
+		if (ret)
+			goto error_free_trigs_4;
+		ret = iio_trigger_register(trig, &dev->dev, i);
+
+		if (ret)
+			goto error_free_trigs_5;
+
+		mutex_lock(&iio_gpio_trigger_list_lock);
+		list_add_tail(&trig->alloc_list, &iio_gpio_trigger_list);
+		mutex_unlock(&iio_gpio_trigger_list_lock);
+	}
+	return 0;
+
+/* First clean up the partly allocated trigger */
+error_free_trigs_5:
+	free_irq(irq, trig);
+
+error_free_trigs_4:
+	gpio_free(trig_info->gpio);
+error_free_trigs_3:
+	kfree(trig->name);
+error_free_trigs_2:
+	kfree(trig_info);
+error_free_trigs_1:
+/* The rest should have been added to the iio_gpio_trigger_list */
+	mutex_lock(&iio_gpio_trigger_list_lock);
+	list_for_each_entry_safe(trig,
+				 trig2,
+				 &iio_gpio_trigger_list,
+				 alloc_list) {
+		trig_info = trig->private_data;
+		iio_trigger_unregister(trig);
+		free_irq(gpio_to_irq(trig_info->gpio), trig);
+		gpio_free(trig_info->gpio);
+		kfree(trig->name);
+		kfree(trig_info);
+		kfree(trig);
+	}
+	mutex_unlock(&iio_gpio_trigger_list_lock);
+
+	return ret;
+}
+
+static int iio_gpio_trigger_remove(struct platform_device *dev)
+{
+	struct iio_trigger *trig, *trig2;
+	struct iio_gpio_trigger_info *trig_info;
+
+	mutex_lock(&iio_gpio_trigger_list_lock);
+	list_for_each_entry_safe(trig, trig2, &iio_gpio_trigger_list, alloc_list) {
+		trig_info = trig->private_data;
+		iio_trigger_unregister(trig);
+		free_irq(gpio_to_irq(trig_info->gpio), trig);
+		gpio_free(trig_info->gpio);
+		kfree(trig->name);
+		kfree(trig_info);
+		kfree(trig);
+	}
+	mutex_unlock(&iio_gpio_trigger_list_lock);
+
+	return 0;
+}
+
+static struct platform_driver iio_gpio_trigger_driver = {
+	.probe = iio_gpio_trigger_probe,
+	.remove = iio_gpio_trigger_remove,
+	.driver = {
+		.name = "iio_gpio_trigger",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init iio_gpio_trig_init(void)
+{
+	return platform_driver_register(&iio_gpio_trigger_driver);
+}
+module_init(iio_gpio_trig_init);
+
+static void __exit iio_gpio_trig_exit(void)
+{
+	platform_driver_unregister(&iio_gpio_trigger_driver);
+}
+module_exit(iio_gpio_trig_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Example gpio trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/industrialio/gpio_trigger.h b/include/linux/industrialio/gpio_trigger.h
new file mode 100644
index 0000000..25be3da
--- /dev/null
+++ b/include/linux/industrialio/gpio_trigger.h
@@ -0,0 +1,6 @@
+
+
+struct iio_gpio_trigger_pdata {
+	int num_gpios;
+	int *gpios;
+};

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

* [Industrial I/O] [7/13] RFC: Maxim MAX1363 driver
       [not found] ` <4933F291.4020001-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
@ 2008-12-01 14:33   ` Jonathan Cameron
  2008-12-01 14:37   ` [Industrial I/O] [11/13] RFC: VTI SCA3000 3d accelerometer driver Jonathan Cameron
  2008-12-01 19:41   ` [Industrial I/O] [0/13] RFC: IIO v3 patchset Alan Cox
  2 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:33 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Dmitry Torokhov, LKML, LM Sensors, David Brownell, Jean Delvare,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Ben Nizette

 
From: Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>

IIO: Maxim MAX1363 driver
Also supports max1361, max1362, max1364, max1136, max1136,
max1137, max1138, max1139, max1236, max1237, max11238, max1239

Signed-off-by: Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
--
This patch provides the core functionality (sysfs control 
and direct device scanning, chrdev event interface).

It illustrates one common form of scan mode configuration
found in numerous similar devices.  A restricted set of
combinations of elements are possible via a set of predefined
scan modes.  How to handle this in a consistent way alongside
devices such as the lis3l02dq accelerometer (any combinations
of channels possible) is very much a question for discussion.

 drivers/industrialio/Kconfig            |    4 +-
 drivers/industrialio/Makefile           |    3 +-
 drivers/industrialio/adc/Kconfig        |   13 +
 drivers/industrialio/adc/Makefile       |    7 +
 drivers/industrialio/adc/max1363.h      |  250 +++++++++++++
 drivers/industrialio/adc/max1363_core.c |  599 +++++++++++++++++++++++++++++++
 6 files changed, 874 insertions(+), 2 deletions(-)

diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
index c20ca2f..f682fbf 100644
--- a/drivers/industrialio/Kconfig
+++ b/drivers/industrialio/Kconfig
@@ -36,4 +36,6 @@ config IIO_SW_RING
 	example software ring buffer implementation.  The design aim
 	of this particular realization was to minize write locking
 	with the intention that some devices would be able to write
-	in interrupt context.
\ No newline at end of file
+	in interrupt context.
+
+source drivers/industrialio/adc/Kconfig
\ No newline at end of file
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
index 518849a..dd6d5ca 100644
--- a/drivers/industrialio/Makefile
+++ b/drivers/industrialio/Makefile
@@ -9,4 +9,5 @@ industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 
-obj-y += triggers/
\ No newline at end of file
+obj-y += triggers/
+obj-y += adc/
\ No newline at end of file
diff --git a/drivers/industrialio/adc/Kconfig b/drivers/industrialio/adc/Kconfig
new file mode 100644
index 0000000..203a5f9
--- /dev/null
+++ b/drivers/industrialio/adc/Kconfig
@@ -0,0 +1,13 @@
+#
+# ADC drivers
+#
+comment "Analog to digital convertors"
+	depends on INDUSTRIALIO
+config MAX1363
+	tristate "MAXIM max1363 ADC driver"
+	depends on INDUSTRIALIO
+	help
+	  Say yes here to build support for many MAXIM i2c analog to digital
+	  convertors (ADC). (max1361, max1362, max1363, max1364, max1136,
+	  max1136, max1137, max1138, max1139, max1236, max1237, max11238,
+	  max1239) Provides direct access via sysfs.
diff --git a/drivers/industrialio/adc/Makefile b/drivers/industrialio/adc/Makefile
new file mode 100644
index 0000000..2a37fee
--- /dev/null
+++ b/drivers/industrialio/adc/Makefile
@@ -0,0 +1,7 @@
+
+# Makefile for industrial I/O ADC drivers
+#
+
+max1363-y := max1363_core.o
+
+obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/industrialio/adc/max1363.h b/drivers/industrialio/adc/max1363.h
new file mode 100644
index 0000000..71e9577
--- /dev/null
+++ b/drivers/industrialio/adc/max1363.h
@@ -0,0 +1,250 @@
+#ifndef _MAX1363_H_
+#define  _MAX1363_H_
+
+#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
+
+
+#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_CONV_RATE_133ksps		0
+#define MAX1363_MON_CONV_RATE_66_5ksps		0x02
+#define MAX1363_MON_CONV_RATE_33_3ksps		0x04
+#define MAX1363_MON_CONV_RATE_16_6ksps		0x06
+#define MAX1363_MON_CONV_RATE_8_3ksps		0x08
+#define MAX1363_MON_CONV_RATE_4_2ksps		0x0A
+#define MAX1363_MON_CONV_RATE_2_0ksps		0x0C
+#define MAX1363_MON_CONV_RATE_1_0ksps		0x0E
+#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
+ * @name:	Name used to identify the scan mode.
+ * @conf:	The corresponding value of the configuration register
+ * @numvals:	The number of values returned by a single scan
+ */
+struct max1363_mode {
+	const char	*name;
+	int8_t		conf;
+	int		numvals;
+};
+
+#define MAX1363_MODE_SINGLE(_num) {					\
+		.name = #_num,						\
+			.conf = MAX1363_CHANNEL_SEL(_num)		\
+			| MAX1363_CONFIG_SCAN_SINGLE_1			\
+			| MAX1363_CONFIG_SE,				\
+			.numvals = 1,					\
+			}
+
+#define MAX1363_MODE_SINGLE_TIMES_8(_num) {				\
+		.name = #_num"x8",					\
+			.conf = MAX1363_CHANNEL_SEL(_num)		\
+			| MAX1363_CONFIG_SCAN_SINGLE_8			\
+			| MAX1363_CONFIG_SE,				\
+			.numvals = 8,					\
+			}
+
+#define MAX1363_MODE_SCAN_TO_CHANNEL(_num) {				\
+		.name = "0..."#_num,					\
+			.conf = MAX1363_CHANNEL_SEL(_num)		\
+			| MAX1363_CONFIG_SCAN_TO_CS			\
+			| MAX1363_CONFIG_SE,				\
+			.numvals = _num + 1,				\
+			}
+
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num) {			\
+		.name = #_mid"..."#_num,				\
+			.conf =MAX1363_CHANNEL_SEL(_num)		\
+			| MAX1236_SCAN_MID_TO_CHANNEL			\
+			| MAX1363_CONFIG_SE,				\
+			.numvals = _num - _mid + 1			\
+}
+
+#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm) {			\
+		.name = #_nump"-"#_numm,				\
+			.conf = MAX1363_CHANNEL_SEL(_nump)		\
+			| MAX1363_CONFIG_SCAN_SINGLE_1			\
+			| MAX1363_CONFIG_DE,				\
+			.numvals = 1,					\
+			}
+
+#define MAX1363_MODE_DIFF_SINGLE_TIMES_8(_nump, _numm) {		\
+		.name = #_nump"-"#_numm,				\
+			.conf = MAX1363_CHANNEL_SEL(_nump)		\
+			| MAX1363_CONFIG_SCAN_SINGLE_8			\
+			| MAX1363_CONFIG_DE,				\
+			.numvals = 1,					\
+			}
+
+/* Can't think how to automate naming so specify for now */
+#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(_name, _num, _numvals) { \
+		.name = #_name,						\
+			.conf = MAX1363_CHANNEL_SEL(_num)		\
+			| MAX1363_CONFIG_SCAN_TO_CS			\
+			| MAX1363_CONFIG_DE,				\
+			.numvals = _numvals,				\
+			}
+
+/* note only available for max1363 hence naming */
+#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(_name, _num, _numvals) { \
+    .name = #_name,							\
+			.conf =MAX1363_CHANNEL_SEL(_num)		\
+			| MAX1236_SCAN_MID_TO_CHANNEL			\
+			| MAX1363_CONFIG_SE,				\
+			.numvals = _numvals,				\
+}
+
+/* Not currently handled */
+#define MAX1363_MODE_MONITOR {					\
+		.name = "monitor",				\
+			.conf = MAX1363_CHANNEL_SEL(3)		\
+			| MAX1363_CONFIG_SCAN_MONITOR_MODE	\
+			| MAX1363_CONFIG_SE,			\
+			.numvals = 10,				\
+		}
+
+/* This may seem an overly long winded way to do this, but at least it makes
+ * clear what all the various options actually do. Alternative suggestions
+ * that don't require user to have intimate knowledge of the chip welcomed.
+ */
+
+/* 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,
+	/* Eight reads of a single channel */
+	se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11,
+	/* Scan to channel */
+	s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+	s0to7, s0to8, s0to9, s0to10, s0to11,
+	/* Differential single read */
+	d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+	d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+	/* Differential single read 8 times */
+	de0m1, de2m3, de4m5, de6m7, de8m9, de10m11,
+	de1m0, de3m2, de5m4, de7m6, de9m8, de11m10,
+	/* Differential scan to channel */
+	d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+	d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+	/* Scan mid to channel max123{6-9} only */
+	s2to3, s6to7, s6to8, s6to9, s6to10, s6to11,
+	/* Differential scan mid to channel */
+	s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10,
+};
+
+/**
+ * struct max1363_chip_info - chip specifc information
+ * @name:		indentification string for chip
+ * @num_inputs:		number of physical inputs on chip
+ * @int_vref_mv:	the internal reference voltage
+ * @monitor_mode:	whether the chip supports monitor interrupts
+ * @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
+ */
+struct max1363_chip_info {
+	const char			*name;
+	u8				num_inputs;
+	u16				int_vref_mv;
+	bool				monitor_mode;
+	const enum max1363_modes	*mode_list;
+	int				num_modes;
+	enum max1363_modes		default_mode;
+};
+
+
+/**
+ * struct max1363_data - driver instance specific data
+ * @indio_dev:		the industrial I/O device
+ * @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
+ * @poll_work:		bottom half of polling interrupt handler
+ * @protect_ring:	used to ensure only one polling bh running at a time
+ */
+struct max1363_data {
+	struct iio_dev			*indio_dev;
+	struct i2c_client		*client;
+	char				setupbyte;
+	char				configbyte;
+	const struct max1363_chip_info	*chip_info;
+	const struct max1363_mode	*current_mode;
+	struct work_struct		poll_work;
+	atomic_t			protect_ring;
+	struct iio_trigger		*trig;
+};
+
+#ifdef CONFIG_MAX1363_RING
+#else /* CONFIG_MAX1363_RING */
+
+static inline ssize_t max1363_scan_from_ring(struct device *dev,
+					     struct device_attribute *attr,
+					     char *buf)
+{
+  return 0;
+};
+
+static inline int
+max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+static inline void max1363_ring_cleanup(struct iio_dev *indio_dev) {};
+
+#endif /* CONFIG_MAX1363_RING */
+
+#endif /* _MAX1363_H_ */
diff --git a/drivers/industrialio/adc/max1363_core.c b/drivers/industrialio/adc/max1363_core.c
new file mode 100644
index 0000000..f83e948
--- /dev/null
+++ b/drivers/industrialio/adc/max1363_core.c
@@ -0,0 +1,599 @@
+ /*
+ * linux/drivers/industrialio/adc/max1363.c
+ * Copyright (C) 2008 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.
+ *
+ * max1363.c
+ *
+ * Partial support for max1363 and similar chips.
+ *
+ * Not currently implemented.
+ *
+ * - Monitor interrrupt generation.
+ * - Control of internal reference.
+ * - Sysfs scan interface currently assumes unipolar mode.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/sysfs.h>
+
+#include "max1363.h"
+
+/* Available scan modes.
+ * Awkwardly the associated enum is in the header so it is available to
+ * the ring buffer code.
+ */
+static const struct  max1363_mode max1363_mode_table[] = {
+	MAX1363_MODE_SINGLE(0),
+	MAX1363_MODE_SINGLE(1),
+	MAX1363_MODE_SINGLE(2),
+	MAX1363_MODE_SINGLE(3),
+	MAX1363_MODE_SINGLE(4),
+	MAX1363_MODE_SINGLE(5),
+	MAX1363_MODE_SINGLE(6),
+	MAX1363_MODE_SINGLE(7),
+	MAX1363_MODE_SINGLE(8),
+	MAX1363_MODE_SINGLE(9),
+	MAX1363_MODE_SINGLE(10),
+	MAX1363_MODE_SINGLE(11),
+
+	MAX1363_MODE_SINGLE_TIMES_8(0),
+	MAX1363_MODE_SINGLE_TIMES_8(1),
+	MAX1363_MODE_SINGLE_TIMES_8(2),
+	MAX1363_MODE_SINGLE_TIMES_8(3),
+	MAX1363_MODE_SINGLE_TIMES_8(4),
+	MAX1363_MODE_SINGLE_TIMES_8(5),
+	MAX1363_MODE_SINGLE_TIMES_8(6),
+	MAX1363_MODE_SINGLE_TIMES_8(7),
+	MAX1363_MODE_SINGLE_TIMES_8(8),
+	MAX1363_MODE_SINGLE_TIMES_8(9),
+	MAX1363_MODE_SINGLE_TIMES_8(10),
+	MAX1363_MODE_SINGLE_TIMES_8(11),
+
+	MAX1363_MODE_SCAN_TO_CHANNEL(1),
+	MAX1363_MODE_SCAN_TO_CHANNEL(2),
+	MAX1363_MODE_SCAN_TO_CHANNEL(3),
+	MAX1363_MODE_SCAN_TO_CHANNEL(4),
+	MAX1363_MODE_SCAN_TO_CHANNEL(5),
+	MAX1363_MODE_SCAN_TO_CHANNEL(6),
+	MAX1363_MODE_SCAN_TO_CHANNEL(7),
+	MAX1363_MODE_SCAN_TO_CHANNEL(8),
+	MAX1363_MODE_SCAN_TO_CHANNEL(9),
+	MAX1363_MODE_SCAN_TO_CHANNEL(10),
+	MAX1363_MODE_SCAN_TO_CHANNEL(11),
+
+	MAX1363_MODE_DIFF_SINGLE(0, 1),
+	MAX1363_MODE_DIFF_SINGLE(2, 3),
+	MAX1363_MODE_DIFF_SINGLE(4, 5),
+	MAX1363_MODE_DIFF_SINGLE(6, 7),
+	MAX1363_MODE_DIFF_SINGLE(8, 9),
+	MAX1363_MODE_DIFF_SINGLE(10, 11),
+	MAX1363_MODE_DIFF_SINGLE(1, 0),
+	MAX1363_MODE_DIFF_SINGLE(3, 2),
+	MAX1363_MODE_DIFF_SINGLE(5, 4),
+	MAX1363_MODE_DIFF_SINGLE(7, 6),
+	MAX1363_MODE_DIFF_SINGLE(9, 8),
+	MAX1363_MODE_DIFF_SINGLE(11, 10),
+
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(0, 1),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(2, 3),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(4, 5),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(6, 7),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(8, 9),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(10, 11),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(1, 0),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(3, 2),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(5, 4),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(7, 6),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(9, 8),
+	MAX1363_MODE_DIFF_SINGLE_TIMES_8(11, 10),
+
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...2-3, 2, 2),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...4-5, 4, 3),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...6-7, 6, 4),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...8-9, 8, 5),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(0-1...10-11, 10, 6),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...3-2, 3, 2),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...5-4, 5, 3),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...7-6, 7, 4),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...9-8, 9, 5),
+	MAX1363_MODE_DIFF_SCAN_TO_CHANNEL_NAMED(1-0...11-10, 11, 6),
+
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10),
+	MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11),
+
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...8-9, 8, 2),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(6-7...10-11, 10, 3),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...9-8, 9, 2),
+	MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL_NAMED(7-6...11-10, 11, 3),
+};
+
+/* Applies to max1363 */
+static const enum max1363_modes max1363_mode_list[] = {
+	_s0, _s1, _s2, _s3,
+	se0, se1, se2, se3,
+	s0to1, s0to2, s0to3,
+	d0m1, d2m3, d1m0, d3m2,
+	de0m1, de2m3, de1m0, de3m2,
+	d0m1to2m3, d1m0to3m2,
+};
+
+/* Appies to max1236, max1237 */
+static const enum max1363_modes max1236_mode_list[] = {
+	_s0, _s1, _s2, _s3,
+	se0, se1, se2, se3,
+	s0to1, s0to2, s0to3,
+	d0m1, d2m3, d1m0, d3m2,
+	de0m1, de2m3, de1m0, de3m2,
+	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,
+	se0, se1, se2, se3, se4, se5, se6, se7, se8, se9, se10, se11,
+	s0to1, s0to2, s0to3, s0to4, s0to5, s0to6,
+	s0to7, s0to8, s0to9, s0to10, s0to11,
+	d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+	d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+	de0m1, de2m3, de4m5, de6m7, de8m9, de10m11,
+	de1m0, de3m2, de5m4, de7m6, de9m8, de11m10,
+	d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11,
+	d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10,
+	s2to3, s6to7, s6to8, s6to9, s6to10, s6to11,
+	s6m7to8m9, s6m7to10m11, s7m6to9m8, s7m6to11m10,
+};
+
+
+enum { max1361,
+       max1362,
+       max1363,
+       max1364,
+       max1136,
+       max1137,
+       max1138,
+       max1139,
+       max1236,
+       max1237,
+       max1238,
+       max1239,
+};
+
+/* max1363 and max1368 tested - rest from data sheet */
+static const struct max1363_chip_info max1363_chip_info_tbl[] = {
+	{
+		.name = "max1361",
+		.num_inputs = 4,
+		.monitor_mode = 1,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1362",
+		.num_inputs = 4,
+		.monitor_mode = 1,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1363",
+		.num_inputs = 4,
+		.monitor_mode = 1,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1364",
+		.num_inputs = 4,
+		.monitor_mode = 1,
+		.mode_list = max1363_mode_list,
+		.num_modes = ARRAY_SIZE(max1363_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1136",
+		.num_inputs = 4,
+		.int_vref_mv = 4096,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1137",
+		.num_inputs = 4,
+		.int_vref_mv = 2048,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1138",
+		.num_inputs = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+	}, {
+		.name = "max1139",
+		.num_inputs = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+	}, {
+		.name = "max1236",
+		.num_inputs = 4,
+		.int_vref_mv = 4096,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1237",
+		.num_inputs = 4,
+		.int_vref_mv = 2048,
+		.mode_list = max1236_mode_list,
+		.num_modes = ARRAY_SIZE(max1236_mode_list),
+		.default_mode = s0to3,
+	}, {
+		.name = "max1238",
+		.num_inputs = 12,
+		.int_vref_mv = 4096,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+	}, {
+		.name = "max1239",
+		.num_inputs = 12,
+		.int_vref_mv = 2048,
+		.mode_list = max1238_mode_list,
+		.num_modes = ARRAY_SIZE(max1238_mode_list),
+		.default_mode = s0to11,
+	},
+};
+
+static int max1363_write_basic_config(struct i2c_client *client,
+				      unsigned char d1,
+				      unsigned char d2)
+{
+	int ret;
+	u8 *tx_buf = kmalloc(2 , GFP_KERNEL);
+	if (!tx_buf)
+		return -ENOMEM;
+	tx_buf[0] = d1;
+	tx_buf[1] = d2;
+	ret = i2c_master_send(client, tx_buf, 2);
+	kfree(tx_buf);
+	return (ret > 0) ? 0 : ret;
+}
+
+static int max1363_set_scan_mode(struct device *dev, struct max1363_data *data)
+{
+	int ret;
+
+	data->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+			      | MAX1363_SCAN_MASK
+			      | MAX1363_SE_DE_MASK);
+
+	data->configbyte |= data->current_mode->conf;
+
+	ret = max1363_write_basic_config(data->client,
+					 data->setupbyte,
+					 data->configbyte);
+
+	return ret;
+}
+
+static int max1363_initial_setup(struct max1363_data *data)
+{
+	int ret;
+
+	data->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;
+
+	data->setupbyte = MAX1363_SETUP_BYTE(data->setupbyte);
+	data->current_mode = &max1363_mode_table[data->chip_info->default_mode];
+	data->configbyte = MAX1363_CONFIG_BYTE(data->configbyte);
+	/* Set scan mode writes the config anyway so don't explicitly do it */
+	ret = max1363_set_scan_mode(data->indio_dev->dev, data);
+
+	return ret;
+}
+
+static ssize_t max1363_show_av_scan_modes(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct max1363_data *data = dev_info->dev_data;
+	int i, len = 0;
+
+	for (i = 0; i < data->chip_info->num_modes; i++)
+		len += sprintf(buf + len, "%s ",
+			       max1363_mode_table[data->chip_info
+						  ->mode_list[i]].name);
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+
+
+static ssize_t max1363_scan_direct(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct max1363_data *data = dev_info->dev_data;
+	int len = 0, ret, i;
+	struct i2c_client *client = to_i2c_client(dev);
+	char *rxbuf;
+
+	if (data->current_mode->numvals == 0)
+		return 0;
+	rxbuf = kmalloc(data->current_mode->numvals*2, GFP_KERNEL);
+	if (rxbuf == NULL)
+		return -ENOMEM;
+
+	/* Interpretation depends on whether these are signed or not!*/
+	/* Assume not for now */
+	ret = i2c_master_recv(client, rxbuf, data->current_mode->numvals*2);
+
+	if (ret < 0)
+		return ret;
+	for (i = 0; i < data->current_mode->numvals; i++)
+		len += sprintf(buf+len, "%d ",
+			       ((int)(rxbuf[i*2+0]&0x0F) << 8)
+			       + ((int)(rxbuf[i*2+1])));
+	kfree(rxbuf);
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static ssize_t max1363_scan(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&dev_info->mlock);
+	if (dev_info->currentmode == INDIO_RING_TRIGGERED)
+		ret = max1363_scan_from_ring(dev, attr, buf);
+	else
+		ret = max1363_scan_direct(dev, attr, buf);
+	mutex_unlock(&dev_info->mlock);
+
+	return ret;
+}
+
+/* Cannot query the device, so use local copy of state */
+static ssize_t max1363_show_scan_mode(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	int len = 0;
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct max1363_data *data = dev_info->dev_data;
+
+	len += sprintf(buf, "%s\n", data->current_mode->name);
+
+	return len;
+}
+
+static const struct max1363_mode
+*__max1363_find_mode_in_ci(const struct max1363_chip_info *info,
+				  const char *buf)
+{
+	int i;
+	for (i = 0; i <  info->num_modes; i++)
+		if (strcmp(max1363_mode_table[info->mode_list[i]].name, buf)
+		    == 0)
+			return &max1363_mode_table[info->mode_list[i]];
+	return NULL;
+}
+
+static ssize_t max1363_store_scan_mode(struct device *dev,
+				       struct device_attribute *attr,
+				       const char *buf,
+				       size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct max1363_data *data = dev_info->dev_data;
+	int ret;
+
+	mutex_lock(&dev_info->mlock);
+	data->current_mode = NULL;
+	/* Avoid state changes if a ring buffer is enabled */
+	if (!iio_ring_enabled(dev_info)) {
+		data->current_mode
+			= __max1363_find_mode_in_ci(data->chip_info, buf);
+		if (!data->current_mode) {
+			ret = -EINVAL;
+			goto error_ret;
+		}
+		ret =  max1363_set_scan_mode(dev, data);
+		if (ret)
+			goto error_ret;
+} else {
+		ret = -EBUSY;
+		goto error_ret;
+	}
+	mutex_unlock(&dev_info->mlock);
+
+	return len;
+
+error_ret:
+	mutex_unlock(&dev_info->mlock);
+
+	return ret;
+}
+
+IIO_DEV_ATTR_AVAIL_SCAN_MODES(max1363_show_av_scan_modes);
+IIO_DEV_ATTR_SCAN_MODE(S_IRUGO | S_IWUSR,
+		       max1363_show_scan_mode,
+		       max1363_store_scan_mode);
+
+IIO_DEV_ATTR_SCAN(max1363_scan);
+
+
+
+static struct attribute *max1363_attributes[] = {
+	&iio_dev_attr_available_scan_modes.dev_attr.attr,
+	&iio_dev_attr_scan_mode.dev_attr.attr,
+	&iio_dev_attr_scan.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group max1363_attribute_group = {
+	.attrs = max1363_attributes,
+};
+
+static int __devinit max1363_probe(struct i2c_client *client,
+				   const struct i2c_device_id *id)
+{
+	int ret, i;
+	struct max1363_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (data == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	atomic_set(&data->protect_ring, 0);
+
+	/* Find the default mode */
+	for (i = 0; i < ARRAY_SIZE(max1363_chip_info_tbl); i++)
+		if (!strcmp(max1363_chip_info_tbl[i].name, id->name)) {
+			data->chip_info = &max1363_chip_info_tbl[i];
+			break;
+		};
+	/* Unsupported default mode? */
+	if (!data->chip_info) {
+		dev_err(&client->dev, "%s is not supported\n", id->name);
+		ret = -ENODEV;
+		goto error_free_data;
+	}
+
+	data->client = client;
+
+	data->indio_dev = kzalloc(sizeof(*data->indio_dev), GFP_KERNEL);
+	if (data->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_data;
+	}
+
+	data->indio_dev->dev = &client->dev;
+	data->indio_dev->attrs = &max1363_attribute_group;
+	data->indio_dev->dev_data = (void *)(data);
+
+	data->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = max1363_register_ring_funcs_and_init(data->indio_dev);
+	if (ret)
+		goto error_free_device;
+
+	ret = iio_device_register(data->indio_dev);
+	if (ret)
+		goto error_cleanup_ring;
+
+	ret = max1363_initial_setup(data);
+	if (ret)
+		goto error_unregister_dev;
+
+	return 0;
+
+error_unregister_dev:
+	iio_device_unregister(data->indio_dev);
+error_cleanup_ring:
+	max1363_ring_cleanup(data->indio_dev);
+error_free_device:
+	kfree(data->indio_dev);
+error_free_data:
+	kfree(data);
+error_ret:
+	return ret;
+}
+
+static int max1363_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct max1363_data *data = indio_dev->dev_data;
+
+/* Currently device left in whatever state it is currently in */
+	iio_device_unregister(indio_dev);
+	max1363_ring_cleanup(indio_dev);
+	kfree(indio_dev);
+	kfree(data);
+
+	return 0;
+}
+
+static const struct i2c_device_id max1363_id[] = {
+	{ "max1361", max1361 },
+	{ "max1362", max1362 },
+	{ "max1363", max1363 },
+	{ "max1364", max1364 },
+	{ "max1136", max1136 },
+	{ "max1137", max1137 },
+	{ "max1138", max1138 },
+	{ "max1139", max1139 },
+	{ "max1236", max1236 },
+	{ "max1237", max1237 },
+	{ "max1238", max1238 },
+	{ "max1239", max1239 },
+	{}
+};
+
+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);
+}
+
+static __exit void max1363_exit(void)
+{
+	i2c_del_driver(&max1363_driver);
+}
+
+MODULE_AUTHOR("Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>");
+MODULE_DESCRIPTION("Maxim 1363 ADC");
+MODULE_LICENSE("GPL v2");
+
+module_init(max1363_init);
+module_exit(max1363_exit);

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

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

* [Industrial I/O] [0/13] RFC:  Maxim MAX1363 driver (ring buffer support)
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (6 preceding siblings ...)
       [not found] ` <4933F291.4020001-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
@ 2008-12-01 14:34 ` Jonathan Cameron
  2008-12-01 14:35 ` [Industrial I/O] [9/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (core) Jonathan Cameron
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:34 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general


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

IIO: Maxim MAX1363 driver (ring buffer support)

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--
 This patch adds triggered software ring buffer support to
 the core driver.  It should act as an example of how many
 of the elements of the IIO subsystem function.

 There is currently an issue with unaligned timestamps on
 arm to be resolved when non multiple of 4 scan modes used.

 drivers/industrialio/adc/Kconfig        |    6 +
 drivers/industrialio/adc/Makefile       |    2 +
 drivers/industrialio/adc/max1363.h      |    9 ++
 drivers/industrialio/adc/max1363_ring.c |  219 +++++++++++++++++++++++++++++++
 4 files changed, 236 insertions(+), 0 deletions(-)

diff --git a/drivers/industrialio/adc/Kconfig b/drivers/industrialio/adc/Kconfig
index 203a5f9..be77ab5 100644
--- a/drivers/industrialio/adc/Kconfig
+++ b/drivers/industrialio/adc/Kconfig
@@ -11,3 +11,9 @@ config MAX1363
 	  convertors (ADC). (max1361, max1362, max1363, max1364, max1136,
 	  max1136, max1137, max1138, max1139, max1236, max1237, max11238,
 	  max1239) Provides direct access via sysfs.
+
+config MAX1363_RING
+       boolean "Ring buffer support"
+       depends on MAX1363 && IIO_SW_RING && IIO_TRIGGERS
+       help
+         Enable software ring buffer support in the max1363 driver.
diff --git a/drivers/industrialio/adc/Makefile b/drivers/industrialio/adc/Makefile
index 2a37fee..a6bff5f 100644
--- a/drivers/industrialio/adc/Makefile
+++ b/drivers/industrialio/adc/Makefile
@@ -3,5 +3,7 @@
 #
 
 max1363-y := max1363_core.o
+#got to be a better way of doing this 
+max1363-$(CONFIG_MAX1363_RING) += max1363_ring.o
 
 obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/industrialio/adc/max1363.h b/drivers/industrialio/adc/max1363.h
index 71e9577..b0e8928 100644
--- a/drivers/industrialio/adc/max1363.h
+++ b/drivers/industrialio/adc/max1363.h
@@ -228,6 +228,15 @@ struct max1363_data {
 };
 
 #ifdef CONFIG_MAX1363_RING
+
+ssize_t max1363_scan_from_ring(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf);
+int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev);
+void max1363_ring_cleanup(struct iio_dev *indio_dev);
+
+
+
 #else /* CONFIG_MAX1363_RING */
 
 static inline ssize_t max1363_scan_from_ring(struct device *dev,
diff --git a/drivers/industrialio/adc/max1363_ring.c b/drivers/industrialio/adc/max1363_ring.c
new file mode 100644
index 0000000..b2b9e18
--- /dev/null
+++ b/drivers/industrialio/adc/max1363_ring.c
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ *
+ * max1363_ring.c
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+
+
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/ring_sw.h>
+#include <linux/industrialio/trigger.h>
+#include <linux/industrialio/sysfs.h>
+
+#include "max1363.h"
+
+ssize_t max1363_scan_from_ring(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct max1363_data *info = dev_info->dev_data;
+	int i, ret, len = 0;
+	char *ring_data;
+
+	ring_data = kmalloc(info->current_mode->numvals*2, GFP_KERNEL);
+	if (ring_data == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	ret = dev_info->ring_access.read_last(dev_info->ring, ring_data);
+	if (ret)
+		goto error_free_ring_data;
+	len += sprintf(buf+len, "ring ");
+	for (i = 0; i < info->current_mode->numvals; i++)
+		len += sprintf(buf + len, "%d ",
+			       ((int)(ring_data[i*2 + 0] & 0x0F) << 8)
+			       + ((int)(ring_data[i*2 + 1])));
+	len += sprintf(buf + len, "\n");
+	kfree(ring_data);
+
+	return len;
+
+error_free_ring_data:
+	kfree(ring_data);
+error_ret:
+	return ret;
+}
+
+/**
+ * max1363_ring_preenable() setup the parameters of the ring before enabling
+ *
+ * The complex nature of the setting of the nuber of bytes per datum is due
+ * to this driver currently ensuring that the timestamp is stored at an 8
+ * byte boundary.
+ **/
+static int max1363_ring_preenable(struct iio_dev *indio_dev)
+{
+	struct max1363_data *data = indio_dev->dev_data;
+	size_t d_size;
+
+	if (indio_dev->ring_access.set_bpd) {
+		d_size = data->current_mode->numvals*2 + sizeof(s64);
+		if (d_size % 8)
+			d_size += 8 - (d_size % 8);
+		indio_dev->ring_access.set_bpd(indio_dev->ring, d_size);
+	}
+
+	return 0;
+}
+
+/**
+ * max1363_ring_postenable() typical ring post enable
+ *
+ * Only not moved into the core for the hardware ring buffer cases
+ * that are more sophisticated.
+ **/
+static int max1363_ring_postenable(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig == NULL)
+		return 0;
+	return iio_trigger_attach_poll_func(indio_dev->trig,
+					    indio_dev->pollfunc);
+}
+
+/**
+ * max1363_ring_predisable() runs just prior to ring buffer being disabled
+ *
+ * Typical predisable function which ensures that no trigger events can
+ * occur before we disable the ring buffer (and hence would have no idea
+ * what to do with them)
+ **/
+static int max1363_ring_predisable(struct iio_dev *indio_dev)
+{
+	if (indio_dev->trig)
+		return iio_trigger_dettach_poll_func(indio_dev->trig,
+						     indio_dev->pollfunc);
+	else
+		return 0;
+}
+
+/**
+ * max1363_poll_func_th() th of trigger launched polling to ring buffer
+ *
+ * As sampling only occurs on i2c comms occuring, leave timestamping until
+ * then.  Some triggers will generate their own time stamp.  Currently
+ * there is no way of notifying them when no one cares.
+ **/
+void max1363_poll_func_th(struct iio_dev *indio_dev)
+{
+	struct max1363_data *data = indio_dev->dev_data;
+
+	schedule_work(&data->poll_work);
+
+	return;
+}
+/**
+ * max1363_poll_bh_to_ring() bh of trigger launched polling to ring buffer
+ * @work_s:	the work struct through which this was scheduled
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ * I think the one copy of this at a time was to avoid problems if the
+ * trigger was set far too high and the reads then locked up the computer.
+ **/
+static void max1363_poll_bh_to_ring(struct work_struct *work_s)
+{
+	struct max1363_data *data = container_of(work_s, struct max1363_data,
+						 poll_work);
+	struct iio_dev *indio_dev = data->indio_dev;
+	struct iio_sw_ring_buffer *ring = indio_dev->ring;
+	s64 time_ns;
+	__u8 *rxbuf;
+	int b_sent;
+	size_t d_size;
+
+	/* Ensure the timestamp is 8 byte aligned */
+	d_size = data->current_mode->numvals*2 + sizeof(s64);
+	if (d_size % sizeof(s64))
+		d_size += sizeof(s64) - (d_size % sizeof(s64));
+
+	/* Ensure only one copy of this function running at a time */
+	if (atomic_inc_return(&data->protect_ring) > 1)
+		return;
+
+	/* Monitor mode prevents reading. Whilst not currently implemented
+	 * might as well have this test in here in the meantime as it does
+	 * no harm.
+	 */
+	if (data->current_mode->numvals == 0)
+		return;
+
+	rxbuf = kmalloc(d_size,	GFP_KERNEL);
+	if (rxbuf == NULL)
+		return;
+
+	b_sent = i2c_master_recv(data->client,
+				 rxbuf,
+				 data->current_mode->numvals*2);
+	if (b_sent < 0)
+		goto done;
+
+	time_ns = iio_get_time_ns();
+
+	memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
+	indio_dev->ring_access.store_to(ring, rxbuf, time_ns);
+done:
+	kfree(rxbuf);
+	atomic_dec(&data->protect_ring);
+}
+
+int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+	struct max1363_data *data = indio_dev->dev_data;
+	int ret = 0;
+
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&data->indio_dev->ring_access);
+
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* Configure the polling function called on trigger interrupts */
+	indio_dev->pollfunc->poll_func_main = &max1363_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+
+	/* Ring buffer functions - here trigger setup related */
+	indio_dev->ring_postenable = &max1363_ring_postenable;
+	indio_dev->ring_preenable = &max1363_ring_preenable;
+	indio_dev->ring_predisable = &max1363_ring_predisable;
+
+	INIT_WORK(&data->poll_work, &max1363_poll_bh_to_ring);
+
+	/* Flag that polled ring buffering is possible */
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+
+error_ret:
+	return ret;
+}
+
+void max1363_ring_cleanup(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+}
+

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

* [Industrial I/O] [9/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (core)
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (7 preceding siblings ...)
  2008-12-01 14:34 ` [Industrial I/O] [0/13] RFC: Maxim MAX1363 driver (ring buffer support) Jonathan Cameron
@ 2008-12-01 14:35 ` Jonathan Cameron
  2008-12-01 14:36 ` [Industrial I/O] [10/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (ring) Jonathan Cameron
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:35 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (core)

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--

This is a fairly typical SPI accelerometer.

I2C functionality will be added at a later date.

Features:
	Variable sampling rates
	Data ready signal to indicate new data
	     -this is awkward as it will go high on any channel
	      having new data and hence needs complex handler.
	Axis by axis gain and offset adjustment.
	Axis threshold detector (single theshold for all 3)

 drivers/industrialio/Kconfig                       |    1 +
 drivers/industrialio/Makefile                      |    3 +-
 drivers/industrialio/accelerometer/Kconfig         |   21 +
 drivers/industrialio/accelerometer/Makefile        |    6 +
 drivers/industrialio/accelerometer/lis3l02dq.h     |  218 +++++
 .../industrialio/accelerometer/lis3l02dq_core.c    |  886 ++++++++++++++++++++
 include/linux/industrialio/accel.h                 |  157 ++++
 7 files changed, 1291 insertions(+), 1 deletions(-)

diff --git a/drivers/industrialio/Kconfig b/drivers/industrialio/Kconfig
index f682fbf..58bfbd7 100644
--- a/drivers/industrialio/Kconfig
+++ b/drivers/industrialio/Kconfig
@@ -38,4 +38,5 @@ config IIO_SW_RING
 	with the intention that some devices would be able to write
 	in interrupt context.
 
+source drivers/industrialio/accelerometer/Kconfig
 source drivers/industrialio/adc/Kconfig
\ No newline at end of file
diff --git a/drivers/industrialio/Makefile b/drivers/industrialio/Makefile
index dd6d5ca..453dcc3 100644
--- a/drivers/industrialio/Makefile
+++ b/drivers/industrialio/Makefile
@@ -10,4 +10,5 @@ industrialio-$(CONFIG_IIO_TRIGGERS) += industrialio-trigger.o
 obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
 
 obj-y += triggers/
-obj-y += adc/
\ No newline at end of file
+obj-y += adc/
+obj-y += accelerometer/
\ No newline at end of file
diff --git a/drivers/industrialio/accelerometer/Kconfig b/drivers/industrialio/accelerometer/Kconfig
new file mode 100644
index 0000000..7e011a6
--- /dev/null
+++ b/drivers/industrialio/accelerometer/Kconfig
@@ -0,0 +1,21 @@
+#
+# Accelerometer drivers
+#
+comment "Accelerometers"
+	depends on INDUSTRIALIO
+config LIS3L02DQ
+	tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
+	depends on INDUSTRIALIO
+	help
+	  Say yes here to build generic support for the ST microelectronics
+	  accelerometer. You will also need to one or more of the bus specific
+	  elements below. The driver supplies direct access via sysfs files
+	  and a software ring buffer using a supplied datardy interrupt.
+
+config LIS3L02DQ_SPI
+	depends on LIS3L02DQ && SPI
+	tristate "SPI support"
+	help
+	  Say yes here to build support for the ST LIS3L02DQ accelerometer via
+	  an SPI bus.
+
diff --git a/drivers/industrialio/accelerometer/Makefile b/drivers/industrialio/accelerometer/Makefile
new file mode 100644
index 0000000..22f3d87
--- /dev/null
+++ b/drivers/industrialio/accelerometer/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+lis3l02dq-y := lis3l02dq_core.o
+obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
diff --git a/drivers/industrialio/accelerometer/lis3l02dq.h b/drivers/industrialio/accelerometer/lis3l02dq.h
new file mode 100644
index 0000000..f53b68b
--- /dev/null
+++ b/drivers/industrialio/accelerometer/lis3l02dq.h
@@ -0,0 +1,218 @@
+/*
+ * LISL02DQ.h -- support STMicroelectronics LISD02DQ
+ *               3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ *
+ * Loosely based upon tle62x0.c
+ *
+ * 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_LIS3L02DQ_H_
+#define SPI_LIS3L02DQ_H_
+#define LIS3L02DQ_READ_REG(a) ((a) | 0x80)
+#define LIS3L02DQ_WRITE_REG(a) a
+
+/* Calibration parameters */
+#define LIS3L02DQ_REG_OFFSET_X_ADDR		0x16
+#define LIS3L02DQ_REG_OFFSET_Y_ADDR		0x17
+#define LIS3L02DQ_REG_OFFSET_Z_ADDR		0x18
+
+#define LIS3L02DQ_REG_GAIN_X_ADDR		0x19
+#define LIS3L02DQ_REG_GAIN_Y_ADDR		0x1A
+#define LIS3L02DQ_REG_GAIN_Z_ADDR		0x1B
+
+/* Control Register (1 of 2) */
+#define LIS3L02DQ_REG_CTRL_1_ADDR		0x20
+/* Power ctrl - either bit set corresponds to on*/
+#define LIS3L02DQ_REG_CTRL_1_PD_ON	0xC0
+
+/* Decimation Factor  */
+#define LIS3L02DQ_DEC_MASK			0x30
+#define LIS3L02DQ_REG_CTRL_1_DF_128		0x00
+#define LIS3L02DQ_REG_CTRL_1_DF_64		0x10
+#define LIS3L02DQ_REG_CTRL_1_DF_32		0x20
+#define LIS3L02DQ_REG_CTRL_1_DF_8		(0x10 | 0x20)
+
+/* Self Test Enable */
+#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON	0x08
+
+/* Axes enable ctrls */
+#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE	0x04
+#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE	0x02
+#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE	0x01
+
+/* Control Register (2 of 2) */
+#define LIS3L02DQ_REG_CTRL_2_ADDR		0x21
+
+/* Block Data Update only after MSB and LSB read */
+#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE	0x40
+
+/* Set to big endian output */
+#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN		0x20
+
+/* Reboot memory content */
+#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY	0x10
+
+/* Interupt Enable - applies data ready to the RDY pad */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT	0x08
+
+/* Enable Data Ready Generation - relationship with previous unclear in docs */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04
+
+/* SPI 3 wire mode */
+#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE	0x02
+
+/* Data alignment, default is 12 bit right justified
+ * - option for 16 bit left justified */
+#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED	0x01
+
+/* Interupt related stuff */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR			0x23
+
+/* Switch from or combination fo conditions to and */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND		0x80
+
+/* Latch interupt request,
+ * if on ack must be given by reading the ack register */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC		0x40
+
+/* Z Interupt on High (above threshold)*/
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH	0x20
+/* Z Interupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW	0x10
+/* Y Interupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH	0x08
+/* Y Interupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW	0x04
+/* X Interupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH	0x02
+/* X Interupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01
+
+/* Register that gives description of what caused interupt
+ * - latched if set in CFG_ADDRES */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDR			0x24
+/* top bit ignored */
+/* Interupt Active */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED	0x40
+/* Interupts that have been triggered */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH	0x20
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW	0x10
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH	0x08
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW	0x04
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH	0x02
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW	0x01
+
+#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDR			0x25
+
+/* Status register */
+#define LIS3L02DQ_REG_STATUS_ADDR			0x27
+/* XYZ axis data overrun - first is all overrun? */
+#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN		0x80
+#define LIS3L02DQ_REG_STATUS_Z_OVERRUN			0x40
+#define LIS3L02DQ_REG_STATUS_Y_OVERRUN			0x20
+#define LIS3L02DQ_REG_STATUS_X_OVERRUN			0x10
+/* XYZ new data available - first is all 3 available? */
+#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
+#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA			0x04
+#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA			0x02
+#define LIS3L02DQ_REG_STATUS_X_NEW_DATA			0x01
+
+/* The accelerometer readings - low and high bytes.
+Form of high byte dependant on justification set in ctrl reg */
+#define LIS3L02DQ_REG_OUT_X_L_ADDR			0x28
+#define LIS3L02DQ_REG_OUT_X_H_ADDR			0x29
+#define LIS3L02DQ_REG_OUT_Y_L_ADDR			0x2A
+#define LIS3L02DQ_REG_OUT_Y_H_ADDR			0x2B
+#define LIS3L02DQ_REG_OUT_Z_L_ADDR			0x2C
+#define LIS3L02DQ_REG_OUT_Z_H_ADDR			0x2D
+
+/* Threshold values for all axes and both above and below thresholds
+ * - i.e. there is only one value */
+#define LIS3L02DQ_REG_THS_L_ADDR			0x2E
+#define LIS3L02DQ_REG_THS_H_ADDR			0x2F
+
+#define LIS3L02DQ_DEFAULT_CTRL1 (LIS3L02DQ_REG_CTRL_1_PD_ON	      \
+				 | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \
+				 | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \
+				 | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \
+				 | LIS3L02DQ_REG_CTRL_1_DF_128)
+
+#define LIS3L02DQ_DEFAULT_CTRL2	0
+
+#define LIS3L02DQ_MAX_TX 12
+#define LIS3L02DQ_MAX_RX 12
+/**
+ * struct lis3l02dq_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @work_cont_thresh: CLEAN
+ * @inter:		used to check if new interrupt has been triggered
+ * @last_timestamp:	passing timestamp from th to bh of interrupt handler
+ * @indio_dev:		industrial I/O device structure
+ * @trig:		data ready trigger registered with iio
+ * @tx:			transmit buffer
+ * @rx:			recieve buffer
+ * @buf_lock:		mutex to protect tx and rx
+ **/
+struct lis3l02dq_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	struct iio_work_cont		work_cont_thresh;
+	bool				inter;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int lis3l02dq_spi_read_reg_8(struct device *dev,
+			     u8 reg_address,
+			     u8 *val);
+
+int lis3l02dq_spi_write_reg_8(struct device *dev,
+			      u8 reg_address,
+			      u8 *val);
+#define LIS3L02DQ_SCAN_ACC_X 0
+#define LIS3L02DQ_SCAN_ACC_Y 1
+#define LIS3L02DQ_SCAN_ACC_Z 2
+/* temporary disable */
+
+static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev){};
+static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+static inline ssize_t
+lis3l02dq_read_accel_from_ring(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	return 0;
+};
+
+
+static inline int lis3l02dq_register_ring_funcs(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+static inline void lis3l02dq_unregister_ring_funcs(struct iio_dev *indio_dev){};
+static inline
+int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length)
+{
+	return 0;
+};
+
+
+#endif /* SPI_LIS3L02DQ_H_ */
+
+
+
+
diff --git a/drivers/industrialio/accelerometer/lis3l02dq_core.c b/drivers/industrialio/accelerometer/lis3l02dq_core.c
new file mode 100644
index 0000000..61d82ce
--- /dev/null
+++ b/drivers/industrialio/accelerometer/lis3l02dq_core.c
@@ -0,0 +1,886 @@
+/*
+ * lis3l02dq.c	support STMicroelectronics LISD02DQ
+ *		3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ *
+ * 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.
+ *
+ * Settings:
+ * 16 bit left justified mode used.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/sysfs.h>
+#include <linux/industrialio/ring_sw.h>
+#include <linux/industrialio/accel.h>
+
+#include "lis3l02dq.h"
+
+/* At the moment the spi framework doesn't allow global setting of cs_change.
+ * It's in the likely to be added comment at the top of spi.h.
+ * This means that use cannot be made of spi_write etc.
+ */
+
+/* This could be down with an spi write then read call, but for consistency
+ * of locking etc it is done longhand */
+int lis3l02dq_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+	struct spi_transfer xfer = {
+		.tx_buf = st->tx,
+		.rx_buf = st->rx,
+		.bits_per_word = 8,
+		.len = 2,
+		.cs_change = 1,
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = LIS3L02DQ_READ_REG(reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->us, &msg);
+	*val = st->rx[1];
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+int lis3l02dq_spi_write_reg_8(struct device *dev,
+				     u8 reg_address,
+				     u8 *val)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+	struct spi_transfer xfer = {
+		.tx_buf = st->tx,
+		.bits_per_word = 8,
+		.len = 2,
+		.cs_change = 1,
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address);
+	st->tx[1] = *val;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret =  spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+
+/* Relies on the MSB being one higher adress than the LSB */
+static int lis3l02dq_spi_write_reg_s16(struct device *dev,
+				       u8 lower_reg_address,
+				       s16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+	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,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = LIS3L02DQ_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;
+}
+
+static int lis3l02dq_spi_read_reg_s16(struct device *dev,
+				      u8 lower_reg_address,
+				      s16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+	int ret;
+	struct spi_transfer xfers[] = { {
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.rx_buf = st->rx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+	st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address+1);
+	st->tx[3] = 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");
+		goto error_ret;
+	}
+	*val = st->rx[1] | (st->rx[3] << 8);
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t lis3l02dq_read_signed(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	int ret;
+	s8 val;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, (u8 *)&val);
+
+	return ret ? ret : sprintf(buf, "%d\n", val);
+}
+
+static ssize_t lis3l02dq_read_unsigned(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	int ret;
+	u8 val;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = lis3l02dq_spi_read_reg_8(dev, this_attr->address, &val);
+
+	return ret ? ret : sprintf(buf, "%d\n", val);
+}
+
+static ssize_t lis3l02dq_write_signed(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t len)
+{
+	long valin;
+	s8 val;
+	int ret;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = strict_strtol(buf, 10, &valin);
+	if (ret)
+		goto error_ret;
+	val = valin;
+	ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, (u8 *)&val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t lis3l02dq_write_unsigned(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t len)
+{
+	int ret;
+	ulong valin;
+	u8 val;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = strict_strtoul(buf, 10, &valin);
+	if (ret)
+		goto err_ret;
+	val = valin;
+	ret = lis3l02dq_spi_write_reg_8(dev, this_attr->address, &val);
+
+err_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t lis3l02dq_read_16bit_signed(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	int ret;
+	s16 val;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = lis3l02dq_spi_read_reg_s16(dev, this_attr->address, &val);
+
+	return ret ? ret : sprintf(buf, "%d\n", val);
+}
+
+
+/**
+ * lis3l02dq_read_accel() Reads acceleration from device or ring
+ *
+ * Prevents the mode from changing during a read
+ **/
+static ssize_t lis3l02dq_read_accel(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t returnval;
+
+	mutex_lock(&indio_dev->mlock);
+	if (indio_dev->currentmode == INDIO_RING_TRIGGERED)
+		returnval = lis3l02dq_read_accel_from_ring(dev, attr, buf);
+	else
+		returnval =  lis3l02dq_read_16bit_signed(dev, attr, buf);
+	mutex_unlock(&indio_dev->mlock);
+
+	return returnval;
+}
+
+static ssize_t lis3l02dq_write_16bit_signed(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf,
+					    size_t len)
+{
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = lis3l02dq_spi_write_reg_s16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static ssize_t lis3l02dq_read_frequency(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	int ret, len;
+	s8 t;
+	ret = lis3l02dq_spi_read_reg_8(dev,
+				       LIS3L02DQ_REG_CTRL_1_ADDR,
+				       (u8 *)&t);
+	if (ret)
+		goto error_ret;
+	t &= LIS3L02DQ_DEC_MASK;
+	if (t == LIS3L02DQ_REG_CTRL_1_DF_128)
+		len = sprintf(buf, "280");
+	else if (t == LIS3L02DQ_REG_CTRL_1_DF_64)
+		len = sprintf(buf, "560");
+	else if (t == LIS3L02DQ_REG_CTRL_1_DF_32)
+		len = sprintf(buf, "1120");
+	else
+		len = sprintf(buf, "4480");
+	len += sprintf(buf + len, "\n");
+
+	return len;
+
+error_ret:
+	return ret;
+}
+
+static ssize_t lis3l02dq_write_frequency(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	long val;
+	int ret;
+	u8 t;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&indio_dev->mlock);
+	ret = lis3l02dq_spi_read_reg_8(dev,
+				       LIS3L02DQ_REG_CTRL_1_ADDR,
+				       &t);
+	if (ret)
+		goto error_ret;
+	/* Wipe the bits clean */
+	t &= ~LIS3L02DQ_DEC_MASK;
+	switch (val) {
+	case 280:
+		t |= LIS3L02DQ_REG_CTRL_1_DF_128;
+		break;
+	case 560:
+		t |= LIS3L02DQ_REG_CTRL_1_DF_64;
+		break;
+	case 1120:
+		t |= LIS3L02DQ_REG_CTRL_1_DF_32;
+		break;
+	case 4480:
+		t |= LIS3L02DQ_REG_CTRL_1_DF_8;
+		break;
+	default:
+		ret = -EINVAL;
+		goto error_ret;
+	};
+
+	ret = lis3l02dq_spi_write_reg_8(dev,
+					LIS3L02DQ_REG_CTRL_1_ADDR,
+					&t);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+static int lis3l02dq_initial_setup(struct lis3l02dq_state *st)
+{
+	int ret;
+	u8 val, valtest;
+
+	st->us->mode = SPI_MODE_3;
+
+	spi_setup(st->us);
+
+	val = LIS3L02DQ_DEFAULT_CTRL1;
+
+	/* Write suitable defaults to ctrl1 */
+	ret = lis3l02dq_spi_write_reg_8(&st->us->dev,
+					LIS3L02DQ_REG_CTRL_1_ADDR,
+					&val);
+	if (ret) {
+		dev_err(&st->us->dev, "problem with setup control register 1");
+		goto err_ret;
+	}
+	/* Read back to check this has worked acts as loose test of correct
+	 * chip */
+	ret = lis3l02dq_spi_read_reg_8(&st->us->dev,
+				       LIS3L02DQ_REG_CTRL_1_ADDR,
+				       &valtest);
+	if (ret || (valtest != val)) {
+		dev_err(st->indio_dev->dev, "device not playing ball");
+		ret = -EINVAL;
+		goto err_ret;
+	}
+
+	val = LIS3L02DQ_DEFAULT_CTRL2;
+	ret = lis3l02dq_spi_write_reg_8(&st->us->dev,
+					LIS3L02DQ_REG_CTRL_2_ADDR,
+					&val);
+	if (ret) {
+		dev_err(&st->us->dev, "problem with setup control register 2");
+		goto err_ret;
+	}
+
+	val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC;
+	ret = lis3l02dq_spi_write_reg_8(&st->us->dev,
+					LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+					&val);
+	if (ret)
+		dev_err(&st->us->dev, "problem with interrupt cfg register");
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+				   lis3l02dq_read_signed,
+				   lis3l02dq_write_signed,
+				   LIS3L02DQ_REG_OFFSET_X_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+				   lis3l02dq_read_signed,
+				   lis3l02dq_write_signed,
+				   LIS3L02DQ_REG_OFFSET_Y_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO,
+				   lis3l02dq_read_signed,
+				   lis3l02dq_write_signed,
+				   LIS3L02DQ_REG_OFFSET_Z_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_X_GAIN(S_IWUSR | S_IRUGO,
+				 lis3l02dq_read_unsigned,
+				 lis3l02dq_write_unsigned,
+				 LIS3L02DQ_REG_GAIN_X_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_Y_GAIN(S_IWUSR | S_IRUGO,
+				 lis3l02dq_read_unsigned,
+				 lis3l02dq_write_unsigned,
+				 LIS3L02DQ_REG_GAIN_Y_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_Z_GAIN(S_IWUSR | S_IRUGO,
+				 lis3l02dq_read_unsigned,
+				 lis3l02dq_write_unsigned,
+				 LIS3L02DQ_REG_GAIN_Z_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_THRESH(S_IWUSR | S_IRUGO,
+				 lis3l02dq_read_16bit_signed,
+				 lis3l02dq_write_16bit_signed,
+				 LIS3L02DQ_REG_THS_L_ADDR);
+
+/* RFC The reading method for these will change depending on whether
+ * ring buffer capture is in use. Is it worth making these take two
+ * functions and let the core handle which to call, or leave as in this
+ * driver where it is the drivers problem to manage this?
+ */
+
+static IIO_DEV_ATTR_ACCEL_X(lis3l02dq_read_accel,
+			    LIS3L02DQ_REG_OUT_X_L_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_Y(lis3l02dq_read_accel,
+			    LIS3L02DQ_REG_OUT_Y_L_ADDR);
+
+static IIO_DEV_ATTR_ACCEL_Z(lis3l02dq_read_accel,
+			    LIS3L02DQ_REG_OUT_Z_L_ADDR);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			      lis3l02dq_read_frequency,
+			      lis3l02dq_write_frequency);
+
+static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("280 560 1120 4480\n");
+
+static ssize_t lis3l02dq_read_interrupt_config(struct device *dev,
+					       struct device_attribute *attr,
+					       char *buf)
+{
+	int ret;
+	s8 val;
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+	ret = lis3l02dq_spi_read_reg_8(dev,
+				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+				       (u8 *)&val);
+
+	return ret ? ret : sprintf(buf, "%d\n",
+				   (val & this_attr->mask) ? 1 : 0);;
+}
+
+static ssize_t lis3l02dq_write_interrupt_config(struct device *dev,
+						struct device_attribute *attr,
+						const char *buf,
+						size_t len)
+{
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	int ret, currentlyset, changed = 0;
+	u8 valold, controlold;
+	bool val;
+
+	val = (buf[0] == '0') ? 0 : 1;
+
+	mutex_lock(&indio_dev->mlock);
+	/* read current value */
+	ret = lis3l02dq_spi_read_reg_8(dev,
+				       LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+				       &valold);
+	if (ret)
+		goto error_mutex_unlock;
+
+	/* read current control */
+	ret = lis3l02dq_spi_read_reg_8(dev,
+				       LIS3L02DQ_REG_CTRL_2_ADDR,
+				       &controlold);
+	if (ret)
+		goto error_mutex_unlock;
+	currentlyset = valold & this_attr->mask ? 1 : 0;
+	if (val == false && currentlyset) {
+		valold &= ~this_attr->mask;
+		changed = 1;
+		ret = iio_remove_event_from_list(this_attr->listel,
+						 &indio_dev->interrupts[0]
+						 ->ev_list);
+		if (ret)
+			goto error_mutex_unlock;
+	} else if (val == true && !currentlyset) {
+		changed = 1;
+		valold |= this_attr->mask;
+		ret = iio_add_event_to_list(this_attr->listel,
+					    &indio_dev->interrupts[0]->ev_list);
+		if (ret)
+			goto error_mutex_unlock;
+	}
+
+	if (changed) {
+		ret = lis3l02dq_spi_write_reg_8(dev,
+						LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+						&valold);
+		if (ret)
+			goto error_mutex_unlock;
+		/* This always enables the interrupt, even if we've remove the
+		 * last thing using it. For this device we can use the reference
+		 * count on the handler to tell us if anyone wants the interrupt
+		 */
+		controlold = this_attr->listel->refcount ?
+			(controlold | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
+			(controlold & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
+		ret = lis3l02dq_spi_write_reg_8(dev,
+						LIS3L02DQ_REG_CTRL_2_ADDR,
+						&controlold);
+		if (ret)
+			goto error_mutex_unlock;
+	}
+error_mutex_unlock:
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret ? ret : len;
+}
+
+
+static int lis3l02dq_thresh_handler_th(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct lis3l02dq_state *st = dev_info->dev_data;
+
+	/* Stash the timestamp somewhere convenient for the bh */
+	st->last_timestamp = timestamp;
+	schedule_work(&st->work_cont_thresh.ws);
+
+	return 0;
+}
+
+
+/* Unforunately it appears the interrupt won't clear unless you read from the
+ * src register.
+ */
+static void lis3l02dq_thresh_handler_bh_no_check(struct work_struct *work_s)
+{
+	struct iio_work_cont *wc
+		= container_of(work_s, struct iio_work_cont, ws_nocheck);
+	struct lis3l02dq_state *st = wc->st;
+
+	s8 t;
+
+	lis3l02dq_spi_read_reg_8(st->indio_dev->dev,
+				 LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+				 (u8 *)&t);
+
+	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_Z_HIGH,
+			       st->last_timestamp);
+
+	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_Z_LOW,
+			       st->last_timestamp);
+
+	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_Y_HIGH,
+			       st->last_timestamp);
+
+	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_Y_LOW,
+			       st->last_timestamp);
+
+	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_X_HIGH,
+			       st->last_timestamp);
+
+	if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_X_LOW,
+			       st->last_timestamp);
+
+	enable_irq(st->us->irq);
+	/* Ack (and allow for new interrupts? (not clear on data sheet) )*/
+	lis3l02dq_spi_read_reg_8(st->indio_dev->dev,
+				 LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
+				 (u8 *)&t);
+
+	return;
+}
+
+/* A shared handler for a number of threshold types */
+IIO_EVENT_SH(threshold, &lis3l02dq_thresh_handler_th);
+
+IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_threshold,
+			       lis3l02dq_read_interrupt_config,
+			       lis3l02dq_write_interrupt_config,
+			       LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH);
+
+IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_threshold,
+			       lis3l02dq_read_interrupt_config,
+			       lis3l02dq_write_interrupt_config,
+			       LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH);
+
+IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_threshold,
+			       lis3l02dq_read_interrupt_config,
+			       lis3l02dq_write_interrupt_config,
+			       LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH);
+
+IIO_EVENT_ATTR_ACCEL_X_LOW_SH(iio_event_threshold,
+			      lis3l02dq_read_interrupt_config,
+			      lis3l02dq_write_interrupt_config,
+			      LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW);
+
+IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(iio_event_threshold,
+			      lis3l02dq_read_interrupt_config,
+			      lis3l02dq_write_interrupt_config,
+			      LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW);
+
+IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(iio_event_threshold,
+			      lis3l02dq_read_interrupt_config,
+			      lis3l02dq_write_interrupt_config,
+			      LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW);
+
+static struct attribute *lis3l02dq_event_attributes[] = {
+	&iio_event_attr_accel_x_high.dev_attr.attr,
+	&iio_event_attr_accel_y_high.dev_attr.attr,
+	&iio_event_attr_accel_z_high.dev_attr.attr,
+	&iio_event_attr_accel_x_low.dev_attr.attr,
+	&iio_event_attr_accel_y_low.dev_attr.attr,
+	&iio_event_attr_accel_z_low.dev_attr.attr,
+	NULL
+};
+
+static struct attribute_group lis3l02dq_event_attribute_group = {
+	.attrs = lis3l02dq_event_attributes,
+};
+
+static IIO_CONST_ATTR(name, "lis3l02dq\n");
+
+static struct attribute *lis3l02dq_attributes[] = {
+	&iio_dev_attr_accel_x_offset.dev_attr.attr,
+	&iio_dev_attr_accel_y_offset.dev_attr.attr,
+	&iio_dev_attr_accel_z_offset.dev_attr.attr,
+	&iio_dev_attr_accel_x_gain.dev_attr.attr,
+	&iio_dev_attr_accel_y_gain.dev_attr.attr,
+	&iio_dev_attr_accel_z_gain.dev_attr.attr,
+	&iio_dev_attr_thresh.dev_attr.attr,
+	&iio_dev_attr_accel_x.dev_attr.attr,
+	&iio_dev_attr_accel_y.dev_attr.attr,
+	&iio_dev_attr_accel_z.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_available_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group lis3l02dq_attribute_group = {
+	.attrs = lis3l02dq_attributes,
+};
+
+static int __devinit lis3l02dq_probe(struct spi_device *spi)
+{
+	struct lis3l02dq_state *st;
+	int ret;
+
+	st = kzalloc(sizeof(struct lis3l02dq_state), GFP_KERNEL);
+	if (st == NULL) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	st->rx = kzalloc(sizeof(*st->rx)*LIS3L02DQ_MAX_RX, GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*LIS3L02DQ_MAX_TX, GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = kzalloc(sizeof(struct iio_dev), GFP_KERNEL);
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev = &spi->dev;
+
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &lis3l02dq_event_attribute_group;
+	st->indio_dev->attrs = &lis3l02dq_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+
+	ret = lis3l02dq_register_ring_funcs(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	/* overly complex */
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+		/* This is a little unusual, in that the device seems
+		   to need a full read of the interrupt source reg before
+		   the interrupt will reset.
+		   Hence the two handlers are the same */
+		iio_init_work_cont(&st->work_cont_thresh,
+				   lis3l02dq_thresh_handler_bh_no_check,
+				   lis3l02dq_thresh_handler_bh_no_check,
+				   LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+				   0,
+				   st);
+		st->inter = 0;
+		ret = iio_register_interrupt_line(spi->irq,
+						  st->indio_dev,
+						  0,
+						  IRQF_TRIGGER_RISING,
+						  "lis3l02dq");
+
+		if (ret)
+			goto error_unregister_dev;
+
+	}
+	ret = lis3l02dq_probe_trigger(st->indio_dev);
+	if (ret)
+		goto error_unregister_line;
+	/* Get the device into a sane initial state */
+	ret = lis3l02dq_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	lis3l02dq_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (st->indio_dev->modes & INDIO_RING_TRIGGERED)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_unregister_dev:
+	iio_device_unregister(st->indio_dev);
+error_unreg_ring_funcs:
+	lis3l02dq_unregister_ring_funcs(st->indio_dev);
+error_free_dev:
+	kfree(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+/* Power down the device */
+static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+	u8 val = 0;
+	mutex_lock(&indio_dev->mlock);
+	ret = lis3l02dq_spi_write_reg_8(&st->us->dev,
+					LIS3L02DQ_REG_CTRL_1_ADDR,
+					&val);
+	if (ret) {
+		dev_err(&st->us->dev, "problem with turning device off: ctrl1");
+		goto err_ret;
+	}
+
+	ret = lis3l02dq_spi_write_reg_8(&st->us->dev,
+					LIS3L02DQ_REG_CTRL_2_ADDR,
+					&val);
+	if (ret)
+		dev_err(&st->us->dev, "problem with turning device off: ctrl2");
+err_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int lis3l02dq_remove(struct spi_device *spi)
+{
+	int ret;
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+
+	ret = lis3l02dq_stop_device(indio_dev);
+	/* Make sure all bottom halfs of interrupts are done */
+	flush_scheduled_work();
+	if (ret)
+		goto err_ret;
+
+	lis3l02dq_remove_trigger(indio_dev);
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(indio_dev, 0);
+	iio_device_unregister(indio_dev);
+
+
+	lis3l02dq_unregister_ring_funcs(indio_dev);
+
+	kfree(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+	return 0;
+
+err_ret:
+	return ret;
+}
+
+static struct spi_driver lis3l02dq_driver = {
+	.driver = {
+		.name = "lis3l02dq",
+		.owner = THIS_MODULE,
+	},
+	.probe = lis3l02dq_probe,
+	.remove = __devexit_p(lis3l02dq_remove),
+};
+
+static __init int lis3l02dq_init(void)
+{
+	return spi_register_driver(&lis3l02dq_driver);
+}
+module_init(lis3l02dq_init);
+
+static __exit void lis3l02dq_exit(void)
+{
+	spi_unregister_driver(&lis3l02dq_driver);
+}
+module_exit(lis3l02dq_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/industrialio/accel.h b/include/linux/industrialio/accel.h
new file mode 100644
index 0000000..39c00b8
--- /dev/null
+++ b/include/linux/industrialio/accel.h
@@ -0,0 +1,157 @@
+
+#include <linux/industrialio/sysfs.h>
+
+/* Accelerometer types of attribute */
+
+/**
+ * IIO_DEV_ATTR_ACCEL_X_OFFSET: accelerometer calibration, x axis offset
+ **/
+#define IIO_DEV_ATTR_ACCEL_X_OFFSET(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(accel_x_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_OFFSET(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(accel_y_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_OFFSET(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(accel_z_offset, _mode, _show, _store, _addr)
+
+/**
+ * IIO_DEV_ATTR_ACCEL_X_GAIN: accelerometer calibration, x axis gain
+ **/
+#define IIO_DEV_ATTR_ACCEL_X_GAIN(_mode, _show, _store, _addr)		\
+	IIO_DEVICE_ATTR(accel_x_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y_GAIN(_mode, _show, _store, _addr)		\
+	IIO_DEVICE_ATTR(accel_y_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z_GAIN(_mode, _show, _store, _addr)		\
+	IIO_DEVICE_ATTR(accel_z_gain, _mode, _show, _store, _addr)
+
+/**
+ * IIO_DEV_ATTR_ACCEL_X: acceleration, x axis
+ **/
+#define IIO_DEV_ATTR_ACCEL_X(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_x, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Y(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_y, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_Z(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_z, S_IRUGO, _show, NULL, _addr)
+
+/* Thresholds are somewhat chip dependent - may need quite a few defs here */
+/* For unified thesholds (shared across all directions */
+
+/**
+ * IIO_DEV_ATTR_ACCEL_THRESH: unified threshold
+ *
+ * This one is for cases where as single threshold covers all directions
+ **/
+#define IIO_DEV_ATTR_ACCEL_THRESH(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh, _mode, _show, _store, _addr)
+
+/**
+ * IIO_DEV_ATTR_ACCEL_THRESH_X: independant direction threshold, x axis
+ **/
+#define IIO_DEV_ATTR_ACCEL_THRESH_X(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh_accel_x, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_Y(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh_accel_y, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_THRESH_Z(_mode, _show, _store, _addr)	\
+	IIO_DEVICE_ATTR(thresh_accel_z, _mode, _show, _store, _addr)
+
+
+/**
+ * IIO_EVENT_ATTR_ACCEL_X_HIGH: threshold event, x acceleration
+ **/
+#define IIO_EVENT_ATTR_ACCEL_X_HIGH(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(accel_x_high, _show, _store, _mask, _handler)
+
+/**
+ * IIO_EVENT_ATTR_ACCEL_X_HIGH_SH: threshold event, x accel high, shared handler
+ **/
+#define IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_x_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_X_HIGH IIO_EVENT_CODE_ACCEL_BASE
+
+#define IIO_EVENT_ATTR_ACCEL_Y_HIGH(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(accel_y_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_y_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_HIGH IIO_EVENT_CODE_ACCEL_BASE + 1
+
+#define IIO_EVENT_ATTR_ACCEL_Z_HIGH(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(accel_z_high, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(accel_z_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_HIGH IIO_EVENT_CODE_ACCEL_BASE + 2
+
+#define IIO_EVENT_ATTR_ACCEL_X_LOW(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(accel_x_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_X_LOW_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(accel_x_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_X_LOW IIO_EVENT_CODE_ACCEL_BASE + 3
+
+#define IIO_EVENT_ATTR_ACCEL_Y_LOW(_show, _store, _mask, _handler) \
+	IIO_EVENT_ATTR(accel_y_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Y_LOW_SH(_evlist, _show, _store, _mask)\
+	IIO_EVENT_ATTR_SH(accel_y_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_LOW IIO_EVENT_CODE_ACCEL_BASE + 4
+
+#define IIO_EVENT_ATTR_ACCEL_Z_LOW(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(accel_z_low, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_ACCEL_Z_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_z_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_LOW IIO_EVENT_CODE_ACCEL_BASE + 5
+
+#define IIO_EVENT_ATTR_FREE_FALL_DETECT(_show, _store, _mask, _handler)	\
+	IIO_EVENT_ATTR(free_fall, _show, _store, _mask, _handler)
+
+#define IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(_evlist, _show, _store, _mask) \
+	IIO_EVENT_ATTR_SH(free_fall, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_FREE_FALL IIO_EVENT_CODE_ACCEL_BASE + 6
+
+
+#define IIO_EVENT_ATTR_ACCEL_X_ROC_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_x_roc_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_X_ROC_HIGH IIO_EVENT_CODE_ACCEL_BASE + 10
+
+#define IIO_EVENT_ATTR_ACCEL_X_ROC_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_x_roc_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_X_ROC_LOW IIO_EVENT_CODE_ACCEL_BASE + 11
+
+#define IIO_EVENT_ATTR_ACCEL_Y_ROC_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_y_roc_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_ROC_HIGH IIO_EVENT_CODE_ACCEL_BASE + 12
+
+#define IIO_EVENT_ATTR_ACCEL_Y_ROC_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_y_roc_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Y_ROC_LOW IIO_EVENT_CODE_ACCEL_BASE + 13
+
+#define IIO_EVENT_ATTR_ACCEL_Z_ROC_HIGH_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_z_roc_high, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_ROC_HIGH IIO_EVENT_CODE_ACCEL_BASE + 14
+
+#define IIO_EVENT_ATTR_ACCEL_Z_ROC_LOW_SH(_evlist, _show, _store, _mask)	\
+	IIO_EVENT_ATTR_SH(accel_z_roc_low, _evlist, _show, _store, _mask)
+
+#define IIO_EVENT_CODE_ACCEL_Z_ROC_LOW IIO_EVENT_CODE_ACCEL_BASE + 15

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

* [Industrial I/O] [10/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (ring)
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (8 preceding siblings ...)
  2008-12-01 14:35 ` [Industrial I/O] [9/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (core) Jonathan Cameron
@ 2008-12-01 14:36 ` Jonathan Cameron
  2008-12-01 14:38 ` [Industrial I/O] [12/13] RFC: IIO Documentation Jonathan Cameron
  2008-12-01 14:39 ` [Industrial I/O] [13/13] RFC: Example user space application Jonathan Cameron
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:36 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

IIO: ST LIS3L02DQ 3d accelerometer driver via SPI (ring)

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--

This patch adds triggered software ring buffer support to the
lis3l02dq driver. It also adds a new trigger from the data
ready signal of the lis3l02dq.  The presence of a data ready
signal is common for these sorts of devices and as one will
often want to trigger other sensors at the same frequency
within multimodal sensing systems, it is useful to have this
as an IIO trigger.

 drivers/industrialio/accelerometer/Makefile        |    4 +-
 drivers/industrialio/accelerometer/lis3l02dq.h     |   20 +-
 .../industrialio/accelerometer/lis3l02dq_ring.c    |  600 ++++++++++++++++++++
 3 files changed, 622 insertions(+), 2 deletions(-)

diff --git a/drivers/industrialio/accelerometer/Makefile b/drivers/industrialio/accelerometer/Makefile
index 22f3d87..3079723 100644
--- a/drivers/industrialio/accelerometer/Makefile
+++ b/drivers/industrialio/accelerometer/Makefile
@@ -3,4 +3,6 @@
 #
 
 lis3l02dq-y := lis3l02dq_core.o
-obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
+#RFC separate config param per device?
+lis3l02dq-$(CONFIG_IIO_RING_BUFFER) += lis3l02dq_ring.o
+obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
\ No newline at end of file
diff --git a/drivers/industrialio/accelerometer/lis3l02dq.h b/drivers/industrialio/accelerometer/lis3l02dq.h
index f53b68b..5ff9bc3 100644
--- a/drivers/industrialio/accelerometer/lis3l02dq.h
+++ b/drivers/industrialio/accelerometer/lis3l02dq.h
@@ -183,6 +183,24 @@ int lis3l02dq_spi_write_reg_8(struct device *dev,
 #define LIS3L02DQ_SCAN_ACC_Y 1
 #define LIS3L02DQ_SCAN_ACC_Z 2
 /* temporary disable */
+#ifdef CONFIG_IIO_RING_BUFFER
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t lis3l02dq_read_accel_from_ring(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf);
+
+
+
+int lis3l02dq_register_ring_funcs(struct iio_dev *indio_dev);
+void lis3l02dq_unregister_ring_funcs(struct iio_dev *indio_dev);
+
+int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length);
+#else
 
 static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev){};
 static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
@@ -210,7 +228,7 @@ int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length)
 	return 0;
 };
 
-
+#endif
 #endif /* SPI_LIS3L02DQ_H_ */
 
 
diff --git a/drivers/industrialio/accelerometer/lis3l02dq_ring.c b/drivers/industrialio/accelerometer/lis3l02dq_ring.c
new file mode 100644
index 0000000..f9b2b50
--- /dev/null
+++ b/drivers/industrialio/accelerometer/lis3l02dq_ring.c
@@ -0,0 +1,600 @@
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/sysfs.h>
+#include <linux/industrialio/ring_sw.h>
+#include <linux/industrialio/accel.h>
+#include <linux/industrialio/trigger.h>
+#include "lis3l02dq.h"
+
+/**
+ * count_to_right() counts number of set bits to right
+ * @full:	variable in which bits are counted
+ * @mask:	the bit mask (single bit set)
+ **/
+static inline u8 count_to_right(u16 full, u16 mask)
+{
+	u8 count = 0;
+	while (mask) {
+		mask >>= 1;
+		if (mask & full)
+			count++;
+	}
+	return count;
+}
+
+/**
+ * combine_8_to_16() utility function to munge to u8s into u16
+ **/
+static inline u16 combine_8_to_16(u8 lower, u8 upper)
+{
+	u16 _lower = lower;
+	u16 _upper = upper;
+	return _lower | (_upper << 8);
+}
+
+/**
+ * lis3l02dq_scan_el_set_state() set whether a scan contains a given channel
+ * @scan_el:	associtate iio scan element attribute
+ * @indio_dev:	the device structure
+ * @bool:	desired state
+ *
+ * mlock already held when this is called.
+ **/
+static int lis3l02dq_scan_el_set_state(struct iio_scan_el *scan_el,
+				       struct iio_dev *indio_dev,
+				       bool state)
+{
+	u8 t, mask;
+	int ret;
+
+	ret = lis3l02dq_spi_read_reg_8(indio_dev->dev,
+				       LIS3L02DQ_REG_CTRL_1_ADDR,
+				       &t);
+	if (ret)
+		goto error_ret;
+	switch (scan_el->label) {
+	case LIS3L02DQ_REG_OUT_X_L_ADDR:
+		mask = LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+		break;
+	case LIS3L02DQ_REG_OUT_Y_L_ADDR:
+		mask = LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+		break;
+	case LIS3L02DQ_REG_OUT_Z_L_ADDR:
+		mask = LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+		break;
+	default:
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
+	if (!(mask & t) == state) {
+		if (state)
+			t |= mask;
+		else
+			t &= ~mask;
+		ret = lis3l02dq_spi_write_reg_8(indio_dev->dev,
+						LIS3L02DQ_REG_CTRL_1_ADDR,
+						&t);
+	}
+error_ret:
+	return ret;
+
+}
+static IIO_SCAN_EL_C(accel_x, LIS3L02DQ_SCAN_ACC_X, IIO_SIGNED(16),
+		     LIS3L02DQ_REG_OUT_X_L_ADDR,
+		     &lis3l02dq_scan_el_set_state);
+static IIO_SCAN_EL_C(accel_y, LIS3L02DQ_SCAN_ACC_Y, IIO_SIGNED(16),
+		     LIS3L02DQ_REG_OUT_Y_L_ADDR,
+		     &lis3l02dq_scan_el_set_state);
+static IIO_SCAN_EL_C(accel_z, LIS3L02DQ_SCAN_ACC_Z, IIO_SIGNED(16),
+		     LIS3L02DQ_REG_OUT_Z_L_ADDR,
+		     &lis3l02dq_scan_el_set_state);
+static IIO_SCAN_EL_TIMESTAMP;
+
+static struct attribute *lis3l02dq_scan_el_attrs[] = {
+	&iio_scan_el_accel_x.dev_attr.attr,
+	&iio_scan_el_accel_y.dev_attr.attr,
+	&iio_scan_el_accel_z.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group lis3l02dq_scan_el_group = {
+	.attrs = lis3l02dq_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * lis3l02dq_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void lis3l02dq_poll_func_th(struct iio_dev *indio_dev)
+{
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+	st->last_timestamp = indio_dev->trig->timestamp;
+	schedule_work(&st->work_trigger_to_ring);
+	/* Indicate that this interrupt is being handled */
+
+	/* Technically this is trigger related, but without this
+	 * handler running there is currently now way for the interrupt
+	 * to clear.
+	 */
+	st->inter = 1;
+}
+
+/**
+ * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int lis3l02dq_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct lis3l02dq_state *st = dev_info->dev_data;
+	struct iio_trigger *trig = st->trig;
+
+	trig->timestamp = timestamp;
+	iio_trigger_poll(trig);
+
+	return IRQ_HANDLED;
+}
+
+/* This is an event as it is a response to a physical interrupt */
+IIO_EVENT_SH(data_rdy_trig, &lis3l02dq_data_rdy_trig_poll);
+
+/**
+ * lis3l02dq_read_accel_from_ring() individual acceleration read from ring
+ **/
+ssize_t lis3l02dq_read_accel_from_ring(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct iio_scan_el *el = NULL;
+	int ret, len = 0, i = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	s16 *data;
+
+	while (dev_info->scan_el_attrs->attrs[i]) {
+		el = to_iio_scan_el((struct device_attribute *)
+				    (dev_info->scan_el_attrs->attrs[i]));
+		/* label is in fact the address */
+		if (el->label == this_attr->address)
+			break;
+		i++;
+	}
+	if (!dev_info->scan_el_attrs->attrs[i]) {
+		ret = -EINVAL;
+		goto error_ret;
+	}
+	/* If this element is in the scan mask */
+	if (el->mask & dev_info->scan_mask) {
+		data = kmalloc(dev_info->ring_access.get_bpd(dev_info->ring),
+			       GFP_KERNEL);
+		if (data == NULL)
+			return -ENOMEM;
+		ret = dev_info->ring_access.read_last(dev_info->ring,
+						      (u8 *)data);
+		if (ret)
+			goto error_free_data;
+	} else {
+		ret = -EINVAL;
+		goto error_ret;
+	}
+
+	len = sprintf(buf, "ring %d\n", data[count_to_right(dev_info->scan_mask,
+							    el->mask)]);
+error_free_data:
+	kfree(data);
+error_ret:
+	return ret ? ret : len;
+
+}
+
+static const u8 read_all_tx_array[] =
+{
+	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0,
+	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0,
+	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0,
+	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDR), 0,
+	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDR), 0,
+	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDR), 0,
+};
+
+/**
+ * lis3l02dq_read_all() Reads all channels currently selected
+ * @st:		device specific state
+ * @rx_array:	(dma capable) recieve array, must be at least
+ * 		4*number of channels
+ **/
+int lis3l02dq_read_all(struct lis3l02dq_state *st, u8 *rx_array)
+{
+	struct spi_transfer *xfers;
+	struct spi_message msg;
+	int ret, i, j = 0;
+
+	xfers = kzalloc((st->indio_dev->scan_count) * 2
+			* sizeof(struct spi_transfer), GFP_KERNEL);
+	if (!xfers)
+		return -ENOMEM;
+
+	mutex_lock(&st->buf_lock);
+
+	for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) {
+		if (st->indio_dev->scan_mask & (1 << i)) {
+			/* lower byte */
+			xfers[j].tx_buf = st->tx + 2*j;
+			st->tx[2*j] = read_all_tx_array[i*4];
+			st->tx[2*j + 1] = 0;
+			if (rx_array)
+				xfers[j].rx_buf = rx_array + j*2;
+			xfers[j].bits_per_word = 8;
+			xfers[j].len = 2;
+			xfers[j].cs_change = 1;
+			j++;
+
+			/* upper byte */
+			xfers[j].tx_buf = st->tx + 2*j;
+			st->tx[2*j] = read_all_tx_array[i*4 + 2];
+			st->tx[2*j + 1] = 0;
+			if (rx_array)
+				xfers[j].rx_buf = rx_array + j*2;
+			xfers[j].bits_per_word = 8;
+			xfers[j].len = 2;
+			xfers[j].cs_change = 1;
+			j++;
+		}
+	}
+	/* After these are transmitted, the rx_buff should have
+	 * values in alternate bytes
+	 */
+	spi_message_init(&msg);
+	for (j = 0; j < st->indio_dev->scan_count * 2; j++)
+		spi_message_add_tail(&xfers[j], &msg);
+	ret = spi_sync(st->us, &msg);
+	mutex_unlock(&st->buf_lock);
+
+	kfree(xfers);
+
+	return ret;
+}
+
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
+ * specific to be rolled into the core.
+ */
+static void lis3l02dq_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct lis3l02dq_state *st
+		= container_of(work_s, struct lis3l02dq_state,
+			       work_trigger_to_ring);
+
+	u8 *rx_array;
+	int i = 0;
+	u16 *data;
+	size_t datasize = st->indio_dev
+		->ring_access.get_bpd(st->indio_dev->ring);
+
+	data = kmalloc(datasize , GFP_KERNEL);
+	if (data == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed in ring bh");
+		return;
+	}
+	/* Due to interleaved nature of transmission this buffer must be
+	 * twice the number of bytes, or 4 times the number of channels
+	 */
+	rx_array = kmalloc(4 * (st->indio_dev->scan_count), GFP_KERNEL);
+	if (rx_array == NULL) {
+		dev_err(&st->us->dev, "memory alloc failed in ring bh");
+		return;
+	}
+
+	/* whilst trigger specific, if this read does nto occur the data
+	   ready interrupt will not be cleared.  Need to add a mechanism
+	   to provide a dummy read function if this is not triggering on
+	   the data ready function but something else is.
+	*/
+	st->inter = 0;
+
+	if (st->indio_dev->scan_count)
+		if (lis3l02dq_read_all(st, rx_array) >= 0)
+			for (; i < st->indio_dev->scan_count; i++)
+				data[i] = combine_8_to_16(rx_array[i*4+1],
+							  rx_array[i*4+3]);
+	/* Guaranteed to be aligned with 8 byte boundary */
+	if (st->indio_dev->scan_timestamp)
+		*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp;
+
+	st->indio_dev->ring_access.store_to(st->indio_dev->ring,
+					    (u8 *)data,
+					    st->last_timestamp);
+
+	iio_trigger_notify_done(st->indio_dev->trig);
+	kfree(rx_array);
+	kfree(data);
+
+	return;
+}
+/* in these circumstances is it better to go with unaligned packing and
+ * deal with the cost?*/
+static int lis3l02dq_data_rdy_ring_preenable(struct iio_dev *indio_dev)
+{
+	size_t size;
+	/* Check if there are any scan elements enabled, if not fail*/
+	if (!(indio_dev->scan_count || indio_dev->scan_timestamp))
+		return -EINVAL;
+
+	if (indio_dev->ring_access.set_bpd) {
+		if (indio_dev->scan_timestamp)
+			if (indio_dev->scan_count) /* Timestamp and data */
+				size = 2*sizeof(s64);
+			else /* Timestamp only  */
+				size = sizeof(s64);
+		else /* Data only */
+			size = indio_dev->scan_count*sizeof(s16);
+		indio_dev->ring_access.set_bpd(indio_dev->ring, size);
+	}
+
+	return 0;
+}
+
+static int lis3l02dq_data_rdy_ring_postenable(struct iio_dev *indio_dev)
+{
+	return indio_dev->trig
+		? iio_trigger_attach_poll_func(indio_dev->trig,
+					       indio_dev->pollfunc)
+		: 0;
+}
+
+static int lis3l02dq_data_rdy_ring_predisable(struct iio_dev *indio_dev)
+{
+	return indio_dev->trig
+		? iio_trigger_dettach_poll_func(indio_dev->trig,
+						indio_dev->pollfunc)
+		: 0;
+}
+
+
+/* Caller responsible for locking as necessary. */
+static int __lis3l02dq_write_data_ready_config(struct device *dev,
+					       struct
+					       iio_event_handler_list *list,
+					       bool state)
+{
+	int ret;
+	u8 valold;
+	bool currentlyset;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+/* Get the current event mask register */
+	ret = lis3l02dq_spi_read_reg_8(dev,
+				       LIS3L02DQ_REG_CTRL_2_ADDR,
+				       &valold);
+	if (ret)
+		goto error_ret;
+/* Find out if data ready is already on */
+	currentlyset
+		= valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+/* Disable requested */
+	if (!state && currentlyset) {
+
+		valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+		/* The double write is to overcome a hardware bug?*/
+		ret = lis3l02dq_spi_write_reg_8(dev,
+						LIS3L02DQ_REG_CTRL_2_ADDR,
+						&valold);
+		if (ret)
+			goto error_ret;
+		ret = lis3l02dq_spi_write_reg_8(dev,
+						LIS3L02DQ_REG_CTRL_2_ADDR,
+						&valold);
+		if (ret)
+			goto error_ret;
+
+		ret = iio_remove_event_from_list(list,
+						 &indio_dev->interrupts[0]
+						 ->ev_list);
+		if (ret)
+			goto error_ret;
+
+/* Enable requested */
+	} else if (state && !currentlyset) {
+		/* if not set, enable requested */
+		valold |= LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+		ret = iio_add_event_to_list(list,
+					    &indio_dev->interrupts[0]->ev_list);
+		if (ret)
+			goto error_ret;
+		ret = lis3l02dq_spi_write_reg_8(dev,
+						LIS3L02DQ_REG_CTRL_2_ADDR,
+						&valold);
+		if (ret)
+			goto error_ret;
+	}
+
+	return 0;
+error_ret:
+	return ret;
+}
+
+/**
+ * lis3l02dq_data_rdy_trigger_set_state() set datardy interrupt state
+ *
+ * If disabling the interrupt also does a final read to ensure it is clear.
+ * This is only important in some cases where the scan enable elements are
+ * switched before the ring is reenabled.
+ **/
+static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct lis3l02dq_state *st = trig->private_data;
+	int ret = 0;
+	u8 t;
+
+	__lis3l02dq_write_data_ready_config(&st->us->dev,
+					    &iio_event_data_rdy_trig,
+					    state);
+	/* Clear any outstanding ready events */
+	if (state == false)
+		ret = lis3l02dq_read_all(st, NULL);
+
+	lis3l02dq_spi_read_reg_8(st->indio_dev->dev,
+				 LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+				 &t);
+	return ret;
+}
+DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *lis3l02dq_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group lis3l02dq_trigger_attr_group = {
+	.attrs = lis3l02dq_trigger_attrs,
+};
+
+/**
+ * lis3l02dq_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ *
+ * As the trigger may occur on any data element being updated it is
+ * really rather likely to occur during the read from the previous
+ * trigger event.  The only way to discover if this has occured on
+ * boards not supporting level interrupts is to take a look at the line.
+ * If it is indicating another interrupt and we don't seem to have a
+ * handler looking at it, then we need to notify the core that we need
+ * to tell the triggering core to try reading all these again.
+ **/
+static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
+{
+	struct lis3l02dq_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	/* If gpio still high (or high again) */
+	if (gpio_get_value(irq_to_gpio(st->us->irq)))
+		if (st->inter == 0) {
+			/* already interrupt handler dealing with it */
+			disable_irq_nosync(st->us->irq);
+			if (st->inter == 1) {
+				/* interrupt handler snuck in between test
+				 * and disable */
+				enable_irq(st->us->irq);
+				return 0;
+			}
+			return -EAGAIN;
+		}
+	/* irq reenabled so success! */
+	return 0;
+}
+
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct lis3l02dq_state *state = indio_dev->dev_data;
+
+	state->trig = kzalloc(sizeof(*state->trig), GFP_KERNEL);
+	if (!state->trig) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	iio_trigger_init(state->trig);
+	state->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL);
+	if (!state->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	snprintf((char *)state->trig->name,
+		 IIO_TRIGGER_NAME_LENGTH,
+		 IIO_TRIGGER_ID_FORMAT, indio_dev->id);
+	state->trig->owner = THIS_MODULE;
+	state->trig->private_data = state;
+	state->trig->set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state;
+	state->trig->try_reenable = &lis3l02dq_trig_try_reen;
+	state->trig->control_attrs = &lis3l02dq_trigger_attr_group;
+	ret = iio_trigger_register(state->trig, indio_dev->dev, 0);
+	if (ret)
+		goto error_free_trig_name;
+	/* Set the default trigger for the device to be it's own data ready */
+	indio_dev->trig = state->trig;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(state->trig->name);
+error_free_trig:
+	kfree(state->trig);
+error_ret:
+	return ret;
+}
+
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct lis3l02dq_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	kfree(state->trig);
+}
+
+int lis3l02dq_register_ring_funcs(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct lis3l02dq_state *st = indio_dev->dev_data;
+
+	INIT_WORK(&st->work_trigger_to_ring, lis3l02dq_trigger_bh_to_ring);
+	/* Set default scan mode */
+	indio_dev->scan_mask = iio_scan_el_accel_x.mask
+		| iio_scan_el_accel_y.mask
+		| iio_scan_el_accel_z.mask;
+	indio_dev->scan_timestamp = true;
+	indio_dev->scan_count = 3;
+	indio_dev->scan_el_attrs = &lis3l02dq_scan_el_group;
+
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&indio_dev->ring_access);
+	indio_dev->ring_preenable = &lis3l02dq_data_rdy_ring_preenable;
+	indio_dev->ring_postenable = &lis3l02dq_data_rdy_ring_postenable;
+	indio_dev->ring_predisable = &lis3l02dq_data_rdy_ring_predisable;
+
+	/* must init this somewhere */
+	INIT_LIST_HEAD(&iio_event_data_rdy_trig.list);
+
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	indio_dev->pollfunc->poll_func_main = &lis3l02dq_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+error_ret:
+	return ret;
+
+}
+
+void lis3l02dq_unregister_ring_funcs(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+}
+
+int lis3l02dq_set_ring_length(struct iio_dev *indio_dev, int length)
+{
+	/* Set sensible defaults for the ring buffer */
+	if (indio_dev->ring_access.set_length)
+		return indio_dev->ring_access.set_length(indio_dev->ring, 500);
+	return 0;
+}
+
+

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

* [Industrial I/O] [11/13] RFC: VTI SCA3000 3d accelerometer driver.
       [not found] ` <4933F291.4020001-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
  2008-12-01 14:33   ` [Industrial I/O] [7/13] RFC: Maxim MAX1363 driver Jonathan Cameron
@ 2008-12-01 14:37   ` Jonathan Cameron
  2008-12-01 19:41   ` [Industrial I/O] [0/13] RFC: IIO v3 patchset Alan Cox
  2 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Dmitry Torokhov, LKML, LM Sensors, David Brownell, Jean Delvare,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Ben Nizette

 
From: Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>

IIO: VTI SCA3000 3d accelerometer driver.

Signed-off-by: Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
--
This patch provides support for the VTI SCA3000 seriese of
accelerometers. These are relatively unusual chips having
internal ring buffers. They provide interrupt signals on
ring buffer events (50% full etc) and so fit well within
the IIO ring buffer architecture.

This patch also introduces the ring_hw.h header which provides
the infrastructure code for hardware ring buffers.

At the current time this driver doesn't support triggers
or software ring buffers.  As it does not supply a data
ready signal it doesn't really make sense to use it as
a trigger, but more complex use of the ring buffer might
include the ability to trigger a copy of current contents
for example on some other event.


 drivers/industrialio/accelerometer/Kconfig        |    8 +
 drivers/industrialio/accelerometer/Makefile       |    6 +-
 drivers/industrialio/accelerometer/sca3000.h      |  266 ++++
 drivers/industrialio/accelerometer/sca3000_core.c | 1522 +++++++++++++++++++++
 drivers/industrialio/accelerometer/sca3000_ring.c |  196 +++
 include/linux/industrialio/ring_hw.h              |    9 +
 include/linux/industrialio/sca3000.h              |   36 +
 7 files changed, 2042 insertions(+), 1 deletions(-)

diff --git a/drivers/industrialio/accelerometer/Kconfig b/drivers/industrialio/accelerometer/Kconfig
index 7e011a6..02bfb53 100644
--- a/drivers/industrialio/accelerometer/Kconfig
+++ b/drivers/industrialio/accelerometer/Kconfig
@@ -19,3 +19,11 @@ config LIS3L02DQ_SPI
 	  Say yes here to build support for the ST LIS3L02DQ accelerometer via
 	  an SPI bus.
 
+config SCA3000
+       depends on INDUSTRIALIO
+       depends on SPI
+       tristate "VTI SCA3000 series accelerometers"
+       help
+         Say yes here to build support for the VTI SCA3000 series of SPI 
+	 accelerometers. These devices use a hardware ring buffer. Direct
+	 access is available unless motion detector mode is enabled.
\ No newline at end of file
diff --git a/drivers/industrialio/accelerometer/Makefile b/drivers/industrialio/accelerometer/Makefile
index 3079723..eabc23e 100644
--- a/drivers/industrialio/accelerometer/Makefile
+++ b/drivers/industrialio/accelerometer/Makefile
@@ -5,4 +5,8 @@
 lis3l02dq-y := lis3l02dq_core.o
 #RFC separate config param per device?
 lis3l02dq-$(CONFIG_IIO_RING_BUFFER) += lis3l02dq_ring.o
-obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
\ No newline at end of file
+obj-$(CONFIG_LIS3L02DQ_SPI)	:= lis3l02dq.o
+
+sca3000-y := sca3000_core.o
+sca3000-$(CONFIG_IIO_RING_BUFFER) += sca3000_ring.o
+obj-$(CONFIG_SCA3000)		+= sca3000.o
diff --git a/drivers/industrialio/accelerometer/sca3000.h b/drivers/industrialio/accelerometer/sca3000.h
new file mode 100644
index 0000000..b05e4aa
--- /dev/null
+++ b/drivers/industrialio/accelerometer/sca3000.h
@@ -0,0 +1,266 @@
+/*
+ * sca3000.c -- support VTI sca3000 series accelerometers
+ *              via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
+ *
+ * Partly based upon tle62x0.c
+ *
+ * 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.
+ *
+ * Initial mode is direct measurement.
+ *
+ * Untested things
+ *
+ * Temperature reading (the e05 I'm testing with doesn't have a sensor)
+ *
+ * Free fall detection mode - supported but untested as I'm not droping my
+ * dubious wire rig far enough to test it.
+ *
+ * Unsupported as yet
+ *
+ * Time stamping of data from ring. Various ideas on how to do this but none
+ * are remotely simple. Suggestions welcome.
+ *
+ * Individual enabling disabling of channels going into ring buffer
+ *
+ * Overflow handling (this is signaled for all but 8 bit ring buffer mode.)
+ *
+ * Motion detector using AND combinations of signals.
+ *
+ * Note: Be very careful about not touching an register bytes marked
+ * as reserved on the data sheet. They really mean it as changing convents of
+ * some will cause the device to lock up.
+ *
+ * Known issues - on rare occasions the interrupts lock up. Not sure why as yet.
+ * Can probably alleviate this by reading the interrupt register on start, but
+ * that is really just brushing the problem under the carpet.
+ */
+#define SCA3000_WRITE_REG(a) ((a) << 2) | 0x02
+#define SCA3000_READ_REG(a) ((a) << 2)
+
+#define SCA3000_REG_ADDR_REVID			0x00
+#define SCA3000_REVID_MAJOR_MASK		0xf0
+#define SCA3000_REVID_MINOR_MASK		0x0f
+
+#define SCA3000_REG_ADDR_STATUS			0x02
+#define SCA3000_LOCKED				0x20
+#define SCA3000_EEPROM_CS_ERROR			0x02
+#define SCA3000_SPI_FRAME_ERROR			0x01
+
+/* All reads done using register decrement so no need to directly access LSBs */
+#define SCA3000_REG_ADDR_X_MSB			0x05
+#define SCA3000_REG_ADDR_Y_MSB			0x07
+#define SCA3000_REG_ADDR_Z_MSB			0x09
+
+#define SCA3000_REG_ADDR_RING_OUT		0x0f
+
+/* Temp read untested - the e05 doesn't have the sensor */
+#define SCA3000_REG_ADDR_TEMP_MSB		0x13
+
+#define SCA3000_REG_ADDR_MODE			0x14
+#define SCA3000_MODE_PROT_MASK			0x28
+
+#define SCA3000_RING_BUF_ENABLE			0x80
+#define SCA3000_RING_BUF_8BIT			0x40
+/* Free fall detection triggers an interrupt if the acceleration
+ * is below a threshold for equivalent of 25cm drop
+ */
+#define SCA3000_FREE_FALL_DETECT		0x10
+#define SCA3000_MEAS_MODE_NORMAL		0x00
+#define SCA3000_MEAS_MODE_OP_1			0x01
+#define SCA3000_MEAS_MODE_OP_2			0x02
+
+/* In motion detection mode the accelerations are band pass filtered
+ * (aprox 1 - 25Hz) and then a programmable theshold used to trigger
+ * and interrupt.
+ */
+#define SCA3000_MEAS_MODE_MOT_DET		0x03
+
+#define SCA3000_REG_ADDR_BUF_COUNT		0x15
+
+#define SCA3000_REG_ADDR_INT_STATUS		0x16
+
+#define SCA3000_INT_STATUS_THREE_QUARTERS	0x80
+#define SCA3000_INT_STATUS_HALF			0x40
+
+#define SCA3000_INT_STATUS_FREE_FALL		0x08
+#define SCA3000_INT_STATUS_Y_TRIGGER		0x04
+#define SCA3000_INT_STATUS_X_TRIGGER		0x02
+#define SCA3000_INT_STATUS_Z_TRIGGER		0x01
+
+/* Used to allow accesss to multiplexed registers */
+#define SCA3000_REG_ADDR_CTRL_SEL		0x18
+/* Only available for SCA3000-D03 and SCA3000-D01 */
+#define SCA3000_REG_CTRL_SEL_I2C_DISABLE	0x01
+#define SCA3000_REG_CTRL_SEL_MD_CTRL		0x02
+#define SCA3000_REG_CTRL_SEL_MD_Y_TH		0x03
+#define SCA3000_REG_CTRL_SEL_MD_X_TH		0x04
+#define SCA3000_REG_CTRL_SEL_MD_Z_TH		0x05
+/* BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device
+   will not function */
+#define SCA3000_REG_CTRL_SEL_OUT_CTRL		0x0B
+#define SCA3000_OUT_CTRL_PROT_MASK		0xE0
+#define SCA3000_OUT_CTRL_BUF_X_EN		0x10
+#define SCA3000_OUT_CTRL_BUF_Y_EN		0x08
+#define SCA3000_OUT_CTRL_BUF_Z_EN		0x04
+#define SCA3000_OUT_CTRL_BUF_DIV_4		0x02
+#define SCA3000_OUT_CTRL_BUF_DIV_2		0x01
+
+/* Control which motion detector interrupts are on.
+ * For now only OR combinations are supported.x
+ */
+#define SCA3000_MD_CTRL_PROT_MASK		0xC0
+#define SCA3000_MD_CTRL_OR_Y			0x01
+#define SCA3000_MD_CTRL_OR_X			0x02
+#define SCA3000_MD_CTRL_OR_Z			0x04
+/* Currently unsupported */
+#define SCA3000_MD_CTRL_AND_Y			0x08
+#define SCA3000_MD_CTRL_AND_X			0x10
+#define SAC3000_MD_CTRL_AND_Z			0x20
+
+/* Some control registers of complex access methods requiring this register to
+ * be used to remove a lock.
+ */
+#define SCA3000_REG_ADDR_UNLOCK			0x1e
+
+#define SCA3000_REG_ADDR_INT_MASK		0x21
+#define SCA3000_INT_MASK_PROT_MASK		0x1C
+
+#define SCA3000_INT_MASK_RING_THREE_QUARTER	0x80
+#define SCA3000_INT_MASK_RING_HALF		0x40
+
+#define SCA3000_INT_MASK_ALL_INTS		0x02
+#define SCA3000_INT_MASK_ACTIVE_HIGH		0x01
+#define SCA3000_INT_MASK_ACTIVE_LOW		0x00
+
+/* Values of mulipexed registers (write to ctrl_data after select) */
+#define SCA3000_REG_ADDR_CTRL_DATA		0x22
+
+/* Measurment modes available on some sca3000 series chips. Code assumes others
+ * may become available in the future.
+ *
+ * Bypass - Bypass the low-pass filter in the signal channel so as to increase
+ *          signal bandwidth.
+ *
+ * Narrow - Narrow low-pass filtering of the signal channel and half output
+ *          data rate by decimation.
+ *
+ * Wide - Widen low-pass filtering of signal channel to increase bandwidth
+ */
+#define SCA3000_OP_MODE_BYPASS			0x01
+#define SCA3000_OP_MODE_NARROW			0x02
+#define SCA3000_OP_MODE_WIDE			0x04
+#define SCA3000_MAX_TX 6
+#define SCA3000_MAX_RX 2
+
+/**
+ * struct sca3000_state - device instance state information
+ * @us: 	 		the associated spi device
+ * @info: 	  		chip variant information
+ * @indio_dev: 	 		device information used by the IIO core
+ * @interrupt_handler_ws: 	event interrupt handler for all events
+ * @last_timestamp: 		the timestamp of the last event
+ * @mo_det_use_count: 		reference counter for the motion detection unit
+ * @lock: 		 	lock used to protect elements of sca3000_state
+ * 	 			and the underlying device state.
+ * @bps: 		 	number of bits per sample
+ * @tx: 		 	dma-able transmit buffer
+ * @rx: 		 	dma-able receive buffer
+ **/
+struct sca3000_state {
+	struct spi_device		*us;
+	const struct sca3000_chip_info	*info;
+	struct iio_dev			*indio_dev;
+	struct work_struct		interrupt_handler_ws;
+	s64				last_timestamp;
+	int				mo_det_use_count;
+	struct mutex			lock;
+	int				bps;
+	u8				*tx;
+	/* not used during a ring buffer read */
+	u8				*rx;
+};
+
+#define sca3000_state_from_dev(dev)					\
+	(((struct iio_dev *)(spi_get_drvdata(to_spi_device(dev))))->dev_data)
+
+/**
+ * struct sca3000_chip_info - model dependant parameters
+ * @name: 			model identification
+ * @temp_output:		some devices have temperature sensors.
+ * @measurement_mode_freq:	normal mode sampling frequency
+ * @option_mode_1:		first optional mode. Not all models have one
+ * @option_mode_1_freq:		option mode 1 sampling frequency
+ * @option_mode_2:		second optional mode. Not all chips have one
+ * @option_mode_2_freq:		option mode 2 sampling frequency
+ *
+ * This structure is used to hold information about the functionality of a given
+ * sca3000 variant.
+ **/
+struct sca3000_chip_info {
+	const char		*name;
+	bool			temp_output;
+	int			measurement_mode_freq;
+	int			option_mode_1;
+	int			option_mode_1_freq;
+	int			option_mode_2;
+	int			option_mode_2_freq;
+};
+
+/**
+ * sca3000_read_data() read a series of values from the device
+ * @dev:		device
+ * @reg_address_high:	start address (decremented read)
+ * @rx:			pointer where recieved data is placed. Callee
+ *			responsible for freeing this.
+ * @len:		number of bytes to read
+ *
+ * The main lock must be held.
+ **/
+int sca3000_read_data(struct device *dev,
+		      u8 reg_address_high,
+		      u8 **rx_p,
+		      int len);
+
+/**
+ * sca3000_write_reg() write a single register
+ * @address:	address of register on chip
+ * @val:	value to be written to register
+ *
+ * The main lock must be held.
+ **/
+int sca3000_write_reg(struct device *dev, u8 address, u8 val);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+/**
+ * sca3000_register_ring_funcs() setup the ring state change functions
+ **/
+void sca3000_register_ring_funcs(struct iio_dev *indio_dev);
+/**
+ * sca3000_register_ring_access_and_init() init and set ring buffer access up
+ *
+ * Principally registers the ring access and control functions with the IIO
+ * core.
+ **/
+int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_ring_int_process() handles ring related event pushing and escalation
+ * @val:	the event code
+ **/
+void sca3000_ring_int_process(u8 val, void *ring);
+
+#else
+static inline void sca3000_register_ring_funcs(struct iio_dev *indio_dev) {};
+
+static inline
+int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev)
+{
+	return 0;
+};
+
+static inline void sca3000_ring_int_process(u8 val, void *ring) {};
+#endif
diff --git a/drivers/industrialio/accelerometer/sca3000_core.c b/drivers/industrialio/accelerometer/sca3000_core.c
new file mode 100644
index 0000000..d6dbb89
--- /dev/null
+++ b/drivers/industrialio/accelerometer/sca3000_core.c
@@ -0,0 +1,1522 @@
+/*
+ * sca3000_core.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * 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.
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
+ *
+ * See industrialio/accels/sca3000.h for comments.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/rtc.h>
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/sysfs.h>
+#include <linux/industrialio/accel.h>
+
+#include <linux/industrialio/sca3000.h>
+#include "sca3000.h"
+
+enum sca3000_variant {
+	d01,
+	d03,
+	e02,
+	e04,
+	e05,
+	l01,
+};
+/* Note where option modes are not defined, the chip simply does not
+ * support any.
+ * Other chips in the sca3000 series use i2c and are not included here.
+ *
+ * Some of these devices are only listed in the family data sheet and
+ * do not actually appear to be available.
+ */
+static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = {
+	{
+		.name = "sca3000-d01",
+		.temp_output = true,
+		.measurement_mode_freq = 250,
+		.option_mode_1 = SCA3000_OP_MODE_BYPASS,
+		.option_mode_1_freq = 250,
+	}, {
+		/* No data sheet available - may be the same as the 3100-d03?*/
+		.name = "sca3000-d03",
+		.temp_output = true,
+	}, {
+		.name = "sca3000-e02",
+		.measurement_mode_freq = 125,
+		.option_mode_1 = SCA3000_OP_MODE_NARROW,
+		.option_mode_1_freq = 63,
+	}, {
+		.name = "sca3000-e04",
+		.measurement_mode_freq = 100,
+		.option_mode_1 = SCA3000_OP_MODE_NARROW,
+		.option_mode_1_freq = 50,
+		.option_mode_2 = SCA3000_OP_MODE_WIDE,
+		.option_mode_2_freq = 400,
+	}, {
+		.name = "sca3000-e05",
+		.measurement_mode_freq = 200,
+		.option_mode_1 = SCA3000_OP_MODE_NARROW,
+		.option_mode_1_freq = 50,
+		.option_mode_2 = SCA3000_OP_MODE_WIDE,
+		.option_mode_2_freq = 400,
+	}, {
+		/* No data sheet available.
+		 * Frequencies are unknown.
+		 */
+		.name = "sca3000-l01",
+		.temp_output = true,
+		.option_mode_1 = SCA3000_OP_MODE_BYPASS,
+	},
+};
+
+
+int sca3000_write_reg(struct device *dev, u8 address, u8 val)
+{
+	struct spi_device *spi_dev = to_spi_device(dev);
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct spi_transfer xfer = {
+		.bits_per_word = 8,
+		.len = 2,
+		.cs_change = 1,
+		.tx_buf = st->tx,
+	};
+	struct spi_message msg;
+
+	st->tx[0] = SCA3000_WRITE_REG(address);
+	st->tx[1] = val;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	return spi_sync(spi_dev, &msg);
+}
+
+
+
+int sca3000_read_data(struct device *dev,
+		      uint8_t reg_address_high,
+		      u8 **rx_p,
+		      int len)
+{
+  	int ret;
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct spi_message msg;
+	struct spi_device *spi_dev = to_spi_device(dev);
+	struct spi_transfer xfer = {
+		.bits_per_word = 8,
+		.len = len + 1,
+		.cs_change = 1,
+		.tx_buf = st->tx,
+	};
+	*rx_p = kmalloc(len + 1, GFP_KERNEL);
+	if (*rx_p == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	xfer.rx_buf = *rx_p;
+	st->tx[0] = SCA3000_READ_REG(reg_address_high);
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(spi_dev, &msg);
+	if (ret) {
+		dev_err(dev, "problem reading register");
+		goto error_free_rx;
+	}
+
+	return 0;
+error_free_rx:
+	kfree(*rx_p);
+error_ret:
+	return ret;
+
+}
+/**
+ * sca3000_reg_lock_on() test if the ctrl register lock is on
+ *
+ * Lock must be held.
+ **/
+static int sca3000_reg_lock_on(struct device *dev)
+{
+	u8 *rx;
+	int ret;
+
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_STATUS, &rx, 1);
+
+	if (ret < 0)
+		return ret;
+	ret = (rx[1] & SCA3000_LOCKED) ? 0 : 1;
+	kfree(rx);
+
+	return ret;
+}
+
+/**
+ * __sca3000_unlock_reg_lock() unlock the control registers
+ *
+ * Note the device does not appear to support doing this in a single transfer.
+ * This should only ever be used as part of ctrl reg read.
+ * Lock must be held before calling this
+ **/
+static int __sca3000_unlock_reg_lock(struct device *dev)
+{
+	struct spi_device *spi_dev = to_spi_device(dev);
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct spi_message msg;
+	struct spi_transfer xfer[3] = {
+		{
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx,
+		}, {
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx + 2,
+		}, {
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx + 4,
+		},
+	};
+	st->tx[0] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+	st->tx[1] = 0x00;
+	st->tx[2] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+	st->tx[3] = 0x50;
+	st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+	st->tx[5] = 0xA0;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer[0], &msg);
+	spi_message_add_tail(&xfer[1], &msg);
+	spi_message_add_tail(&xfer[2], &msg);
+
+	return spi_sync(spi_dev, &msg);
+}
+
+/**
+ * sca3000_write_ctrl_reg() write to a lock protect ctrl register
+ * @sel: selects which registers we wish to write to
+ * @val: the value to be written
+ *
+ * Certain control registers are protected against overwriting by the lock
+ * register and use a shared write address. This function allows writing of
+ * these registers.
+ **/
+static int sca3000_write_ctrl_reg(struct device *dev, uint8_t sel, uint8_t val)
+{
+
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct sca3000_state *st = indio_dev->dev_data;
+	/* Check whether the lock is on and unlock if needed. */
+	mutex_lock(&st->lock);
+	ret = sca3000_reg_lock_on(dev);
+	if (ret < 0)
+		goto error_ret;
+	if (ret) {
+		ret = __sca3000_unlock_reg_lock(dev);
+		if (ret)
+			goto error_ret;
+	}
+
+	/* Set the control select register */
+	ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_CTRL_SEL, sel);
+	if (ret)
+		goto error_ret;
+
+	/* Write the actual value into the register */
+	ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_CTRL_DATA, val);
+
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+/* Crucial that lock is called before calling this */
+/**
+ * sca3000_read_ctrl_reg() read from lock protected control register.
+ *
+ * Lock must be held.
+ **/
+static int sca3000_read_ctrl_reg(struct device *dev,
+				 u8 ctrl_reg,
+				 u8 **rx_p)
+{
+	int ret;
+
+
+	ret = sca3000_reg_lock_on(dev);
+	if (ret < 0)
+		goto error_ret;
+	if (ret) {
+		ret = __sca3000_unlock_reg_lock(dev);
+		if (ret)
+			goto error_ret;
+	}
+	/* Set the control select register */
+	ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_CTRL_SEL, ctrl_reg);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_CTRL_DATA, rx_p, 1);
+
+error_ret:
+	return ret;
+}
+
+/**
+ * sca3000_check_status() check the status register
+ *
+ * Only used for debugging purposes
+ **/
+static int sca3000_check_status(struct device *dev)
+{
+	u8 *rx;
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct sca3000_state *st = indio_dev->dev_data;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_STATUS, &rx, 1);
+	if (ret < 0)
+		goto error_ret;
+	if (rx[1] & SCA3000_EEPROM_CS_ERROR)
+		dev_err(dev, "eeprom error \n");
+	if (rx[1] & SCA3000_SPI_FRAME_ERROR)
+		dev_err(dev, "Previous SPI Frame was corrupt\n");
+	kfree(rx);
+
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+/**
+ * sca3000_read_13bit_signed() sysfs interface to read 13 bit signed registers
+ *
+ * These are described as signed 12 bit on the data sheet, which appears
+ * to be a conventional 2's complement 13 bit.
+ **/
+static ssize_t sca3000_read_13bit_signed(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	int len = 0, ret;
+	int val;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	u8 *rx;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct sca3000_state *st = indio_dev->dev_data;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, this_attr->address, &rx, 2);
+	if (ret < 0)
+		goto error_ret;
+	val = sca3000_13bit_convert(rx[1], rx[2]);
+	len += sprintf(buf + len, "%d\n", val);
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+/**
+ * sca3000_show_reg() sysfs interface to read the chip revision number
+ **/
+static ssize_t sca3000_show_rev(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	int len = 0, ret;
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_REVID, &rx, 1);
+	if (ret < 0)
+		goto error_ret;
+	len += sprintf(buf + len,
+		       "major=%d, minor=%d\n",
+		       rx[1] & SCA3000_REVID_MAJOR_MASK,
+		       rx[1] & SCA3000_REVID_MINOR_MASK);
+	kfree(rx);
+
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+/**
+ * sca3000_show_available_measurement_modes() display available modes
+ *
+ * This is all read from chip specific data in the driver. Not all
+ * of the sca3000 series support modes other than normal.
+ **/
+static ssize_t
+sca3000_show_available_measurement_modes(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int len = 0;
+
+	len += sprintf(buf + len, "0 - normal mode");
+	switch (st->info->option_mode_1) {
+	case SCA3000_OP_MODE_NARROW:
+		len += sprintf(buf + len, ", 1 - narrow mode");
+		break;
+	case SCA3000_OP_MODE_BYPASS:
+		len += sprintf(buf + len, ", 1 - bypass mode");
+		break;
+	};
+	switch (st->info->option_mode_2) {
+	case SCA3000_OP_MODE_WIDE:
+		len += sprintf(buf + len, ", 2 - wide mode");
+		break;
+	}
+	/* always supported */
+	len += sprintf(buf + len, " 3 - motion detection \n");
+
+	return len;
+}
+
+/**
+ * sca3000_show_measurmenet_mode() sysfs read of current mode
+ **/
+static ssize_t
+sca3000_show_measurement_mode(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int len = 0, ret;
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+	/* mask bottom 2 bits - only ones that are relevant */
+	rx[1] &= 0x03;
+	switch (rx[1]) {
+	case SCA3000_MEAS_MODE_NORMAL:
+		len += sprintf(buf + len, "0 - normal mode\n");
+		break;
+	case SCA3000_MEAS_MODE_MOT_DET:
+		len += sprintf(buf + len, "3 - motion detection\n");
+		break;
+	case SCA3000_MEAS_MODE_OP_1:
+		switch (st->info->option_mode_1) {
+		case SCA3000_OP_MODE_NARROW:
+			len += sprintf(buf + len, "1 - narrow mode\n");
+			break;
+		case SCA3000_OP_MODE_BYPASS:
+			len += sprintf(buf + len, "1 - bypass mode\n");
+			break;
+		};
+		break;
+	case SCA3000_MEAS_MODE_OP_2:
+		switch (st->info->option_mode_2) {
+		case SCA3000_OP_MODE_WIDE:
+			len += sprintf(buf + len, "2 - wide mode\n");
+			break;
+		}
+		break;
+	};
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret ? ret : len;
+}
+
+/**
+ * sca3000_store_measurement_mode() set the current mode
+ **/
+static ssize_t
+sca3000_store_measurement_mode(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf,
+			       size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int ret;
+	u8 *rx;
+	int mask = 0x03;
+	long val;
+
+	mutex_lock(&st->lock);
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+	rx[1] &= ~mask;
+	rx[1] |= (val & mask);
+	ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE, rx[1]);
+	if (ret)
+		goto error_free_rx;
+	mutex_unlock(&st->lock);
+
+	return len;
+
+error_free_rx:
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+
+/* Not even vaguely standard attributes so defined here rather than
+ * in the relevant IIO core headers
+ */
+static IIO_DEVICE_ATTR(available_measurement_modes, S_IRUGO,
+		       sca3000_show_available_measurement_modes,
+		       NULL, 0);
+
+static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR,
+		       sca3000_show_measurement_mode,
+		       sca3000_store_measurement_mode,
+		       0);
+
+/* More standard attributes */
+static IIO_DEV_ATTR_REV(sca3000_show_rev);
+
+static IIO_DEV_ATTR_ACCEL_X(sca3000_read_13bit_signed,
+			    SCA3000_REG_ADDR_X_MSB);
+static IIO_DEV_ATTR_ACCEL_Y(sca3000_read_13bit_signed,
+			    SCA3000_REG_ADDR_Y_MSB);
+static IIO_DEV_ATTR_ACCEL_Z(sca3000_read_13bit_signed,
+			    SCA3000_REG_ADDR_Z_MSB);
+
+
+/**
+ * sca3000_read_av_freq() sysfs function to get available frequencies
+ *
+ * The later modes are only relevant to the ring buffer - and depend on current
+ * mode. Note that data sheet gives rather wide tolerances for these so integer
+ * division will give good enough answer and not all chips have them specified
+ * at all.
+ **/
+static ssize_t sca3000_read_av_freq(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int len = 0, ret;
+	u8 *rx;
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	mutex_unlock(&st->lock);
+	if (ret)
+		goto error_ret;
+	rx[1] &= 0x03;
+	switch (rx[1]) {
+	case SCA3000_MEAS_MODE_NORMAL:
+		len += sprintf(buf + len, "%d %d %d\n",
+			       st->info->measurement_mode_freq,
+			       st->info->measurement_mode_freq/2,
+			       st->info->measurement_mode_freq/4);
+		break;
+	case SCA3000_MEAS_MODE_OP_1:
+		len += sprintf(buf + len, "%d %d %d\n",
+			       st->info->option_mode_1_freq,
+			       st->info->option_mode_1_freq/2,
+			       st->info->option_mode_1_freq/4);
+		break;
+	case SCA3000_MEAS_MODE_OP_2:
+		len += sprintf(buf + len, "%d %d %d\n",
+			       st->info->option_mode_2_freq,
+			       st->info->option_mode_2_freq/2,
+			       st->info->option_mode_2_freq/4);
+		break;
+	};
+	kfree(rx);
+	return len;
+error_ret:
+	return ret;
+}
+/**
+ * __sca3000_get_base_frequency() obtain mode specific base frequency
+ *
+ * lock must be held
+ **/
+static inline int __sca3000_get_base_freq(struct device *dev,
+					  const struct sca3000_chip_info *info,
+					  int *base_freq)
+{
+	int ret;
+	u8 *rx;
+
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+	switch (0x03 & rx[1]) {
+	case SCA3000_MEAS_MODE_NORMAL:
+		*base_freq = info->measurement_mode_freq;
+		break;
+	case SCA3000_MEAS_MODE_OP_1:
+		*base_freq = info->option_mode_1_freq;
+		break;
+	case SCA3000_MEAS_MODE_OP_2:
+		*base_freq = info->option_mode_2_freq;
+		break;
+	};
+	kfree(rx);
+error_ret:
+	return ret;
+}
+/**
+ * sca3000_read_frequency() sysfs interface to get the current frequency
+ **/
+static ssize_t sca3000_read_frequency(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int ret, len = 0, base_freq = 0;
+	u8 *rx;
+	mutex_lock(&st->lock);
+	ret = __sca3000_get_base_freq(dev, st->info, &base_freq);
+	if (ret)
+		goto error_ret_mut;
+	ret = sca3000_read_ctrl_reg(dev, SCA3000_REG_CTRL_SEL_OUT_CTRL, &rx);
+	mutex_unlock(&st->lock);
+	if (ret)
+		goto error_ret;
+	if (base_freq > 0)
+		switch (rx[1]&0x03) {
+		case 0x00:
+		case 0x03:
+			len = sprintf(buf, "%d\n", base_freq);
+			break;
+		case 0x01:
+			len = sprintf(buf, "%d\n", base_freq/2);
+			break;
+		case 0x02:
+			len = sprintf(buf, "%d\n", base_freq/4);
+			break;
+	};
+			kfree(rx);
+	return len;
+error_ret_mut:
+	mutex_unlock(&st->lock);
+error_ret:
+	return ret;
+}
+/**
+ * sca3000_set_frequency() sysfs interface to set the current frequency
+ **/
+static ssize_t sca3000_set_frequency(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf,
+			      size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int ret, base_freq = 0;
+	u8 *rx;
+	long val;
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&st->lock);
+	/* What mode are we in? */
+	ret = __sca3000_get_base_freq(dev, st->info, &base_freq);
+	if (ret)
+		goto error_free_lock;
+
+	ret = sca3000_read_ctrl_reg(dev, SCA3000_REG_CTRL_SEL_OUT_CTRL, &rx);
+	if (ret)
+		goto error_free_lock;
+	/* clear the bits */
+	rx[1] &= ~0x03;
+
+	if (val == base_freq/2) {
+		rx[1] |= SCA3000_OUT_CTRL_BUF_DIV_2;
+	} else if (val == base_freq/4) {
+		rx[1] |= SCA3000_OUT_CTRL_BUF_DIV_4;
+	} else if (val != base_freq) {
+		ret = -EINVAL;
+		goto error_free_lock;
+	}
+	ret = sca3000_write_ctrl_reg(dev, SCA3000_REG_CTRL_SEL_OUT_CTRL, rx[1]);
+error_free_lock:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+/* Should only really be registered if ring buffer support is compiled in.
+ * Does no harm however and doing in right would add a fair bit of complexity
+ */
+static IIO_DEV_ATTR_AVAIL_SAMP_FREQ(sca3000_read_av_freq);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+			      sca3000_read_frequency,
+			      sca3000_set_frequency);
+
+
+/**
+ * sca3000_read_temp() sysfs interface to get the temperature when available
+ *
+* The alignment of data in here is downright odd. See data sheet.
+* Converting this into a meaningful value is left to inline functions in
+* userspace part of header.
+**/
+static ssize_t sca3000_read_temp(struct device *dev,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	int len = 0, ret;
+	int val;
+	u8 *rx;
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_TEMP_MSB, &rx, 2);
+	if (ret < 0)
+		goto error_ret;
+	val = ((rx[1]&0x3F) << 3) | ((rx[2] & 0xE0) >> 5);
+	len += sprintf(buf + len, "%d\n", val);
+	kfree(rx);
+
+	return len;
+
+error_ret:
+	return ret;
+}
+static IIO_DEV_ATTR_TEMP(sca3000_read_temp);
+/* FIXME - move to ring funcs */
+static IIO_CONST_ATTR(ring_bps_available, "8, 11\n");
+
+/**
+ * sca3000_show_ring_bps() sysfs function to query bits per sample from ring
+ **/
+static ssize_t sca3000_show_ring_bps(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	int len= 0, ret;
+	u8 *rx;
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+	len = sprintf(buf, "%d\n", (rx[1] & SCA3000_RING_BUF_8BIT) ? 8: 11);
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+/**
+ * sca3000_store_ring_bps() sysfs functio to store bits per sample form ring
+ **/
+static ssize_t sca3000_store_ring_bps(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int ret;
+	u8 *rx;
+	long val;
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&st->lock);
+
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (!ret)
+		switch (val) {
+		case 8:
+			ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+						rx[1] | SCA3000_RING_BUF_8BIT);
+			st->bps = 8;
+			break;
+		case 11:
+			ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+						rx[1] & ~SCA3000_RING_BUF_8BIT);
+			st->bps = 11;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_RING_BPS(S_IRUGO | S_IWUSR,
+			     sca3000_show_ring_bps,
+			     sca3000_store_ring_bps);
+/**
+ * sca3000_show_thresh() sysfs query of a theshold
+ **/
+static ssize_t sca3000_show_thresh(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int len = 0, ret;
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_ctrl_reg(dev,
+				    this_attr->address,
+				    &rx);
+	mutex_unlock(&st->lock);
+	if (ret)
+		return ret;
+	len += sprintf(buf + len, "%d\n", rx[1]);
+	kfree(rx);
+
+	return len;
+}
+
+/**
+ * sca3000_write_thresh() sysfs control of threshold
+ **/
+static ssize_t sca3000_write_thresh(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf,
+				    size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret;
+	long val;
+
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+	mutex_lock(&st->lock);
+	ret = sca3000_write_ctrl_reg(dev, this_attr->address, val);
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+
+static IIO_DEV_ATTR_ACCEL_THRESH_X(S_IRUGO | S_IWUSR,
+				   sca3000_show_thresh,
+				   sca3000_write_thresh,
+				   SCA3000_REG_CTRL_SEL_MD_X_TH);
+static IIO_DEV_ATTR_ACCEL_THRESH_Y(S_IRUGO | S_IWUSR,
+				   sca3000_show_thresh,
+				   sca3000_write_thresh,
+				   SCA3000_REG_CTRL_SEL_MD_Y_TH);
+static IIO_DEV_ATTR_ACCEL_THRESH_Z(S_IRUGO | S_IWUSR,
+				   sca3000_show_thresh,
+				   sca3000_write_thresh,
+				   SCA3000_REG_CTRL_SEL_MD_Z_TH);
+
+/*
+ * fixme, cleaner and readable way of handling these two subtly different tables?
+ * RFC
+ */
+static struct attribute *sca3000_attributes[] = {
+	&iio_dev_attr_revision.dev_attr.attr,
+	&iio_dev_attr_accel_x.dev_attr.attr,
+	&iio_dev_attr_accel_y.dev_attr.attr,
+	&iio_dev_attr_accel_z.dev_attr.attr,
+	&iio_dev_attr_thresh_accel_x.dev_attr.attr,
+	&iio_dev_attr_thresh_accel_y.dev_attr.attr,
+	&iio_dev_attr_thresh_accel_z.dev_attr.attr,
+	&iio_dev_attr_available_measurement_modes.dev_attr.attr,
+	&iio_dev_attr_measurement_mode.dev_attr.attr,
+	&iio_dev_attr_ring_bps.dev_attr.attr,
+	&iio_const_attr_ring_bps_available.dev_attr.attr,
+	&iio_dev_attr_available_sampling_frequency.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *sca3000_attributes_with_temp[] = {
+	&iio_dev_attr_revision.dev_attr.attr,
+	&iio_dev_attr_accel_x.dev_attr.attr,
+	&iio_dev_attr_accel_y.dev_attr.attr,
+	&iio_dev_attr_accel_z.dev_attr.attr,
+	&iio_dev_attr_thresh_accel_x.dev_attr.attr,
+	&iio_dev_attr_thresh_accel_y.dev_attr.attr,
+	&iio_dev_attr_thresh_accel_z.dev_attr.attr,
+	&iio_dev_attr_available_measurement_modes.dev_attr.attr,
+	&iio_dev_attr_measurement_mode.dev_attr.attr,
+	&iio_dev_attr_ring_bps.dev_attr.attr,
+	&iio_const_attr_ring_bps_available.dev_attr.attr,
+	&iio_dev_attr_available_sampling_frequency.dev_attr.attr,
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	/* Only present if temp sensor is */
+	&iio_dev_attr_temp.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group sca3000_attribute_group = {
+	.attrs = sca3000_attributes,
+};
+
+static const struct attribute_group sca3000_attribute_group_with_temp = {
+	.attrs = sca3000_attributes_with_temp,
+};
+
+/* RING RELATED interrupt handler */
+/* depending on event, push to the ring buffer event chrdev or the event one */
+
+/**
+ * sca3000_interrupt_handler_bh() - handling ring and non ring events
+ *
+ * This function is complicated by the fact that the devices can signify ring
+ * and non ring events via the same interrupt line and they can only
+ * be distinguished via a read of the relevant status register.
+ **/
+static void sca3000_interrupt_handler_bh(struct work_struct *work_s)
+{
+	struct sca3000_state *st
+		= container_of(work_s, struct sca3000_state,
+			       interrupt_handler_ws);
+	struct spi_device *spi_dev  = to_spi_device(st->indio_dev->dev);
+	u8 *rx;
+	int ret;
+
+	/* Could lead if badly timed to an extra read of status reg,
+	 * but ensures no interrupt is missed.
+	 */
+	enable_irq(spi_dev->irq);
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(&spi_dev->dev, SCA3000_REG_ADDR_INT_STATUS,
+				&rx, 1);
+	mutex_unlock(&st->lock);
+	if (ret)
+		goto done;
+
+	sca3000_ring_int_process(rx[1], st->indio_dev->ring);
+
+	if (rx[1] & SCA3000_INT_STATUS_FREE_FALL)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_FREE_FALL,
+			       st->last_timestamp);
+
+	if (rx[1] & SCA3000_INT_STATUS_Y_TRIGGER)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_Y_HIGH,
+			       st->last_timestamp);
+
+	if (rx[1] & SCA3000_INT_STATUS_X_TRIGGER)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_X_HIGH,
+			       st->last_timestamp);
+
+	if (rx[1] & SCA3000_INT_STATUS_Z_TRIGGER)
+		iio_push_event(st->indio_dev, 0,
+			       IIO_EVENT_CODE_ACCEL_Z_HIGH,
+			       st->last_timestamp);
+
+done:
+	kfree(rx);
+	return;
+}
+
+/**
+ * sca3000_handler_th() handles all interrupt events from device
+ *
+ * These devices deploy unified interrupt status registers meaning
+ * all interrupts must be handled together
+ **/
+static int sca3000_handler_th(struct iio_dev *dev_info,
+			      int index,
+			      s64 timestamp,
+			      int no_test)
+{
+	struct sca3000_state *st = dev_info->dev_data;
+	st->last_timestamp = timestamp;
+	schedule_work(&st->interrupt_handler_ws);
+	return 0;
+}
+
+/**
+ * sca3000_query_mo_det() is motion detection enabled for this axis
+ *
+ * First queries if motion detection is enabled and then if this axis is
+ * on.
+ **/
+static ssize_t sca3000_query_mo_det(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+	int ret, len = 0;
+	u8 *rx;
+	u8 protect_mask = 0x03;
+
+	/* read current value of mode register */
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+
+	if ((rx[1]&protect_mask) != SCA3000_MEAS_MODE_MOT_DET)
+		len += sprintf(buf + len, "0\n");
+	else {
+		kfree(rx);
+		ret = sca3000_read_ctrl_reg(dev,
+					    SCA3000_REG_CTRL_SEL_MD_CTRL,
+					    &rx);
+		if (ret)
+			goto error_ret;
+		/* only supporting logical or's for now */
+		len += sprintf(buf + len, "%d\n",
+			       (rx[1] & this_attr->mask) ? 1 : 0);
+	}
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+/**
+ * sca3000_query_free_fall_mode() is free fall mode enabled
+ **/
+static ssize_t sca3000_query_free_fall_mode(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	int ret, len;
+	u8 *rx;
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	mutex_unlock(&st->lock);
+	if (ret)
+		return ret;
+	len = sprintf(buf, "%d\n",
+		      !!(rx[1] & SCA3000_FREE_FALL_DETECT));
+	kfree(rx);
+
+	return len;
+}
+/**
+ * sca3000_query_ring_int() is the hardware ring status interrupt enabled
+ **/
+static ssize_t sca3000_query_ring_int(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+	int ret, len;
+	u8 *rx;
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_INT_MASK, &rx, 1);
+	mutex_unlock(&st->lock);
+	if (ret)
+		return ret;
+	len = sprintf(buf, "%d\n", (rx[1] & this_attr->mask) ? 1 : 0);
+	kfree(rx);
+
+	return len;
+}
+/**
+ * sca3000_set_ring_int() set state of ring status interrupt
+ **/
+static ssize_t sca3000_set_ring_int(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+
+	long val;
+	int ret;
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_INT_MASK, &rx, 1);
+	if (ret)
+		goto error_ret;
+	if (val)
+		ret = sca3000_write_reg(dev,
+					SCA3000_REG_ADDR_INT_MASK,
+					rx[1] | this_attr->mask);
+	else
+		ret = sca3000_write_reg(dev,
+					SCA3000_REG_ADDR_INT_MASK,
+					rx[1] & ~this_attr->mask);
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+/**
+ * sca3000_set_free_fall_mode() simple on off control for free fall int
+ *
+ * In these chips the free fall detector should send an interrupt if
+ * the device falls more than 25cm.  This has not been tested due
+ * to fragile wiring.
+ **/
+
+static ssize_t sca3000_set_free_fall_mode(struct device *dev,
+					  struct device_attribute *attr,
+					  const char *buf,
+					  size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	long val;
+	int ret;
+	u8 *rx;
+	u8 protect_mask = SCA3000_FREE_FALL_DETECT;
+
+	mutex_lock(&st->lock);
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		goto error_ret;
+
+	/* read current value of mode register */
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+
+	/*if off and should be on*/
+	if (val && !(rx[1] & protect_mask))
+		ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+					(rx[1] | SCA3000_FREE_FALL_DETECT));
+	/* if on and should be off */
+	else if (!val && (rx[1]&protect_mask))
+		ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+					(rx[1] & ~protect_mask));
+
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+
+}
+
+/**
+ * sca3000_set_mo_det() simple on off control for motion detector
+ *
+ * This is a per axis control, but enabling any will result in the
+ * motion detector unit being enabled.
+ * N.B. enabling motion detector stops normal data acquisition.
+ * There is a complexity in knowing which mode to return to when
+ * this mode is disabled.  Currently normal mode is assumed.
+ **/
+static ssize_t sca3000_set_mo_det(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf,
+				  size_t len)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	struct iio_event_attr *this_attr = to_iio_event_attr(attr);
+	long val;
+	int ret;
+	u8 *rx;
+	u8 protect_mask = 0x03;
+	ret = strict_strtol(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&st->lock);
+	/* First read the motion detector config to find out if
+	 * this axis is on*/
+	ret = sca3000_read_ctrl_reg(dev,
+				    SCA3000_REG_CTRL_SEL_MD_CTRL,
+				    &rx);
+	if (ret)
+		goto exit_point;
+	/* Off and should be on */
+	if (val && !(rx[1] & this_attr->mask)) {
+		ret = sca3000_write_ctrl_reg(dev,
+					     SCA3000_REG_CTRL_SEL_MD_CTRL,
+					     rx[1] | this_attr->mask);
+		if (ret)
+			goto exit_point_free_rx;
+		st->mo_det_use_count++;
+	} else if (!val && (rx[1]&this_attr->mask)) {
+		ret = sca3000_write_ctrl_reg(dev,
+					     SCA3000_REG_CTRL_SEL_MD_CTRL,
+					     rx[1] & ~(this_attr->mask));
+		if (ret)
+			goto exit_point_free_rx;
+		st->mo_det_use_count--;
+	} else /* relies on clean state for device on boot */
+		goto exit_point_free_rx;
+	kfree(rx);
+	/* read current value of mode register */
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto exit_point;
+	/*if off and should be on*/
+	if ((st->mo_det_use_count)
+	    && ((rx[1]&protect_mask) != SCA3000_MEAS_MODE_MOT_DET))
+		ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+					(rx[1] & ~protect_mask)
+					| SCA3000_MEAS_MODE_MOT_DET);
+	/* if on and should be off */
+	else if (!(st->mo_det_use_count)
+		 && ((rx[1]&protect_mask) == SCA3000_MEAS_MODE_MOT_DET))
+		ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_MODE,
+					(rx[1] & ~protect_mask));
+exit_point_free_rx:
+	kfree(rx);
+exit_point:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : len;
+}
+
+/* Shared event handler for all events as single event status register */
+IIO_EVENT_SH(all, &sca3000_handler_th);
+
+/* Free fall detector related event attribute */
+IIO_EVENT_ATTR_FREE_FALL_DETECT_SH(iio_event_all,
+				   sca3000_query_free_fall_mode,
+				   sca3000_set_free_fall_mode,
+				   0)
+
+/* Motion detector related event attributes */
+IIO_EVENT_ATTR_ACCEL_X_HIGH_SH(iio_event_all,
+			       sca3000_query_mo_det,
+			       sca3000_set_mo_det,
+			       SCA3000_MD_CTRL_OR_X);
+
+IIO_EVENT_ATTR_ACCEL_Y_HIGH_SH(iio_event_all,
+			       sca3000_query_mo_det,
+			       sca3000_set_mo_det,
+			       SCA3000_MD_CTRL_OR_Y);
+
+IIO_EVENT_ATTR_ACCEL_Z_HIGH_SH(iio_event_all,
+			       sca3000_query_mo_det,
+			       sca3000_set_mo_det,
+			       SCA3000_MD_CTRL_OR_Z);
+
+/* Hardware ring buffer related event attributes */
+IIO_EVENT_ATTR_RING_50_FULL_SH(iio_event_all,
+			       sca3000_query_ring_int,
+			       sca3000_set_ring_int,
+			       SCA3000_INT_MASK_RING_HALF);
+
+IIO_EVENT_ATTR_RING_75_FULL_SH(iio_event_all,
+			       sca3000_query_ring_int,
+			       sca3000_set_ring_int,
+			       SCA3000_INT_MASK_RING_THREE_QUARTER);
+
+static struct attribute *sca3000_event_attributes[] = {
+	&iio_event_attr_free_fall.dev_attr.attr,
+	&iio_event_attr_accel_x_high.dev_attr.attr,
+	&iio_event_attr_accel_y_high.dev_attr.attr,
+	&iio_event_attr_accel_z_high.dev_attr.attr,
+	&iio_event_attr_ring_50_full.dev_attr.attr,
+	&iio_event_attr_ring_75_full.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group sca3000_event_attribute_group = {
+	.attrs = sca3000_event_attributes,
+};
+
+/**
+ * sca3000_clean_setup() get the device into a predictable state
+ *
+ * Devices use flash memory to store many of the register values
+ * and hence can come up in somewhat unpredictable states.
+ * Hence reset everything on driver load.
+  **/
+static int sca3000_clean_setup(struct device *dev)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int ret;
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	/* Ensure all interrupts have been acknowledged */
+	ret = sca3000_read_data(dev,
+				SCA3000_REG_ADDR_INT_STATUS, &rx, 1);
+	if (ret)
+		goto error_ret;
+	kfree(rx);
+
+	/* Turn off all motion detection channels */
+	ret = sca3000_read_ctrl_reg(dev,
+				    SCA3000_REG_CTRL_SEL_MD_CTRL,
+				    &rx);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_ctrl_reg(dev,
+				     SCA3000_REG_CTRL_SEL_MD_CTRL,
+				     rx[1] & SCA3000_MD_CTRL_PROT_MASK);
+	kfree(rx);
+	if (ret)
+		goto error_ret;
+
+	/* Disable ring buffer */
+	sca3000_read_ctrl_reg(dev,
+			      SCA3000_REG_CTRL_SEL_OUT_CTRL,
+			      &rx);
+	/* Frequency of ring buffer sampling deliberately restricted to make
+	 * debugging easier - add control of this later */
+	ret = sca3000_write_ctrl_reg(dev,
+				     SCA3000_REG_CTRL_SEL_OUT_CTRL,
+				     (rx[1] & SCA3000_OUT_CTRL_PROT_MASK)
+				     | SCA3000_OUT_CTRL_BUF_X_EN
+				     | SCA3000_OUT_CTRL_BUF_Y_EN
+				     | SCA3000_OUT_CTRL_BUF_Z_EN
+				     | SCA3000_OUT_CTRL_BUF_DIV_4);
+	kfree(rx);
+
+	if (ret)
+		goto error_ret;
+	/* Enable interrupts, relevant to mode and set up as active low */
+	ret = sca3000_read_data(dev,
+			  SCA3000_REG_ADDR_INT_MASK,
+			  &rx, 1);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_reg(dev,
+				SCA3000_REG_ADDR_INT_MASK,
+				(rx[1] & SCA3000_INT_MASK_PROT_MASK)
+				| SCA3000_INT_MASK_ACTIVE_LOW);
+	kfree(rx);
+	if (ret)
+		goto error_ret;
+	/* Select normal measurement mode, free fall off, ring off */
+	/* Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5
+	 * as that occurs in one of the example on the datasheet */
+	ret = sca3000_read_data(dev,
+			  SCA3000_REG_ADDR_MODE,
+			  &rx, 1);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_reg(dev,
+				SCA3000_REG_ADDR_MODE,
+				(rx[1] & SCA3000_MODE_PROT_MASK));
+	kfree(rx);
+	st->bps = 11;
+
+error_ret:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
+static int __devinit __sca3000_probe(struct spi_device *spi,
+				     enum sca3000_variant variant)
+{
+	int ret;
+	struct sca3000_state *st;
+
+	st = kzalloc(sizeof(struct sca3000_state), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	st->us = spi;
+	mutex_init(&st->lock);
+	st->info = &sca3000_spi_chip_info_tbl[variant];
+
+	st->indio_dev = kzalloc(sizeof(struct iio_dev), GFP_KERNEL);
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_clear_st;
+	}
+
+	st->indio_dev->dev = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &sca3000_event_attribute_group;
+	if (st->info->temp_output)
+		st->indio_dev->attrs = &sca3000_attribute_group_with_temp;
+	else
+		st->indio_dev->attrs = &sca3000_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	sca3000_register_ring_access_and_init(st->indio_dev);
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret < 0)
+		goto error_free_dev;
+
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0) {
+		INIT_WORK(&st->interrupt_handler_ws,
+			  sca3000_interrupt_handler_bh);
+		ret = iio_register_interrupt_line(spi->irq,
+						  st->indio_dev,
+						  0,
+						  IRQF_TRIGGER_FALLING,
+						  "sca3000");
+		if (ret)
+			goto error_unregister_dev;
+		/* RFC
+		 * Probably a common situation.  All interrupts need an ack
+		 * and there is only one handler so the complicated list system
+		 * is overkill.  At very least a simpler registration method
+		 * might be worthwhile.
+		 */
+		ret = iio_add_event_to_list(iio_event_attr_accel_z_high.listel,
+					    &st->indio_dev
+					    ->interrupts[0]->ev_list);
+		if (ret < 0)
+			goto error_unregister_interrupt_line;
+	}
+	sca3000_register_ring_funcs(st->indio_dev);
+
+	ret = sca3000_clean_setup(&spi->dev);
+	if (ret)
+		goto error_unregister_interrupt_line;
+	return 0;
+
+error_unregister_interrupt_line:
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_unregister_dev:
+	iio_device_unregister(st->indio_dev);
+error_free_dev:
+	kfree(st->indio_dev);
+error_clear_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int sca3000_stop_all_interrupts(struct device *dev)
+{
+	struct sca3000_state *st = sca3000_state_from_dev(dev);
+	int ret;
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(dev, SCA3000_REG_ADDR_INT_MASK, &rx, 1);
+	if (ret)
+		goto error_ret;
+	ret = sca3000_write_reg(dev, SCA3000_REG_ADDR_INT_MASK,
+				(rx[1] & ~(SCA3000_INT_MASK_RING_THREE_QUARTER
+					   | SCA3000_INT_MASK_RING_HALF
+					   | SCA3000_INT_MASK_ALL_INTS)));
+error_ret:
+	kfree(rx);
+	return ret;
+
+}
+
+static int sca3000_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct sca3000_state *st = indio_dev->dev_data;
+	int ret;
+	/* Must ensure no interrupts can be generated after this!*/
+	ret = sca3000_stop_all_interrupts(&spi->dev);
+	if (ret)
+		return ret;
+	if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+	iio_device_unregister(st->indio_dev);
+
+	kfree(st->indio_dev);
+	kfree(st);
+
+	return 0;
+}
+
+/* These macros save on an awful lot of repeated code */
+#define SCA3000_VARIANT_PROBE(_name)				\
+	static int __devinit					\
+	sca3000_##_name##_probe(struct spi_device *spi)		\
+	{							\
+		return __sca3000_probe(spi, _name);		\
+	}
+#define SCA3000_VARIANT_DRIVER(_name)				\
+	SCA3000_VARIANT_PROBE(_name);				\
+	static struct spi_driver sca3000_##_name##_driver = {	\
+		.driver = {					\
+			.name = "sca3000_##_name##",		\
+			.owner = THIS_MODULE,			\
+		},						\
+		.probe = sca3000_##_name##_probe,		\
+		.remove = __devexit_p(sca3000_remove),		\
+	};
+
+SCA3000_VARIANT_DRIVER(d01);
+SCA3000_VARIANT_DRIVER(d03);
+SCA3000_VARIANT_DRIVER(e02);
+SCA3000_VARIANT_DRIVER(e04);
+SCA3000_VARIANT_DRIVER(e05);
+SCA3000_VARIANT_DRIVER(l01);
+
+static __init int sca3000_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&sca3000_d01_driver);
+	if (ret)
+		goto error_ret;
+	ret = spi_register_driver(&sca3000_d03_driver);
+	if (ret)
+		goto error_unreg_d01;
+	ret = spi_register_driver(&sca3000_e02_driver);
+	if (ret)
+		goto error_unreg_d03;
+	ret = spi_register_driver(&sca3000_e04_driver);
+	if (ret)
+		goto error_unreg_e02;
+	ret = spi_register_driver(&sca3000_e05_driver);
+	if (ret)
+		goto error_unreg_e04;
+	ret = spi_register_driver(&sca3000_l01_driver);
+	if (ret)
+		goto error_unreg_e05;
+
+	return 0;
+
+error_unreg_e05:
+	spi_unregister_driver(&sca3000_e05_driver);
+error_unreg_e04:
+	spi_unregister_driver(&sca3000_e04_driver);
+error_unreg_e02:
+	spi_unregister_driver(&sca3000_e02_driver);
+error_unreg_d03:
+	spi_unregister_driver(&sca3000_d03_driver);
+error_unreg_d01:
+	spi_unregister_driver(&sca3000_d01_driver);
+error_ret:
+
+	return ret;
+}
+
+static __exit void sca3000_exit(void)
+{
+	spi_unregister_driver(&sca3000_l01_driver);
+	spi_unregister_driver(&sca3000_e05_driver);
+	spi_unregister_driver(&sca3000_e04_driver);
+	spi_unregister_driver(&sca3000_e02_driver);
+	spi_unregister_driver(&sca3000_d03_driver);
+	spi_unregister_driver(&sca3000_d01_driver);
+}
+
+module_init(sca3000_init);
+module_exit(sca3000_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>");
+MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/industrialio/accelerometer/sca3000_ring.c b/drivers/industrialio/accelerometer/sca3000_ring.c
new file mode 100644
index 0000000..cedeaba
--- /dev/null
+++ b/drivers/industrialio/accelerometer/sca3000_ring.c
@@ -0,0 +1,196 @@
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/rtc.h>
+#include <linux/industrialio/iio.h>
+#include <linux/industrialio/sysfs.h>
+#include <linux/industrialio/accel.h>
+/* Shared kernel / userspace conversion functions */
+#include <linux/industrialio/sca3000.h>
+#include <linux/industrialio/ring_hw.h>
+/* Local stuff such as register locations */
+#include "sca3000.h"
+
+/* RFC / future work
+ *
+ * The internal ring buffer doesn't actually change what it holds depending
+ * on which signals are enabled etc, merely whether you can read them.
+ * As such the scan mode selection is somewhat different than for a software
+ * ring buffer and changing it actually covers any data already in the buffer.
+ * Currently scan elements aren't configured so it doesn't matter.
+ */
+
+/**
+ * sca3000_rip_hw_rb() main ring access function, attempts to pull data from ring
+ * @r:			the ring
+ * @count:		number of samples to try and pull
+ * @data:		output the actual samples pulled from the hw ring
+ * @dead_offset:	cheating a bit here: Set to 1 so as to allow for the
+ *			leading byte used in bus comms.
+ *
+ * Currently does not provide timestamps.  As the hardware doesn't add them they
+ * can only be inferred aproximately from ring buffer events such as 50% full and
+ * knowledge of when buffer was last emptied.  This is left to userspace.
+ **/
+static int sca3000_rip_hw_rb(void *r, size_t count, u8 **data, int *dead_offset)
+{
+	struct iio_hw_ring_buffer *hw_ring = r;
+	struct iio_dev *indio_dev = hw_ring->private;
+	struct sca3000_state *st = indio_dev->dev_data;
+	u8 *rx;
+	int ret, num_available, num_read = 0;
+	int bytes_per_sample = 1;
+
+	if (st->bps == 11)
+		bytes_per_sample = 2;
+
+	mutex_lock(&st->lock);
+	/* Check how much data is available:
+	 * RFC: Implement an ioctl to not bother checking whether there
+	 * is enough data in the ring?  Afterall, if we are responding
+	 * to an interrupt we have a minimum content guaranteed so it
+	 * seems slight silly to waste time checking it is there.
+	 */
+	ret = sca3000_read_data(st->indio_dev->dev,
+				SCA3000_REG_ADDR_BUF_COUNT,
+				&rx, 1);
+	if (ret)
+		goto error_ret;
+	else
+		num_available = rx[1];
+	/* num_available is the total number of samples available
+	 * i.e. number of time points * number of channels.
+	 */
+	kfree(rx);
+	if (count > num_available * bytes_per_sample)
+		num_read = num_available*bytes_per_sample;
+	else
+		num_read = count - (count % (bytes_per_sample));
+
+	*dead_offset = 1;
+	ret = sca3000_read_data(st->indio_dev->dev,
+				SCA3000_REG_ADDR_RING_OUT,
+				data, num_read);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret ? ret : num_read;
+}
+
+/**
+ * sca3000_create_ring_buffer() creates the appropriate ring buffer
+ **/
+static int sca3000_create_ring_buffer(void **r)
+{
+	*r = kzalloc(sizeof(struct iio_hw_ring_buffer),
+		     GFP_KERNEL);
+	if (*r == 0)
+		return -ENOMEM;
+	return 0;
+}
+
+/**
+ * sca3000_init_ring_buffer() minimal amount of init for hw ring buffer
+ **/
+static int sca3000_init_ring_buffer(void *r, void *iio_dev)
+{
+	struct iio_hw_ring_buffer *ring = r;
+
+	ring->private = iio_dev;
+
+	return 0;
+}
+
+static void sca3000_free_ring_buffer(void *r)
+{
+	kfree(r);
+}
+
+/**
+ * sca3000_register_ring_access_and_init() notify iio core of ring funcs
+ *
+ * The hardware ring buffer only needs far fewer ring buffer functions than
+ * a software one as a lot of things are handled automatically.
+ * This function also tells the iio core that our device supports a
+ * hardware ring buffer mode.
+ **/
+int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev)
+{
+	struct iio_ring_access_funcs *ra = &indio_dev->ring_access;
+	indio_dev->modes |= INDIO_RING_HARDWARE_BUFFER;
+	ra->_create = &sca3000_create_ring_buffer;
+	ra->_free = &sca3000_free_ring_buffer;
+	ra->_init = &sca3000_init_ring_buffer;
+
+	ra->rip_lots = &sca3000_rip_hw_rb;
+	return 0;
+}
+
+static inline
+int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
+{
+	struct sca3000_state *st = indio_dev->dev_data;
+	int ret;
+	u8 *rx;
+
+	mutex_lock(&st->lock);
+	ret = sca3000_read_data(indio_dev->dev, SCA3000_REG_ADDR_MODE, &rx, 1);
+	if (ret)
+		goto error_ret;
+	if (state)
+		ret = sca3000_write_reg(indio_dev->dev,
+					SCA3000_REG_ADDR_MODE,
+					(rx[1] | SCA3000_RING_BUF_ENABLE));
+	else
+		ret = sca3000_write_reg(indio_dev->dev,
+					SCA3000_REG_ADDR_MODE,
+					(rx[1] & ~SCA3000_RING_BUF_ENABLE));
+	kfree(rx);
+error_ret:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+/**
+ * sca3000_hw_ring_preenable() hw ring buffer preenable function
+ *
+ * Very simple enable function as the chip will allows normal reads
+ * during ring buffer operation so as long as it is indeed running
+ * before we notify the core, the precise ordering does not matter.
+ **/
+static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev)
+{
+	return __sca3000_hw_ring_state_set(indio_dev, 1);
+}
+
+static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)
+{
+	return __sca3000_hw_ring_state_set(indio_dev, 0);
+}
+
+void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
+{
+	indio_dev->ring_preenable = &sca3000_hw_ring_preenable;
+	indio_dev->ring_postdisable = &sca3000_hw_ring_postdisable;
+}
+
+/**
+ * sca3000_ring_int_process() ring specific interrupt handling.
+ *
+ * This is only split from the main interrupt handler so as to
+ * reduce the amount of code if the ring buffer is not enabled.
+ **/
+void sca3000_ring_int_process(u8 val, void *ring)
+{
+	if (val & SCA3000_INT_STATUS_THREE_QUARTERS)
+		iio_push_or_escallate_ring_event(ring,
+						 IIO_EVENT_CODE_RING_75_FULL, 0);
+	else if (val & SCA3000_INT_STATUS_HALF)
+		iio_push_ring_event(ring,
+				    IIO_EVENT_CODE_RING_50_FULL, 0);
+}
diff --git a/include/linux/industrialio/ring_hw.h b/include/linux/industrialio/ring_hw.h
new file mode 100644
index 0000000..93b0cae
--- /dev/null
+++ b/include/linux/industrialio/ring_hw.h
@@ -0,0 +1,9 @@
+/**
+ * struct iio_hw_ring_buffer- hardware ring buffer
+ * @buf:	generic ring buffer elements
+ * @private:	device specific data
+ */
+struct iio_hw_ring_buffer {
+	struct iio_ring_buffer buf;
+	void *private;
+};
diff --git a/include/linux/industrialio/sca3000.h b/include/linux/industrialio/sca3000.h
new file mode 100644
index 0000000..a07c70d
--- /dev/null
+++ b/include/linux/industrialio/sca3000.h
@@ -0,0 +1,36 @@
+/*
+ * sca3000.h -- conversion functions related to VTI sca3000 series accelerometers
+ *
+ * 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.
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
+ *
+ */
+
+#ifndef _SCA3000_H_USER_
+#define _SCA3000_H_USER_
+
+/* Conversion function for use with the ring buffer when in 11bit mode */
+static inline int sca3000_11bit_convert(uint8_t msb, uint8_t lsb)
+{
+	int16_t val;
+
+	val = ((lsb >> 3) & 0x1C) | (msb << 5);
+	val |= (val & (1 << 12)) ? 0xE000 : 0;
+
+	return val;
+};
+
+static inline int sca3000_13bit_convert(uint8_t msb, uint8_t lsb)
+{
+	s16 val;
+
+	val = ((lsb >> 3) & 0x1F) | (msb << 5);
+	/* sign fill */
+	val |= (val & (1 << 12)) ? 0xE000 : 0;
+
+	return val;
+}
+#endif /* _SCA3000_H_USER_ */

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

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

* [Industrial I/O] [12/13] RFC: IIO Documentation
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (9 preceding siblings ...)
  2008-12-01 14:36 ` [Industrial I/O] [10/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (ring) Jonathan Cameron
@ 2008-12-01 14:38 ` Jonathan Cameron
  2008-12-01 14:39 ` [Industrial I/O] [13/13] RFC: Example user space application Jonathan Cameron
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:38 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

The Industrial I/O Documentation.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
--

This patch provides some documentation for the industrial I/O (IIO)
subsystem. Much of the documentation is as kernel-doc comments
within the code, but an overview is provided here along with
some specific details of ring buffering and the max1363 driver.

 Documentation/industrialio/iio_ring.txt |   90 +++++++++++++++++++++++++++++++
 Documentation/industrialio/max1363.txt  |   37 +++++++++++++
 Documentation/industrialio/overview.txt |   76 ++++++++++++++++++++++++++
 3 files changed, 203 insertions(+), 0 deletions(-)

diff --git a/Documentation/industrialio/iio_ring.txt b/Documentation/industrialio/iio_ring.txt
new file mode 100644
index 0000000..410a552
--- /dev/null
+++ b/Documentation/industrialio/iio_ring.txt
@@ -0,0 +1,90 @@
+Industrialio subsystem ring buffers
+
+The industrial io subsystem supports both hardware (eg. sca3000) and software
+(eg. max1363 and lis3l02dq) ring buffers.
+
+For both types a chrdev is used to provide events to userspace. These merely
+contain an id and a timestamp.
+
+This is used to provide notifications of the ring having reached a certain level
+(50%, 75%, 100%).
+
+Direct access to the contents of the ring buffer is available via a second
+chrdev.  The data output is pretty much raw device readings, so a userspace
+function is needed to convert these into appropriate SI units.  This function
+should be provided in a device specific header if appropriate.
+
+The hardware ring buffer simply implements the above functionality by pulling
+data directly from the device on demand (sca3000).
+
+The software ring buffer is somewhat more complex.
+
+The design considerations for this are:
+
+1) Writing should be as latency free as possible (preferably lock free)
+2) As few samples as possible should be missed (ideally none - but as
+   we aren't dealing with a realtime OS some will occasionally be lost).
+3) Reading does not lock the buffer but instead takes a copy then validate
+   what data is definitely good approach.
+4) The filling of the buffer should be either driver by the device (datardy)
+   or if that is not possible via a periodic time source.
+5) All latencies should be as small as possible (wishful thinking ;)
+
+The code in industrialio-ring.c meets most of these requirements and hopefuly
+does not have any critical failure cases.
+
+A number of pointers into a static array are maintained.
+
+write_p - the next location to write to.
+read_p - the oldest location from which we may read (start point for copying)
+last_written_p - the newest location from which we may read (used to provide
+	       direct access whilst the ring buffer is in use, without adding
+	       to the communications with the sensor.
+
+half_p - Kept half the length of the buffer behind the write pointer and used
+       in conjunction with read_p to trigger an event when the buffer is half
+       full.
+
+The events are designed to escalate until we reach the point of buffer 100%
+full. Thus a single event has it's code changed when it becomes outdated.
+
+The other interesting bit is reading data from the ring. Currently this is via
+normal file reads rather than mmaping the ring buffer.
+
+1) A copy of the ring buffer between read_p and write_p is made.  This is done
+without locking the buffer in anyway so the data is not guaranteed to have not
+been changed by subsequent writes.
+
+2) The value of read_p after the copy is used to provide a worst case location
+for where we have clean data from.  There is an obvious nasty case of the read
+pointer having wrapped all the way round the buffer. For now we assume the
+capture rate is slow enough that this will not have happened.
+
+Only the valid data is then sent to userspace.
+
+What the iio_dev ring parameters are
+
+@ring_bytes_per_datum Number of bytes per 'reading', this includes timestamp
+
+@ring_length Number of readings in the ring
+
+The four functions are concerned with behaviour around the point where the
+device mode actually switches.
+@ring_preenable - usually things like disabling unwanted (sensor side)
+		interrupts.
+
+@ring_postenable - usually actually enabling the data ready generation if
+		 appropriate.
+
+@ring_predisable - usually disabling data ready generation
+@ring_postdisable - restoring anything disable before the the ring came into
+		  use.
+
+@ring_poll_func - For perioidic timer based rings, this function is called on
+		each timer interrupt. Reads from the device and pushes the
+		data into the ring. Also tends to grab a timestamp at the
+		point likely to be as close as possible to when the data
+		was acquired. Sensor specific offsets can compensate for
+		some fixed lags (particularly at low bus speeds).
+
+More information is available in <linux/industrialio/ring_generic.h>
diff --git a/Documentation/industrialio/max1363.txt b/Documentation/industrialio/max1363.txt
new file mode 100644
index 0000000..ad388d9
--- /dev/null
+++ b/Documentation/industrialio/max1363.txt
@@ -0,0 +1,37 @@
+
+Documentation for the max1363 industrialio driver via i2c interface.
+
+This driver has been tested with max1363 and max1238 chips.
+
+Please report any problems / success in using with other chips.
+
+Supported chips
+
+With monitor and smbus alert.
+
+max1361, max1362 - 10 bit versions
+max1363, max1364 - 12 bit versions
+
+Simple
+max1136, max1137 - 4 channel 10 bit versions
+max1138, max1139 - 12 channel 10 bit versions
+
+max1236, max1237 - 4 channel 12 bit versions
+max1238, max1239 - 12 channel 12 bit versions.
+
+
+May add support for the following in the future:
+
+* max103{6-9} - 8 bit versions. Obviously the read code will be somewhat
+simpler for these.
+
+* max1069, max1169 - 14, 16 bit single channel i2c adapters.
+
+Please point out any other similar devices - or even better submit a patch
+adding support.
+
+Probably not similar enough to support via the same driver:
+
+* max127, max128 - multirange 8 channel adc's.
+
+* max1153,max1154,max1253 and max1254 - more complex devices.
diff --git a/Documentation/industrialio/overview.txt b/Documentation/industrialio/overview.txt
new file mode 100644
index 0000000..6dae28d
--- /dev/null
+++ b/Documentation/industrialio/overview.txt
@@ -0,0 +1,76 @@
+Overview of the industrialio subsystem.
+
+Main Components
+
+Currently the industrial I/O subsystem has a core component which is used by
+all drivers and 2 optional components.
+
+The first of these provides the infrastructure used by ring buffer drivers.
+These ring buffers can either exit in software (handled by the kernel) or
+as elements of the hardware to which IIO is interfacing.
+
+The second main component is concerned with IIO triggers. These triggers
+are effectively an interrupt handling interface that allows run time
+configuration of what occurs on a particular interrupt.
+
+Core
+
+industrialio-core.c contains the main registration code for devices using the
+subsytem. The key function is
+
+int iio_device_register(struct iio_dev *dev_info);
+
+This takes a structure containing a number of user specified variables which
+control how the device driver interacts with the subsystem components.
+
+For documentation of this see <linux/industrialio/iio.h> in which all
+relevant parameters are described.
+
+What happens when a iio_dev is registered.
+
+1) Unique id obtained.
+2) Sysfs direct read and configuration elements registered.
+3) Device event (sensor interrupts) registered.
+4) If software ring to be used, setup the ring but don't actually allocate.
+   This occurs on first enabled / when reenabled after parameter change.
+4) If triggered ring then register the fact this is a trigger consumer.
+   Some but not all device drivers will which to specify a default trigger
+   (typcially the devices own data ready signal).
+
+
+Ring Buffer
+
+industrialio-ring.c provides the registration and access architecture
+common to all IIO ring buffer implementations. What actually happens on
+calls to ring buffer functionality is handled via a number of functions
+supplied by individual drivers (see <linux/industrialio/ring_generic.h>
+ring_sw is an example of a software ring buffer using this architecture
+and the sca3000 driver contrains an example of interfacing with a hardware
+ring buffer using much the same framework (an awful lot of which is
+optional)  More detail in iio_ring.txt.
+
+Triggers
+
+industrialio-trigger.c provides the registration and access architecture
+common to IIO triggers.  These triggers act as a means to drive one or more
+device functions (on any number of devices.) Currently they are restricted
+in the drivers to causing data to be sampled (or in case of data ready
+signals aquire that data that we are being notified about) and then pushed
+to a ring buffer. 
+
+Examples of triggers include 
+
+2 non device specific cases:
+
+iio-trig-gpio which allows the use of an external sync signal via the
+generic GPIO infrastructure
+
+iio-trig-prtc which uses available real time clocks that are capable of
+periodic interrupts to provide regular sampling of data.
+
+Many hardware devices that operate on internal clocks produce data ready
+signals and the lis3l02dq driver provides an example of using this signal
+as an IIO trigger.  Note the complexities this causes if other devices
+want to use it by the lis3l02dq itself does not (clearing data ready does
+not occur).
+

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

* [Industrial I/O] [13/13] RFC: Example user space application
  2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
                   ` (10 preceding siblings ...)
  2008-12-01 14:38 ` [Industrial I/O] [12/13] RFC: IIO Documentation Jonathan Cameron
@ 2008-12-01 14:39 ` Jonathan Cameron
  11 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-01 14:39 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

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

IIO: Example user space application. Configures and then reads
from a software ring buffer which is filled based on a the
lis3l02dq data ready trigger and is a software ring buffer.

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>

--
Quite a lot of this program would be simplified if a couple
of generic library routines were available.  It also illustrates
the necessary separation between user space and kernel space
elements of the IIO headers (cannot currently be used in here
so a couple of things have to be redefined).

 Documentation/industrialio/TestRingLIS3L02DQ.c |  340 ++++++++++++++++++++++++
 1 files changed, 340 insertions(+), 0 deletions(-)

diff --git a/Documentation/industrialio/TestRingLIS3L02DQ.c b/Documentation/industrialio/TestRingLIS3L02DQ.c
new file mode 100644
index 0000000..e963d61
--- /dev/null
+++ b/Documentation/industrialio/TestRingLIS3L02DQ.c
@@ -0,0 +1,340 @@
+/* Industrialio test ring buffer for lis3l02dq
+ *
+ * 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.
+ *
+ * There are numerous places where this code could be shortened but that
+ * is work for another day.  The basic principles are what matter at
+ * this stage.
+ */
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+
+/* FIXME: Get this from main headers - Separation into userspace
+ * accessible parts and kernel only parts still needs to be done.
+ */
+struct iio_event_data {
+	int id;
+	__s64 timestamp;
+};
+
+#define IIO_EVENT_CODE_RING_50_FULL 200
+#define IIO_EVENT_CODE_RING_75_FULL 201
+#define IIO_EVENT_CODE_RING_100_FULL 202
+/* Returns the base device directory of a named sensor.
+ * This sort of useful function should go in a generic userspace
+ * library */
+char *find_device_by_name(const char *name)
+{
+	const char *iio_dir = "/sys/class/industrialio";
+	const struct dirent *ent;
+	int cnt, pos, pos2;
+	FILE *nameFile;
+	DIR *dp;
+	char thisname[100];
+	char temp[100];
+
+	char *returnstring = NULL;
+	struct stat Stat;
+	pos = sprintf(temp, "%s", iio_dir);
+	pos += sprintf(temp + pos, "/");
+	dp = opendir(iio_dir);
+	if (dp == NULL) {
+		printf("No industrialio devices available");
+		return NULL;
+	}
+	while (ent = readdir(dp), ent != NULL) {
+		cnt++;
+		/*reject . and .. */
+		if (strcmp(ent->d_name, ".") != 0 &&
+		    strcmp(ent->d_name, "..") != 0)  {
+			/*make sure it isn't a trigger!*/
+			if (strncmp(ent->d_name, "iio_trigger", 10) != 0) {
+				/* build full path to new file */
+				pos2 = pos + sprintf(temp+pos, "%s/",
+						     ent->d_name);
+				sprintf(temp+pos2, "name");
+				printf("search location %s\n", temp);
+				nameFile = fopen(temp, "r");
+				if (!nameFile) {
+					sprintf(temp + pos2, "modalias",
+						ent->d_name);
+					nameFile = fopen(temp, "r");
+					if (!nameFile) {
+						printf("Failed to find name\n");
+						return NULL;
+					}
+				}
+				fscanf(nameFile, "%s", thisname);
+				if (strcmp(name, thisname) == 0) {
+					returnstring = malloc(strlen(temp) + 1);
+					sprintf(temp + pos2, "");
+					strcpy(returnstring, temp);
+					return returnstring;
+				}
+				fclose(nameFile);
+
+			}
+		}
+	}
+}
+
+int RingLength = 100;
+int NumVals = 3;
+
+int main(int argc, char **argv)
+{
+	int toread, sample = 0;
+	uint16_t tempx, tempy, tempz;
+	FILE  *sysfsfp, *fp_ev, *ringcont;
+	int fp;
+	char *data;
+	const char *device_name = "lis3l02dq";
+	size_t read_size;
+	int i, j, k, temps;
+	int minor, minor_ev;
+	char name_chrdevevt[100];
+	char name_chrdevring[100];
+	char command[100];
+	int device_no;
+	char temp[200];
+	int pos;
+	struct iio_event_data dat;
+	int scanx = 1, scany = 1, scanz = 1;
+	int scants = 1;
+
+	int datum_size = 0;
+	char *BaseDirectoryName;
+
+	/* Parameters to disable particular channels */
+	for (i = 1; i < argc; i++) {
+		if (strcmp(argv[i], "-x") == 0)
+			scanx = 0;
+		if (strcmp(argv[i], "-y") == 0)
+			scany = 0;
+		if (strcmp(argv[i], "-z") == 0)
+			scanz = 0;
+		if (strcmp(argv[i], "-ts") == 0)
+			scants = 0;
+	}
+	/* Getting the s64 aligned correctly - this will end up in
+	 * the lis3l02dq userspace header
+	 */
+	if (scants && (scanx || scany || scanz))
+		datum_size = 16;
+	else if (scants)
+		datum_size = 8;
+	else {
+		if (scanx)
+			datum_size += sizeof(__s16);
+		if (scany)
+			datum_size += sizeof(__s16);
+		if (scanz)
+			datum_size += sizeof(__s16);
+	}
+	data = malloc(datum_size*RingLength);
+	BaseDirectoryName = find_device_by_name(device_name);
+
+	if (BaseDirectoryName == NULL) {
+		printf("Failed to find the %s \n", device_name);
+		return -1;
+	}
+
+	/* The next section sets up all the device parameters to
+	 * appropriate values before intializing the ring.
+	 */
+	/* Set a sensible ring buffer length */
+	sprintf(temp, "%sring_buffer/length", BaseDirectoryName);
+	sysfsfp = fopen(temp, "w");
+	if (sysfsfp == NULL) {
+		printf("failed to open the rb length file at %s\n", temp);
+		return -1;
+	}
+	fprintf(sysfsfp, "%d", RingLength);
+	fclose(sysfsfp);
+	/* Create relevant device Nodes */
+	sprintf(temp, "%sring_buf0_acc_minor", BaseDirectoryName);
+	sysfsfp = fopen(temp, "r");
+	if (sysfsfp == NULL) {
+		printf("failed to open minor stuff %s \n", temp);
+		return -1;
+	}
+
+	fscanf(sysfsfp, "%d\n", &minor);
+	sprintf(name_chrdevring, "/dev/indring%d", minor);
+
+	fclose(sysfsfp);
+	sprintf(temp, "%sring_buf0_ev_minor", BaseDirectoryName);
+	sysfsfp = fopen(temp, "r");
+	if (sysfsfp == NULL) {
+		printf("failed to open minor stuff \n");
+		return -1;
+	}
+
+	fscanf(sysfsfp, "%d\n", &minor_ev);
+	fclose(sysfsfp);
+
+	/* set scan of x accel */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_accel_x",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_accel_x\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scanx);
+		fclose(sysfsfp);
+	}
+
+
+	/* set scan of y accel */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_accel_y",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_accel_y\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scany);
+		fclose(sysfsfp);
+	}
+
+
+	/* set scan of z accel */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_accel_z",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_accel_z\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scanz);
+		fclose(sysfsfp);
+	}
+
+
+	/* set scan of ts */
+	{
+		sprintf(temp, "%s/scan_elements/scan_en_timestamp",
+			BaseDirectoryName);
+		sysfsfp = fopen(temp, "w");
+		if (sysfsfp == NULL) {
+			printf("failed to open scan_en_timestamp\n");
+			return -1;
+		}
+		fprintf(sysfsfp, "%d", scants);
+		fclose(sysfsfp);
+	}
+
+	sprintf(name_chrdevevt, "/dev/indringev%d", minor_ev);
+	/* Enable the ring buffer */
+	{
+		sprintf(temp, "%sring_buffer/ring_enable", BaseDirectoryName);
+		ringcont = fopen(temp, "w");
+
+		if (ringcont == NULL) {
+			printf("Failed to open the rb control file \n");
+			return -1;
+		};
+		fprintf(ringcont, "1");
+		fclose(ringcont);
+	}
+	/* Attempt to open non blocking the access dev */
+	fp = open(name_chrdevring, O_RDONLY | O_NONBLOCK);
+	if (fp == -1) { /*If it isn't there make the node */
+		sprintf(command, "mknod %s c 244 %d; mknod %s c 244 %d; ",
+			name_chrdevring, minor, name_chrdevevt, minor_ev);
+		system(command);
+		sleep(5);
+		fp = open(name_chrdevring, O_RDONLY | O_NONBLOCK);
+		if (fp == -1) {
+			printf("Unable to open %s\n", name_chrdevring);
+			return -1;
+		}
+	}
+	/* Attempt to open the event access dev (blocking this time) */
+	fp_ev = fopen(name_chrdevevt, "rb");
+	if (fp_ev == NULL) {
+
+		printf("bug opening %s\n", name_chrdevevt);
+		return -1;
+	}
+
+	/* Wait for events 10 times */
+	for (j = 0; j < 10; j++) {
+		read_size = fread(&dat, 1, sizeof(struct iio_event_data),
+				  fp_ev);
+		switch (dat.id) {
+		case IIO_EVENT_CODE_RING_100_FULL:
+			toread = RingLength;
+			break;
+		case IIO_EVENT_CODE_RING_75_FULL:
+			toread = RingLength*3/4;
+			break;
+		case IIO_EVENT_CODE_RING_50_FULL:
+			toread = RingLength/2;
+			break;
+		default:
+			printf("Unexpecteded event code\n");
+		}
+		read_size = read(fp, (char *)(data), toread*(datum_size));
+		if (read_size == -EAGAIN)
+			printf("nothing available \n");
+		/* Print out the first 10% of the values recieved from the rb*/
+		for (i = 0; i < (read_size  / datum_size)*0.1; i++) {
+			k = 0;
+			printf("%d, ", sample++);
+			if (scanx)
+				printf("%d, ",
+				       *((__s16 *)(data + i*(datum_size))
+					 + k++));
+			if (scany)
+				printf("%d, ",
+				       *((__s16 *)(data + i*(datum_size))
+					 + k++));
+			if (scanz)
+				printf("%d, ",
+				       *((__s16 *)(data + i*(datum_size))
+					 + k++));
+			if (scants)
+				printf(" %lld ",
+				       *((__s64 *)(data + i*(datum_size))
+					 + ((k+3)/4)));
+			printf("\n");
+
+		}
+
+	}
+	close(fp);
+	fclose(fp_ev);
+	/* Stop the ring buffer */
+	{
+
+		sprintf(temp, "%sring_buffer/ring_enable", BaseDirectoryName);
+		ringcont = fopen(temp, "w");
+		if (ringcont == NULL) {
+			printf("Failed to open the rb control file \n");
+			return -1;
+		};
+		fprintf(ringcont, "0");
+
+		fclose(ringcont);
+	}
+
+	free(BaseDirectoryName);
+	free(data);
+
+	return 0;
+}

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

* Re: [Industrial I/O] [0/13] RFC: IIO v3 patchset
       [not found] ` <4933F291.4020001-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
  2008-12-01 14:33   ` [Industrial I/O] [7/13] RFC: Maxim MAX1363 driver Jonathan Cameron
  2008-12-01 14:37   ` [Industrial I/O] [11/13] RFC: VTI SCA3000 3d accelerometer driver Jonathan Cameron
@ 2008-12-01 19:41   ` Alan Cox
  2008-12-02 13:43     ` Jonathan Cameron
  2 siblings, 1 reply; 16+ messages in thread
From: Alan Cox @ 2008-12-01 19:41 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Dmitry Torokhov, LM, LKML, Sensors, David Brownell,
	Jonathan Cameron, Jean Delvare,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Ben Nizette

> have both functions. The sort of devices we are talking typically
> communicate over I2C or SPI buses though drivers for rs232 devices etc are
> definitely on the cards. Basically we are interested in devices where direct
> memory mapped access is not possible.

We have I2C and SPI drivers so I assume you will use the lower layers of
the stacks to do this ?

> For discussion of why these don't fit within existing subsystems see 
> http://lkml.org/lkml/2008/5/20/135 and the rest of the associated thread.

I don't see much there which says why you can't unify all this in a user
space library.

For RS232/423/.. devices you can go this path if you want (but I would
keep it all in userspace anyway) as you can use a line discipline to sit
on top of the port and provide another interface (eg the way PPP does)

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

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

* Re: [Industrial I/O] [0/13] RFC: IIO v3 patchset
  2008-12-01 19:41   ` [Industrial I/O] [0/13] RFC: IIO v3 patchset Alan Cox
@ 2008-12-02 13:43     ` Jonathan Cameron
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Cameron @ 2008-12-02 13:43 UTC (permalink / raw)
  To: Alan Cox
  Cc: LKML, Dmitry Torokhov, David Brownell, Jean Delvare, Ben Nizette,
	LM Sensors, spi-devel-general

Dear Alan,
>> have both functions. The sort of devices we are talking typically
>> communicate over I2C or SPI buses though drivers for rs232 devices etc are
>> definitely on the cards. Basically we are interested in devices where direct
>> memory mapped access is not possible.
> 
> We have I2C and SPI drivers so I assume you will use the lower layers of
> the stacks to do this ?
Yes, in much the same way that for example hwmon is built on top of these
(amongst other interfaces)
> 
>> For discussion of why these don't fit within existing subsystems see 
>> http://lkml.org/lkml/2008/5/20/135 and the rest of the associated thread.
 
> I don't see much there which says why you can't unify all this in a user
> space library.
Indeed, that was an option considered at the time and the majority of that
discussion focused on how to do things in a similar way to existing similar
kernel subsystems.

Perhaps this question needs further clarification.

Much of the stuff provided in this initial patch set could indeed be provided
by a fairly complex user space library.

I get the feeling this rest of this response is going to look pretty familiar.
Though as I have never attempted to code anything other than simple userspace
drivers I suspect others may have more detailed responses.  These are in no
particular order.

I believe the principle argument in favour of user space drivers is that of
enhanced stability as they are unlikely to be able to bring the kernel down.
This advantage is indeed valid here.

The other advantage is that the number of copies required to get data to and
from hardware are reduced.  For the devices considered here a userspace driver
would have to go via one of the chrdev interfaces and neither i2c-dev or
spidev are able to support mmap ed interfaces.

The problems I can forsee (or have encountered) with user space drivers
for this sort of device:
* In many cases the ADCs form part of more complex devices which already
  have a support within the kernel. (e.g. PMICs such as those supported
  by the DA903x mfd driver) and as such any additional interface will
  require at least some kernel parts.  Other places I've seen ADCs that
  I'd like to have available via a consistent interface, include firewire
  and usb cameras (aux ADC's and DAC's are common for controlling pan
  tilt heads etc).  Some of these could be implemented in userspace (and
  quite possibly should be,) but others would require driver changes that
  will effectively equal those of adding direct support for the subsystem
  described in these patches.

* Spidev and i2cdev are fairly restricted interfaces which I have used
  extensively for initial investigation of sensors. The key thing here
  is that almost no devices use purely these interfaces. They include
  interrupt lines etc requiring relatively complex handlers. Thus any
  driver would probably require at least some kernel space element per
  chip anyway. Also worth noting that this tends to be the most complex
  and hence probably error prone part of these drivers.  The spidev
  documentation talks about the limitations of the driver.

  An example of the problems that would be encountered with the current i2c-dev
  implementation within a full driver would be a control register read
  on a VTI SCA3000 (i2c) accelerometer.  This would require an initial 3 byte
  write followed by a 1 byte read.   In kernel space this whole thing
  can be set up in a dma capable buffer and done in one go with copies out
  to userspace only needed if the value is meant to be read by the user.

* Need to provide kernel space interfaces. This actually fulls under the RFC
  question of what the scope of this subsystem should be. For example,
  things like battery chargers, free fall detectors etc frequently use
  generic components of the types that IIO will include. The systems that
  use these sometimes require kernel level access which a userspace driver
  clearly cannot supply.

* Performance: Thinks like time stamping of data ready signals needs to be
  as near as possible to the source interrupt.  As for the complex question
  of which is optimal for the ring buffering functionality, this would need
  thorough optimization of both approaches before it could be addressed.
  I'm not aware of any similar comparisons but would be grateful if anyone
  could direct me to any they know of.  It is also worth noting here that many
  applications that will use the ring buffered interface will never actually
  read the contents of the buffer as they are interested in only restricted
  regions of data (based on some external trigger).

In conclusion, I'd be interested in the long run in the possibility of
moving all the above functionality into userspace but at the moment don't
believe it to be the sensible option here.
The alternative of would be to provide only those drivers that would
benefit from a kernel space driver within this subsystem.
This would lose the advantages to be gained from a consistent interface.
This could in turn be made transparent via a userspace library but only
at the cost of unnecessary complexity.

> 
> For RS232/423/.. devices you can go this path if you want (but I would
> keep it all in userspace anyway) as you can use a line discipline to sit
> on top of the port and provide another interface (eg the way PPP does)

An interesting suggestion. The main reason I'd be interested in bringing
these into the kernel is to do with the handling of additional interrupt
lines (which a number of similar devices have).

Thanks for the comments,

--
Jonathan Cameron


 

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

end of thread, other threads:[~2008-12-02 13:43 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-12-01 14:20 [Industrial I/O] [0/13] RFC: IIO v3 patchset Jonathan Cameron
2008-12-01 14:23 ` [Industrial I/O] [1/13] RFC: IIO core functionality Jonathan Cameron
2008-12-01 14:25 ` [Industrial I/O] [2/13] RFC: IIO ring buffer support (core) Jonathan Cameron
2008-12-01 14:27 ` [Industrial I/O] [3/13] RFC: IIO Software ring buffer implementation Jonathan Cameron
2008-12-01 14:29 ` [Industrial I/O] [4/13] RFC: IIO trigger support Jonathan Cameron
2008-12-01 14:30 ` [Industrial I/O] [5/13] RFC: IIO Periodic real time clock based trigger Jonathan Cameron
2008-12-01 14:32 ` [Industrial I/O] [6/13] RFC: Proof of concept GPIO based IIO trigger Jonathan Cameron
     [not found] ` <4933F291.4020001-KWPb1pKIrIJaa/9Udqfwiw@public.gmane.org>
2008-12-01 14:33   ` [Industrial I/O] [7/13] RFC: Maxim MAX1363 driver Jonathan Cameron
2008-12-01 14:37   ` [Industrial I/O] [11/13] RFC: VTI SCA3000 3d accelerometer driver Jonathan Cameron
2008-12-01 19:41   ` [Industrial I/O] [0/13] RFC: IIO v3 patchset Alan Cox
2008-12-02 13:43     ` Jonathan Cameron
2008-12-01 14:34 ` [Industrial I/O] [0/13] RFC: Maxim MAX1363 driver (ring buffer support) Jonathan Cameron
2008-12-01 14:35 ` [Industrial I/O] [9/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (core) Jonathan Cameron
2008-12-01 14:36 ` [Industrial I/O] [10/13] RFC: ST LIS3L02DQ 3d accelerometer driver via SPI (ring) Jonathan Cameron
2008-12-01 14:38 ` [Industrial I/O] [12/13] RFC: IIO Documentation Jonathan Cameron
2008-12-01 14:39 ` [Industrial I/O] [13/13] RFC: Example user space application Jonathan Cameron

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