summaryrefslogtreecommitdiff
path: root/drivers/staging/iio/accel
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/iio/accel')
-rw-r--r--drivers/staging/iio/accel/Kconfig101
-rw-r--r--drivers/staging/iio/accel/Makefile28
-rw-r--r--drivers/staging/iio/accel/adis16201.h66
-rw-r--r--drivers/staging/iio/accel/adis16201_core.c248
-rw-r--r--drivers/staging/iio/accel/adis16203.h59
-rw-r--r--drivers/staging/iio/accel/adis16203_core.c216
-rw-r--r--drivers/staging/iio/accel/adis16204.h68
-rw-r--r--drivers/staging/iio/accel/adis16204_core.c254
-rw-r--r--drivers/staging/iio/accel/adis16209.h105
-rw-r--r--drivers/staging/iio/accel/adis16209_core.c248
-rw-r--r--drivers/staging/iio/accel/adis16220.h140
-rw-r--r--drivers/staging/iio/accel/adis16220_core.c495
-rw-r--r--drivers/staging/iio/accel/adis16240.h129
-rw-r--r--drivers/staging/iio/accel/adis16240_core.c301
-rw-r--r--drivers/staging/iio/accel/lis3l02dq.h212
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_core.c813
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_ring.c426
-rw-r--r--drivers/staging/iio/accel/sca3000.h278
-rw-r--r--drivers/staging/iio/accel/sca3000_core.c1209
-rw-r--r--drivers/staging/iio/accel/sca3000_ring.c348
20 files changed, 5744 insertions, 0 deletions
diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
new file mode 100644
index 000000000..07b7ffa00
--- /dev/null
+++ b/drivers/staging/iio/accel/Kconfig
@@ -0,0 +1,101 @@
+#
+# Accelerometer drivers
+#
+menu "Accelerometers"
+
+config ADIS16201
+ tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16201 dual-axis
+ digital inclinometer and accelerometer.
+
+ To compile this driver as a module, say M here: the module will
+ be called adis16201.
+
+config ADIS16203
+ tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16203 Programmable
+ 360 Degrees Inclinometer.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16203.
+
+config ADIS16204
+ tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16204 Programmable
+ High-g Digital Impact Sensor and Recorder.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16204.
+
+config ADIS16209
+ tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
+ and accelerometer.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16209.
+
+config ADIS16220
+ tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor"
+ depends on SPI
+ select IIO_ADIS_LIB
+ help
+ Say Y here to build support for Analog Devices adis16220 programmable
+ digital vibration sensor.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16220.
+
+config ADIS16240
+ tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16240 programmable
+ impact Sensor and recorder.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16240.
+
+config LIS3L02DQ
+ tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
+ depends on SPI
+ select IIO_TRIGGER if IIO_BUFFER
+ depends on !IIO_BUFFER || IIO_KFIFO_BUF
+ depends on GPIOLIB
+ help
+ Say Y here to build SPI support for the ST microelectronics
+ accelerometer. The driver supplies direct access via sysfs files
+ and an event interface via a character device.
+
+ To compile this driver as a module, say M here: the module will be
+ called lis3l02dq.
+
+config SCA3000
+ depends on IIO_BUFFER
+ depends on SPI
+ tristate "VTI SCA3000 series accelerometers"
+ help
+ Say Y here to build support for the VTI SCA3000 series of SPI
+ accelerometers. These devices use a hardware ring buffer.
+
+ To compile this driver as a module, say M here: the module will be
+ called sca3000.
+endmenu
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
new file mode 100644
index 000000000..1ed137f1a
--- /dev/null
+++ b/drivers/staging/iio/accel/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+adis16201-y := adis16201_core.o
+obj-$(CONFIG_ADIS16201) += adis16201.o
+
+adis16203-y := adis16203_core.o
+obj-$(CONFIG_ADIS16203) += adis16203.o
+
+adis16204-y := adis16204_core.o
+obj-$(CONFIG_ADIS16204) += adis16204.o
+
+adis16209-y := adis16209_core.o
+obj-$(CONFIG_ADIS16209) += adis16209.o
+
+adis16220-y := adis16220_core.o
+obj-$(CONFIG_ADIS16220) += adis16220.o
+
+adis16240-y := adis16240_core.o
+obj-$(CONFIG_ADIS16240) += adis16240.o
+
+lis3l02dq-y := lis3l02dq_core.o
+lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o
+obj-$(CONFIG_LIS3L02DQ) += lis3l02dq.o
+
+sca3000-y := sca3000_core.o sca3000_ring.o
+obj-$(CONFIG_SCA3000) += sca3000.o
diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
new file mode 100644
index 000000000..e6b8c9af6
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201.h
@@ -0,0 +1,66 @@
+#ifndef SPI_ADIS16201_H_
+#define SPI_ADIS16201_H_
+
+#define ADIS16201_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16201_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16201_SUPPLY_OUT 0x02 /* Output, power supply */
+#define ADIS16201_XACCL_OUT 0x04 /* Output, x-axis accelerometer */
+#define ADIS16201_YACCL_OUT 0x06 /* Output, y-axis accelerometer */
+#define ADIS16201_AUX_ADC 0x08 /* Output, auxiliary ADC input */
+#define ADIS16201_TEMP_OUT 0x0A /* Output, temperature */
+#define ADIS16201_XINCL_OUT 0x0C /* Output, x-axis inclination */
+#define ADIS16201_YINCL_OUT 0x0E /* Output, y-axis inclination */
+#define ADIS16201_XACCL_OFFS 0x10 /* Calibration, x-axis acceleration offset */
+#define ADIS16201_YACCL_OFFS 0x12 /* Calibration, y-axis acceleration offset */
+#define ADIS16201_XACCL_SCALE 0x14 /* x-axis acceleration scale factor */
+#define ADIS16201_YACCL_SCALE 0x16 /* y-axis acceleration scale factor */
+#define ADIS16201_XINCL_OFFS 0x18 /* Calibration, x-axis inclination offset */
+#define ADIS16201_YINCL_OFFS 0x1A /* Calibration, y-axis inclination offset */
+#define ADIS16201_XINCL_SCALE 0x1C /* x-axis inclination scale factor */
+#define ADIS16201_YINCL_SCALE 0x1E /* y-axis inclination scale factor */
+#define ADIS16201_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16201_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16201_ALM_SMPL1 0x24 /* Alarm 1, sample period */
+#define ADIS16201_ALM_SMPL2 0x26 /* Alarm 2, sample period */
+#define ADIS16201_ALM_CTRL 0x28 /* Alarm control */
+#define ADIS16201_AUX_DAC 0x30 /* Auxiliary DAC data */
+#define ADIS16201_GPIO_CTRL 0x32 /* General-purpose digital input/output control */
+#define ADIS16201_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16201_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16201_AVG_CNT 0x38 /* Operation, filter configuration */
+#define ADIS16201_SLP_CNT 0x3A /* Operation, sleep mode control */
+#define ADIS16201_DIAG_STAT 0x3C /* Diagnostics, system status register */
+#define ADIS16201_GLOB_CMD 0x3E /* Operation, system command register */
+
+/* MSC_CTRL */
+#define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */
+#define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16201_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0) /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16201_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
+#define ADIS16201_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
+#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
+#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16201_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16201_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16201_ERROR_ACTIVE BIT(14)
+
+enum adis16201_scan {
+ ADIS16201_SCAN_ACC_X,
+ ADIS16201_SCAN_ACC_Y,
+ ADIS16201_SCAN_INCLI_X,
+ ADIS16201_SCAN_INCLI_Y,
+ ADIS16201_SCAN_SUPPLY,
+ ADIS16201_SCAN_AUX_ADC,
+ ADIS16201_SCAN_TEMP,
+};
+
+#endif /* SPI_ADIS16201_H_ */
diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
new file mode 100644
index 000000000..10db68581
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_core.c
@@ -0,0 +1,248 @@
+/*
+ * ADIS16201 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16201.h"
+
+static const u8 adis16201_addresses[] = {
+ [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS,
+ [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS,
+ [ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS,
+ [ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS,
+};
+
+static int adis16201_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16201_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220000; /* 1.22 mV */
+ } else {
+ *val = 0;
+ *val2 = 610000; /* 0.610 mV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* 0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(462400); /* 0.4624 mg */
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_INCLI:
+ *val = 0;
+ *val2 = 100000; /* 0.1 degree */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 12;
+ break;
+ case IIO_INCLI:
+ bits = 9;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16201_addresses[chan->scan_index];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adis16201_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 12;
+ break;
+ case IIO_INCLI:
+ bits = 9;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16201_addresses[chan->scan_index];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16201_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 0, 12),
+ ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(7)
+};
+
+static const struct iio_info adis16201_info = {
+ .read_raw = &adis16201_read_raw,
+ .write_raw = &adis16201_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16201_status_error_msgs[] = {
+ [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16201_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16201_data = {
+ .read_delay = 20,
+ .msc_ctrl_reg = ADIS16201_MSC_CTRL,
+ .glob_cmd_reg = ADIS16201_GLOB_CMD,
+ .diag_stat_reg = ADIS16201_DIAG_STAT,
+
+ .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16201_STARTUP_DELAY,
+
+ .status_error_msgs = adis16201_status_error_msgs,
+ .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16201_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16201_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16201_info;
+
+ indio_dev->channels = adis16201_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16201_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16201_data);
+ if (ret)
+ return ret;
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_cleanup_buffer_trigger;
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16201_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16201_driver = {
+ .driver = {
+ .name = "adis16201",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16201_probe,
+ .remove = adis16201_remove,
+};
+module_spi_driver(adis16201_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16201");
diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h
new file mode 100644
index 000000000..6426e38bf
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203.h
@@ -0,0 +1,59 @@
+#ifndef SPI_ADIS16203_H_
+#define SPI_ADIS16203_H_
+
+#define ADIS16203_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16203_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16203_SUPPLY_OUT 0x02 /* Output, power supply */
+#define ADIS16203_AUX_ADC 0x08 /* Output, auxiliary ADC input */
+#define ADIS16203_TEMP_OUT 0x0A /* Output, temperature */
+#define ADIS16203_XINCL_OUT 0x0C /* Output, x-axis inclination */
+#define ADIS16203_YINCL_OUT 0x0E /* Output, y-axis inclination */
+#define ADIS16203_INCL_NULL 0x18 /* Incline null calibration */
+#define ADIS16203_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16203_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16203_ALM_SMPL1 0x24 /* Alarm 1, sample period */
+#define ADIS16203_ALM_SMPL2 0x26 /* Alarm 2, sample period */
+#define ADIS16203_ALM_CTRL 0x28 /* Alarm control */
+#define ADIS16203_AUX_DAC 0x30 /* Auxiliary DAC data */
+#define ADIS16203_GPIO_CTRL 0x32 /* General-purpose digital input/output control */
+#define ADIS16203_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16203_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16203_AVG_CNT 0x38 /* Operation, filter configuration */
+#define ADIS16203_SLP_CNT 0x3A /* Operation, sleep mode control */
+#define ADIS16203_DIAG_STAT 0x3C /* Diagnostics, system status register */
+#define ADIS16203_GLOB_CMD 0x3E /* Operation, system command register */
+
+/* MSC_CTRL */
+#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST BIT(10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN BIT(9) /* Reverses rotation of both inclination outputs */
+#define ADIS16203_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */
+#define ADIS16203_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16203_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1 BIT(0) /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16203_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag */
+#define ADIS16203_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
+#define ADIS16203_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
+#define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
+#define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16203_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16203_GLOB_CMD_CLEAR_STAT BIT(4)
+#define ADIS16203_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16203_ERROR_ACTIVE BIT(14)
+
+enum adis16203_scan {
+ ADIS16203_SCAN_INCLI_X,
+ ADIS16203_SCAN_INCLI_Y,
+ ADIS16203_SCAN_SUPPLY,
+ ADIS16203_SCAN_AUX_ADC,
+ ADIS16203_SCAN_TEMP,
+};
+
+#endif /* SPI_ADIS16203_H_ */
diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
new file mode 100644
index 000000000..fb593d23d
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_core.c
@@ -0,0 +1,216 @@
+/*
+ * ADIS16203 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2030 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16203.h"
+
+#define DRIVER_NAME "adis16203"
+
+static const u8 adis16203_addresses[] = {
+ [ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL,
+};
+
+static int adis16203_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ /* currently only one writable parameter which keeps this simple */
+ u8 addr = adis16203_addresses[chan->scan_index];
+
+ return adis_write_reg_16(st, addr, val & 0x3FFF);
+}
+
+static int adis16203_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16203_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220000; /* 1.22 mV */
+ } else {
+ *val = 0;
+ *val2 = 610000; /* 0.61 mV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* -0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_INCLI:
+ *val = 0;
+ *val2 = 25000; /* 0.025 degree */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ bits = 14;
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16203_addresses[chan->scan_index];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec adis16203_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 0, 12),
+ ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ /* Fixme: Not what it appears to be - see data sheet */
+ ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y,
+ 0, 0, 14),
+ ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 0, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(5),
+};
+
+static const struct iio_info adis16203_info = {
+ .read_raw = &adis16203_read_raw,
+ .write_raw = &adis16203_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16203_status_error_msgs[] = {
+ [ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16203_data = {
+ .read_delay = 20,
+ .msc_ctrl_reg = ADIS16203_MSC_CTRL,
+ .glob_cmd_reg = ADIS16203_GLOB_CMD,
+ .diag_stat_reg = ADIS16203_DIAG_STAT,
+
+ .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16203_STARTUP_DELAY,
+
+ .status_error_msgs = adis16203_status_error_msgs,
+ .status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16203_probe(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct adis *st;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->channels = adis16203_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16203_channels);
+ indio_dev->info = &adis16203_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16203_data);
+ if (ret)
+ return ret;
+
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16203_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16203_driver = {
+ .driver = {
+ .name = "adis16203",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16203_probe,
+ .remove = adis16203_remove,
+};
+module_spi_driver(adis16203_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16203");
diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h
new file mode 100644
index 000000000..0b23f0b5c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204.h
@@ -0,0 +1,68 @@
+#ifndef SPI_ADIS16204_H_
+#define SPI_ADIS16204_H_
+
+#define ADIS16204_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16204_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16204_SUPPLY_OUT 0x02 /* Output, power supply */
+#define ADIS16204_XACCL_OUT 0x04 /* Output, x-axis accelerometer */
+#define ADIS16204_YACCL_OUT 0x06 /* Output, y-axis accelerometer */
+#define ADIS16204_AUX_ADC 0x08 /* Output, auxiliary ADC input */
+#define ADIS16204_TEMP_OUT 0x0A /* Output, temperature */
+#define ADIS16204_X_PEAK_OUT 0x0C /* Twos complement */
+#define ADIS16204_Y_PEAK_OUT 0x0E /* Twos complement */
+#define ADIS16204_XACCL_NULL 0x10 /* Calibration, x-axis acceleration offset null */
+#define ADIS16204_YACCL_NULL 0x12 /* Calibration, y-axis acceleration offset null */
+#define ADIS16204_XACCL_SCALE 0x14 /* X-axis scale factor calibration register */
+#define ADIS16204_YACCL_SCALE 0x16 /* Y-axis scale factor calibration register */
+#define ADIS16204_XY_RSS_OUT 0x18 /* XY combined acceleration (RSS) */
+#define ADIS16204_XY_PEAK_OUT 0x1A /* Peak, XY combined output (RSS) */
+#define ADIS16204_CAP_BUF_1 0x1C /* Capture buffer output register 1 */
+#define ADIS16204_CAP_BUF_2 0x1E /* Capture buffer output register 2 */
+#define ADIS16204_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16204_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16204_ALM_CTRL 0x28 /* Alarm control */
+#define ADIS16204_CAPT_PNTR 0x2A /* Capture register address pointer */
+#define ADIS16204_AUX_DAC 0x30 /* Auxiliary DAC data */
+#define ADIS16204_GPIO_CTRL 0x32 /* General-purpose digital input/output control */
+#define ADIS16204_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16204_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16204_AVG_CNT 0x38 /* Operation, filter configuration */
+#define ADIS16204_SLP_CNT 0x3A /* Operation, sleep mode control */
+#define ADIS16204_DIAG_STAT 0x3C /* Diagnostics, system status register */
+#define ADIS16204_GLOB_CMD 0x3E /* Operation, system command register */
+
+/* MSC_CTRL */
+#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST BIT(10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16204_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */
+#define ADIS16204_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16204_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2 BIT(0) /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+
+/* DIAG_STAT */
+#define ADIS16204_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag: 1 = error condition,
+ 0 = normal operation */
+#define ADIS16204_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
+#define ADIS16204_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
+#define ADIS16204_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
+#define ADIS16204_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 2.975 V */
+
+/* GLOB_CMD */
+#define ADIS16204_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16204_GLOB_CMD_CLEAR_STAT BIT(4)
+#define ADIS16204_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16204_ERROR_ACTIVE BIT(14)
+
+enum adis16204_scan {
+ ADIS16204_SCAN_ACC_X,
+ ADIS16204_SCAN_ACC_Y,
+ ADIS16204_SCAN_ACC_XY,
+ ADIS16204_SCAN_SUPPLY,
+ ADIS16204_SCAN_AUX_ADC,
+ ADIS16204_SCAN_TEMP,
+};
+
+#endif /* SPI_ADIS16204_H_ */
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
new file mode 100644
index 000000000..ea0ac2467
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -0,0 +1,254 @@
+/*
+ * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16204.h"
+
+/* Unique to this driver currently */
+
+static const u8 adis16204_addresses[][2] = {
+ [ADIS16204_SCAN_ACC_X] = { ADIS16204_XACCL_NULL, ADIS16204_X_PEAK_OUT },
+ [ADIS16204_SCAN_ACC_Y] = { ADIS16204_YACCL_NULL, ADIS16204_Y_PEAK_OUT },
+ [ADIS16204_SCAN_ACC_XY] = { 0, ADIS16204_XY_PEAK_OUT },
+};
+
+static int adis16204_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+ int addrind;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16204_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220000; /* 1.22 mV */
+ } else {
+ *val = 0;
+ *val2 = 610000; /* 0.61 mV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* 0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ case IIO_MOD_ROOT_SUM_SQUARED_X_Y:
+ *val2 = IIO_G_TO_M_S_2(17125); /* 17.125 mg */
+ break;
+ case IIO_MOD_Y:
+ case IIO_MOD_Z:
+ *val2 = IIO_G_TO_M_S_2(8407); /* 8.407 mg */
+ break;
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ case IIO_CHAN_INFO_PEAK:
+ if (mask == IIO_CHAN_INFO_CALIBBIAS) {
+ bits = 12;
+ addrind = 0;
+ } else { /* PEAK_SEPARATE */
+ bits = 14;
+ addrind = 1;
+ }
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16204_addresses[chan->scan_index][addrind];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adis16204_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16204_addresses[chan->scan_index][1];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16204_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 0, 12),
+ ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 0, 12),
+ ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 14),
+ ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 14),
+ ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT,
+ ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 0, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(5),
+};
+
+static const struct iio_info adis16204_info = {
+ .read_raw = &adis16204_read_raw,
+ .write_raw = &adis16204_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16204_status_error_msgs[] = {
+ [ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16204_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16204_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16204_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16204_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
+};
+
+static const struct adis_data adis16204_data = {
+ .read_delay = 20,
+ .msc_ctrl_reg = ADIS16204_MSC_CTRL,
+ .glob_cmd_reg = ADIS16204_GLOB_CMD,
+ .diag_stat_reg = ADIS16204_DIAG_STAT,
+
+ .self_test_mask = ADIS16204_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16204_STARTUP_DELAY,
+
+ .status_error_msgs = adis16204_status_error_msgs,
+ .status_error_mask = BIT(ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16204_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16204_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16204_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16204_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16204_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16204_info;
+ indio_dev->channels = adis16204_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16204_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16204_data);
+ if (ret)
+ return ret;
+
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16204_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16204_driver = {
+ .driver = {
+ .name = "adis16204",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16204_probe,
+ .remove = adis16204_remove,
+};
+module_spi_driver(adis16204_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("ADIS16204 High-g Digital Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16204");
diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h
new file mode 100644
index 000000000..813698d18
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209.h
@@ -0,0 +1,105 @@
+#ifndef SPI_ADIS16209_H_
+#define SPI_ADIS16209_H_
+
+#define ADIS16209_STARTUP_DELAY 220 /* ms */
+
+/* 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
+
+/* MSC_CTRL */
+/* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10)
+/* Self-test enable */
+#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8)
+/* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2)
+/* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1)
+/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
+
+/* DIAG_STAT */
+/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16209_DIAG_STAT_ALARM2 BIT(9)
+/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16209_DIAG_STAT_ALARM1 BIT(8)
+/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */
+#define ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT 5
+/* SPI communications failure */
+#define ADIS16209_DIAG_STAT_SPI_FAIL_BIT 3
+/* Flash update failure */
+#define ADIS16209_DIAG_STAT_FLASH_UPT_BIT 2
+/* Power supply above 3.625 V */
+#define ADIS16209_DIAG_STAT_POWER_HIGH_BIT 1
+/* Power supply below 3.15 V */
+#define ADIS16209_DIAG_STAT_POWER_LOW_BIT 0
+
+/* GLOB_CMD */
+#define ADIS16209_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16209_GLOB_CMD_CLEAR_STAT BIT(4)
+#define ADIS16209_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16209_ERROR_ACTIVE BIT(14)
+
+#define ADIS16209_SCAN_SUPPLY 0
+#define ADIS16209_SCAN_ACC_X 1
+#define ADIS16209_SCAN_ACC_Y 2
+#define ADIS16209_SCAN_AUX_ADC 3
+#define ADIS16209_SCAN_TEMP 4
+#define ADIS16209_SCAN_INCLI_X 5
+#define ADIS16209_SCAN_INCLI_Y 6
+#define ADIS16209_SCAN_ROT 7
+
+#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 000000000..d1dc1a3cb
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209_core.c
@@ -0,0 +1,248 @@
+/*
+ * ADIS16209 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16209.h"
+
+static const u8 adis16209_addresses[8][1] = {
+ [ADIS16209_SCAN_SUPPLY] = { },
+ [ADIS16209_SCAN_AUX_ADC] = { },
+ [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL },
+ [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL },
+ [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL },
+ [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL },
+ [ADIS16209_SCAN_ROT] = { },
+ [ADIS16209_SCAN_TEMP] = { },
+};
+
+static int adis16209_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ case IIO_INCLI:
+ bits = 14;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16209_addresses[chan->scan_index][0];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static int adis16209_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16209_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = 0;
+ if (chan->channel == 0)
+ *val2 = 305180; /* 0.30518 mV */
+ else
+ *val2 = 610500; /* 0.6105 mV */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* -0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_INCLI:
+ case IIO_ROT:
+ *val = 0;
+ *val2 = 25000; /* 0.025 degree */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 14;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16209_addresses[chan->scan_index][0];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16209_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 0, 14),
+ ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X,
+ 0, 0, 14),
+ ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y,
+ 0, 0, 14),
+ ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 0, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
+static const struct iio_info adis16209_info = {
+ .read_raw = &adis16209_read_raw,
+ .write_raw = &adis16209_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16209_status_error_msgs[] = {
+ [ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16209_data = {
+ .read_delay = 30,
+ .msc_ctrl_reg = ADIS16209_MSC_CTRL,
+ .glob_cmd_reg = ADIS16209_GLOB_CMD,
+ .diag_stat_reg = ADIS16209_DIAG_STAT,
+
+ .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16209_STARTUP_DELAY,
+
+ .status_error_msgs = adis16209_status_error_msgs,
+ .status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16209_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16209_info;
+ indio_dev->channels = adis16209_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16209_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16209_data);
+ if (ret)
+ return ret;
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16209_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16209_driver = {
+ .driver = {
+ .name = "adis16209",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16209_probe,
+ .remove = adis16209_remove,
+};
+module_spi_driver(adis16209_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16209");
diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h
new file mode 100644
index 000000000..eab863311
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16220.h
@@ -0,0 +1,140 @@
+#ifndef SPI_ADIS16220_H_
+#define SPI_ADIS16220_H_
+
+#include <linux/iio/imu/adis.h>
+
+#define ADIS16220_STARTUP_DELAY 220 /* ms */
+
+/* Flash memory write count */
+#define ADIS16220_FLASH_CNT 0x00
+/* Control, acceleration offset adjustment control */
+#define ADIS16220_ACCL_NULL 0x02
+/* Control, AIN1 offset adjustment control */
+#define ADIS16220_AIN1_NULL 0x04
+/* Control, AIN2 offset adjustment control */
+#define ADIS16220_AIN2_NULL 0x06
+/* Output, power supply during capture */
+#define ADIS16220_CAPT_SUPPLY 0x0A
+/* Output, temperature during capture */
+#define ADIS16220_CAPT_TEMP 0x0C
+/* Output, peak acceleration during capture */
+#define ADIS16220_CAPT_PEAKA 0x0E
+/* Output, peak AIN1 level during capture */
+#define ADIS16220_CAPT_PEAK1 0x10
+/* Output, peak AIN2 level during capture */
+#define ADIS16220_CAPT_PEAK2 0x12
+/* Output, capture buffer for acceleration */
+#define ADIS16220_CAPT_BUFA 0x14
+/* Output, capture buffer for AIN1 */
+#define ADIS16220_CAPT_BUF1 0x16
+/* Output, capture buffer for AIN2 */
+#define ADIS16220_CAPT_BUF2 0x18
+/* Control, capture buffer address pointer */
+#define ADIS16220_CAPT_PNTR 0x1A
+/* Control, capture control register */
+#define ADIS16220_CAPT_CTRL 0x1C
+/* Control, capture period (automatic mode) */
+#define ADIS16220_CAPT_PRD 0x1E
+/* Control, Alarm A, acceleration peak threshold */
+#define ADIS16220_ALM_MAGA 0x20
+/* Control, Alarm 1, AIN1 peak threshold */
+#define ADIS16220_ALM_MAG1 0x22
+/* Control, Alarm 2, AIN2 peak threshold */
+#define ADIS16220_ALM_MAG2 0x24
+/* Control, Alarm S, peak threshold */
+#define ADIS16220_ALM_MAGS 0x26
+/* Control, alarm configuration register */
+#define ADIS16220_ALM_CTRL 0x28
+/* Control, general I/O configuration */
+#define ADIS16220_GPIO_CTRL 0x32
+/* Control, self-test control, AIN configuration */
+#define ADIS16220_MSC_CTRL 0x34
+/* Control, digital I/O configuration */
+#define ADIS16220_DIO_CTRL 0x36
+/* Control, filter configuration */
+#define ADIS16220_AVG_CNT 0x38
+/* Status, system status */
+#define ADIS16220_DIAG_STAT 0x3C
+/* Control, system commands */
+#define ADIS16220_GLOB_CMD 0x3E
+/* Status, self-test response */
+#define ADIS16220_ST_DELTA 0x40
+/* Lot Identification Code 1 */
+#define ADIS16220_LOT_ID1 0x52
+/* Lot Identification Code 2 */
+#define ADIS16220_LOT_ID2 0x54
+/* Product identifier; convert to decimal = 16220 */
+#define ADIS16220_PROD_ID 0x56
+/* Serial number */
+#define ADIS16220_SERIAL_NUM 0x58
+
+#define ADIS16220_CAPTURE_SIZE 2048
+
+/* MSC_CTRL */
+#define ADIS16220_MSC_CTRL_SELF_TEST_EN BIT(8)
+#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN1 BIT(1)
+#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN2 BIT(0)
+
+/* DIO_CTRL */
+#define ADIS16220_MSC_CTRL_DIO2_BUSY_IND (BIT(5) | BIT(4))
+#define ADIS16220_MSC_CTRL_DIO1_BUSY_IND (BIT(3) | BIT(2))
+#define ADIS16220_MSC_CTRL_DIO2_ACT_HIGH BIT(1)
+#define ADIS16220_MSC_CTRL_DIO1_ACT_HIGH BIT(0)
+
+/* DIAG_STAT */
+/* AIN2 sample > ALM_MAG2 */
+#define ADIS16220_DIAG_STAT_ALM_MAG2 BIT(14)
+/* AIN1 sample > ALM_MAG1 */
+#define ADIS16220_DIAG_STAT_ALM_MAG1 BIT(13)
+/* Acceleration sample > ALM_MAGA */
+#define ADIS16220_DIAG_STAT_ALM_MAGA BIT(12)
+/* Error condition programmed into ALM_MAGS[11:0] and ALM_CTRL[5:4] is true */
+#define ADIS16220_DIAG_STAT_ALM_MAGS BIT(11)
+/* |Peak value in AIN2 data capture| > ALM_MAG2 */
+#define ADIS16220_DIAG_STAT_PEAK_AIN2 BIT(10)
+/* |Peak value in AIN1 data capture| > ALM_MAG1 */
+#define ADIS16220_DIAG_STAT_PEAK_AIN1 BIT(9)
+/* |Peak value in acceleration data capture| > ALM_MAGA */
+#define ADIS16220_DIAG_STAT_PEAK_ACCEL BIT(8)
+/* Data ready, capture complete */
+#define ADIS16220_DIAG_STAT_DATA_RDY BIT(7)
+#define ADIS16220_DIAG_STAT_FLASH_CHK BIT(6)
+#define ADIS16220_DIAG_STAT_SELF_TEST BIT(5)
+/* Capture period violation/interruption */
+#define ADIS16220_DIAG_STAT_VIOLATION_BIT 4
+/* SPI communications failure */
+#define ADIS16220_DIAG_STAT_SPI_FAIL_BIT 3
+/* Flash update failure */
+#define ADIS16220_DIAG_STAT_FLASH_UPT_BIT 2
+/* Power supply above 3.625 V */
+#define ADIS16220_DIAG_STAT_POWER_HIGH_BIT 1
+/* Power supply below 3.15 V */
+#define ADIS16220_DIAG_STAT_POWER_LOW_BIT 0
+
+/* GLOB_CMD */
+#define ADIS16220_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16220_GLOB_CMD_SELF_TEST BIT(2)
+#define ADIS16220_GLOB_CMD_PWR_DOWN BIT(1)
+
+#define ADIS16220_MAX_TX 2048
+#define ADIS16220_MAX_RX 2048
+
+#define ADIS16220_SPI_BURST (u32)(1000 * 1000)
+#define ADIS16220_SPI_FAST (u32)(2000 * 1000)
+
+/**
+ * struct adis16220_state - device instance specific data
+ * @adis: adis device
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ * @buf_lock: mutex to protect tx and rx
+ **/
+struct adis16220_state {
+ struct adis adis;
+
+ struct mutex buf_lock;
+ u8 tx[ADIS16220_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADIS16220_MAX_RX];
+};
+
+#endif /* SPI_ADIS16220_H_ */
diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c
new file mode 100644
index 000000000..e46a91c69
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16220_core.c
@@ -0,0 +1,495 @@
+/*
+ * ADIS16220 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "adis16220.h"
+
+static ssize_t adis16220_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adis16220_state *st = iio_priv(indio_dev);
+ ssize_t ret;
+ u16 val;
+
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis_read_reg_16(&st->adis, this_attr->address, &val);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t adis16220_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct adis16220_state *st = iio_priv(indio_dev);
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = adis_write_reg_16(&st->adis, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int adis16220_capture(struct iio_dev *indio_dev)
+{
+ struct adis16220_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /* initiates a manual data capture */
+ ret = adis_write_reg_16(&st->adis, ADIS16220_GLOB_CMD, 0xBF08);
+ if (ret)
+ dev_err(&indio_dev->dev, "problem beginning capture");
+
+ usleep_range(10000, 11000); /* delay for capture to finish */
+
+ return ret;
+}
+
+static ssize_t adis16220_write_capture(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool val;
+ int ret;
+
+ ret = strtobool(buf, &val);
+ if (ret)
+ return ret;
+ if (!val)
+ return -EINVAL;
+ ret = adis16220_capture(indio_dev);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,
+ char *buf,
+ loff_t off,
+ size_t count,
+ int addr)
+{
+ struct adis16220_state *st = iio_priv(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,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .cs_change = 1,
+ .delay_usecs = 25,
+ },
+ };
+ int ret;
+ int i;
+
+ if (unlikely(!count))
+ return count;
+
+ if ((off >= ADIS16220_CAPTURE_SIZE) || (count & 1) || (off & 1))
+ return -EINVAL;
+
+ if (off + count > ADIS16220_CAPTURE_SIZE)
+ count = ADIS16220_CAPTURE_SIZE - off;
+
+ /* write the begin position of capture buffer */
+ ret = adis_write_reg_16(&st->adis,
+ ADIS16220_CAPT_PNTR,
+ off > 1);
+ if (ret)
+ return -EIO;
+
+ /* read count/2 values from capture buffer */
+ mutex_lock(&st->buf_lock);
+
+ for (i = 0; i < count; i += 2) {
+ st->tx[i] = ADIS_READ_REG(addr);
+ st->tx[i + 1] = 0;
+ }
+ xfers[1].len = count;
+
+ ret = spi_sync_transfer(st->adis.spi, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ mutex_unlock(&st->buf_lock);
+ return -EIO;
+ }
+
+ memcpy(buf, st->rx, count);
+
+ mutex_unlock(&st->buf_lock);
+ return count;
+}
+
+static ssize_t adis16220_accel_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
+
+ return adis16220_capture_buffer_read(indio_dev, buf,
+ off, count,
+ ADIS16220_CAPT_BUFA);
+}
+
+static struct bin_attribute accel_bin = {
+ .attr = {
+ .name = "accel_bin",
+ .mode = S_IRUGO,
+ },
+ .read = adis16220_accel_bin_read,
+ .size = ADIS16220_CAPTURE_SIZE,
+};
+
+static ssize_t adis16220_adc1_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
+
+ return adis16220_capture_buffer_read(indio_dev, buf,
+ off, count,
+ ADIS16220_CAPT_BUF1);
+}
+
+static struct bin_attribute adc1_bin = {
+ .attr = {
+ .name = "in0_bin",
+ .mode = S_IRUGO,
+ },
+ .read = adis16220_adc1_bin_read,
+ .size = ADIS16220_CAPTURE_SIZE,
+};
+
+static ssize_t adis16220_adc2_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
+
+ return adis16220_capture_buffer_read(indio_dev, buf,
+ off, count,
+ ADIS16220_CAPT_BUF2);
+}
+
+static struct bin_attribute adc2_bin = {
+ .attr = {
+ .name = "in1_bin",
+ .mode = S_IRUGO,
+ },
+ .read = adis16220_adc2_bin_read,
+ .size = ADIS16220_CAPTURE_SIZE,
+};
+
+#define IIO_DEV_ATTR_CAPTURE(_store) \
+ IIO_DEVICE_ATTR(capture, S_IWUSR, NULL, _store, 0)
+
+static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture);
+
+#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(capture_count, _mode, _show, _store, _addr)
+
+static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO,
+ adis16220_read_16bit,
+ adis16220_write_16bit,
+ ADIS16220_CAPT_PNTR);
+
+enum adis16220_channel {
+ in_supply, in_1, in_2, accel, temp
+};
+
+struct adis16220_address_spec {
+ u8 addr;
+ u8 bits;
+ bool sign;
+};
+
+/* Address / bits / signed */
+static const struct adis16220_address_spec adis16220_addresses[][3] = {
+ [in_supply] = { { ADIS16220_CAPT_SUPPLY, 12, 0 }, },
+ [in_1] = { { ADIS16220_CAPT_BUF1, 16, 1 },
+ { ADIS16220_AIN1_NULL, 16, 1 },
+ { ADIS16220_CAPT_PEAK1, 16, 1 }, },
+ [in_2] = { { ADIS16220_CAPT_BUF2, 16, 1 },
+ { ADIS16220_AIN2_NULL, 16, 1 },
+ { ADIS16220_CAPT_PEAK2, 16, 1 }, },
+ [accel] = { { ADIS16220_CAPT_BUFA, 16, 1 },
+ { ADIS16220_ACCL_NULL, 16, 1 },
+ { ADIS16220_CAPT_PEAKA, 16, 1 }, },
+ [temp] = { { ADIS16220_CAPT_TEMP, 12, 0 }, }
+};
+
+static int adis16220_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis16220_state *st = iio_priv(indio_dev);
+ const struct adis16220_address_spec *addr;
+ int ret = -EINVAL;
+ int addrind = 0;
+ u16 uval;
+ s16 sval;
+ u8 bits;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ addrind = 0;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ }
+ addrind = 1;
+ break;
+ case IIO_CHAN_INFO_PEAK:
+ addrind = 2;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = -470; /* -0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val2 = IIO_G_TO_M_S_2(19073); /* 19.073 g */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220700; /* 1.2207 mV */
+ } else {
+ /* Should really be dependent on VDD */
+ *val2 = 305180; /* 305.18 uV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+ addr = &adis16220_addresses[chan->address][addrind];
+ if (addr->sign) {
+ ret = adis_read_reg_16(&st->adis, addr->addr, &sval);
+ if (ret)
+ return ret;
+ bits = addr->bits;
+ sval &= (1 << bits) - 1;
+ sval = (s16)(sval << (16 - bits)) >> (16 - bits);
+ *val = sval;
+ return IIO_VAL_INT;
+ }
+ ret = adis_read_reg_16(&st->adis, addr->addr, &uval);
+ if (ret)
+ return ret;
+ bits = addr->bits;
+ uval &= (1 << bits) - 1;
+ *val = uval;
+ return IIO_VAL_INT;
+}
+
+static const struct iio_chan_spec adis16220_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "supply",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = in_supply,
+ }, {
+ .type = IIO_ACCEL,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_PEAK),
+ .address = accel,
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = temp,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = in_1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = in_2,
+ }
+};
+
+static struct attribute *adis16220_attributes[] = {
+ &iio_dev_attr_capture.dev_attr.attr,
+ &iio_dev_attr_capture_count.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16220_attribute_group = {
+ .attrs = adis16220_attributes,
+};
+
+static const struct iio_info adis16220_info = {
+ .attrs = &adis16220_attribute_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &adis16220_read_raw,
+};
+
+static const char * const adis16220_status_error_msgs[] = {
+ [ADIS16220_DIAG_STAT_VIOLATION_BIT] = "Capture period violation/interruption",
+ [ADIS16220_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16220_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16220_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16220_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16220_data = {
+ .read_delay = 35,
+ .write_delay = 35,
+ .msc_ctrl_reg = ADIS16220_MSC_CTRL,
+ .glob_cmd_reg = ADIS16220_GLOB_CMD,
+ .diag_stat_reg = ADIS16220_DIAG_STAT,
+
+ .self_test_mask = ADIS16220_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16220_STARTUP_DELAY,
+
+ .status_error_msgs = adis16220_status_error_msgs,
+ .status_error_mask = BIT(ADIS16220_DIAG_STAT_VIOLATION_BIT) |
+ BIT(ADIS16220_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16220_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16220_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16220_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16220_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis16220_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16220_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = adis16220_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16220_channels);
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &accel_bin);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc1_bin);
+ if (ret)
+ goto error_rm_accel_bin;
+
+ ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc2_bin);
+ if (ret)
+ goto error_rm_adc1_bin;
+
+ ret = adis_init(&st->adis, indio_dev, spi, &adis16220_data);
+ if (ret)
+ goto error_rm_adc2_bin;
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(&st->adis);
+ if (ret)
+ goto error_rm_adc2_bin;
+ return 0;
+
+error_rm_adc2_bin:
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin);
+error_rm_adc1_bin:
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin);
+error_rm_accel_bin:
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin);
+ return ret;
+}
+
+static int adis16220_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin);
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin);
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin);
+
+ return 0;
+}
+
+static struct spi_driver adis16220_driver = {
+ .driver = {
+ .name = "adis16220",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16220_probe,
+ .remove = adis16220_remove,
+};
+module_spi_driver(adis16220_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16220 Digital Vibration Sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16220");
diff --git a/drivers/staging/iio/accel/adis16240.h b/drivers/staging/iio/accel/adis16240.h
new file mode 100644
index 000000000..66b5ad2f4
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240.h
@@ -0,0 +1,129 @@
+#ifndef SPI_ADIS16240_H_
+#define SPI_ADIS16240_H_
+
+#define ADIS16240_STARTUP_DELAY 220 /* ms */
+
+/* Flash memory write count */
+#define ADIS16240_FLASH_CNT 0x00
+/* Output, power supply */
+#define ADIS16240_SUPPLY_OUT 0x02
+/* Output, x-axis accelerometer */
+#define ADIS16240_XACCL_OUT 0x04
+/* Output, y-axis accelerometer */
+#define ADIS16240_YACCL_OUT 0x06
+/* Output, z-axis accelerometer */
+#define ADIS16240_ZACCL_OUT 0x08
+/* Output, auxiliary ADC input */
+#define ADIS16240_AUX_ADC 0x0A
+/* Output, temperature */
+#define ADIS16240_TEMP_OUT 0x0C
+/* Output, x-axis acceleration peak */
+#define ADIS16240_XPEAK_OUT 0x0E
+/* Output, y-axis acceleration peak */
+#define ADIS16240_YPEAK_OUT 0x10
+/* Output, z-axis acceleration peak */
+#define ADIS16240_ZPEAK_OUT 0x12
+/* Output, sum-of-squares acceleration peak */
+#define ADIS16240_XYZPEAK_OUT 0x14
+/* Output, Capture Buffer 1, X and Y acceleration */
+#define ADIS16240_CAPT_BUF1 0x16
+/* Output, Capture Buffer 2, Z acceleration */
+#define ADIS16240_CAPT_BUF2 0x18
+/* Diagnostic, error flags */
+#define ADIS16240_DIAG_STAT 0x1A
+/* Diagnostic, event counter */
+#define ADIS16240_EVNT_CNTR 0x1C
+/* Diagnostic, check sum value from firmware test */
+#define ADIS16240_CHK_SUM 0x1E
+/* Calibration, x-axis acceleration offset adjustment */
+#define ADIS16240_XACCL_OFF 0x20
+/* Calibration, y-axis acceleration offset adjustment */
+#define ADIS16240_YACCL_OFF 0x22
+/* Calibration, z-axis acceleration offset adjustment */
+#define ADIS16240_ZACCL_OFF 0x24
+/* Clock, hour and minute */
+#define ADIS16240_CLK_TIME 0x2E
+/* Clock, month and day */
+#define ADIS16240_CLK_DATE 0x30
+/* Clock, year */
+#define ADIS16240_CLK_YEAR 0x32
+/* Wake-up setting, hour and minute */
+#define ADIS16240_WAKE_TIME 0x34
+/* Wake-up setting, month and day */
+#define ADIS16240_WAKE_DATE 0x36
+/* Alarm 1 amplitude threshold */
+#define ADIS16240_ALM_MAG1 0x38
+/* Alarm 2 amplitude threshold */
+#define ADIS16240_ALM_MAG2 0x3A
+/* Alarm control */
+#define ADIS16240_ALM_CTRL 0x3C
+/* Capture, external trigger control */
+#define ADIS16240_XTRIG_CTRL 0x3E
+/* Capture, address pointer */
+#define ADIS16240_CAPT_PNTR 0x40
+/* Capture, configuration and control */
+#define ADIS16240_CAPT_CTRL 0x42
+/* General-purpose digital input/output control */
+#define ADIS16240_GPIO_CTRL 0x44
+/* Miscellaneous control */
+#define ADIS16240_MSC_CTRL 0x46
+/* Internal sample period (rate) control */
+#define ADIS16240_SMPL_PRD 0x48
+/* System command */
+#define ADIS16240_GLOB_CMD 0x4A
+
+/* MSC_CTRL */
+/* Enables sum-of-squares output (XYZPEAK_OUT) */
+#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN BIT(15)
+/* Enables peak tracking output (XPEAK_OUT, YPEAK_OUT, and ZPEAK_OUT) */
+#define ADIS16240_MSC_CTRL_X_Y_ZPEAK_OUT_EN BIT(14)
+/* Self-test enable: 1 = apply electrostatic force, 0 = disabled */
+#define ADIS16240_MSC_CTRL_SELF_TEST_EN BIT(8)
+/* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16240_MSC_CTRL_DATA_RDY_EN BIT(2)
+/* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16240_MSC_CTRL_ACTIVE_HIGH BIT(1)
+/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+#define ADIS16240_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
+
+/* DIAG_STAT */
+/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16240_DIAG_STAT_ALARM2 BIT(9)
+/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16240_DIAG_STAT_ALARM1 BIT(8)
+/* Capture buffer full: 1 = capture buffer is full */
+#define ADIS16240_DIAG_STAT_CPT_BUF_FUL BIT(7)
+/* Flash test, checksum flag: 1 = mismatch, 0 = match */
+#define ADIS16240_DIAG_STAT_CHKSUM BIT(6)
+/* Power-on, self-test flag: 1 = failure, 0 = pass */
+#define ADIS16240_DIAG_STAT_PWRON_FAIL_BIT 5
+/* Power-on self-test: 1 = in-progress, 0 = complete */
+#define ADIS16240_DIAG_STAT_PWRON_BUSY BIT(4)
+/* SPI communications failure */
+#define ADIS16240_DIAG_STAT_SPI_FAIL_BIT 3
+/* Flash update failure */
+#define ADIS16240_DIAG_STAT_FLASH_UPT_BIT 2
+/* Power supply above 3.625 V */
+#define ADIS16240_DIAG_STAT_POWER_HIGH_BIT 1
+ /* Power supply below 3.15 V */
+#define ADIS16240_DIAG_STAT_POWER_LOW_BIT 0
+
+/* GLOB_CMD */
+#define ADIS16240_GLOB_CMD_RESUME BIT(8)
+#define ADIS16240_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16240_GLOB_CMD_STANDBY BIT(2)
+
+#define ADIS16240_ERROR_ACTIVE BIT(14)
+
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+#define ADIS16240_SCAN_ACC_X 0
+#define ADIS16240_SCAN_ACC_Y 1
+#define ADIS16240_SCAN_ACC_Z 2
+#define ADIS16240_SCAN_SUPPLY 3
+#define ADIS16240_SCAN_AUX_ADC 4
+#define ADIS16240_SCAN_TEMP 5
+
+#endif /* SPI_ADIS16240_H_ */
diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c
new file mode 100644
index 000000000..cb074e864
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240_core.c
@@ -0,0 +1,301 @@
+/*
+ * 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/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16240.h"
+
+static ssize_t adis16240_spi_read_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ unsigned bits)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ s16 val = 0;
+ unsigned shift = 16 - bits;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = adis_read_reg_16(st,
+ this_attr->address, (u16 *)&val);
+ if (ret)
+ return ret;
+
+ if (val & ADIS16240_ERROR_ACTIVE)
+ adis_check_status(st);
+
+ val = (s16)(val << shift) >> shift;
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t adis16240_read_12bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+
+ /* 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 IIO_DEVICE_ATTR(in_accel_xyz_squared_peak_raw, S_IRUGO,
+ adis16240_read_12bit_signed, NULL,
+ ADIS16240_XYZPEAK_OUT);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("4096");
+
+static const u8 adis16240_addresses[][2] = {
+ [ADIS16240_SCAN_ACC_X] = { ADIS16240_XACCL_OFF, ADIS16240_XPEAK_OUT },
+ [ADIS16240_SCAN_ACC_Y] = { ADIS16240_YACCL_OFF, ADIS16240_YPEAK_OUT },
+ [ADIS16240_SCAN_ACC_Z] = { ADIS16240_ZACCL_OFF, ADIS16240_ZPEAK_OUT },
+};
+
+static int adis16240_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16240_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 4;
+ *val2 = 880000; /* 4.88 mV */
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+ case IIO_TEMP:
+ *val = 244; /* 0.244 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_PEAK_SCALE:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / 244 - 0x133; /* 25 C = 0x133 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ bits = 10;
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16240_addresses[chan->scan_index][0];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PEAK:
+ bits = 10;
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16240_addresses[chan->scan_index][1];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adis16240_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits = 10;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16240_addresses[chan->scan_index][0];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16240_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 0, 10),
+ ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 0, 10),
+ ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 10),
+ ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 10),
+ ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 10),
+ ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 0, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(6)
+};
+
+static struct attribute *adis16240_attributes[] = {
+ &iio_dev_attr_in_accel_xyz_squared_peak_raw.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16240_attribute_group = {
+ .attrs = adis16240_attributes,
+};
+
+static const struct iio_info adis16240_info = {
+ .attrs = &adis16240_attribute_group,
+ .read_raw = &adis16240_read_raw,
+ .write_raw = &adis16240_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16240_status_error_msgs[] = {
+ [ADIS16240_DIAG_STAT_PWRON_FAIL_BIT] = "Power on, self-test failed",
+ [ADIS16240_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16240_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16240_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16240_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.225V",
+};
+
+static const struct adis_data adis16240_data = {
+ .write_delay = 35,
+ .read_delay = 35,
+ .msc_ctrl_reg = ADIS16240_MSC_CTRL,
+ .glob_cmd_reg = ADIS16240_GLOB_CMD,
+ .diag_stat_reg = ADIS16240_DIAG_STAT,
+
+ .self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16240_STARTUP_DELAY,
+
+ .status_error_msgs = adis16240_status_error_msgs,
+ .status_error_mask = BIT(ADIS16240_DIAG_STAT_PWRON_FAIL_BIT) |
+ BIT(ADIS16240_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16240_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16240_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16240_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16240_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16240_info;
+ indio_dev->channels = adis16240_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16240_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16240_data);
+ if (ret)
+ return ret;
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16240_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16240_driver = {
+ .driver = {
+ .name = "adis16240",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16240_probe,
+ .remove = adis16240_remove,
+};
+module_spi_driver(adis16240_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16240");
diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
new file mode 100644
index 000000000..3f24c629b
--- /dev/null
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -0,0 +1,212 @@
+/*
+ * LISL02DQ.h -- support STMicroelectronics LISD02DQ
+ * 3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * Loosely based upon tle62x0.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SPI_LIS3L02DQ_H_
+#define SPI_LIS3L02DQ_H_
+#define LIS3L02DQ_READ_REG(a) ((a) | 0x80)
+#define LIS3L02DQ_WRITE_REG(a) a
+
+/* Calibration parameters */
+#define LIS3L02DQ_REG_OFFSET_X_ADDR 0x16
+#define LIS3L02DQ_REG_OFFSET_Y_ADDR 0x17
+#define LIS3L02DQ_REG_OFFSET_Z_ADDR 0x18
+
+#define LIS3L02DQ_REG_GAIN_X_ADDR 0x19
+#define LIS3L02DQ_REG_GAIN_Y_ADDR 0x1A
+#define LIS3L02DQ_REG_GAIN_Z_ADDR 0x1B
+
+/* Control Register (1 of 2) */
+#define LIS3L02DQ_REG_CTRL_1_ADDR 0x20
+/* Power ctrl - either bit set corresponds to on*/
+#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0
+
+/* Decimation Factor */
+#define LIS3L02DQ_DEC_MASK 0x30
+#define LIS3L02DQ_REG_CTRL_1_DF_128 0x00
+#define LIS3L02DQ_REG_CTRL_1_DF_64 0x10
+#define LIS3L02DQ_REG_CTRL_1_DF_32 0x20
+#define LIS3L02DQ_REG_CTRL_1_DF_8 (0x10 | 0x20)
+
+/* Self Test Enable */
+#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON 0x08
+
+/* Axes enable ctrls */
+#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE 0x04
+#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE 0x02
+#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE 0x01
+
+/* Control Register (2 of 2) */
+#define LIS3L02DQ_REG_CTRL_2_ADDR 0x21
+
+/* Block Data Update only after MSB and LSB read */
+#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE 0x40
+
+/* Set to big endian output */
+#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN 0x20
+
+/* Reboot memory content */
+#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY 0x10
+
+/* Interrupt Enable - applies data ready to the RDY pad */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT 0x08
+
+/* Enable Data Ready Generation - relationship with previous unclear in docs */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04
+
+/* SPI 3 wire mode */
+#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE 0x02
+
+/* Data alignment, default is 12 bit right justified
+ * - option for 16 bit left justified */
+#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED 0x01
+
+/* Interrupt related stuff */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR 0x23
+
+/* Switch from or combination of conditions to and */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND 0x80
+
+/* Latch interrupt request,
+ * if on ack must be given by reading the ack register */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC 0x40
+
+/* Z Interrupt on High (above threshold) */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH 0x20
+/* Z Interrupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW 0x10
+/* Y Interrupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH 0x08
+/* Y Interrupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW 0x04
+/* X Interrupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH 0x02
+/* X Interrupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01
+
+/* Register that gives description of what caused interrupt
+ * - latched if set in CFG_ADDRES */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDR 0x24
+/* top bit ignored */
+/* Interrupt Active */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED 0x40
+/* Interupts that have been triggered */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH 0x20
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW 0x10
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH 0x08
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW 0x04
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH 0x02
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW 0x01
+
+#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDR 0x25
+
+/* Status register */
+#define LIS3L02DQ_REG_STATUS_ADDR 0x27
+/* XYZ axis data overrun - first is all overrun? */
+#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN 0x80
+#define LIS3L02DQ_REG_STATUS_Z_OVERRUN 0x40
+#define LIS3L02DQ_REG_STATUS_Y_OVERRUN 0x20
+#define LIS3L02DQ_REG_STATUS_X_OVERRUN 0x10
+/* XYZ new data available - first is all 3 available? */
+#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
+#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA 0x04
+#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA 0x02
+#define LIS3L02DQ_REG_STATUS_X_NEW_DATA 0x01
+
+/* The accelerometer readings - low and high bytes.
+ * Form of high byte dependent on justification set in ctrl reg */
+#define LIS3L02DQ_REG_OUT_X_L_ADDR 0x28
+#define LIS3L02DQ_REG_OUT_X_H_ADDR 0x29
+#define LIS3L02DQ_REG_OUT_Y_L_ADDR 0x2A
+#define LIS3L02DQ_REG_OUT_Y_H_ADDR 0x2B
+#define LIS3L02DQ_REG_OUT_Z_L_ADDR 0x2C
+#define LIS3L02DQ_REG_OUT_Z_H_ADDR 0x2D
+
+/* Threshold values for all axes and both above and below thresholds
+ * - i.e. there is only one value */
+#define LIS3L02DQ_REG_THS_L_ADDR 0x2E
+#define LIS3L02DQ_REG_THS_H_ADDR 0x2F
+
+#define LIS3L02DQ_DEFAULT_CTRL1 (LIS3L02DQ_REG_CTRL_1_PD_ON \
+ | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_DF_128)
+
+#define LIS3L02DQ_DEFAULT_CTRL2 0
+
+#define LIS3L02DQ_MAX_TX 12
+#define LIS3L02DQ_MAX_RX 12
+/**
+ * struct lis3l02dq_state - device instance specific data
+ * @us: actual spi_device
+ * @trig: data ready trigger registered with iio
+ * @buf_lock: mutex to protect tx and rx
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ **/
+struct lis3l02dq_state {
+ struct spi_device *us;
+ struct iio_trigger *trig;
+ struct mutex buf_lock;
+ int gpio;
+ bool trigger_on;
+
+ u8 tx[LIS3L02DQ_MAX_RX] ____cacheline_aligned;
+ u8 rx[LIS3L02DQ_MAX_RX] ____cacheline_aligned;
+};
+
+int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 *val);
+
+int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 val);
+
+int lis3l02dq_disable_all_events(struct iio_dev *indio_dev);
+
+#ifdef CONFIG_IIO_BUFFER
+/* At the moment triggers are only used for buffer
+ * filling. This may change!
+ */
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);
+
+int lis3l02dq_configure_buffer(struct iio_dev *indio_dev);
+void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev);
+
+irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private);
+#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll
+
+#else /* CONFIG_IIO_BUFFER */
+#define lis3l02dq_th lis3l02dq_nobuffer
+
+static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_IIO_BUFFER */
+#endif /* SPI_LIS3L02DQ_H_ */
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
new file mode 100644
index 000000000..ebcab56c8
--- /dev/null
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -0,0 +1,813 @@
+/*
+ * lis3l02dq.c support STMicroelectronics LISD02DQ
+ * 3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Settings:
+ * 16 bit left justified mode used.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+
+#include "lis3l02dq.h"
+
+/* At the moment the spi framework doesn't allow global setting of cs_change.
+ * It's in the likely to be added comment at the top of spi.h.
+ * This means that use cannot be made of spi_write etc.
+ */
+/* direct copy of the irq_default_primary_handler */
+#ifndef CONFIG_IIO_BUFFER
+static irqreturn_t lis3l02dq_nobuffer(int irq, void *private)
+{
+ return IRQ_WAKE_THREAD;
+}
+#endif
+
+/**
+ * lis3l02dq_spi_read_reg_8() - read single byte from a single register
+ * @indio_dev: iio_dev for this actual device
+ * @reg_address: the address of the register to be read
+ * @val: pass back the resulting value
+ **/
+int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address, u8 *val)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_READ_REG(reg_address);
+ st->tx[1] = 0;
+
+ ret = spi_sync_transfer(st->us, &xfer, 1);
+ *val = st->rx[1];
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * lis3l02dq_spi_write_reg_8() - write single byte to a register
+ * @indio_dev: iio_dev for this device
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address);
+ st->tx[1] = val;
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers
+ * @indio_dev: iio_dev for this device
+ * @lower_reg_address: the address of the lower of the two registers.
+ * Second register is assumed to have address one greater.
+ * @value: value to be written
+ **/
+static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev,
+ u8 lower_reg_address,
+ s16 value)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = { {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx + 2,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address);
+ st->tx[1] = value & 0xFF;
+ st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1);
+ st->tx[3] = (value >> 8) & 0xFF;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev,
+ u8 lower_reg_address,
+ int *val)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int ret;
+ s16 tempval;
+ struct spi_transfer xfers[] = { {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx + 2,
+ .rx_buf = st->rx + 2,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address);
+ st->tx[1] = 0;
+ st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1);
+ st->tx[3] = 0;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register");
+ goto error_ret;
+ }
+ tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8);
+
+ *val = tempval;
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+enum lis3l02dq_rm_ind {
+ LIS3L02DQ_ACCEL,
+ LIS3L02DQ_GAIN,
+ LIS3L02DQ_BIAS,
+};
+
+static u8 lis3l02dq_axis_map[3][3] = {
+ [LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR,
+ LIS3L02DQ_REG_OUT_Y_L_ADDR,
+ LIS3L02DQ_REG_OUT_Z_L_ADDR },
+ [LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR,
+ LIS3L02DQ_REG_GAIN_Y_ADDR,
+ LIS3L02DQ_REG_GAIN_Z_ADDR },
+ [LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR,
+ LIS3L02DQ_REG_OFFSET_Y_ADDR,
+ LIS3L02DQ_REG_OFFSET_Z_ADDR }
+};
+
+static int lis3l02dq_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret;
+
+ ret = lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+}
+
+static int lis3l02dq_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ u16 value = val;
+
+ return lis3l02dq_spi_write_reg_s16(indio_dev,
+ LIS3L02DQ_REG_THS_L_ADDR,
+ value);
+}
+
+static int lis3l02dq_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int ret = -EINVAL, reg;
+ u8 uval;
+ s8 sval;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val > 255 || val < -256)
+ return -EINVAL;
+ sval = val;
+ reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
+ ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, sval);
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val & ~0xFF)
+ return -EINVAL;
+ uval = val;
+ reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
+ ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, uval);
+ break;
+ }
+ return ret;
+}
+
+static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ u8 utemp;
+ s8 stemp;
+ ssize_t ret = 0;
+ u8 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = -EBUSY;
+ } else {
+ reg = lis3l02dq_axis_map
+ [LIS3L02DQ_ACCEL][chan->address];
+ ret = lis3l02dq_read_reg_s16(indio_dev, reg, val);
+ }
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ goto error_ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 9580;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
+ ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp);
+ if (ret)
+ goto error_ret;
+ /* to match with what previous code does */
+ *val = utemp;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
+ ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp);
+ /* to match with what previous code does */
+ *val = stemp;
+ return IIO_VAL_INT;
+ }
+error_ret:
+ return ret;
+}
+
+static ssize_t lis3l02dq_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret, len = 0;
+ s8 t;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ (u8 *)&t);
+ if (ret)
+ return ret;
+ t &= LIS3L02DQ_DEC_MASK;
+ switch (t) {
+ case LIS3L02DQ_REG_CTRL_1_DF_128:
+ len = sprintf(buf, "280\n");
+ break;
+ case LIS3L02DQ_REG_CTRL_1_DF_64:
+ len = sprintf(buf, "560\n");
+ break;
+ case LIS3L02DQ_REG_CTRL_1_DF_32:
+ len = sprintf(buf, "1120\n");
+ break;
+ case LIS3L02DQ_REG_CTRL_1_DF_8:
+ len = sprintf(buf, "4480\n");
+ break;
+ }
+ return len;
+}
+
+static ssize_t lis3l02dq_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ unsigned long val;
+ int ret;
+ u8 t;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret_mutex;
+ /* Wipe the bits clean */
+ t &= ~LIS3L02DQ_DEC_MASK;
+ switch (val) {
+ case 280:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_128;
+ break;
+ case 560:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_64;
+ break;
+ case 1120:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_32;
+ break;
+ case 4480:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret_mutex;
+ }
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ t);
+
+error_ret_mutex:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static int lis3l02dq_initial_setup(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 val, valtest;
+
+ st->us->mode = SPI_MODE_3;
+
+ spi_setup(st->us);
+
+ val = LIS3L02DQ_DEFAULT_CTRL1;
+ /* Write suitable defaults to ctrl1 */
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 1");
+ goto err_ret;
+ }
+ /* Repeat as sometimes doesn't work first time? */
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 1");
+ goto err_ret;
+ }
+
+ /* Read back to check this has worked acts as loose test of correct
+ * chip */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &valtest);
+ if (ret || (valtest != val)) {
+ dev_err(&indio_dev->dev,
+ "device not playing ball %d %d\n", valtest, val);
+ ret = -EINVAL;
+ goto err_ret;
+ }
+
+ val = LIS3L02DQ_DEFAULT_CTRL2;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 2");
+ goto err_ret;
+ }
+
+ val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ val);
+ if (ret)
+ dev_err(&st->us->dev, "problem with interrupt cfg register");
+err_ret:
+
+ return ret;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_frequency,
+ lis3l02dq_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480");
+
+static irqreturn_t lis3l02dq_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ u8 t;
+
+ s64 timestamp = iio_get_time_ns();
+
+ lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+ &t);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ /* Ack and allow for new interrupts */
+ lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
+ &t);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_event_spec lis3l02dq_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ }
+};
+
+#define LIS3L02DQ_CHAN(index, mod) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .event_spec = lis3l02dq_event, \
+ .num_event_specs = ARRAY_SIZE(lis3l02dq_event), \
+ }
+
+static const struct iio_chan_spec lis3l02dq_channels[] = {
+ LIS3L02DQ_CHAN(0, IIO_MOD_X),
+ LIS3L02DQ_CHAN(1, IIO_MOD_Y),
+ LIS3L02DQ_CHAN(2, IIO_MOD_Z),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static int lis3l02dq_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ u8 val;
+ int ret;
+ u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING)));
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & mask);
+}
+
+int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
+{
+ int ret;
+ u8 control, val;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
+
+ control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ control);
+ if (ret)
+ goto error_ret;
+ /* Also for consistency clear the mask */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret)
+ goto error_ret;
+ val &= ~0x3f;
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ val);
+ if (ret)
+ goto error_ret;
+
+ ret = control;
+error_ret:
+ return ret;
+}
+
+static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ int ret = 0;
+ u8 val, control;
+ u8 currentlyset;
+ bool changed = false;
+ u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING)));
+
+ mutex_lock(&indio_dev->mlock);
+ /* read current control */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
+ if (ret)
+ goto error_ret;
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret < 0)
+ goto error_ret;
+ currentlyset = val & mask;
+
+ if (!currentlyset && state) {
+ changed = true;
+ val |= mask;
+ } else if (currentlyset && !state) {
+ changed = true;
+ val &= ~mask;
+ }
+
+ if (changed) {
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ val);
+ if (ret)
+ goto error_ret;
+ control = val & 0x3f ?
+ (control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
+ (control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ control);
+ if (ret)
+ goto error_ret;
+ }
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static struct attribute *lis3l02dq_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lis3l02dq_attribute_group = {
+ .attrs = lis3l02dq_attributes,
+};
+
+static const struct iio_info lis3l02dq_info = {
+ .read_raw = &lis3l02dq_read_raw,
+ .write_raw = &lis3l02dq_write_raw,
+ .read_event_value = &lis3l02dq_read_thresh,
+ .write_event_value = &lis3l02dq_write_thresh,
+ .write_event_config = &lis3l02dq_write_event_config,
+ .read_event_config = &lis3l02dq_read_event_config,
+ .driver_module = THIS_MODULE,
+ .attrs = &lis3l02dq_attribute_group,
+};
+
+static int lis3l02dq_probe(struct spi_device *spi)
+{
+ int ret;
+ struct lis3l02dq_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ st->us = spi;
+ st->gpio = of_get_gpio(spi->dev.of_node, 0);
+ mutex_init(&st->buf_lock);
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &lis3l02dq_info;
+ indio_dev->channels = lis3l02dq_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels);
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = lis3l02dq_configure_buffer(indio_dev);
+ if (ret)
+ return ret;
+
+ if (spi->irq) {
+ ret = request_threaded_irq(st->us->irq,
+ &lis3l02dq_th,
+ &lis3l02dq_event_handler,
+ IRQF_TRIGGER_RISING,
+ "lis3l02dq",
+ indio_dev);
+ if (ret)
+ goto error_unreg_buffer_funcs;
+
+ ret = lis3l02dq_probe_trigger(indio_dev);
+ if (ret)
+ goto error_free_interrupt;
+ }
+
+ /* Get the device into a sane initial state */
+ ret = lis3l02dq_initial_setup(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ return 0;
+
+error_remove_trigger:
+ if (spi->irq)
+ lis3l02dq_remove_trigger(indio_dev);
+error_free_interrupt:
+ if (spi->irq)
+ free_irq(st->us->irq, indio_dev);
+error_unreg_buffer_funcs:
+ lis3l02dq_unconfigure_buffer(indio_dev);
+ return ret;
+}
+
+/* Power down the device */
+static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ u8 val = 0;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with turning device off: ctrl1");
+ goto err_ret;
+ }
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ val);
+ if (ret)
+ dev_err(&st->us->dev, "problem with turning device off: ctrl2");
+err_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int lis3l02dq_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ lis3l02dq_disable_all_events(indio_dev);
+ lis3l02dq_stop_device(indio_dev);
+
+ if (spi->irq)
+ free_irq(st->us->irq, indio_dev);
+
+ lis3l02dq_remove_trigger(indio_dev);
+ lis3l02dq_unconfigure_buffer(indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver lis3l02dq_driver = {
+ .driver = {
+ .name = "lis3l02dq",
+ .owner = THIS_MODULE,
+ },
+ .probe = lis3l02dq_probe,
+ .remove = lis3l02dq_remove,
+};
+module_spi_driver(lis3l02dq_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:lis3l02dq");
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
new file mode 100644
index 000000000..b892f2cf5
--- /dev/null
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -0,0 +1,426 @@
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include "lis3l02dq.h"
+
+/**
+ * combine_8_to_16() utility function to munge two u8s into u16
+ **/
+static inline u16 combine_8_to_16(u8 lower, u8 upper)
+{
+ u16 _lower = lower;
+ u16 _upper = upper;
+
+ return _lower | (_upper << 8);
+}
+
+/**
+ * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ if (st->trigger_on) {
+ iio_trigger_poll(st->trig);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static const u8 read_all_tx_array[] = {
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDR), 0,
+};
+
+/**
+ * lis3l02dq_read_all() Reads all channels currently selected
+ * @indio_dev: IIO device state
+ * @rx_array: (dma capable) receive array, must be at least
+ * 4*number of channels
+ **/
+static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ struct spi_transfer *xfers;
+ struct spi_message msg;
+ int ret, i, j = 0;
+
+ xfers = kcalloc(bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength) * 2,
+ sizeof(*xfers), GFP_KERNEL);
+ if (!xfers)
+ return -ENOMEM;
+
+ mutex_lock(&st->buf_lock);
+
+ for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++)
+ if (test_bit(i, indio_dev->active_scan_mask)) {
+ /* lower byte */
+ xfers[j].tx_buf = st->tx + 2*j;
+ st->tx[2*j] = read_all_tx_array[i*4];
+ st->tx[2*j + 1] = 0;
+ if (rx_array)
+ xfers[j].rx_buf = rx_array + j*2;
+ xfers[j].bits_per_word = 8;
+ xfers[j].len = 2;
+ xfers[j].cs_change = 1;
+ j++;
+
+ /* upper byte */
+ xfers[j].tx_buf = st->tx + 2*j;
+ st->tx[2*j] = read_all_tx_array[i*4 + 2];
+ st->tx[2*j + 1] = 0;
+ if (rx_array)
+ xfers[j].rx_buf = rx_array + j*2;
+ xfers[j].bits_per_word = 8;
+ xfers[j].len = 2;
+ xfers[j].cs_change = 1;
+ j++;
+ }
+
+ /* After these are transmitted, the rx_buff should have
+ * values in alternate bytes
+ */
+ spi_message_init(&msg);
+ for (j = 0; j < bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength) * 2; j++)
+ spi_message_add_tail(&xfers[j], &msg);
+
+ ret = spi_sync(st->us, &msg);
+ mutex_unlock(&st->buf_lock);
+ kfree(xfers);
+
+ return ret;
+}
+
+static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev,
+ u8 *buf)
+{
+ int ret, i;
+ u8 *rx_array;
+ s16 *data = (s16 *)buf;
+ int scan_count = bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+
+ rx_array = kcalloc(4, scan_count, GFP_KERNEL);
+ if (!rx_array)
+ return -ENOMEM;
+ ret = lis3l02dq_read_all(indio_dev, rx_array);
+ if (ret < 0) {
+ kfree(rx_array);
+ return ret;
+ }
+ for (i = 0; i < scan_count; i++)
+ data[i] = combine_8_to_16(rx_array[i*4+1],
+ rx_array[i*4+3]);
+ kfree(rx_array);
+
+ return i*sizeof(data[0]);
+}
+
+static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ int len = 0;
+ char *data;
+
+ data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!data)
+ goto done;
+
+ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+ len = lis3l02dq_get_buffer_element(indio_dev, data);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);
+
+ kfree(data);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+/* Caller responsible for locking as necessary. */
+static int
+__lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state)
+{
+ int ret;
+ u8 valold;
+ bool currentlyset;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ /* Get the current event mask register */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &valold);
+ if (ret)
+ goto error_ret;
+ /* Find out if data ready is already on */
+ currentlyset
+ = valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+ /* Disable requested */
+ if (!state && currentlyset) {
+ /* Disable the data ready signal */
+ valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+ /* The double write is to overcome a hardware bug? */
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ valold);
+ if (ret)
+ goto error_ret;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ valold);
+ if (ret)
+ goto error_ret;
+ st->trigger_on = false;
+ /* Enable requested */
+ } else if (state && !currentlyset) {
+ /* If not set, enable requested
+ * first disable all events */
+ ret = lis3l02dq_disable_all_events(indio_dev);
+ if (ret < 0)
+ goto error_ret;
+
+ valold = ret |
+ LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+ st->trigger_on = true;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ valold);
+ if (ret)
+ goto error_ret;
+ }
+
+ return 0;
+error_ret:
+ return ret;
+}
+
+/**
+ * lis3l02dq_data_rdy_trigger_set_state() set datardy interrupt state
+ *
+ * If disabling the interrupt also does a final read to ensure it is clear.
+ * This is only important in some cases where the scan enable elements are
+ * switched before the buffer is reenabled.
+ **/
+static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ int ret = 0;
+ u8 t;
+
+ __lis3l02dq_write_data_ready_config(indio_dev, state);
+ if (!state) {
+ /*
+ * A possible quirk with the handler is currently worked around
+ * by ensuring outstanding read events are cleared.
+ */
+ ret = lis3l02dq_read_all(indio_dev, NULL);
+ }
+ lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+ &t);
+ return ret;
+}
+
+/**
+ * lis3l02dq_trig_try_reen() try reenabling irq for data rdy trigger
+ * @trig: the datardy trigger
+ */
+static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int i;
+
+ /* If gpio still high (or high again)
+ * In theory possible we will need to do this several times */
+ for (i = 0; i < 5; i++)
+ if (gpio_get_value(st->gpio))
+ lis3l02dq_read_all(indio_dev, NULL);
+ else
+ break;
+ if (i == 5)
+ pr_info("Failed to clear the interrupt for lis3l02dq\n");
+
+ /* irq reenabled so success! */
+ return 0;
+}
+
+static const struct iio_trigger_ops lis3l02dq_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state,
+ .try_reenable = &lis3l02dq_trig_try_reen,
+};
+
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("lis3l02dq-dev%d", indio_dev->id);
+ if (!st->trig) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ st->trig->dev.parent = &st->us->dev;
+ st->trig->ops = &lis3l02dq_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_trigger_free(st->trig);
+error_ret:
+ return ret;
+}
+
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_trigger_free(st->trig);
+}
+
+void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
+{
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
+ iio_kfifo_free(indio_dev->buffer);
+}
+
+static int lis3l02dq_buffer_postenable(struct iio_dev *indio_dev)
+{
+ /* Disable unwanted channels otherwise the interrupt will not clear */
+ u8 t;
+ int ret;
+ bool oneenabled = false;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+
+ if (test_bit(0, indio_dev->active_scan_mask)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+ oneenabled = true;
+ } else {
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+ }
+ if (test_bit(1, indio_dev->active_scan_mask)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+ oneenabled = true;
+ } else {
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+ }
+ if (test_bit(2, indio_dev->active_scan_mask)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+ oneenabled = true;
+ } else {
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+ }
+ if (!oneenabled) /* what happens in this case is unknown */
+ return -EINVAL;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ t);
+ if (ret)
+ goto error_ret;
+
+ return iio_triggered_buffer_postenable(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Turn all channels on again */
+static int lis3l02dq_buffer_predisable(struct iio_dev *indio_dev)
+{
+ u8 t;
+ int ret;
+
+ ret = iio_triggered_buffer_predisable(indio_dev);
+ if (ret)
+ goto error_ret;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE |
+ LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE |
+ LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ t);
+
+error_ret:
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops lis3l02dq_buffer_setup_ops = {
+ .postenable = &lis3l02dq_buffer_postenable,
+ .predisable = &lis3l02dq_buffer_predisable,
+};
+
+int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct iio_buffer *buffer;
+
+ buffer = iio_kfifo_allocate();
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ buffer->scan_timestamp = true;
+ indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops;
+
+ /* Functions are NULL as we set handler below */
+ indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
+ &lis3l02dq_trigger_handler,
+ 0,
+ indio_dev,
+ "lis3l02dq_consumer%d",
+ indio_dev->id);
+
+ if (!indio_dev->pollfunc) {
+ ret = -ENOMEM;
+ goto error_iio_sw_rb_free;
+ }
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+ return ret;
+}
diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h
new file mode 100644
index 000000000..9c8a9587d
--- /dev/null
+++ b/drivers/staging/iio/accel/sca3000.h
@@ -0,0 +1,278 @@
+/*
+ * sca3000.c -- support VTI sca3000 series accelerometers
+ * via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * Partly based upon tle62x0.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Initial mode is direct measurement.
+ *
+ * Untested things
+ *
+ * Temperature reading (the e05 I'm testing with doesn't have a sensor)
+ *
+ * Free fall detection mode - supported but untested as I'm not droping my
+ * dubious wire rig far enough to test it.
+ *
+ * Unsupported as yet
+ *
+ * Time stamping of data from ring. Various ideas on how to do this but none
+ * are remotely simple. Suggestions welcome.
+ *
+ * Individual enabling disabling of channels going into ring buffer
+ *
+ * Overflow handling (this is signaled for all but 8 bit ring buffer mode.)
+ *
+ * Motion detector using AND combinations of signals.
+ *
+ * Note: Be very careful about not touching an register bytes marked
+ * as reserved on the data sheet. They really mean it as changing convents of
+ * some will cause the device to lock up.
+ *
+ * Known issues - on rare occasions the interrupts lock up. Not sure why as yet.
+ * Can probably alleviate this by reading the interrupt register on start, but
+ * that is really just brushing the problem under the carpet.
+ */
+#ifndef _SCA3000
+#define _SCA3000
+
+#define SCA3000_WRITE_REG(a) (((a) << 2) | 0x02)
+#define SCA3000_READ_REG(a) ((a) << 2)
+
+#define SCA3000_REG_ADDR_REVID 0x00
+#define SCA3000_REVID_MAJOR_MASK 0xf0
+#define SCA3000_REVID_MINOR_MASK 0x0f
+
+#define SCA3000_REG_ADDR_STATUS 0x02
+#define SCA3000_LOCKED 0x20
+#define SCA3000_EEPROM_CS_ERROR 0x02
+#define SCA3000_SPI_FRAME_ERROR 0x01
+
+/* All reads done using register decrement so no need to directly access LSBs */
+#define SCA3000_REG_ADDR_X_MSB 0x05
+#define SCA3000_REG_ADDR_Y_MSB 0x07
+#define SCA3000_REG_ADDR_Z_MSB 0x09
+
+#define SCA3000_REG_ADDR_RING_OUT 0x0f
+
+/* Temp read untested - the e05 doesn't have the sensor */
+#define SCA3000_REG_ADDR_TEMP_MSB 0x13
+
+#define SCA3000_REG_ADDR_MODE 0x14
+#define SCA3000_MODE_PROT_MASK 0x28
+
+#define SCA3000_RING_BUF_ENABLE 0x80
+#define SCA3000_RING_BUF_8BIT 0x40
+/*
+ * Free fall detection triggers an interrupt if the acceleration
+ * is below a threshold for equivalent of 25cm drop
+ */
+#define SCA3000_FREE_FALL_DETECT 0x10
+#define SCA3000_MEAS_MODE_NORMAL 0x00
+#define SCA3000_MEAS_MODE_OP_1 0x01
+#define SCA3000_MEAS_MODE_OP_2 0x02
+
+/*
+ * In motion detection mode the accelerations are band pass filtered
+ * (approx 1 - 25Hz) and then a programmable threshold used to trigger
+ * and interrupt.
+ */
+#define SCA3000_MEAS_MODE_MOT_DET 0x03
+
+#define SCA3000_REG_ADDR_BUF_COUNT 0x15
+
+#define SCA3000_REG_ADDR_INT_STATUS 0x16
+
+#define SCA3000_INT_STATUS_THREE_QUARTERS 0x80
+#define SCA3000_INT_STATUS_HALF 0x40
+
+#define SCA3000_INT_STATUS_FREE_FALL 0x08
+#define SCA3000_INT_STATUS_Y_TRIGGER 0x04
+#define SCA3000_INT_STATUS_X_TRIGGER 0x02
+#define SCA3000_INT_STATUS_Z_TRIGGER 0x01
+
+/* Used to allow access to multiplexed registers */
+#define SCA3000_REG_ADDR_CTRL_SEL 0x18
+/* Only available for SCA3000-D03 and SCA3000-D01 */
+#define SCA3000_REG_CTRL_SEL_I2C_DISABLE 0x01
+#define SCA3000_REG_CTRL_SEL_MD_CTRL 0x02
+#define SCA3000_REG_CTRL_SEL_MD_Y_TH 0x03
+#define SCA3000_REG_CTRL_SEL_MD_X_TH 0x04
+#define SCA3000_REG_CTRL_SEL_MD_Z_TH 0x05
+/*
+ * BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device
+ * will not function
+ */
+#define SCA3000_REG_CTRL_SEL_OUT_CTRL 0x0B
+#define SCA3000_OUT_CTRL_PROT_MASK 0xE0
+#define SCA3000_OUT_CTRL_BUF_X_EN 0x10
+#define SCA3000_OUT_CTRL_BUF_Y_EN 0x08
+#define SCA3000_OUT_CTRL_BUF_Z_EN 0x04
+#define SCA3000_OUT_CTRL_BUF_DIV_4 0x02
+#define SCA3000_OUT_CTRL_BUF_DIV_2 0x01
+
+/*
+ * Control which motion detector interrupts are on.
+ * For now only OR combinations are supported.
+ */
+#define SCA3000_MD_CTRL_PROT_MASK 0xC0
+#define SCA3000_MD_CTRL_OR_Y 0x01
+#define SCA3000_MD_CTRL_OR_X 0x02
+#define SCA3000_MD_CTRL_OR_Z 0x04
+/* Currently unsupported */
+#define SCA3000_MD_CTRL_AND_Y 0x08
+#define SCA3000_MD_CTRL_AND_X 0x10
+#define SAC3000_MD_CTRL_AND_Z 0x20
+
+/*
+ * Some control registers of complex access methods requiring this register to
+ * be used to remove a lock.
+ */
+#define SCA3000_REG_ADDR_UNLOCK 0x1e
+
+#define SCA3000_REG_ADDR_INT_MASK 0x21
+#define SCA3000_INT_MASK_PROT_MASK 0x1C
+
+#define SCA3000_INT_MASK_RING_THREE_QUARTER 0x80
+#define SCA3000_INT_MASK_RING_HALF 0x40
+
+#define SCA3000_INT_MASK_ALL_INTS 0x02
+#define SCA3000_INT_MASK_ACTIVE_HIGH 0x01
+#define SCA3000_INT_MASK_ACTIVE_LOW 0x00
+
+/* Values of multiplexed registers (write to ctrl_data after select) */
+#define SCA3000_REG_ADDR_CTRL_DATA 0x22
+
+/*
+ * Measurement modes available on some sca3000 series chips. Code assumes others
+ * may become available in the future.
+ *
+ * Bypass - Bypass the low-pass filter in the signal channel so as to increase
+ * signal bandwidth.
+ *
+ * Narrow - Narrow low-pass filtering of the signal channel and half output
+ * data rate by decimation.
+ *
+ * Wide - Widen low-pass filtering of signal channel to increase bandwidth
+ */
+#define SCA3000_OP_MODE_BYPASS 0x01
+#define SCA3000_OP_MODE_NARROW 0x02
+#define SCA3000_OP_MODE_WIDE 0x04
+#define SCA3000_MAX_TX 6
+#define SCA3000_MAX_RX 2
+
+/**
+ * struct sca3000_state - device instance state information
+ * @us: the associated spi device
+ * @info: chip variant information
+ * @interrupt_handler_ws: event interrupt handler for all events
+ * @last_timestamp: the timestamp of the last event
+ * @mo_det_use_count: reference counter for the motion detection unit
+ * @lock: lock used to protect elements of sca3000_state
+ * and the underlying device state.
+ * @bpse: number of bits per scan element
+ * @tx: dma-able transmit buffer
+ * @rx: dma-able receive buffer
+ **/
+struct sca3000_state {
+ struct spi_device *us;
+ const struct sca3000_chip_info *info;
+ struct work_struct interrupt_handler_ws;
+ s64 last_timestamp;
+ int mo_det_use_count;
+ struct mutex lock;
+ int bpse;
+ /* Can these share a cacheline ? */
+ u8 rx[2] ____cacheline_aligned;
+ u8 tx[6] ____cacheline_aligned;
+};
+
+/**
+ * struct sca3000_chip_info - model dependent parameters
+ * @scale: scale * 10^-6
+ * @temp_output: some devices have temperature sensors.
+ * @measurement_mode_freq: normal mode sampling frequency
+ * @option_mode_1: first optional mode. Not all models have one
+ * @option_mode_1_freq: option mode 1 sampling frequency
+ * @option_mode_2: second optional mode. Not all chips have one
+ * @option_mode_2_freq: option mode 2 sampling frequency
+ *
+ * This structure is used to hold information about the functionality of a given
+ * sca3000 variant.
+ **/
+struct sca3000_chip_info {
+ unsigned int scale;
+ bool temp_output;
+ int measurement_mode_freq;
+ int option_mode_1;
+ int option_mode_1_freq;
+ int option_mode_2;
+ int option_mode_2_freq;
+ int mot_det_mult_xz[6];
+ int mot_det_mult_y[7];
+};
+
+int sca3000_read_data_short(struct sca3000_state *st,
+ u8 reg_address_high,
+ int len);
+
+/**
+ * sca3000_write_reg() write a single register
+ * @address: address of register on chip
+ * @val: value to be written to register
+ *
+ * The main lock must be held.
+ **/
+int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val);
+
+#ifdef CONFIG_IIO_BUFFER
+/**
+ * sca3000_register_ring_funcs() setup the ring state change functions
+ **/
+void sca3000_register_ring_funcs(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_configure_ring() - allocate and configure ring buffer
+ * @indio_dev: iio-core device whose ring is to be configured
+ *
+ * The hardware ring buffer needs far fewer ring buffer functions than
+ * a software one as a lot of things are handled automatically.
+ * This function also tells the iio core that our device supports a
+ * hardware ring buffer mode.
+ **/
+int sca3000_configure_ring(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_unconfigure_ring() - deallocate the ring buffer
+ * @indio_dev: iio-core device whose ring we are freeing
+ **/
+void sca3000_unconfigure_ring(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_ring_int_process() handles ring related event pushing and escalation
+ * @val: the event code
+ **/
+void sca3000_ring_int_process(u8 val, struct iio_buffer *ring);
+
+#else
+static inline void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
+{
+}
+
+static inline
+int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void sca3000_ring_int_process(u8 val, void *ring)
+{
+}
+
+#endif
+#endif /* _SCA3000 */
diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c
new file mode 100644
index 000000000..b614f272b
--- /dev/null
+++ b/drivers/staging/iio/accel/sca3000_core.c
@@ -0,0 +1,1209 @@
+/*
+ * sca3000_core.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ * See industrialio/accels/sca3000.h for comments.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+
+#include "sca3000.h"
+
+enum sca3000_variant {
+ d01,
+ e02,
+ e04,
+ e05,
+};
+
+/*
+ * Note where option modes are not defined, the chip simply does not
+ * support any.
+ * Other chips in the sca3000 series use i2c and are not included here.
+ *
+ * Some of these devices are only listed in the family data sheet and
+ * do not actually appear to be available.
+ */
+static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = {
+ [d01] = {
+ .scale = 7357,
+ .temp_output = true,
+ .measurement_mode_freq = 250,
+ .option_mode_1 = SCA3000_OP_MODE_BYPASS,
+ .option_mode_1_freq = 250,
+ .mot_det_mult_xz = {50, 100, 200, 350, 650, 1300},
+ .mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750},
+ },
+ [e02] = {
+ .scale = 9810,
+ .measurement_mode_freq = 125,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 63,
+ .mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050},
+ .mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700},
+ },
+ [e04] = {
+ .scale = 19620,
+ .measurement_mode_freq = 100,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ .mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100},
+ .mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000},
+ },
+ [e05] = {
+ .scale = 61313,
+ .measurement_mode_freq = 200,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ .mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900},
+ .mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600},
+ },
+};
+
+int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val)
+{
+ st->tx[0] = SCA3000_WRITE_REG(address);
+ st->tx[1] = val;
+ return spi_write(st->us, st->tx, 2);
+}
+
+int sca3000_read_data_short(struct sca3000_state *st,
+ uint8_t reg_address_high,
+ int len)
+{
+ struct spi_transfer xfer[2] = {
+ {
+ .len = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = len,
+ .rx_buf = st->rx,
+ }
+ };
+ st->tx[0] = SCA3000_READ_REG(reg_address_high);
+
+ return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_reg_lock_on() test if the ctrl register lock is on
+ *
+ * Lock must be held.
+ **/
+static int sca3000_reg_lock_on(struct sca3000_state *st)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_STATUS, 1);
+ if (ret < 0)
+ return ret;
+
+ return !(st->rx[0] & SCA3000_LOCKED);
+}
+
+/**
+ * __sca3000_unlock_reg_lock() unlock the control registers
+ *
+ * Note the device does not appear to support doing this in a single transfer.
+ * This should only ever be used as part of ctrl reg read.
+ * Lock must be held before calling this
+ **/
+static int __sca3000_unlock_reg_lock(struct sca3000_state *st)
+{
+ struct spi_transfer xfer[3] = {
+ {
+ .len = 2,
+ .cs_change = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = 2,
+ .cs_change = 1,
+ .tx_buf = st->tx + 2,
+ }, {
+ .len = 2,
+ .tx_buf = st->tx + 4,
+ },
+ };
+ st->tx[0] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ st->tx[1] = 0x00;
+ st->tx[2] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ st->tx[3] = 0x50;
+ st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ st->tx[5] = 0xA0;
+
+ return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_write_ctrl_reg() write to a lock protect ctrl register
+ * @sel: selects which registers we wish to write to
+ * @val: the value to be written
+ *
+ * Certain control registers are protected against overwriting by the lock
+ * register and use a shared write address. This function allows writing of
+ * these registers.
+ * Lock must be held.
+ **/
+static int sca3000_write_ctrl_reg(struct sca3000_state *st,
+ uint8_t sel,
+ uint8_t val)
+{
+
+ int ret;
+
+ ret = sca3000_reg_lock_on(st);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(st);
+ if (ret)
+ goto error_ret;
+ }
+
+ /* Set the control select register */
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, sel);
+ if (ret)
+ goto error_ret;
+
+ /* Write the actual value into the register */
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_DATA, val);
+
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_ctrl_reg() read from lock protected control register.
+ *
+ * Lock must be held.
+ **/
+static int sca3000_read_ctrl_reg(struct sca3000_state *st,
+ u8 ctrl_reg)
+{
+ int ret;
+
+ ret = sca3000_reg_lock_on(st);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(st);
+ if (ret)
+ goto error_ret;
+ }
+ /* Set the control select register */
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, ctrl_reg);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_CTRL_DATA, 1);
+ if (ret)
+ goto error_ret;
+ else
+ return st->rx[0];
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_show_rev() - sysfs interface to read the chip revision number
+ **/
+static ssize_t sca3000_show_rev(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0, ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_REVID, 1);
+ if (ret < 0)
+ goto error_ret;
+ len += sprintf(buf + len,
+ "major=%d, minor=%d\n",
+ st->rx[0] & SCA3000_REVID_MAJOR_MASK,
+ st->rx[0] & SCA3000_REVID_MINOR_MASK);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/**
+ * sca3000_show_available_measurement_modes() display available modes
+ *
+ * This is all read from chip specific data in the driver. Not all
+ * of the sca3000 series support modes other than normal.
+ **/
+static ssize_t
+sca3000_show_available_measurement_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0;
+
+ len += sprintf(buf + len, "0 - normal mode");
+ switch (st->info->option_mode_1) {
+ case SCA3000_OP_MODE_NARROW:
+ len += sprintf(buf + len, ", 1 - narrow mode");
+ break;
+ case SCA3000_OP_MODE_BYPASS:
+ len += sprintf(buf + len, ", 1 - bypass mode");
+ break;
+ }
+ switch (st->info->option_mode_2) {
+ case SCA3000_OP_MODE_WIDE:
+ len += sprintf(buf + len, ", 2 - wide mode");
+ break;
+ }
+ /* always supported */
+ len += sprintf(buf + len, " 3 - motion detection\n");
+
+ return len;
+}
+
+/**
+ * sca3000_show_measurement_mode() sysfs read of current mode
+ **/
+static ssize_t
+sca3000_show_measurement_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0, ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ /* mask bottom 2 bits - only ones that are relevant */
+ st->rx[0] &= 0x03;
+ switch (st->rx[0]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "0 - normal mode\n");
+ break;
+ case SCA3000_MEAS_MODE_MOT_DET:
+ len += sprintf(buf + len, "3 - motion detection\n");
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ switch (st->info->option_mode_1) {
+ case SCA3000_OP_MODE_NARROW:
+ len += sprintf(buf + len, "1 - narrow mode\n");
+ break;
+ case SCA3000_OP_MODE_BYPASS:
+ len += sprintf(buf + len, "1 - bypass mode\n");
+ break;
+ }
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ switch (st->info->option_mode_2) {
+ case SCA3000_OP_MODE_WIDE:
+ len += sprintf(buf + len, "2 - wide mode\n");
+ break;
+ }
+ break;
+ }
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/**
+ * sca3000_store_measurement_mode() set the current mode
+ **/
+static ssize_t
+sca3000_store_measurement_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 mask = 0x03;
+ u8 val;
+
+ mutex_lock(&st->lock);
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ if (val > 3) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ st->rx[0] &= ~mask;
+ st->rx[0] |= (val & mask);
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, st->rx[0]);
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&st->lock);
+
+ return len;
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+
+/*
+ * Not even vaguely standard attributes so defined here rather than
+ * in the relevant IIO core headers
+ */
+static IIO_DEVICE_ATTR(measurement_mode_available, S_IRUGO,
+ sca3000_show_available_measurement_modes,
+ NULL, 0);
+
+static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR,
+ sca3000_show_measurement_mode,
+ sca3000_store_measurement_mode,
+ 0);
+
+/* More standard attributes */
+
+static IIO_DEVICE_ATTR(revision, S_IRUGO, sca3000_show_rev, NULL, 0);
+
+static const struct iio_event_spec sca3000_event = {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+
+#define SCA3000_CHAN(index, mod) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 11, \
+ .storagebits = 16, \
+ .shift = 5, \
+ }, \
+ .event_spec = &sca3000_event, \
+ .num_event_specs = 1, \
+ }
+
+static const struct iio_chan_spec sca3000_channels[] = {
+ SCA3000_CHAN(0, IIO_MOD_X),
+ SCA3000_CHAN(1, IIO_MOD_Y),
+ SCA3000_CHAN(2, IIO_MOD_Z),
+};
+
+static const struct iio_chan_spec sca3000_channels_with_temp[] = {
+ SCA3000_CHAN(0, IIO_MOD_X),
+ SCA3000_CHAN(1, IIO_MOD_Y),
+ SCA3000_CHAN(2, IIO_MOD_Z),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ /* No buffer support */
+ .scan_index = -1,
+ },
+};
+
+static u8 sca3000_addresses[3][3] = {
+ [0] = {SCA3000_REG_ADDR_X_MSB, SCA3000_REG_CTRL_SEL_MD_X_TH,
+ SCA3000_MD_CTRL_OR_X},
+ [1] = {SCA3000_REG_ADDR_Y_MSB, SCA3000_REG_CTRL_SEL_MD_Y_TH,
+ SCA3000_MD_CTRL_OR_Y},
+ [2] = {SCA3000_REG_ADDR_Z_MSB, SCA3000_REG_CTRL_SEL_MD_Z_TH,
+ SCA3000_MD_CTRL_OR_Z},
+};
+
+static int sca3000_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 address;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ if (chan->type == IIO_ACCEL) {
+ if (st->mo_det_use_count) {
+ mutex_unlock(&st->lock);
+ return -EBUSY;
+ }
+ address = sca3000_addresses[chan->address][0];
+ ret = sca3000_read_data_short(st, address, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF;
+ *val = ((*val) << (sizeof(*val)*8 - 13)) >>
+ (sizeof(*val)*8 - 13);
+ } else {
+ /* get the temperature when available */
+ ret = sca3000_read_data_short(st,
+ SCA3000_REG_ADDR_TEMP_MSB, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = ((st->rx[0] & 0x3F) << 3) |
+ ((st->rx[1] & 0xE0) >> 5);
+ }
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ if (chan->type == IIO_ACCEL)
+ *val2 = st->info->scale;
+ else /* temperature */
+ *val2 = 555556;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -214;
+ *val2 = 600000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * sca3000_read_av_freq() sysfs function to get available frequencies
+ *
+ * The later modes are only relevant to the ring buffer - and depend on current
+ * mode. Note that data sheet gives rather wide tolerances for these so integer
+ * division will give good enough answer and not all chips have them specified
+ * at all.
+ **/
+static ssize_t sca3000_read_av_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0, ret, val;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+
+ switch (val & 0x03) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->measurement_mode_freq,
+ st->info->measurement_mode_freq/2,
+ st->info->measurement_mode_freq/4);
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_1_freq,
+ st->info->option_mode_1_freq/2,
+ st->info->option_mode_1_freq/4);
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_2_freq,
+ st->info->option_mode_2_freq/2,
+ st->info->option_mode_2_freq/4);
+ break;
+ }
+ return len;
+error_ret:
+ return ret;
+}
+/**
+ * __sca3000_get_base_freq() obtain mode specific base frequency
+ *
+ * lock must be held
+ **/
+static inline int __sca3000_get_base_freq(struct sca3000_state *st,
+ const struct sca3000_chip_info *info,
+ int *base_freq)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ switch (0x03 & st->rx[0]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ *base_freq = info->measurement_mode_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ *base_freq = info->option_mode_1_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ *base_freq = info->option_mode_2_freq;
+ break;
+ }
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_frequency() sysfs interface to get the current frequency
+ **/
+static ssize_t sca3000_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, len = 0, base_freq = 0, val;
+
+ mutex_lock(&st->lock);
+ ret = __sca3000_get_base_freq(st, st->info, &base_freq);
+ if (ret)
+ goto error_ret_mut;
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+ val = ret;
+ if (base_freq > 0)
+ switch (val & 0x03) {
+ case 0x00:
+ case 0x03:
+ len = sprintf(buf, "%d\n", base_freq);
+ break;
+ case 0x01:
+ len = sprintf(buf, "%d\n", base_freq/2);
+ break;
+ case 0x02:
+ len = sprintf(buf, "%d\n", base_freq/4);
+ break;
+ }
+
+ return len;
+error_ret_mut:
+ mutex_unlock(&st->lock);
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_set_frequency() sysfs interface to set the current frequency
+ **/
+static ssize_t sca3000_set_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, base_freq = 0;
+ int ctrlval;
+ int val;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ /* What mode are we in? */
+ ret = __sca3000_get_base_freq(st, st->info, &base_freq);
+ if (ret)
+ goto error_free_lock;
+
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ if (ret < 0)
+ goto error_free_lock;
+ ctrlval = ret;
+ /* clear the bits */
+ ctrlval &= ~0x03;
+
+ if (val == base_freq/2) {
+ ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2;
+ } else if (val == base_freq/4) {
+ ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4;
+ } else if (val != base_freq) {
+ ret = -EINVAL;
+ goto error_free_lock;
+ }
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ ctrlval);
+error_free_lock:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/*
+ * Should only really be registered if ring buffer support is compiled in.
+ * Does no harm however and doing it right would add a fair bit of complexity
+ */
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ sca3000_read_frequency,
+ sca3000_set_frequency);
+
+/**
+ * sca3000_read_thresh() - query of a threshold
+ **/
+static int sca3000_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret, i;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int num = chan->channel2;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_ctrl_reg(st, sca3000_addresses[num][1]);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ *val = 0;
+ if (num == 1)
+ for_each_set_bit(i, (unsigned long *)&ret,
+ ARRAY_SIZE(st->info->mot_det_mult_y))
+ *val += st->info->mot_det_mult_y[i];
+ else
+ for_each_set_bit(i, (unsigned long *)&ret,
+ ARRAY_SIZE(st->info->mot_det_mult_xz))
+ *val += st->info->mot_det_mult_xz[i];
+
+ return IIO_VAL_INT;
+}
+
+/**
+ * sca3000_write_thresh() control of threshold
+ **/
+static int sca3000_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int num = chan->channel2;
+ int ret;
+ int i;
+ u8 nonlinear = 0;
+
+ if (num == 1) {
+ i = ARRAY_SIZE(st->info->mot_det_mult_y);
+ while (i > 0)
+ if (val >= st->info->mot_det_mult_y[--i]) {
+ nonlinear |= (1 << i);
+ val -= st->info->mot_det_mult_y[i];
+ }
+ } else {
+ i = ARRAY_SIZE(st->info->mot_det_mult_xz);
+ while (i > 0)
+ if (val >= st->info->mot_det_mult_xz[--i]) {
+ nonlinear |= (1 << i);
+ val -= st->info->mot_det_mult_xz[i];
+ }
+ }
+
+ mutex_lock(&st->lock);
+ ret = sca3000_write_ctrl_reg(st, sca3000_addresses[num][1], nonlinear);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static struct attribute *sca3000_attributes[] = {
+ &iio_dev_attr_revision.dev_attr.attr,
+ &iio_dev_attr_measurement_mode_available.dev_attr.attr,
+ &iio_dev_attr_measurement_mode.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group sca3000_attribute_group = {
+ .attrs = sca3000_attributes,
+};
+
+/**
+ * sca3000_event_handler() - handling ring and non ring events
+ *
+ * Ring related interrupt handler. Depending on event, push to
+ * the ring buffer event chrdev or the event one.
+ *
+ * This function is complicated by the fact that the devices can signify ring
+ * and non ring events via the same interrupt line and they can only
+ * be distinguished via a read of the relevant status register.
+ **/
+static irqreturn_t sca3000_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, val;
+ s64 last_timestamp = iio_get_time_ns();
+
+ /*
+ * Could lead if badly timed to an extra read of status reg,
+ * but ensures no interrupt is missed.
+ */
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto done;
+
+ sca3000_ring_int_process(val, indio_dev->buffer);
+
+ if (val & SCA3000_INT_STATUS_FREE_FALL)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X_AND_Y_AND_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_FALLING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_Y_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_X_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_Z_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+done:
+ return IRQ_HANDLED;
+}
+
+/**
+ * sca3000_read_event_config() what events are enabled
+ **/
+static int sca3000_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 protect_mask = 0x03;
+ int num = chan->channel2;
+
+ /* read current value of mode register */
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+
+ if ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET)
+ ret = 0;
+ else {
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ /* only supporting logical or's for now */
+ ret = !!(ret & sca3000_addresses[num][2]);
+ }
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+/**
+ * sca3000_query_free_fall_mode() is free fall mode enabled
+ **/
+static ssize_t sca3000_query_free_fall_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int val;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", !!(val & SCA3000_FREE_FALL_DETECT));
+}
+
+/**
+ * sca3000_set_free_fall_mode() simple on off control for free fall int
+ *
+ * In these chips the free fall detector should send an interrupt if
+ * the device falls more than 25cm. This has not been tested due
+ * to fragile wiring.
+ **/
+static ssize_t sca3000_set_free_fall_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ u8 val;
+ int ret;
+ u8 protect_mask = SCA3000_FREE_FALL_DETECT;
+
+ mutex_lock(&st->lock);
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ /* read current value of mode register */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+
+ /* if off and should be on */
+ if (val && !(st->rx[0] & protect_mask))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] | SCA3000_FREE_FALL_DETECT));
+ /* if on and should be off */
+ else if (!val && (st->rx[0] & protect_mask))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~protect_mask));
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/**
+ * sca3000_write_event_config() simple on off control for motion detector
+ *
+ * This is a per axis control, but enabling any will result in the
+ * motion detector unit being enabled.
+ * N.B. enabling motion detector stops normal data acquisition.
+ * There is a complexity in knowing which mode to return to when
+ * this mode is disabled. Currently normal mode is assumed.
+ **/
+static int sca3000_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, ctrlval;
+ u8 protect_mask = 0x03;
+ int num = chan->channel2;
+
+ mutex_lock(&st->lock);
+ /*
+ * First read the motion detector config to find out if
+ * this axis is on
+ */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto exit_point;
+ ctrlval = ret;
+ /* if off and should be on */
+ if (state && !(ctrlval & sca3000_addresses[num][2])) {
+ ret = sca3000_write_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ctrlval |
+ sca3000_addresses[num][2]);
+ if (ret)
+ goto exit_point;
+ st->mo_det_use_count++;
+ } else if (!state && (ctrlval & sca3000_addresses[num][2])) {
+ ret = sca3000_write_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ctrlval &
+ ~(sca3000_addresses[num][2]));
+ if (ret)
+ goto exit_point;
+ st->mo_det_use_count--;
+ }
+
+ /* read current value of mode register */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto exit_point;
+ /* if off and should be on */
+ if ((st->mo_det_use_count)
+ && ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~protect_mask)
+ | SCA3000_MEAS_MODE_MOT_DET);
+ /* if on and should be off */
+ else if (!(st->mo_det_use_count)
+ && ((st->rx[0] & protect_mask) == SCA3000_MEAS_MODE_MOT_DET))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~protect_mask));
+exit_point:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+/* Free fall detector related event attribute */
+static IIO_DEVICE_ATTR_NAMED(accel_xayaz_mag_falling_en,
+ in_accel_x&y&z_mag_falling_en,
+ S_IRUGO | S_IWUSR,
+ sca3000_query_free_fall_mode,
+ sca3000_set_free_fall_mode,
+ 0);
+
+static IIO_CONST_ATTR_NAMED(accel_xayaz_mag_falling_period,
+ in_accel_x&y&z_mag_falling_period,
+ "0.226");
+
+static struct attribute *sca3000_event_attributes[] = {
+ &iio_dev_attr_accel_xayaz_mag_falling_en.dev_attr.attr,
+ &iio_const_attr_accel_xayaz_mag_falling_period.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group sca3000_event_attribute_group = {
+ .attrs = sca3000_event_attributes,
+ .name = "events",
+};
+
+/**
+ * sca3000_clean_setup() get the device into a predictable state
+ *
+ * Devices use flash memory to store many of the register values
+ * and hence can come up in somewhat unpredictable states.
+ * Hence reset everything on driver load.
+ **/
+static int sca3000_clean_setup(struct sca3000_state *st)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ /* Ensure all interrupts have been acknowledged */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1);
+ if (ret)
+ goto error_ret;
+
+ /* Turn off all motion detection channels */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ret & SCA3000_MD_CTRL_PROT_MASK);
+ if (ret)
+ goto error_ret;
+
+ /* Disable ring buffer */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ (ret & SCA3000_OUT_CTRL_PROT_MASK)
+ | SCA3000_OUT_CTRL_BUF_X_EN
+ | SCA3000_OUT_CTRL_BUF_Y_EN
+ | SCA3000_OUT_CTRL_BUF_Z_EN
+ | SCA3000_OUT_CTRL_BUF_DIV_4);
+ if (ret)
+ goto error_ret;
+ /* Enable interrupts, relevant to mode and set up as active low */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_INT_MASK,
+ (ret & SCA3000_INT_MASK_PROT_MASK)
+ | SCA3000_INT_MASK_ACTIVE_LOW);
+ if (ret)
+ goto error_ret;
+ /*
+ * Select normal measurement mode, free fall off, ring off
+ * Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5
+ * as that occurs in one of the example on the datasheet
+ */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & SCA3000_MODE_PROT_MASK));
+ st->bpse = 11;
+
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static const struct iio_info sca3000_info = {
+ .attrs = &sca3000_attribute_group,
+ .read_raw = &sca3000_read_raw,
+ .event_attrs = &sca3000_event_attribute_group,
+ .read_event_value = &sca3000_read_thresh,
+ .write_event_value = &sca3000_write_thresh,
+ .read_event_config = &sca3000_read_event_config,
+ .write_event_config = &sca3000_write_event_config,
+ .driver_module = THIS_MODULE,
+};
+
+static int sca3000_probe(struct spi_device *spi)
+{
+ int ret;
+ struct sca3000_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->us = spi;
+ mutex_init(&st->lock);
+ st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi)
+ ->driver_data];
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &sca3000_info;
+ if (st->info->temp_output) {
+ indio_dev->channels = sca3000_channels_with_temp;
+ indio_dev->num_channels =
+ ARRAY_SIZE(sca3000_channels_with_temp);
+ } else {
+ indio_dev->channels = sca3000_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sca3000_channels);
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ sca3000_configure_ring(indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ if (spi->irq) {
+ ret = request_threaded_irq(spi->irq,
+ NULL,
+ &sca3000_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "sca3000",
+ indio_dev);
+ if (ret)
+ goto error_unregister_dev;
+ }
+ sca3000_register_ring_funcs(indio_dev);
+ ret = sca3000_clean_setup(st);
+ if (ret)
+ goto error_free_irq;
+ return 0;
+
+error_free_irq:
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+error_unregister_dev:
+ iio_device_unregister(indio_dev);
+ return ret;
+}
+
+static int sca3000_stop_all_interrupts(struct sca3000_state *st)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_INT_MASK,
+ (st->rx[0] &
+ ~(SCA3000_INT_MASK_RING_THREE_QUARTER |
+ SCA3000_INT_MASK_RING_HALF |
+ SCA3000_INT_MASK_ALL_INTS)));
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int sca3000_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ /* Must ensure no interrupts can be generated after this! */
+ sca3000_stop_all_interrupts(st);
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+ iio_device_unregister(indio_dev);
+ sca3000_unconfigure_ring(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id sca3000_id[] = {
+ {"sca3000_d01", d01},
+ {"sca3000_e02", e02},
+ {"sca3000_e04", e04},
+ {"sca3000_e05", e05},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, sca3000_id);
+
+static struct spi_driver sca3000_driver = {
+ .driver = {
+ .name = "sca3000",
+ .owner = THIS_MODULE,
+ },
+ .probe = sca3000_probe,
+ .remove = sca3000_remove,
+ .id_table = sca3000_id,
+};
+module_spi_driver(sca3000_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c
new file mode 100644
index 000000000..8589eade1
--- /dev/null
+++ b/drivers/staging/iio/accel/sca3000_ring.c
@@ -0,0 +1,348 @@
+/*
+ * sca3000_ring.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include "../ring_hw.h"
+#include "sca3000.h"
+
+/* RFC / future work
+ *
+ * The internal ring buffer doesn't actually change what it holds depending
+ * on which signals are enabled etc, merely whether you can read them.
+ * As such the scan mode selection is somewhat different than for a software
+ * ring buffer and changing it actually covers any data already in the buffer.
+ * Currently scan elements aren't configured so it doesn't matter.
+ */
+
+static int sca3000_read_data(struct sca3000_state *st,
+ uint8_t reg_address_high,
+ u8 **rx_p,
+ int len)
+{
+ int ret;
+ struct spi_transfer xfer[2] = {
+ {
+ .len = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = len,
+ }
+ };
+ *rx_p = kmalloc(len, GFP_KERNEL);
+ if (*rx_p == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ xfer[1].rx_buf = *rx_p;
+ st->tx[0] = SCA3000_READ_REG(reg_address_high);
+ ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+ if (ret) {
+ dev_err(get_device(&st->us->dev), "problem reading register");
+ goto error_free_rx;
+ }
+
+ return 0;
+error_free_rx:
+ kfree(*rx_p);
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_first_n_hw_rb() - main ring access, pulls data from ring
+ * @r: the ring
+ * @count: number of samples to try and pull
+ * @data: output the actual samples pulled from the hw ring
+ *
+ * Currently does not provide timestamps. As the hardware doesn't add them they
+ * can only be inferred approximately from ring buffer events such as 50% full
+ * and knowledge of when buffer was last emptied. This is left to userspace.
+ **/
+static int sca3000_read_first_n_hw_rb(struct iio_buffer *r,
+ size_t count, char __user *buf)
+{
+ struct iio_hw_buffer *hw_ring = iio_to_hw_buf(r);
+ struct iio_dev *indio_dev = hw_ring->private;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ u8 *rx;
+ int ret, i, num_available, num_read = 0;
+ int bytes_per_sample = 1;
+
+ if (st->bpse == 11)
+ bytes_per_sample = 2;
+
+ mutex_lock(&st->lock);
+ if (count % bytes_per_sample) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_BUF_COUNT, 1);
+ if (ret)
+ goto error_ret;
+ else
+ num_available = st->rx[0];
+ /*
+ * num_available is the total number of samples available
+ * i.e. number of time points * number of channels.
+ */
+ if (count > num_available * bytes_per_sample)
+ num_read = num_available*bytes_per_sample;
+ else
+ num_read = count;
+
+ ret = sca3000_read_data(st,
+ SCA3000_REG_ADDR_RING_OUT,
+ &rx, num_read);
+ if (ret)
+ goto error_ret;
+
+ for (i = 0; i < num_read; i++)
+ *(((u16 *)rx) + i) = be16_to_cpup((__be16 *)rx + i);
+
+ if (copy_to_user(buf, rx, num_read))
+ ret = -EFAULT;
+ kfree(rx);
+ r->stufftoread = 0;
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : num_read;
+}
+
+static size_t sca3000_ring_buf_data_available(struct iio_buffer *r)
+{
+ return r->stufftoread ? r->watermark : 0;
+}
+
+/**
+ * sca3000_query_ring_int() is the hardware ring status interrupt enabled
+ **/
+static ssize_t sca3000_query_ring_int(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret, val;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", !!(val & this_attr->address));
+}
+
+/**
+ * sca3000_set_ring_int() set state of ring status interrupt
+ **/
+static ssize_t sca3000_set_ring_int(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ u8 val;
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ if (ret)
+ goto error_ret;
+ if (val)
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_INT_MASK,
+ st->rx[0] | this_attr->address);
+ else
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_INT_MASK,
+ st->rx[0] & ~this_attr->address);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(50_percent, S_IRUGO | S_IWUSR,
+ sca3000_query_ring_int,
+ sca3000_set_ring_int,
+ SCA3000_INT_MASK_RING_HALF);
+
+static IIO_DEVICE_ATTR(75_percent, S_IRUGO | S_IWUSR,
+ sca3000_query_ring_int,
+ sca3000_set_ring_int,
+ SCA3000_INT_MASK_RING_THREE_QUARTER);
+
+static ssize_t sca3000_show_buffer_scale(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "0.%06d\n", 4*st->info->scale);
+}
+
+static IIO_DEVICE_ATTR(in_accel_scale,
+ S_IRUGO,
+ sca3000_show_buffer_scale,
+ NULL,
+ 0);
+
+/*
+ * Ring buffer attributes
+ * This device is a bit unusual in that the sampling frequency and bpse
+ * only apply to the ring buffer. At all times full rate and accuracy
+ * is available via direct reading from registers.
+ */
+static const struct attribute *sca3000_ring_attributes[] = {
+ &iio_dev_attr_50_percent.dev_attr.attr,
+ &iio_dev_attr_75_percent.dev_attr.attr,
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ NULL,
+};
+
+static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buf;
+ struct iio_hw_buffer *ring;
+
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+ if (!ring)
+ return NULL;
+
+ ring->private = indio_dev;
+ buf = &ring->buf;
+ buf->stufftoread = 0;
+ buf->length = 64;
+ buf->attrs = sca3000_ring_attributes;
+ iio_buffer_init(buf);
+
+ return buf;
+}
+
+static void sca3000_ring_release(struct iio_buffer *r)
+{
+ kfree(iio_to_hw_buf(r));
+}
+
+static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = {
+ .read_first_n = &sca3000_read_first_n_hw_rb,
+ .data_available = sca3000_ring_buf_data_available,
+ .release = sca3000_ring_release,
+};
+
+int sca3000_configure_ring(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+
+ buffer = sca3000_rb_allocate(indio_dev);
+ if (buffer == NULL)
+ return -ENOMEM;
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ indio_dev->buffer->access = &sca3000_ring_access_funcs;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return 0;
+}
+
+void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_buffer_put(indio_dev->buffer);
+}
+
+static inline
+int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ if (state) {
+ dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n");
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_MODE,
+ (st->rx[0] | SCA3000_RING_BUF_ENABLE));
+ } else
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~SCA3000_RING_BUF_ENABLE));
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+/**
+ * sca3000_hw_ring_preenable() hw ring buffer preenable function
+ *
+ * Very simple enable function as the chip will allows normal reads
+ * during ring buffer operation so as long as it is indeed running
+ * before we notify the core, the precise ordering does not matter.
+ **/
+static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev)
+{
+ return __sca3000_hw_ring_state_set(indio_dev, 1);
+}
+
+static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)
+{
+ return __sca3000_hw_ring_state_set(indio_dev, 0);
+}
+
+static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = {
+ .preenable = &sca3000_hw_ring_preenable,
+ .postdisable = &sca3000_hw_ring_postdisable,
+};
+
+void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
+{
+ indio_dev->setup_ops = &sca3000_ring_setup_ops;
+}
+
+/**
+ * sca3000_ring_int_process() ring specific interrupt handling.
+ *
+ * This is only split from the main interrupt handler so as to
+ * reduce the amount of code if the ring buffer is not enabled.
+ **/
+void sca3000_ring_int_process(u8 val, struct iio_buffer *ring)
+{
+ if (val & (SCA3000_INT_STATUS_THREE_QUARTERS |
+ SCA3000_INT_STATUS_HALF)) {
+ ring->stufftoread = true;
+ wake_up_interruptible(&ring->pollq);
+ }
+}