* [PATCH 1/2] staging:iio: adis16209 driver
@ 2010-05-06 13:42 Jonathan Cameron
2010-05-06 13:42 ` [PATCH 2/2] staging:iio: adis16240 driver Jonathan Cameron
0 siblings, 1 reply; 4+ messages in thread
From: Jonathan Cameron @ 2010-05-06 13:42 UTC (permalink / raw)
To: linux-iio; +Cc: Barry Song, Jonathan Cameron
From: Barry Song <Barry.Song@analog.com>
Signed-off-by: Barry Song <Barry.Song@analog.com>
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
This includes:
* the changes Barry made in the blackfin tree in response to review,
* reapplying the changes to make it build on current mainline
* clean ups to get rid of check patch warnings etc.
drivers/staging/iio/accel/Kconfig | 9 +
drivers/staging/iio/accel/Makefile | 4 +
drivers/staging/iio/accel/adis16209.h | 193 ++++++++
drivers/staging/iio/accel/adis16209_core.c | 615 +++++++++++++++++++++++++
drivers/staging/iio/accel/adis16209_ring.c | 266 +++++++++++
drivers/staging/iio/accel/adis16209_trigger.c | 124 +++++
6 files changed, 1211 insertions(+), 0 deletions(-)
diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index 3d3c333..1d89e21 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -3,6 +3,15 @@
#
comment "Accelerometers"
+config ADIS16209
+ tristate "Analog Devices ADIS16209 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 adis16209 dual-axis digital inclinometer
+ and accelerometer.
+
config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver"
depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index d5335f9..f8f2124 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -1,6 +1,10 @@
#
# Makefile for industrial I/O accelerometer drivers
#
+adis16209-y := adis16209_core.o
+adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
+obj-$(CONFIG_ADIS16209) += adis16209.o
+
obj-$(CONFIG_KXSD9) += kxsd9.o
lis3l02dq-y := lis3l02dq_core.o
diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h
new file mode 100644
index 0000000..877fd2a
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209.h
@@ -0,0 +1,193 @@
+#ifndef SPI_ADIS16209_H_
+#define SPI_ADIS16209_H_
+
+#define ADIS16209_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16209_READ_REG(a) a
+#define ADIS16209_WRITE_REG(a) ((a) | 0x80)
+
+/* Flash memory write count */
+#define ADIS16209_FLASH_CNT 0x00
+/* Output, power supply */
+#define ADIS16209_SUPPLY_OUT 0x02
+/* Output, x-axis accelerometer */
+#define ADIS16209_XACCL_OUT 0x04
+/* Output, y-axis accelerometer */
+#define ADIS16209_YACCL_OUT 0x06
+/* Output, auxiliary ADC input */
+#define ADIS16209_AUX_ADC 0x08
+/* Output, temperature */
+#define ADIS16209_TEMP_OUT 0x0A
+/* Output, x-axis inclination */
+#define ADIS16209_XINCL_OUT 0x0C
+/* Output, y-axis inclination */
+#define ADIS16209_YINCL_OUT 0x0E
+/* Output, +/-180 vertical rotational position */
+#define ADIS16209_ROT_OUT 0x10
+/* Calibration, x-axis acceleration offset null */
+#define ADIS16209_XACCL_NULL 0x12
+/* Calibration, y-axis acceleration offset null */
+#define ADIS16209_YACCL_NULL 0x14
+/* Calibration, x-axis inclination offset null */
+#define ADIS16209_XINCL_NULL 0x16
+/* Calibration, y-axis inclination offset null */
+#define ADIS16209_YINCL_NULL 0x18
+/* Calibration, vertical rotation offset null */
+#define ADIS16209_ROT_NULL 0x1A
+/* Alarm 1 amplitude threshold */
+#define ADIS16209_ALM_MAG1 0x20
+/* Alarm 2 amplitude threshold */
+#define ADIS16209_ALM_MAG2 0x22
+/* Alarm 1, sample period */
+#define ADIS16209_ALM_SMPL1 0x24
+/* Alarm 2, sample period */
+#define ADIS16209_ALM_SMPL2 0x26
+/* Alarm control */
+#define ADIS16209_ALM_CTRL 0x28
+/* Auxiliary DAC data */
+#define ADIS16209_AUX_DAC 0x30
+/* General-purpose digital input/output control */
+#define ADIS16209_GPIO_CTRL 0x32
+/* Miscellaneous control */
+#define ADIS16209_MSC_CTRL 0x34
+/* Internal sample period (rate) control */
+#define ADIS16209_SMPL_PRD 0x36
+/* Operation, filter configuration */
+#define ADIS16209_AVG_CNT 0x38
+/* Operation, sleep mode control */
+#define ADIS16209_SLP_CNT 0x3A
+/* Diagnostics, system status register */
+#define ADIS16209_DIAG_STAT 0x3C
+/* Operation, system command register */
+#define ADIS16209_GLOB_CMD 0x3E
+
+#define ADIS16209_OUTPUTS 8
+
+/* MSC_CTRL */
+/* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST (1 << 10)
+/* Self-test enable */
+#define ADIS16209_MSC_CTRL_SELF_TEST_EN (1 << 8)
+/* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16209_MSC_CTRL_DATA_RDY_EN (1 << 2)
+/* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16209_MSC_CTRL_ACTIVE_HIGH (1 << 1)
+/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 (1 << 0)
+
+/* DIAG_STAT */
+/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16209_DIAG_STAT_ALARM2 (1<<9)
+/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16209_DIAG_STAT_ALARM1 (1<<8)
+/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */
+#define ADIS16209_DIAG_STAT_SELFTEST_FAIL (1<<5)
+/* SPI communications failure */
+#define ADIS16209_DIAG_STAT_SPI_FAIL (1<<3)
+/* Flash update failure */
+#define ADIS16209_DIAG_STAT_FLASH_UPT (1<<2)
+/* Power supply above 3.625 V */
+#define ADIS16209_DIAG_STAT_POWER_HIGH (1<<1)
+/* Power supply below 3.15 V */
+#define ADIS16209_DIAG_STAT_POWER_LOW (1<<0)
+
+/* GLOB_CMD */
+#define ADIS16209_GLOB_CMD_SW_RESET (1<<7)
+#define ADIS16209_GLOB_CMD_CLEAR_STAT (1<<4)
+#define ADIS16209_GLOB_CMD_FACTORY_CAL (1<<1)
+
+#define ADIS16209_MAX_TX 24
+#define ADIS16209_MAX_RX 24
+
+#define ADIS16209_ERROR_ACTIVE (1<<14)
+
+/**
+ * struct adis16209_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 adis16209_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 adis16209_set_irq(struct device *dev, bool enable);
+
+#ifdef CONFIG_IIO_RING_BUFFER
+enum adis16209_scan {
+ ADIS16209_SCAN_SUPPLY,
+ ADIS16209_SCAN_ACC_X,
+ ADIS16209_SCAN_ACC_Y,
+ ADIS16209_SCAN_AUX_ADC,
+ ADIS16209_SCAN_TEMP,
+ ADIS16209_SCAN_INCLI_X,
+ ADIS16209_SCAN_INCLI_Y,
+ ADIS16209_SCAN_ROT,
+};
+
+void adis16209_remove_trigger(struct iio_dev *indio_dev);
+int adis16209_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t adis16209_read_data_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+int adis16209_configure_ring(struct iio_dev *indio_dev);
+void adis16209_unconfigure_ring(struct iio_dev *indio_dev);
+
+int adis16209_initialize_ring(struct iio_ring_buffer *ring);
+void adis16209_uninitialize_ring(struct iio_ring_buffer *ring);
+#else /* CONFIG_IIO_RING_BUFFER */
+
+static inline void adis16209_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16209_probe_trigger(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline ssize_t
+adis16209_read_data_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+static int adis16209_configure_ring(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void adis16209_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+
+static inline int adis16209_initialize_ring(struct iio_ring_buffer *ring)
+{
+ return 0;
+}
+
+static inline void adis16209_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+}
+
+#endif /* CONFIG_IIO_RING_BUFFER */
+#endif /* SPI_ADIS16209_H_ */
diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c
new file mode 100644
index 0000000..ac375c5
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209_core.c
@@ -0,0 +1,615 @@
+/*
+ * ADIS16209 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/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 "adis16209.h"
+
+#define DRIVER_NAME "adis16209"
+
+static int adis16209_check_status(struct device *dev);
+
+/**
+ * adis16209_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 adis16209_spi_write_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct adis16209_state *st = iio_dev_get_devdata(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16209_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * adis16209_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 adis16209_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 adis16209_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] = ADIS16209_WRITE_REG(lower_reg_address);
+ st->tx[1] = value & 0xFF;
+ st->tx[2] = ADIS16209_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;
+}
+
+/**
+ * adis16209_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 adis16209_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 adis16209_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] = ADIS16209_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 adis16209_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 = adis16209_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ if (val & ADIS16209_ERROR_ACTIVE)
+ adis16209_check_status(dev);
+
+ return sprintf(buf, "%u\n", val & 0x0FFF);
+}
+
+static ssize_t adis16209_read_14bit_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 = adis16209_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ if (val & ADIS16209_ERROR_ACTIVE)
+ adis16209_check_status(dev);
+
+ return sprintf(buf, "%u\n", val & 0x3FFF);
+}
+
+static ssize_t adis16209_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 = adis16209_spi_read_reg_16(dev, ADIS16209_TEMP_OUT, (u16 *)&val);
+ if (ret)
+ goto error_ret;
+
+ if (val & ADIS16209_ERROR_ACTIVE)
+ adis16209_check_status(dev);
+
+ val &= 0xFFF;
+ ret = sprintf(buf, "%d\n", val);
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static ssize_t adis16209_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 = adis16209_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+ if (!ret) {
+ if (val & ADIS16209_ERROR_ACTIVE)
+ adis16209_check_status(dev);
+
+ val = ((s16)(val << 2) >> 2);
+ ret = sprintf(buf, "%d\n", val);
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static ssize_t adis16209_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 = adis16209_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int adis16209_reset(struct device *dev)
+{
+ int ret;
+ ret = adis16209_spi_write_reg_8(dev,
+ ADIS16209_GLOB_CMD,
+ ADIS16209_GLOB_CMD_SW_RESET);
+ if (ret)
+ dev_err(dev, "problem resetting device");
+
+ return ret;
+}
+
+static ssize_t adis16209_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 adis16209_reset(dev);
+ }
+ return -EINVAL;
+}
+
+int adis16209_set_irq(struct device *dev, bool enable)
+{
+ int ret = 0;
+ u16 msc;
+
+ ret = adis16209_spi_read_reg_16(dev, ADIS16209_MSC_CTRL, &msc);
+ if (ret)
+ goto error_ret;
+
+ msc |= ADIS16209_MSC_CTRL_ACTIVE_HIGH;
+ msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_DIO2;
+ if (enable)
+ msc |= ADIS16209_MSC_CTRL_DATA_RDY_EN;
+ else
+ msc &= ~ADIS16209_MSC_CTRL_DATA_RDY_EN;
+
+ ret = adis16209_spi_write_reg_16(dev, ADIS16209_MSC_CTRL, msc);
+
+error_ret:
+ return ret;
+}
+
+static int adis16209_check_status(struct device *dev)
+{
+ u16 status;
+ int ret;
+
+ ret = adis16209_spi_read_reg_16(dev, ADIS16209_DIAG_STAT, &status);
+ if (ret < 0) {
+ dev_err(dev, "Reading status failed\n");
+ goto error_ret;
+ }
+ ret = status & 0x1F;
+
+ if (status & ADIS16209_DIAG_STAT_SELFTEST_FAIL)
+ dev_err(dev, "Self test failure\n");
+ if (status & ADIS16209_DIAG_STAT_SPI_FAIL)
+ dev_err(dev, "SPI failure\n");
+ if (status & ADIS16209_DIAG_STAT_FLASH_UPT)
+ dev_err(dev, "Flash update failed\n");
+ if (status & ADIS16209_DIAG_STAT_POWER_HIGH)
+ dev_err(dev, "Power supply above 3.625V\n");
+ if (status & ADIS16209_DIAG_STAT_POWER_LOW)
+ dev_err(dev, "Power supply below 3.15V\n");
+
+error_ret:
+ return ret;
+}
+
+static int adis16209_self_test(struct device *dev)
+{
+ int ret;
+ ret = adis16209_spi_write_reg_16(dev,
+ ADIS16209_MSC_CTRL,
+ ADIS16209_MSC_CTRL_SELF_TEST_EN);
+ if (ret) {
+ dev_err(dev, "problem starting self test");
+ goto err_ret;
+ }
+
+ adis16209_check_status(dev);
+
+err_ret:
+ return ret;
+}
+
+static int adis16209_initial_setup(struct adis16209_state *st)
+{
+ int ret;
+ struct device *dev = &st->indio_dev->dev;
+
+ /* Disable IRQ */
+ ret = adis16209_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ /* Do self test */
+ ret = adis16209_self_test(dev);
+ if (ret) {
+ dev_err(dev, "self test failure");
+ goto err_ret;
+ }
+
+ /* Read status register to check the result */
+ ret = adis16209_check_status(dev);
+ if (ret) {
+ adis16209_reset(dev);
+ dev_err(dev, "device not playing ball -> reset");
+ msleep(ADIS16209_STARTUP_DELAY);
+ ret = adis16209_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, adis16209_read_14bit_unsigned,
+ ADIS16209_SUPPLY_OUT);
+static IIO_CONST_ATTR(in_supply_scale, "0.30518");
+static IIO_DEV_ATTR_IN_RAW(0, adis16209_read_12bit_unsigned,
+ ADIS16209_AUX_ADC);
+static IIO_CONST_ATTR(in0_scale, "0.6105");
+
+static IIO_DEV_ATTR_ACCEL_X(adis16209_read_14bit_signed,
+ ADIS16209_XACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16209_read_14bit_signed,
+ ADIS16209_YACCL_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+ adis16209_read_14bit_signed,
+ adis16209_write_16bit,
+ ADIS16209_XACCL_NULL);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+ adis16209_read_14bit_signed,
+ adis16209_write_16bit,
+ ADIS16209_YACCL_NULL);
+static IIO_CONST_ATTR(accel_scale, "0.24414");
+
+static IIO_DEV_ATTR_INCLI_X(adis16209_read_14bit_signed,
+ ADIS16209_XINCL_OUT);
+static IIO_DEV_ATTR_INCLI_Y(adis16209_read_14bit_signed,
+ ADIS16209_YINCL_OUT);
+static IIO_DEV_ATTR_INCLI_X_OFFSET(S_IWUSR | S_IRUGO,
+ adis16209_read_14bit_signed,
+ adis16209_write_16bit,
+ ADIS16209_XACCL_NULL);
+static IIO_DEV_ATTR_INCLI_Y_OFFSET(S_IWUSR | S_IRUGO,
+ adis16209_read_14bit_signed,
+ adis16209_write_16bit,
+ ADIS16209_YACCL_NULL);
+static IIO_CONST_ATTR(incli_scale, "0.025");
+
+static IIO_DEVICE_ATTR(rot_raw, S_IRUGO, adis16209_read_14bit_signed,
+ NULL, ADIS16209_ROT_OUT);
+
+static IIO_DEV_ATTR_TEMP(adis16209_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, adis16209_write_reset, 0);
+
+static IIO_CONST_ATTR(name, "adis16209");
+
+static struct attribute *adis16209_event_attributes[] = {
+ NULL
+};
+
+static struct attribute_group adis16209_event_attribute_group = {
+ .attrs = adis16209_event_attributes,
+};
+
+static struct attribute *adis16209_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,
+ &iio_dev_attr_rot_raw.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16209_attribute_group = {
+ .attrs = adis16209_attributes,
+};
+
+static int __devinit adis16209_probe(struct spi_device *spi)
+{
+ int ret, regdone = 0;
+ struct adis16209_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)*ADIS16209_MAX_RX, GFP_KERNEL);
+ if (st->rx == NULL) {
+ ret = -ENOMEM;
+ goto error_free_st;
+ }
+ st->tx = kzalloc(sizeof(*st->tx)*ADIS16209_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 = &adis16209_event_attribute_group;
+ st->indio_dev->attrs = &adis16209_attribute_group;
+ st->indio_dev->dev_data = (void *)(st);
+ st->indio_dev->driver_module = THIS_MODULE;
+ st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis16209_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 = adis16209_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,
+ "adis16209");
+ if (ret)
+ goto error_uninitialize_ring;
+
+ ret = adis16209_probe_trigger(st->indio_dev);
+ if (ret)
+ goto error_unregister_line;
+ }
+
+ /* Get the device into a sane initial state */
+ ret = adis16209_initial_setup(st);
+ if (ret)
+ goto error_remove_trigger;
+ return 0;
+
+error_remove_trigger:
+ adis16209_remove_trigger(st->indio_dev);
+error_unregister_line:
+ if (spi->irq)
+ iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+ adis16209_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+ adis16209_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 adis16209_remove(struct spi_device *spi)
+{
+ struct adis16209_state *st = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = st->indio_dev;
+
+ flush_scheduled_work();
+
+ adis16209_remove_trigger(indio_dev);
+ if (spi->irq)
+ iio_unregister_interrupt_line(indio_dev, 0);
+
+ adis16209_uninitialize_ring(indio_dev->ring);
+ iio_device_unregister(indio_dev);
+ adis16209_unconfigure_ring(indio_dev);
+ kfree(st->tx);
+ kfree(st->rx);
+ kfree(st);
+
+ return 0;
+}
+
+static struct spi_driver adis16209_driver = {
+ .driver = {
+ .name = "adis16209",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16209_probe,
+ .remove = __devexit_p(adis16209_remove),
+};
+
+static __init int adis16209_init(void)
+{
+ return spi_register_driver(&adis16209_driver);
+}
+module_init(adis16209_init);
+
+static __exit void adis16209_exit(void)
+{
+ spi_unregister_driver(&adis16209_driver);
+}
+module_exit(adis16209_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c
new file mode 100644
index 0000000..533e285
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209_ring.c
@@ -0,0 +1,266 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16209.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, ADIS16209_SCAN_SUPPLY, IIO_UNSIGNED(14),
+ ADIS16209_SUPPLY_OUT, NULL);
+static IIO_SCAN_EL_C(accel_x, ADIS16209_SCAN_ACC_X, IIO_SIGNED(14),
+ ADIS16209_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16209_SCAN_ACC_Y, IIO_SIGNED(14),
+ ADIS16209_YACCL_OUT, NULL);
+static IIO_SCAN_EL_C(aux_adc, ADIS16209_SCAN_AUX_ADC, IIO_UNSIGNED(12),
+ ADIS16209_AUX_ADC, NULL);
+static IIO_SCAN_EL_C(temp, ADIS16209_SCAN_TEMP, IIO_UNSIGNED(12),
+ ADIS16209_TEMP_OUT, NULL);
+static IIO_SCAN_EL_C(incli_x, ADIS16209_SCAN_INCLI_X, IIO_SIGNED(14),
+ ADIS16209_XINCL_OUT, NULL);
+static IIO_SCAN_EL_C(incli_y, ADIS16209_SCAN_INCLI_Y, IIO_SIGNED(14),
+ ADIS16209_YINCL_OUT, NULL);
+static IIO_SCAN_EL_C(rot, ADIS16209_SCAN_ROT, IIO_SIGNED(14),
+ ADIS16209_ROT_OUT, NULL);
+
+static IIO_SCAN_EL_TIMESTAMP(8);
+
+static struct attribute *adis16209_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_rot.dev_attr.attr,
+ &iio_scan_el_timestamp.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group adis16209_scan_el_group = {
+ .attrs = adis16209_scan_el_attrs,
+ .name = "scan_elements",
+};
+
+/**
+ * adis16209_poll_func_th() top half interrupt handler called by trigger
+ * @private_data: iio_dev
+ **/
+static void adis16209_poll_func_th(struct iio_dev *indio_dev)
+{
+ struct adis16209_state *st = iio_dev_get_devdata(indio_dev);
+ st->last_timestamp = indio_dev->trig->timestamp;
+ schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16209_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 adis16209_read_ring_data(struct device *dev, u8 *rx)
+{
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct adis16209_state *st = iio_dev_get_devdata(indio_dev);
+ struct spi_transfer xfers[ADIS16209_OUTPUTS + 1];
+ int ret;
+ int i;
+
+ mutex_lock(&st->buf_lock);
+
+ spi_message_init(&msg);
+
+ memset(xfers, 0, sizeof(xfers));
+ for (i = 0; i <= ADIS16209_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]
+ = ADIS16209_READ_REG(ADIS16209_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 adis16209_trigger_bh_to_ring(struct work_struct *work_s)
+{
+ struct adis16209_state *st
+ = container_of(work_s, struct adis16209_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 (adis16209_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 adis16209_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 (aligned to s64) and data */
+ 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 adis16209_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 adis16209_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 adis16209_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ kfree(indio_dev->pollfunc);
+ iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16209_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct adis16209_state *st = indio_dev->dev_data;
+ struct iio_ring_buffer *ring;
+ INIT_WORK(&st->work_trigger_to_ring, adis16209_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_rot.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 = &adis16209_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 = &adis16209_data_rdy_ring_preenable;
+ ring->postenable = &adis16209_data_rdy_ring_postenable;
+ ring->predisable = &adis16209_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 = &adis16209_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 adis16209_initialize_ring(struct iio_ring_buffer *ring)
+{
+ return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16209_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+ iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16209_trigger.c b/drivers/staging/iio/accel/adis16209_trigger.c
new file mode 100644
index 0000000..4a0507c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209_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 "adis16209.h"
+
+/**
+ * adis16209_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16209_data_rdy_trig_poll(struct iio_dev *dev_info,
+ int index,
+ s64 timestamp,
+ int no_test)
+{
+ struct adis16209_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, &adis16209_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16209_trigger_attrs[] = {
+ &dev_attr_name.attr,
+ NULL,
+};
+
+static const struct attribute_group adis16209_trigger_attr_group = {
+ .attrs = adis16209_trigger_attrs,
+};
+
+/**
+ * adis16209_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16209_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct adis16209_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 = adis16209_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;
+}
+
+/**
+ * adis16209_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig: the datardy trigger
+ **/
+static int adis16209_trig_try_reen(struct iio_trigger *trig)
+{
+ struct adis16209_state *st = trig->private_data;
+ enable_irq(st->us->irq);
+ return 0;
+}
+
+int adis16209_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct adis16209_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,
+ "adis16209-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 = &adis16209_data_rdy_trigger_set_state;
+ st->trig->try_reenable = &adis16209_trig_try_reen;
+ st->trig->control_attrs = &adis16209_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 adis16209_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct adis16209_state *state = indio_dev->dev_data;
+
+ iio_trigger_unregister(state->trig);
+ kfree(state->trig->name);
+ iio_free_trigger(state->trig);
+}
--
1.6.4.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] staging:iio: adis16240 driver
2010-05-06 13:42 [PATCH 1/2] staging:iio: adis16209 driver Jonathan Cameron
@ 2010-05-06 13:42 ` Jonathan Cameron
2010-05-07 2:54 ` Barry Song
0 siblings, 1 reply; 4+ messages in thread
From: Jonathan Cameron @ 2010-05-06 13:42 UTC (permalink / raw)
To: linux-iio; +Cc: Barry Song, Jonathan Cameron
From: Barry Song <Barry.Song@analog.com>
Signed-off-by: Barry Song <Barry.Song@analog.com>
Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
---
This one is new to the list. Barry informed me that they have completed
testing and hopefully I haven't broken anything. (Barry, please confirm
your sign off, I've put it above to make sure I don't forget it)
Changes I've made in merging this with mainline tree:
* Usual renames to add _raw and get rid of volt etc.
* Other changes:
* accel_xpeak -> accel_x_peak_raw
* accel_xyzpeak -> accel_xyz_squared_raw
* I have taken a number of attribute definitions out of accel.h and into
this driver as I am yet to be convinced that they are general enough.
They can move to there at a later date if we get them turning up in a
couple of drivers.
* Checkpatch and sparse related fixes
Clearly this is a very interesting chip and there are lots of interesting
features not currently supported by the driver. Still is a very good base
to build on.
drivers/staging/iio/accel/Kconfig | 9 +
drivers/staging/iio/accel/Makefile | 4 +
drivers/staging/iio/accel/adis16240_core.c | 599 +++++++++++++++++++++++++
drivers/staging/iio/accel/adis16240_ring.c | 254 +++++++++++
drivers/staging/iio/accel/adis16240_trigger.c | 124 +++++
5 files changed, 990 insertions(+), 0 deletions(-)
diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index 1d89e21..8f3f70f 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -12,6 +12,15 @@ config ADIS16209
Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer
and accelerometer.
+config ADIS16240
+ tristate "Analog Devices ADIS16240 Programmable 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 adis16240 programmable
+ impact Sensor and recorder.
+
config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver"
depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index f8f2124..0e6762c 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -5,6 +5,10 @@ adis16209-y := adis16209_core.o
adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
obj-$(CONFIG_ADIS16209) += adis16209.o
+adis16240-y := adis16240_core.o
+adis16240-$(CONFIG_IIO_RING_BUFFER) += adis16240_ring.o adis16240_trigger.o
+obj-$(CONFIG_ADIS16240) += adis16240.o
+
obj-$(CONFIG_KXSD9) += kxsd9.o
lis3l02dq-y := lis3l02dq_core.o
diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c
new file mode 100644
index 0000000..54fd6d7
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240_core.c
@@ -0,0 +1,599 @@
+/*
+ * ADIS16240 Programmable Impact Sensor and Recorder 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/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "accel.h"
+#include "../adc/adc.h"
+
+#include "adis16240.h"
+
+#define DRIVER_NAME "adis16240"
+
+static int adis16240_check_status(struct device *dev);
+
+/**
+ * adis16240_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 adis16240_spi_write_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16240_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * adis16240_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 adis16240_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 adis16240_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,
+ .delay_usecs = 25,
+ }, {
+ .tx_buf = st->tx + 2,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ .delay_usecs = 25,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16240_WRITE_REG(lower_reg_address);
+ st->tx[1] = value & 0xFF;
+ st->tx[2] = ADIS16240_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;
+}
+
+/**
+ * adis16240_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 adis16240_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 adis16240_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 = 25,
+ }, {
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ .delay_usecs = 25,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADIS16240_READ_REG(lower_reg_address);
+ st->tx[1] = 0;
+ st->tx[2] = 0;
+ st->tx[3] = 0;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ spi_message_add_tail(&xfers[1], &msg);
+ ret = spi_sync(st->us, &msg);
+ if (ret) {
+ dev_err(&st->us->dev,
+ "problem when reading 16 bit register 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 adis16240_spi_read_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ unsigned bits)
+{
+ int ret;
+ s16 val = 0;
+ unsigned shift = 16 - bits;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = adis16240_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
+ if (ret)
+ return ret;
+
+ if (val & ADIS16240_ERROR_ACTIVE)
+ adis16240_check_status(dev);
+
+ val = ((s16)(val << shift) >> shift);
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t adis16240_read_10bit_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 = adis16240_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ if (val & ADIS16240_ERROR_ACTIVE)
+ adis16240_check_status(dev);
+
+ return sprintf(buf, "%u\n", val & 0x03FF);
+}
+
+static ssize_t adis16240_read_10bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16240_spi_read_signed(dev, attr, buf, 10);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static ssize_t adis16240_read_12bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ ssize_t ret;
+
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16240_spi_read_signed(dev, attr, buf, 12);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static ssize_t adis16240_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 = adis16240_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int adis16240_reset(struct device *dev)
+{
+ int ret;
+ ret = adis16240_spi_write_reg_8(dev,
+ ADIS16240_GLOB_CMD,
+ ADIS16240_GLOB_CMD_SW_RESET);
+ if (ret)
+ dev_err(dev, "problem resetting device");
+
+ return ret;
+}
+
+static ssize_t adis16240_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 adis16240_reset(dev);
+ }
+ return -EINVAL;
+}
+
+int adis16240_set_irq(struct device *dev, bool enable)
+{
+ int ret = 0;
+ u16 msc;
+
+ ret = adis16240_spi_read_reg_16(dev, ADIS16240_MSC_CTRL, &msc);
+ if (ret)
+ goto error_ret;
+
+ msc |= ADIS16240_MSC_CTRL_ACTIVE_HIGH;
+ msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_DIO2;
+ if (enable)
+ msc |= ADIS16240_MSC_CTRL_DATA_RDY_EN;
+ else
+ msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_EN;
+
+ ret = adis16240_spi_write_reg_16(dev, ADIS16240_MSC_CTRL, msc);
+
+error_ret:
+ return ret;
+}
+
+static int adis16240_self_test(struct device *dev)
+{
+ int ret;
+ ret = adis16240_spi_write_reg_16(dev,
+ ADIS16240_MSC_CTRL,
+ ADIS16240_MSC_CTRL_SELF_TEST_EN);
+ if (ret) {
+ dev_err(dev, "problem starting self test");
+ goto err_ret;
+ }
+
+ msleep(ADIS16240_STARTUP_DELAY);
+
+ adis16240_check_status(dev);
+
+err_ret:
+ return ret;
+}
+
+static int adis16240_check_status(struct device *dev)
+{
+ u16 status;
+ int ret;
+
+ ret = adis16240_spi_read_reg_16(dev, ADIS16240_DIAG_STAT, &status);
+
+ if (ret < 0) {
+ dev_err(dev, "Reading status failed\n");
+ goto error_ret;
+ }
+
+ ret = status & 0x2F;
+ if (status & ADIS16240_DIAG_STAT_PWRON_FAIL)
+ dev_err(dev, "Power-on, self-test fail\n");
+ if (status & ADIS16240_DIAG_STAT_SPI_FAIL)
+ dev_err(dev, "SPI failure\n");
+ if (status & ADIS16240_DIAG_STAT_FLASH_UPT)
+ dev_err(dev, "Flash update failed\n");
+ if (status & ADIS16240_DIAG_STAT_POWER_HIGH)
+ dev_err(dev, "Power supply above 3.625V\n");
+ if (status & ADIS16240_DIAG_STAT_POWER_LOW)
+ dev_err(dev, "Power supply below 2.225V\n");
+
+error_ret:
+ return ret;
+}
+
+static int adis16240_initial_setup(struct adis16240_state *st)
+{
+ int ret;
+ struct device *dev = &st->indio_dev->dev;
+
+ /* Disable IRQ */
+ ret = adis16240_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ /* Do self test */
+ ret = adis16240_self_test(dev);
+ if (ret) {
+ dev_err(dev, "self test failure");
+ goto err_ret;
+ }
+
+ /* Read status register to check the result */
+ ret = adis16240_check_status(dev);
+ if (ret) {
+ adis16240_reset(dev);
+ dev_err(dev, "device not playing ball -> reset");
+ msleep(ADIS16240_STARTUP_DELAY);
+ ret = adis16240_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, adis16240_read_10bit_unsigned,
+ ADIS16240_SUPPLY_OUT);
+static IIO_DEV_ATTR_IN_RAW(0, adis16240_read_10bit_signed,
+ ADIS16240_AUX_ADC);
+static IIO_CONST_ATTR(in_supply_scale, "0.00488");
+static IIO_DEV_ATTR_ACCEL_X(adis16240_read_10bit_signed,
+ ADIS16240_XACCL_OUT);
+static IIO_DEVICE_ATTR(accel_x_peak_raw, S_IRUGO,
+ adis16240_read_10bit_signed, NULL,
+ ADIS16240_XPEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_Y(adis16240_read_10bit_signed,
+ ADIS16240_YACCL_OUT);
+static IIO_DEVICE_ATTR(accel_y_peak_raw, S_IRUGO,
+ adis16240_read_10bit_signed, NULL,
+ ADIS16240_YPEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_Z(adis16240_read_10bit_signed,
+ ADIS16240_ZACCL_OUT);
+static IIO_DEVICE_ATTR(accel_z_peak_raw, S_IRUGO,
+ adis16240_read_10bit_signed, NULL,
+ ADIS16240_ZPEAK_OUT);
+
+static IIO_DEVICE_ATTR(accel_xyz_squared_peak_raw, S_IRUGO,
+ adis16240_read_12bit_signed, NULL,
+ ADIS16240_XYZPEAK_OUT);
+static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
+ adis16240_read_10bit_signed,
+ adis16240_write_16bit,
+ ADIS16240_XACCL_OFF);
+static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
+ adis16240_read_10bit_signed,
+ adis16240_write_16bit,
+ ADIS16240_YACCL_OFF);
+static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO,
+ adis16240_read_10bit_signed,
+ adis16240_write_16bit,
+ ADIS16240_ZACCL_OFF);
+static IIO_DEV_ATTR_TEMP_RAW(adis16240_read_10bit_unsigned);
+static IIO_CONST_ATTR(temp_scale, "0.244");
+
+static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16240_write_reset, 0);
+
+static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("4096");
+
+static IIO_CONST_ATTR(name, "adis16240");
+
+static struct attribute *adis16240_event_attributes[] = {
+ NULL
+};
+
+static struct attribute_group adis16240_event_attribute_group = {
+ .attrs = adis16240_event_attributes,
+};
+
+static struct attribute *adis16240_attributes[] = {
+ &iio_dev_attr_in_supply_raw.dev_attr.attr,
+ &iio_const_attr_in_supply_scale.dev_attr.attr,
+ &iio_dev_attr_in0_raw.dev_attr.attr,
+ &iio_dev_attr_accel_x_raw.dev_attr.attr,
+ &iio_dev_attr_accel_x_offset.dev_attr.attr,
+ &iio_dev_attr_accel_x_peak_raw.dev_attr.attr,
+ &iio_dev_attr_accel_y_raw.dev_attr.attr,
+ &iio_dev_attr_accel_y_offset.dev_attr.attr,
+ &iio_dev_attr_accel_y_peak_raw.dev_attr.attr,
+ &iio_dev_attr_accel_z_raw.dev_attr.attr,
+ &iio_dev_attr_accel_z_offset.dev_attr.attr,
+ &iio_dev_attr_accel_z_peak_raw.dev_attr.attr,
+ &iio_dev_attr_accel_xyz_squared_peak_raw.dev_attr.attr,
+ &iio_dev_attr_temp_raw.dev_attr.attr,
+ &iio_const_attr_temp_scale.dev_attr.attr,
+ &iio_const_attr_available_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_reset.dev_attr.attr,
+ &iio_const_attr_name.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16240_attribute_group = {
+ .attrs = adis16240_attributes,
+};
+
+static int __devinit adis16240_probe(struct spi_device *spi)
+{
+ int ret, regdone = 0;
+ struct adis16240_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)*ADIS16240_MAX_RX, GFP_KERNEL);
+ if (st->rx == NULL) {
+ ret = -ENOMEM;
+ goto error_free_st;
+ }
+ st->tx = kzalloc(sizeof(*st->tx)*ADIS16240_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 = &adis16240_event_attribute_group;
+ st->indio_dev->attrs = &adis16240_attribute_group;
+ st->indio_dev->dev_data = (void *)(st);
+ st->indio_dev->driver_module = THIS_MODULE;
+ st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis16240_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 = adis16240_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,
+ "adis16240");
+ if (ret)
+ goto error_uninitialize_ring;
+
+ ret = adis16240_probe_trigger(st->indio_dev);
+ if (ret)
+ goto error_unregister_line;
+ }
+
+ /* Get the device into a sane initial state */
+ ret = adis16240_initial_setup(st);
+ if (ret)
+ goto error_remove_trigger;
+ return 0;
+
+error_remove_trigger:
+ adis16240_remove_trigger(st->indio_dev);
+error_unregister_line:
+ if (spi->irq)
+ iio_unregister_interrupt_line(st->indio_dev, 0);
+error_uninitialize_ring:
+ adis16240_uninitialize_ring(st->indio_dev->ring);
+error_unreg_ring_funcs:
+ adis16240_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 adis16240_remove(struct spi_device *spi)
+{
+ struct adis16240_state *st = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = st->indio_dev;
+
+ flush_scheduled_work();
+
+ adis16240_remove_trigger(indio_dev);
+ if (spi->irq)
+ iio_unregister_interrupt_line(indio_dev, 0);
+
+ adis16240_uninitialize_ring(indio_dev->ring);
+ iio_device_unregister(indio_dev);
+ adis16240_unconfigure_ring(indio_dev);
+ kfree(st->tx);
+ kfree(st->rx);
+ kfree(st);
+
+ return 0;
+}
+
+static struct spi_driver adis16240_driver = {
+ .driver = {
+ .name = "adis16240",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16240_probe,
+ .remove = __devexit_p(adis16240_remove),
+};
+
+static __init int adis16240_init(void)
+{
+ return spi_register_driver(&adis16240_driver);
+}
+module_init(adis16240_init);
+
+static __exit void adis16240_exit(void)
+{
+ spi_unregister_driver(&adis16240_driver);
+}
+module_exit(adis16240_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c
new file mode 100644
index 0000000..26b677b
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240_ring.c
@@ -0,0 +1,254 @@
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../ring_sw.h"
+#include "accel.h"
+#include "../trigger.h"
+#include "adis16240.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, ADIS16240_SCAN_SUPPLY, IIO_UNSIGNED(10),
+ ADIS16240_SUPPLY_OUT, NULL);
+static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, IIO_SIGNED(10),
+ ADIS16240_XACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, IIO_SIGNED(10),
+ ADIS16240_YACCL_OUT, NULL);
+static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, IIO_SIGNED(10),
+ ADIS16240_ZACCL_OUT, NULL);
+static IIO_SCAN_EL_C(aux_adc, ADIS16240_SCAN_AUX_ADC, IIO_UNSIGNED(10),
+ ADIS16240_AUX_ADC, NULL);
+static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, IIO_UNSIGNED(10),
+ ADIS16240_TEMP_OUT, NULL);
+
+static IIO_SCAN_EL_TIMESTAMP(6);
+
+static struct attribute *adis16240_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_accel_z.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 adis16240_scan_el_group = {
+ .attrs = adis16240_scan_el_attrs,
+ .name = "scan_elements",
+};
+
+/**
+ * adis16240_poll_func_th() top half interrupt handler called by trigger
+ * @private_data: iio_dev
+ **/
+static void adis16240_poll_func_th(struct iio_dev *indio_dev)
+{
+ struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
+ st->last_timestamp = indio_dev->trig->timestamp;
+ schedule_work(&st->work_trigger_to_ring);
+}
+
+/**
+ * adis16240_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 adis16240_read_ring_data(struct device *dev, u8 *rx)
+{
+ struct spi_message msg;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
+ struct spi_transfer xfers[ADIS16240_OUTPUTS + 1];
+ int ret;
+ int i;
+
+ mutex_lock(&st->buf_lock);
+
+ spi_message_init(&msg);
+
+ memset(xfers, 0, sizeof(xfers));
+ for (i = 0; i <= ADIS16240_OUTPUTS; i++) {
+ xfers[i].bits_per_word = 8;
+ xfers[i].cs_change = 1;
+ xfers[i].len = 2;
+ xfers[i].delay_usecs = 30;
+ xfers[i].tx_buf = st->tx + 2 * i;
+ st->tx[2 * i]
+ = ADIS16240_READ_REG(ADIS16240_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;
+}
+
+
+static void adis16240_trigger_bh_to_ring(struct work_struct *work_s)
+{
+ struct adis16240_state *st
+ = container_of(work_s, struct adis16240_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 (adis16240_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;
+}
+
+static int adis16240_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 (aligned sizeof(s64) and data */
+ 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 adis16240_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 adis16240_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 adis16240_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ kfree(indio_dev->pollfunc);
+ iio_sw_rb_free(indio_dev->ring);
+}
+
+int adis16240_configure_ring(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ struct adis16240_state *st = indio_dev->dev_data;
+ struct iio_ring_buffer *ring;
+ INIT_WORK(&st->work_trigger_to_ring, adis16240_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_accel_z.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 = &adis16240_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 = &adis16240_data_rdy_ring_preenable;
+ ring->postenable = &adis16240_data_rdy_ring_postenable;
+ ring->predisable = &adis16240_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 = &adis16240_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 adis16240_initialize_ring(struct iio_ring_buffer *ring)
+{
+ return iio_ring_buffer_register(ring, 0);
+}
+
+void adis16240_uninitialize_ring(struct iio_ring_buffer *ring)
+{
+ iio_ring_buffer_unregister(ring);
+}
diff --git a/drivers/staging/iio/accel/adis16240_trigger.c b/drivers/staging/iio/accel/adis16240_trigger.c
new file mode 100644
index 0000000..df1312e
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240_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 "adis16240.h"
+
+/**
+ * adis16240_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static int adis16240_data_rdy_trig_poll(struct iio_dev *dev_info,
+ int index,
+ s64 timestamp,
+ int no_test)
+{
+ struct adis16240_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, &adis16240_data_rdy_trig_poll);
+
+static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *adis16240_trigger_attrs[] = {
+ &dev_attr_name.attr,
+ NULL,
+};
+
+static const struct attribute_group adis16240_trigger_attr_group = {
+ .attrs = adis16240_trigger_attrs,
+};
+
+/**
+ * adis16240_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct adis16240_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 = adis16240_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;
+}
+
+/**
+ * adis16240_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig: the datardy trigger
+ **/
+static int adis16240_trig_try_reen(struct iio_trigger *trig)
+{
+ struct adis16240_state *st = trig->private_data;
+ enable_irq(st->us->irq);
+ return 0;
+}
+
+int adis16240_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct adis16240_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,
+ "adis16240-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 = &adis16240_data_rdy_trigger_set_state;
+ st->trig->try_reenable = &adis16240_trig_try_reen;
+ st->trig->control_attrs = &adis16240_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 adis16240_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct adis16240_state *state = indio_dev->dev_data;
+
+ iio_trigger_unregister(state->trig);
+ kfree(state->trig->name);
+ iio_free_trigger(state->trig);
+}
--
1.6.4.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] staging:iio: adis16240 driver
2010-05-06 13:42 ` [PATCH 2/2] staging:iio: adis16240 driver Jonathan Cameron
@ 2010-05-07 2:54 ` Barry Song
2010-05-07 14:24 ` Jonathan Cameron
0 siblings, 1 reply; 4+ messages in thread
From: Barry Song @ 2010-05-07 2:54 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: linux-iio, Barry Song
On Thu, May 6, 2010 at 9:42 PM, Jonathan Cameron <jic23@cam.ac.uk> wrot=
e:
> From: Barry Song <Barry.Song@analog.com>
>
> Signed-off-by: Barry Song <Barry.Song@analog.com>
> Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
> ---
>
> =C2=A0This one is new to the list. Barry informed me that they have c=
ompleted
> =C2=A0testing and hopefully I haven't broken anything. (Barry, please=
confirm
> =C2=A0your sign off, I've put it above to make sure I don't forget it=
)
Thanks!
Signed-off-by: Barry Song <Barry.Song@analog.com>
>
> =C2=A0Changes I've made in merging this with mainline tree:
> =C2=A0* Usual renames to add _raw and get rid of volt etc.
> =C2=A0* Other changes:
> =C2=A0 * accel_xpeak -> accel_x_peak_raw
> =C2=A0 * accel_xyzpeak -> accel_xyz_squared_raw
> =C2=A0* I have taken a number of attribute definitions out of accel.h=
and into
> =C2=A0 this driver as I am yet to be convinced that they are general =
enough.
> =C2=A0 They can move to there at a later date if we get them turning =
up in a
> =C2=A0 couple of drivers.
> =C2=A0* Checkpatch and sparse related fixes
>
> Clearly this is a very interesting chip and there are lots of interes=
ting
> features not currently supported by the driver. =C2=A0Still is a very=
good base
> to build on.
>
> =C2=A0drivers/staging/iio/accel/Kconfig =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 | =C2=A0 =C2=A09 +
> =C2=A0drivers/staging/iio/accel/Makefile =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0| =C2=A0 =C2=A04 +
> =C2=A0drivers/staging/iio/accel/adis16240_core.c =C2=A0 =C2=A0| =C2=A0=
599 +++++++++++++++++++++++++
> =C2=A0drivers/staging/iio/accel/adis16240_ring.c =C2=A0 =C2=A0| =C2=A0=
254 +++++++++++
> =C2=A0drivers/staging/iio/accel/adis16240_trigger.c | =C2=A0124 +++++
> =C2=A05 files changed, 990 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/=
accel/Kconfig
> index 1d89e21..8f3f70f 100644
> --- a/drivers/staging/iio/accel/Kconfig
> +++ b/drivers/staging/iio/accel/Kconfig
> @@ -12,6 +12,15 @@ config ADIS16209
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Say yes here to build support for A=
nalog Devices adis16209 dual-axis digital inclinometer
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 and accelerometer.
>
> +config ADIS16240
> + =C2=A0 =C2=A0 =C2=A0 tristate "Analog Devices ADIS16240 Programmabl=
e Impact Sensor and Recorder"
> + =C2=A0 =C2=A0 =C2=A0 depends on SPI
> + =C2=A0 =C2=A0 =C2=A0 select IIO_TRIGGER if IIO_RING_BUFFER
> + =C2=A0 =C2=A0 =C2=A0 select IIO_SW_RING if IIO_RING_BUFFER
> + =C2=A0 =C2=A0 =C2=A0 help
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 Say yes here to build support for Analo=
g Devices adis16240 programmable
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0impact Sensor and recorder.
> +
> =C2=A0config KXSD9
> =C2=A0 =C2=A0 =C2=A0 =C2=A0tristate "Kionix KXSD9 Accelerometer Drive=
r"
> =C2=A0 =C2=A0 =C2=A0 =C2=A0depends on SPI
> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio=
/accel/Makefile
> index f8f2124..0e6762c 100644
> --- a/drivers/staging/iio/accel/Makefile
> +++ b/drivers/staging/iio/accel/Makefile
> @@ -5,6 +5,10 @@ adis16209-y =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
:=3D adis16209_core.o
> =C2=A0adis16209-$(CONFIG_IIO_RING_BUFFER) +=3D adis16209_ring.o adis1=
6209_trigger.o
> =C2=A0obj-$(CONFIG_ADIS16209) +=3D adis16209.o
>
> +adis16240-y =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 :=3D adis16240=
_core.o
> +adis16240-$(CONFIG_IIO_RING_BUFFER) +=3D adis16240_ring.o adis16240_=
trigger.o
> +obj-$(CONFIG_ADIS16240) +=3D adis16240.o
> +
> =C2=A0obj-$(CONFIG_KXSD9) =C2=A0 =C2=A0+=3D kxsd9.o
>
> =C2=A0lis3l02dq-y =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0:=3D lis3l=
02dq_core.o
> diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/sta=
ging/iio/accel/adis16240_core.c
> new file mode 100644
> index 0000000..54fd6d7
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16240_core.c
> @@ -0,0 +1,599 @@
> +/*
> + * ADIS16240 Programmable Impact Sensor and Recorder 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/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "accel.h"
> +#include "../adc/adc.h"
> +
> +#include "adis16240.h"
> +
> +#define DRIVER_NAME =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"adis16=
240"
> +
> +static int adis16240_check_status(struct device *dev);
> +
> +/**
> + * adis16240_spi_write_reg_8() - write single byte to a register
> + * @dev: device associated with child of actual device (iio_dev or i=
io_trig)
> + * @reg_address: the address of the register to be written
> + * @val: the value to write
> + **/
> +static int adis16240_spi_write_reg_8(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 u8 reg_address,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 u8 val)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D dev_get_drvdata(=
dev);
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D iio_dev_get_dev=
data(indio_dev);
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&st->buf_lock);
> + =C2=A0 =C2=A0 =C2=A0 st->tx[0] =3D ADIS16240_WRITE_REG(reg_address)=
;
> + =C2=A0 =C2=A0 =C2=A0 st->tx[1] =3D val;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D spi_write(st->us, st->tx, 2);
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&st->buf_lock);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +/**
> + * adis16240_spi_write_reg_16() - write 2 bytes to a pair of registe=
rs
> + * @dev: device associated with child of actual device (iio_dev or i=
io_trig)
> + * @reg_address: the address of the lower of the two registers. Seco=
nd register
> + * =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is assumed to ha=
ve address one greater.
> + * @val: value to be written
> + **/
> +static int adis16240_spi_write_reg_16(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 u8 lower_reg_addre=
ss,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 u16 value)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 struct spi_message msg;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D dev_get_drvdata(=
dev);
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D iio_dev_get_dev=
data(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 struct spi_transfer xfers[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .tx_buf =3D st->tx,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .bits_per_word =3D 8,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .len =3D 2,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .cs_change =3D 1,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .delay_usecs =3D 25,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }, {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .tx_buf =3D st->tx + 2,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .bits_per_word =3D 8,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .len =3D 2,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .cs_change =3D 1,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .delay_usecs =3D 25,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 },
> + =C2=A0 =C2=A0 =C2=A0 };
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&st->buf_lock);
> + =C2=A0 =C2=A0 =C2=A0 st->tx[0] =3D ADIS16240_WRITE_REG(lower_reg_ad=
dress);
> + =C2=A0 =C2=A0 =C2=A0 st->tx[1] =3D value & 0xFF;
> + =C2=A0 =C2=A0 =C2=A0 st->tx[2] =3D ADIS16240_WRITE_REG(lower_reg_ad=
dress + 1);
> + =C2=A0 =C2=A0 =C2=A0 st->tx[3] =3D (value >> 8) & 0xFF;
> +
> + =C2=A0 =C2=A0 =C2=A0 spi_message_init(&msg);
> + =C2=A0 =C2=A0 =C2=A0 spi_message_add_tail(&xfers[0], &msg);
> + =C2=A0 =C2=A0 =C2=A0 spi_message_add_tail(&xfers[1], &msg);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D spi_sync(st->us, &msg);
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&st->buf_lock);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +/**
> + * adis16240_spi_read_reg_16() - read 2 bytes from a 16-bit register
> + * @dev: device associated with child of actual device (iio_dev or i=
io_trig)
> + * @reg_address: the address of the lower of the two registers. Seco=
nd register
> + * =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 is assumed to ha=
ve address one greater.
> + * @val: somewhere to pass back the value read
> + **/
> +static int adis16240_spi_read_reg_16(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 u8 lower_reg_addre=
ss,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 u16 *val)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct spi_message msg;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D dev_get_drvdata(=
dev);
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D iio_dev_get_dev=
data(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 struct spi_transfer xfers[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .tx_buf =3D st->tx,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .bits_per_word =3D 8,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .len =3D 2,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .cs_change =3D 1,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .delay_usecs =3D 25,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }, {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .rx_buf =3D st->rx,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .bits_per_word =3D 8,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .len =3D 2,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .cs_change =3D 1,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 .delay_usecs =3D 25,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 },
> + =C2=A0 =C2=A0 =C2=A0 };
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&st->buf_lock);
> + =C2=A0 =C2=A0 =C2=A0 st->tx[0] =3D ADIS16240_READ_REG(lower_reg_add=
ress);
> + =C2=A0 =C2=A0 =C2=A0 st->tx[1] =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 st->tx[2] =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 st->tx[3] =3D 0;
> +
> + =C2=A0 =C2=A0 =C2=A0 spi_message_init(&msg);
> + =C2=A0 =C2=A0 =C2=A0 spi_message_add_tail(&xfers[0], &msg);
> + =C2=A0 =C2=A0 =C2=A0 spi_message_add_tail(&xfers[1], &msg);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D spi_sync(st->us, &msg);
> + =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&st->us->d=
ev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 "problem when reading 16 bit register 0x%02X",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 lower_reg_address);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 *val =3D (st->rx[0] << 8) | st->rx[1];
> +
> +error_ret:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&st->buf_lock);
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static ssize_t adis16240_spi_read_signed(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 struct device_attr=
ibute *attr,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char *buf,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unsigned bits)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 s16 val =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 unsigned shift =3D 16 - bits;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev_attr *this_attr =3D to_iio_dev_=
attr(attr);
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_read_reg_16(dev, this_at=
tr->address, (u16 *)&val);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (val & ADIS16240_ERROR_ACTIVE)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_check_st=
atus(dev);
> +
> + =C2=A0 =C2=A0 =C2=A0 val =3D ((s16)(val << shift) >> shift);
> + =C2=A0 =C2=A0 =C2=A0 return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t adis16240_read_10bit_unsigned(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 struct device_attr=
ibute *attr,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char *buf)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 u16 val =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev_attr *this_attr =3D to_iio_dev_=
attr(attr);
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_read_reg_16(dev, this_at=
tr->address, &val);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (val & ADIS16240_ERROR_ACTIVE)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_check_st=
atus(dev);
> +
> + =C2=A0 =C2=A0 =C2=A0 return sprintf(buf, "%u\n", val & 0x03FF);
> +}
> +
> +static ssize_t adis16240_read_10bit_signed(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 struct device_attr=
ibute *attr,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char *buf)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D dev_get_drvdata(=
dev);
> + =C2=A0 =C2=A0 =C2=A0 ssize_t ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Take the iio_dev status lock */
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&indio_dev->mlock);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D =C2=A0adis16240_spi_read_signed(dev, a=
ttr, buf, 10);
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&indio_dev->mlock);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static ssize_t adis16240_read_12bit_signed(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 struct device_attr=
ibute *attr,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 char *buf)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D dev_get_drvdata(=
dev);
> + =C2=A0 =C2=A0 =C2=A0 ssize_t ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Take the iio_dev status lock */
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&indio_dev->mlock);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D =C2=A0adis16240_spi_read_signed(dev, a=
ttr, buf, 12);
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&indio_dev->mlock);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static ssize_t adis16240_write_16bit(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 struct device_attr=
ibute *attr,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 const char *buf,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size_t len)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev_attr *this_attr =3D to_iio_dev_=
attr(attr);
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 long val;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D strict_strtol(buf, 10, &val);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_ret;
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_write_reg_16(dev, this_a=
ttr->address, val);
> +
> +error_ret:
> + =C2=A0 =C2=A0 =C2=A0 return ret ? ret : len;
> +}
> +
> +static int adis16240_reset(struct device *dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_write_reg_8(dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 ADIS16240_GLOB_CMD,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 ADIS16240_GLOB_CMD_SW_RESET);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "prob=
lem resetting device");
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static ssize_t adis16240_write_reset(struct device *dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 struct device_attr=
ibute *attr,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 const char *buf, s=
ize_t len)
> +{
> + =C2=A0 =C2=A0 =C2=A0 if (len < 1)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> + =C2=A0 =C2=A0 =C2=A0 switch (buf[0]) {
> + =C2=A0 =C2=A0 =C2=A0 case '1':
> + =C2=A0 =C2=A0 =C2=A0 case 'y':
> + =C2=A0 =C2=A0 =C2=A0 case 'Y':
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return adis16240_r=
eset(dev);
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> +}
> +
> +int adis16240_set_irq(struct device *dev, bool enable)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 u16 msc;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_read_reg_16(dev, ADIS162=
40_MSC_CTRL, &msc);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 msc |=3D ADIS16240_MSC_CTRL_ACTIVE_HIGH;
> + =C2=A0 =C2=A0 =C2=A0 msc &=3D ~ADIS16240_MSC_CTRL_DATA_RDY_DIO2;
> + =C2=A0 =C2=A0 =C2=A0 if (enable)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 msc |=3D ADIS16240=
_MSC_CTRL_DATA_RDY_EN;
> + =C2=A0 =C2=A0 =C2=A0 else
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 msc &=3D ~ADIS1624=
0_MSC_CTRL_DATA_RDY_EN;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_write_reg_16(dev, ADIS16=
240_MSC_CTRL, msc);
> +
> +error_ret:
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static int adis16240_self_test(struct device *dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_write_reg_16(dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 ADIS16240_MSC_CTRL,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 ADIS16240_MSC_CTRL_SELF_TEST_EN);
> + =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "prob=
lem starting self test");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto err_ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 msleep(ADIS16240_STARTUP_DELAY);
> +
> + =C2=A0 =C2=A0 =C2=A0 adis16240_check_status(dev);
> +
> +err_ret:
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static int adis16240_check_status(struct device *dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 u16 status;
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_spi_read_reg_16(dev, ADIS162=
40_DIAG_STAT, &status);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "Read=
ing status failed\n");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D status & 0x2F;
> + =C2=A0 =C2=A0 =C2=A0 if (status & ADIS16240_DIAG_STAT_PWRON_FAIL)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "Powe=
r-on, self-test fail\n");
> + =C2=A0 =C2=A0 =C2=A0 if (status & ADIS16240_DIAG_STAT_SPI_FAIL)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "SPI =
failure\n");
> + =C2=A0 =C2=A0 =C2=A0 if (status & ADIS16240_DIAG_STAT_FLASH_UPT)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "Flas=
h update failed\n");
> + =C2=A0 =C2=A0 =C2=A0 if (status & ADIS16240_DIAG_STAT_POWER_HIGH)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "Powe=
r supply above 3.625V\n");
> + =C2=A0 =C2=A0 =C2=A0 if (status & ADIS16240_DIAG_STAT_POWER_LOW)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "Powe=
r supply below 2.225V\n");
> +
> +error_ret:
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static int adis16240_initial_setup(struct adis16240_state *st)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 struct device *dev =3D &st->indio_dev->dev;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Disable IRQ */
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_set_irq(dev, false);
> + =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "disa=
ble irq failed");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto err_ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Do self test */
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_self_test(dev);
> + =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "self=
test failure");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto err_ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Read status register to check the result */
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_check_status(dev);
> + =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_reset(de=
v);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(dev, "devi=
ce not playing ball -> reset");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 msleep(ADIS16240_S=
TARTUP_DELAY);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_=
check_status(dev);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 dev_err(dev, "giving up");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 goto err_ret;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 printk(KERN_INFO DRIVER_NAME ": at CS%d (irq %=
d)\n",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 st->us->chip_select, st->us->irq);
> +
> +err_ret:
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static IIO_DEV_ATTR_IN_NAMED_RAW(supply, adis16240_read_10bit_unsign=
ed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_SUPPLY_O=
UT);
> +static IIO_DEV_ATTR_IN_RAW(0, adis16240_read_10bit_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_AUX_ADC)=
;
> +static IIO_CONST_ATTR(in_supply_scale, "0.00488");
> +static IIO_DEV_ATTR_ACCEL_X(adis16240_read_10bit_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_XACCL_OU=
T);
> +static IIO_DEVICE_ATTR(accel_x_peak_raw, S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0adis16240_read_10bit_signed, NULL,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0ADIS16240_XPEAK_OUT);
> +static IIO_DEV_ATTR_ACCEL_Y(adis16240_read_10bit_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_YACCL_OU=
T);
> +static IIO_DEVICE_ATTR(accel_y_peak_raw, S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0adis16240_read_10bit_signed, NULL,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0ADIS16240_YPEAK_OUT);
> +static IIO_DEV_ATTR_ACCEL_Z(adis16240_read_10bit_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_ZACCL_OU=
T);
> +static IIO_DEVICE_ATTR(accel_z_peak_raw, S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0adis16240_read_10bit_signed, NULL,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0ADIS16240_ZPEAK_OUT);
> +
> +static IIO_DEVICE_ATTR(accel_xyz_squared_peak_raw, S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0adis16240_read_12bit_signed, NULL,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0ADIS16240_XYZPEAK_OUT);
> +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_read_10b=
it_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_write_16=
bit,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_XACCL_OF=
=46);
> +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_read_10b=
it_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_write_16=
bit,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_YACCL_OF=
=46);
> +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_read_10b=
it_signed,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 adis16240_write_16=
bit,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_ZACCL_OF=
=46);
> +static IIO_DEV_ATTR_TEMP_RAW(adis16240_read_10bit_unsigned);
> +static IIO_CONST_ATTR(temp_scale, "0.244");
> +
> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16240_write_reset, =
0);
> +
> +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("4096");
> +
> +static IIO_CONST_ATTR(name, "adis16240");
> +
> +static struct attribute *adis16240_event_attributes[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 NULL
> +};
> +
> +static struct attribute_group adis16240_event_attribute_group =3D {
> + =C2=A0 =C2=A0 =C2=A0 .attrs =3D adis16240_event_attributes,
> +};
> +
> +static struct attribute *adis16240_attributes[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_in_supply_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_const_attr_in_supply_scale.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_in0_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_x_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_x_offset.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_x_peak_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_y_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_y_offset.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_y_peak_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_z_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_z_offset.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_z_peak_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_accel_xyz_squared_peak_raw.dev_a=
ttr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_temp_raw.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_const_attr_temp_scale.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_const_attr_available_sampling_frequency.d=
ev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_dev_attr_reset.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_const_attr_name.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 NULL
> +};
> +
> +static const struct attribute_group adis16240_attribute_group =3D {
> + =C2=A0 =C2=A0 =C2=A0 .attrs =3D adis16240_attributes,
> +};
> +
> +static int __devinit adis16240_probe(struct spi_device *spi)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret, regdone =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D kzalloc(sizeof =
*st, GFP_KERNEL);
> + =C2=A0 =C2=A0 =C2=A0 if (!st) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D =C2=A0-ENO=
MEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 /* this is only used for removal purposes */
> + =C2=A0 =C2=A0 =C2=A0 spi_set_drvdata(spi, st);
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Allocate the comms buffers */
> + =C2=A0 =C2=A0 =C2=A0 st->rx =3D kzalloc(sizeof(*st->rx)*ADIS16240_M=
AX_RX, GFP_KERNEL);
> + =C2=A0 =C2=A0 =C2=A0 if (st->rx =3D=3D NULL) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D -ENOMEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_free_st=
;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 st->tx =3D kzalloc(sizeof(*st->tx)*ADIS16240_M=
AX_TX, GFP_KERNEL);
> + =C2=A0 =C2=A0 =C2=A0 if (st->tx =3D=3D NULL) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D -ENOMEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_free_rx=
;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 st->us =3D spi;
> + =C2=A0 =C2=A0 =C2=A0 mutex_init(&st->buf_lock);
> + =C2=A0 =C2=A0 =C2=A0 /* setup the industrialio driver allocated ele=
ments */
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev =3D iio_allocate_device();
> + =C2=A0 =C2=A0 =C2=A0 if (st->indio_dev =3D=3D NULL) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D -ENOMEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_free_tx=
;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->dev.parent =3D &spi->dev;
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->num_interrupt_lines =3D 1;
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->event_attrs =3D &adis16240_even=
t_attribute_group;
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->attrs =3D &adis16240_attribute_=
group;
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->dev_data =3D (void *)(st);
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->driver_module =3D THIS_MODULE;
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->modes =3D INDIO_DIRECT_MODE;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_configure_ring(st->indio_dev=
);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_free_de=
v;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D iio_device_register(st->indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_unreg_r=
ing_funcs;
> + =C2=A0 =C2=A0 =C2=A0 regdone =3D 1;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_initialize_ring(st->indio_de=
v->ring);
> + =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 printk(KERN_ERR "f=
ailed to initialize the ring\n");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_unreg_r=
ing_funcs;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 if (spi->irq) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D iio_regist=
er_interrupt_line(spi->irq,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 st->indio_dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 IRQF_TRIGGER_RISING,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "adis16240");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 goto error_uninitialize_ring;
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_=
probe_trigger(st->indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 goto error_unregister_line;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Get the device into a sane initial state */
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_initial_setup(st);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_remove_=
trigger;
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +
> +error_remove_trigger:
> + =C2=A0 =C2=A0 =C2=A0 adis16240_remove_trigger(st->indio_dev);
> +error_unregister_line:
> + =C2=A0 =C2=A0 =C2=A0 if (spi->irq)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 iio_unregister_int=
errupt_line(st->indio_dev, 0);
> +error_uninitialize_ring:
> + =C2=A0 =C2=A0 =C2=A0 adis16240_uninitialize_ring(st->indio_dev->rin=
g);
> +error_unreg_ring_funcs:
> + =C2=A0 =C2=A0 =C2=A0 adis16240_unconfigure_ring(st->indio_dev);
> +error_free_dev:
> + =C2=A0 =C2=A0 =C2=A0 if (regdone)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 iio_device_unregis=
ter(st->indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 else
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 iio_free_device(st=
->indio_dev);
> +error_free_tx:
> + =C2=A0 =C2=A0 =C2=A0 kfree(st->tx);
> +error_free_rx:
> + =C2=A0 =C2=A0 =C2=A0 kfree(st->rx);
> +error_free_st:
> + =C2=A0 =C2=A0 =C2=A0 kfree(st);
> +error_ret:
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static int adis16240_remove(struct spi_device *spi)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D spi_get_drvdata=
(spi);
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D st->indio_dev;
> +
> + =C2=A0 =C2=A0 =C2=A0 flush_scheduled_work();
> +
> + =C2=A0 =C2=A0 =C2=A0 adis16240_remove_trigger(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 if (spi->irq)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 iio_unregister_int=
errupt_line(indio_dev, 0);
> +
> + =C2=A0 =C2=A0 =C2=A0 adis16240_uninitialize_ring(indio_dev->ring);
> + =C2=A0 =C2=A0 =C2=A0 iio_device_unregister(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 adis16240_unconfigure_ring(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 kfree(st->tx);
> + =C2=A0 =C2=A0 =C2=A0 kfree(st->rx);
> + =C2=A0 =C2=A0 =C2=A0 kfree(st);
> +
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +}
> +
> +static struct spi_driver adis16240_driver =3D {
> + =C2=A0 =C2=A0 =C2=A0 .driver =3D {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =3D "adis162=
40",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .owner =3D THIS_MO=
DULE,
> + =C2=A0 =C2=A0 =C2=A0 },
> + =C2=A0 =C2=A0 =C2=A0 .probe =3D adis16240_probe,
> + =C2=A0 =C2=A0 =C2=A0 .remove =3D __devexit_p(adis16240_remove),
> +};
> +
> +static __init int adis16240_init(void)
> +{
> + =C2=A0 =C2=A0 =C2=A0 return spi_register_driver(&adis16240_driver);
> +}
> +module_init(adis16240_init);
> +
> +static __exit void adis16240_exit(void)
> +{
> + =C2=A0 =C2=A0 =C2=A0 spi_unregister_driver(&adis16240_driver);
> +}
> +module_exit(adis16240_exit);
> +
> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
> +MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Re=
corder");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/sta=
ging/iio/accel/adis16240_ring.c
> new file mode 100644
> index 0000000..26b677b
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16240_ring.c
> @@ -0,0 +1,254 @@
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/gpio.h>
> +#include <linux/workqueue.h>
> +#include <linux/mutex.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +#include <linux/sysfs.h>
> +#include <linux/list.h>
> +
> +#include "../iio.h"
> +#include "../sysfs.h"
> +#include "../ring_sw.h"
> +#include "accel.h"
> +#include "../trigger.h"
> +#include "adis16240.h"
> +
> +/**
> + * combine_8_to_16() utility function to munge to u8s into u16
> + **/
> +static inline u16 combine_8_to_16(u8 lower, u8 upper)
> +{
> + =C2=A0 =C2=A0 =C2=A0 u16 _lower =3D lower;
> + =C2=A0 =C2=A0 =C2=A0 u16 _upper =3D upper;
> + =C2=A0 =C2=A0 =C2=A0 return _lower | (_upper << 8);
> +}
> +
> +static IIO_SCAN_EL_C(supply, ADIS16240_SCAN_SUPPLY, IIO_UNSIGNED(10)=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_SUPPLY_O=
UT, NULL);
> +static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, IIO_SIGNED(10),
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_XACCL_OU=
T, NULL);
> +static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, IIO_SIGNED(10),
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_YACCL_OU=
T, NULL);
> +static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, IIO_SIGNED(10),
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_ZACCL_OU=
T, NULL);
> +static IIO_SCAN_EL_C(aux_adc, ADIS16240_SCAN_AUX_ADC, IIO_UNSIGNED(1=
0),
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_AUX_ADC,=
NULL);
> +static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, IIO_UNSIGNED(10),
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ADIS16240_TEMP_OUT=
, NULL);
> +
> +static IIO_SCAN_EL_TIMESTAMP(6);
> +
> +static struct attribute *adis16240_scan_el_attrs[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_supply.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_accel_x.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_accel_y.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_accel_z.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_aux_adc.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_temp.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 &iio_scan_el_timestamp.dev_attr.attr,
> + =C2=A0 =C2=A0 =C2=A0 NULL,
> +};
> +
> +static struct attribute_group adis16240_scan_el_group =3D {
> + =C2=A0 =C2=A0 =C2=A0 .attrs =3D adis16240_scan_el_attrs,
> + =C2=A0 =C2=A0 =C2=A0 .name =3D "scan_elements",
> +};
> +
> +/**
> + * adis16240_poll_func_th() top half interrupt handler called by tri=
gger
> + * @private_data: =C2=A0 =C2=A0 =C2=A0iio_dev
> + **/
> +static void adis16240_poll_func_th(struct iio_dev *indio_dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D iio_dev_get_dev=
data(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 st->last_timestamp =3D indio_dev->trig->timest=
amp;
> + =C2=A0 =C2=A0 =C2=A0 schedule_work(&st->work_trigger_to_ring);
> +}
> +
> +/**
> + * adis16240_read_ring_data() read data registers which will be plac=
ed into ring
> + * @dev: device associated with child of actual device (iio_dev or i=
io_trig)
> + * @rx: somewhere to pass back the value read
> + **/
> +static int adis16240_read_ring_data(struct device *dev, u8 *rx)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct spi_message msg;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D dev_get_drvdata(=
dev);
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D iio_dev_get_dev=
data(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 struct spi_transfer xfers[ADIS16240_OUTPUTS + =
1];
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 int i;
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&st->buf_lock);
> +
> + =C2=A0 =C2=A0 =C2=A0 spi_message_init(&msg);
> +
> + =C2=A0 =C2=A0 =C2=A0 memset(xfers, 0, sizeof(xfers));
> + =C2=A0 =C2=A0 =C2=A0 for (i =3D 0; i <=3D ADIS16240_OUTPUTS; i++) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xfers[i].bits_per_=
word =3D 8;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xfers[i].cs_change=
=3D 1;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xfers[i].len =3D 2=
;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xfers[i].delay_use=
cs =3D 30;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 xfers[i].tx_buf =3D=
st->tx + 2 * i;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 st->tx[2 * i]
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =3D ADIS16240_READ_REG(ADIS16240_SUPPLY_OUT + 2 * i);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 st->tx[2 * i + 1] =
=3D 0;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (i >=3D 1)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 xfers[i].rx_buf =3D rx + 2 * (i - 1);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 spi_message_add_ta=
il(&xfers[i], &msg);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D spi_sync(st->us, &msg);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&st->us->d=
ev, "problem when burst reading");
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&st->buf_lock);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +
> +static void adis16240_trigger_bh_to_ring(struct work_struct *work_s)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D container_of(w=
ork_s, struct adis16240_state,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 work_trigger_to_ring);
> +
> + =C2=A0 =C2=A0 =C2=A0 int i =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 s16 *data;
> + =C2=A0 =C2=A0 =C2=A0 size_t datasize =3D st->indio_dev
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ->ring->access.get=
_bpd(st->indio_dev->ring);
> +
> + =C2=A0 =C2=A0 =C2=A0 data =3D kmalloc(datasize , GFP_KERNEL);
> + =C2=A0 =C2=A0 =C2=A0 if (data =3D=3D NULL) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&st->us->d=
ev, "memory alloc failed in ring bh");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 if (st->indio_dev->scan_count)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (adis16240_read=
_ring_data(&st->indio_dev->dev, st->rx) >=3D 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 for (; i < st->indio_dev->scan_count; i++) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data[i] =3D combine_8_to_16(st->rx[=
i*2+1],
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 st->rx[i*2]);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Guaranteed to be aligned with 8 byte bounda=
ry */
> + =C2=A0 =C2=A0 =C2=A0 if (st->indio_dev->scan_timestamp)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *((s64 *)(data + (=
(i + 3)/4)*4)) =3D st->last_timestamp;
> +
> + =C2=A0 =C2=A0 =C2=A0 st->indio_dev->ring->access.store_to(st->indio=
_dev->ring,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 (u8 *)data,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 st->last_timestamp);
> +
> + =C2=A0 =C2=A0 =C2=A0 iio_trigger_notify_done(st->indio_dev->trig);
> + =C2=A0 =C2=A0 =C2=A0 kfree(data);
> +
> + =C2=A0 =C2=A0 =C2=A0 return;
> +}
> +
> +static int adis16240_data_rdy_ring_preenable(struct iio_dev *indio_d=
ev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 size_t size;
> + =C2=A0 =C2=A0 =C2=A0 dev_dbg(&indio_dev->dev, "%s\n", __func__);
> + =C2=A0 =C2=A0 =C2=A0 /* Check if there are any scan elements enable=
d, if not fail*/
> + =C2=A0 =C2=A0 =C2=A0 if (!(indio_dev->scan_count || indio_dev->scan=
_timestamp))
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (indio_dev->ring->access.set_bpd) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (indio_dev->sca=
n_timestamp)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 if (indio_dev->scan_count)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Timestamp (aligned sizeof(s64) a=
nd data */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size =3D (((indio_dev->scan_count *=
sizeof(s16))
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0+=
sizeof(s64) - 1)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 & ~(siz=
eof(s64) - 1))
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 + sizeo=
f(s64);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 else /* Timestamp only =C2=A0*/
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size =3D sizeof(s64);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else /* Data only =
*/
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 size =3D indio_dev->scan_count*sizeof(s16);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 indio_dev->ring->a=
ccess.set_bpd(indio_dev->ring, size);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +}
> +
> +static int adis16240_data_rdy_ring_postenable(struct iio_dev *indio_=
dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 return indio_dev->trig
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ? iio_trigger_atta=
ch_poll_func(indio_dev->trig,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 indio_dev->pollfunc)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 : 0;
> +}
> +
> +static int adis16240_data_rdy_ring_predisable(struct iio_dev *indio_=
dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 return indio_dev->trig
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ? iio_trigger_dett=
ach_poll_func(indio_dev->trig,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 indio_dev->pollfunc)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 : 0;
> +}
> +
> +void adis16240_unconfigure_ring(struct iio_dev *indio_dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 kfree(indio_dev->pollfunc);
> + =C2=A0 =C2=A0 =C2=A0 iio_sw_rb_free(indio_dev->ring);
> +}
> +
> +int adis16240_configure_ring(struct iio_dev *indio_dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D indio_dev->dev_=
data;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_ring_buffer *ring;
> + =C2=A0 =C2=A0 =C2=A0 INIT_WORK(&st->work_trigger_to_ring, adis16240=
_trigger_bh_to_ring);
> + =C2=A0 =C2=A0 =C2=A0 /* Set default scan mode */
> +
> + =C2=A0 =C2=A0 =C2=A0 iio_scan_mask_set(indio_dev, iio_scan_el_suppl=
y.number);
> + =C2=A0 =C2=A0 =C2=A0 iio_scan_mask_set(indio_dev, iio_scan_el_accel=
_x.number);
> + =C2=A0 =C2=A0 =C2=A0 iio_scan_mask_set(indio_dev, iio_scan_el_accel=
_y.number);
> + =C2=A0 =C2=A0 =C2=A0 iio_scan_mask_set(indio_dev, iio_scan_el_accel=
_z.number);
> + =C2=A0 =C2=A0 =C2=A0 iio_scan_mask_set(indio_dev, iio_scan_el_temp.=
number);
> + =C2=A0 =C2=A0 =C2=A0 iio_scan_mask_set(indio_dev, iio_scan_el_aux_a=
dc.number);
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->scan_timestamp =3D true;
> +
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->scan_el_attrs =3D &adis16240_scan_e=
l_group;
> +
> + =C2=A0 =C2=A0 =C2=A0 ring =3D iio_sw_rb_allocate(indio_dev);
> + =C2=A0 =C2=A0 =C2=A0 if (!ring) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D -ENOMEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->ring =3D ring;
> + =C2=A0 =C2=A0 =C2=A0 /* Effectively select the ring buffer implemen=
tation */
> + =C2=A0 =C2=A0 =C2=A0 iio_ring_sw_register_funcs(&ring->access);
> + =C2=A0 =C2=A0 =C2=A0 ring->preenable =3D &adis16240_data_rdy_ring_p=
reenable;
> + =C2=A0 =C2=A0 =C2=A0 ring->postenable =3D &adis16240_data_rdy_ring_=
postenable;
> + =C2=A0 =C2=A0 =C2=A0 ring->predisable =3D &adis16240_data_rdy_ring_=
predisable;
> + =C2=A0 =C2=A0 =C2=A0 ring->owner =3D THIS_MODULE;
> +
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->pollfunc =3D kzalloc(sizeof(*indio_=
dev->pollfunc), GFP_KERNEL);
> + =C2=A0 =C2=A0 =C2=A0 if (indio_dev->pollfunc =3D=3D NULL) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D -ENOMEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_iio_sw_=
rb_free;;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->pollfunc->poll_func_main =3D &adis1=
6240_poll_func_th;
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->pollfunc->private_data =3D indio_de=
v;
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->modes |=3D INDIO_RING_TRIGGERED;
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +
> +error_iio_sw_rb_free:
> + =C2=A0 =C2=A0 =C2=A0 iio_sw_rb_free(indio_dev->ring);
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +int adis16240_initialize_ring(struct iio_ring_buffer *ring)
> +{
> + =C2=A0 =C2=A0 =C2=A0 return iio_ring_buffer_register(ring, 0);
> +}
> +
> +void adis16240_uninitialize_ring(struct iio_ring_buffer *ring)
> +{
> + =C2=A0 =C2=A0 =C2=A0 iio_ring_buffer_unregister(ring);
> +}
> diff --git a/drivers/staging/iio/accel/adis16240_trigger.c b/drivers/=
staging/iio/accel/adis16240_trigger.c
> new file mode 100644
> index 0000000..df1312e
> --- /dev/null
> +++ b/drivers/staging/iio/accel/adis16240_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 "adis16240.h"
> +
> +/**
> + * adis16240_data_rdy_trig_poll() the event handler for the data rdy=
trig
> + **/
> +static int adis16240_data_rdy_trig_poll(struct iio_dev *dev_info,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int inde=
x,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0s64 time=
stamp,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int no_t=
est)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D iio_dev_get_dev=
data(dev_info);
> + =C2=A0 =C2=A0 =C2=A0 struct iio_trigger *trig =3D st->trig;
> +
> + =C2=A0 =C2=A0 =C2=A0 trig->timestamp =3D timestamp;
> + =C2=A0 =C2=A0 =C2=A0 iio_trigger_poll(trig);
> +
> + =C2=A0 =C2=A0 =C2=A0 return IRQ_HANDLED;
> +}
> +
> +IIO_EVENT_SH(data_rdy_trig, &adis16240_data_rdy_trig_poll);
> +
> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
> +
> +static struct attribute *adis16240_trigger_attrs[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 &dev_attr_name.attr,
> + =C2=A0 =C2=A0 =C2=A0 NULL,
> +};
> +
> +static const struct attribute_group adis16240_trigger_attr_group =3D=
{
> + =C2=A0 =C2=A0 =C2=A0 .attrs =3D adis16240_trigger_attrs,
> +};
> +
> +/**
> + * adis16240_data_rdy_trigger_set_state() set datardy interrupt stat=
e
> + **/
> +static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *=
trig,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 bool state)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D trig->private_d=
ata;
> + =C2=A0 =C2=A0 =C2=A0 struct iio_dev *indio_dev =3D st->indio_dev;
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> +
> + =C2=A0 =C2=A0 =C2=A0 dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__=
, state);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D adis16240_set_irq(&st->indio_dev->dev,=
state);
> + =C2=A0 =C2=A0 =C2=A0 if (state =3D=3D false) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 iio_remove_event_f=
rom_list(&iio_event_data_rdy_trig,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0&indio_dev->interrupts[0]
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0->ev_list);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flush_scheduled_wo=
rk();
> + =C2=A0 =C2=A0 =C2=A0 } else {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 iio_add_event_to_l=
ist(&iio_event_data_rdy_trig,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &indio_dev->in=
terrupts[0]->ev_list);
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +/**
> + * adis16240_trig_try_reen() try renabling irq for data rdy trigger
> + * @trig: =C2=A0 =C2=A0 =C2=A0the datardy trigger
> + **/
> +static int adis16240_trig_try_reen(struct iio_trigger *trig)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D trig->private_d=
ata;
> + =C2=A0 =C2=A0 =C2=A0 enable_irq(st->us->irq);
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +}
> +
> +int adis16240_probe_trigger(struct iio_dev *indio_dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *st =3D indio_dev->dev_=
data;
> +
> + =C2=A0 =C2=A0 =C2=A0 st->trig =3D iio_allocate_trigger();
> + =C2=A0 =C2=A0 =C2=A0 st->trig->name =3D kmalloc(IIO_TRIGGER_NAME_LE=
NGTH, GFP_KERNEL);
> + =C2=A0 =C2=A0 =C2=A0 if (!st->trig->name) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D -ENOMEM;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_free_tr=
ig;
> + =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 snprintf((char *)st->trig->name,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0IIO_TRIGGER_=
NAME_LENGTH,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"adis16240-d=
ev%d", indio_dev->id);
> + =C2=A0 =C2=A0 =C2=A0 st->trig->dev.parent =3D &st->us->dev;
> + =C2=A0 =C2=A0 =C2=A0 st->trig->owner =3D THIS_MODULE;
> + =C2=A0 =C2=A0 =C2=A0 st->trig->private_data =3D st;
> + =C2=A0 =C2=A0 =C2=A0 st->trig->set_trigger_state =3D &adis16240_dat=
a_rdy_trigger_set_state;
> + =C2=A0 =C2=A0 =C2=A0 st->trig->try_reenable =3D &adis16240_trig_try=
_reen;
> + =C2=A0 =C2=A0 =C2=A0 st->trig->control_attrs =3D &adis16240_trigger=
_attr_group;
> + =C2=A0 =C2=A0 =C2=A0 ret =3D iio_trigger_register(st->trig);
> +
> + =C2=A0 =C2=A0 =C2=A0 /* select default trigger */
> + =C2=A0 =C2=A0 =C2=A0 indio_dev->trig =3D st->trig;
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto error_free_tr=
ig_name;
> +
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +
> +error_free_trig_name:
> + =C2=A0 =C2=A0 =C2=A0 kfree(st->trig->name);
> +error_free_trig:
> + =C2=A0 =C2=A0 =C2=A0 iio_free_trigger(st->trig);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +void adis16240_remove_trigger(struct iio_dev *indio_dev)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct adis16240_state *state =3D indio_dev->d=
ev_data;
> +
> + =C2=A0 =C2=A0 =C2=A0 iio_trigger_unregister(state->trig);
> + =C2=A0 =C2=A0 =C2=A0 kfree(state->trig->name);
> + =C2=A0 =C2=A0 =C2=A0 iio_free_trigger(state->trig);
> +}
> --
> 1.6.4.4
>
> --
> 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 =C2=A0http://vger.kernel.org/majordomo-info.ht=
ml
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 2/2] staging:iio: adis16240 driver
2010-05-07 2:54 ` Barry Song
@ 2010-05-07 14:24 ` Jonathan Cameron
0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Cameron @ 2010-05-07 14:24 UTC (permalink / raw)
To: Barry Song; +Cc: linux-iio, Barry Song
On 05/07/10 03:54, Barry Song wrote:
> On Thu, May 6, 2010 at 9:42 PM, Jonathan Cameron <jic23@cam.ac.uk> wrote:
>> From: Barry Song <Barry.Song@analog.com>
>>
>> Signed-off-by: Barry Song <Barry.Song@analog.com>
>> Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
>> ---
>>
>> This one is new to the list. Barry informed me that they have completed
>> testing and hopefully I haven't broken anything. (Barry, please confirm
>> your sign off, I've put it above to make sure I don't forget it)
> Thanks!
>
> Signed-off-by: Barry Song <Barry.Song@analog.com>
*Coughs* and we both missed the deliberate mistake. There is no header
in this patch ;) I'll add that in and send these to Greg now along with
all but the one you nacked from the last series. Rumour (possibly completely
false) is that we may be very close indeed to the merge window opening.
I'll keep queuing new drivers. If they miss then they can sit in Greg's
for-next tree ready for 2.6.36 from the moment he sets that up.
Jonathan
>
>>
>> Changes I've made in merging this with mainline tree:
>> * Usual renames to add _raw and get rid of volt etc.
>> * Other changes:
>> * accel_xpeak -> accel_x_peak_raw
>> * accel_xyzpeak -> accel_xyz_squared_raw
>> * I have taken a number of attribute definitions out of accel.h and into
>> this driver as I am yet to be convinced that they are general enough.
>> They can move to there at a later date if we get them turning up in a
>> couple of drivers.
>> * Checkpatch and sparse related fixes
>>
>> Clearly this is a very interesting chip and there are lots of interesting
>> features not currently supported by the driver. Still is a very good base
>> to build on.
>>
>> drivers/staging/iio/accel/Kconfig | 9 +
>> drivers/staging/iio/accel/Makefile | 4 +
>> drivers/staging/iio/accel/adis16240_core.c | 599 +++++++++++++++++++++++++
>> drivers/staging/iio/accel/adis16240_ring.c | 254 +++++++++++
>> drivers/staging/iio/accel/adis16240_trigger.c | 124 +++++
>> 5 files changed, 990 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
>> index 1d89e21..8f3f70f 100644
>> --- a/drivers/staging/iio/accel/Kconfig
>> +++ b/drivers/staging/iio/accel/Kconfig
>> @@ -12,6 +12,15 @@ config ADIS16209
>> Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer
>> and accelerometer.
>>
>> +config ADIS16240
>> + tristate "Analog Devices ADIS16240 Programmable 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 adis16240 programmable
>> + impact Sensor and recorder.
>> +
>> config KXSD9
>> tristate "Kionix KXSD9 Accelerometer Driver"
>> depends on SPI
>> diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
>> index f8f2124..0e6762c 100644
>> --- a/drivers/staging/iio/accel/Makefile
>> +++ b/drivers/staging/iio/accel/Makefile
>> @@ -5,6 +5,10 @@ adis16209-y := adis16209_core.o
>> adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
>> obj-$(CONFIG_ADIS16209) += adis16209.o
>>
>> +adis16240-y := adis16240_core.o
>> +adis16240-$(CONFIG_IIO_RING_BUFFER) += adis16240_ring.o adis16240_trigger.o
>> +obj-$(CONFIG_ADIS16240) += adis16240.o
>> +
>> obj-$(CONFIG_KXSD9) += kxsd9.o
>>
>> lis3l02dq-y := lis3l02dq_core.o
>> diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c
>> new file mode 100644
>> index 0000000..54fd6d7
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16240_core.c
>> @@ -0,0 +1,599 @@
>> +/*
>> + * ADIS16240 Programmable Impact Sensor and Recorder 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/sysfs.h>
>> +#include <linux/list.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +#include "accel.h"
>> +#include "../adc/adc.h"
>> +
>> +#include "adis16240.h"
>> +
>> +#define DRIVER_NAME "adis16240"
>> +
>> +static int adis16240_check_status(struct device *dev);
>> +
>> +/**
>> + * adis16240_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 adis16240_spi_write_reg_8(struct device *dev,
>> + u8 reg_address,
>> + u8 val)
>> +{
>> + int ret;
>> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> + struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
>> +
>> + mutex_lock(&st->buf_lock);
>> + st->tx[0] = ADIS16240_WRITE_REG(reg_address);
>> + st->tx[1] = val;
>> +
>> + ret = spi_write(st->us, st->tx, 2);
>> + mutex_unlock(&st->buf_lock);
>> +
>> + return ret;
>> +}
>> +
>> +/**
>> + * adis16240_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 adis16240_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 adis16240_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,
>> + .delay_usecs = 25,
>> + }, {
>> + .tx_buf = st->tx + 2,
>> + .bits_per_word = 8,
>> + .len = 2,
>> + .cs_change = 1,
>> + .delay_usecs = 25,
>> + },
>> + };
>> +
>> + mutex_lock(&st->buf_lock);
>> + st->tx[0] = ADIS16240_WRITE_REG(lower_reg_address);
>> + st->tx[1] = value & 0xFF;
>> + st->tx[2] = ADIS16240_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;
>> +}
>> +
>> +/**
>> + * adis16240_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 adis16240_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 adis16240_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 = 25,
>> + }, {
>> + .rx_buf = st->rx,
>> + .bits_per_word = 8,
>> + .len = 2,
>> + .cs_change = 1,
>> + .delay_usecs = 25,
>> + },
>> + };
>> +
>> + mutex_lock(&st->buf_lock);
>> + st->tx[0] = ADIS16240_READ_REG(lower_reg_address);
>> + st->tx[1] = 0;
>> + st->tx[2] = 0;
>> + st->tx[3] = 0;
>> +
>> + spi_message_init(&msg);
>> + spi_message_add_tail(&xfers[0], &msg);
>> + spi_message_add_tail(&xfers[1], &msg);
>> + ret = spi_sync(st->us, &msg);
>> + if (ret) {
>> + dev_err(&st->us->dev,
>> + "problem when reading 16 bit register 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 adis16240_spi_read_signed(struct device *dev,
>> + struct device_attribute *attr,
>> + char *buf,
>> + unsigned bits)
>> +{
>> + int ret;
>> + s16 val = 0;
>> + unsigned shift = 16 - bits;
>> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>> +
>> + ret = adis16240_spi_read_reg_16(dev, this_attr->address, (u16 *)&val);
>> + if (ret)
>> + return ret;
>> +
>> + if (val & ADIS16240_ERROR_ACTIVE)
>> + adis16240_check_status(dev);
>> +
>> + val = ((s16)(val << shift) >> shift);
>> + return sprintf(buf, "%d\n", val);
>> +}
>> +
>> +static ssize_t adis16240_read_10bit_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 = adis16240_spi_read_reg_16(dev, this_attr->address, &val);
>> + if (ret)
>> + return ret;
>> +
>> + if (val & ADIS16240_ERROR_ACTIVE)
>> + adis16240_check_status(dev);
>> +
>> + return sprintf(buf, "%u\n", val & 0x03FF);
>> +}
>> +
>> +static ssize_t adis16240_read_10bit_signed(struct device *dev,
>> + struct device_attribute *attr,
>> + char *buf)
>> +{
>> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> + ssize_t ret;
>> +
>> + /* Take the iio_dev status lock */
>> + mutex_lock(&indio_dev->mlock);
>> + ret = adis16240_spi_read_signed(dev, attr, buf, 10);
>> + mutex_unlock(&indio_dev->mlock);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t adis16240_read_12bit_signed(struct device *dev,
>> + struct device_attribute *attr,
>> + char *buf)
>> +{
>> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> + ssize_t ret;
>> +
>> + /* Take the iio_dev status lock */
>> + mutex_lock(&indio_dev->mlock);
>> + ret = adis16240_spi_read_signed(dev, attr, buf, 12);
>> + mutex_unlock(&indio_dev->mlock);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t adis16240_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 = adis16240_spi_write_reg_16(dev, this_attr->address, val);
>> +
>> +error_ret:
>> + return ret ? ret : len;
>> +}
>> +
>> +static int adis16240_reset(struct device *dev)
>> +{
>> + int ret;
>> + ret = adis16240_spi_write_reg_8(dev,
>> + ADIS16240_GLOB_CMD,
>> + ADIS16240_GLOB_CMD_SW_RESET);
>> + if (ret)
>> + dev_err(dev, "problem resetting device");
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t adis16240_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 adis16240_reset(dev);
>> + }
>> + return -EINVAL;
>> +}
>> +
>> +int adis16240_set_irq(struct device *dev, bool enable)
>> +{
>> + int ret = 0;
>> + u16 msc;
>> +
>> + ret = adis16240_spi_read_reg_16(dev, ADIS16240_MSC_CTRL, &msc);
>> + if (ret)
>> + goto error_ret;
>> +
>> + msc |= ADIS16240_MSC_CTRL_ACTIVE_HIGH;
>> + msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_DIO2;
>> + if (enable)
>> + msc |= ADIS16240_MSC_CTRL_DATA_RDY_EN;
>> + else
>> + msc &= ~ADIS16240_MSC_CTRL_DATA_RDY_EN;
>> +
>> + ret = adis16240_spi_write_reg_16(dev, ADIS16240_MSC_CTRL, msc);
>> +
>> +error_ret:
>> + return ret;
>> +}
>> +
>> +static int adis16240_self_test(struct device *dev)
>> +{
>> + int ret;
>> + ret = adis16240_spi_write_reg_16(dev,
>> + ADIS16240_MSC_CTRL,
>> + ADIS16240_MSC_CTRL_SELF_TEST_EN);
>> + if (ret) {
>> + dev_err(dev, "problem starting self test");
>> + goto err_ret;
>> + }
>> +
>> + msleep(ADIS16240_STARTUP_DELAY);
>> +
>> + adis16240_check_status(dev);
>> +
>> +err_ret:
>> + return ret;
>> +}
>> +
>> +static int adis16240_check_status(struct device *dev)
>> +{
>> + u16 status;
>> + int ret;
>> +
>> + ret = adis16240_spi_read_reg_16(dev, ADIS16240_DIAG_STAT, &status);
>> +
>> + if (ret < 0) {
>> + dev_err(dev, "Reading status failed\n");
>> + goto error_ret;
>> + }
>> +
>> + ret = status & 0x2F;
>> + if (status & ADIS16240_DIAG_STAT_PWRON_FAIL)
>> + dev_err(dev, "Power-on, self-test fail\n");
>> + if (status & ADIS16240_DIAG_STAT_SPI_FAIL)
>> + dev_err(dev, "SPI failure\n");
>> + if (status & ADIS16240_DIAG_STAT_FLASH_UPT)
>> + dev_err(dev, "Flash update failed\n");
>> + if (status & ADIS16240_DIAG_STAT_POWER_HIGH)
>> + dev_err(dev, "Power supply above 3.625V\n");
>> + if (status & ADIS16240_DIAG_STAT_POWER_LOW)
>> + dev_err(dev, "Power supply below 2.225V\n");
>> +
>> +error_ret:
>> + return ret;
>> +}
>> +
>> +static int adis16240_initial_setup(struct adis16240_state *st)
>> +{
>> + int ret;
>> + struct device *dev = &st->indio_dev->dev;
>> +
>> + /* Disable IRQ */
>> + ret = adis16240_set_irq(dev, false);
>> + if (ret) {
>> + dev_err(dev, "disable irq failed");
>> + goto err_ret;
>> + }
>> +
>> + /* Do self test */
>> + ret = adis16240_self_test(dev);
>> + if (ret) {
>> + dev_err(dev, "self test failure");
>> + goto err_ret;
>> + }
>> +
>> + /* Read status register to check the result */
>> + ret = adis16240_check_status(dev);
>> + if (ret) {
>> + adis16240_reset(dev);
>> + dev_err(dev, "device not playing ball -> reset");
>> + msleep(ADIS16240_STARTUP_DELAY);
>> + ret = adis16240_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, adis16240_read_10bit_unsigned,
>> + ADIS16240_SUPPLY_OUT);
>> +static IIO_DEV_ATTR_IN_RAW(0, adis16240_read_10bit_signed,
>> + ADIS16240_AUX_ADC);
>> +static IIO_CONST_ATTR(in_supply_scale, "0.00488");
>> +static IIO_DEV_ATTR_ACCEL_X(adis16240_read_10bit_signed,
>> + ADIS16240_XACCL_OUT);
>> +static IIO_DEVICE_ATTR(accel_x_peak_raw, S_IRUGO,
>> + adis16240_read_10bit_signed, NULL,
>> + ADIS16240_XPEAK_OUT);
>> +static IIO_DEV_ATTR_ACCEL_Y(adis16240_read_10bit_signed,
>> + ADIS16240_YACCL_OUT);
>> +static IIO_DEVICE_ATTR(accel_y_peak_raw, S_IRUGO,
>> + adis16240_read_10bit_signed, NULL,
>> + ADIS16240_YPEAK_OUT);
>> +static IIO_DEV_ATTR_ACCEL_Z(adis16240_read_10bit_signed,
>> + ADIS16240_ZACCL_OUT);
>> +static IIO_DEVICE_ATTR(accel_z_peak_raw, S_IRUGO,
>> + adis16240_read_10bit_signed, NULL,
>> + ADIS16240_ZPEAK_OUT);
>> +
>> +static IIO_DEVICE_ATTR(accel_xyz_squared_peak_raw, S_IRUGO,
>> + adis16240_read_12bit_signed, NULL,
>> + ADIS16240_XYZPEAK_OUT);
>> +static IIO_DEV_ATTR_ACCEL_X_OFFSET(S_IWUSR | S_IRUGO,
>> + adis16240_read_10bit_signed,
>> + adis16240_write_16bit,
>> + ADIS16240_XACCL_OFF);
>> +static IIO_DEV_ATTR_ACCEL_Y_OFFSET(S_IWUSR | S_IRUGO,
>> + adis16240_read_10bit_signed,
>> + adis16240_write_16bit,
>> + ADIS16240_YACCL_OFF);
>> +static IIO_DEV_ATTR_ACCEL_Z_OFFSET(S_IWUSR | S_IRUGO,
>> + adis16240_read_10bit_signed,
>> + adis16240_write_16bit,
>> + ADIS16240_ZACCL_OFF);
>> +static IIO_DEV_ATTR_TEMP_RAW(adis16240_read_10bit_unsigned);
>> +static IIO_CONST_ATTR(temp_scale, "0.244");
>> +
>> +static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, adis16240_write_reset, 0);
>> +
>> +static IIO_CONST_ATTR_AVAIL_SAMP_FREQ("4096");
>> +
>> +static IIO_CONST_ATTR(name, "adis16240");
>> +
>> +static struct attribute *adis16240_event_attributes[] = {
>> + NULL
>> +};
>> +
>> +static struct attribute_group adis16240_event_attribute_group = {
>> + .attrs = adis16240_event_attributes,
>> +};
>> +
>> +static struct attribute *adis16240_attributes[] = {
>> + &iio_dev_attr_in_supply_raw.dev_attr.attr,
>> + &iio_const_attr_in_supply_scale.dev_attr.attr,
>> + &iio_dev_attr_in0_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_x_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_x_offset.dev_attr.attr,
>> + &iio_dev_attr_accel_x_peak_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_y_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_y_offset.dev_attr.attr,
>> + &iio_dev_attr_accel_y_peak_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_z_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_z_offset.dev_attr.attr,
>> + &iio_dev_attr_accel_z_peak_raw.dev_attr.attr,
>> + &iio_dev_attr_accel_xyz_squared_peak_raw.dev_attr.attr,
>> + &iio_dev_attr_temp_raw.dev_attr.attr,
>> + &iio_const_attr_temp_scale.dev_attr.attr,
>> + &iio_const_attr_available_sampling_frequency.dev_attr.attr,
>> + &iio_dev_attr_reset.dev_attr.attr,
>> + &iio_const_attr_name.dev_attr.attr,
>> + NULL
>> +};
>> +
>> +static const struct attribute_group adis16240_attribute_group = {
>> + .attrs = adis16240_attributes,
>> +};
>> +
>> +static int __devinit adis16240_probe(struct spi_device *spi)
>> +{
>> + int ret, regdone = 0;
>> + struct adis16240_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)*ADIS16240_MAX_RX, GFP_KERNEL);
>> + if (st->rx == NULL) {
>> + ret = -ENOMEM;
>> + goto error_free_st;
>> + }
>> + st->tx = kzalloc(sizeof(*st->tx)*ADIS16240_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 = &adis16240_event_attribute_group;
>> + st->indio_dev->attrs = &adis16240_attribute_group;
>> + st->indio_dev->dev_data = (void *)(st);
>> + st->indio_dev->driver_module = THIS_MODULE;
>> + st->indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> + ret = adis16240_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 = adis16240_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,
>> + "adis16240");
>> + if (ret)
>> + goto error_uninitialize_ring;
>> +
>> + ret = adis16240_probe_trigger(st->indio_dev);
>> + if (ret)
>> + goto error_unregister_line;
>> + }
>> +
>> + /* Get the device into a sane initial state */
>> + ret = adis16240_initial_setup(st);
>> + if (ret)
>> + goto error_remove_trigger;
>> + return 0;
>> +
>> +error_remove_trigger:
>> + adis16240_remove_trigger(st->indio_dev);
>> +error_unregister_line:
>> + if (spi->irq)
>> + iio_unregister_interrupt_line(st->indio_dev, 0);
>> +error_uninitialize_ring:
>> + adis16240_uninitialize_ring(st->indio_dev->ring);
>> +error_unreg_ring_funcs:
>> + adis16240_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 adis16240_remove(struct spi_device *spi)
>> +{
>> + struct adis16240_state *st = spi_get_drvdata(spi);
>> + struct iio_dev *indio_dev = st->indio_dev;
>> +
>> + flush_scheduled_work();
>> +
>> + adis16240_remove_trigger(indio_dev);
>> + if (spi->irq)
>> + iio_unregister_interrupt_line(indio_dev, 0);
>> +
>> + adis16240_uninitialize_ring(indio_dev->ring);
>> + iio_device_unregister(indio_dev);
>> + adis16240_unconfigure_ring(indio_dev);
>> + kfree(st->tx);
>> + kfree(st->rx);
>> + kfree(st);
>> +
>> + return 0;
>> +}
>> +
>> +static struct spi_driver adis16240_driver = {
>> + .driver = {
>> + .name = "adis16240",
>> + .owner = THIS_MODULE,
>> + },
>> + .probe = adis16240_probe,
>> + .remove = __devexit_p(adis16240_remove),
>> +};
>> +
>> +static __init int adis16240_init(void)
>> +{
>> + return spi_register_driver(&adis16240_driver);
>> +}
>> +module_init(adis16240_init);
>> +
>> +static __exit void adis16240_exit(void)
>> +{
>> + spi_unregister_driver(&adis16240_driver);
>> +}
>> +module_exit(adis16240_exit);
>> +
>> +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
>> +MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c
>> new file mode 100644
>> index 0000000..26b677b
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16240_ring.c
>> @@ -0,0 +1,254 @@
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/gpio.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/mutex.h>
>> +#include <linux/device.h>
>> +#include <linux/kernel.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/list.h>
>> +
>> +#include "../iio.h"
>> +#include "../sysfs.h"
>> +#include "../ring_sw.h"
>> +#include "accel.h"
>> +#include "../trigger.h"
>> +#include "adis16240.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, ADIS16240_SCAN_SUPPLY, IIO_UNSIGNED(10),
>> + ADIS16240_SUPPLY_OUT, NULL);
>> +static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, IIO_SIGNED(10),
>> + ADIS16240_XACCL_OUT, NULL);
>> +static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, IIO_SIGNED(10),
>> + ADIS16240_YACCL_OUT, NULL);
>> +static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, IIO_SIGNED(10),
>> + ADIS16240_ZACCL_OUT, NULL);
>> +static IIO_SCAN_EL_C(aux_adc, ADIS16240_SCAN_AUX_ADC, IIO_UNSIGNED(10),
>> + ADIS16240_AUX_ADC, NULL);
>> +static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, IIO_UNSIGNED(10),
>> + ADIS16240_TEMP_OUT, NULL);
>> +
>> +static IIO_SCAN_EL_TIMESTAMP(6);
>> +
>> +static struct attribute *adis16240_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_accel_z.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 adis16240_scan_el_group = {
>> + .attrs = adis16240_scan_el_attrs,
>> + .name = "scan_elements",
>> +};
>> +
>> +/**
>> + * adis16240_poll_func_th() top half interrupt handler called by trigger
>> + * @private_data: iio_dev
>> + **/
>> +static void adis16240_poll_func_th(struct iio_dev *indio_dev)
>> +{
>> + struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
>> + st->last_timestamp = indio_dev->trig->timestamp;
>> + schedule_work(&st->work_trigger_to_ring);
>> +}
>> +
>> +/**
>> + * adis16240_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 adis16240_read_ring_data(struct device *dev, u8 *rx)
>> +{
>> + struct spi_message msg;
>> + struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> + struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
>> + struct spi_transfer xfers[ADIS16240_OUTPUTS + 1];
>> + int ret;
>> + int i;
>> +
>> + mutex_lock(&st->buf_lock);
>> +
>> + spi_message_init(&msg);
>> +
>> + memset(xfers, 0, sizeof(xfers));
>> + for (i = 0; i <= ADIS16240_OUTPUTS; i++) {
>> + xfers[i].bits_per_word = 8;
>> + xfers[i].cs_change = 1;
>> + xfers[i].len = 2;
>> + xfers[i].delay_usecs = 30;
>> + xfers[i].tx_buf = st->tx + 2 * i;
>> + st->tx[2 * i]
>> + = ADIS16240_READ_REG(ADIS16240_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;
>> +}
>> +
>> +
>> +static void adis16240_trigger_bh_to_ring(struct work_struct *work_s)
>> +{
>> + struct adis16240_state *st
>> + = container_of(work_s, struct adis16240_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 (adis16240_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;
>> +}
>> +
>> +static int adis16240_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 (aligned sizeof(s64) and data */
>> + 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 adis16240_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 adis16240_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 adis16240_unconfigure_ring(struct iio_dev *indio_dev)
>> +{
>> + kfree(indio_dev->pollfunc);
>> + iio_sw_rb_free(indio_dev->ring);
>> +}
>> +
>> +int adis16240_configure_ring(struct iio_dev *indio_dev)
>> +{
>> + int ret = 0;
>> + struct adis16240_state *st = indio_dev->dev_data;
>> + struct iio_ring_buffer *ring;
>> + INIT_WORK(&st->work_trigger_to_ring, adis16240_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_accel_z.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 = &adis16240_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 = &adis16240_data_rdy_ring_preenable;
>> + ring->postenable = &adis16240_data_rdy_ring_postenable;
>> + ring->predisable = &adis16240_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 = &adis16240_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 adis16240_initialize_ring(struct iio_ring_buffer *ring)
>> +{
>> + return iio_ring_buffer_register(ring, 0);
>> +}
>> +
>> +void adis16240_uninitialize_ring(struct iio_ring_buffer *ring)
>> +{
>> + iio_ring_buffer_unregister(ring);
>> +}
>> diff --git a/drivers/staging/iio/accel/adis16240_trigger.c b/drivers/staging/iio/accel/adis16240_trigger.c
>> new file mode 100644
>> index 0000000..df1312e
>> --- /dev/null
>> +++ b/drivers/staging/iio/accel/adis16240_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 "adis16240.h"
>> +
>> +/**
>> + * adis16240_data_rdy_trig_poll() the event handler for the data rdy trig
>> + **/
>> +static int adis16240_data_rdy_trig_poll(struct iio_dev *dev_info,
>> + int index,
>> + s64 timestamp,
>> + int no_test)
>> +{
>> + struct adis16240_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, &adis16240_data_rdy_trig_poll);
>> +
>> +static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
>> +
>> +static struct attribute *adis16240_trigger_attrs[] = {
>> + &dev_attr_name.attr,
>> + NULL,
>> +};
>> +
>> +static const struct attribute_group adis16240_trigger_attr_group = {
>> + .attrs = adis16240_trigger_attrs,
>> +};
>> +
>> +/**
>> + * adis16240_data_rdy_trigger_set_state() set datardy interrupt state
>> + **/
>> +static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *trig,
>> + bool state)
>> +{
>> + struct adis16240_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 = adis16240_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;
>> +}
>> +
>> +/**
>> + * adis16240_trig_try_reen() try renabling irq for data rdy trigger
>> + * @trig: the datardy trigger
>> + **/
>> +static int adis16240_trig_try_reen(struct iio_trigger *trig)
>> +{
>> + struct adis16240_state *st = trig->private_data;
>> + enable_irq(st->us->irq);
>> + return 0;
>> +}
>> +
>> +int adis16240_probe_trigger(struct iio_dev *indio_dev)
>> +{
>> + int ret;
>> + struct adis16240_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,
>> + "adis16240-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 = &adis16240_data_rdy_trigger_set_state;
>> + st->trig->try_reenable = &adis16240_trig_try_reen;
>> + st->trig->control_attrs = &adis16240_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 adis16240_remove_trigger(struct iio_dev *indio_dev)
>> +{
>> + struct adis16240_state *state = indio_dev->dev_data;
>> +
>> + iio_trigger_unregister(state->trig);
>> + kfree(state->trig->name);
>> + iio_free_trigger(state->trig);
>> +}
>> --
>> 1.6.4.4
>>
>> --
>> 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
>>
> --
> 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] 4+ messages in thread
end of thread, other threads:[~2010-05-07 14:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-06 13:42 [PATCH 1/2] staging:iio: adis16209 driver Jonathan Cameron
2010-05-06 13:42 ` [PATCH 2/2] staging:iio: adis16240 driver Jonathan Cameron
2010-05-07 2:54 ` Barry Song
2010-05-07 14:24 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox