All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] staging: iio: new adis16201 driver
@ 2010-10-23 10:49 Mike Frysinger
  2010-10-23 10:49 ` [PATCH 2/3] staging: iio: new adis16203 driver Mike Frysinger
                   ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: Mike Frysinger @ 2010-10-23 10:49 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Barry Song

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

IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/adis16201.h         |  150 ++++++
 drivers/staging/iio/accel/adis16201_core.c    |  640 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16201_ring.c    |  260 ++++++++++
 drivers/staging/iio/accel/adis16201_trigger.c |  122 +++++
 6 files changed, 1185 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16201.h
 create mode 100644 drivers/staging/iio/accel/adis16201_core.c
 create mode 100644 drivers/staging/iio/accel/adis16201_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16201_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index 5926c03..0b87557 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -3,6 +3,15 @@
 #
 comment "Accelerometers"
 
+config ADIS16201
+	tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16201 dual-axis
+	  digital inclinometer and accelerometer.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index ff84703..4a22a01 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -2,6 +2,10 @@
 # Makefile for industrial I/O accelerometer drivers
 #
 
+adis16201-y             := adis16201_core.o
+adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
+obj-$(CONFIG_ADIS16201) += adis16201.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
new file mode 100644
index 0000000..b739ea2
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201.h
@@ -0,0 +1,150 @@
+#ifndef SPI_ADIS16201_H_
+#define SPI_ADIS16201_H_
+
+#define ADIS16201_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16201_READ_REG(a)    a
+#define ADIS16201_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16201_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16201_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16201_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
+#define ADIS16201_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
+#define ADIS16201_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16201_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16201_XINCL_OUT      0x0C /* Output, x-axis inclination */
+#define ADIS16201_YINCL_OUT      0x0E /* Output, y-axis inclination */
+#define ADIS16201_XACCL_OFFS     0x10 /* Calibration, x-axis acceleration offset */
+#define ADIS16201_YACCL_OFFS     0x12 /* Calibration, y-axis acceleration offset */
+#define ADIS16201_XACCL_SCALE    0x14 /* x-axis acceleration scale factor */
+#define ADIS16201_YACCL_SCALE    0x16 /* y-axis acceleration scale factor */
+#define ADIS16201_XINCL_OFFS     0x18 /* Calibration, x-axis inclination offset */
+#define ADIS16201_YINCL_OFFS     0x1A /* Calibration, y-axis inclination offset */
+#define ADIS16201_XINCL_SCALE    0x1C /* x-axis inclination scale factor */
+#define ADIS16201_YINCL_SCALE    0x1E /* y-axis inclination scale factor */
+#define ADIS16201_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16201_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16201_ALM_SMPL1      0x24 /* Alarm 1, sample period */
+#define ADIS16201_ALM_SMPL2      0x26 /* Alarm 2, sample period */
+#define ADIS16201_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16201_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16201_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16201_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16201_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16201_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16201_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16201_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16201_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16201_OUTPUTS        7
+
+/* MSC_CTRL */
+#define ADIS16201_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16201_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16201_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16201_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16201_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16201_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16201_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16201_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16201_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16201_MAX_TX 24
+#define ADIS16201_MAX_RX 24
+
+#define ADIS16201_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16201_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @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 adis16201_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16201_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16201_scan {
+	ADIS16201_SCAN_SUPPLY,
+	ADIS16201_SCAN_ACC_X,
+	ADIS16201_SCAN_ACC_Y,
+	ADIS16201_SCAN_AUX_ADC,
+	ADIS16201_SCAN_TEMP,
+	ADIS16201_SCAN_INCLI_X,
+	ADIS16201_SCAN_INCLI_Y,
+};
+
+void adis16201_remove_trigger(struct iio_dev *indio_dev);
+int adis16201_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16201_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16201_configure_ring(struct iio_dev *indio_dev);
+void adis16201_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16201_initialize_ring(struct iio_ring_buffer *ring);
+void adis16201_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16201_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16201_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16201_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16201_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16201_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16201_H_ */
diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
new file mode 100644
index 0000000..c2f85a5
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_core.c
@@ -0,0 +1,640 @@
+/*
+ * ADIS16201 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "inclinometer.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16201.h"
+
+#define DRIVER_NAME		"adis16201"
+
+static int adis16201_check_status(struct device *dev);
+
+/**
+ * adis16201_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16201_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16201_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16201_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16201_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16201_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16201_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;
+}
+
+/**
+ * adis16201_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16201_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16201_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16201_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16201_ERROR_ACTIVE)
+		adis16201_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16201_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, ADIS16201_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16201_ERROR_ACTIVE)
+		adis16201_check_status(dev);
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16201_read_9bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16201_ERROR_ACTIVE)
+			adis16201_check_status(dev);
+
+		val = ((s16)(val << 7) >> 7);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16201_read_12bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16201_ERROR_ACTIVE)
+			adis16201_check_status(dev);
+
+		val = ((s16)(val << 4) >> 4);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16201_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16201_ERROR_ACTIVE)
+			adis16201_check_status(dev);
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16201_write_16bit(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 = adis16201_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16201_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16201_spi_write_reg_8(dev,
+			ADIS16201_GLOB_CMD,
+			ADIS16201_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16201_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16201_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16201_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16201_spi_read_reg_16(dev, ADIS16201_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16201_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_DIO1;
+	if (enable)
+		msc |= ADIS16201_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16201_spi_write_reg_16(dev, ADIS16201_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16201_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16201_spi_read_reg_16(dev, ADIS16201_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0xF;
+
+	if (status & ADIS16201_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16201_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16201_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16201_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 3.15V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16201_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16201_spi_write_reg_16(dev,
+			ADIS16201_MSC_CTRL,
+			ADIS16201_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16201_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16201_initial_setup(struct adis16201_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16201_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16201_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16201_check_status(dev);
+	if (ret) {
+		adis16201_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16201_STARTUP_DELAY);
+		ret = adis16201_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16201_read_12bit_unsigned,
+		ADIS16201_SUPPLY_OUT);
+static IIO_CONST_ATTR(in_supply_scale, "0.00122");
+static IIO_DEV_ATTR_IN_RAW(0, adis16201_read_12bit_unsigned,
+		ADIS16201_AUX_ADC);
+static IIO_CONST_ATTR(in0_scale, "0.00061");
+
+static IIO_DEV_ATTR_ACCEL_X(adis16201_read_14bit_signed,
+		ADIS16201_XACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16201_read_14bit_signed,
+		ADIS16201_YACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_12bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_XACCL_OFFS);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_12bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_YACCL_OFFS);
+static IIO_CONST_ATTR(accel_scale, "0.4625");
+
+static IIO_DEV_ATTR_INCLI_X(adis16201_read_14bit_signed,
+		ADIS16201_XINCL_OUT);
+static IIO_DEV_ATTR_INCLI_Y(adis16201_read_14bit_signed,
+		ADIS16201_YINCL_OUT);
+static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_9bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_XACCL_OFFS);
+static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16201_read_9bit_signed,
+		adis16201_write_16bit,
+		ADIS16201_YACCL_OFFS);
+static IIO_CONST_ATTR(incli_scale, "0.1");
+
+static IIO_DEV_ATTR_TEMP(adis16201_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16201_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16201");
+
+static struct attribute *adis16201_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16201_event_attribute_group = {
+	.attrs = adis16201_event_attributes,
+};
+
+static struct attribute *adis16201_attributes[] = {
+	&iio_dev_attr_in_supply_raw.dev_attr.attr,
+	&iio_const_attr_in_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_const_attr_in0_scale.dev_attr.attr,
+	&iio_dev_attr_accel_x_raw.dev_attr.attr,
+	&iio_dev_attr_accel_y_raw.dev_attr.attr,
+	&iio_dev_attr_accel_x_offset.dev_attr.attr,
+	&iio_dev_attr_accel_y_offset.dev_attr.attr,
+	&iio_const_attr_accel_scale.dev_attr.attr,
+	&iio_dev_attr_incli_x_raw.dev_attr.attr,
+	&iio_dev_attr_incli_y_raw.dev_attr.attr,
+	&iio_dev_attr_incli_x_offset.dev_attr.attr,
+	&iio_dev_attr_incli_y_offset.dev_attr.attr,
+	&iio_const_attr_incli_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16201_attribute_group = {
+	.attrs = adis16201_attributes,
+};
+
+static int __devinit adis16201_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16201_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16201_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16201_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16201_event_attribute_group;
+	st->indio_dev->attrs = &adis16201_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16201_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16201_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16201");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16201_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16201_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16201_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16201_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16201_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16201_remove(struct spi_device *spi)
+{
+	struct adis16201_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16201_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16201_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16201_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16201_driver = {
+	.driver = {
+		.name = "adis16201",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16201_probe,
+	.remove = __devexit_p(adis16201_remove),
+};
+
+static __init int adis16201_init(void)
+{
+	return spi_register_driver(&adis16201_driver);
+}
+module_init(adis16201_init);
+
+static __exit void adis16201_exit(void)
+{
+	spi_unregister_driver(&adis16201_driver);
+}
+module_exit(adis16201_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c
new file mode 100644
index 0000000..83fee28
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_ring.c
@@ -0,0 +1,260 @@
+#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/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16201.h"
+
+/**
+ * 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);
+}
+
+static IIO_SCAN_EL_C(supply, ADIS16201_SCAN_SUPPLY, IIO_UNSIGNED(12),
+		     ADIS16201_SUPPLY_OUT, NULL);
+static IIO_SCAN_EL_C(accel_x, ADIS16201_SCAN_ACC_X, IIO_SIGNED(14),
+		     ADIS16201_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16201_SCAN_ACC_Y, IIO_SIGNED(14),
+		     ADIS16201_YACCL_OUT, NULL);
+static IIO_SCAN_EL_C(aux_adc, ADIS16201_SCAN_AUX_ADC, IIO_UNSIGNED(12),
+		     ADIS16201_AUX_ADC, NULL);
+static IIO_SCAN_EL_C(temp, ADIS16201_SCAN_TEMP, IIO_UNSIGNED(12),
+		     ADIS16201_TEMP_OUT, NULL);
+static IIO_SCAN_EL_C(incli_x, ADIS16201_SCAN_INCLI_X, IIO_SIGNED(14),
+		     ADIS16201_XINCL_OUT, NULL);
+static IIO_SCAN_EL_C(incli_y, ADIS16201_SCAN_INCLI_Y, IIO_SIGNED(14),
+		     ADIS16201_YINCL_OUT, NULL);
+
+static IIO_SCAN_EL_TIMESTAMP(7);
+
+static struct attribute *adis16201_scan_el_attrs[] = {
+	&iio_scan_el_supply.dev_attr.attr,
+	&iio_scan_el_accel_x.dev_attr.attr,
+	&iio_scan_el_accel_y.dev_attr.attr,
+	&iio_scan_el_aux_adc.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_scan_el_incli_x.dev_attr.attr,
+	&iio_scan_el_incli_y.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16201_scan_el_group = {
+	.attrs = adis16201_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16201_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16201_poll_func_th(struct iio_dev *indio_dev, s64 time)
+{
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = time;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16201_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16201_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16201_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16201_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		st->tx[2 * i] = ADIS16201_READ_REG(ADIS16201_SUPPLY_OUT + 2 * i);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	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 adis16201_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16201_state *st
+		= container_of(work_s, struct adis16201_state,
+			       work_trigger_to_ring);
+
+	int i = 0;
+	s16 *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;
+	}
+
+	if (st->indio_dev->scan_count)
+		if (adis16201_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < st->indio_dev->scan_count; i++) {
+				data[i] = combine_8_to_16(st->rx[i*2+1],
+							  st->rx[i*2]);
+			}
+
+	/* 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(data);
+
+	return;
+}
+
+/* in these circumstances is it better to go with unaligned packing and
+ * deal with the cost?*/
+static int adis16201_data_rdy_ring_preenable(struct iio_dev *indio_dev)
+{
+	size_t size;
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* 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, let timestamp aligned with sizeof(s64) */
+				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
+					+ 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 adis16201_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 adis16201_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;
+}
+
+void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16201_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16201_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16201_trigger_bh_to_ring);
+	/* Set default scan mode */
+
+	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number);
+	indio_dev->scan_timestamp = true;
+
+	indio_dev->scan_el_attrs = &adis16201_scan_el_group;
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->preenable = &adis16201_data_rdy_ring_preenable;
+	ring->postenable = &adis16201_data_rdy_ring_postenable;
+	ring->predisable = &adis16201_data_rdy_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_iio_sw_rb_free;;
+	}
+	indio_dev->pollfunc->poll_func_main = &adis16201_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16201_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16201_trigger.c b/drivers/staging/iio/accel/adis16201_trigger.c
new file mode 100644
index 0000000..8a9cea19
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_trigger.c
@@ -0,0 +1,122 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16201.h"
+
+/**
+ * adis16201_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16201_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16201_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16201_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16201_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16201_trigger_attr_group = {
+	.attrs = adis16201_trigger_attrs,
+};
+
+/**
+ * adis16201_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16201_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16201_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16201_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16201_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16201_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16201_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16201_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16201_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"adis16201-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16201_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16201_trig_try_reen;
+	st->trig->control_attrs = &adis16201_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16201_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16201_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

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

* [PATCH 2/3] staging: iio: new adis16203 driver
  2010-10-23 10:49 [PATCH 1/3] staging: iio: new adis16201 driver Mike Frysinger
@ 2010-10-23 10:49 ` Mike Frysinger
  2010-10-23 17:22   ` Jonathan Cameron
  2010-10-23 10:49 ` [PATCH 3/3] staging: iio: new adis16204 driver Mike Frysinger
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 17+ messages in thread
From: Mike Frysinger @ 2010-10-23 10:49 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Barry Song

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

IIO driver for Programmable 360 Degrees Inclinometer adis16203 parts.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/adis16203.h         |  143 +++++++
 drivers/staging/iio/accel/adis16203_core.c    |  568 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16203_ring.c    |  255 +++++++++++
 drivers/staging/iio/accel/adis16203_trigger.c |  122 ++++++
 6 files changed, 1101 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16203.h
 create mode 100644 drivers/staging/iio/accel/adis16203_core.c
 create mode 100644 drivers/staging/iio/accel/adis16203_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16203_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index 0b87557..cc057c6 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -12,6 +12,15 @@ config ADIS16201
 	  Say yes here to build support for Analog Devices adis16201 dual-axis
 	  digital inclinometer and accelerometer.
 
+config ADIS16203
+	tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16203 Programmable
+	  360 Degrees Inclinometer.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index 4a22a01..76cc92f 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -6,6 +6,10 @@ adis16201-y             := adis16201_core.o
 adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
 obj-$(CONFIG_ADIS16201) += adis16201.o
 
+adis16203-y             := adis16203_core.o
+adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
+obj-$(CONFIG_ADIS16203) += adis16203.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h
new file mode 100644
index 0000000..f0a1d01
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203.h
@@ -0,0 +1,143 @@
+#ifndef SPI_ADIS16203_H_
+#define SPI_ADIS16203_H_
+
+#define ADIS16203_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16203_READ_REG(a)    a
+#define ADIS16203_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16203_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16203_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16203_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16203_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16203_XINCL_OUT      0x0C /* Output, x-axis inclination */
+#define ADIS16203_YINCL_OUT      0x0E /* Output, y-axis inclination */
+#define ADIS16203_INCL_NULL      0x18 /* Incline null calibration */
+#define ADIS16203_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16203_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16203_ALM_SMPL1      0x24 /* Alarm 1, sample period */
+#define ADIS16203_ALM_SMPL2      0x26 /* Alarm 2, sample period */
+#define ADIS16203_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16203_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16203_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16203_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16203_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16203_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16203_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16203_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16203_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16203_OUTPUTS        5
+
+/* MSC_CTRL */
+#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN	(1 << 9)  /* Reverses rotation of both inclination outputs */
+#define ADIS16203_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16203_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16203_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16203_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag */
+#define ADIS16203_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16203_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16203_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16203_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16203_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16203_GLOB_CMD_CLEAR_STAT	(1<<4)
+#define ADIS16203_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16203_MAX_TX 24
+#define ADIS16203_MAX_RX 24
+
+#define ADIS16203_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16203_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @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 adis16203_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16203_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16203_scan {
+	ADIS16203_SCAN_SUPPLY,
+	ADIS16203_SCAN_AUX_ADC,
+	ADIS16203_SCAN_TEMP,
+	ADIS16203_SCAN_INCLI_X,
+	ADIS16203_SCAN_INCLI_Y,
+};
+
+void adis16203_remove_trigger(struct iio_dev *indio_dev);
+int adis16203_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16203_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16203_configure_ring(struct iio_dev *indio_dev);
+void adis16203_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16203_initialize_ring(struct iio_ring_buffer *ring);
+void adis16203_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16203_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16203_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16203_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16203_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16203_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16203_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16203_H_ */
diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
new file mode 100644
index 0000000..81eeb05
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_core.c
@@ -0,0 +1,568 @@
+/*
+ * ADIS16203 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "inclinometer.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16203.h"
+
+#define DRIVER_NAME		"adis16203"
+
+static int adis16203_check_status(struct device *dev);
+
+/**
+ * adis16203_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16203_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16203_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16203_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16203_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16203_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16203_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;
+}
+
+/**
+ * adis16203_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16203_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16203_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16203_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16203_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16203_ERROR_ACTIVE)
+		adis16203_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16203_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16203_spi_read_reg_16(dev, ADIS16203_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16203_ERROR_ACTIVE)
+		adis16203_check_status(dev);
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16203_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16203_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16203_ERROR_ACTIVE)
+			adis16203_check_status(dev);
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16203_write_16bit(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 = adis16203_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16203_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16203_spi_write_reg_8(dev,
+			ADIS16203_GLOB_CMD,
+			ADIS16203_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16203_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16203_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16203_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16203_spi_read_reg_16(dev, ADIS16203_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16203_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_DIO1;
+	if (enable)
+		msc |= ADIS16203_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16203_spi_write_reg_16(dev, ADIS16203_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16203_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16203_spi_read_reg_16(dev, ADIS16203_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0x1F;
+
+	if (status & ADIS16203_DIAG_STAT_SELFTEST_FAIL)
+		dev_err(dev, "Self test failure\n");
+	if (status & ADIS16203_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16203_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16203_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16203_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 3.15V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16203_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16203_spi_write_reg_16(dev,
+			ADIS16203_MSC_CTRL,
+			ADIS16203_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16203_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16203_initial_setup(struct adis16203_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16203_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16203_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16203_check_status(dev);
+	if (ret) {
+		adis16203_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16203_STARTUP_DELAY);
+		ret = adis16203_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16203_read_12bit_unsigned,
+		ADIS16203_SUPPLY_OUT);
+static IIO_CONST_ATTR(in_supply_scale, "0.00122");
+static IIO_DEV_ATTR_IN_RAW(0, adis16203_read_12bit_unsigned,
+		ADIS16203_AUX_ADC);
+static IIO_CONST_ATTR(in0_scale, "0.00061");
+
+static IIO_DEV_ATTR_INCLI_X(adis16203_read_14bit_signed,
+		ADIS16203_XINCL_OUT);
+static IIO_DEV_ATTR_INCLI_Y(adis16203_read_14bit_signed,
+		ADIS16203_YINCL_OUT);
+static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16203_read_14bit_signed,
+		adis16203_write_16bit,
+		ADIS16203_INCL_NULL);
+static IIO_CONST_ATTR(incli_scale, "0.025");
+
+static IIO_DEV_ATTR_TEMP(adis16203_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16203_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16203");
+
+static struct attribute *adis16203_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16203_event_attribute_group = {
+	.attrs = adis16203_event_attributes,
+};
+
+static struct attribute *adis16203_attributes[] = {
+	&iio_dev_attr_in_supply_raw.dev_attr.attr,
+	&iio_const_attr_in_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_const_attr_in0_scale.dev_attr.attr,
+	&iio_dev_attr_incli_x_raw.dev_attr.attr,
+	&iio_dev_attr_incli_y_raw.dev_attr.attr,
+	&iio_dev_attr_incli_x_offset.dev_attr.attr,
+	&iio_const_attr_incli_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16203_attribute_group = {
+	.attrs = adis16203_attributes,
+};
+
+static int __devinit adis16203_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16203_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16203_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16203_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16203_event_attribute_group;
+	st->indio_dev->attrs = &adis16203_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16203_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16203_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16203");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16203_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16203_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16203_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16203_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16203_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16203_remove(struct spi_device *spi)
+{
+	struct adis16203_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16203_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16203_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16203_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16203_driver = {
+	.driver = {
+		.name = "adis16203",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16203_probe,
+	.remove = __devexit_p(adis16203_remove),
+};
+
+static __init int adis16203_init(void)
+{
+	return spi_register_driver(&adis16203_driver);
+}
+module_init(adis16203_init);
+
+static __exit void adis16203_exit(void)
+{
+	spi_unregister_driver(&adis16203_driver);
+}
+module_exit(adis16203_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c
new file mode 100644
index 0000000..77166a0
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_ring.c
@@ -0,0 +1,255 @@
+#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/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16203.h"
+
+/**
+ * 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);
+}
+
+static IIO_SCAN_EL_C(supply, ADIS16203_SCAN_SUPPLY, IIO_UNSIGNED(12),
+		     ADIS16203_SUPPLY_OUT, NULL);
+static IIO_SCAN_EL_C(aux_adc, ADIS16203_SCAN_AUX_ADC, IIO_UNSIGNED(12),
+		     ADIS16203_AUX_ADC, NULL);
+static IIO_SCAN_EL_C(temp, ADIS16203_SCAN_TEMP, IIO_UNSIGNED(12),
+		     ADIS16203_TEMP_OUT, NULL);
+static IIO_SCAN_EL_C(incli_x, ADIS16203_SCAN_INCLI_X, IIO_SIGNED(14),
+		     ADIS16203_XINCL_OUT, NULL);
+static IIO_SCAN_EL_C(incli_y, ADIS16203_SCAN_INCLI_Y, IIO_SIGNED(14),
+		     ADIS16203_YINCL_OUT, NULL);
+
+static IIO_SCAN_EL_TIMESTAMP(5);
+
+static struct attribute *adis16203_scan_el_attrs[] = {
+	&iio_scan_el_supply.dev_attr.attr,
+	&iio_scan_el_aux_adc.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_scan_el_incli_x.dev_attr.attr,
+	&iio_scan_el_incli_y.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16203_scan_el_group = {
+	.attrs = adis16203_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16203_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16203_poll_func_th(struct iio_dev *indio_dev, s64 timestamp)
+{
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = timestamp;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16203_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16203_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16203_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16203_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		if (i < 1) /* SUPPLY_OUT: 0x02, AUX_ADC: 0x08 */
+			st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i);
+		else
+			st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i + 6);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	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 adis16203_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16203_state *st
+		= container_of(work_s, struct adis16203_state,
+			       work_trigger_to_ring);
+
+	int i = 0;
+	s16 *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;
+	}
+
+	if (st->indio_dev->scan_count)
+		if (adis16203_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < st->indio_dev->scan_count; i++) {
+				data[i] = combine_8_to_16(st->rx[i*2+1],
+							  st->rx[i*2]);
+			}
+
+	/* 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(data);
+
+	return;
+}
+
+/* in these circumstances is it better to go with unaligned packing and
+ * deal with the cost?*/
+static int adis16203_data_rdy_ring_preenable(struct iio_dev *indio_dev)
+{
+	size_t size;
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* 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, let timestamp aligned with sizeof(s64) */
+				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
+					+ 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 adis16203_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 adis16203_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;
+}
+
+void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16203_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16203_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16203_trigger_bh_to_ring);
+	/* Set default scan mode */
+
+	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number);
+	indio_dev->scan_timestamp = true;
+
+	indio_dev->scan_el_attrs = &adis16203_scan_el_group;
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->preenable = &adis16203_data_rdy_ring_preenable;
+	ring->postenable = &adis16203_data_rdy_ring_postenable;
+	ring->predisable = &adis16203_data_rdy_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_iio_sw_rb_free;;
+	}
+	indio_dev->pollfunc->poll_func_main = &adis16203_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16203_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16203_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16203_trigger.c b/drivers/staging/iio/accel/adis16203_trigger.c
new file mode 100644
index 0000000..50be51c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_trigger.c
@@ -0,0 +1,122 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16203.h"
+
+/**
+ * adis16203_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16203_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16203_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16203_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16203_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16203_trigger_attr_group = {
+	.attrs = adis16203_trigger_attrs,
+};
+
+/**
+ * adis16203_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16203_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16203_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16203_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16203_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16203_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16203_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16203_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16203_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"adis16203-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16203_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16203_trig_try_reen;
+	st->trig->control_attrs = &adis16203_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16203_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16203_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

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

* [PATCH 3/3] staging: iio: new adis16204 driver
  2010-10-23 10:49 [PATCH 1/3] staging: iio: new adis16201 driver Mike Frysinger
  2010-10-23 10:49 ` [PATCH 2/3] staging: iio: new adis16203 driver Mike Frysinger
@ 2010-10-23 10:49 ` Mike Frysinger
  2010-10-23 17:45   ` Jonathan Cameron
  2010-10-23 17:04 ` [PATCH 1/3] staging: iio: new adis16201 driver Jonathan Cameron
  2010-10-23 17:52 ` Jonathan Cameron
  3 siblings, 1 reply; 17+ messages in thread
From: Mike Frysinger @ 2010-10-23 10:49 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: device-drivers-devel, Barry Song

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

IIO driver for Programmable High-g Digital Impact Sensor and Recorder.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/accel.h             |   22 +
 drivers/staging/iio/accel/adis16204.h         |  151 ++++++
 drivers/staging/iio/accel/adis16204_core.c    |  613 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16204_ring.c    |  252 ++++++++++
 drivers/staging/iio/accel/adis16204_trigger.c |  122 +++++
 7 files changed, 1173 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16204.h
 create mode 100644 drivers/staging/iio/accel/adis16204_core.c
 create mode 100644 drivers/staging/iio/accel/adis16204_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16204_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index cc057c6..a34f1d3 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -21,6 +21,15 @@ config ADIS16203
 	  Say yes here to build support for Analog Devices adis16203 Programmable
 	  360 Degrees Inclinometer.
 
+config ADIS16204
+	tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16204 Programmable
+	  High-g Digital Impact Sensor and Recorder.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index 76cc92f..1b2a6d3 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -10,6 +10,10 @@ adis16203-y             := adis16203_core.o
 adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
 obj-$(CONFIG_ADIS16203) += adis16203.o
 
+adis16204-y             := adis16204_core.o
+adis16204-$(CONFIG_IIO_RING_BUFFER) += adis16204_ring.o adis16204_trigger.o
+obj-$(CONFIG_ADIS16204) += adis16204.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h
index 1b6e37f..8183ebb 100644
--- a/drivers/staging/iio/accel/accel.h
+++ b/drivers/staging/iio/accel/accel.h
@@ -35,6 +35,28 @@
 #define IIO_DEV_ATTR_ACCEL_Z(_show, _addr)			\
 	IIO_DEVICE_ATTR(accel_z_raw, S_IRUGO, _show, NULL, _addr)
 
+/* XY combined acceleration */
+#define IIO_DEV_ATTR_ACCEL_XY(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_xy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_PEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_peak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_xpeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_YPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_ypeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_ZPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_zpeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XYPEAK(_show, _addr)		\
+	IIO_DEVICE_ATTR(accel_xypeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XYZPEAK(_show, _addr)		\
+	IIO_DEVICE_ATTR(accel_xyzpeak, S_IRUGO, _show, NULL, _addr)
+
 /* Thresholds are somewhat chip dependent - may need quite a few defs here */
 /* For unified thresholds (shared across all directions */
 
diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h
new file mode 100644
index 0000000..e9ed7cb
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204.h
@@ -0,0 +1,151 @@
+#ifndef SPI_ADIS16204_H_
+#define SPI_ADIS16204_H_
+
+#define ADIS16204_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16204_READ_REG(a)    a
+#define ADIS16204_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16204_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16204_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16204_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
+#define ADIS16204_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
+#define ADIS16204_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16204_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16204_X_PEAK_OUT     0x0C /* Twos complement */
+#define ADIS16204_Y_PEAK_OUT     0x0E /* Twos complement */
+#define ADIS16204_XACCL_NULL     0x10 /* Calibration, x-axis acceleration offset null */
+#define ADIS16204_YACCL_NULL     0x12 /* Calibration, y-axis acceleration offset null */
+#define ADIS16204_XACCL_SCALE    0x14 /* X-axis scale factor calibration register */
+#define ADIS16204_YACCL_SCALE    0x16 /* Y-axis scale factor calibration register */
+#define ADIS16204_XY_RSS_OUT     0x18 /* XY combined acceleration (RSS) */
+#define ADIS16204_XY_PEAK_OUT    0x1A /* Peak, XY combined output (RSS) */
+#define ADIS16204_CAP_BUF_1      0x1C /* Capture buffer output register 1 */
+#define ADIS16204_CAP_BUF_2      0x1E /* Capture buffer output register 2 */
+#define ADIS16204_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16204_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16204_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16204_CAPT_PNTR      0x2A /* Capture register address pointer */
+#define ADIS16204_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16204_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16204_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16204_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16204_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16204_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16204_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16204_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16204_OUTPUTS        5
+
+/* MSC_CTRL */
+#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16204_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16204_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16204_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2	(1 << 0)  /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+
+/* DIAG_STAT */
+#define ADIS16204_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag: 1 = error condition,
+						0 = normal operation */
+#define ADIS16204_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16204_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16204_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16204_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 2.975 V */
+
+/* GLOB_CMD */
+#define ADIS16204_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16204_GLOB_CMD_CLEAR_STAT	(1<<4)
+#define ADIS16204_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16204_MAX_TX 24
+#define ADIS16204_MAX_RX 24
+
+#define ADIS16204_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16204_state - device instance specific data
+ * @us:			actual spi_device
+ * @work_trigger_to_ring: bh for triggered event handling
+ * @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 adis16204_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16204_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16204_scan {
+	ADIS16204_SCAN_SUPPLY,
+	ADIS16204_SCAN_ACC_X,
+	ADIS16204_SCAN_ACC_Y,
+	ADIS16204_SCAN_AUX_ADC,
+	ADIS16204_SCAN_TEMP,
+};
+
+void adis16204_remove_trigger(struct iio_dev *indio_dev);
+int adis16204_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16204_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16204_configure_ring(struct iio_dev *indio_dev);
+void adis16204_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16204_initialize_ring(struct iio_ring_buffer *ring);
+void adis16204_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16204_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16204_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16204_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16204_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16204_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16204_H_ */
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
new file mode 100644
index 0000000..25ce8dc
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -0,0 +1,613 @@
+/*
+ * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16204.h"
+
+#define DRIVER_NAME		"adis16204"
+
+static int adis16204_check_status(struct device *dev);
+
+/**
+ * adis16204_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16204_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16204_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16204_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16204_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;
+}
+
+/**
+ * adis16204_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16204_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16204_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16204_ERROR_ACTIVE)
+		adis16204_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16204_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16204_ERROR_ACTIVE)
+		adis16204_check_status(dev);
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16204_read_12bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16204_ERROR_ACTIVE)
+			adis16204_check_status(dev);
+
+		val = ((s16)(val << 4) >> 4);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16204_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16204_ERROR_ACTIVE)
+			adis16204_check_status(dev);
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16204_write_16bit(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 = adis16204_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16204_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16204_spi_write_reg_8(dev,
+			ADIS16204_GLOB_CMD,
+			ADIS16204_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16204_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16204_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16204_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16204_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_DIO2;
+	if (enable)
+		msc |= ADIS16204_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16204_spi_write_reg_16(dev, ADIS16204_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16204_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0x1F;
+
+	if (status & ADIS16204_DIAG_STAT_SELFTEST_FAIL)
+		dev_err(dev, "Self test failure\n");
+	if (status & ADIS16204_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16204_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16204_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16204_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 2.975V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16204_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16204_spi_write_reg_16(dev,
+			ADIS16204_MSC_CTRL,
+			ADIS16204_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16204_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16204_initial_setup(struct adis16204_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16204_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16204_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16204_check_status(dev);
+	if (ret) {
+		adis16204_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16204_STARTUP_DELAY);
+		ret = adis16204_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16204_read_12bit_unsigned,
+		ADIS16204_SUPPLY_OUT);
+static IIO_CONST_ATTR(in_supply_scale, "0.00122");
+static IIO_DEV_ATTR_IN_RAW(0, adis16204_read_12bit_unsigned,
+		ADIS16204_AUX_ADC);
+static IIO_CONST_ATTR(in0_scale, "0.00061");
+
+static IIO_DEV_ATTR_ACCEL_X(adis16204_read_14bit_signed,
+		ADIS16204_XACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16204_read_14bit_signed,
+		ADIS16204_YACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_XY(adis16204_read_14bit_signed,
+		ADIS16204_XY_RSS_OUT);
+static IIO_DEV_ATTR_ACCEL_XPEAK(adis16204_read_14bit_signed,
+		ADIS16204_X_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_YPEAK(adis16204_read_14bit_signed,
+		ADIS16204_Y_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_XYPEAK(adis16204_read_14bit_signed,
+		ADIS16204_XY_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16204_read_12bit_signed,
+		adis16204_write_16bit,
+		ADIS16204_XACCL_NULL);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16204_read_12bit_signed,
+		adis16204_write_16bit,
+		ADIS16204_YACCL_NULL);
+static IIO_CONST_ATTR(accel_x_scale, "0.017125");
+static IIO_CONST_ATTR(accel_y_scale, "0.008407");
+static IIO_CONST_ATTR(accel_xy_scale, "0.017125");
+
+static IIO_DEV_ATTR_TEMP(adis16204_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16204_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16204");
+
+static struct attribute *adis16204_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16204_event_attribute_group = {
+	.attrs = adis16204_event_attributes,
+};
+
+static struct attribute *adis16204_attributes[] = {
+	&iio_dev_attr_in_supply_raw.dev_attr.attr,
+	&iio_const_attr_in_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_in0_raw.dev_attr.attr,
+	&iio_const_attr_in0_scale.dev_attr.attr,
+	&iio_dev_attr_accel_x_raw.dev_attr.attr,
+	&iio_dev_attr_accel_y_raw.dev_attr.attr,
+	&iio_dev_attr_accel_xy.dev_attr.attr,
+	&iio_dev_attr_accel_xpeak.dev_attr.attr,
+	&iio_dev_attr_accel_ypeak.dev_attr.attr,
+	&iio_dev_attr_accel_xypeak.dev_attr.attr,
+	&iio_dev_attr_accel_x_offset.dev_attr.attr,
+	&iio_dev_attr_accel_y_offset.dev_attr.attr,
+	&iio_const_attr_accel_x_scale.dev_attr.attr,
+	&iio_const_attr_accel_y_scale.dev_attr.attr,
+	&iio_const_attr_accel_xy_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16204_attribute_group = {
+	.attrs = adis16204_attributes,
+};
+
+static int __devinit adis16204_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16204_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16204_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16204_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16204_event_attribute_group;
+	st->indio_dev->attrs = &adis16204_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16204_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16204_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16204");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16204_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16204_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16204_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16204_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16204_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16204_remove(struct spi_device *spi)
+{
+	struct adis16204_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16204_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16204_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16204_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16204_driver = {
+	.driver = {
+		.name = "adis16204",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16204_probe,
+	.remove = __devexit_p(adis16204_remove),
+};
+
+static __init int adis16204_init(void)
+{
+	return spi_register_driver(&adis16204_driver);
+}
+module_init(adis16204_init);
+
+static __exit void adis16204_exit(void)
+{
+	spi_unregister_driver(&adis16204_driver);
+}
+module_exit(adis16204_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c
new file mode 100644
index 0000000..0264a50
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_ring.c
@@ -0,0 +1,252 @@
+#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/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16204.h"
+
+/**
+ * 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);
+}
+
+static IIO_SCAN_EL_C(supply, ADIS16204_SCAN_SUPPLY, IIO_UNSIGNED(12),
+		     ADIS16204_SUPPLY_OUT, NULL);
+static IIO_SCAN_EL_C(accel_x, ADIS16204_SCAN_ACC_X, IIO_SIGNED(14),
+		     ADIS16204_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16204_SCAN_ACC_Y, IIO_SIGNED(14),
+		     ADIS16204_YACCL_OUT, NULL);
+static IIO_SCAN_EL_C(aux_adc, ADIS16204_SCAN_AUX_ADC, IIO_UNSIGNED(12),
+		     ADIS16204_AUX_ADC, NULL);
+static IIO_SCAN_EL_C(temp, ADIS16204_SCAN_TEMP, IIO_UNSIGNED(12),
+		     ADIS16204_TEMP_OUT, NULL);
+
+static IIO_SCAN_EL_TIMESTAMP(5);
+
+static struct attribute *adis16204_scan_el_attrs[] = {
+	&iio_scan_el_supply.dev_attr.attr,
+	&iio_scan_el_accel_x.dev_attr.attr,
+	&iio_scan_el_accel_y.dev_attr.attr,
+	&iio_scan_el_aux_adc.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16204_scan_el_group = {
+	.attrs = adis16204_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16204_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16204_poll_func_th(struct iio_dev *indio_dev, s64 timestamp)
+{
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = timestamp;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16204_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16204_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16204_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16204_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		st->tx[2 * i] = ADIS16204_READ_REG(ADIS16204_SUPPLY_OUT + 2 * i);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	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 adis16204_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16204_state *st
+		= container_of(work_s, struct adis16204_state,
+			       work_trigger_to_ring);
+
+	int i = 0;
+	s16 *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;
+	}
+
+	if (st->indio_dev->scan_count)
+		if (adis16204_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < st->indio_dev->scan_count; i++) {
+				data[i] = combine_8_to_16(st->rx[i*2+1],
+							  st->rx[i*2]);
+			}
+
+	/* 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(data);
+
+	return;
+}
+
+/* in these circumstances is it better to go with unaligned packing and
+ * deal with the cost?*/
+static int adis16204_data_rdy_ring_preenable(struct iio_dev *indio_dev)
+{
+	size_t size;
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* 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, let timestamp aligned with sizeof(s64) */
+				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
+					+ 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 adis16204_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 adis16204_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;
+}
+
+void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16204_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16204_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16204_trigger_bh_to_ring);
+	/* Set default scan mode */
+
+	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
+	indio_dev->scan_timestamp = true;
+
+	indio_dev->scan_el_attrs = &adis16204_scan_el_group;
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->preenable = &adis16204_data_rdy_ring_preenable;
+	ring->postenable = &adis16204_data_rdy_ring_postenable;
+	ring->predisable = &adis16204_data_rdy_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_iio_sw_rb_free;;
+	}
+	indio_dev->pollfunc->poll_func_main = &adis16204_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16204_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16204_trigger.c b/drivers/staging/iio/accel/adis16204_trigger.c
new file mode 100644
index 0000000..8e9db90
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_trigger.c
@@ -0,0 +1,122 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16204.h"
+
+/**
+ * adis16204_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16204_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16204_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	iio_trigger_poll(trig, timestamp);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16204_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16204_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16204_trigger_attr_group = {
+	.attrs = adis16204_trigger_attrs,
+};
+
+/**
+ * adis16204_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16204_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16204_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16204_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16204_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16204_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16204_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16204_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16204_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kasprintf(GFP_KERNEL,
+				"adis16204-dev%d",
+				indio_dev->id);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16204_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16204_trig_try_reen;
+	st->trig->control_attrs = &adis16204_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16204_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16204_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.3.2

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

* Re: [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-23 10:49 [PATCH 1/3] staging: iio: new adis16201 driver Mike Frysinger
  2010-10-23 10:49 ` [PATCH 2/3] staging: iio: new adis16203 driver Mike Frysinger
  2010-10-23 10:49 ` [PATCH 3/3] staging: iio: new adis16204 driver Mike Frysinger
@ 2010-10-23 17:04 ` Jonathan Cameron
  2010-10-23 17:21   ` Jonathan Cameron
  2010-10-23 17:52 ` Jonathan Cameron
  3 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-23 17:04 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Barry Song

On 10/23/10 11:49, Mike Frysinger wrote:
> From: Barry Song <barry.song@analog.com>
> 
Hi Mike,

> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.
Couple of nitpicks in an otherwise excellent driver.  We can probably
shorten it a fair bit using some of the recent helper function additions
but that can happen post merge.

Anyhow, the nitpicks:
* Capitalization is a bit random in the help message... 
* Often the status check functions return isn't handled.  This means
we print out a dev_err message, but then push data on up to userspace
instad of an appropriate error message.
* This does the same unnecessary abuse of the event infrastructure
to handle the trigger as several other drivers.  That's a problem for
another day and as it's common enough in tree, lets leave it be in this
driver for now and fix this one along side the others.
* The combine_8_to_16 never made sense for adis devices, but can be
trivially ripped out in a follow up patch.  See the other drivers
in mainline for which particular endian fixing function is needed...
* Please run checkpatch over this, couple of obvious issues it will
probably moan about.

So before sending to Greg, please do a checkpatch pass (I may be wrong
and it might be fine for some reason).  The rest can easily occur
after the event and as it might make the merge window if you send
it to Greg KH quickly send it on asap!

Of course, if you have time to cleanup the various other things
mentioned inline that would be great but even with them I'm happy
to Ack.
> 
> Signed-off-by: Barry Song <barry.song@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>

> ---
>  drivers/staging/iio/accel/Kconfig             |    9 +
>  drivers/staging/iio/accel/Makefile            |    4 +
>  drivers/staging/iio/accel/adis16201.h         |  150 ++++++
>  drivers/staging/iio/accel/adis16201_core.c    |  640 +++++++++++++++++++++++++
>  drivers/staging/iio/accel/adis16201_ring.c    |  260 ++++++++++
>  drivers/staging/iio/accel/adis16201_trigger.c |  122 +++++
>  6 files changed, 1185 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/accel/adis16201.h
>  create mode 100644 drivers/staging/iio/accel/adis16201_core.c
>  create mode 100644 drivers/staging/iio/accel/adis16201_ring.c
>  create mode 100644 drivers/staging/iio/accel/adis16201_trigger.c
> 
> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
> index 5926c03..0b87557 100644
> --- a/drivers/staging/iio/accel/Kconfig
> +++ b/drivers/staging/iio/accel/Kconfig
> @@ -3,6 +3,15 @@
>  #
>  comment "Accelerometers"
>  
> +config ADIS16201
> +	tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
> +	depends on SPI
> +	select IIO_TRIGGER if IIO_RING_BUFFER
> +	select IIO_SW_RING if IIO_RING_BUFFER
> +	help
> +	  Say yes here to build support for Analog Devices adis16201 dual-axis
> +	  digital inclinometer and accelerometer.
> +
>  config ADIS16209
>  	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
>  	depends on SPI
> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
> index ff84703..4a22a01 100644
> --- a/drivers/staging/iio/accel/Makefile
> +++ b/drivers/staging/iio/accel/Makefile
> @@ -2,6 +2,10 @@
>  # Makefile for industrial I/O accelerometer drivers
>  #
>  
> +adis16201-y             := adis16201_core.o
> +adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
> +obj-$(CONFIG_ADIS16201) += adis16201.o
> +
>  adis16209-y             := adis16209_core.o
>  adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
>  obj-$(CONFIG_ADIS16209) += adis16209.o
> diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
> new file mode 100644
> index 0000000..b739ea2
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201.h
> @@ -0,0 +1,150 @@
> +#ifndef SPI_ADIS16201_H_
> +#define SPI_ADIS16201_H_
> +
> +#define ADIS16201_STARTUP_DELAY	220 /* ms */
> +
> +#define ADIS16201_READ_REG(a)    a
> +#define ADIS16201_WRITE_REG(a) ((a) | 0x80)
> +
> +#define ADIS16201_FLASH_CNT      0x00 /* Flash memory write count */
> +#define ADIS16201_SUPPLY_OUT     0x02 /* Output, power supply */
> +#define ADIS16201_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
> +#define ADIS16201_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
> +#define ADIS16201_AUX_ADC        0x08 /* Output, auxiliary ADC input */
> +#define ADIS16201_TEMP_OUT       0x0A /* Output, temperature */
> +#define ADIS16201_XINCL_OUT      0x0C /* Output, x-axis inclination */
> +#define ADIS16201_YINCL_OUT      0x0E /* Output, y-axis inclination */
> +#define ADIS16201_XACCL_OFFS     0x10 /* Calibration, x-axis acceleration offset */
> +#define ADIS16201_YACCL_OFFS     0x12 /* Calibration, y-axis acceleration offset */
> +#define ADIS16201_XACCL_SCALE    0x14 /* x-axis acceleration scale factor */
> +#define ADIS16201_YACCL_SCALE    0x16 /* y-axis acceleration scale factor */
> +#define ADIS16201_XINCL_OFFS     0x18 /* Calibration, x-axis inclination offset */
> +#define ADIS16201_YINCL_OFFS     0x1A /* Calibration, y-axis inclination offset */
> +#define ADIS16201_XINCL_SCALE    0x1C /* x-axis inclination scale factor */
> +#define ADIS16201_YINCL_SCALE    0x1E /* y-axis inclination scale factor */
> +#define ADIS16201_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
> +#define ADIS16201_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
> +#define ADIS16201_ALM_SMPL1      0x24 /* Alarm 1, sample period */
> +#define ADIS16201_ALM_SMPL2      0x26 /* Alarm 2, sample period */
> +#define ADIS16201_ALM_CTRL       0x28 /* Alarm control */
> +#define ADIS16201_AUX_DAC        0x30 /* Auxiliary DAC data */
> +#define ADIS16201_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
> +#define ADIS16201_MSC_CTRL       0x34 /* Miscellaneous control */
> +#define ADIS16201_SMPL_PRD       0x36 /* Internal sample period (rate) control */
> +#define ADIS16201_AVG_CNT        0x38 /* Operation, filter configuration */
> +#define ADIS16201_SLP_CNT        0x3A /* Operation, sleep mode control */
> +#define ADIS16201_DIAG_STAT      0x3C /* Diagnostics, system status register */
> +#define ADIS16201_GLOB_CMD       0x3E /* Operation, system command register */
> +
> +#define ADIS16201_OUTPUTS        7
> +
> +/* MSC_CTRL */
> +#define ADIS16201_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
> +#define ADIS16201_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
> +#define ADIS16201_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
> +#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
> +
> +/* DIAG_STAT */
> +#define ADIS16201_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16201_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16201_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
> +#define ADIS16201_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
> +#define ADIS16201_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
> +#define ADIS16201_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
> +
> +/* GLOB_CMD */
> +#define ADIS16201_GLOB_CMD_SW_RESET	(1<<7)
> +#define ADIS16201_GLOB_CMD_FACTORY_CAL	(1<<1)
> +
> +#define ADIS16201_MAX_TX 24
This looks a bit big to me. Worst case is read_ring_data.  That looks to 
use &st->tx[14] and the length is 2 so I'm guessing this only needs to be 16
rather than 24 (probably a cut and paste value from another driver?)

> +#define ADIS16201_MAX_RX 24
I think rx only goes up to 14 though please confirm that.
> +
> +#define ADIS16201_ERROR_ACTIVE          (1<<14)
> +
> +/**
> + * struct adis16201_state - device instance specific data
> + * @us:			actual spi_device
> + * @work_trigger_to_ring: bh for triggered event handling
> + * @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 adis16201_state {
> +	struct spi_device		*us;
> +	struct work_struct		work_trigger_to_ring;
> +	s64				last_timestamp;
> +	struct iio_dev			*indio_dev;
> +	struct iio_trigger		*trig;
> +	u8				*tx;
> +	u8				*rx;
> +	struct mutex			buf_lock;
> +};
> +
> +int adis16201_set_irq(struct device *dev, bool enable);
> +
> +#ifdef CONFIG_IIO_RING_BUFFER
> +enum adis16201_scan {
> +	ADIS16201_SCAN_SUPPLY,
> +	ADIS16201_SCAN_ACC_X,
> +	ADIS16201_SCAN_ACC_Y,
> +	ADIS16201_SCAN_AUX_ADC,
> +	ADIS16201_SCAN_TEMP,
> +	ADIS16201_SCAN_INCLI_X,
> +	ADIS16201_SCAN_INCLI_Y,
> +};
> +
> +void adis16201_remove_trigger(struct iio_dev *indio_dev);
> +int adis16201_probe_trigger(struct iio_dev *indio_dev);
> +
> +ssize_t adis16201_read_data_from_ring(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf);
> +
> +int adis16201_configure_ring(struct iio_dev *indio_dev);
> +void adis16201_unconfigure_ring(struct iio_dev *indio_dev);
> +
> +int adis16201_initialize_ring(struct iio_ring_buffer *ring);
> +void adis16201_uninitialize_ring(struct iio_ring_buffer *ring);
> +#else /* CONFIG_IIO_RING_BUFFER */
> +
> +static inline void adis16201_remove_trigger(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16201_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline ssize_t
> +adis16201_read_data_from_ring(struct device *dev,
> +			      struct device_attribute *attr,
> +			      char *buf)
> +{
> +	return 0;
> +}
> +
> +static int adis16201_configure_ring(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16201_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +}
> +
> +#endif /* CONFIG_IIO_RING_BUFFER */
> +#endif /* SPI_ADIS16201_H_ */
> diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
> new file mode 100644
> index 0000000..c2f85a5
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201_core.c
> @@ -0,0 +1,640 @@
> +/*
> + * ADIS16201 Programmable Digital Vibration Sensor driver
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "accel.h"
> +#include "inclinometer.h"
> +#include "../gyro/gyro.h"
> +#include "../adc/adc.h"
> +
> +#include "adis16201.h"
> +
> +#define DRIVER_NAME		"adis16201"
> +
> +static int adis16201_check_status(struct device *dev);
> +
> +/**
> + * adis16201_spi_write_reg_8() - write single byte to a register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the register to be written
> + * @val: the value to write
> + **/
> +static int adis16201_spi_write_reg_8(struct device *dev,
> +		u8 reg_address,
> +		u8 val)
> +{
> +	int ret;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16201_WRITE_REG(reg_address);
> +	st->tx[1] = val;
> +
> +	ret = spi_write(st->us, st->tx, 2);
> +	mutex_unlock(&st->buf_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * adis16201_spi_write_reg_16() - write 2 bytes to a pair of registers
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: value to be written
> + **/
> +static int adis16201_spi_write_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 value)
> +{
> +	int ret;
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		}, {
> +			.tx_buf = st->tx + 2,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16201_WRITE_REG(lower_reg_address);
> +	st->tx[1] = value & 0xFF;
> +	st->tx[2] = ADIS16201_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;
> +}
> +
> +/**
> + * adis16201_spi_read_reg_16() - read 2 bytes from a 16-bit register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: somewhere to pass back the value read
> + **/
> +static int adis16201_spi_read_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 *val)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	int ret;
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		}, {
> +			.rx_buf = st->rx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16201_READ_REG(lower_reg_address);
> +	st->tx[1] = 0;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	ret = spi_sync(st->us, &msg);
> +	if (ret) {
> +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
> +				lower_reg_address);
> +		goto error_ret;
> +	}
> +	*val = (st->rx[0] << 8) | st->rx[1];
> +
> +error_ret:
> +	mutex_unlock(&st->buf_lock);
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_12bit_unsigned(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int ret;
> +	u16 val = 0;
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & ADIS16201_ERROR_ACTIVE)
> +		adis16201_check_status(dev);
> +
> +	return sprintf(buf, "%u\n", val & 0x0FFF);
> +}
> +
> +static ssize_t adis16201_read_temp(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	ssize_t ret;
> +	u16 val;
> +
> +	/* Take the iio_dev status lock */
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_TEMP_OUT, (u16 *)&val);
> +	if (ret)
> +		goto error_ret;
> +
> +	if (val & ADIS16201_ERROR_ACTIVE)
> +		adis16201_check_status(dev);
> +
> +	val &= 0xFFF;
> +	ret = sprintf(buf, "%d\n", val);
> +
> +error_ret:
> +	mutex_unlock(&indio_dev->mlock);
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_9bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16201_ERROR_ACTIVE)
> +			adis16201_check_status(dev);
Return val eaten.
> +
> +		val = ((s16)(val << 7) >> 7);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_12bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16201_ERROR_ACTIVE)
> +			adis16201_check_status(dev);
Eating possibly useful return value?
> +
> +		val = ((s16)(val << 4) >> 4);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_14bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16201_ERROR_ACTIVE)
> +			adis16201_check_status(dev);
Should be taking notice of the return value of check_status.
> +
> +		val = ((s16)(val << 2) >> 2);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_write_16bit(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 = adis16201_spi_write_reg_16(dev, this_attr->address, val);
> +
> +error_ret:
> +	return ret ? ret : len;
> +}
> +
> +static int adis16201_reset(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16201_spi_write_reg_8(dev,
> +			ADIS16201_GLOB_CMD,
> +			ADIS16201_GLOB_CMD_SW_RESET);
> +	if (ret)
> +		dev_err(dev, "problem resetting device");
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_write_reset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t len)
> +{
> +	if (len < 1)
> +		return -EINVAL;
> +	switch (buf[0]) {
> +	case '1':
> +	case 'y':
> +	case 'Y':
> +		return adis16201_reset(dev);
> +	}
> +	return -EINVAL;
> +}
> +
> +int adis16201_set_irq(struct device *dev, bool enable)
> +{
> +	int ret = 0;
> +	u16 msc;
> +
> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_MSC_CTRL, &msc);
> +	if (ret)
> +		goto error_ret;
> +
Ideally these should probably be controlled by some platform data, but
as ever that can be added by whoever needs it.
> +	msc |= ADIS16201_MSC_CTRL_ACTIVE_HIGH;
> +	msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_DIO1;
> +	if (enable)
> +		msc |= ADIS16201_MSC_CTRL_DATA_RDY_EN;
> +	else
> +		msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_EN;
> +
> +	ret = adis16201_spi_write_reg_16(dev, ADIS16201_MSC_CTRL, msc);
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16201_check_status(struct device *dev)
> +{
> +	u16 status;
> +	int ret;
> +
> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_DIAG_STAT, &status);
> +	if (ret < 0) {
> +		dev_err(dev, "Reading status failed\n");
> +		goto error_ret;
> +	}
> +	ret = status & 0xF;
> +
> +	if (status & ADIS16201_DIAG_STAT_SPI_FAIL)
> +		dev_err(dev, "SPI failure\n");
> +	if (status & ADIS16201_DIAG_STAT_FLASH_UPT)
> +		dev_err(dev, "Flash update failed\n");
> +	if (status & ADIS16201_DIAG_STAT_POWER_HIGH)
> +		dev_err(dev, "Power supply above 3.625V\n");
> +	if (status & ADIS16201_DIAG_STAT_POWER_LOW)
> +		dev_err(dev, "Power supply below 3.15V\n");
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16201_self_test(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16201_spi_write_reg_16(dev,
> +			ADIS16201_MSC_CTRL,
> +			ADIS16201_MSC_CTRL_SELF_TEST_EN);
> +	if (ret) {
> +		dev_err(dev, "problem starting self test");
> +		goto err_ret;
> +	}
> +
> +	adis16201_check_status(dev);
again I'd like to see any errors from this passed on.
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static int adis16201_initial_setup(struct adis16201_state *st)
> +{
> +	int ret;
> +	struct device *dev = &st->indio_dev->dev;
> +
> +	/* Disable IRQ */
> +	ret = adis16201_set_irq(dev, false);
> +	if (ret) {
> +		dev_err(dev, "disable irq failed");
> +		goto err_ret;
> +	}
> +
> +	/* Do self test */
> +	ret = adis16201_self_test(dev);
> +	if (ret) {
> +		dev_err(dev, "self test failure");
> +		goto err_ret;
> +	}
> +
> +	/* Read status register to check the result */
> +	ret = adis16201_check_status(dev);
> +	if (ret) {
> +		adis16201_reset(dev);
> +		dev_err(dev, "device not playing ball -> reset");
> +		msleep(ADIS16201_STARTUP_DELAY);
> +		ret = adis16201_check_status(dev);
> +		if (ret) {
> +			dev_err(dev, "giving up");
> +			goto err_ret;
> +		}
> +	}
> +
> +	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
> +			st->us->chip_select, st->us->irq);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16201_read_12bit_unsigned,
> +		ADIS16201_SUPPLY_OUT);
> +static IIO_CONST_ATTR(in_supply_scale, "0.00122");
> +static IIO_DEV_ATTR_IN_RAW(0, adis16201_read_12bit_unsigned,
> +		ADIS16201_AUX_ADC);
> +static IIO_CONST_ATTR(in0_scale, "0.00061");
> +
> +static IIO_DEV_ATTR_ACCEL_X(adis16201_read_14bit_signed,
> +		ADIS16201_XACCL_OUT);
> +static IIO_DEV_ATTR_ACCEL_Y(adis16201_read_14bit_signed,
> +		ADIS16201_YACCL_OUT);
> +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_12bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_XACCL_OFFS);
> +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_12bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_YACCL_OFFS);
> +static IIO_CONST_ATTR(accel_scale, "0.4625");
> +
> +static IIO_DEV_ATTR_INCLI_X(adis16201_read_14bit_signed,
> +		ADIS16201_XINCL_OUT);
> +static IIO_DEV_ATTR_INCLI_Y(adis16201_read_14bit_signed,
> +		ADIS16201_YINCL_OUT);
> +static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_9bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_XACCL_OFFS);
> +static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_9bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_YACCL_OFFS);
> +static IIO_CONST_ATTR(incli_scale, "0.1");
> +
> +static IIO_DEV_ATTR_TEMP(adis16201_read_temp);
> +static IIO_CONST_ATTR(temp_offset, "25");
> +static IIO_CONST_ATTR(temp_scale, "-0.47");
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16201_write_reset, 0);
> +
> +static IIO_CONST_ATTR(name, "adis16201");
> +

This is always a bad sign.  If there are no event attribute then we
shouldn't be using the event infrastructure at all.  This one snuck
into some of Analog's drivers a while back.  I'll clean them all out
once we have ironed out some problems with the adis16350 mass merge
of drivers (that may or may not be connnected to the replacement of
this form of code!).

Still, not great to see it again, but it does work so lets leave it
here for now.
> +static struct attribute *adis16201_event_attributes[] = {
> +	NULL
> +};
> +
> +static struct attribute_group adis16201_event_attribute_group = {
> +	.attrs = adis16201_event_attributes,
> +};
> +
> +static struct attribute *adis16201_attributes[] = {
> +	&iio_dev_attr_in_supply_raw.dev_attr.attr,
> +	&iio_const_attr_in_supply_scale.dev_attr.attr,
> +	&iio_dev_attr_temp.dev_attr.attr,
> +	&iio_const_attr_temp_offset.dev_attr.attr,
> +	&iio_const_attr_temp_scale.dev_attr.attr,
> +	&iio_dev_attr_reset.dev_attr.attr,
> +	&iio_const_attr_name.dev_attr.attr,
> +	&iio_dev_attr_in0_raw.dev_attr.attr,
> +	&iio_const_attr_in0_scale.dev_attr.attr,
> +	&iio_dev_attr_accel_x_raw.dev_attr.attr,
> +	&iio_dev_attr_accel_y_raw.dev_attr.attr,
> +	&iio_dev_attr_accel_x_offset.dev_attr.attr,
> +	&iio_dev_attr_accel_y_offset.dev_attr.attr,
> +	&iio_const_attr_accel_scale.dev_attr.attr,
> +	&iio_dev_attr_incli_x_raw.dev_attr.attr,
> +	&iio_dev_attr_incli_y_raw.dev_attr.attr,
> +	&iio_dev_attr_incli_x_offset.dev_attr.attr,
> +	&iio_dev_attr_incli_y_offset.dev_attr.attr,
> +	&iio_const_attr_incli_scale.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adis16201_attribute_group = {
> +	.attrs = adis16201_attributes,
> +};
> +
> +static int __devinit adis16201_probe(struct spi_device *spi)
> +{
> +	int ret, regdone = 0;
> +	struct adis16201_state *st = kzalloc(sizeof *st, GFP_KERNEL);
> +	if (!st) {
> +		ret =  -ENOMEM;
> +		goto error_ret;
> +	}
> +	/* this is only used for removal purposes */
> +	spi_set_drvdata(spi, st);
> +
> +	/* Allocate the comms buffers */
> +	st->rx = kzalloc(sizeof(*st->rx)*ADIS16201_MAX_RX, GFP_KERNEL);
> +	if (st->rx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_st;
> +	}
> +	st->tx = kzalloc(sizeof(*st->tx)*ADIS16201_MAX_TX, GFP_KERNEL);
> +	if (st->tx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_rx;
> +	}
> +	st->us = spi;
> +	mutex_init(&st->buf_lock);
> +	/* setup the industrialio driver allocated elements */
> +	st->indio_dev = iio_allocate_device();
> +	if (st->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_tx;
> +	}
> +
> +	st->indio_dev->dev.parent = &spi->dev;
> +	st->indio_dev->num_interrupt_lines = 1;
> +	st->indio_dev->event_attrs = &adis16201_event_attribute_group;
> +	st->indio_dev->attrs = &adis16201_attribute_group;
> +	st->indio_dev->dev_data = (void *)(st);
> +	st->indio_dev->driver_module = THIS_MODULE;
> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = adis16201_configure_ring(st->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = iio_device_register(st->indio_dev);
> +	if (ret)
> +		goto error_unreg_ring_funcs;
> +	regdone = 1;
> +
> +	ret = adis16201_initialize_ring(st->indio_dev->ring);
> +	if (ret) {
> +		printk(KERN_ERR "failed to initialize the ring\n");
> +		goto error_unreg_ring_funcs;
> +	}
> +
> +	if (spi->irq) {
> +		ret = iio_register_interrupt_line(spi->irq,
> +				st->indio_dev,
> +				0,
> +				IRQF_TRIGGER_RISING,
> +				"adis16201");
> +		if (ret)
> +			goto error_uninitialize_ring;
> +
> +		ret = adis16201_probe_trigger(st->indio_dev);
> +		if (ret)
> +			goto error_unregister_line;
> +	}
> +
> +	/* Get the device into a sane initial state */
> +	ret = adis16201_initial_setup(st);
> +	if (ret)
> +		goto error_remove_trigger;
> +	return 0;
> +
> +error_remove_trigger:
> +	adis16201_remove_trigger(st->indio_dev);
> +error_unregister_line:
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(st->indio_dev, 0);
> +error_uninitialize_ring:
> +	adis16201_uninitialize_ring(st->indio_dev->ring);
> +error_unreg_ring_funcs:
> +	adis16201_unconfigure_ring(st->indio_dev);
> +error_free_dev:
> +	if (regdone)
> +		iio_device_unregister(st->indio_dev);
> +	else
> +		iio_free_device(st->indio_dev);
> +error_free_tx:
> +	kfree(st->tx);
> +error_free_rx:
> +	kfree(st->rx);
> +error_free_st:
> +	kfree(st);
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16201_remove(struct spi_device *spi)
> +{
> +	struct adis16201_state *st = spi_get_drvdata(spi);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +
> +	flush_scheduled_work();
> +
> +	adis16201_remove_trigger(indio_dev);
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +
> +	adis16201_uninitialize_ring(indio_dev->ring);
> +	iio_device_unregister(indio_dev);
> +	adis16201_unconfigure_ring(indio_dev);
> +	kfree(st->tx);
> +	kfree(st->rx);
> +	kfree(st);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver adis16201_driver = {
> +	.driver = {
> +		.name = "adis16201",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = adis16201_probe,
> +	.remove = __devexit_p(adis16201_remove),
> +};
> +
> +static __init int adis16201_init(void)
> +{
> +	return spi_register_driver(&adis16201_driver);
> +}
> +module_init(adis16201_init);
> +
> +static __exit void adis16201_exit(void)
> +{
> +	spi_unregister_driver(&adis16201_driver);
> +}
> +module_exit(adis16201_exit);
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c
> new file mode 100644
> index 0000000..83fee28
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201_ring.c
> @@ -0,0 +1,260 @@
> +#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/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../ring_sw.h"
> +#include "accel.h"
> +#include "../trigger.h"
> +#include "adis16201.h"
> +
> +/**
> + * 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);
> +}
This is a hangover from Barry copying the lis3l02dq driver (where this
sort of function made sense). Needs replacing with a generic endian
fiddling alternative.  We can do that as a follow up patch.
> +
> +static IIO_SCAN_EL_C(supply, ADIS16201_SCAN_SUPPLY, IIO_UNSIGNED(12),
> +		     ADIS16201_SUPPLY_OUT, NULL);
> +static IIO_SCAN_EL_C(accel_x, ADIS16201_SCAN_ACC_X, IIO_SIGNED(14),
> +		     ADIS16201_XACCL_OUT, NULL);
> +static IIO_SCAN_EL_C(accel_y, ADIS16201_SCAN_ACC_Y, IIO_SIGNED(14),
> +		     ADIS16201_YACCL_OUT, NULL);
> +static IIO_SCAN_EL_C(aux_adc, ADIS16201_SCAN_AUX_ADC, IIO_UNSIGNED(12),
> +		     ADIS16201_AUX_ADC, NULL);
> +static IIO_SCAN_EL_C(temp, ADIS16201_SCAN_TEMP, IIO_UNSIGNED(12),
> +		     ADIS16201_TEMP_OUT, NULL);
> +static IIO_SCAN_EL_C(incli_x, ADIS16201_SCAN_INCLI_X, IIO_SIGNED(14),
> +		     ADIS16201_XINCL_OUT, NULL);
> +static IIO_SCAN_EL_C(incli_y, ADIS16201_SCAN_INCLI_Y, IIO_SIGNED(14),
> +		     ADIS16201_YINCL_OUT, NULL);
> +
> +static IIO_SCAN_EL_TIMESTAMP(7);
> +
> +static struct attribute *adis16201_scan_el_attrs[] = {
> +	&iio_scan_el_supply.dev_attr.attr,
> +	&iio_scan_el_accel_x.dev_attr.attr,
> +	&iio_scan_el_accel_y.dev_attr.attr,
> +	&iio_scan_el_aux_adc.dev_attr.attr,
> +	&iio_scan_el_temp.dev_attr.attr,
> +	&iio_scan_el_incli_x.dev_attr.attr,
> +	&iio_scan_el_incli_y.dev_attr.attr,
> +	&iio_scan_el_timestamp.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group adis16201_scan_el_group = {
> +	.attrs = adis16201_scan_el_attrs,
> +	.name = "scan_elements",
> +};
> +
> +/**
> + * adis16201_poll_func_th() top half interrupt handler called by trigger
> + * @private_data:	iio_dev
> + **/
> +static void adis16201_poll_func_th(struct iio_dev *indio_dev, s64 time)
> +{
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	st->last_timestamp = time;
> +	schedule_work(&st->work_trigger_to_ring);
> +}
> +
> +/**
> + * adis16201_read_ring_data() read data registers which will be placed into ring
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @rx: somewhere to pass back the value read
> + **/
> +static int adis16201_read_ring_data(struct device *dev, u8 *rx)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[ADIS16201_OUTPUTS + 1];
> +	int ret;
> +	int i;
> +
> +	mutex_lock(&st->buf_lock);
> +
> +	spi_message_init(&msg);
> +
> +	memset(xfers, 0, sizeof(xfers));
> +	for (i = 0; i <= ADIS16201_OUTPUTS; i++) {
> +		xfers[i].bits_per_word = 8;
> +		xfers[i].cs_change = 1;
> +		xfers[i].len = 2;
> +		xfers[i].delay_usecs = 20;
> +		xfers[i].tx_buf = st->tx + 2 * i;
> +		st->tx[2 * i] = ADIS16201_READ_REG(ADIS16201_SUPPLY_OUT + 2 * i);
> +		st->tx[2 * i + 1] = 0;
> +		if (i >= 1)
> +			xfers[i].rx_buf = rx + 2 * (i - 1);
> +		spi_message_add_tail(&xfers[i], &msg);
> +	}
Might be nice to play some caching games here, but that's true of
most of the drivers in the tree so definitely a job for another day!
> +
> +	ret = spi_sync(st->us, &msg);
> +	if (ret)
> +		dev_err(&st->us->dev, "problem when burst reading");
> +
> +	mutex_unlock(&st->buf_lock);
> +
> +	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 adis16201_trigger_bh_to_ring(struct work_struct *work_s)
> +{
> +	struct adis16201_state *st
> +		= container_of(work_s, struct adis16201_state,
> +			       work_trigger_to_ring);
> +
> +	int i = 0;
> +	s16 *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;
> +	}
> +
> +	if (st->indio_dev->scan_count)
> +		if (adis16201_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
> +			for (; i < st->indio_dev->scan_count; i++) {
> +				data[i] = combine_8_to_16(st->rx[i*2+1],
> +							  st->rx[i*2]);
> +			}
Unwanted brackets around the statement above.  Would have thorught checkpatch
would moan about that.
> +
> +	/* 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(data);
> +
> +	return;
> +}
> +
> +/* in these circumstances is it better to go with unaligned packing and
> + * deal with the cost?*/
> +static int adis16201_data_rdy_ring_preenable(struct iio_dev *indio_dev)
> +{
> +	size_t size;
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* 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, let timestamp aligned with sizeof(s64) */
That's looks rather a long line.  Guessing checkpatch moans?
> +				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
> +					+ 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;
> +}
> +
These can all be replaced with the generic calls, but that can trivially occur
as a follow up patch (we did it for all the in tree drivers a while back).
> +static int adis16201_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 adis16201_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;
> +}
> +
> +void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +	kfree(indio_dev->pollfunc);
> +	iio_sw_rb_free(indio_dev->ring);
> +}
> +
> +int adis16201_configure_ring(struct iio_dev *indio_dev)
> +{
> +	int ret = 0;
> +	struct adis16201_state *st = indio_dev->dev_data;
> +	struct iio_ring_buffer *ring;
> +	INIT_WORK(&st->work_trigger_to_ring, adis16201_trigger_bh_to_ring);
> +	/* Set default scan mode */
> +
> +	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number);
> +	indio_dev->scan_timestamp = true;
> +
> +	indio_dev->scan_el_attrs = &adis16201_scan_el_group;
> +
> +	ring = iio_sw_rb_allocate(indio_dev);
> +	if (!ring) {
> +		ret = -ENOMEM;
> +		return ret;
> +	}
> +	indio_dev->ring = ring;
> +	/* Effectively select the ring buffer implementation */
> +	iio_ring_sw_register_funcs(&ring->access);
> +	ring->preenable = &adis16201_data_rdy_ring_preenable;
> +	ring->postenable = &adis16201_data_rdy_ring_postenable;
> +	ring->predisable = &adis16201_data_rdy_ring_predisable;
> +	ring->owner = THIS_MODULE;
> +
> +	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
> +	if (indio_dev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_iio_sw_rb_free;;
> +	}
> +	indio_dev->pollfunc->poll_func_main = &adis16201_poll_func_th;
> +	indio_dev->pollfunc->private_data = indio_dev;
> +	indio_dev->modes |= INDIO_RING_TRIGGERED;
> +	return 0;
> +
> +error_iio_sw_rb_free:
> +	iio_sw_rb_free(indio_dev->ring);
> +	return ret;
> +}
> +

Nothing wrong with this driver, but this makes it pretty obvious that stubbs
for iio_ring_buffer_register in the core would save us a few pointless functions
in the case where we don't have ring support.   One for the todo list.
> +int adis16201_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return iio_ring_buffer_register(ring, 0);
> +}
> +
> +void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +	iio_ring_buffer_unregister(ring);
> +}
> diff --git a/drivers/staging/iio/accel/adis16201_trigger.c b/drivers/staging/iio/accel/adis16201_trigger.c
> new file mode 100644
> index 0000000..8a9cea19
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201_trigger.c
> @@ -0,0 +1,122 @@
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +#include <linux/spi/spi.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../trigger.h"
> +#include "adis16201.h"
> +
As stated above this should really directly setup and handle the interrupt
itself rather than using the events infrastructure.  We can easily do that
at a later date.
> +/**
> + * adis16201_data_rdy_trig_poll() the event handler for the data rdy trig
> + **/
> +static int adis16201_data_rdy_trig_poll(struct iio_dev *dev_info,
> +				       int index,
> +				       s64 timestamp,
> +				       int no_test)
> +{
> +	struct adis16201_state *st = iio_dev_get_devdata(dev_info);
> +	struct iio_trigger *trig = st->trig;
> +
> +	iio_trigger_poll(trig, timestamp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +IIO_EVENT_SH(data_rdy_trig, &adis16201_data_rdy_trig_poll);
> +
> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
> +
> +static struct attribute *adis16201_trigger_attrs[] = {
> +	&dev_attr_name.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group adis16201_trigger_attr_group = {
> +	.attrs = adis16201_trigger_attrs,
> +};
> +
> +/**
> + * adis16201_data_rdy_trigger_set_state() set datardy interrupt state
> + **/
> +static int adis16201_data_rdy_trigger_set_state(struct iio_trigger *trig,
> +						bool state)
> +{
> +	struct adis16201_state *st = trig->private_data;
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	int ret = 0;
> +
> +	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
> +	ret = adis16201_set_irq(&st->indio_dev->dev, state);
> +	if (state == false) {
> +		iio_remove_event_from_list(&iio_event_data_rdy_trig,
> +					   &indio_dev->interrupts[0]
> +					   ->ev_list);
> +		flush_scheduled_work();
> +	} else {
> +		iio_add_event_to_list(&iio_event_data_rdy_trig,
> +				      &indio_dev->interrupts[0]->ev_list);
> +	}
> +	return ret;
> +}
> +
> +/**
> + * adis16201_trig_try_reen() try renabling irq for data rdy trigger
> + * @trig:	the datardy trigger
> + **/
> +static int adis16201_trig_try_reen(struct iio_trigger *trig)
> +{
> +	struct adis16201_state *st = trig->private_data;
> +	enable_irq(st->us->irq);
> +	return 0;
> +}
> +
> +int adis16201_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct adis16201_state *st = indio_dev->dev_data;
> +
> +	st->trig = iio_allocate_trigger();
> +	st->trig->name = kasprintf(GFP_KERNEL,
> +				"adis16201-dev%d",
> +				indio_dev->id);
> +	if (!st->trig->name) {
> +		ret = -ENOMEM;
> +		goto error_free_trig;
> +	}
> +	st->trig->dev.parent = &st->us->dev;
> +	st->trig->owner = THIS_MODULE;
> +	st->trig->private_data = st;
> +	st->trig->set_trigger_state = &adis16201_data_rdy_trigger_set_state;
> +	st->trig->try_reenable = &adis16201_trig_try_reen;
> +	st->trig->control_attrs = &adis16201_trigger_attr_group;
> +	ret = iio_trigger_register(st->trig);
> +
> +	/* select default trigger */
> +	indio_dev->trig = st->trig;
> +	if (ret)
> +		goto error_free_trig_name;
> +
> +	return 0;
> +
> +error_free_trig_name:
> +	kfree(st->trig->name);
> +error_free_trig:
> +	iio_free_trigger(st->trig);
> +
> +	return ret;
> +}
> +
> +void adis16201_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	struct adis16201_state *state = indio_dev->dev_data;
> +
> +	iio_trigger_unregister(state->trig);
> +	kfree(state->trig->name);
> +	iio_free_trigger(state->trig);
> +}


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

* Re: [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-23 17:04 ` [PATCH 1/3] staging: iio: new adis16201 driver Jonathan Cameron
@ 2010-10-23 17:21   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-23 17:21 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mike Frysinger, linux-iio, device-drivers-devel, Barry Song

On 10/23/10 18:04, Jonathan Cameron wrote:
> On 10/23/10 11:49, Mike Frysinger wrote:
>> From: Barry Song <barry.song@analog.com>
>>
> Hi Mike,
> 
>> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.
> Couple of nitpicks in an otherwise excellent driver.  We can probably
> shorten it a fair bit using some of the recent helper function additions
> but that can happen post merge.
> 
> Anyhow, the nitpicks:
> * Capitalization is a bit random in the help message... 
> * Often the status check functions return isn't handled.  This means
> we print out a dev_err message, but then push data on up to userspace
> instad of an appropriate error message.
> * This does the same unnecessary abuse of the event infrastructure
> to handle the trigger as several other drivers.  That's a problem for
> another day and as it's common enough in tree, lets leave it be in this
> driver for now and fix this one along side the others.
> * The combine_8_to_16 never made sense for adis devices, but can be
> trivially ripped out in a follow up patch.  See the other drivers
> in mainline for which particular endian fixing function is needed...
> * Please run checkpatch over this, couple of obvious issues it will
> probably moan about.
> 
> So before sending to Greg, please do a checkpatch pass (I may be wrong
> and it might be fine for some reason).  The rest can easily occur
> after the event and as it might make the merge window if you send
> it to Greg KH quickly send it on asap!
> 
> Of course, if you have time to cleanup the various other things
> mentioned inline that would be great but even with them I'm happy
> to Ack.

I just noticed in reviewing your next driver in this series, that there
are missing elements that are obligatory in scan_elements attribute
group.  Please fix those before sending on (should be fairly trivial).
>>
>> Signed-off-by: Barry Song <barry.song@analog.com>
>> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
> 
>> ---
>>  drivers/staging/iio/accel/Kconfig             |    9 +
>>  drivers/staging/iio/accel/Makefile            |    4 +
>>  drivers/staging/iio/accel/adis16201.h         |  150 ++++++
>>  drivers/staging/iio/accel/adis16201_core.c    |  640 +++++++++++++++++++++++++
>>  drivers/staging/iio/accel/adis16201_ring.c    |  260 ++++++++++
>>  drivers/staging/iio/accel/adis16201_trigger.c |  122 +++++
>>  6 files changed, 1185 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/staging/iio/accel/adis16201.h
>>  create mode 100644 drivers/staging/iio/accel/adis16201_core.c
>>  create mode 100644 drivers/staging/iio/accel/adis16201_ring.c
>>  create mode 100644 drivers/staging/iio/accel/adis16201_trigger.c
>>
>> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
>> index 5926c03..0b87557 100644
>> --- a/drivers/staging/iio/accel/Kconfig
>> +++ b/drivers/staging/iio/accel/Kconfig
>> @@ -3,6 +3,15 @@
>>  #
>>  comment "Accelerometers"
>>  
>> +config ADIS16201
>> +	tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
>> +	depends on SPI
>> +	select IIO_TRIGGER if IIO_RING_BUFFER
>> +	select IIO_SW_RING if IIO_RING_BUFFER
>> +	help
>> +	  Say yes here to build support for Analog Devices adis16201 dual-axis
>> +	  digital inclinometer and accelerometer.
>> +
>>  config ADIS16209
>>  	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
>>  	depends on SPI
>> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
>> index ff84703..4a22a01 100644
>> --- a/drivers/staging/iio/accel/Makefile
>> +++ b/drivers/staging/iio/accel/Makefile
>> @@ -2,6 +2,10 @@
>>  # Makefile for industrial I/O accelerometer drivers
>>  #
>>  
>> +adis16201-y             := adis16201_core.o
>> +adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
>> +obj-$(CONFIG_ADIS16201) += adis16201.o
>> +
>>  adis16209-y             := adis16209_core.o
>>  adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
>>  obj-$(CONFIG_ADIS16209) += adis16209.o
>> diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
>> new file mode 100644
>> index 0000000..b739ea2
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16201.h
>> @@ -0,0 +1,150 @@
>> +#ifndef SPI_ADIS16201_H_
>> +#define SPI_ADIS16201_H_
>> +
>> +#define ADIS16201_STARTUP_DELAY	220 /* ms */
>> +
>> +#define ADIS16201_READ_REG(a)    a
>> +#define ADIS16201_WRITE_REG(a) ((a) | 0x80)
>> +
>> +#define ADIS16201_FLASH_CNT      0x00 /* Flash memory write count */
>> +#define ADIS16201_SUPPLY_OUT     0x02 /* Output, power supply */
>> +#define ADIS16201_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
>> +#define ADIS16201_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
>> +#define ADIS16201_AUX_ADC        0x08 /* Output, auxiliary ADC input */
>> +#define ADIS16201_TEMP_OUT       0x0A /* Output, temperature */
>> +#define ADIS16201_XINCL_OUT      0x0C /* Output, x-axis inclination */
>> +#define ADIS16201_YINCL_OUT      0x0E /* Output, y-axis inclination */
>> +#define ADIS16201_XACCL_OFFS     0x10 /* Calibration, x-axis acceleration offset */
>> +#define ADIS16201_YACCL_OFFS     0x12 /* Calibration, y-axis acceleration offset */
>> +#define ADIS16201_XACCL_SCALE    0x14 /* x-axis acceleration scale factor */
>> +#define ADIS16201_YACCL_SCALE    0x16 /* y-axis acceleration scale factor */
>> +#define ADIS16201_XINCL_OFFS     0x18 /* Calibration, x-axis inclination offset */
>> +#define ADIS16201_YINCL_OFFS     0x1A /* Calibration, y-axis inclination offset */
>> +#define ADIS16201_XINCL_SCALE    0x1C /* x-axis inclination scale factor */
>> +#define ADIS16201_YINCL_SCALE    0x1E /* y-axis inclination scale factor */
>> +#define ADIS16201_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
>> +#define ADIS16201_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
>> +#define ADIS16201_ALM_SMPL1      0x24 /* Alarm 1, sample period */
>> +#define ADIS16201_ALM_SMPL2      0x26 /* Alarm 2, sample period */
>> +#define ADIS16201_ALM_CTRL       0x28 /* Alarm control */
>> +#define ADIS16201_AUX_DAC        0x30 /* Auxiliary DAC data */
>> +#define ADIS16201_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
>> +#define ADIS16201_MSC_CTRL       0x34 /* Miscellaneous control */
>> +#define ADIS16201_SMPL_PRD       0x36 /* Internal sample period (rate) control */
>> +#define ADIS16201_AVG_CNT        0x38 /* Operation, filter configuration */
>> +#define ADIS16201_SLP_CNT        0x3A /* Operation, sleep mode control */
>> +#define ADIS16201_DIAG_STAT      0x3C /* Diagnostics, system status register */
>> +#define ADIS16201_GLOB_CMD       0x3E /* Operation, system command register */
>> +
>> +#define ADIS16201_OUTPUTS        7
>> +
>> +/* MSC_CTRL */
>> +#define ADIS16201_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
>> +#define ADIS16201_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
>> +#define ADIS16201_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
>> +#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
>> +
>> +/* DIAG_STAT */
>> +#define ADIS16201_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
>> +#define ADIS16201_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
>> +#define ADIS16201_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
>> +#define ADIS16201_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
>> +#define ADIS16201_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
>> +#define ADIS16201_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
>> +
>> +/* GLOB_CMD */
>> +#define ADIS16201_GLOB_CMD_SW_RESET	(1<<7)
>> +#define ADIS16201_GLOB_CMD_FACTORY_CAL	(1<<1)
>> +
>> +#define ADIS16201_MAX_TX 24
> This looks a bit big to me. Worst case is read_ring_data.  That looks to 
> use &st->tx[14] and the length is 2 so I'm guessing this only needs to be 16
> rather than 24 (probably a cut and paste value from another driver?)
> 
>> +#define ADIS16201_MAX_RX 24
> I think rx only goes up to 14 though please confirm that.
>> +
>> +#define ADIS16201_ERROR_ACTIVE          (1<<14)
>> +
>> +/**
>> + * struct adis16201_state - device instance specific data
>> + * @us:			actual spi_device
>> + * @work_trigger_to_ring: bh for triggered event handling
>> + * @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 adis16201_state {
>> +	struct spi_device		*us;
>> +	struct work_struct		work_trigger_to_ring;
>> +	s64				last_timestamp;
>> +	struct iio_dev			*indio_dev;
>> +	struct iio_trigger		*trig;
>> +	u8				*tx;
>> +	u8				*rx;
>> +	struct mutex			buf_lock;
>> +};
>> +
>> +int adis16201_set_irq(struct device *dev, bool enable);
>> +
>> +#ifdef CONFIG_IIO_RING_BUFFER
>> +enum adis16201_scan {
>> +	ADIS16201_SCAN_SUPPLY,
>> +	ADIS16201_SCAN_ACC_X,
>> +	ADIS16201_SCAN_ACC_Y,
>> +	ADIS16201_SCAN_AUX_ADC,
>> +	ADIS16201_SCAN_TEMP,
>> +	ADIS16201_SCAN_INCLI_X,
>> +	ADIS16201_SCAN_INCLI_Y,
>> +};
>> +
>> +void adis16201_remove_trigger(struct iio_dev *indio_dev);
>> +int adis16201_probe_trigger(struct iio_dev *indio_dev);
>> +
>> +ssize_t adis16201_read_data_from_ring(struct device *dev,
>> +				      struct device_attribute *attr,
>> +				      char *buf);
>> +
>> +int adis16201_configure_ring(struct iio_dev *indio_dev);
>> +void adis16201_unconfigure_ring(struct iio_dev *indio_dev);
>> +
>> +int adis16201_initialize_ring(struct iio_ring_buffer *ring);
>> +void adis16201_uninitialize_ring(struct iio_ring_buffer *ring);
>> +#else /* CONFIG_IIO_RING_BUFFER */
>> +
>> +static inline void adis16201_remove_trigger(struct iio_dev *indio_dev)
>> +{
>> +}
>> +
>> +static inline int adis16201_probe_trigger(struct iio_dev *indio_dev)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline ssize_t
>> +adis16201_read_data_from_ring(struct device *dev,
>> +			      struct device_attribute *attr,
>> +			      char *buf)
>> +{
>> +	return 0;
>> +}
>> +
>> +static int adis16201_configure_ring(struct iio_dev *indio_dev)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
>> +{
>> +}
>> +
>> +static inline int adis16201_initialize_ring(struct iio_ring_buffer *ring)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
>> +{
>> +}
>> +
>> +#endif /* CONFIG_IIO_RING_BUFFER */
>> +#endif /* SPI_ADIS16201_H_ */
>> diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
>> new file mode 100644
>> index 0000000..c2f85a5
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16201_core.c
>> @@ -0,0 +1,640 @@
>> +/*
>> + * ADIS16201 Programmable Digital Vibration Sensor driver
>> + *
>> + * Copyright 2010 Analog Devices Inc.
>> + *
>> + * Licensed under the GPL-2 or later.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/gpio.h>
>> +#include <linux/delay.h>
>> +#include <linux/mutex.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/slab.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/list.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +#include "accel.h"
>> +#include "inclinometer.h"
>> +#include "../gyro/gyro.h"
>> +#include "../adc/adc.h"
>> +
>> +#include "adis16201.h"
>> +
>> +#define DRIVER_NAME		"adis16201"
>> +
>> +static int adis16201_check_status(struct device *dev);
>> +
>> +/**
>> + * adis16201_spi_write_reg_8() - write single byte to a register
>> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
>> + * @reg_address: the address of the register to be written
>> + * @val: the value to write
>> + **/
>> +static int adis16201_spi_write_reg_8(struct device *dev,
>> +		u8 reg_address,
>> +		u8 val)
>> +{
>> +	int ret;
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
>> +
>> +	mutex_lock(&st->buf_lock);
>> +	st->tx[0] = ADIS16201_WRITE_REG(reg_address);
>> +	st->tx[1] = val;
>> +
>> +	ret = spi_write(st->us, st->tx, 2);
>> +	mutex_unlock(&st->buf_lock);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * adis16201_spi_write_reg_16() - write 2 bytes to a pair of registers
>> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
>> + * @reg_address: the address of the lower of the two registers. Second register
>> + *               is assumed to have address one greater.
>> + * @val: value to be written
>> + **/
>> +static int adis16201_spi_write_reg_16(struct device *dev,
>> +		u8 lower_reg_address,
>> +		u16 value)
>> +{
>> +	int ret;
>> +	struct spi_message msg;
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
>> +	struct spi_transfer xfers[] = {
>> +		{
>> +			.tx_buf = st->tx,
>> +			.bits_per_word = 8,
>> +			.len = 2,
>> +			.cs_change = 1,
>> +		}, {
>> +			.tx_buf = st->tx + 2,
>> +			.bits_per_word = 8,
>> +			.len = 2,
>> +			.cs_change = 1,
>> +		},
>> +	};
>> +
>> +	mutex_lock(&st->buf_lock);
>> +	st->tx[0] = ADIS16201_WRITE_REG(lower_reg_address);
>> +	st->tx[1] = value & 0xFF;
>> +	st->tx[2] = ADIS16201_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;
>> +}
>> +
>> +/**
>> + * adis16201_spi_read_reg_16() - read 2 bytes from a 16-bit register
>> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
>> + * @reg_address: the address of the lower of the two registers. Second register
>> + *               is assumed to have address one greater.
>> + * @val: somewhere to pass back the value read
>> + **/
>> +static int adis16201_spi_read_reg_16(struct device *dev,
>> +		u8 lower_reg_address,
>> +		u16 *val)
>> +{
>> +	struct spi_message msg;
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
>> +	int ret;
>> +	struct spi_transfer xfers[] = {
>> +		{
>> +			.tx_buf = st->tx,
>> +			.bits_per_word = 8,
>> +			.len = 2,
>> +			.cs_change = 1,
>> +			.delay_usecs = 20,
>> +		}, {
>> +			.rx_buf = st->rx,
>> +			.bits_per_word = 8,
>> +			.len = 2,
>> +			.cs_change = 1,
>> +			.delay_usecs = 20,
>> +		},
>> +	};
>> +
>> +	mutex_lock(&st->buf_lock);
>> +	st->tx[0] = ADIS16201_READ_REG(lower_reg_address);
>> +	st->tx[1] = 0;
>> +
>> +	spi_message_init(&msg);
>> +	spi_message_add_tail(&xfers[0], &msg);
>> +	spi_message_add_tail(&xfers[1], &msg);
>> +	ret = spi_sync(st->us, &msg);
>> +	if (ret) {
>> +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
>> +				lower_reg_address);
>> +		goto error_ret;
>> +	}
>> +	*val = (st->rx[0] << 8) | st->rx[1];
>> +
>> +error_ret:
>> +	mutex_unlock(&st->buf_lock);
>> +	return ret;
>> +}
>> +
>> +static ssize_t adis16201_read_12bit_unsigned(struct device *dev,
>> +		struct device_attribute *attr,
>> +		char *buf)
>> +{
>> +	int ret;
>> +	u16 val = 0;
>> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, &val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (val & ADIS16201_ERROR_ACTIVE)
>> +		adis16201_check_status(dev);
>> +
>> +	return sprintf(buf, "%u\n", val & 0x0FFF);
>> +}
>> +
>> +static ssize_t adis16201_read_temp(struct device *dev,
>> +		struct device_attribute *attr,
>> +		char *buf)
>> +{
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	ssize_t ret;
>> +	u16 val;
>> +
>> +	/* Take the iio_dev status lock */
>> +	mutex_lock(&indio_dev->mlock);
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_TEMP_OUT, (u16 *)&val);
>> +	if (ret)
>> +		goto error_ret;
>> +
>> +	if (val & ADIS16201_ERROR_ACTIVE)
>> +		adis16201_check_status(dev);
>> +
>> +	val &= 0xFFF;
>> +	ret = sprintf(buf, "%d\n", val);
>> +
>> +error_ret:
>> +	mutex_unlock(&indio_dev->mlock);
>> +	return ret;
>> +}
>> +
>> +static ssize_t adis16201_read_9bit_signed(struct device *dev,
>> +		struct device_attribute *attr,
>> +		char *buf)
>> +{
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +	s16 val = 0;
>> +	ssize_t ret;
>> +
>> +	mutex_lock(&indio_dev->mlock);
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
>> +	if (!ret) {
>> +		if (val & ADIS16201_ERROR_ACTIVE)
>> +			adis16201_check_status(dev);
> Return val eaten.
>> +
>> +		val = ((s16)(val << 7) >> 7);
>> +		ret = sprintf(buf, "%d\n", val);
>> +	}
>> +
>> +	mutex_unlock(&indio_dev->mlock);
>> +
>> +	return ret;
>> +}
>> +
>> +static ssize_t adis16201_read_12bit_signed(struct device *dev,
>> +		struct device_attribute *attr,
>> +		char *buf)
>> +{
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +	s16 val = 0;
>> +	ssize_t ret;
>> +
>> +	mutex_lock(&indio_dev->mlock);
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
>> +	if (!ret) {
>> +		if (val & ADIS16201_ERROR_ACTIVE)
>> +			adis16201_check_status(dev);
> Eating possibly useful return value?
>> +
>> +		val = ((s16)(val << 4) >> 4);
>> +		ret = sprintf(buf, "%d\n", val);
>> +	}
>> +
>> +	mutex_unlock(&indio_dev->mlock);
>> +
>> +	return ret;
>> +}
>> +
>> +static ssize_t adis16201_read_14bit_signed(struct device *dev,
>> +		struct device_attribute *attr,
>> +		char *buf)
>> +{
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +	s16 val = 0;
>> +	ssize_t ret;
>> +
>> +	mutex_lock(&indio_dev->mlock);
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
>> +	if (!ret) {
>> +		if (val & ADIS16201_ERROR_ACTIVE)
>> +			adis16201_check_status(dev);
> Should be taking notice of the return value of check_status.
>> +
>> +		val = ((s16)(val << 2) >> 2);
>> +		ret = sprintf(buf, "%d\n", val);
>> +	}
>> +
>> +	mutex_unlock(&indio_dev->mlock);
>> +
>> +	return ret;
>> +}
>> +
>> +static ssize_t adis16201_write_16bit(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 = adis16201_spi_write_reg_16(dev, this_attr->address, val);
>> +
>> +error_ret:
>> +	return ret ? ret : len;
>> +}
>> +
>> +static int adis16201_reset(struct device *dev)
>> +{
>> +	int ret;
>> +	ret = adis16201_spi_write_reg_8(dev,
>> +			ADIS16201_GLOB_CMD,
>> +			ADIS16201_GLOB_CMD_SW_RESET);
>> +	if (ret)
>> +		dev_err(dev, "problem resetting device");
>> +
>> +	return ret;
>> +}
>> +
>> +static ssize_t adis16201_write_reset(struct device *dev,
>> +		struct device_attribute *attr,
>> +		const char *buf, size_t len)
>> +{
>> +	if (len < 1)
>> +		return -EINVAL;
>> +	switch (buf[0]) {
>> +	case '1':
>> +	case 'y':
>> +	case 'Y':
>> +		return adis16201_reset(dev);
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +int adis16201_set_irq(struct device *dev, bool enable)
>> +{
>> +	int ret = 0;
>> +	u16 msc;
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_MSC_CTRL, &msc);
>> +	if (ret)
>> +		goto error_ret;
>> +
> Ideally these should probably be controlled by some platform data, but
> as ever that can be added by whoever needs it.
>> +	msc |= ADIS16201_MSC_CTRL_ACTIVE_HIGH;
>> +	msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_DIO1;
>> +	if (enable)
>> +		msc |= ADIS16201_MSC_CTRL_DATA_RDY_EN;
>> +	else
>> +		msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_EN;
>> +
>> +	ret = adis16201_spi_write_reg_16(dev, ADIS16201_MSC_CTRL, msc);
>> +
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int adis16201_check_status(struct device *dev)
>> +{
>> +	u16 status;
>> +	int ret;
>> +
>> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_DIAG_STAT, &status);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Reading status failed\n");
>> +		goto error_ret;
>> +	}
>> +	ret = status & 0xF;
>> +
>> +	if (status & ADIS16201_DIAG_STAT_SPI_FAIL)
>> +		dev_err(dev, "SPI failure\n");
>> +	if (status & ADIS16201_DIAG_STAT_FLASH_UPT)
>> +		dev_err(dev, "Flash update failed\n");
>> +	if (status & ADIS16201_DIAG_STAT_POWER_HIGH)
>> +		dev_err(dev, "Power supply above 3.625V\n");
>> +	if (status & ADIS16201_DIAG_STAT_POWER_LOW)
>> +		dev_err(dev, "Power supply below 3.15V\n");
>> +
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int adis16201_self_test(struct device *dev)
>> +{
>> +	int ret;
>> +	ret = adis16201_spi_write_reg_16(dev,
>> +			ADIS16201_MSC_CTRL,
>> +			ADIS16201_MSC_CTRL_SELF_TEST_EN);
>> +	if (ret) {
>> +		dev_err(dev, "problem starting self test");
>> +		goto err_ret;
>> +	}
>> +
>> +	adis16201_check_status(dev);
> again I'd like to see any errors from this passed on.
>> +
>> +err_ret:
>> +	return ret;
>> +}
>> +
>> +static int adis16201_initial_setup(struct adis16201_state *st)
>> +{
>> +	int ret;
>> +	struct device *dev = &st->indio_dev->dev;
>> +
>> +	/* Disable IRQ */
>> +	ret = adis16201_set_irq(dev, false);
>> +	if (ret) {
>> +		dev_err(dev, "disable irq failed");
>> +		goto err_ret;
>> +	}
>> +
>> +	/* Do self test */
>> +	ret = adis16201_self_test(dev);
>> +	if (ret) {
>> +		dev_err(dev, "self test failure");
>> +		goto err_ret;
>> +	}
>> +
>> +	/* Read status register to check the result */
>> +	ret = adis16201_check_status(dev);
>> +	if (ret) {
>> +		adis16201_reset(dev);
>> +		dev_err(dev, "device not playing ball -> reset");
>> +		msleep(ADIS16201_STARTUP_DELAY);
>> +		ret = adis16201_check_status(dev);
>> +		if (ret) {
>> +			dev_err(dev, "giving up");
>> +			goto err_ret;
>> +		}
>> +	}
>> +
>> +	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
>> +			st->us->chip_select, st->us->irq);
>> +
>> +err_ret:
>> +	return ret;
>> +}
>> +
>> +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16201_read_12bit_unsigned,
>> +		ADIS16201_SUPPLY_OUT);
>> +static IIO_CONST_ATTR(in_supply_scale, "0.00122");
>> +static IIO_DEV_ATTR_IN_RAW(0, adis16201_read_12bit_unsigned,
>> +		ADIS16201_AUX_ADC);
>> +static IIO_CONST_ATTR(in0_scale, "0.00061");
>> +
>> +static IIO_DEV_ATTR_ACCEL_X(adis16201_read_14bit_signed,
>> +		ADIS16201_XACCL_OUT);
>> +static IIO_DEV_ATTR_ACCEL_Y(adis16201_read_14bit_signed,
>> +		ADIS16201_YACCL_OUT);
>> +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
>> +		adis16201_read_12bit_signed,
>> +		adis16201_write_16bit,
>> +		ADIS16201_XACCL_OFFS);
>> +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
>> +		adis16201_read_12bit_signed,
>> +		adis16201_write_16bit,
>> +		ADIS16201_YACCL_OFFS);
>> +static IIO_CONST_ATTR(accel_scale, "0.4625");
>> +
>> +static IIO_DEV_ATTR_INCLI_X(adis16201_read_14bit_signed,
>> +		ADIS16201_XINCL_OUT);
>> +static IIO_DEV_ATTR_INCLI_Y(adis16201_read_14bit_signed,
>> +		ADIS16201_YINCL_OUT);
>> +static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
>> +		adis16201_read_9bit_signed,
>> +		adis16201_write_16bit,
>> +		ADIS16201_XACCL_OFFS);
>> +static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO,
>> +		adis16201_read_9bit_signed,
>> +		adis16201_write_16bit,
>> +		ADIS16201_YACCL_OFFS);
>> +static IIO_CONST_ATTR(incli_scale, "0.1");
>> +
>> +static IIO_DEV_ATTR_TEMP(adis16201_read_temp);
>> +static IIO_CONST_ATTR(temp_offset, "25");
>> +static IIO_CONST_ATTR(temp_scale, "-0.47");
>> +
>> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16201_write_reset, 0);
>> +
>> +static IIO_CONST_ATTR(name, "adis16201");
>> +
> 
> This is always a bad sign.  If there are no event attribute then we
> shouldn't be using the event infrastructure at all.  This one snuck
> into some of Analog's drivers a while back.  I'll clean them all out
> once we have ironed out some problems with the adis16350 mass merge
> of drivers (that may or may not be connnected to the replacement of
> this form of code!).
> 
> Still, not great to see it again, but it does work so lets leave it
> here for now.
>> +static struct attribute *adis16201_event_attributes[] = {
>> +	NULL
>> +};
>> +
>> +static struct attribute_group adis16201_event_attribute_group = {
>> +	.attrs = adis16201_event_attributes,
>> +};
>> +
>> +static struct attribute *adis16201_attributes[] = {
>> +	&iio_dev_attr_in_supply_raw.dev_attr.attr,
>> +	&iio_const_attr_in_supply_scale.dev_attr.attr,
>> +	&iio_dev_attr_temp.dev_attr.attr,
>> +	&iio_const_attr_temp_offset.dev_attr.attr,
>> +	&iio_const_attr_temp_scale.dev_attr.attr,
>> +	&iio_dev_attr_reset.dev_attr.attr,
>> +	&iio_const_attr_name.dev_attr.attr,
>> +	&iio_dev_attr_in0_raw.dev_attr.attr,
>> +	&iio_const_attr_in0_scale.dev_attr.attr,
>> +	&iio_dev_attr_accel_x_raw.dev_attr.attr,
>> +	&iio_dev_attr_accel_y_raw.dev_attr.attr,
>> +	&iio_dev_attr_accel_x_offset.dev_attr.attr,
>> +	&iio_dev_attr_accel_y_offset.dev_attr.attr,
>> +	&iio_const_attr_accel_scale.dev_attr.attr,
>> +	&iio_dev_attr_incli_x_raw.dev_attr.attr,
>> +	&iio_dev_attr_incli_y_raw.dev_attr.attr,
>> +	&iio_dev_attr_incli_x_offset.dev_attr.attr,
>> +	&iio_dev_attr_incli_y_offset.dev_attr.attr,
>> +	&iio_const_attr_incli_scale.dev_attr.attr,
>> +	NULL
>> +};
>> +
>> +static const struct attribute_group adis16201_attribute_group = {
>> +	.attrs = adis16201_attributes,
>> +};
>> +
>> +static int __devinit adis16201_probe(struct spi_device *spi)
>> +{
>> +	int ret, regdone = 0;
>> +	struct adis16201_state *st = kzalloc(sizeof *st, GFP_KERNEL);
>> +	if (!st) {
>> +		ret =  -ENOMEM;
>> +		goto error_ret;
>> +	}
>> +	/* this is only used for removal purposes */
>> +	spi_set_drvdata(spi, st);
>> +
>> +	/* Allocate the comms buffers */
>> +	st->rx = kzalloc(sizeof(*st->rx)*ADIS16201_MAX_RX, GFP_KERNEL);
>> +	if (st->rx == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_free_st;
>> +	}
>> +	st->tx = kzalloc(sizeof(*st->tx)*ADIS16201_MAX_TX, GFP_KERNEL);
>> +	if (st->tx == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_free_rx;
>> +	}
>> +	st->us = spi;
>> +	mutex_init(&st->buf_lock);
>> +	/* setup the industrialio driver allocated elements */
>> +	st->indio_dev = iio_allocate_device();
>> +	if (st->indio_dev == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_free_tx;
>> +	}
>> +
>> +	st->indio_dev->dev.parent = &spi->dev;
>> +	st->indio_dev->num_interrupt_lines = 1;
>> +	st->indio_dev->event_attrs = &adis16201_event_attribute_group;
>> +	st->indio_dev->attrs = &adis16201_attribute_group;
>> +	st->indio_dev->dev_data = (void *)(st);
>> +	st->indio_dev->driver_module = THIS_MODULE;
>> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +	ret = adis16201_configure_ring(st->indio_dev);
>> +	if (ret)
>> +		goto error_free_dev;
>> +
>> +	ret = iio_device_register(st->indio_dev);
>> +	if (ret)
>> +		goto error_unreg_ring_funcs;
>> +	regdone = 1;
>> +
>> +	ret = adis16201_initialize_ring(st->indio_dev->ring);
>> +	if (ret) {
>> +		printk(KERN_ERR "failed to initialize the ring\n");
>> +		goto error_unreg_ring_funcs;
>> +	}
>> +
>> +	if (spi->irq) {
>> +		ret = iio_register_interrupt_line(spi->irq,
>> +				st->indio_dev,
>> +				0,
>> +				IRQF_TRIGGER_RISING,
>> +				"adis16201");
>> +		if (ret)
>> +			goto error_uninitialize_ring;
>> +
>> +		ret = adis16201_probe_trigger(st->indio_dev);
>> +		if (ret)
>> +			goto error_unregister_line;
>> +	}
>> +
>> +	/* Get the device into a sane initial state */
>> +	ret = adis16201_initial_setup(st);
>> +	if (ret)
>> +		goto error_remove_trigger;
>> +	return 0;
>> +
>> +error_remove_trigger:
>> +	adis16201_remove_trigger(st->indio_dev);
>> +error_unregister_line:
>> +	if (spi->irq)
>> +		iio_unregister_interrupt_line(st->indio_dev, 0);
>> +error_uninitialize_ring:
>> +	adis16201_uninitialize_ring(st->indio_dev->ring);
>> +error_unreg_ring_funcs:
>> +	adis16201_unconfigure_ring(st->indio_dev);
>> +error_free_dev:
>> +	if (regdone)
>> +		iio_device_unregister(st->indio_dev);
>> +	else
>> +		iio_free_device(st->indio_dev);
>> +error_free_tx:
>> +	kfree(st->tx);
>> +error_free_rx:
>> +	kfree(st->rx);
>> +error_free_st:
>> +	kfree(st);
>> +error_ret:
>> +	return ret;
>> +}
>> +
>> +static int adis16201_remove(struct spi_device *spi)
>> +{
>> +	struct adis16201_state *st = spi_get_drvdata(spi);
>> +	struct iio_dev *indio_dev = st->indio_dev;
>> +
>> +	flush_scheduled_work();
>> +
>> +	adis16201_remove_trigger(indio_dev);
>> +	if (spi->irq)
>> +		iio_unregister_interrupt_line(indio_dev, 0);
>> +
>> +	adis16201_uninitialize_ring(indio_dev->ring);
>> +	iio_device_unregister(indio_dev);
>> +	adis16201_unconfigure_ring(indio_dev);
>> +	kfree(st->tx);
>> +	kfree(st->rx);
>> +	kfree(st);
>> +
>> +	return 0;
>> +}
>> +
>> +static struct spi_driver adis16201_driver = {
>> +	.driver = {
>> +		.name = "adis16201",
>> +		.owner = THIS_MODULE,
>> +	},
>> +	.probe = adis16201_probe,
>> +	.remove = __devexit_p(adis16201_remove),
>> +};
>> +
>> +static __init int adis16201_init(void)
>> +{
>> +	return spi_register_driver(&adis16201_driver);
>> +}
>> +module_init(adis16201_init);
>> +
>> +static __exit void adis16201_exit(void)
>> +{
>> +	spi_unregister_driver(&adis16201_driver);
>> +}
>> +module_exit(adis16201_exit);
>> +
>> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
>> +MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c
>> new file mode 100644
>> index 0000000..83fee28
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16201_ring.c
>> @@ -0,0 +1,260 @@
>> +#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/slab.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/list.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +#include "../ring_sw.h"
>> +#include "accel.h"
>> +#include "../trigger.h"
>> +#include "adis16201.h"
>> +
>> +/**
>> + * 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);
>> +}
> This is a hangover from Barry copying the lis3l02dq driver (where this
> sort of function made sense). Needs replacing with a generic endian
> fiddling alternative.  We can do that as a follow up patch.
>> +
>> +static IIO_SCAN_EL_C(supply, ADIS16201_SCAN_SUPPLY, IIO_UNSIGNED(12),
>> +		     ADIS16201_SUPPLY_OUT, NULL);
>> +static IIO_SCAN_EL_C(accel_x, ADIS16201_SCAN_ACC_X, IIO_SIGNED(14),
>> +		     ADIS16201_XACCL_OUT, NULL);
>> +static IIO_SCAN_EL_C(accel_y, ADIS16201_SCAN_ACC_Y, IIO_SIGNED(14),
>> +		     ADIS16201_YACCL_OUT, NULL);
>> +static IIO_SCAN_EL_C(aux_adc, ADIS16201_SCAN_AUX_ADC, IIO_UNSIGNED(12),
>> +		     ADIS16201_AUX_ADC, NULL);
>> +static IIO_SCAN_EL_C(temp, ADIS16201_SCAN_TEMP, IIO_UNSIGNED(12),
>> +		     ADIS16201_TEMP_OUT, NULL);
>> +static IIO_SCAN_EL_C(incli_x, ADIS16201_SCAN_INCLI_X, IIO_SIGNED(14),
>> +		     ADIS16201_XINCL_OUT, NULL);
>> +static IIO_SCAN_EL_C(incli_y, ADIS16201_SCAN_INCLI_Y, IIO_SIGNED(14),
>> +		     ADIS16201_YINCL_OUT, NULL);
>> +
>> +static IIO_SCAN_EL_TIMESTAMP(7);
>> +
>> +static struct attribute *adis16201_scan_el_attrs[] = {
>> +	&iio_scan_el_supply.dev_attr.attr,
>> +	&iio_scan_el_accel_x.dev_attr.attr,
>> +	&iio_scan_el_accel_y.dev_attr.attr,
>> +	&iio_scan_el_aux_adc.dev_attr.attr,
>> +	&iio_scan_el_temp.dev_attr.attr,
>> +	&iio_scan_el_incli_x.dev_attr.attr,
>> +	&iio_scan_el_incli_y.dev_attr.attr,
>> +	&iio_scan_el_timestamp.dev_attr.attr,
>> +	NULL,
>> +};
>> +
>> +static struct attribute_group adis16201_scan_el_group = {
>> +	.attrs = adis16201_scan_el_attrs,
>> +	.name = "scan_elements",
>> +};
>> +
>> +/**
>> + * adis16201_poll_func_th() top half interrupt handler called by trigger
>> + * @private_data:	iio_dev
>> + **/
>> +static void adis16201_poll_func_th(struct iio_dev *indio_dev, s64 time)
>> +{
>> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
>> +	st->last_timestamp = time;
>> +	schedule_work(&st->work_trigger_to_ring);
>> +}
>> +
>> +/**
>> + * adis16201_read_ring_data() read data registers which will be placed into ring
>> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
>> + * @rx: somewhere to pass back the value read
>> + **/
>> +static int adis16201_read_ring_data(struct device *dev, u8 *rx)
>> +{
>> +	struct spi_message msg;
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
>> +	struct spi_transfer xfers[ADIS16201_OUTPUTS + 1];
>> +	int ret;
>> +	int i;
>> +
>> +	mutex_lock(&st->buf_lock);
>> +
>> +	spi_message_init(&msg);
>> +
>> +	memset(xfers, 0, sizeof(xfers));
>> +	for (i = 0; i <= ADIS16201_OUTPUTS; i++) {
>> +		xfers[i].bits_per_word = 8;
>> +		xfers[i].cs_change = 1;
>> +		xfers[i].len = 2;
>> +		xfers[i].delay_usecs = 20;
>> +		xfers[i].tx_buf = st->tx + 2 * i;
>> +		st->tx[2 * i] = ADIS16201_READ_REG(ADIS16201_SUPPLY_OUT + 2 * i);
>> +		st->tx[2 * i + 1] = 0;
>> +		if (i >= 1)
>> +			xfers[i].rx_buf = rx + 2 * (i - 1);
>> +		spi_message_add_tail(&xfers[i], &msg);
>> +	}
> Might be nice to play some caching games here, but that's true of
> most of the drivers in the tree so definitely a job for another day!
>> +
>> +	ret = spi_sync(st->us, &msg);
>> +	if (ret)
>> +		dev_err(&st->us->dev, "problem when burst reading");
>> +
>> +	mutex_unlock(&st->buf_lock);
>> +
>> +	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 adis16201_trigger_bh_to_ring(struct work_struct *work_s)
>> +{
>> +	struct adis16201_state *st
>> +		= container_of(work_s, struct adis16201_state,
>> +			       work_trigger_to_ring);
>> +
>> +	int i = 0;
>> +	s16 *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;
>> +	}
>> +
>> +	if (st->indio_dev->scan_count)
>> +		if (adis16201_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
>> +			for (; i < st->indio_dev->scan_count; i++) {
>> +				data[i] = combine_8_to_16(st->rx[i*2+1],
>> +							  st->rx[i*2]);
>> +			}
> Unwanted brackets around the statement above.  Would have thorught checkpatch
> would moan about that.
>> +
>> +	/* 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(data);
>> +
>> +	return;
>> +}
>> +
>> +/* in these circumstances is it better to go with unaligned packing and
>> + * deal with the cost?*/
>> +static int adis16201_data_rdy_ring_preenable(struct iio_dev *indio_dev)
>> +{
>> +	size_t size;
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	/* 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, let timestamp aligned with sizeof(s64) */
> That's looks rather a long line.  Guessing checkpatch moans?
>> +				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
>> +					+ 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;
>> +}
>> +
> These can all be replaced with the generic calls, but that can trivially occur
> as a follow up patch (we did it for all the in tree drivers a while back).
>> +static int adis16201_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 adis16201_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;
>> +}
>> +
>> +void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
>> +{
>> +	kfree(indio_dev->pollfunc);
>> +	iio_sw_rb_free(indio_dev->ring);
>> +}
>> +
>> +int adis16201_configure_ring(struct iio_dev *indio_dev)
>> +{
>> +	int ret = 0;
>> +	struct adis16201_state *st = indio_dev->dev_data;
>> +	struct iio_ring_buffer *ring;
>> +	INIT_WORK(&st->work_trigger_to_ring, adis16201_trigger_bh_to_ring);
>> +	/* Set default scan mode */
>> +
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number);
>> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number);
>> +	indio_dev->scan_timestamp = true;
>> +
>> +	indio_dev->scan_el_attrs = &adis16201_scan_el_group;
>> +
>> +	ring = iio_sw_rb_allocate(indio_dev);
>> +	if (!ring) {
>> +		ret = -ENOMEM;
>> +		return ret;
>> +	}
>> +	indio_dev->ring = ring;
>> +	/* Effectively select the ring buffer implementation */
>> +	iio_ring_sw_register_funcs(&ring->access);
>> +	ring->preenable = &adis16201_data_rdy_ring_preenable;
>> +	ring->postenable = &adis16201_data_rdy_ring_postenable;
>> +	ring->predisable = &adis16201_data_rdy_ring_predisable;
>> +	ring->owner = THIS_MODULE;
>> +
>> +	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
>> +	if (indio_dev->pollfunc == NULL) {
>> +		ret = -ENOMEM;
>> +		goto error_iio_sw_rb_free;;
>> +	}
>> +	indio_dev->pollfunc->poll_func_main = &adis16201_poll_func_th;
>> +	indio_dev->pollfunc->private_data = indio_dev;
>> +	indio_dev->modes |= INDIO_RING_TRIGGERED;
>> +	return 0;
>> +
>> +error_iio_sw_rb_free:
>> +	iio_sw_rb_free(indio_dev->ring);
>> +	return ret;
>> +}
>> +
> 
> Nothing wrong with this driver, but this makes it pretty obvious that stubbs
> for iio_ring_buffer_register in the core would save us a few pointless functions
> in the case where we don't have ring support.   One for the todo list.
>> +int adis16201_initialize_ring(struct iio_ring_buffer *ring)
>> +{
>> +	return iio_ring_buffer_register(ring, 0);
>> +}
>> +
>> +void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
>> +{
>> +	iio_ring_buffer_unregister(ring);
>> +}
>> diff --git a/drivers/staging/iio/accel/adis16201_trigger.c b/drivers/staging/iio/accel/adis16201_trigger.c
>> new file mode 100644
>> index 0000000..8a9cea19
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16201_trigger.c
>> @@ -0,0 +1,122 @@
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/mutex.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/list.h>
>> +#include <linux/spi/spi.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +#include "../trigger.h"
>> +#include "adis16201.h"
>> +
> As stated above this should really directly setup and handle the interrupt
> itself rather than using the events infrastructure.  We can easily do that
> at a later date.
>> +/**
>> + * adis16201_data_rdy_trig_poll() the event handler for the data rdy trig
>> + **/
>> +static int adis16201_data_rdy_trig_poll(struct iio_dev *dev_info,
>> +				       int index,
>> +				       s64 timestamp,
>> +				       int no_test)
>> +{
>> +	struct adis16201_state *st = iio_dev_get_devdata(dev_info);
>> +	struct iio_trigger *trig = st->trig;
>> +
>> +	iio_trigger_poll(trig, timestamp);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +IIO_EVENT_SH(data_rdy_trig, &adis16201_data_rdy_trig_poll);
>> +
>> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
>> +
>> +static struct attribute *adis16201_trigger_attrs[] = {
>> +	&dev_attr_name.attr,
>> +	NULL,
>> +};
>> +
>> +static const struct attribute_group adis16201_trigger_attr_group = {
>> +	.attrs = adis16201_trigger_attrs,
>> +};
>> +
>> +/**
>> + * adis16201_data_rdy_trigger_set_state() set datardy interrupt state
>> + **/
>> +static int adis16201_data_rdy_trigger_set_state(struct iio_trigger *trig,
>> +						bool state)
>> +{
>> +	struct adis16201_state *st = trig->private_data;
>> +	struct iio_dev *indio_dev = st->indio_dev;
>> +	int ret = 0;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
>> +	ret = adis16201_set_irq(&st->indio_dev->dev, state);
>> +	if (state == false) {
>> +		iio_remove_event_from_list(&iio_event_data_rdy_trig,
>> +					   &indio_dev->interrupts[0]
>> +					   ->ev_list);
>> +		flush_scheduled_work();
>> +	} else {
>> +		iio_add_event_to_list(&iio_event_data_rdy_trig,
>> +				      &indio_dev->interrupts[0]->ev_list);
>> +	}
>> +	return ret;
>> +}
>> +
>> +/**
>> + * adis16201_trig_try_reen() try renabling irq for data rdy trigger
>> + * @trig:	the datardy trigger
>> + **/
>> +static int adis16201_trig_try_reen(struct iio_trigger *trig)
>> +{
>> +	struct adis16201_state *st = trig->private_data;
>> +	enable_irq(st->us->irq);
>> +	return 0;
>> +}
>> +
>> +int adis16201_probe_trigger(struct iio_dev *indio_dev)
>> +{
>> +	int ret;
>> +	struct adis16201_state *st = indio_dev->dev_data;
>> +
>> +	st->trig = iio_allocate_trigger();
>> +	st->trig->name = kasprintf(GFP_KERNEL,
>> +				"adis16201-dev%d",
>> +				indio_dev->id);
>> +	if (!st->trig->name) {
>> +		ret = -ENOMEM;
>> +		goto error_free_trig;
>> +	}
>> +	st->trig->dev.parent = &st->us->dev;
>> +	st->trig->owner = THIS_MODULE;
>> +	st->trig->private_data = st;
>> +	st->trig->set_trigger_state = &adis16201_data_rdy_trigger_set_state;
>> +	st->trig->try_reenable = &adis16201_trig_try_reen;
>> +	st->trig->control_attrs = &adis16201_trigger_attr_group;
>> +	ret = iio_trigger_register(st->trig);
>> +
>> +	/* select default trigger */
>> +	indio_dev->trig = st->trig;
>> +	if (ret)
>> +		goto error_free_trig_name;
>> +
>> +	return 0;
>> +
>> +error_free_trig_name:
>> +	kfree(st->trig->name);
>> +error_free_trig:
>> +	iio_free_trigger(st->trig);
>> +
>> +	return ret;
>> +}
>> +
>> +void adis16201_remove_trigger(struct iio_dev *indio_dev)
>> +{
>> +	struct adis16201_state *state = indio_dev->dev_data;
>> +
>> +	iio_trigger_unregister(state->trig);
>> +	kfree(state->trig->name);
>> +	iio_free_trigger(state->trig);
>> +}
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/3] staging: iio: new adis16203 driver
  2010-10-23 10:49 ` [PATCH 2/3] staging: iio: new adis16203 driver Mike Frysinger
@ 2010-10-23 17:22   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-23 17:22 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Barry Song

On 10/23/10 11:49, Mike Frysinger wrote:
> From: Barry Song <barry.song@analog.com>
> 
> IIO driver for Programmable 360 Degrees Inclinometer adis16203 parts.
> 

Again, nice clean driver. Comments are pretty much the same as for the
previous review, so please transfer those over (so I can be lazy :)

Looking at this driver, it 'may' make sense to merge it with the
16201 driver at a later date.  There are clearly some differences, but
a lot of similarities as well.  That can happen whenever anyone has
the time and inclination to do it.  It will be vaguely similar to
the adis16300/350/400 merge that is in progress currently.  Looks
to me like Analog carefully kept most register addresses the same
across these two parts (where they overlap).

I clearly didn't review pervious one thoroughly enough as I missed
some issues with naming of and missing attributes for the scan_elements
group.  We have to have appropriate index and type attributes for
all those channels for userspace to have any idea what to do with
them. Please fix this before sending on.  Otherwise I'm happy with this
patch and will be away for a while so please send on to Greg KH
(and with the changes requested feel free to add
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
as long as it builds fine against staging-next)

> Signed-off-by: Barry Song <barry.song@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/accel/Kconfig             |    9 +
>  drivers/staging/iio/accel/Makefile            |    4 +
>  drivers/staging/iio/accel/adis16203.h         |  143 +++++++
>  drivers/staging/iio/accel/adis16203_core.c    |  568 +++++++++++++++++++++++++
>  drivers/staging/iio/accel/adis16203_ring.c    |  255 +++++++++++
>  drivers/staging/iio/accel/adis16203_trigger.c |  122 ++++++
>  6 files changed, 1101 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/accel/adis16203.h
>  create mode 100644 drivers/staging/iio/accel/adis16203_core.c
>  create mode 100644 drivers/staging/iio/accel/adis16203_ring.c
>  create mode 100644 drivers/staging/iio/accel/adis16203_trigger.c
> 
> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
> index 0b87557..cc057c6 100644
> --- a/drivers/staging/iio/accel/Kconfig
> +++ b/drivers/staging/iio/accel/Kconfig
> @@ -12,6 +12,15 @@ config ADIS16201
>  	  Say yes here to build support for Analog Devices adis16201 dual-axis
>  	  digital inclinometer and accelerometer.
>  
> +config ADIS16203
> +	tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"
> +	depends on SPI
> +	select IIO_TRIGGER if IIO_RING_BUFFER
> +	select IIO_SW_RING if IIO_RING_BUFFER
> +	help
> +	  Say yes here to build support for Analog Devices adis16203 Programmable
> +	  360 Degrees Inclinometer.
> +
>  config ADIS16209
>  	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
>  	depends on SPI
> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
> index 4a22a01..76cc92f 100644
> --- a/drivers/staging/iio/accel/Makefile
> +++ b/drivers/staging/iio/accel/Makefile
> @@ -6,6 +6,10 @@ adis16201-y             := adis16201_core.o
>  adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
>  obj-$(CONFIG_ADIS16201) += adis16201.o
>  
> +adis16203-y             := adis16203_core.o
> +adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
> +obj-$(CONFIG_ADIS16203) += adis16203.o
> +
>  adis16209-y             := adis16209_core.o
>  adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
>  obj-$(CONFIG_ADIS16209) += adis16209.o
> diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h
> new file mode 100644
> index 0000000..f0a1d01
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16203.h
> @@ -0,0 +1,143 @@
> +#ifndef SPI_ADIS16203_H_
> +#define SPI_ADIS16203_H_
> +
> +#define ADIS16203_STARTUP_DELAY	220 /* ms */
> +
> +#define ADIS16203_READ_REG(a)    a
> +#define ADIS16203_WRITE_REG(a) ((a) | 0x80)
> +
> +#define ADIS16203_FLASH_CNT      0x00 /* Flash memory write count */
> +#define ADIS16203_SUPPLY_OUT     0x02 /* Output, power supply */
> +#define ADIS16203_AUX_ADC        0x08 /* Output, auxiliary ADC input */
> +#define ADIS16203_TEMP_OUT       0x0A /* Output, temperature */
> +#define ADIS16203_XINCL_OUT      0x0C /* Output, x-axis inclination */
> +#define ADIS16203_YINCL_OUT      0x0E /* Output, y-axis inclination */
> +#define ADIS16203_INCL_NULL      0x18 /* Incline null calibration */
> +#define ADIS16203_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
> +#define ADIS16203_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
> +#define ADIS16203_ALM_SMPL1      0x24 /* Alarm 1, sample period */
> +#define ADIS16203_ALM_SMPL2      0x26 /* Alarm 2, sample period */
> +#define ADIS16203_ALM_CTRL       0x28 /* Alarm control */
> +#define ADIS16203_AUX_DAC        0x30 /* Auxiliary DAC data */
> +#define ADIS16203_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
> +#define ADIS16203_MSC_CTRL       0x34 /* Miscellaneous control */
> +#define ADIS16203_SMPL_PRD       0x36 /* Internal sample period (rate) control */
> +#define ADIS16203_AVG_CNT        0x38 /* Operation, filter configuration */
> +#define ADIS16203_SLP_CNT        0x3A /* Operation, sleep mode control */
> +#define ADIS16203_DIAG_STAT      0x3C /* Diagnostics, system status register */
> +#define ADIS16203_GLOB_CMD       0x3E /* Operation, system command register */
> +
> +#define ADIS16203_OUTPUTS        5
> +
> +/* MSC_CTRL */
> +#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
> +#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN	(1 << 9)  /* Reverses rotation of both inclination outputs */
> +#define ADIS16203_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
> +#define ADIS16203_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
> +#define ADIS16203_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
> +#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
> +
> +/* DIAG_STAT */
> +#define ADIS16203_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16203_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16203_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag */
> +#define ADIS16203_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
> +#define ADIS16203_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
> +#define ADIS16203_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
> +#define ADIS16203_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
> +
> +/* GLOB_CMD */
> +#define ADIS16203_GLOB_CMD_SW_RESET	(1<<7)
> +#define ADIS16203_GLOB_CMD_CLEAR_STAT	(1<<4)
> +#define ADIS16203_GLOB_CMD_FACTORY_CAL	(1<<1)
> +
> +#define ADIS16203_MAX_TX 24
These two are too large, I think based on quick glance that they
should be 12 and 10.  Please check
> +#define ADIS16203_MAX_RX 24
> +
> +#define ADIS16203_ERROR_ACTIVE          (1<<14)
> +
> +/**
> + * struct adis16203_state - device instance specific data
> + * @us:			actual spi_device
> + * @work_trigger_to_ring: bh for triggered event handling
> + * @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 adis16203_state {
> +	struct spi_device		*us;
> +	struct work_struct		work_trigger_to_ring;
> +	s64				last_timestamp;
> +	struct iio_dev			*indio_dev;
> +	struct iio_trigger		*trig;
> +	u8				*tx;
> +	u8				*rx;
> +	struct mutex			buf_lock;
> +};
> +
> +int adis16203_set_irq(struct device *dev, bool enable);
> +
> +#ifdef CONFIG_IIO_RING_BUFFER
> +enum adis16203_scan {
> +	ADIS16203_SCAN_SUPPLY,
> +	ADIS16203_SCAN_AUX_ADC,
> +	ADIS16203_SCAN_TEMP,
> +	ADIS16203_SCAN_INCLI_X,
> +	ADIS16203_SCAN_INCLI_Y,
> +};
> +
> +void adis16203_remove_trigger(struct iio_dev *indio_dev);
> +int adis16203_probe_trigger(struct iio_dev *indio_dev);
> +
> +ssize_t adis16203_read_data_from_ring(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf);
> +
> +int adis16203_configure_ring(struct iio_dev *indio_dev);
> +void adis16203_unconfigure_ring(struct iio_dev *indio_dev);
> +
> +int adis16203_initialize_ring(struct iio_ring_buffer *ring);
> +void adis16203_uninitialize_ring(struct iio_ring_buffer *ring);
> +#else /* CONFIG_IIO_RING_BUFFER */
> +
> +static inline void adis16203_remove_trigger(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16203_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline ssize_t
> +adis16203_read_data_from_ring(struct device *dev,
> +			      struct device_attribute *attr,
> +			      char *buf)
> +{
> +	return 0;
> +}
> +
> +static int adis16203_configure_ring(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16203_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16203_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +}
> +
> +#endif /* CONFIG_IIO_RING_BUFFER */
> +#endif /* SPI_ADIS16203_H_ */
> diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
> new file mode 100644
> index 0000000..81eeb05
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16203_core.c
> @@ -0,0 +1,568 @@
> +/*
> + * ADIS16203 Programmable Digital Vibration Sensor driver
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "accel.h"
> +#include "inclinometer.h"
> +#include "../gyro/gyro.h"
> +#include "../adc/adc.h"
> +
> +#include "adis16203.h"
> +
Used only once (and that's not all that useful!).
Move into where it is used or remove entirely.

> +#define DRIVER_NAME		"adis16203"
> +
> +static int adis16203_check_status(struct device *dev);
> +
> +/**
> + * adis16203_spi_write_reg_8() - write single byte to a register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the register to be written
> + * @val: the value to write
> + **/
> +static int adis16203_spi_write_reg_8(struct device *dev,
> +		u8 reg_address,
> +		u8 val)
> +{
> +	int ret;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16203_WRITE_REG(reg_address);
> +	st->tx[1] = val;
> +
> +	ret = spi_write(st->us, st->tx, 2);
> +	mutex_unlock(&st->buf_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * adis16203_spi_write_reg_16() - write 2 bytes to a pair of registers
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: value to be written
> + **/
> +static int adis16203_spi_write_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 value)
> +{
> +	int ret;
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		}, {
> +			.tx_buf = st->tx + 2,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16203_WRITE_REG(lower_reg_address);
> +	st->tx[1] = value & 0xFF;
> +	st->tx[2] = ADIS16203_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;
> +}
> +
> +/**
> + * adis16203_spi_read_reg_16() - read 2 bytes from a 16-bit register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: somewhere to pass back the value read
> + **/
> +static int adis16203_spi_read_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 *val)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
> +	int ret;
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		}, {
> +			.rx_buf = st->rx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16203_READ_REG(lower_reg_address);
> +	st->tx[1] = 0;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	ret = spi_sync(st->us, &msg);
> +	if (ret) {
> +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
> +				lower_reg_address);
> +		goto error_ret;
> +	}
> +	*val = (st->rx[0] << 8) | st->rx[1];
> +
> +error_ret:
> +	mutex_unlock(&st->buf_lock);
> +	return ret;
> +}
> +
> +static ssize_t adis16203_read_12bit_unsigned(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int ret;
> +	u16 val = 0;
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +
> +	ret = adis16203_spi_read_reg_16(dev, this_attr->address, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & ADIS16203_ERROR_ACTIVE)
> +		adis16203_check_status(dev);
> +
> +	return sprintf(buf, "%u\n", val & 0x0FFF);
> +}
> +
> +static ssize_t adis16203_read_temp(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	ssize_t ret;
> +	u16 val;
> +
> +	/* Take the iio_dev status lock */
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16203_spi_read_reg_16(dev, ADIS16203_TEMP_OUT, (u16 *)&val);
> +	if (ret)
> +		goto error_ret;
> +
> +	if (val & ADIS16203_ERROR_ACTIVE)
> +		adis16203_check_status(dev);
> +
> +	val &= 0xFFF;
> +	ret = sprintf(buf, "%d\n", val);
> +
> +error_ret:
> +	mutex_unlock(&indio_dev->mlock);
> +	return ret;
> +}
> +
> +static ssize_t adis16203_read_14bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16203_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16203_ERROR_ACTIVE)
> +			adis16203_check_status(dev);
> +
> +		val = ((s16)(val << 2) >> 2);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16203_write_16bit(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 = adis16203_spi_write_reg_16(dev, this_attr->address, val);
> +
> +error_ret:
> +	return ret ? ret : len;
> +}
> +
> +static int adis16203_reset(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16203_spi_write_reg_8(dev,
> +			ADIS16203_GLOB_CMD,
> +			ADIS16203_GLOB_CMD_SW_RESET);
> +	if (ret)
> +		dev_err(dev, "problem resetting device");
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16203_write_reset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t len)
> +{
> +	if (len < 1)
> +		return -EINVAL;
> +	switch (buf[0]) {
> +	case '1':
> +	case 'y':
> +	case 'Y':
> +		return adis16203_reset(dev);
> +	}
> +	return -EINVAL;
> +}
> +
> +int adis16203_set_irq(struct device *dev, bool enable)
> +{
> +	int ret = 0;
> +	u16 msc;
> +
> +	ret = adis16203_spi_read_reg_16(dev, ADIS16203_MSC_CTRL, &msc);
> +	if (ret)
> +		goto error_ret;
> +
> +	msc |= ADIS16203_MSC_CTRL_ACTIVE_HIGH;
> +	msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_DIO1;
> +	if (enable)
> +		msc |= ADIS16203_MSC_CTRL_DATA_RDY_EN;
> +	else
> +		msc &= ~ADIS16203_MSC_CTRL_DATA_RDY_EN;
> +
> +	ret = adis16203_spi_write_reg_16(dev, ADIS16203_MSC_CTRL, msc);
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16203_check_status(struct device *dev)
> +{
> +	u16 status;
> +	int ret;
> +
> +	ret = adis16203_spi_read_reg_16(dev, ADIS16203_DIAG_STAT, &status);
> +	if (ret < 0) {
> +		dev_err(dev, "Reading status failed\n");
> +		goto error_ret;
> +	}
> +	ret = status & 0x1F;
> +
> +	if (status & ADIS16203_DIAG_STAT_SELFTEST_FAIL)
> +		dev_err(dev, "Self test failure\n");
> +	if (status & ADIS16203_DIAG_STAT_SPI_FAIL)
> +		dev_err(dev, "SPI failure\n");
> +	if (status & ADIS16203_DIAG_STAT_FLASH_UPT)
> +		dev_err(dev, "Flash update failed\n");
> +	if (status & ADIS16203_DIAG_STAT_POWER_HIGH)
> +		dev_err(dev, "Power supply above 3.625V\n");
> +	if (status & ADIS16203_DIAG_STAT_POWER_LOW)
> +		dev_err(dev, "Power supply below 3.15V\n");
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16203_self_test(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16203_spi_write_reg_16(dev,
> +			ADIS16203_MSC_CTRL,
> +			ADIS16203_MSC_CTRL_SELF_TEST_EN);
> +	if (ret) {
> +		dev_err(dev, "problem starting self test");
> +		goto err_ret;
> +	}
> +
> +	adis16203_check_status(dev);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static int adis16203_initial_setup(struct adis16203_state *st)
> +{
> +	int ret;
> +	struct device *dev = &st->indio_dev->dev;
> +
> +	/* Disable IRQ */
> +	ret = adis16203_set_irq(dev, false);
> +	if (ret) {
> +		dev_err(dev, "disable irq failed");
> +		goto err_ret;
> +	}
> +
> +	/* Do self test */
> +	ret = adis16203_self_test(dev);
> +	if (ret) {
> +		dev_err(dev, "self test failure");
> +		goto err_ret;
> +	}
> +
> +	/* Read status register to check the result */
> +	ret = adis16203_check_status(dev);
> +	if (ret) {
> +		adis16203_reset(dev);
> +		dev_err(dev, "device not playing ball -> reset");
> +		msleep(ADIS16203_STARTUP_DELAY);
> +		ret = adis16203_check_status(dev);
> +		if (ret) {
> +			dev_err(dev, "giving up");
> +			goto err_ret;
> +		}
> +	}
> +
> +	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
> +			st->us->chip_select, st->us->irq);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16203_read_12bit_unsigned,
> +		ADIS16203_SUPPLY_OUT);
> +static IIO_CONST_ATTR(in_supply_scale, "0.00122");
> +static IIO_DEV_ATTR_IN_RAW(0, adis16203_read_12bit_unsigned,
> +		ADIS16203_AUX_ADC);
> +static IIO_CONST_ATTR(in0_scale, "0.00061");
> +
> +static IIO_DEV_ATTR_INCLI_X(adis16203_read_14bit_signed,
> +		ADIS16203_XINCL_OUT);
> +static IIO_DEV_ATTR_INCLI_Y(adis16203_read_14bit_signed,
> +		ADIS16203_YINCL_OUT);
> +static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16203_read_14bit_signed,
> +		adis16203_write_16bit,
> +		ADIS16203_INCL_NULL);
> +static IIO_CONST_ATTR(incli_scale, "0.025");
> +
> +static IIO_DEV_ATTR_TEMP(adis16203_read_temp);
> +static IIO_CONST_ATTR(temp_offset, "25");
> +static IIO_CONST_ATTR(temp_scale, "-0.47");
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16203_write_reset, 0);
> +
> +static IIO_CONST_ATTR(name, "adis16203");
> +
> +static struct attribute *adis16203_event_attributes[] = {
> +	NULL
> +};
> +
> +static struct attribute_group adis16203_event_attribute_group = {
> +	.attrs = adis16203_event_attributes,
> +};
> +
> +static struct attribute *adis16203_attributes[] = {
> +	&iio_dev_attr_in_supply_raw.dev_attr.attr,
> +	&iio_const_attr_in_supply_scale.dev_attr.attr,
> +	&iio_dev_attr_temp.dev_attr.attr,
> +	&iio_const_attr_temp_offset.dev_attr.attr,
> +	&iio_const_attr_temp_scale.dev_attr.attr,
> +	&iio_dev_attr_reset.dev_attr.attr,
> +	&iio_const_attr_name.dev_attr.attr,
> +	&iio_dev_attr_in0_raw.dev_attr.attr,
> +	&iio_const_attr_in0_scale.dev_attr.attr,
> +	&iio_dev_attr_incli_x_raw.dev_attr.attr,
> +	&iio_dev_attr_incli_y_raw.dev_attr.attr,
> +	&iio_dev_attr_incli_x_offset.dev_attr.attr,
> +	&iio_const_attr_incli_scale.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adis16203_attribute_group = {
> +	.attrs = adis16203_attributes,
> +};
> +
> +static int __devinit adis16203_probe(struct spi_device *spi)
> +{
> +	int ret, regdone = 0;
> +	struct adis16203_state *st = kzalloc(sizeof *st, GFP_KERNEL);
> +	if (!st) {
> +		ret =  -ENOMEM;
> +		goto error_ret;
> +	}
> +	/* this is only used for removal purposes */
> +	spi_set_drvdata(spi, st);
> +
> +	/* Allocate the comms buffers */
> +	st->rx = kzalloc(sizeof(*st->rx)*ADIS16203_MAX_RX, GFP_KERNEL);
> +	if (st->rx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_st;
> +	}
> +	st->tx = kzalloc(sizeof(*st->tx)*ADIS16203_MAX_TX, GFP_KERNEL);
> +	if (st->tx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_rx;
> +	}
> +	st->us = spi;
> +	mutex_init(&st->buf_lock);
> +	/* setup the industrialio driver allocated elements */
> +	st->indio_dev = iio_allocate_device();
> +	if (st->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_tx;
> +	}
> +
> +	st->indio_dev->dev.parent = &spi->dev;
> +	st->indio_dev->num_interrupt_lines = 1;
> +	st->indio_dev->event_attrs = &adis16203_event_attribute_group;
> +	st->indio_dev->attrs = &adis16203_attribute_group;
> +	st->indio_dev->dev_data = (void *)(st);
> +	st->indio_dev->driver_module = THIS_MODULE;
> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = adis16203_configure_ring(st->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = iio_device_register(st->indio_dev);
> +	if (ret)
> +		goto error_unreg_ring_funcs;
> +	regdone = 1;
> +
> +	ret = adis16203_initialize_ring(st->indio_dev->ring);
> +	if (ret) {
> +		printk(KERN_ERR "failed to initialize the ring\n");
> +		goto error_unreg_ring_funcs;
> +	}
> +
> +	if (spi->irq) {
> +		ret = iio_register_interrupt_line(spi->irq,
> +				st->indio_dev,
> +				0,
> +				IRQF_TRIGGER_RISING,
> +				"adis16203");
> +		if (ret)
> +			goto error_uninitialize_ring;
> +
> +		ret = adis16203_probe_trigger(st->indio_dev);
> +		if (ret)
> +			goto error_unregister_line;
> +	}
> +
> +	/* Get the device into a sane initial state */
> +	ret = adis16203_initial_setup(st);
> +	if (ret)
> +		goto error_remove_trigger;
> +	return 0;
> +
> +error_remove_trigger:
> +	adis16203_remove_trigger(st->indio_dev);
> +error_unregister_line:
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(st->indio_dev, 0);
> +error_uninitialize_ring:
> +	adis16203_uninitialize_ring(st->indio_dev->ring);
> +error_unreg_ring_funcs:
> +	adis16203_unconfigure_ring(st->indio_dev);
> +error_free_dev:
> +	if (regdone)
> +		iio_device_unregister(st->indio_dev);
> +	else
> +		iio_free_device(st->indio_dev);
> +error_free_tx:
> +	kfree(st->tx);
> +error_free_rx:
> +	kfree(st->rx);
> +error_free_st:
> +	kfree(st);
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16203_remove(struct spi_device *spi)
> +{
> +	struct adis16203_state *st = spi_get_drvdata(spi);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +
> +	flush_scheduled_work();
> +
> +	adis16203_remove_trigger(indio_dev);
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +
> +	adis16203_uninitialize_ring(indio_dev->ring);
> +	iio_device_unregister(indio_dev);
> +	adis16203_unconfigure_ring(indio_dev);
> +	kfree(st->tx);
> +	kfree(st->rx);
> +	kfree(st);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver adis16203_driver = {
> +	.driver = {
> +		.name = "adis16203",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = adis16203_probe,
> +	.remove = __devexit_p(adis16203_remove),
> +};
> +
> +static __init int adis16203_init(void)
> +{
> +	return spi_register_driver(&adis16203_driver);
> +}
> +module_init(adis16203_init);
> +
> +static __exit void adis16203_exit(void)
> +{
> +	spi_unregister_driver(&adis16203_driver);
> +}
> +module_exit(adis16203_exit);
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c
> new file mode 100644
> index 0000000..77166a0
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16203_ring.c
> @@ -0,0 +1,255 @@
> +#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/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../ring_sw.h"
> +#include "accel.h"
> +#include "../trigger.h"
> +#include "adis16203.h"
> +
> +/**
> + * 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);
> +}
> +
> +static IIO_SCAN_EL_C(supply, ADIS16203_SCAN_SUPPLY, IIO_UNSIGNED(12),
> +		     ADIS16203_SUPPLY_OUT, NULL);
> +static IIO_SCAN_EL_C(aux_adc, ADIS16203_SCAN_AUX_ADC, IIO_UNSIGNED(12),
> +		     ADIS16203_AUX_ADC, NULL);
> +static IIO_SCAN_EL_C(temp, ADIS16203_SCAN_TEMP, IIO_UNSIGNED(12),
> +		     ADIS16203_TEMP_OUT, NULL);
> +static IIO_SCAN_EL_C(incli_x, ADIS16203_SCAN_INCLI_X, IIO_SIGNED(14),
> +		     ADIS16203_XINCL_OUT, NULL);
> +static IIO_SCAN_EL_C(incli_y, ADIS16203_SCAN_INCLI_Y, IIO_SIGNED(14),
> +		     ADIS16203_YINCL_OUT, NULL);
> +
> +static IIO_SCAN_EL_TIMESTAMP(5);
> +
> +static struct attribute *adis16203_scan_el_attrs[] = {
> +	&iio_scan_el_supply.dev_attr.attr,
Guess I missed this in the other review, but this naming isn't right.
We don't allow aux_adc as a scan element, it's either in0 or
in1 (depending on whether we have in0_supply - which is needed to
associate events if anyone ever adds event support to this driver.).

I also failed to notice the absence in index and type for each of
these parameters both of which are obligatory now.


> +	&iio_scan_el_aux_adc.dev_attr.attr,
> +	&iio_scan_el_temp.dev_attr.attr,
> +	&iio_scan_el_incli_x.dev_attr.attr,
> +	&iio_scan_el_incli_y.dev_attr.attr,
> +	&iio_scan_el_timestamp.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group adis16203_scan_el_group = {
> +	.attrs = adis16203_scan_el_attrs,
> +	.name = "scan_elements",
> +};
> +
> +/**
> + * adis16203_poll_func_th() top half interrupt handler called by trigger
> + * @private_data:	iio_dev
> + **/
> +static void adis16203_poll_func_th(struct iio_dev *indio_dev, s64 timestamp)
> +{
> +	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
> +	st->last_timestamp = timestamp;
> +	schedule_work(&st->work_trigger_to_ring);
> +}
> +
> +/**
> + * adis16203_read_ring_data() read data registers which will be placed into ring
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @rx: somewhere to pass back the value read
> + **/
> +static int adis16203_read_ring_data(struct device *dev, u8 *rx)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16203_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[ADIS16203_OUTPUTS + 1];
> +	int ret;
> +	int i;
> +
> +	mutex_lock(&st->buf_lock);
> +
> +	spi_message_init(&msg);
> +
> +	memset(xfers, 0, sizeof(xfers));
> +	for (i = 0; i <= ADIS16203_OUTPUTS; i++) {
> +		xfers[i].bits_per_word = 8;
> +		xfers[i].cs_change = 1;
> +		xfers[i].len = 2;
> +		xfers[i].delay_usecs = 20;
> +		xfers[i].tx_buf = st->tx + 2 * i;
> +		if (i < 1) /* SUPPLY_OUT: 0x02, AUX_ADC: 0x08 */
> +			st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i);
> +		else
> +			st->tx[2 * i] = ADIS16203_READ_REG(ADIS16203_SUPPLY_OUT + 2 * i + 6);
> +		st->tx[2 * i + 1] = 0;
> +		if (i >= 1)
> +			xfers[i].rx_buf = rx + 2 * (i - 1);
> +		spi_message_add_tail(&xfers[i], &msg);
> +	}
> +
> +	ret = spi_sync(st->us, &msg);
> +	if (ret)
> +		dev_err(&st->us->dev, "problem when burst reading");
> +
> +	mutex_unlock(&st->buf_lock);
> +
> +	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 adis16203_trigger_bh_to_ring(struct work_struct *work_s)
> +{
> +	struct adis16203_state *st
> +		= container_of(work_s, struct adis16203_state,
> +			       work_trigger_to_ring);
> +
> +	int i = 0;
> +	s16 *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;
> +	}
> +
> +	if (st->indio_dev->scan_count)
> +		if (adis16203_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
> +			for (; i < st->indio_dev->scan_count; i++) {
> +				data[i] = combine_8_to_16(st->rx[i*2+1],
> +							  st->rx[i*2]);
> +			}
> +
> +	/* 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(data);
> +
> +	return;
> +}
> +
> +/* in these circumstances is it better to go with unaligned packing and
> + * deal with the cost?*/
> +static int adis16203_data_rdy_ring_preenable(struct iio_dev *indio_dev)
> +{
> +	size_t size;
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* 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, let timestamp aligned with sizeof(s64) */
> +				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
> +					+ 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;
> +}
> +
As before, there are generic helpers for these now.
> +static int adis16203_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 adis16203_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;
> +}
> +
> +void adis16203_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +	kfree(indio_dev->pollfunc);
> +	iio_sw_rb_free(indio_dev->ring);
> +}
> +
> +int adis16203_configure_ring(struct iio_dev *indio_dev)
> +{
> +	int ret = 0;
> +	struct adis16203_state *st = indio_dev->dev_data;
> +	struct iio_ring_buffer *ring;
> +	INIT_WORK(&st->work_trigger_to_ring, adis16203_trigger_bh_to_ring);
> +	/* Set default scan mode */
> +
> +	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number);
> +	indio_dev->scan_timestamp = true;
> +
> +	indio_dev->scan_el_attrs = &adis16203_scan_el_group;
> +
> +	ring = iio_sw_rb_allocate(indio_dev);
> +	if (!ring) {
> +		ret = -ENOMEM;
> +		return ret;
> +	}
> +	indio_dev->ring = ring;
> +	/* Effectively select the ring buffer implementation */
> +	iio_ring_sw_register_funcs(&ring->access);
> +	ring->preenable = &adis16203_data_rdy_ring_preenable;
> +	ring->postenable = &adis16203_data_rdy_ring_postenable;
> +	ring->predisable = &adis16203_data_rdy_ring_predisable;
> +	ring->owner = THIS_MODULE;
> +
> +	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
> +	if (indio_dev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_iio_sw_rb_free;;
> +	}
> +	indio_dev->pollfunc->poll_func_main = &adis16203_poll_func_th;
> +	indio_dev->pollfunc->private_data = indio_dev;
> +	indio_dev->modes |= INDIO_RING_TRIGGERED;
> +	return 0;
> +
> +error_iio_sw_rb_free:
> +	iio_sw_rb_free(indio_dev->ring);
> +	return ret;
> +}
> +
> +int adis16203_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return iio_ring_buffer_register(ring, 0);
> +}
> +
> +void adis16203_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +	iio_ring_buffer_unregister(ring);
> +}
> diff --git a/drivers/staging/iio/accel/adis16203_trigger.c b/drivers/staging/iio/accel/adis16203_trigger.c
> new file mode 100644
> index 0000000..50be51c
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16203_trigger.c
> @@ -0,0 +1,122 @@
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +#include <linux/spi/spi.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../trigger.h"
> +#include "adis16203.h"
> +
> +/**
> + * adis16203_data_rdy_trig_poll() the event handler for the data rdy trig
> + **/
> +static int adis16203_data_rdy_trig_poll(struct iio_dev *dev_info,
> +				       int index,
> +				       s64 timestamp,
> +				       int no_test)
> +{
> +	struct adis16203_state *st = iio_dev_get_devdata(dev_info);
> +	struct iio_trigger *trig = st->trig;
> +
> +	iio_trigger_poll(trig, timestamp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +IIO_EVENT_SH(data_rdy_trig, &adis16203_data_rdy_trig_poll);
> +
> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
> +
> +static struct attribute *adis16203_trigger_attrs[] = {
> +	&dev_attr_name.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group adis16203_trigger_attr_group = {
> +	.attrs = adis16203_trigger_attrs,
> +};
> +
> +/**
> + * adis16203_data_rdy_trigger_set_state() set datardy interrupt state
> + **/
> +static int adis16203_data_rdy_trigger_set_state(struct iio_trigger *trig,
> +						bool state)
> +{
> +	struct adis16203_state *st = trig->private_data;
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	int ret = 0;
> +
> +	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
> +	ret = adis16203_set_irq(&st->indio_dev->dev, state);
> +	if (state == false) {
> +		iio_remove_event_from_list(&iio_event_data_rdy_trig,
> +					   &indio_dev->interrupts[0]
> +					   ->ev_list);
> +		flush_scheduled_work();
> +	} else {
> +		iio_add_event_to_list(&iio_event_data_rdy_trig,
> +				      &indio_dev->interrupts[0]->ev_list);
> +	}
> +	return ret;
> +}
> +
> +/**
> + * adis16203_trig_try_reen() try renabling irq for data rdy trigger
> + * @trig:	the datardy trigger
> + **/
> +static int adis16203_trig_try_reen(struct iio_trigger *trig)
> +{
> +	struct adis16203_state *st = trig->private_data;
> +	enable_irq(st->us->irq);
> +	return 0;
> +}
> +
> +int adis16203_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct adis16203_state *st = indio_dev->dev_data;
> +
> +	st->trig = iio_allocate_trigger();
> +	st->trig->name = kasprintf(GFP_KERNEL,
> +				"adis16203-dev%d",
> +				indio_dev->id);
> +	if (!st->trig->name) {
> +		ret = -ENOMEM;
> +		goto error_free_trig;
> +	}
> +	st->trig->dev.parent = &st->us->dev;
> +	st->trig->owner = THIS_MODULE;
> +	st->trig->private_data = st;
> +	st->trig->set_trigger_state = &adis16203_data_rdy_trigger_set_state;
> +	st->trig->try_reenable = &adis16203_trig_try_reen;
> +	st->trig->control_attrs = &adis16203_trigger_attr_group;
> +	ret = iio_trigger_register(st->trig);
> +
> +	/* select default trigger */
> +	indio_dev->trig = st->trig;
> +	if (ret)
> +		goto error_free_trig_name;
> +
> +	return 0;
> +
> +error_free_trig_name:
> +	kfree(st->trig->name);
> +error_free_trig:
> +	iio_free_trigger(st->trig);
> +
> +	return ret;
> +}
> +
> +void adis16203_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	struct adis16203_state *state = indio_dev->dev_data;
> +
> +	iio_trigger_unregister(state->trig);
> +	kfree(state->trig->name);
> +	iio_free_trigger(state->trig);
> +}


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

* Re: [PATCH 3/3] staging: iio: new adis16204 driver
  2010-10-23 10:49 ` [PATCH 3/3] staging: iio: new adis16204 driver Mike Frysinger
@ 2010-10-23 17:45   ` Jonathan Cameron
  0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-23 17:45 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel, Barry Song, Drivers

On 10/23/10 11:49, Mike Frysinger wrote:
> From: Barry Song <barry.song@analog.com>
> 
> IIO driver for Programmable High-g Digital Impact Sensor and Recorder.
Hi Mike,

This driver has a few controversial abi additions.  I've suggested what I 
would prefer to see below.  I'll hopefully send updated abi docs onto
Greg KH before going away.  Please then add whatever new additions there are
to that document in the same series as they are introduced in. I REALLY want
to keep that up to date from now on.

Currently this driver is just treating this device as a high g accelerometer
which avoids the controversial elements of how to handle the recorder.
As such I'm happy to see it merge subject to cleaning up the same stuff
as in the other two drivers and clarifying the attribute naming for the
sqrt(x^2+y^2) values.

Again, nice clear driver which was a pleasure to review.

Jonathan
> 
> Signed-off-by: Barry Song <barry.song@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/accel/Kconfig             |    9 +
>  drivers/staging/iio/accel/Makefile            |    4 +
>  drivers/staging/iio/accel/accel.h             |   22 +
>  drivers/staging/iio/accel/adis16204.h         |  151 ++++++
>  drivers/staging/iio/accel/adis16204_core.c    |  613 +++++++++++++++++++++++++
>  drivers/staging/iio/accel/adis16204_ring.c    |  252 ++++++++++
>  drivers/staging/iio/accel/adis16204_trigger.c |  122 +++++
>  7 files changed, 1173 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/accel/adis16204.h
>  create mode 100644 drivers/staging/iio/accel/adis16204_core.c
>  create mode 100644 drivers/staging/iio/accel/adis16204_ring.c
>  create mode 100644 drivers/staging/iio/accel/adis16204_trigger.c
> 
> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
> index cc057c6..a34f1d3 100644
> --- a/drivers/staging/iio/accel/Kconfig
> +++ b/drivers/staging/iio/accel/Kconfig
> @@ -21,6 +21,15 @@ config ADIS16203
>  	  Say yes here to build support for Analog Devices adis16203 Programmable
>  	  360 Degrees Inclinometer.
>  
> +config ADIS16204
> +	tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
> +	depends on SPI
> +	select IIO_TRIGGER if IIO_RING_BUFFER
> +	select IIO_SW_RING if IIO_RING_BUFFER
> +	help
> +	  Say yes here to build support for Analog Devices adis16204 Programmable
> +	  High-g Digital Impact Sensor and Recorder.
> +
>  config ADIS16209
>  	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
>  	depends on SPI
> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
> index 76cc92f..1b2a6d3 100644
> --- a/drivers/staging/iio/accel/Makefile
> +++ b/drivers/staging/iio/accel/Makefile
> @@ -10,6 +10,10 @@ adis16203-y             := adis16203_core.o
>  adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
>  obj-$(CONFIG_ADIS16203) += adis16203.o
>  
> +adis16204-y             := adis16204_core.o
> +adis16204-$(CONFIG_IIO_RING_BUFFER) += adis16204_ring.o adis16204_trigger.o
> +obj-$(CONFIG_ADIS16204) += adis16204.o
> +
>  adis16209-y             := adis16209_core.o
>  adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
>  obj-$(CONFIG_ADIS16209) += adis16209.o
> diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h
> index 1b6e37f..8183ebb 100644
> --- a/drivers/staging/iio/accel/accel.h
> +++ b/drivers/staging/iio/accel/accel.h
> @@ -35,6 +35,28 @@
>  #define IIO_DEV_ATTR_ACCEL_Z(_show, _addr)			\
>  	IIO_DEVICE_ATTR(accel_z_raw, S_IRUGO, _show, NULL, _addr)
>  
> +/* XY combined acceleration */
> +#define IIO_DEV_ATTR_ACCEL_XY(_show, _addr)			\
> +	IIO_DEVICE_ATTR(accel_xy, S_IRUGO, _show, NULL, _addr)
The naming isn't clear enough for this.  We allow fairly unusual
characters in naming so lets think what would be clearest here.
How about accel_sqrt(x^2+y^2)_raw ?  It's for things like this that the
'NAMED' macros exist.  The internal name might be accel_sqrtxspys_raw?
With relevant escape characters I don't think there are any problems
with that as a filename and it is nice and clear.


> +
> +#define IIO_DEV_ATTR_ACCEL_PEAK(_show, _addr)			\
> +	IIO_DEVICE_ATTR(accel_peak, S_IRUGO, _show, NULL, _addr)
Not used so don't add it.
> +
> +#define IIO_DEV_ATTR_ACCEL_XPEAK(_show, _addr)			\
> +	IIO_DEVICE_ATTR(accel_xpeak, S_IRUGO, _show, NULL, _addr)
> +
x_peak_raw would be preferable I think.  What do others think on this?
> +#define IIO_DEV_ATTR_ACCEL_YPEAK(_show, _addr)			\
> +	IIO_DEVICE_ATTR(accel_ypeak, S_IRUGO, _show, NULL, _addr)
> +
> +#define IIO_DEV_ATTR_ACCEL_ZPEAK(_show, _addr)			\
> +	IIO_DEVICE_ATTR(accel_zpeak, S_IRUGO, _show, NULL, _addr)
Not used so don't add it.
> +
> +#define IIO_DEV_ATTR_ACCEL_XYPEAK(_show, _addr)		\
> +	IIO_DEVICE_ATTR(accel_xypeak, S_IRUGO, _show, NULL, _addr)
I suggest similar naming to that described for the direct channel above.

> +
> +#define IIO_DEV_ATTR_ACCEL_XYZPEAK(_show, _addr)		\
> +	IIO_DEVICE_ATTR(accel_xyzpeak, S_IRUGO, _show, NULL, _addr)
Please don't define things that aren't used.  
> +
>  /* Thresholds are somewhat chip dependent - may need quite a few defs here */
>  /* For unified thresholds (shared across all directions */
>  
> diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h
> new file mode 100644
> index 0000000..e9ed7cb
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16204.h
> @@ -0,0 +1,151 @@
> +#ifndef SPI_ADIS16204_H_
> +#define SPI_ADIS16204_H_
> +
> +#define ADIS16204_STARTUP_DELAY	220 /* ms */
> +
> +#define ADIS16204_READ_REG(a)    a
> +#define ADIS16204_WRITE_REG(a) ((a) | 0x80)
> +
> +#define ADIS16204_FLASH_CNT      0x00 /* Flash memory write count */
> +#define ADIS16204_SUPPLY_OUT     0x02 /* Output, power supply */
> +#define ADIS16204_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
> +#define ADIS16204_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
> +#define ADIS16204_AUX_ADC        0x08 /* Output, auxiliary ADC input */
> +#define ADIS16204_TEMP_OUT       0x0A /* Output, temperature */
> +#define ADIS16204_X_PEAK_OUT     0x0C /* Twos complement */
> +#define ADIS16204_Y_PEAK_OUT     0x0E /* Twos complement */
> +#define ADIS16204_XACCL_NULL     0x10 /* Calibration, x-axis acceleration offset null */
> +#define ADIS16204_YACCL_NULL     0x12 /* Calibration, y-axis acceleration offset null */
> +#define ADIS16204_XACCL_SCALE    0x14 /* X-axis scale factor calibration register */
> +#define ADIS16204_YACCL_SCALE    0x16 /* Y-axis scale factor calibration register */
> +#define ADIS16204_XY_RSS_OUT     0x18 /* XY combined acceleration (RSS) */
> +#define ADIS16204_XY_PEAK_OUT    0x1A /* Peak, XY combined output (RSS) */
> +#define ADIS16204_CAP_BUF_1      0x1C /* Capture buffer output register 1 */
> +#define ADIS16204_CAP_BUF_2      0x1E /* Capture buffer output register 2 */
> +#define ADIS16204_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
> +#define ADIS16204_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
> +#define ADIS16204_ALM_CTRL       0x28 /* Alarm control */
> +#define ADIS16204_CAPT_PNTR      0x2A /* Capture register address pointer */
> +#define ADIS16204_AUX_DAC        0x30 /* Auxiliary DAC data */
> +#define ADIS16204_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
> +#define ADIS16204_MSC_CTRL       0x34 /* Miscellaneous control */
> +#define ADIS16204_SMPL_PRD       0x36 /* Internal sample period (rate) control */
> +#define ADIS16204_AVG_CNT        0x38 /* Operation, filter configuration */
> +#define ADIS16204_SLP_CNT        0x3A /* Operation, sleep mode control */
> +#define ADIS16204_DIAG_STAT      0x3C /* Diagnostics, system status register */
> +#define ADIS16204_GLOB_CMD       0x3E /* Operation, system command register */
> +
> +#define ADIS16204_OUTPUTS        5
> +
> +/* MSC_CTRL */
> +#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
> +#define ADIS16204_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
> +#define ADIS16204_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
> +#define ADIS16204_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
> +#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2	(1 << 0)  /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
> +
> +/* DIAG_STAT */
> +#define ADIS16204_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16204_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16204_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag: 1 = error condition,
> +						0 = normal operation */
> +#define ADIS16204_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
> +#define ADIS16204_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
> +#define ADIS16204_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
> +#define ADIS16204_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 2.975 V */
> +
> +/* GLOB_CMD */
> +#define ADIS16204_GLOB_CMD_SW_RESET	(1<<7)
> +#define ADIS16204_GLOB_CMD_CLEAR_STAT	(1<<4)
> +#define ADIS16204_GLOB_CMD_FACTORY_CAL	(1<<1)
> +
> +#define ADIS16204_MAX_TX 24
> +#define ADIS16204_MAX_RX 24
> +
> +#define ADIS16204_ERROR_ACTIVE          (1<<14)
> +
> +/**
> + * struct adis16204_state - device instance specific data
> + * @us:			actual spi_device
> + * @work_trigger_to_ring: bh for triggered event handling
> + * @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 adis16204_state {
> +	struct spi_device		*us;
> +	struct work_struct		work_trigger_to_ring;
> +	s64				last_timestamp;
> +	struct iio_dev			*indio_dev;
> +	struct iio_trigger		*trig;
> +	u8				*tx;
> +	u8				*rx;
> +	struct mutex			buf_lock;
> +};
> +
> +int adis16204_set_irq(struct device *dev, bool enable);
> +
> +#ifdef CONFIG_IIO_RING_BUFFER
> +enum adis16204_scan {
> +	ADIS16204_SCAN_SUPPLY,
> +	ADIS16204_SCAN_ACC_X,
> +	ADIS16204_SCAN_ACC_Y,
> +	ADIS16204_SCAN_AUX_ADC,
> +	ADIS16204_SCAN_TEMP,
> +};
> +
> +void adis16204_remove_trigger(struct iio_dev *indio_dev);
> +int adis16204_probe_trigger(struct iio_dev *indio_dev);
> +
> +ssize_t adis16204_read_data_from_ring(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf);
> +
> +int adis16204_configure_ring(struct iio_dev *indio_dev);
> +void adis16204_unconfigure_ring(struct iio_dev *indio_dev);
> +
> +int adis16204_initialize_ring(struct iio_ring_buffer *ring);
> +void adis16204_uninitialize_ring(struct iio_ring_buffer *ring);
> +#else /* CONFIG_IIO_RING_BUFFER */
> +
> +static inline void adis16204_remove_trigger(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16204_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline ssize_t
> +adis16204_read_data_from_ring(struct device *dev,
> +			      struct device_attribute *attr,
> +			      char *buf)
> +{
> +	return 0;
> +}
> +
> +static int adis16204_configure_ring(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16204_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +}
> +
> +#endif /* CONFIG_IIO_RING_BUFFER */
> +#endif /* SPI_ADIS16204_H_ */
> diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
> new file mode 100644
> index 0000000..25ce8dc
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16204_core.c
> @@ -0,0 +1,613 @@
> +/*
> + * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "accel.h"
> +#include "../gyro/gyro.h"
> +#include "../adc/adc.h"
> +
> +#include "adis16204.h"
> +
Used once and I'm not sure even that is needed, please clean up.
> +#define DRIVER_NAME		"adis16204"
> +
> +static int adis16204_check_status(struct device *dev);
> +
> +/**
> + * adis16204_spi_write_reg_8() - write single byte to a register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the register to be written
> + * @val: the value to write
> + **/
> +static int adis16204_spi_write_reg_8(struct device *dev,
> +		u8 reg_address,
> +		u8 val)
> +{
> +	int ret;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16204_WRITE_REG(reg_address);
> +	st->tx[1] = val;
> +
> +	ret = spi_write(st->us, st->tx, 2);
> +	mutex_unlock(&st->buf_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * adis16204_spi_write_reg_16() - write 2 bytes to a pair of registers
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: value to be written
> + **/
> +static int adis16204_spi_write_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 value)
> +{
> +	int ret;
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		}, {
> +			.tx_buf = st->tx + 2,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16204_WRITE_REG(lower_reg_address);
> +	st->tx[1] = value & 0xFF;
> +	st->tx[2] = ADIS16204_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;
> +}
> +
> +/**
> + * adis16204_spi_read_reg_16() - read 2 bytes from a 16-bit register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: somewhere to pass back the value read
> + **/
> +static int adis16204_spi_read_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 *val)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
> +	int ret;
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		}, {
> +			.rx_buf = st->rx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16204_READ_REG(lower_reg_address);
> +	st->tx[1] = 0;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	ret = spi_sync(st->us, &msg);
> +	if (ret) {
> +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
> +				lower_reg_address);
> +		goto error_ret;
> +	}
> +	*val = (st->rx[0] << 8) | st->rx[1];
> +
> +error_ret:
> +	mutex_unlock(&st->buf_lock);
> +	return ret;
> +}
> +
> +static ssize_t adis16204_read_12bit_unsigned(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int ret;
> +	u16 val = 0;
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +
> +	ret = adis16204_spi_read_reg_16(dev, this_attr->address, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & ADIS16204_ERROR_ACTIVE)
> +		adis16204_check_status(dev);
> +
> +	return sprintf(buf, "%u\n", val & 0x0FFF);
> +}
> +
> +static ssize_t adis16204_read_temp(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	ssize_t ret;
> +	u16 val;
> +
> +	/* Take the iio_dev status lock */
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16204_spi_read_reg_16(dev, ADIS16204_TEMP_OUT, (u16 *)&val);
> +	if (ret)
> +		goto error_ret;
> +
> +	if (val & ADIS16204_ERROR_ACTIVE)
> +		adis16204_check_status(dev);
> +
> +	val &= 0xFFF;
> +	ret = sprintf(buf, "%d\n", val);
> +
> +error_ret:
> +	mutex_unlock(&indio_dev->mlock);
> +	return ret;
> +}
> +
> +static ssize_t adis16204_read_12bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16204_ERROR_ACTIVE)
> +			adis16204_check_status(dev);
> +
> +		val = ((s16)(val << 4) >> 4);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16204_read_14bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16204_ERROR_ACTIVE)
> +			adis16204_check_status(dev);
> +
> +		val = ((s16)(val << 2) >> 2);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16204_write_16bit(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 = adis16204_spi_write_reg_16(dev, this_attr->address, val);
> +
> +error_ret:
> +	return ret ? ret : len;
> +}
> +
> +static int adis16204_reset(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16204_spi_write_reg_8(dev,
> +			ADIS16204_GLOB_CMD,
> +			ADIS16204_GLOB_CMD_SW_RESET);
> +	if (ret)
> +		dev_err(dev, "problem resetting device");
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16204_write_reset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t len)
> +{
> +	if (len < 1)
> +		return -EINVAL;
> +	switch (buf[0]) {
> +	case '1':
> +	case 'y':
> +	case 'Y':
> +		return adis16204_reset(dev);
> +	}
> +	return -EINVAL;
> +}
> +
> +int adis16204_set_irq(struct device *dev, bool enable)
> +{
> +	int ret = 0;
> +	u16 msc;
> +
> +	ret = adis16204_spi_read_reg_16(dev, ADIS16204_MSC_CTRL, &msc);
> +	if (ret)
> +		goto error_ret;
> +
> +	msc |= ADIS16204_MSC_CTRL_ACTIVE_HIGH;
> +	msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_DIO2;
> +	if (enable)
> +		msc |= ADIS16204_MSC_CTRL_DATA_RDY_EN;
> +	else
> +		msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_EN;
> +
> +	ret = adis16204_spi_write_reg_16(dev, ADIS16204_MSC_CTRL, msc);
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16204_check_status(struct device *dev)
> +{
> +	u16 status;
> +	int ret;
> +
> +	ret = adis16204_spi_read_reg_16(dev, ADIS16204_DIAG_STAT, &status);
> +	if (ret < 0) {
> +		dev_err(dev, "Reading status failed\n");
> +		goto error_ret;
> +	}
> +	ret = status & 0x1F;
> +
> +	if (status & ADIS16204_DIAG_STAT_SELFTEST_FAIL)
> +		dev_err(dev, "Self test failure\n");
> +	if (status & ADIS16204_DIAG_STAT_SPI_FAIL)
> +		dev_err(dev, "SPI failure\n");
> +	if (status & ADIS16204_DIAG_STAT_FLASH_UPT)
> +		dev_err(dev, "Flash update failed\n");
> +	if (status & ADIS16204_DIAG_STAT_POWER_HIGH)
> +		dev_err(dev, "Power supply above 3.625V\n");
> +	if (status & ADIS16204_DIAG_STAT_POWER_LOW)
> +		dev_err(dev, "Power supply below 2.975V\n");
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16204_self_test(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16204_spi_write_reg_16(dev,
> +			ADIS16204_MSC_CTRL,
> +			ADIS16204_MSC_CTRL_SELF_TEST_EN);
> +	if (ret) {
> +		dev_err(dev, "problem starting self test");
> +		goto err_ret;
> +	}
> +
> +	adis16204_check_status(dev);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static int adis16204_initial_setup(struct adis16204_state *st)
> +{
> +	int ret;
> +	struct device *dev = &st->indio_dev->dev;
> +
> +	/* Disable IRQ */
> +	ret = adis16204_set_irq(dev, false);
> +	if (ret) {
> +		dev_err(dev, "disable irq failed");
> +		goto err_ret;
> +	}
> +
> +	/* Do self test */
> +	ret = adis16204_self_test(dev);
> +	if (ret) {
> +		dev_err(dev, "self test failure");
> +		goto err_ret;
> +	}
> +
> +	/* Read status register to check the result */
> +	ret = adis16204_check_status(dev);
> +	if (ret) {
> +		adis16204_reset(dev);
> +		dev_err(dev, "device not playing ball -> reset");
> +		msleep(ADIS16204_STARTUP_DELAY);
> +		ret = adis16204_check_status(dev);
> +		if (ret) {
> +			dev_err(dev, "giving up");
> +			goto err_ret;
> +		}
> +	}
> +
> +	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
> +			st->us->chip_select, st->us->irq);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16204_read_12bit_unsigned,
> +		ADIS16204_SUPPLY_OUT);
> +static IIO_CONST_ATTR(in_supply_scale, "0.00122");
> +static IIO_DEV_ATTR_IN_RAW(0, adis16204_read_12bit_unsigned,
> +		ADIS16204_AUX_ADC);
> +static IIO_CONST_ATTR(in0_scale, "0.00061");
> +
> +static IIO_DEV_ATTR_ACCEL_X(adis16204_read_14bit_signed,
> +		ADIS16204_XACCL_OUT);
> +static IIO_DEV_ATTR_ACCEL_Y(adis16204_read_14bit_signed,
> +		ADIS16204_YACCL_OUT);
> +static IIO_DEV_ATTR_ACCEL_XY(adis16204_read_14bit_signed,
> +		ADIS16204_XY_RSS_OUT);
> +static IIO_DEV_ATTR_ACCEL_XPEAK(adis16204_read_14bit_signed,
> +		ADIS16204_X_PEAK_OUT);
> +static IIO_DEV_ATTR_ACCEL_YPEAK(adis16204_read_14bit_signed,
> +		ADIS16204_Y_PEAK_OUT);
> +static IIO_DEV_ATTR_ACCEL_XYPEAK(adis16204_read_14bit_signed,
> +		ADIS16204_XY_PEAK_OUT);
> +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16204_read_12bit_signed,
> +		adis16204_write_16bit,
> +		ADIS16204_XACCL_NULL);
> +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16204_read_12bit_signed,
> +		adis16204_write_16bit,
> +		ADIS16204_YACCL_NULL);
> +static IIO_CONST_ATTR(accel_x_scale, "0.017125");
> +static IIO_CONST_ATTR(accel_y_scale, "0.008407");

Obviously naming changes suggested above will feed through
to these.  Feel free to add NAMED macros where needed.
> +static IIO_CONST_ATTR(accel_xy_scale, "0.017125");
> +
> +static IIO_DEV_ATTR_TEMP(adis16204_read_temp);
> +static IIO_CONST_ATTR(temp_offset, "25");
> +static IIO_CONST_ATTR(temp_scale, "-0.47");
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16204_write_reset, 0);
> +
> +static IIO_CONST_ATTR(name, "adis16204");
> +
> +static struct attribute *adis16204_event_attributes[] = {
> +	NULL
> +};
> +
> +static struct attribute_group adis16204_event_attribute_group = {
> +	.attrs = adis16204_event_attributes,
> +};
> +
> +static struct attribute *adis16204_attributes[] = {
> +	&iio_dev_attr_in_supply_raw.dev_attr.attr,
> +	&iio_const_attr_in_supply_scale.dev_attr.attr,
> +	&iio_dev_attr_temp.dev_attr.attr,
> +	&iio_const_attr_temp_offset.dev_attr.attr,
> +	&iio_const_attr_temp_scale.dev_attr.attr,
> +	&iio_dev_attr_reset.dev_attr.attr,
> +	&iio_const_attr_name.dev_attr.attr,
> +	&iio_dev_attr_in0_raw.dev_attr.attr,
> +	&iio_const_attr_in0_scale.dev_attr.attr,
> +	&iio_dev_attr_accel_x_raw.dev_attr.attr,
> +	&iio_dev_attr_accel_y_raw.dev_attr.attr,
> +	&iio_dev_attr_accel_xy.dev_attr.attr,
> +	&iio_dev_attr_accel_xpeak.dev_attr.attr,
> +	&iio_dev_attr_accel_ypeak.dev_attr.attr,
> +	&iio_dev_attr_accel_xypeak.dev_attr.attr,
> +	&iio_dev_attr_accel_x_offset.dev_attr.attr,
> +	&iio_dev_attr_accel_y_offset.dev_attr.attr,
> +	&iio_const_attr_accel_x_scale.dev_attr.attr,
> +	&iio_const_attr_accel_y_scale.dev_attr.attr,
> +	&iio_const_attr_accel_xy_scale.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adis16204_attribute_group = {
> +	.attrs = adis16204_attributes,
> +};
> +
> +static int __devinit adis16204_probe(struct spi_device *spi)
> +{
> +	int ret, regdone = 0;
> +	struct adis16204_state *st = kzalloc(sizeof *st, GFP_KERNEL);
> +	if (!st) {
> +		ret =  -ENOMEM;
> +		goto error_ret;
> +	}
> +	/* this is only used for removal purposes */
> +	spi_set_drvdata(spi, st);
> +
> +	/* Allocate the comms buffers */
> +	st->rx = kzalloc(sizeof(*st->rx)*ADIS16204_MAX_RX, GFP_KERNEL);
> +	if (st->rx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_st;
> +	}
> +	st->tx = kzalloc(sizeof(*st->tx)*ADIS16204_MAX_TX, GFP_KERNEL);
> +	if (st->tx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_rx;
> +	}
> +	st->us = spi;
> +	mutex_init(&st->buf_lock);
> +	/* setup the industrialio driver allocated elements */
> +	st->indio_dev = iio_allocate_device();
> +	if (st->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_tx;
> +	}
> +
> +	st->indio_dev->dev.parent = &spi->dev;
> +	st->indio_dev->num_interrupt_lines = 1;
> +	st->indio_dev->event_attrs = &adis16204_event_attribute_group;
> +	st->indio_dev->attrs = &adis16204_attribute_group;
> +	st->indio_dev->dev_data = (void *)(st);
> +	st->indio_dev->driver_module = THIS_MODULE;
> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = adis16204_configure_ring(st->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = iio_device_register(st->indio_dev);
> +	if (ret)
> +		goto error_unreg_ring_funcs;
> +	regdone = 1;
> +
> +	ret = adis16204_initialize_ring(st->indio_dev->ring);
> +	if (ret) {
> +		printk(KERN_ERR "failed to initialize the ring\n");
> +		goto error_unreg_ring_funcs;
> +	}
> +
> +	if (spi->irq) {
> +		ret = iio_register_interrupt_line(spi->irq,
> +				st->indio_dev,
> +				0,
> +				IRQF_TRIGGER_RISING,
> +				"adis16204");
> +		if (ret)
> +			goto error_uninitialize_ring;
> +
> +		ret = adis16204_probe_trigger(st->indio_dev);
> +		if (ret)
> +			goto error_unregister_line;
> +	}
> +
> +	/* Get the device into a sane initial state */
> +	ret = adis16204_initial_setup(st);
> +	if (ret)
> +		goto error_remove_trigger;
> +	return 0;
> +
> +error_remove_trigger:
> +	adis16204_remove_trigger(st->indio_dev);
> +error_unregister_line:
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(st->indio_dev, 0);
> +error_uninitialize_ring:
> +	adis16204_uninitialize_ring(st->indio_dev->ring);
> +error_unreg_ring_funcs:
> +	adis16204_unconfigure_ring(st->indio_dev);
> +error_free_dev:
> +	if (regdone)
> +		iio_device_unregister(st->indio_dev);
> +	else
> +		iio_free_device(st->indio_dev);
> +error_free_tx:
> +	kfree(st->tx);
> +error_free_rx:
> +	kfree(st->rx);
> +error_free_st:
> +	kfree(st);
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16204_remove(struct spi_device *spi)
> +{
> +	struct adis16204_state *st = spi_get_drvdata(spi);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +
> +	flush_scheduled_work();
> +
> +	adis16204_remove_trigger(indio_dev);
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +
> +	adis16204_uninitialize_ring(indio_dev->ring);
> +	iio_device_unregister(indio_dev);
> +	adis16204_unconfigure_ring(indio_dev);
> +	kfree(st->tx);
> +	kfree(st->rx);
> +	kfree(st);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver adis16204_driver = {
> +	.driver = {
> +		.name = "adis16204",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = adis16204_probe,
> +	.remove = __devexit_p(adis16204_remove),
> +};
> +
> +static __init int adis16204_init(void)
> +{
> +	return spi_register_driver(&adis16204_driver);
> +}
> +module_init(adis16204_init);
> +
> +static __exit void adis16204_exit(void)
> +{
> +	spi_unregister_driver(&adis16204_driver);
> +}
> +module_exit(adis16204_exit);
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c
> new file mode 100644
> index 0000000..0264a50
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16204_ring.c
> @@ -0,0 +1,252 @@
> +#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/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../ring_sw.h"
> +#include "accel.h"
> +#include "../trigger.h"
> +#include "adis16204.h"
> +
Again this should go.
> +/**
> + * 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);
> +}
> +
> +static IIO_SCAN_EL_C(supply, ADIS16204_SCAN_SUPPLY, IIO_UNSIGNED(12),
> +		     ADIS16204_SUPPLY_OUT, NULL);
> +static IIO_SCAN_EL_C(accel_x, ADIS16204_SCAN_ACC_X, IIO_SIGNED(14),
> +		     ADIS16204_XACCL_OUT, NULL);
> +static IIO_SCAN_EL_C(accel_y, ADIS16204_SCAN_ACC_Y, IIO_SIGNED(14),
> +		     ADIS16204_YACCL_OUT, NULL);
> +static IIO_SCAN_EL_C(aux_adc, ADIS16204_SCAN_AUX_ADC, IIO_UNSIGNED(12),
> +		     ADIS16204_AUX_ADC, NULL);
> +static IIO_SCAN_EL_C(temp, ADIS16204_SCAN_TEMP, IIO_UNSIGNED(12),
> +		     ADIS16204_TEMP_OUT, NULL);
> +
> +static IIO_SCAN_EL_TIMESTAMP(5);
> +
> +static struct attribute *adis16204_scan_el_attrs[] = {
> +	&iio_scan_el_supply.dev_attr.attr,
> +	&iio_scan_el_accel_x.dev_attr.attr,
> +	&iio_scan_el_accel_y.dev_attr.attr,

Again no to naming. in0 please.
> +	&iio_scan_el_aux_adc.dev_attr.attr,
> +	&iio_scan_el_temp.dev_attr.attr,
> +	&iio_scan_el_timestamp.dev_attr.attr,
> +	NULL,
All need relevant type and index attributes.
> +};
> +
> +static struct attribute_group adis16204_scan_el_group = {
> +	.attrs = adis16204_scan_el_attrs,
> +	.name = "scan_elements",
> +};
> +
> +/**
> + * adis16204_poll_func_th() top half interrupt handler called by trigger
> + * @private_data:	iio_dev
> + **/
> +static void adis16204_poll_func_th(struct iio_dev *indio_dev, s64 timestamp)
> +{
> +	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
> +	st->last_timestamp = timestamp;
> +	schedule_work(&st->work_trigger_to_ring);
> +}
> +
> +/**
> + * adis16204_read_ring_data() read data registers which will be placed into ring
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @rx: somewhere to pass back the value read
> + **/
> +static int adis16204_read_ring_data(struct device *dev, u8 *rx)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[ADIS16204_OUTPUTS + 1];
> +	int ret;
> +	int i;
> +
> +	mutex_lock(&st->buf_lock);
> +
> +	spi_message_init(&msg);
> +
> +	memset(xfers, 0, sizeof(xfers));
> +	for (i = 0; i <= ADIS16204_OUTPUTS; i++) {
> +		xfers[i].bits_per_word = 8;
> +		xfers[i].cs_change = 1;
> +		xfers[i].len = 2;
> +		xfers[i].delay_usecs = 20;
> +		xfers[i].tx_buf = st->tx + 2 * i;
> +		st->tx[2 * i] = ADIS16204_READ_REG(ADIS16204_SUPPLY_OUT + 2 * i);
> +		st->tx[2 * i + 1] = 0;
> +		if (i >= 1)
> +			xfers[i].rx_buf = rx + 2 * (i - 1);
> +		spi_message_add_tail(&xfers[i], &msg);
> +	}
> +
> +	ret = spi_sync(st->us, &msg);
> +	if (ret)
> +		dev_err(&st->us->dev, "problem when burst reading");
> +
> +	mutex_unlock(&st->buf_lock);
> +
> +	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 adis16204_trigger_bh_to_ring(struct work_struct *work_s)
> +{
> +	struct adis16204_state *st
> +		= container_of(work_s, struct adis16204_state,
> +			       work_trigger_to_ring);
> +
> +	int i = 0;
> +	s16 *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;
> +	}
> +
> +	if (st->indio_dev->scan_count)
> +		if (adis16204_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
> +			for (; i < st->indio_dev->scan_count; i++) {
> +				data[i] = combine_8_to_16(st->rx[i*2+1],
> +							  st->rx[i*2]);
> +			}
> +
> +	/* 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(data);
> +
> +	return;
> +}
> +
> +/* in these circumstances is it better to go with unaligned packing and
> + * deal with the cost?*/
> +static int adis16204_data_rdy_ring_preenable(struct iio_dev *indio_dev)
> +{
> +	size_t size;
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* 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, let timestamp aligned with sizeof(s64) */
> +				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
> +					+ 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;
> +}
> +
Use helpers.
> +static int adis16204_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 adis16204_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;
> +}
> +
> +void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +	kfree(indio_dev->pollfunc);
> +	iio_sw_rb_free(indio_dev->ring);
> +}
> +
> +int adis16204_configure_ring(struct iio_dev *indio_dev)
> +{
> +	int ret = 0;
> +	struct adis16204_state *st = indio_dev->dev_data;
> +	struct iio_ring_buffer *ring;
> +	INIT_WORK(&st->work_trigger_to_ring, adis16204_trigger_bh_to_ring);
> +	/* Set default scan mode */
> +
> +	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
> +	indio_dev->scan_timestamp = true;
> +
> +	indio_dev->scan_el_attrs = &adis16204_scan_el_group;
> +
> +	ring = iio_sw_rb_allocate(indio_dev);
> +	if (!ring) {
> +		ret = -ENOMEM;
> +		return ret;
> +	}
> +	indio_dev->ring = ring;
> +	/* Effectively select the ring buffer implementation */
> +	iio_ring_sw_register_funcs(&ring->access);
> +	ring->preenable = &adis16204_data_rdy_ring_preenable;
> +	ring->postenable = &adis16204_data_rdy_ring_postenable;
> +	ring->predisable = &adis16204_data_rdy_ring_predisable;
> +	ring->owner = THIS_MODULE;
> +
> +	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
> +	if (indio_dev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_iio_sw_rb_free;;
> +	}
> +	indio_dev->pollfunc->poll_func_main = &adis16204_poll_func_th;
> +	indio_dev->pollfunc->private_data = indio_dev;
> +	indio_dev->modes |= INDIO_RING_TRIGGERED;
> +	return 0;
> +
> +error_iio_sw_rb_free:
> +	iio_sw_rb_free(indio_dev->ring);
> +	return ret;
> +}
> +
> +int adis16204_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return iio_ring_buffer_register(ring, 0);
> +}
> +
> +void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +	iio_ring_buffer_unregister(ring);
> +}
> diff --git a/drivers/staging/iio/accel/adis16204_trigger.c b/drivers/staging/iio/accel/adis16204_trigger.c
> new file mode 100644
> index 0000000..8e9db90
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16204_trigger.c
> @@ -0,0 +1,122 @@
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +#include <linux/spi/spi.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../trigger.h"
> +#include "adis16204.h"
> +
> +/**
> + * adis16204_data_rdy_trig_poll() the event handler for the data rdy trig
> + **/
> +static int adis16204_data_rdy_trig_poll(struct iio_dev *dev_info,
> +				       int index,
> +				       s64 timestamp,
> +				       int no_test)
> +{
> +	struct adis16204_state *st = iio_dev_get_devdata(dev_info);
> +	struct iio_trigger *trig = st->trig;
> +
> +	iio_trigger_poll(trig, timestamp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +IIO_EVENT_SH(data_rdy_trig, &adis16204_data_rdy_trig_poll);
> +
> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
> +
> +static struct attribute *adis16204_trigger_attrs[] = {
> +	&dev_attr_name.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group adis16204_trigger_attr_group = {
> +	.attrs = adis16204_trigger_attrs,
> +};
> +
> +/**
> + * adis16204_data_rdy_trigger_set_state() set datardy interrupt state
> + **/
> +static int adis16204_data_rdy_trigger_set_state(struct iio_trigger *trig,
> +						bool state)
> +{
> +	struct adis16204_state *st = trig->private_data;
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	int ret = 0;
> +
> +	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
> +	ret = adis16204_set_irq(&st->indio_dev->dev, state);
> +	if (state == false) {
> +		iio_remove_event_from_list(&iio_event_data_rdy_trig,
> +					   &indio_dev->interrupts[0]
> +					   ->ev_list);
> +		flush_scheduled_work();
> +	} else {
> +		iio_add_event_to_list(&iio_event_data_rdy_trig,
> +				      &indio_dev->interrupts[0]->ev_list);
> +	}
> +	return ret;
> +}
> +
> +/**
> + * adis16204_trig_try_reen() try renabling irq for data rdy trigger
> + * @trig:	the datardy trigger
> + **/
> +static int adis16204_trig_try_reen(struct iio_trigger *trig)
> +{
> +	struct adis16204_state *st = trig->private_data;
> +	enable_irq(st->us->irq);
> +	return 0;
> +}
> +
> +int adis16204_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct adis16204_state *st = indio_dev->dev_data;
> +
> +	st->trig = iio_allocate_trigger();
> +	st->trig->name = kasprintf(GFP_KERNEL,
> +				"adis16204-dev%d",
> +				indio_dev->id);
> +	if (!st->trig->name) {
> +		ret = -ENOMEM;
> +		goto error_free_trig;
> +	}
> +	st->trig->dev.parent = &st->us->dev;
> +	st->trig->owner = THIS_MODULE;
> +	st->trig->private_data = st;
> +	st->trig->set_trigger_state = &adis16204_data_rdy_trigger_set_state;
> +	st->trig->try_reenable = &adis16204_trig_try_reen;
> +	st->trig->control_attrs = &adis16204_trigger_attr_group;
> +	ret = iio_trigger_register(st->trig);
> +
> +	/* select default trigger */
> +	indio_dev->trig = st->trig;
> +	if (ret)
> +		goto error_free_trig_name;
> +
> +	return 0;
> +
> +error_free_trig_name:
> +	kfree(st->trig->name);
> +error_free_trig:
> +	iio_free_trigger(st->trig);
> +
> +	return ret;
> +}
> +
> +void adis16204_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	struct adis16204_state *state = indio_dev->dev_data;
> +
> +	iio_trigger_unregister(state->trig);
> +	kfree(state->trig->name);
> +	iio_free_trigger(state->trig);
> +}


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

* Re: [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-23 10:49 [PATCH 1/3] staging: iio: new adis16201 driver Mike Frysinger
                   ` (2 preceding siblings ...)
  2010-10-23 17:04 ` [PATCH 1/3] staging: iio: new adis16201 driver Jonathan Cameron
@ 2010-10-23 17:52 ` Jonathan Cameron
  2010-10-23 20:32   ` [Device-drivers-devel] " Mike Frysinger
  3 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-23 17:52 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel

On 10/23/10 11:49, Mike Frysinger wrote:
> From: Barry Song <barry.song@analog.com>
> 
> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.

Please check these for building against staging next.  There have been
a couple of cleanup and abi fix series merged since the tree you are
working against (at least I'm guessing that's what causes...

drivers/staging/iio/accel/adis16201_core.c:421:23: error: macro "IIO_DEV_ATTR_IN_NAMED_RAW" requires 4 arguments, but only 3 given
drivers/staging/iio/accel/adis16201_core.c:420: warning: type defaults to 'int' in declaration of 'IIO_DEV_ATTR_IN_NAMED_RAW'
drivers/staging/iio/accel/adis16201_core.c:455: warning: type defaults to 'int' in declaration of 'IIO_DEV_ATTR_TEMP'
drivers/staging/iio/accel/adis16201_core.c:455: warning: parameter names (without types) in function declaration
drivers/staging/iio/accel/adis16201_core.c:472: error: 'iio_dev_attr_in_supply_raw' undeclared here (not in a function)
drivers/staging/iio/accel/adis16201_core.c:474: error: 'iio_dev_attr_temp' undeclared here (not in a function)

The first is due to all channels now requiring a number so we can
do association of events with them in userspace (silly thing that I
missed when I wrote the original abi)  That attr_temp should never
have been there in the first place.  It's called iio_dev_attr_temp_raw.

Jonathan
> 
> Signed-off-by: Barry Song <barry.song@analog.com>
> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
> ---
>  drivers/staging/iio/accel/Kconfig             |    9 +
>  drivers/staging/iio/accel/Makefile            |    4 +
>  drivers/staging/iio/accel/adis16201.h         |  150 ++++++
>  drivers/staging/iio/accel/adis16201_core.c    |  640 +++++++++++++++++++++++++
>  drivers/staging/iio/accel/adis16201_ring.c    |  260 ++++++++++
>  drivers/staging/iio/accel/adis16201_trigger.c |  122 +++++
>  6 files changed, 1185 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/staging/iio/accel/adis16201.h
>  create mode 100644 drivers/staging/iio/accel/adis16201_core.c
>  create mode 100644 drivers/staging/iio/accel/adis16201_ring.c
>  create mode 100644 drivers/staging/iio/accel/adis16201_trigger.c
> 
> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
> index 5926c03..0b87557 100644
> --- a/drivers/staging/iio/accel/Kconfig
> +++ b/drivers/staging/iio/accel/Kconfig
> @@ -3,6 +3,15 @@
>  #
>  comment "Accelerometers"
>  
> +config ADIS16201
> +	tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
> +	depends on SPI
> +	select IIO_TRIGGER if IIO_RING_BUFFER
> +	select IIO_SW_RING if IIO_RING_BUFFER
> +	help
> +	  Say yes here to build support for Analog Devices adis16201 dual-axis
> +	  digital inclinometer and accelerometer.
> +
>  config ADIS16209
>  	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
>  	depends on SPI
> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
> index ff84703..4a22a01 100644
> --- a/drivers/staging/iio/accel/Makefile
> +++ b/drivers/staging/iio/accel/Makefile
> @@ -2,6 +2,10 @@
>  # Makefile for industrial I/O accelerometer drivers
>  #
>  
> +adis16201-y             := adis16201_core.o
> +adis16201-$(CONFIG_IIO_RING_BUFFER) += adis16201_ring.o adis16201_trigger.o
> +obj-$(CONFIG_ADIS16201) += adis16201.o
> +
>  adis16209-y             := adis16209_core.o
>  adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
>  obj-$(CONFIG_ADIS16209) += adis16209.o
> diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
> new file mode 100644
> index 0000000..b739ea2
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201.h
> @@ -0,0 +1,150 @@
> +#ifndef SPI_ADIS16201_H_
> +#define SPI_ADIS16201_H_
> +
> +#define ADIS16201_STARTUP_DELAY	220 /* ms */
> +
> +#define ADIS16201_READ_REG(a)    a
> +#define ADIS16201_WRITE_REG(a) ((a) | 0x80)
> +
> +#define ADIS16201_FLASH_CNT      0x00 /* Flash memory write count */
> +#define ADIS16201_SUPPLY_OUT     0x02 /* Output, power supply */
> +#define ADIS16201_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
> +#define ADIS16201_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
> +#define ADIS16201_AUX_ADC        0x08 /* Output, auxiliary ADC input */
> +#define ADIS16201_TEMP_OUT       0x0A /* Output, temperature */
> +#define ADIS16201_XINCL_OUT      0x0C /* Output, x-axis inclination */
> +#define ADIS16201_YINCL_OUT      0x0E /* Output, y-axis inclination */
> +#define ADIS16201_XACCL_OFFS     0x10 /* Calibration, x-axis acceleration offset */
> +#define ADIS16201_YACCL_OFFS     0x12 /* Calibration, y-axis acceleration offset */
> +#define ADIS16201_XACCL_SCALE    0x14 /* x-axis acceleration scale factor */
> +#define ADIS16201_YACCL_SCALE    0x16 /* y-axis acceleration scale factor */
> +#define ADIS16201_XINCL_OFFS     0x18 /* Calibration, x-axis inclination offset */
> +#define ADIS16201_YINCL_OFFS     0x1A /* Calibration, y-axis inclination offset */
> +#define ADIS16201_XINCL_SCALE    0x1C /* x-axis inclination scale factor */
> +#define ADIS16201_YINCL_SCALE    0x1E /* y-axis inclination scale factor */
> +#define ADIS16201_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
> +#define ADIS16201_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
> +#define ADIS16201_ALM_SMPL1      0x24 /* Alarm 1, sample period */
> +#define ADIS16201_ALM_SMPL2      0x26 /* Alarm 2, sample period */
> +#define ADIS16201_ALM_CTRL       0x28 /* Alarm control */
> +#define ADIS16201_AUX_DAC        0x30 /* Auxiliary DAC data */
> +#define ADIS16201_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
> +#define ADIS16201_MSC_CTRL       0x34 /* Miscellaneous control */
> +#define ADIS16201_SMPL_PRD       0x36 /* Internal sample period (rate) control */
> +#define ADIS16201_AVG_CNT        0x38 /* Operation, filter configuration */
> +#define ADIS16201_SLP_CNT        0x3A /* Operation, sleep mode control */
> +#define ADIS16201_DIAG_STAT      0x3C /* Diagnostics, system status register */
> +#define ADIS16201_GLOB_CMD       0x3E /* Operation, system command register */
> +
> +#define ADIS16201_OUTPUTS        7
> +
> +/* MSC_CTRL */
> +#define ADIS16201_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
> +#define ADIS16201_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
> +#define ADIS16201_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
> +#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1	(1 << 0)  /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
> +
> +/* DIAG_STAT */
> +#define ADIS16201_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16201_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
> +#define ADIS16201_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
> +#define ADIS16201_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
> +#define ADIS16201_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
> +#define ADIS16201_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 3.15 V */
> +
> +/* GLOB_CMD */
> +#define ADIS16201_GLOB_CMD_SW_RESET	(1<<7)
> +#define ADIS16201_GLOB_CMD_FACTORY_CAL	(1<<1)
> +
> +#define ADIS16201_MAX_TX 24
> +#define ADIS16201_MAX_RX 24
> +
> +#define ADIS16201_ERROR_ACTIVE          (1<<14)
> +
> +/**
> + * struct adis16201_state - device instance specific data
> + * @us:			actual spi_device
> + * @work_trigger_to_ring: bh for triggered event handling
> + * @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 adis16201_state {
> +	struct spi_device		*us;
> +	struct work_struct		work_trigger_to_ring;
> +	s64				last_timestamp;
> +	struct iio_dev			*indio_dev;
> +	struct iio_trigger		*trig;
> +	u8				*tx;
> +	u8				*rx;
> +	struct mutex			buf_lock;
> +};
> +
> +int adis16201_set_irq(struct device *dev, bool enable);
> +
> +#ifdef CONFIG_IIO_RING_BUFFER
> +enum adis16201_scan {
> +	ADIS16201_SCAN_SUPPLY,
> +	ADIS16201_SCAN_ACC_X,
> +	ADIS16201_SCAN_ACC_Y,
> +	ADIS16201_SCAN_AUX_ADC,
> +	ADIS16201_SCAN_TEMP,
> +	ADIS16201_SCAN_INCLI_X,
> +	ADIS16201_SCAN_INCLI_Y,
> +};
> +
> +void adis16201_remove_trigger(struct iio_dev *indio_dev);
> +int adis16201_probe_trigger(struct iio_dev *indio_dev);
> +
> +ssize_t adis16201_read_data_from_ring(struct device *dev,
> +				      struct device_attribute *attr,
> +				      char *buf);
> +
> +int adis16201_configure_ring(struct iio_dev *indio_dev);
> +void adis16201_unconfigure_ring(struct iio_dev *indio_dev);
> +
> +int adis16201_initialize_ring(struct iio_ring_buffer *ring);
> +void adis16201_uninitialize_ring(struct iio_ring_buffer *ring);
> +#else /* CONFIG_IIO_RING_BUFFER */
> +
> +static inline void adis16201_remove_trigger(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16201_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline ssize_t
> +adis16201_read_data_from_ring(struct device *dev,
> +			      struct device_attribute *attr,
> +			      char *buf)
> +{
> +	return 0;
> +}
> +
> +static int adis16201_configure_ring(struct iio_dev *indio_dev)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +}
> +
> +static inline int adis16201_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return 0;
> +}
> +
> +static inline void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +}
> +
> +#endif /* CONFIG_IIO_RING_BUFFER */
> +#endif /* SPI_ADIS16201_H_ */
> diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
> new file mode 100644
> index 0000000..c2f85a5
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201_core.c
> @@ -0,0 +1,640 @@
> +/*
> + * ADIS16201 Programmable Digital Vibration Sensor driver
> + *
> + * Copyright 2010 Analog Devices Inc.
> + *
> + * Licensed under the GPL-2 or later.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/delay.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "accel.h"
> +#include "inclinometer.h"
> +#include "../gyro/gyro.h"
> +#include "../adc/adc.h"
> +
> +#include "adis16201.h"
> +
> +#define DRIVER_NAME		"adis16201"
> +
> +static int adis16201_check_status(struct device *dev);
> +
> +/**
> + * adis16201_spi_write_reg_8() - write single byte to a register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the register to be written
> + * @val: the value to write
> + **/
> +static int adis16201_spi_write_reg_8(struct device *dev,
> +		u8 reg_address,
> +		u8 val)
> +{
> +	int ret;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16201_WRITE_REG(reg_address);
> +	st->tx[1] = val;
> +
> +	ret = spi_write(st->us, st->tx, 2);
> +	mutex_unlock(&st->buf_lock);
> +
> +	return ret;
> +}
> +
> +/**
> + * adis16201_spi_write_reg_16() - write 2 bytes to a pair of registers
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: value to be written
> + **/
> +static int adis16201_spi_write_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 value)
> +{
> +	int ret;
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		}, {
> +			.tx_buf = st->tx + 2,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16201_WRITE_REG(lower_reg_address);
> +	st->tx[1] = value & 0xFF;
> +	st->tx[2] = ADIS16201_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;
> +}
> +
> +/**
> + * adis16201_spi_read_reg_16() - read 2 bytes from a 16-bit register
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @reg_address: the address of the lower of the two registers. Second register
> + *               is assumed to have address one greater.
> + * @val: somewhere to pass back the value read
> + **/
> +static int adis16201_spi_read_reg_16(struct device *dev,
> +		u8 lower_reg_address,
> +		u16 *val)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	int ret;
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		}, {
> +			.rx_buf = st->rx,
> +			.bits_per_word = 8,
> +			.len = 2,
> +			.cs_change = 1,
> +			.delay_usecs = 20,
> +		},
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx[0] = ADIS16201_READ_REG(lower_reg_address);
> +	st->tx[1] = 0;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	ret = spi_sync(st->us, &msg);
> +	if (ret) {
> +		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
> +				lower_reg_address);
> +		goto error_ret;
> +	}
> +	*val = (st->rx[0] << 8) | st->rx[1];
> +
> +error_ret:
> +	mutex_unlock(&st->buf_lock);
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_12bit_unsigned(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	int ret;
> +	u16 val = 0;
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & ADIS16201_ERROR_ACTIVE)
> +		adis16201_check_status(dev);
> +
> +	return sprintf(buf, "%u\n", val & 0x0FFF);
> +}
> +
> +static ssize_t adis16201_read_temp(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	ssize_t ret;
> +	u16 val;
> +
> +	/* Take the iio_dev status lock */
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_TEMP_OUT, (u16 *)&val);
> +	if (ret)
> +		goto error_ret;
> +
> +	if (val & ADIS16201_ERROR_ACTIVE)
> +		adis16201_check_status(dev);
> +
> +	val &= 0xFFF;
> +	ret = sprintf(buf, "%d\n", val);
> +
> +error_ret:
> +	mutex_unlock(&indio_dev->mlock);
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_9bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16201_ERROR_ACTIVE)
> +			adis16201_check_status(dev);
> +
> +		val = ((s16)(val << 7) >> 7);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_12bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16201_ERROR_ACTIVE)
> +			adis16201_check_status(dev);
> +
> +		val = ((s16)(val << 4) >> 4);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_read_14bit_signed(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> +	s16 val = 0;
> +	ssize_t ret;
> +
> +	mutex_lock(&indio_dev->mlock);
> +
> +	ret = adis16201_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
> +	if (!ret) {
> +		if (val & ADIS16201_ERROR_ACTIVE)
> +			adis16201_check_status(dev);
> +
> +		val = ((s16)(val << 2) >> 2);
> +		ret = sprintf(buf, "%d\n", val);
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_write_16bit(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 = adis16201_spi_write_reg_16(dev, this_attr->address, val);
> +
> +error_ret:
> +	return ret ? ret : len;
> +}
> +
> +static int adis16201_reset(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16201_spi_write_reg_8(dev,
> +			ADIS16201_GLOB_CMD,
> +			ADIS16201_GLOB_CMD_SW_RESET);
> +	if (ret)
> +		dev_err(dev, "problem resetting device");
> +
> +	return ret;
> +}
> +
> +static ssize_t adis16201_write_reset(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf, size_t len)
> +{
> +	if (len < 1)
> +		return -EINVAL;
> +	switch (buf[0]) {
> +	case '1':
> +	case 'y':
> +	case 'Y':
> +		return adis16201_reset(dev);
> +	}
> +	return -EINVAL;
> +}
> +
> +int adis16201_set_irq(struct device *dev, bool enable)
> +{
> +	int ret = 0;
> +	u16 msc;
> +
> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_MSC_CTRL, &msc);
> +	if (ret)
> +		goto error_ret;
> +
> +	msc |= ADIS16201_MSC_CTRL_ACTIVE_HIGH;
> +	msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_DIO1;
> +	if (enable)
> +		msc |= ADIS16201_MSC_CTRL_DATA_RDY_EN;
> +	else
> +		msc &= ~ADIS16201_MSC_CTRL_DATA_RDY_EN;
> +
> +	ret = adis16201_spi_write_reg_16(dev, ADIS16201_MSC_CTRL, msc);
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16201_check_status(struct device *dev)
> +{
> +	u16 status;
> +	int ret;
> +
> +	ret = adis16201_spi_read_reg_16(dev, ADIS16201_DIAG_STAT, &status);
> +	if (ret < 0) {
> +		dev_err(dev, "Reading status failed\n");
> +		goto error_ret;
> +	}
> +	ret = status & 0xF;
> +
> +	if (status & ADIS16201_DIAG_STAT_SPI_FAIL)
> +		dev_err(dev, "SPI failure\n");
> +	if (status & ADIS16201_DIAG_STAT_FLASH_UPT)
> +		dev_err(dev, "Flash update failed\n");
> +	if (status & ADIS16201_DIAG_STAT_POWER_HIGH)
> +		dev_err(dev, "Power supply above 3.625V\n");
> +	if (status & ADIS16201_DIAG_STAT_POWER_LOW)
> +		dev_err(dev, "Power supply below 3.15V\n");
> +
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16201_self_test(struct device *dev)
> +{
> +	int ret;
> +	ret = adis16201_spi_write_reg_16(dev,
> +			ADIS16201_MSC_CTRL,
> +			ADIS16201_MSC_CTRL_SELF_TEST_EN);
> +	if (ret) {
> +		dev_err(dev, "problem starting self test");
> +		goto err_ret;
> +	}
> +
> +	adis16201_check_status(dev);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static int adis16201_initial_setup(struct adis16201_state *st)
> +{
> +	int ret;
> +	struct device *dev = &st->indio_dev->dev;
> +
> +	/* Disable IRQ */
> +	ret = adis16201_set_irq(dev, false);
> +	if (ret) {
> +		dev_err(dev, "disable irq failed");
> +		goto err_ret;
> +	}
> +
> +	/* Do self test */
> +	ret = adis16201_self_test(dev);
> +	if (ret) {
> +		dev_err(dev, "self test failure");
> +		goto err_ret;
> +	}
> +
> +	/* Read status register to check the result */
> +	ret = adis16201_check_status(dev);
> +	if (ret) {
> +		adis16201_reset(dev);
> +		dev_err(dev, "device not playing ball -> reset");
> +		msleep(ADIS16201_STARTUP_DELAY);
> +		ret = adis16201_check_status(dev);
> +		if (ret) {
> +			dev_err(dev, "giving up");
> +			goto err_ret;
> +		}
> +	}
> +
> +	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
> +			st->us->chip_select, st->us->irq);
> +
> +err_ret:
> +	return ret;
> +}
> +
> +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16201_read_12bit_unsigned,
> +		ADIS16201_SUPPLY_OUT);
> +static IIO_CONST_ATTR(in_supply_scale, "0.00122");
> +static IIO_DEV_ATTR_IN_RAW(0, adis16201_read_12bit_unsigned,
> +		ADIS16201_AUX_ADC);
> +static IIO_CONST_ATTR(in0_scale, "0.00061");
> +
> +static IIO_DEV_ATTR_ACCEL_X(adis16201_read_14bit_signed,
> +		ADIS16201_XACCL_OUT);
> +static IIO_DEV_ATTR_ACCEL_Y(adis16201_read_14bit_signed,
> +		ADIS16201_YACCL_OUT);
> +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_12bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_XACCL_OFFS);
> +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_12bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_YACCL_OFFS);
> +static IIO_CONST_ATTR(accel_scale, "0.4625");
> +
> +static IIO_DEV_ATTR_INCLI_X(adis16201_read_14bit_signed,
> +		ADIS16201_XINCL_OUT);
> +static IIO_DEV_ATTR_INCLI_Y(adis16201_read_14bit_signed,
> +		ADIS16201_YINCL_OUT);
> +static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_9bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_XACCL_OFFS);
> +static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO,
> +		adis16201_read_9bit_signed,
> +		adis16201_write_16bit,
> +		ADIS16201_YACCL_OFFS);
> +static IIO_CONST_ATTR(incli_scale, "0.1");
> +
> +static IIO_DEV_ATTR_TEMP(adis16201_read_temp);
> +static IIO_CONST_ATTR(temp_offset, "25");
> +static IIO_CONST_ATTR(temp_scale, "-0.47");
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16201_write_reset, 0);
> +
> +static IIO_CONST_ATTR(name, "adis16201");
> +
> +static struct attribute *adis16201_event_attributes[] = {
> +	NULL
> +};
> +
> +static struct attribute_group adis16201_event_attribute_group = {
> +	.attrs = adis16201_event_attributes,
> +};
> +
> +static struct attribute *adis16201_attributes[] = {
> +	&iio_dev_attr_in_supply_raw.dev_attr.attr,
> +	&iio_const_attr_in_supply_scale.dev_attr.attr,
> +	&iio_dev_attr_temp.dev_attr.attr,
> +	&iio_const_attr_temp_offset.dev_attr.attr,
> +	&iio_const_attr_temp_scale.dev_attr.attr,
> +	&iio_dev_attr_reset.dev_attr.attr,
> +	&iio_const_attr_name.dev_attr.attr,
> +	&iio_dev_attr_in0_raw.dev_attr.attr,
> +	&iio_const_attr_in0_scale.dev_attr.attr,
> +	&iio_dev_attr_accel_x_raw.dev_attr.attr,
> +	&iio_dev_attr_accel_y_raw.dev_attr.attr,
> +	&iio_dev_attr_accel_x_offset.dev_attr.attr,
> +	&iio_dev_attr_accel_y_offset.dev_attr.attr,
> +	&iio_const_attr_accel_scale.dev_attr.attr,
> +	&iio_dev_attr_incli_x_raw.dev_attr.attr,
> +	&iio_dev_attr_incli_y_raw.dev_attr.attr,
> +	&iio_dev_attr_incli_x_offset.dev_attr.attr,
> +	&iio_dev_attr_incli_y_offset.dev_attr.attr,
> +	&iio_const_attr_incli_scale.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group adis16201_attribute_group = {
> +	.attrs = adis16201_attributes,
> +};
> +
> +static int __devinit adis16201_probe(struct spi_device *spi)
> +{
> +	int ret, regdone = 0;
> +	struct adis16201_state *st = kzalloc(sizeof *st, GFP_KERNEL);
> +	if (!st) {
> +		ret =  -ENOMEM;
> +		goto error_ret;
> +	}
> +	/* this is only used for removal purposes */
> +	spi_set_drvdata(spi, st);
> +
> +	/* Allocate the comms buffers */
> +	st->rx = kzalloc(sizeof(*st->rx)*ADIS16201_MAX_RX, GFP_KERNEL);
> +	if (st->rx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_st;
> +	}
> +	st->tx = kzalloc(sizeof(*st->tx)*ADIS16201_MAX_TX, GFP_KERNEL);
> +	if (st->tx == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_rx;
> +	}
> +	st->us = spi;
> +	mutex_init(&st->buf_lock);
> +	/* setup the industrialio driver allocated elements */
> +	st->indio_dev = iio_allocate_device();
> +	if (st->indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		goto error_free_tx;
> +	}
> +
> +	st->indio_dev->dev.parent = &spi->dev;
> +	st->indio_dev->num_interrupt_lines = 1;
> +	st->indio_dev->event_attrs = &adis16201_event_attribute_group;
> +	st->indio_dev->attrs = &adis16201_attribute_group;
> +	st->indio_dev->dev_data = (void *)(st);
> +	st->indio_dev->driver_module = THIS_MODULE;
> +	st->indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	ret = adis16201_configure_ring(st->indio_dev);
> +	if (ret)
> +		goto error_free_dev;
> +
> +	ret = iio_device_register(st->indio_dev);
> +	if (ret)
> +		goto error_unreg_ring_funcs;
> +	regdone = 1;
> +
> +	ret = adis16201_initialize_ring(st->indio_dev->ring);
> +	if (ret) {
> +		printk(KERN_ERR "failed to initialize the ring\n");
> +		goto error_unreg_ring_funcs;
> +	}
> +
> +	if (spi->irq) {
> +		ret = iio_register_interrupt_line(spi->irq,
> +				st->indio_dev,
> +				0,
> +				IRQF_TRIGGER_RISING,
> +				"adis16201");
> +		if (ret)
> +			goto error_uninitialize_ring;
> +
> +		ret = adis16201_probe_trigger(st->indio_dev);
> +		if (ret)
> +			goto error_unregister_line;
> +	}
> +
> +	/* Get the device into a sane initial state */
> +	ret = adis16201_initial_setup(st);
> +	if (ret)
> +		goto error_remove_trigger;
> +	return 0;
> +
> +error_remove_trigger:
> +	adis16201_remove_trigger(st->indio_dev);
> +error_unregister_line:
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(st->indio_dev, 0);
> +error_uninitialize_ring:
> +	adis16201_uninitialize_ring(st->indio_dev->ring);
> +error_unreg_ring_funcs:
> +	adis16201_unconfigure_ring(st->indio_dev);
> +error_free_dev:
> +	if (regdone)
> +		iio_device_unregister(st->indio_dev);
> +	else
> +		iio_free_device(st->indio_dev);
> +error_free_tx:
> +	kfree(st->tx);
> +error_free_rx:
> +	kfree(st->rx);
> +error_free_st:
> +	kfree(st);
> +error_ret:
> +	return ret;
> +}
> +
> +static int adis16201_remove(struct spi_device *spi)
> +{
> +	struct adis16201_state *st = spi_get_drvdata(spi);
> +	struct iio_dev *indio_dev = st->indio_dev;
> +
> +	flush_scheduled_work();
> +
> +	adis16201_remove_trigger(indio_dev);
> +	if (spi->irq)
> +		iio_unregister_interrupt_line(indio_dev, 0);
> +
> +	adis16201_uninitialize_ring(indio_dev->ring);
> +	iio_device_unregister(indio_dev);
> +	adis16201_unconfigure_ring(indio_dev);
> +	kfree(st->tx);
> +	kfree(st->rx);
> +	kfree(st);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver adis16201_driver = {
> +	.driver = {
> +		.name = "adis16201",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = adis16201_probe,
> +	.remove = __devexit_p(adis16201_remove),
> +};
> +
> +static __init int adis16201_init(void)
> +{
> +	return spi_register_driver(&adis16201_driver);
> +}
> +module_init(adis16201_init);
> +
> +static __exit void adis16201_exit(void)
> +{
> +	spi_unregister_driver(&adis16201_driver);
> +}
> +module_exit(adis16201_exit);
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c
> new file mode 100644
> index 0000000..83fee28
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201_ring.c
> @@ -0,0 +1,260 @@
> +#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/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../ring_sw.h"
> +#include "accel.h"
> +#include "../trigger.h"
> +#include "adis16201.h"
> +
> +/**
> + * 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);
> +}
> +
> +static IIO_SCAN_EL_C(supply, ADIS16201_SCAN_SUPPLY, IIO_UNSIGNED(12),
> +		     ADIS16201_SUPPLY_OUT, NULL);
> +static IIO_SCAN_EL_C(accel_x, ADIS16201_SCAN_ACC_X, IIO_SIGNED(14),
> +		     ADIS16201_XACCL_OUT, NULL);
> +static IIO_SCAN_EL_C(accel_y, ADIS16201_SCAN_ACC_Y, IIO_SIGNED(14),
> +		     ADIS16201_YACCL_OUT, NULL);
> +static IIO_SCAN_EL_C(aux_adc, ADIS16201_SCAN_AUX_ADC, IIO_UNSIGNED(12),
> +		     ADIS16201_AUX_ADC, NULL);
> +static IIO_SCAN_EL_C(temp, ADIS16201_SCAN_TEMP, IIO_UNSIGNED(12),
> +		     ADIS16201_TEMP_OUT, NULL);
> +static IIO_SCAN_EL_C(incli_x, ADIS16201_SCAN_INCLI_X, IIO_SIGNED(14),
> +		     ADIS16201_XINCL_OUT, NULL);
> +static IIO_SCAN_EL_C(incli_y, ADIS16201_SCAN_INCLI_Y, IIO_SIGNED(14),
> +		     ADIS16201_YINCL_OUT, NULL);
> +
> +static IIO_SCAN_EL_TIMESTAMP(7);
> +
> +static struct attribute *adis16201_scan_el_attrs[] = {
> +	&iio_scan_el_supply.dev_attr.attr,
> +	&iio_scan_el_accel_x.dev_attr.attr,
> +	&iio_scan_el_accel_y.dev_attr.attr,
> +	&iio_scan_el_aux_adc.dev_attr.attr,
> +	&iio_scan_el_temp.dev_attr.attr,
> +	&iio_scan_el_incli_x.dev_attr.attr,
> +	&iio_scan_el_incli_y.dev_attr.attr,
> +	&iio_scan_el_timestamp.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute_group adis16201_scan_el_group = {
> +	.attrs = adis16201_scan_el_attrs,
> +	.name = "scan_elements",
> +};
> +
> +/**
> + * adis16201_poll_func_th() top half interrupt handler called by trigger
> + * @private_data:	iio_dev
> + **/
> +static void adis16201_poll_func_th(struct iio_dev *indio_dev, s64 time)
> +{
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	st->last_timestamp = time;
> +	schedule_work(&st->work_trigger_to_ring);
> +}
> +
> +/**
> + * adis16201_read_ring_data() read data registers which will be placed into ring
> + * @dev: device associated with child of actual device (iio_dev or iio_trig)
> + * @rx: somewhere to pass back the value read
> + **/
> +static int adis16201_read_ring_data(struct device *dev, u8 *rx)
> +{
> +	struct spi_message msg;
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct adis16201_state *st = iio_dev_get_devdata(indio_dev);
> +	struct spi_transfer xfers[ADIS16201_OUTPUTS + 1];
> +	int ret;
> +	int i;
> +
> +	mutex_lock(&st->buf_lock);
> +
> +	spi_message_init(&msg);
> +
> +	memset(xfers, 0, sizeof(xfers));
> +	for (i = 0; i <= ADIS16201_OUTPUTS; i++) {
> +		xfers[i].bits_per_word = 8;
> +		xfers[i].cs_change = 1;
> +		xfers[i].len = 2;
> +		xfers[i].delay_usecs = 20;
> +		xfers[i].tx_buf = st->tx + 2 * i;
> +		st->tx[2 * i] = ADIS16201_READ_REG(ADIS16201_SUPPLY_OUT + 2 * i);
> +		st->tx[2 * i + 1] = 0;
> +		if (i >= 1)
> +			xfers[i].rx_buf = rx + 2 * (i - 1);
> +		spi_message_add_tail(&xfers[i], &msg);
> +	}
> +
> +	ret = spi_sync(st->us, &msg);
> +	if (ret)
> +		dev_err(&st->us->dev, "problem when burst reading");
> +
> +	mutex_unlock(&st->buf_lock);
> +
> +	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 adis16201_trigger_bh_to_ring(struct work_struct *work_s)
> +{
> +	struct adis16201_state *st
> +		= container_of(work_s, struct adis16201_state,
> +			       work_trigger_to_ring);
> +
> +	int i = 0;
> +	s16 *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;
> +	}
> +
> +	if (st->indio_dev->scan_count)
> +		if (adis16201_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
> +			for (; i < st->indio_dev->scan_count; i++) {
> +				data[i] = combine_8_to_16(st->rx[i*2+1],
> +							  st->rx[i*2]);
> +			}
> +
> +	/* 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(data);
> +
> +	return;
> +}
> +
> +/* in these circumstances is it better to go with unaligned packing and
> + * deal with the cost?*/
> +static int adis16201_data_rdy_ring_preenable(struct iio_dev *indio_dev)
> +{
> +	size_t size;
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* 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, let timestamp aligned with sizeof(s64) */
> +				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
> +					+ 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 adis16201_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 adis16201_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;
> +}
> +
> +void adis16201_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> +	kfree(indio_dev->pollfunc);
> +	iio_sw_rb_free(indio_dev->ring);
> +}
> +
> +int adis16201_configure_ring(struct iio_dev *indio_dev)
> +{
> +	int ret = 0;
> +	struct adis16201_state *st = indio_dev->dev_data;
> +	struct iio_ring_buffer *ring;
> +	INIT_WORK(&st->work_trigger_to_ring, adis16201_trigger_bh_to_ring);
> +	/* Set default scan mode */
> +
> +	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_x.number);
> +	iio_scan_mask_set(indio_dev, iio_scan_el_incli_y.number);
> +	indio_dev->scan_timestamp = true;
> +
> +	indio_dev->scan_el_attrs = &adis16201_scan_el_group;
> +
> +	ring = iio_sw_rb_allocate(indio_dev);
> +	if (!ring) {
> +		ret = -ENOMEM;
> +		return ret;
> +	}
> +	indio_dev->ring = ring;
> +	/* Effectively select the ring buffer implementation */
> +	iio_ring_sw_register_funcs(&ring->access);
> +	ring->preenable = &adis16201_data_rdy_ring_preenable;
> +	ring->postenable = &adis16201_data_rdy_ring_postenable;
> +	ring->predisable = &adis16201_data_rdy_ring_predisable;
> +	ring->owner = THIS_MODULE;
> +
> +	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
> +	if (indio_dev->pollfunc == NULL) {
> +		ret = -ENOMEM;
> +		goto error_iio_sw_rb_free;;
> +	}
> +	indio_dev->pollfunc->poll_func_main = &adis16201_poll_func_th;
> +	indio_dev->pollfunc->private_data = indio_dev;
> +	indio_dev->modes |= INDIO_RING_TRIGGERED;
> +	return 0;
> +
> +error_iio_sw_rb_free:
> +	iio_sw_rb_free(indio_dev->ring);
> +	return ret;
> +}
> +
> +int adis16201_initialize_ring(struct iio_ring_buffer *ring)
> +{
> +	return iio_ring_buffer_register(ring, 0);
> +}
> +
> +void adis16201_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> +	iio_ring_buffer_unregister(ring);
> +}
> diff --git a/drivers/staging/iio/accel/adis16201_trigger.c b/drivers/staging/iio/accel/adis16201_trigger.c
> new file mode 100644
> index 0000000..8a9cea19
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16201_trigger.c
> @@ -0,0 +1,122 @@
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +#include <linux/spi/spi.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../trigger.h"
> +#include "adis16201.h"
> +
> +/**
> + * adis16201_data_rdy_trig_poll() the event handler for the data rdy trig
> + **/
> +static int adis16201_data_rdy_trig_poll(struct iio_dev *dev_info,
> +				       int index,
> +				       s64 timestamp,
> +				       int no_test)
> +{
> +	struct adis16201_state *st = iio_dev_get_devdata(dev_info);
> +	struct iio_trigger *trig = st->trig;
> +
> +	iio_trigger_poll(trig, timestamp);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +IIO_EVENT_SH(data_rdy_trig, &adis16201_data_rdy_trig_poll);
> +
> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
> +
> +static struct attribute *adis16201_trigger_attrs[] = {
> +	&dev_attr_name.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group adis16201_trigger_attr_group = {
> +	.attrs = adis16201_trigger_attrs,
> +};
> +
> +/**
> + * adis16201_data_rdy_trigger_set_state() set datardy interrupt state
> + **/
> +static int adis16201_data_rdy_trigger_set_state(struct iio_trigger *trig,
> +						bool state)
> +{
> +	struct adis16201_state *st = trig->private_data;
> +	struct iio_dev *indio_dev = st->indio_dev;
> +	int ret = 0;
> +
> +	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
> +	ret = adis16201_set_irq(&st->indio_dev->dev, state);
> +	if (state == false) {
> +		iio_remove_event_from_list(&iio_event_data_rdy_trig,
> +					   &indio_dev->interrupts[0]
> +					   ->ev_list);
> +		flush_scheduled_work();
> +	} else {
> +		iio_add_event_to_list(&iio_event_data_rdy_trig,
> +				      &indio_dev->interrupts[0]->ev_list);
> +	}
> +	return ret;
> +}
> +
> +/**
> + * adis16201_trig_try_reen() try renabling irq for data rdy trigger
> + * @trig:	the datardy trigger
> + **/
> +static int adis16201_trig_try_reen(struct iio_trigger *trig)
> +{
> +	struct adis16201_state *st = trig->private_data;
> +	enable_irq(st->us->irq);
> +	return 0;
> +}
> +
> +int adis16201_probe_trigger(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct adis16201_state *st = indio_dev->dev_data;
> +
> +	st->trig = iio_allocate_trigger();
> +	st->trig->name = kasprintf(GFP_KERNEL,
> +				"adis16201-dev%d",
> +				indio_dev->id);
> +	if (!st->trig->name) {
> +		ret = -ENOMEM;
> +		goto error_free_trig;
> +	}
> +	st->trig->dev.parent = &st->us->dev;
> +	st->trig->owner = THIS_MODULE;
> +	st->trig->private_data = st;
> +	st->trig->set_trigger_state = &adis16201_data_rdy_trigger_set_state;
> +	st->trig->try_reenable = &adis16201_trig_try_reen;
> +	st->trig->control_attrs = &adis16201_trigger_attr_group;
> +	ret = iio_trigger_register(st->trig);
> +
> +	/* select default trigger */
> +	indio_dev->trig = st->trig;
> +	if (ret)
> +		goto error_free_trig_name;
> +
> +	return 0;
> +
> +error_free_trig_name:
> +	kfree(st->trig->name);
> +error_free_trig:
> +	iio_free_trigger(st->trig);
> +
> +	return ret;
> +}
> +
> +void adis16201_remove_trigger(struct iio_dev *indio_dev)
> +{
> +	struct adis16201_state *state = indio_dev->dev_data;
> +
> +	iio_trigger_unregister(state->trig);
> +	kfree(state->trig->name);
> +	iio_free_trigger(state->trig);
> +}


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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-23 17:52 ` Jonathan Cameron
@ 2010-10-23 20:32   ` Mike Frysinger
  2010-10-24 11:09     ` Jonathan Cameron
  0 siblings, 1 reply; 17+ messages in thread
From: Mike Frysinger @ 2010-10-23 20:32 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel

On Sat, Oct 23, 2010 at 13:52, Jonathan Cameron wrote:
> On 10/23/10 11:49, Mike Frysinger wrote:
>> From: Barry Song <barry.song@analog.com>
>>
>> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.
>
> Please check these for building against staging next. =C2=A0There have be=
en
> a couple of cleanup and abi fix series merged since the tree you are
> working against (at least I'm guessing that's what causes...

testing against current mainline
-mike

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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-23 20:32   ` [Device-drivers-devel] " Mike Frysinger
@ 2010-10-24 11:09     ` Jonathan Cameron
  2010-10-24 15:58       ` Mike Frysinger
  0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-24 11:09 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel

On 10/23/10 21:32, Mike Frysinger wrote:
> On Sat, Oct 23, 2010 at 13:52, Jonathan Cameron wrote:
>> On 10/23/10 11:49, Mike Frysinger wrote:
>>> From: Barry Song <barry.song@analog.com>
>>>
>>> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.
>>
>> Please check these for building against staging next.  There have been
>> a couple of cleanup and abi fix series merged since the tree you are
>> working against (at least I'm guessing that's what causes...
> 
> testing against current mainline
Unfortunately that isn't where they are going to merge.  The change
required won't be huge, but Greg isn't going to take them if they
don't merge cleanly.

Still, fine to review them against mainline.  

Thanks,

Jonathan

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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-24 11:09     ` Jonathan Cameron
@ 2010-10-24 15:58       ` Mike Frysinger
  2010-10-24 18:24         ` Jonathan Cameron
  0 siblings, 1 reply; 17+ messages in thread
From: Mike Frysinger @ 2010-10-24 15:58 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, device-drivers-devel

On Sun, Oct 24, 2010 at 07:09, Jonathan Cameron wrote:
> On 10/23/10 21:32, Mike Frysinger wrote:
>> On Sat, Oct 23, 2010 at 13:52, Jonathan Cameron wrote:
>>> On 10/23/10 11:49, Mike Frysinger wrote:
>>>> From: Barry Song <barry.song@analog.com>
>>>>
>>>> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.
>>>
>>> Please check these for building against staging next. =C2=A0There have =
been
>>> a couple of cleanup and abi fix series merged since the tree you are
>>> working against (at least I'm guessing that's what causes...
>>
>> testing against current mainline
>
> Unfortunately that isn't where they are going to merge. =C2=A0The change
> required won't be huge, but Greg isn't going to take them if they
> don't merge cleanly.

they merge cleanly, they just dont compile :p.  what tree exactly
contains these changes you're referring to ?
-mike

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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-24 18:24         ` Jonathan Cameron
@ 2010-10-24 18:22           ` Mike Frysinger
  2010-10-24 18:43             ` Jonathan Cameron
  2010-10-29  1:30             ` Greg KH
  2010-10-24 18:36           ` Michael Frysinger
  1 sibling, 2 replies; 17+ messages in thread
From: Mike Frysinger @ 2010-10-24 18:22 UTC (permalink / raw)
  To: Jonathan Cameron, Greg KH; +Cc: linux-iio, device-drivers-devel

On Sun, Oct 24, 2010 at 14:24, Jonathan Cameron wrote:
> On 10/24/10 16:58, Mike Frysinger wrote:
>> On Sun, Oct 24, 2010 at 07:09, Jonathan Cameron wrote:
>>> Unfortunately that isn't where they are going to merge. =C2=A0The chang=
e
>>> required won't be huge, but Greg isn't going to take them if they
>>> don't merge cleanly.
>>
>> they merge cleanly, they just dont compile :p. =C2=A0what tree exactly
>> contains these changes you're referring to ?
>>
> either linux-next or staging-next branch of
> http://git.kernel.org/?p=3Dlinux/kernel/git/gregkh/staging-next-2.6.git;a=
=3Dsummary
>
> The all went in a few weeks back.

i guess the trouble is that that tree is labeled as "junk tree, please
ignore".  maybe gregkh can fix the labeling of his trees to reflect
reality.

> Personally I just work against Gregs staging-next tree.

our current development tends to focus on current release since that
is what we are pushing to customers.  i know this doesnt properly
reflect the merging process, but it's what we have resources for
today.
-mike

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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-24 15:58       ` Mike Frysinger
@ 2010-10-24 18:24         ` Jonathan Cameron
  2010-10-24 18:22           ` Mike Frysinger
  2010-10-24 18:36           ` Michael Frysinger
  0 siblings, 2 replies; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-24 18:24 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: linux-iio, device-drivers-devel

On 10/24/10 16:58, Mike Frysinger wrote:
> On Sun, Oct 24, 2010 at 07:09, Jonathan Cameron wrote:
>> On 10/23/10 21:32, Mike Frysinger wrote:
>>> On Sat, Oct 23, 2010 at 13:52, Jonathan Cameron wrote:
>>>> On 10/23/10 11:49, Mike Frysinger wrote:
>>>>> From: Barry Song <barry.song@analog.com>
>>>>>
>>>>> IIO driver for dual Axis Accelerometer/inclinometer adis16201 parts.
>>>>
>>>> Please check these for building against staging next.  There have been
>>>> a couple of cleanup and abi fix series merged since the tree you are
>>>> working against (at least I'm guessing that's what causes...
>>>
>>> testing against current mainline
>>
>> Unfortunately that isn't where they are going to merge.  The change
>> required won't be huge, but Greg isn't going to take them if they
>> don't merge cleanly.
> 
> they merge cleanly, they just dont compile :p.  what tree exactly
> contains these changes you're referring to ?
> -mike
> 
either linux-next or staging-next branch of
http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-next-2.6.git;a=summary

The all went in a few weeks back.

Personally I just work against Gregs staging-next tree.


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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-24 18:24         ` Jonathan Cameron
  2010-10-24 18:22           ` Mike Frysinger
@ 2010-10-24 18:36           ` Michael Frysinger
  1 sibling, 0 replies; 17+ messages in thread
From: Michael Frysinger @ 2010-10-24 18:36 UTC (permalink / raw)
  To: device-drivers-devel; +Cc: Jonathan Cameron, Mike Frysinger, linux-iio

On Sunday, October 24, 2010 14:24:00 Jonathan Cameron wrote:
> On 10/24/10 16:58, Mike Frysinger wrote:
> > On Sun, Oct 24, 2010 at 07:09, Jonathan Cameron wrote:
> >> On 10/23/10 21:32, Mike Frysinger wrote:
> >>> On Sat, Oct 23, 2010 at 13:52, Jonathan Cameron wrote:
> >>>> Please check these for building against staging next.  There have been
> >>>> a couple of cleanup and abi fix series merged since the tree you are
> >>>> working against (at least I'm guessing that's what causes...
> >>> 
> >>> testing against current mainline
> >> 
> >> Unfortunately that isn't where they are going to merge.  The change
> >> required won't be huge, but Greg isn't going to take them if they
> >> don't merge cleanly.
> > 
> > they merge cleanly, they just dont compile :p.  what tree exactly
> > contains these changes you're referring to ?
> 
> either linux-next or staging-next branch of
> http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-next-2.6.git;a=sum
> mary
> 
> The all went in a few weeks back.
> 
> Personally I just work against Gregs staging-next tree.

btw, do you know of any issues off the top of your head of importing these
into 2.6.36 ?  i'm looking at importing these:

2a28f6c 2010-10-14 10:17:18 +0200 staging: iio: ad7476: fix !CONFIG_IIO_RING_BUFFER build issue reported by Randy 
Dunlap
1139df5 2010-10-12 10:30:51 +0200 staging: iio: adc: ad799x miscellaneous cleanups
668413e 2010-10-11 18:05:56 +0200 staging: iio: adc: ad7476 more list review feedback by Jonathan Cameron
349282d 2010-10-11 18:05:37 +0200 staging: iio: adc: ad7476 new SPI ADC driver
e58537c 2010-10-08 12:14:14 +0100 staging: iio: update example application.
1755b0a 2010-10-08 12:14:13 +0100 staging: iio: adc add numbers to naming of all adc channels as needed to 
associate events with them.
2f3cc66 2010-10-08 12:14:12 +0100 staging: iio: events update documentation to match new abi.
f9f0d35 2010-10-08 12:14:11 +0100 staging: iio: light remove empty light.h file
45dd3b3 2010-10-08 12:14:10 +0100 staging: iio: tsl2563 event attribute name updates
d750e37 2010-10-08 12:14:09 +0100 staging: iio: accel remove deprecated and misleading event macros.
fc5d0e4 2010-10-08 12:14:08 +0100 staging: iio: sca3000 event attribute name updates
c33680c 2010-10-08 12:14:07 +0100 staging: iio: lis3l02dq event attribute renames
4be2de4 2010-10-08 12:14:06 +0100 staging: iio: events move buffer codes so they do not clash with new scheme
da1d8b6 2010-10-08 12:14:05 +0100 staging: iio: tsl2563 move to new event numbering scheme and remove old 
definitions
deb02fc 2010-10-08 12:14:04 +0100 staging: iio: accel remove old event codes
de9fe32 2010-10-08 12:14:03 +0100 staging: iio: sca3000 move to new event code scheme and add combined orientations
18e69a9 2010-10-08 12:14:02 +0100 staging: iio: lis3l02dq move to new event code scheme
820e882 2010-10-08 12:14:01 +0100 staging: iio: adc events update to new numbering system
f4704d7 2010-10-08 12:14:00 +0100 staging: iio: events define macros for new event code system
9404287 2010-10-07 15:48:09 -0700 staging: iio: light: Adding driver for ISL29018 ALS
e968d09 2010-10-07 14:24:33 +0200 staging: iio: ring_generic: provide IIO_CONST_ATTR_SCAN_EL_TYPE_WITH_SHIFT
3ba2e49 2010-10-07 16:14:10 +0200 staging: iio: adc: ad799x drop in_precision in favor of new in_type
a7861ff 2010-10-07 13:10:21 +0100 staging: iio: max1363 fix bug due to scan_element directory move.
7aa3bae 2010-10-07 13:10:20 +0100 staging: iio: sca3000 fix bug due to scan_element directory move.
0986452 2010-10-07 13:10:19 +0100 staging: iio: timestamp_en naming typo fix.
ba65849 2010-10-06 17:49:02 +0200 staging: iio: adc: max1363: remove unused variable
d0a5304 2010-10-07 13:27:37 +0200 staging: iio: iio_ring_rip_outer return immediately if rip_lots returns <= 0
aecac19 2010-10-06 10:25:49 +0200 staging: iio: adc: ad799x: Address iio list feedback
e1517c0 2010-10-06 16:15:37 +0200 staging: iio: adc: max1363: prevent buffer overflow
858f1cc 2010-10-06 16:22:17 +0200 staging: iio: adc: ad799x: prevent buffer overflow
30b5309 2010-10-01 16:41:52 +0200 staging: iio: adc: Address mailing list feedback by Jonathan Cameron
985dbe7 2010-10-01 16:41:51 +0200 staging: iio: adc: Enable driver support for ad799x AD converters
0abd242 2010-09-26 01:11:09 +0200 staging: iio: whitespace cleanup
10ba8ea 2010-09-21 14:41:08 +0100 staging: iio: sysfs abi documentation. Add _type and _index attrs
3215e31 2010-09-21 14:41:07 +0100 staging: iio: remove deprecated form of scan_el attribute naming.
d16061b 2010-09-21 14:41:06 +0100 staging: iio: sca3000 add _index attribute registration
77b3d3f 2010-09-21 14:41:05 +0100 staging: iio: max1363 add _index attribute registration
67dd265 2010-09-21 14:41:04 +0100 staging: iio: adis16400 add _index attribute registration
de9b493 2010-09-21 14:41:03 +0100 staging: iio: adis16350 add _index attribute registration
4744374 2010-09-21 14:41:02 +0100 staging: iio: adis16300 add _index attribute registration
c045e44 2010-09-21 14:41:01 +0100 staging: iio: adis16260 add _index attribute registration
6d2174f 2010-09-21 14:41:00 +0100 staging: iio: adis16240 add _index attribute registration
49624fa 2010-09-21 14:40:59 +0100 staging: iio: adis16209 add _index attribute registration
13a091a 2010-09-21 14:40:58 +0100 staging: iio: lis3l02dq add _index attribute registration
fc89b38 2010-09-21 14:40:57 +0100 staging: iio: amend macros for SCAN_EL creation to add separate index attr
44f270d 2010-09-21 14:40:56 +0100 staging: iio: Remove unused bit_count from struct iio_scan_el
5df2145 2010-09-21 14:40:55 +0100 staging: iio: adis16400 add _type attributes for all scan elements
3b401a9 2010-09-21 14:40:54 +0100 staging: iio: adis16350 add _type attributes for all scan elements
88b76d1 2010-09-21 14:40:53 +0100 staging: iio: adis16300 add _type attributes for all scan elements
357f020 2010-09-21 14:40:52 +0100 staging: iio: adis16260 add _type attributes for all scan elements
ef26b83 2010-09-21 14:40:51 +0100 staging: iio: sca3000 add _type attributes for all scan elements
be3ebd1 2010-09-21 14:40:50 +0100 staging: iio: adis16240 add _type attributes for all scan elements
6a4429c 2010-09-21 14:40:49 +0100 staging: iio: adis16209 add _type attributes for all scan elements
8d9c636 2010-09-21 14:40:48 +0100 staging: iio: max1363 add _type attributes for all scan elements
6a36e61 2010-09-21 14:40:47 +0100 staging: iio: lis3l02dq add _type attributes for all scan elements
6267ea6 2010-09-09 17:40:11 +0200 staging: iio: sca3000 convert byte order of ring buffer data
b68d58a 2010-09-09 17:40:10 +0200 staging: iio: sca3000 fix sca3000_[show,store]_ring_bpse
3285aae 2010-09-08 22:02:17 -0700 staging: iio: ak8975: Add ak8975 magnetometer sensor
7223234 2010-09-08 22:02:16 -0700 staging: iio: Change GAIN macros to SCALE.
6cd5a9a 2010-09-01 15:40:23 +0200 staging/trivial: fix typos concerning "access"
853098b 2010-09-04 17:54:51 +0100 staging: iio: documentation - partial update to make more of it true.
04b7081 2010-09-04 17:54:50 +0100 staging: iio: lis3l02dqbuffersimple.c bring example up to date.
299475e 2010-09-04 17:54:49 +0100 staging: iio: remove odd structure definition.
9dbfb6f 2010-09-04 17:54:48 +0100 staging: iio: Make use of the convenient IIO_TRIGGER_NAME_ATTR macro
2d7770a 2010-09-04 17:54:47 +0100 staging: iio: Remove deprecated ATTR_TEMP in favour of ATTR_TEMP_RAW
99e5dc4 2010-09-04 17:54:46 +0100 staging: iio: Remove long dead function definitions from headers
c3e5d41 2010-09-04 17:54:45 +0100 staging: iio: unecessary header removal and kernel doc clean up
b6e5d69 2010-09-04 17:54:44 +0100 staging: iio: simplify logic in iio_interrupt_handler
b156cf7 2010-09-04 17:54:43 +0100 staging: iio: convert idr to ida as pointer never provided
11f05c7 2010-08-31 14:27:33 +0200 staging: iio: hmc5843 change ABI to comply with documentation
4f64b80 2010-08-31 11:32:55 +0200 staging: iio sync scale units
51a0a5b 2010-08-31 11:32:54 +0200 staging: iio sync drivers with current ABI
5770b8f 2010-08-31 11:32:53 +0200 staging: iio partial documentation update
bf32963 2010-08-31 11:32:52 +0200 staging: iio move scan_elements into ring buffer
07e6229 2010-08-31 11:32:51 +0200 staging: iio: lis3l02dq adjust lis3l02dq_configure_ring
ffcab07 2010-08-31 11:32:50 +0200 staging: iio rename ring attributes
213fd22 2010-08-10 07:37:33 +0200 Staging: iio: signedness bug
d1ae4da 2010-08-04 21:54:26 +0200 drivers/staging/iio: call mutex_unlock in error handling code
-mike

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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-24 18:22           ` Mike Frysinger
@ 2010-10-24 18:43             ` Jonathan Cameron
  2010-10-29  1:30             ` Greg KH
  1 sibling, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2010-10-24 18:43 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: Greg KH, linux-iio, device-drivers-devel

On 10/24/10 19:22, Mike Frysinger wrote:
> On Sun, Oct 24, 2010 at 14:24, Jonathan Cameron wrote:
>> On 10/24/10 16:58, Mike Frysinger wrote:
>>> On Sun, Oct 24, 2010 at 07:09, Jonathan Cameron wrote:
>>>> Unfortunately that isn't where they are going to merge.  The change
>>>> required won't be huge, but Greg isn't going to take them if they
>>>> don't merge cleanly.
>>>
>>> they merge cleanly, they just dont compile :p.  what tree exactly
>>> contains these changes you're referring to ?
>>>
>> either linux-next or staging-next branch of
>> http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-next-2.6.git;a=summary
>>
>> The all went in a few weeks back.
> 
> i guess the trouble is that that tree is labeled as "junk tree, please
> ignore".  maybe gregkh can fix the labeling of his trees to reflect
> reality.
> 
>> Personally I just work against Gregs staging-next tree.
> 
> our current development tends to focus on current release since that
> is what we are pushing to customers.  i know this doesnt properly
> reflect the merging process, but it's what we have resources for
> today.
Sure. I know the feeling.  Hopefully IIO will be generally more stable
now the vast majority of dodgy corners effecting the driver core interface
have been cleaned up ;)  In this case for most drivers, the switch over
is a 5 minute job.  Admittedly that's a fair time given the number you have
sent on this time round though so you have my sympathy!

Bits where they never conformed to the abi are more of an issue, but we can
pin those down in tree.

Jonathan

> -mike
> 


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

* Re: [Device-drivers-devel] [PATCH 1/3] staging: iio: new adis16201 driver
  2010-10-24 18:22           ` Mike Frysinger
  2010-10-24 18:43             ` Jonathan Cameron
@ 2010-10-29  1:30             ` Greg KH
  1 sibling, 0 replies; 17+ messages in thread
From: Greg KH @ 2010-10-29  1:30 UTC (permalink / raw)
  To: Mike Frysinger; +Cc: Jonathan Cameron, linux-iio, device-drivers-devel

On Sun, Oct 24, 2010 at 02:22:11PM -0400, Mike Frysinger wrote:
> On Sun, Oct 24, 2010 at 14:24, Jonathan Cameron wrote:
> > On 10/24/10 16:58, Mike Frysinger wrote:
> >> On Sun, Oct 24, 2010 at 07:09, Jonathan Cameron wrote:
> >>> Unfortunately that isn't where they are going to merge. ??The change
> >>> required won't be huge, but Greg isn't going to take them if they
> >>> don't merge cleanly.
> >>
> >> they merge cleanly, they just dont compile :p. ??what tree exactly
> >> contains these changes you're referring to ?
> >>
> > either linux-next or staging-next branch of
> > http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-next-2.6.git;a=summary
> >
> > The all went in a few weeks back.
> 
> i guess the trouble is that that tree is labeled as "junk tree, please
> ignore".  maybe gregkh can fix the labeling of his trees to reflect
> reality.

Oops, sorry about that, now fixed.

greg k-h

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

* [PATCH 3/3] staging: iio: new adis16204 driver
  2010-08-07  4:44 Mike Frysinger
@ 2010-08-07  4:44 ` Mike Frysinger
  0 siblings, 0 replies; 17+ messages in thread
From: Mike Frysinger @ 2010-08-07  4:44 UTC (permalink / raw)
  To: linux-iio, Jonathan Cameron; +Cc: uclinux-dist-devel, Barry Song

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

IIO driver for Programmable High-g Digital Impact Sensor and Recorder.

Signed-off-by: Barry Song <barry.song@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 drivers/staging/iio/accel/Kconfig             |    9 +
 drivers/staging/iio/accel/Makefile            |    4 +
 drivers/staging/iio/accel/accel.h             |   22 +
 drivers/staging/iio/accel/adis16204.h         |  153 ++++++
 drivers/staging/iio/accel/adis16204_core.c    |  614 +++++++++++++++++++++++++
 drivers/staging/iio/accel/adis16204_ring.c    |  252 ++++++++++
 drivers/staging/iio/accel/adis16204_trigger.c |  124 +++++
 7 files changed, 1178 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/adis16204.h
 create mode 100644 drivers/staging/iio/accel/adis16204_core.c
 create mode 100644 drivers/staging/iio/accel/adis16204_ring.c
 create mode 100644 drivers/staging/iio/accel/adis16204_trigger.c

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index cc057c6..a34f1d3 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -21,6 +21,15 @@ config ADIS16203
 	  Say yes here to build support for Analog Devices adis16203 Programmable
 	  360 Degrees Inclinometer.
 
+config ADIS16204
+	tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
+	depends on SPI
+	select IIO_TRIGGER if IIO_RING_BUFFER
+	select IIO_SW_RING if IIO_RING_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16204 Programmable
+	  High-g Digital Impact Sensor and Recorder.
+
 config ADIS16209
 	tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index 76cc92f..1b2a6d3 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -10,6 +10,10 @@ adis16203-y             := adis16203_core.o
 adis16203-$(CONFIG_IIO_RING_BUFFER) += adis16203_ring.o adis16203_trigger.o
 obj-$(CONFIG_ADIS16203) += adis16203.o
 
+adis16204-y             := adis16204_core.o
+adis16204-$(CONFIG_IIO_RING_BUFFER) += adis16204_ring.o adis16204_trigger.o
+obj-$(CONFIG_ADIS16204) += adis16204.o
+
 adis16209-y             := adis16209_core.o
 adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
 obj-$(CONFIG_ADIS16209) += adis16209.o
diff --git a/drivers/staging/iio/accel/accel.h b/drivers/staging/iio/accel/accel.h
index 1b6e37f..8183ebb 100644
--- a/drivers/staging/iio/accel/accel.h
+++ b/drivers/staging/iio/accel/accel.h
@@ -35,6 +35,28 @@
 #define IIO_DEV_ATTR_ACCEL_Z(_show, _addr)			\
 	IIO_DEVICE_ATTR(accel_z_raw, S_IRUGO, _show, NULL, _addr)
 
+/* XY combined acceleration */
+#define IIO_DEV_ATTR_ACCEL_XY(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_xy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_PEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_peak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_xpeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_YPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_ypeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_ZPEAK(_show, _addr)			\
+	IIO_DEVICE_ATTR(accel_zpeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XYPEAK(_show, _addr)		\
+	IIO_DEVICE_ATTR(accel_xypeak, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ACCEL_XYZPEAK(_show, _addr)		\
+	IIO_DEVICE_ATTR(accel_xyzpeak, S_IRUGO, _show, NULL, _addr)
+
 /* Thresholds are somewhat chip dependent - may need quite a few defs here */
 /* For unified thresholds (shared across all directions */
 
diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h
new file mode 100644
index 0000000..fdd121c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204.h
@@ -0,0 +1,153 @@
+#ifndef SPI_ADIS16204_H_
+#define SPI_ADIS16204_H_
+
+#define ADIS16204_STARTUP_DELAY	220 /* ms */
+
+#define ADIS16204_READ_REG(a)    a
+#define ADIS16204_WRITE_REG(a) ((a) | 0x80)
+
+#define ADIS16204_FLASH_CNT      0x00 /* Flash memory write count */
+#define ADIS16204_SUPPLY_OUT     0x02 /* Output, power supply */
+#define ADIS16204_XACCL_OUT      0x04 /* Output, x-axis accelerometer */
+#define ADIS16204_YACCL_OUT      0x06 /* Output, y-axis accelerometer */
+#define ADIS16204_AUX_ADC        0x08 /* Output, auxiliary ADC input */
+#define ADIS16204_TEMP_OUT       0x0A /* Output, temperature */
+#define ADIS16204_X_PEAK_OUT     0x0C /* Twos complement */
+#define ADIS16204_Y_PEAK_OUT     0x0E /* Twos complement */
+#define ADIS16204_XACCL_NULL     0x10 /* Calibration, x-axis acceleration offset null */
+#define ADIS16204_YACCL_NULL     0x12 /* Calibration, y-axis acceleration offset null */
+#define ADIS16204_XACCL_SCALE    0x14 /* X-axis scale factor calibration register */
+#define ADIS16204_YACCL_SCALE    0x16 /* Y-axis scale factor calibration register */
+#define ADIS16204_XY_RSS_OUT     0x18 /* XY combined acceleration (RSS) */
+#define ADIS16204_XY_PEAK_OUT    0x1A /* Peak, XY combined output (RSS) */
+#define ADIS16204_CAP_BUF_1      0x1C /* Capture buffer output register 1 */
+#define ADIS16204_CAP_BUF_2      0x1E /* Capture buffer output register 2 */
+#define ADIS16204_ALM_MAG1       0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16204_ALM_MAG2       0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16204_ALM_CTRL       0x28 /* Alarm control */
+#define ADIS16204_CAPT_PNTR      0x2A /* Capture register address pointer */
+#define ADIS16204_AUX_DAC        0x30 /* Auxiliary DAC data */
+#define ADIS16204_GPIO_CTRL      0x32 /* General-purpose digital input/output control */
+#define ADIS16204_MSC_CTRL       0x34 /* Miscellaneous control */
+#define ADIS16204_SMPL_PRD       0x36 /* Internal sample period (rate) control */
+#define ADIS16204_AVG_CNT        0x38 /* Operation, filter configuration */
+#define ADIS16204_SLP_CNT        0x3A /* Operation, sleep mode control */
+#define ADIS16204_DIAG_STAT      0x3C /* Diagnostics, system status register */
+#define ADIS16204_GLOB_CMD       0x3E /* Operation, system command register */
+
+#define ADIS16204_OUTPUTS        5
+
+/* MSC_CTRL */
+#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST	(1 << 10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16204_MSC_CTRL_SELF_TEST_EN	        (1 << 8)  /* Self-test enable */
+#define ADIS16204_MSC_CTRL_DATA_RDY_EN	        (1 << 2)  /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16204_MSC_CTRL_ACTIVE_HIGH	        (1 << 1)  /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2	(1 << 0)  /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+
+/* DIAG_STAT */
+#define ADIS16204_DIAG_STAT_ALARM2        (1<<9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_ALARM1        (1<<8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_SELFTEST_FAIL (1<<5) /* Self-test diagnostic error flag: 1 = error condition,
+						0 = normal operation */
+#define ADIS16204_DIAG_STAT_SPI_FAIL	  (1<<3) /* SPI communications failure */
+#define ADIS16204_DIAG_STAT_FLASH_UPT	  (1<<2) /* Flash update failure */
+#define ADIS16204_DIAG_STAT_POWER_HIGH	  (1<<1) /* Power supply above 3.625 V */
+#define ADIS16204_DIAG_STAT_POWER_LOW	  (1<<0) /* Power supply below 2.975 V */
+
+/* GLOB_CMD */
+#define ADIS16204_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16204_GLOB_CMD_CLEAR_STAT	(1<<4)
+#define ADIS16204_GLOB_CMD_FACTORY_CAL	(1<<1)
+
+#define ADIS16204_MAX_TX 24
+#define ADIS16204_MAX_RX 24
+
+#define ADIS16204_ERROR_ACTIVE          (1<<14)
+
+/**
+ * struct adis16204_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 adis16204_state {
+	struct spi_device		*us;
+	struct work_struct		work_trigger_to_ring;
+	struct iio_work_cont		work_cont_thresh;
+	s64				last_timestamp;
+	struct iio_dev			*indio_dev;
+	struct iio_trigger		*trig;
+	u8				*tx;
+	u8				*rx;
+	struct mutex			buf_lock;
+};
+
+int adis16204_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16204_scan {
+	ADIS16204_SCAN_SUPPLY,
+	ADIS16204_SCAN_ACC_X,
+	ADIS16204_SCAN_ACC_Y,
+	ADIS16204_SCAN_AUX_ADC,
+	ADIS16204_SCAN_TEMP,
+};
+
+void adis16204_remove_trigger(struct iio_dev *indio_dev);
+int adis16204_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16204_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+int adis16204_configure_ring(struct iio_dev *indio_dev);
+void adis16204_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16204_initialize_ring(struct iio_ring_buffer *ring);
+void adis16204_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16204_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16204_probe_trigger(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline ssize_t
+adis16204_read_data_from_ring(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	return 0;
+}
+
+static int adis16204_configure_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+static inline void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16204_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return 0;
+}
+
+static inline void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16204_H_ */
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
new file mode 100644
index 0000000..94a3111
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -0,0 +1,614 @@
+/*
+ * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "../imu/volt.h"
+#include "../gyro/gyro.h"
+#include "../adc/adc.h"
+
+#include "adis16204.h"
+
+#define DRIVER_NAME		"adis16204"
+
+static int adis16204_check_status(struct device *dev);
+
+/**
+ * adis16204_spi_write_reg_8() - write single byte to a register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+static int adis16204_spi_write_reg_8(struct device *dev,
+		u8 reg_address,
+		u8 val)
+{
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_WRITE_REG(reg_address);
+	st->tx[1] = val;
+
+	ret = spi_write(st->us, st->tx, 2);
+	mutex_unlock(&st->buf_lock);
+
+	return ret;
+}
+
+/**
+ * adis16204_spi_write_reg_16() - write 2 bytes to a pair of registers
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: value to be written
+ **/
+static int adis16204_spi_write_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 value)
+{
+	int ret;
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.tx_buf = st->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_WRITE_REG(lower_reg_address);
+	st->tx[1] = value & 0xFF;
+	st->tx[2] = ADIS16204_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;
+}
+
+/**
+ * adis16204_spi_read_reg_16() - read 2 bytes from a 16-bit register
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @reg_address: the address of the lower of the two registers. Second register
+ *               is assumed to have address one greater.
+ * @val: somewhere to pass back the value read
+ **/
+static int adis16204_spi_read_reg_16(struct device *dev,
+		u8 lower_reg_address,
+		u16 *val)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = st->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		}, {
+			.rx_buf = st->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = 20,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = ADIS16204_READ_REG(lower_reg_address);
+	st->tx[1] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret) {
+		dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+				lower_reg_address);
+		goto error_ret;
+	}
+	*val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret;
+}
+
+static ssize_t adis16204_read_12bit_unsigned(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int ret;
+	u16 val = 0;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, &val);
+	if (ret)
+		return ret;
+
+	if (val & ADIS16204_ERROR_ACTIVE)
+		adis16204_check_status(dev);
+
+	return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16204_read_temp(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 val;
+
+	/* Take the iio_dev status lock */
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_TEMP_OUT, (u16 *)&val);
+	if (ret)
+		goto error_ret;
+
+	if (val & ADIS16204_ERROR_ACTIVE)
+		adis16204_check_status(dev);
+
+	val &= 0xFFF;
+	ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+static ssize_t adis16204_read_12bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16204_ERROR_ACTIVE)
+			adis16204_check_status(dev);
+
+		val = ((s16)(val << 4) >> 4);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16204_read_14bit_signed(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s16 val = 0;
+	ssize_t ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis16204_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+	if (!ret) {
+		if (val & ADIS16204_ERROR_ACTIVE)
+			adis16204_check_status(dev);
+
+		val = ((s16)(val << 2) >> 2);
+		ret = sprintf(buf, "%d\n", val);
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+
+	return ret;
+}
+
+static ssize_t adis16204_write_16bit(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 = adis16204_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+	return ret ? ret : len;
+}
+
+static int adis16204_reset(struct device *dev)
+{
+	int ret;
+	ret = adis16204_spi_write_reg_8(dev,
+			ADIS16204_GLOB_CMD,
+			ADIS16204_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(dev, "problem resetting device");
+
+	return ret;
+}
+
+static ssize_t adis16204_write_reset(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t len)
+{
+	if (len < 1)
+		return -EINVAL;
+	switch (buf[0]) {
+	case '1':
+	case 'y':
+	case 'Y':
+		return adis16204_reset(dev);
+	}
+	return -EINVAL;
+}
+
+int adis16204_set_irq(struct device *dev, bool enable)
+{
+	int ret = 0;
+	u16 msc;
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_MSC_CTRL, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS16204_MSC_CTRL_ACTIVE_HIGH;
+	msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_DIO2;
+	if (enable)
+		msc |= ADIS16204_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS16204_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis16204_spi_write_reg_16(dev, ADIS16204_MSC_CTRL, msc);
+
+error_ret:
+	return ret;
+}
+
+static int adis16204_check_status(struct device *dev)
+{
+	u16 status;
+	int ret;
+
+	ret = adis16204_spi_read_reg_16(dev, ADIS16204_DIAG_STAT, &status);
+	if (ret < 0) {
+		dev_err(dev, "Reading status failed\n");
+		goto error_ret;
+	}
+	ret = status & 0x1F;
+
+	if (status & ADIS16204_DIAG_STAT_SELFTEST_FAIL)
+		dev_err(dev, "Self test failure\n");
+	if (status & ADIS16204_DIAG_STAT_SPI_FAIL)
+		dev_err(dev, "SPI failure\n");
+	if (status & ADIS16204_DIAG_STAT_FLASH_UPT)
+		dev_err(dev, "Flash update failed\n");
+	if (status & ADIS16204_DIAG_STAT_POWER_HIGH)
+		dev_err(dev, "Power supply above 3.625V\n");
+	if (status & ADIS16204_DIAG_STAT_POWER_LOW)
+		dev_err(dev, "Power supply below 2.975V\n");
+
+error_ret:
+	return ret;
+}
+
+static int adis16204_self_test(struct device *dev)
+{
+	int ret;
+	ret = adis16204_spi_write_reg_16(dev,
+			ADIS16204_MSC_CTRL,
+			ADIS16204_MSC_CTRL_SELF_TEST_EN);
+	if (ret) {
+		dev_err(dev, "problem starting self test");
+		goto err_ret;
+	}
+
+	adis16204_check_status(dev);
+
+err_ret:
+	return ret;
+}
+
+static int adis16204_initial_setup(struct adis16204_state *st)
+{
+	int ret;
+	struct device *dev = &st->indio_dev->dev;
+
+	/* Disable IRQ */
+	ret = adis16204_set_irq(dev, false);
+	if (ret) {
+		dev_err(dev, "disable irq failed");
+		goto err_ret;
+	}
+
+	/* Do self test */
+	ret = adis16204_self_test(dev);
+	if (ret) {
+		dev_err(dev, "self test failure");
+		goto err_ret;
+	}
+
+	/* Read status register to check the result */
+	ret = adis16204_check_status(dev);
+	if (ret) {
+		adis16204_reset(dev);
+		dev_err(dev, "device not playing ball -> reset");
+		msleep(ADIS16204_STARTUP_DELAY);
+		ret = adis16204_check_status(dev);
+		if (ret) {
+			dev_err(dev, "giving up");
+			goto err_ret;
+		}
+	}
+
+	printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %d)\n",
+			st->us->chip_select, st->us->irq);
+
+err_ret:
+	return ret;
+}
+
+static IIO_DEV_ATTR_VOLT(supply, adis16204_read_12bit_unsigned,
+		ADIS16204_SUPPLY_OUT);
+static IIO_CONST_ATTR(volt_supply_scale, "0.00122");
+static IIO_DEV_ATTR_VOLT(aux, adis16204_read_12bit_unsigned,
+		ADIS16204_AUX_ADC);
+static IIO_CONST_ATTR(volt_aux_scale, "0.00061");
+
+static IIO_DEV_ATTR_ACCEL_X(adis16204_read_14bit_signed,
+		ADIS16204_XACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16204_read_14bit_signed,
+		ADIS16204_YACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_XY(adis16204_read_14bit_signed,
+		ADIS16204_XY_RSS_OUT);
+static IIO_DEV_ATTR_ACCEL_XPEAK(adis16204_read_14bit_signed,
+		ADIS16204_X_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_YPEAK(adis16204_read_14bit_signed,
+		ADIS16204_Y_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_XYPEAK(adis16204_read_14bit_signed,
+		ADIS16204_XY_PEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+		adis16204_read_12bit_signed,
+		adis16204_write_16bit,
+		ADIS16204_XACCL_NULL);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+		adis16204_read_12bit_signed,
+		adis16204_write_16bit,
+		ADIS16204_YACCL_NULL);
+static IIO_CONST_ATTR(accel_x_scale, "0.017125");
+static IIO_CONST_ATTR(accel_y_scale, "0.008407");
+static IIO_CONST_ATTR(accel_xy_scale, "0.017125");
+
+static IIO_DEV_ATTR_TEMP(adis16204_read_temp);
+static IIO_CONST_ATTR(temp_offset, "25");
+static IIO_CONST_ATTR(temp_scale, "-0.47");
+
+static IIO_DEV_ATTR_RESET(adis16204_write_reset);
+
+static IIO_CONST_ATTR(name, "adis16204");
+
+static struct attribute *adis16204_event_attributes[] = {
+	NULL
+};
+
+static struct attribute_group adis16204_event_attribute_group = {
+	.attrs = adis16204_event_attributes,
+};
+
+static struct attribute *adis16204_attributes[] = {
+	&iio_dev_attr_volt_supply.dev_attr.attr,
+	&iio_const_attr_volt_supply_scale.dev_attr.attr,
+	&iio_dev_attr_temp.dev_attr.attr,
+	&iio_const_attr_temp_offset.dev_attr.attr,
+	&iio_const_attr_temp_scale.dev_attr.attr,
+	&iio_dev_attr_reset.dev_attr.attr,
+	&iio_const_attr_name.dev_attr.attr,
+	&iio_dev_attr_volt_aux.dev_attr.attr,
+	&iio_const_attr_volt_aux_scale.dev_attr.attr,
+	&iio_dev_attr_accel_x_raw.dev_attr.attr,
+	&iio_dev_attr_accel_y_raw.dev_attr.attr,
+	&iio_dev_attr_accel_xy.dev_attr.attr,
+	&iio_dev_attr_accel_xpeak.dev_attr.attr,
+	&iio_dev_attr_accel_ypeak.dev_attr.attr,
+	&iio_dev_attr_accel_xypeak.dev_attr.attr,
+	&iio_dev_attr_accel_x_offset.dev_attr.attr,
+	&iio_dev_attr_accel_y_offset.dev_attr.attr,
+	&iio_const_attr_accel_x_scale.dev_attr.attr,
+	&iio_const_attr_accel_y_scale.dev_attr.attr,
+	&iio_const_attr_accel_xy_scale.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group adis16204_attribute_group = {
+	.attrs = adis16204_attributes,
+};
+
+static int __devinit adis16204_probe(struct spi_device *spi)
+{
+	int ret, regdone = 0;
+	struct adis16204_state *st = kzalloc(sizeof *st, GFP_KERNEL);
+	if (!st) {
+		ret =  -ENOMEM;
+		goto error_ret;
+	}
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, st);
+
+	/* Allocate the comms buffers */
+	st->rx = kzalloc(sizeof(*st->rx)*ADIS16204_MAX_RX, GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kzalloc(sizeof(*st->tx)*ADIS16204_MAX_TX, GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	/* setup the industrialio driver allocated elements */
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+
+	st->indio_dev->dev.parent = &spi->dev;
+	st->indio_dev->num_interrupt_lines = 1;
+	st->indio_dev->event_attrs = &adis16204_event_attribute_group;
+	st->indio_dev->attrs = &adis16204_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis16204_configure_ring(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_unreg_ring_funcs;
+	regdone = 1;
+
+	ret = adis16204_initialize_ring(st->indio_dev->ring);
+	if (ret) {
+		printk(KERN_ERR "failed to initialize the ring\n");
+		goto error_unreg_ring_funcs;
+	}
+
+	if (spi->irq) {
+		ret = iio_register_interrupt_line(spi->irq,
+				st->indio_dev,
+				0,
+				IRQF_TRIGGER_RISING,
+				"adis16204");
+		if (ret)
+			goto error_uninitialize_ring;
+
+		ret = adis16204_probe_trigger(st->indio_dev);
+		if (ret)
+			goto error_unregister_line;
+	}
+
+	/* Get the device into a sane initial state */
+	ret = adis16204_initial_setup(st);
+	if (ret)
+		goto error_remove_trigger;
+	return 0;
+
+error_remove_trigger:
+	adis16204_remove_trigger(st->indio_dev);
+error_unregister_line:
+	if (spi->irq)
+		iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+	adis16204_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+	adis16204_unconfigure_ring(st->indio_dev);
+error_free_dev:
+	if (regdone)
+		iio_device_unregister(st->indio_dev);
+	else
+		iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int adis16204_remove(struct spi_device *spi)
+{
+	struct adis16204_state *st = spi_get_drvdata(spi);
+	struct iio_dev *indio_dev = st->indio_dev;
+
+	flush_scheduled_work();
+
+	adis16204_remove_trigger(indio_dev);
+	if (spi->irq)
+		iio_unregister_interrupt_line(indio_dev, 0);
+
+	adis16204_uninitialize_ring(indio_dev->ring);
+	iio_device_unregister(indio_dev);
+	adis16204_unconfigure_ring(indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver adis16204_driver = {
+	.driver = {
+		.name = "adis16204",
+		.owner = THIS_MODULE,
+	},
+	.probe = adis16204_probe,
+	.remove = __devexit_p(adis16204_remove),
+};
+
+static __init int adis16204_init(void)
+{
+	return spi_register_driver(&adis16204_driver);
+}
+module_init(adis16204_init);
+
+static __exit void adis16204_exit(void)
+{
+	spi_unregister_driver(&adis16204_driver);
+}
+module_exit(adis16204_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c
new file mode 100644
index 0000000..e7f88b3
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_ring.c
@@ -0,0 +1,252 @@
+#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/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16204.h"
+
+/**
+ * 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);
+}
+
+static IIO_SCAN_EL_C(supply, ADIS16204_SCAN_SUPPLY, IIO_UNSIGNED(12),
+		     ADIS16204_SUPPLY_OUT, NULL);
+static IIO_SCAN_EL_C(accel_x, ADIS16204_SCAN_ACC_X, IIO_SIGNED(14),
+		     ADIS16204_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16204_SCAN_ACC_Y, IIO_SIGNED(14),
+		     ADIS16204_YACCL_OUT, NULL);
+static IIO_SCAN_EL_C(aux_adc, ADIS16204_SCAN_AUX_ADC, IIO_UNSIGNED(12),
+		     ADIS16204_AUX_ADC, NULL);
+static IIO_SCAN_EL_C(temp, ADIS16204_SCAN_TEMP, IIO_UNSIGNED(12),
+		     ADIS16204_TEMP_OUT, NULL);
+
+static IIO_SCAN_EL_TIMESTAMP(5);
+
+static struct attribute *adis16204_scan_el_attrs[] = {
+	&iio_scan_el_supply.dev_attr.attr,
+	&iio_scan_el_accel_x.dev_attr.attr,
+	&iio_scan_el_accel_y.dev_attr.attr,
+	&iio_scan_el_aux_adc.dev_attr.attr,
+	&iio_scan_el_temp.dev_attr.attr,
+	&iio_scan_el_timestamp.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group adis16204_scan_el_group = {
+	.attrs = adis16204_scan_el_attrs,
+	.name = "scan_elements",
+};
+
+/**
+ * adis16204_poll_func_th() top half interrupt handler called by trigger
+ * @private_data:	iio_dev
+ **/
+static void adis16204_poll_func_th(struct iio_dev *indio_dev)
+{
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	st->last_timestamp = indio_dev->trig->timestamp;
+	schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16204_read_ring_data() read data registers which will be placed into ring
+ * @dev: device associated with child of actual device (iio_dev or iio_trig)
+ * @rx: somewhere to pass back the value read
+ **/
+static int adis16204_read_ring_data(struct device *dev, u8 *rx)
+{
+	struct spi_message msg;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct adis16204_state *st = iio_dev_get_devdata(indio_dev);
+	struct spi_transfer xfers[ADIS16204_OUTPUTS + 1];
+	int ret;
+	int i;
+
+	mutex_lock(&st->buf_lock);
+
+	spi_message_init(&msg);
+
+	memset(xfers, 0, sizeof(xfers));
+	for (i = 0; i <= ADIS16204_OUTPUTS; i++) {
+		xfers[i].bits_per_word = 8;
+		xfers[i].cs_change = 1;
+		xfers[i].len = 2;
+		xfers[i].delay_usecs = 20;
+		xfers[i].tx_buf = st->tx + 2 * i;
+		st->tx[2 * i] = ADIS16204_READ_REG(ADIS16204_SUPPLY_OUT + 2 * i);
+		st->tx[2 * i + 1] = 0;
+		if (i >= 1)
+			xfers[i].rx_buf = rx + 2 * (i - 1);
+		spi_message_add_tail(&xfers[i], &msg);
+	}
+
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		dev_err(&st->us->dev, "problem when burst reading");
+
+	mutex_unlock(&st->buf_lock);
+
+	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 adis16204_trigger_bh_to_ring(struct work_struct *work_s)
+{
+	struct adis16204_state *st
+		= container_of(work_s, struct adis16204_state,
+			       work_trigger_to_ring);
+
+	int i = 0;
+	s16 *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;
+	}
+
+	if (st->indio_dev->scan_count)
+		if (adis16204_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
+			for (; i < st->indio_dev->scan_count; i++) {
+				data[i] = combine_8_to_16(st->rx[i*2+1],
+							  st->rx[i*2]);
+			}
+
+	/* 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(data);
+
+	return;
+}
+
+/* in these circumstances is it better to go with unaligned packing and
+ * deal with the cost?*/
+static int adis16204_data_rdy_ring_preenable(struct iio_dev *indio_dev)
+{
+	size_t size;
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* 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, let timestamp aligned with sizeof(s64) */
+				size = (((indio_dev->scan_count * sizeof(s16)) + sizeof(s64) - 1) & ~(sizeof(s64) - 1))
+					+ 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 adis16204_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 adis16204_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;
+}
+
+void adis16204_unconfigure_ring(struct iio_dev *indio_dev)
+{
+	kfree(indio_dev->pollfunc);
+	iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16204_configure_ring(struct iio_dev *indio_dev)
+{
+	int ret = 0;
+	struct adis16204_state *st = indio_dev->dev_data;
+	struct iio_ring_buffer *ring;
+	INIT_WORK(&st->work_trigger_to_ring, adis16204_trigger_bh_to_ring);
+	/* Set default scan mode */
+
+	iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
+	iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
+	indio_dev->scan_timestamp = true;
+
+	indio_dev->scan_el_attrs = &adis16204_scan_el_group;
+
+	ring = iio_sw_rb_allocate(indio_dev);
+	if (!ring) {
+		ret = -ENOMEM;
+		return ret;
+	}
+	indio_dev->ring = ring;
+	/* Effectively select the ring buffer implementation */
+	iio_ring_sw_register_funcs(&ring->access);
+	ring->preenable = &adis16204_data_rdy_ring_preenable;
+	ring->postenable = &adis16204_data_rdy_ring_postenable;
+	ring->predisable = &adis16204_data_rdy_ring_predisable;
+	ring->owner = THIS_MODULE;
+
+	indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
+	if (indio_dev->pollfunc == NULL) {
+		ret = -ENOMEM;
+		goto error_iio_sw_rb_free;;
+	}
+	indio_dev->pollfunc->poll_func_main = &adis16204_poll_func_th;
+	indio_dev->pollfunc->private_data = indio_dev;
+	indio_dev->modes |= INDIO_RING_TRIGGERED;
+	return 0;
+
+error_iio_sw_rb_free:
+	iio_sw_rb_free(indio_dev->ring);
+	return ret;
+}
+
+int adis16204_initialize_ring(struct iio_ring_buffer *ring)
+{
+	return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16204_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+	iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16204_trigger.c b/drivers/staging/iio/accel/adis16204_trigger.c
new file mode 100644
index 0000000..2bc05dc
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_trigger.c
@@ -0,0 +1,124 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../trigger.h"
+#include "adis16204.h"
+
+/**
+ * adis16204_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16204_data_rdy_trig_poll(struct iio_dev *dev_info,
+				       int index,
+				       s64 timestamp,
+				       int no_test)
+{
+	struct adis16204_state *st = iio_dev_get_devdata(dev_info);
+	struct iio_trigger *trig = st->trig;
+
+	trig->timestamp = timestamp;
+	iio_trigger_poll(trig);
+
+	return IRQ_HANDLED;
+}
+
+IIO_EVENT_SH(data_rdy_trig, &adis16204_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16204_trigger_attrs[] = {
+	&dev_attr_name.attr,
+	NULL,
+};
+
+static const struct attribute_group adis16204_trigger_attr_group = {
+	.attrs = adis16204_trigger_attrs,
+};
+
+/**
+ * adis16204_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16204_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis16204_state *st = trig->private_data;
+	struct iio_dev *indio_dev = st->indio_dev;
+	int ret = 0;
+
+	dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+	ret = adis16204_set_irq(&st->indio_dev->dev, state);
+	if (state == false) {
+		iio_remove_event_from_list(&iio_event_data_rdy_trig,
+					   &indio_dev->interrupts[0]
+					   ->ev_list);
+		flush_scheduled_work();
+	} else {
+		iio_add_event_to_list(&iio_event_data_rdy_trig,
+				      &indio_dev->interrupts[0]->ev_list);
+	}
+	return ret;
+}
+
+/**
+ * adis16204_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig:	the datardy trigger
+ **/
+static int adis16204_trig_try_reen(struct iio_trigger *trig)
+{
+	struct adis16204_state *st = trig->private_data;
+	enable_irq(st->us->irq);
+	return 0;
+}
+
+int adis16204_probe_trigger(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct adis16204_state *st = indio_dev->dev_data;
+
+	st->trig = iio_allocate_trigger();
+	st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL);
+	if (!st->trig->name) {
+		ret = -ENOMEM;
+		goto error_free_trig;
+	}
+	snprintf((char *)st->trig->name,
+		 IIO_TRIGGER_NAME_LENGTH,
+		 "adis16204-dev%d", indio_dev->id);
+	st->trig->dev.parent = &st->us->dev;
+	st->trig->owner = THIS_MODULE;
+	st->trig->private_data = st;
+	st->trig->set_trigger_state = &adis16204_data_rdy_trigger_set_state;
+	st->trig->try_reenable = &adis16204_trig_try_reen;
+	st->trig->control_attrs = &adis16204_trigger_attr_group;
+	ret = iio_trigger_register(st->trig);
+
+	/* select default trigger */
+	indio_dev->trig = st->trig;
+	if (ret)
+		goto error_free_trig_name;
+
+	return 0;
+
+error_free_trig_name:
+	kfree(st->trig->name);
+error_free_trig:
+	iio_free_trigger(st->trig);
+
+	return ret;
+}
+
+void adis16204_remove_trigger(struct iio_dev *indio_dev)
+{
+	struct adis16204_state *state = indio_dev->dev_data;
+
+	iio_trigger_unregister(state->trig);
+	kfree(state->trig->name);
+	iio_free_trigger(state->trig);
+}
-- 
1.7.2

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

end of thread, other threads:[~2010-10-29  1:30 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-23 10:49 [PATCH 1/3] staging: iio: new adis16201 driver Mike Frysinger
2010-10-23 10:49 ` [PATCH 2/3] staging: iio: new adis16203 driver Mike Frysinger
2010-10-23 17:22   ` Jonathan Cameron
2010-10-23 10:49 ` [PATCH 3/3] staging: iio: new adis16204 driver Mike Frysinger
2010-10-23 17:45   ` Jonathan Cameron
2010-10-23 17:04 ` [PATCH 1/3] staging: iio: new adis16201 driver Jonathan Cameron
2010-10-23 17:21   ` Jonathan Cameron
2010-10-23 17:52 ` Jonathan Cameron
2010-10-23 20:32   ` [Device-drivers-devel] " Mike Frysinger
2010-10-24 11:09     ` Jonathan Cameron
2010-10-24 15:58       ` Mike Frysinger
2010-10-24 18:24         ` Jonathan Cameron
2010-10-24 18:22           ` Mike Frysinger
2010-10-24 18:43             ` Jonathan Cameron
2010-10-29  1:30             ` Greg KH
2010-10-24 18:36           ` Michael Frysinger
  -- strict thread matches above, loose matches on Subject: below --
2010-08-07  4:44 Mike Frysinger
2010-08-07  4:44 ` [PATCH 3/3] staging: iio: new adis16204 driver Mike Frysinger

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