v4.19.13 snapshot.
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
new file mode 100644
index 0000000..156630a
--- /dev/null
+++ b/drivers/iio/imu/Kconfig
@@ -0,0 +1,56 @@
+#
+# IIO imu drivers configuration
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Inertial measurement units"
+
+config ADIS16400
+	tristate "Analog Devices ADIS16400 and similar IMU SPI driver"
+	depends on SPI
+	select IIO_ADIS_LIB
+	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+	help
+	  Say yes here to build support for Analog Devices adis16300, adis16344,
+	  adis16350, adis16354, adis16355, adis16360, adis16362, adis16364,
+	  adis16365, adis16400 and adis16405 triaxial inertial sensors
+	  (adis16400 series also have magnetometers).
+
+config ADIS16480
+	tristate "Analog Devices ADIS16480 and similar IMU driver"
+	depends on SPI
+	select IIO_ADIS_LIB
+	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+	help
+	  Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
+	  ADIS16485, ADIS16488 inertial sensors.
+
+source "drivers/iio/imu/bmi160/Kconfig"
+
+config KMX61
+	tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
+	depends on I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say Y here if you want to build a driver for Kionix KMX61 6-axis
+	  accelerometer and magnetometer.
+	  To compile this driver as module, choose M here: the module will
+	  be called kmx61.
+
+source "drivers/iio/imu/inv_mpu6050/Kconfig"
+source "drivers/iio/imu/st_lsm6dsx/Kconfig"
+
+endmenu
+
+config IIO_ADIS_LIB
+	tristate
+	help
+	  A set of IO helper functions for the Analog Devices ADIS* device family.
+
+config IIO_ADIS_LIB_BUFFER
+	bool
+	select IIO_TRIGGERED_BUFFER
+	help
+	  A set of buffer helper functions for the Analog Devices ADIS* device
+	  family.
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
new file mode 100644
index 0000000..68629c6
--- /dev/null
+++ b/drivers/iio/imu/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Inertial Measurement Units
+#
+
+# When adding new entries keep the list in alphabetical order
+adis16400-y             := adis16400_core.o
+adis16400-$(CONFIG_IIO_BUFFER) += adis16400_buffer.o
+obj-$(CONFIG_ADIS16400) += adis16400.o
+obj-$(CONFIG_ADIS16480) += adis16480.o
+
+adis_lib-y += adis.o
+adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
+adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
+obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
+
+obj-y += bmi160/
+obj-y += inv_mpu6050/
+
+obj-$(CONFIG_KMX61) += kmx61.o
+
+obj-y += st_lsm6dsx/
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
new file mode 100644
index 0000000..c771ae6
--- /dev/null
+++ b/drivers/iio/imu/adis.c
@@ -0,0 +1,448 @@
+/*
+ * Common library for ADIS16XXX devices
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * 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 <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#define ADIS_MSC_CTRL_DATA_RDY_EN	BIT(2)
+#define ADIS_MSC_CTRL_DATA_RDY_POL_HIGH	BIT(1)
+#define ADIS_MSC_CTRL_DATA_RDY_DIO2	BIT(0)
+#define ADIS_GLOB_CMD_SW_RESET		BIT(7)
+
+int adis_write_reg(struct adis *adis, unsigned int reg,
+	unsigned int value, unsigned int size)
+{
+	unsigned int page = reg / ADIS_PAGE_SIZE;
+	int ret, i;
+	struct spi_message msg;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = adis->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 4,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 6,
+			.bits_per_word = 8,
+			.len = 2,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 8,
+			.bits_per_word = 8,
+			.len = 2,
+			.delay_usecs = adis->data->write_delay,
+		},
+	};
+
+	mutex_lock(&adis->txrx_lock);
+
+	spi_message_init(&msg);
+
+	if (adis->current_page != page) {
+		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+		adis->tx[1] = page;
+		spi_message_add_tail(&xfers[0], &msg);
+	}
+
+	switch (size) {
+	case 4:
+		adis->tx[8] = ADIS_WRITE_REG(reg + 3);
+		adis->tx[9] = (value >> 24) & 0xff;
+		adis->tx[6] = ADIS_WRITE_REG(reg + 2);
+		adis->tx[7] = (value >> 16) & 0xff;
+		/* fall through */
+	case 2:
+		adis->tx[4] = ADIS_WRITE_REG(reg + 1);
+		adis->tx[5] = (value >> 8) & 0xff;
+		/* fall through */
+	case 1:
+		adis->tx[2] = ADIS_WRITE_REG(reg);
+		adis->tx[3] = value & 0xff;
+		break;
+	default:
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	xfers[size].cs_change = 0;
+
+	for (i = 1; i <= size; i++)
+		spi_message_add_tail(&xfers[i], &msg);
+
+	ret = spi_sync(adis->spi, &msg);
+	if (ret) {
+		dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n",
+				reg, ret);
+	} else {
+		adis->current_page = page;
+	}
+
+out_unlock:
+	mutex_unlock(&adis->txrx_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adis_write_reg);
+
+/**
+ * adis_read_reg() - read 2 bytes from a 16-bit register
+ * @adis: The adis device
+ * @reg: The address of the lower of the two registers
+ * @val: The value read back from the device
+ */
+int adis_read_reg(struct adis *adis, unsigned int reg,
+	unsigned int *val, unsigned int size)
+{
+	unsigned int page = reg / ADIS_PAGE_SIZE;
+	struct spi_message msg;
+	int ret;
+	struct spi_transfer xfers[] = {
+		{
+			.tx_buf = adis->tx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->write_delay,
+		}, {
+			.tx_buf = adis->tx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->read_delay,
+		}, {
+			.tx_buf = adis->tx + 4,
+			.rx_buf = adis->rx,
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.delay_usecs = adis->data->read_delay,
+		}, {
+			.rx_buf = adis->rx + 2,
+			.bits_per_word = 8,
+			.len = 2,
+			.delay_usecs = adis->data->read_delay,
+		},
+	};
+
+	mutex_lock(&adis->txrx_lock);
+	spi_message_init(&msg);
+
+	if (adis->current_page != page) {
+		adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+		adis->tx[1] = page;
+		spi_message_add_tail(&xfers[0], &msg);
+	}
+
+	switch (size) {
+	case 4:
+		adis->tx[2] = ADIS_READ_REG(reg + 2);
+		adis->tx[3] = 0;
+		spi_message_add_tail(&xfers[1], &msg);
+		/* fall through */
+	case 2:
+		adis->tx[4] = ADIS_READ_REG(reg);
+		adis->tx[5] = 0;
+		spi_message_add_tail(&xfers[2], &msg);
+		spi_message_add_tail(&xfers[3], &msg);
+		break;
+	default:
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	ret = spi_sync(adis->spi, &msg);
+	if (ret) {
+		dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n",
+				reg, ret);
+		goto out_unlock;
+	} else {
+		adis->current_page = page;
+	}
+
+	switch (size) {
+	case 4:
+		*val = get_unaligned_be32(adis->rx);
+		break;
+	case 2:
+		*val = get_unaligned_be16(adis->rx + 2);
+		break;
+	}
+
+out_unlock:
+	mutex_unlock(&adis->txrx_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adis_read_reg);
+
+#ifdef CONFIG_DEBUG_FS
+
+int adis_debugfs_reg_access(struct iio_dev *indio_dev,
+	unsigned int reg, unsigned int writeval, unsigned int *readval)
+{
+	struct adis *adis = iio_device_get_drvdata(indio_dev);
+
+	if (readval) {
+		uint16_t val16;
+		int ret;
+
+		ret = adis_read_reg_16(adis, reg, &val16);
+		*readval = val16;
+
+		return ret;
+	} else {
+		return adis_write_reg_16(adis, reg, writeval);
+	}
+}
+EXPORT_SYMBOL(adis_debugfs_reg_access);
+
+#endif
+
+/**
+ * adis_enable_irq() - Enable or disable data ready IRQ
+ * @adis: The adis device
+ * @enable: Whether to enable the IRQ
+ *
+ * Returns 0 on success, negative error code otherwise
+ */
+int adis_enable_irq(struct adis *adis, bool enable)
+{
+	int ret = 0;
+	uint16_t msc;
+
+	if (adis->data->enable_irq)
+		return adis->data->enable_irq(adis, enable);
+
+	ret = adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc);
+	if (ret)
+		goto error_ret;
+
+	msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH;
+	msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2;
+	if (enable)
+		msc |= ADIS_MSC_CTRL_DATA_RDY_EN;
+	else
+		msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN;
+
+	ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc);
+
+error_ret:
+	return ret;
+}
+EXPORT_SYMBOL(adis_enable_irq);
+
+/**
+ * adis_check_status() - Check the device for error conditions
+ * @adis: The adis device
+ *
+ * Returns 0 on success, a negative error code otherwise
+ */
+int adis_check_status(struct adis *adis)
+{
+	uint16_t status;
+	int ret;
+	int i;
+
+	ret = adis_read_reg_16(adis, adis->data->diag_stat_reg, &status);
+	if (ret < 0)
+		return ret;
+
+	status &= adis->data->status_error_mask;
+
+	if (status == 0)
+		return 0;
+
+	for (i = 0; i < 16; ++i) {
+		if (status & BIT(i)) {
+			dev_err(&adis->spi->dev, "%s.\n",
+				adis->data->status_error_msgs[i]);
+		}
+	}
+
+	return -EIO;
+}
+EXPORT_SYMBOL_GPL(adis_check_status);
+
+/**
+ * adis_reset() - Reset the device
+ * @adis: The adis device
+ *
+ * Returns 0 on success, a negative error code otherwise
+ */
+int adis_reset(struct adis *adis)
+{
+	int ret;
+
+	ret = adis_write_reg_8(adis, adis->data->glob_cmd_reg,
+			ADIS_GLOB_CMD_SW_RESET);
+	if (ret)
+		dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adis_reset);
+
+static int adis_self_test(struct adis *adis)
+{
+	int ret;
+
+	ret = adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
+			adis->data->self_test_mask);
+	if (ret) {
+		dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
+			ret);
+		return ret;
+	}
+
+	msleep(adis->data->startup_delay);
+
+	ret = adis_check_status(adis);
+
+	if (adis->data->self_test_no_autoclear)
+		adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00);
+
+	return ret;
+}
+
+/**
+ * adis_inital_startup() - Performs device self-test
+ * @adis: The adis device
+ *
+ * Returns 0 if the device is operational, a negative error code otherwise.
+ *
+ * This function should be called early on in the device initialization sequence
+ * to ensure that the device is in a sane and known state and that it is usable.
+ */
+int adis_initial_startup(struct adis *adis)
+{
+	int ret;
+
+	ret = adis_self_test(adis);
+	if (ret) {
+		dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
+		adis_reset(adis);
+		msleep(adis->data->startup_delay);
+		ret = adis_self_test(adis);
+		if (ret) {
+			dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adis_initial_startup);
+
+/**
+ * adis_single_conversion() - Performs a single sample conversion
+ * @indio_dev: The IIO device
+ * @chan: The IIO channel
+ * @error_mask: Mask for the error bit
+ * @val: Result of the conversion
+ *
+ * Returns IIO_VAL_INT on success, a negative error code otherwise.
+ *
+ * The function performs a single conversion on a given channel and post
+ * processes the value accordingly to the channel spec. If a error_mask is given
+ * the function will check if the mask is set in the returned raw value. If it
+ * is set the function will perform a self-check. If the device does not report
+ * a error bit in the channels raw value set error_mask to 0.
+ */
+int adis_single_conversion(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int error_mask, int *val)
+{
+	struct adis *adis = iio_device_get_drvdata(indio_dev);
+	unsigned int uval;
+	int ret;
+
+	mutex_lock(&indio_dev->mlock);
+
+	ret = adis_read_reg(adis, chan->address, &uval,
+			chan->scan_type.storagebits / 8);
+	if (ret)
+		goto err_unlock;
+
+	if (uval & error_mask) {
+		ret = adis_check_status(adis);
+		if (ret)
+			goto err_unlock;
+	}
+
+	if (chan->scan_type.sign == 's')
+		*val = sign_extend32(uval, chan->scan_type.realbits - 1);
+	else
+		*val = uval & ((1 << chan->scan_type.realbits) - 1);
+
+	ret = IIO_VAL_INT;
+err_unlock:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adis_single_conversion);
+
+/**
+ * adis_init() - Initialize adis device structure
+ * @adis:	The adis device
+ * @indio_dev:	The iio device
+ * @spi:	The spi device
+ * @data:	Chip specific data
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * This function must be called, before any other adis helper function may be
+ * called.
+ */
+int adis_init(struct adis *adis, struct iio_dev *indio_dev,
+	struct spi_device *spi, const struct adis_data *data)
+{
+	mutex_init(&adis->txrx_lock);
+	adis->spi = spi;
+	adis->data = data;
+	iio_device_set_drvdata(indio_dev, adis);
+
+	if (data->has_paging) {
+		/* Need to set the page before first read/write */
+		adis->current_page = -1;
+	} else {
+		/* Page will always be 0 */
+		adis->current_page = 0;
+	}
+
+	return adis_enable_irq(adis, false);
+}
+EXPORT_SYMBOL_GPL(adis_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Common library code for ADIS16XXX devices");
diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h
new file mode 100644
index 0000000..73b189c
--- /dev/null
+++ b/drivers/iio/imu/adis16400.h
@@ -0,0 +1,215 @@
+/*
+ * adis16400.h	support Analog Devices ADIS16400
+ *		3d 18g accelerometers,
+ *		3d gyroscopes,
+ *		3d 2.5gauss magnetometers via SPI
+ *
+ * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * Loosely based upon lis3l02dq.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SPI_ADIS16400_H_
+#define SPI_ADIS16400_H_
+
+#include <linux/iio/imu/adis.h>
+
+#define ADIS16400_STARTUP_DELAY	290 /* ms */
+#define ADIS16400_MTEST_DELAY 90 /* ms */
+
+#define ADIS16400_FLASH_CNT  0x00 /* Flash memory write count */
+#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
+#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
+#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */
+#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */
+#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */
+#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */
+#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */
+#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */
+#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */
+#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */
+#define ADIS16400_TEMP_OUT  0x16 /* Temperature output */
+#define ADIS16400_AUX_ADC   0x18 /* Auxiliary ADC measurement */
+
+#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */
+#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */
+#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */
+
+#define ADIS16300_PITCH_OUT 0x12 /* X axis inclinometer output measurement */
+#define ADIS16300_ROLL_OUT  0x14 /* Y axis inclinometer output measurement */
+#define ADIS16300_AUX_ADC   0x16 /* Auxiliary ADC measurement */
+
+#define ADIS16448_BARO_OUT	0x16 /* Barometric pressure output */
+#define ADIS16448_TEMP_OUT  0x18 /* Temperature output */
+
+/* Calibration parameters */
+#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
+#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
+#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */
+#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */
+#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */
+#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */
+#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */
+#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */
+#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */
+#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */
+#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */
+#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */
+
+#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */
+#define ADIS16400_MSC_CTRL  0x34 /* Miscellaneous control */
+#define ADIS16400_SMPL_PRD  0x36 /* Internal sample period (rate) control */
+#define ADIS16400_SENS_AVG  0x38 /* Dynamic range and digital filter control */
+#define ADIS16400_SLP_CNT   0x3A /* Sleep mode control */
+#define ADIS16400_DIAG_STAT 0x3C /* System status */
+
+/* Alarm functions */
+#define ADIS16400_GLOB_CMD  0x3E /* System command */
+#define ADIS16400_ALM_MAG1  0x40 /* Alarm 1 amplitude threshold */
+#define ADIS16400_ALM_MAG2  0x42 /* Alarm 2 amplitude threshold */
+#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */
+#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */
+#define ADIS16400_ALM_CTRL  0x48 /* Alarm control */
+#define ADIS16400_AUX_DAC   0x4A /* Auxiliary DAC data */
+
+#define ADIS16334_LOT_ID1   0x52 /* Lot identification code 1 */
+#define ADIS16334_LOT_ID2   0x54 /* Lot identification code 2 */
+#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
+#define ADIS16334_SERIAL_NUMBER 0x58 /* Serial number, lot specific */
+
+#define ADIS16400_ERROR_ACTIVE			(1<<14)
+#define ADIS16400_NEW_DATA			(1<<14)
+
+/* MSC_CTRL */
+#define ADIS16400_MSC_CTRL_MEM_TEST		(1<<11)
+#define ADIS16400_MSC_CTRL_INT_SELF_TEST	(1<<10)
+#define ADIS16400_MSC_CTRL_NEG_SELF_TEST	(1<<9)
+#define ADIS16400_MSC_CTRL_POS_SELF_TEST	(1<<8)
+#define ADIS16400_MSC_CTRL_GYRO_BIAS		(1<<7)
+#define ADIS16400_MSC_CTRL_ACCL_ALIGN		(1<<6)
+#define ADIS16400_MSC_CTRL_DATA_RDY_EN		(1<<2)
+#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH	(1<<1)
+#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2	(1<<0)
+
+/* SMPL_PRD */
+#define ADIS16400_SMPL_PRD_TIME_BASE	(1<<7)
+#define ADIS16400_SMPL_PRD_DIV_MASK	0x7F
+
+/* DIAG_STAT */
+#define ADIS16400_DIAG_STAT_ZACCL_FAIL	15
+#define ADIS16400_DIAG_STAT_YACCL_FAIL	14
+#define ADIS16400_DIAG_STAT_XACCL_FAIL	13
+#define ADIS16400_DIAG_STAT_XGYRO_FAIL	12
+#define ADIS16400_DIAG_STAT_YGYRO_FAIL	11
+#define ADIS16400_DIAG_STAT_ZGYRO_FAIL	10
+#define ADIS16400_DIAG_STAT_ALARM2	9
+#define ADIS16400_DIAG_STAT_ALARM1	8
+#define ADIS16400_DIAG_STAT_FLASH_CHK	6
+#define ADIS16400_DIAG_STAT_SELF_TEST	5
+#define ADIS16400_DIAG_STAT_OVERFLOW	4
+#define ADIS16400_DIAG_STAT_SPI_FAIL	3
+#define ADIS16400_DIAG_STAT_FLASH_UPT	2
+#define ADIS16400_DIAG_STAT_POWER_HIGH	1
+#define ADIS16400_DIAG_STAT_POWER_LOW	0
+
+/* GLOB_CMD */
+#define ADIS16400_GLOB_CMD_SW_RESET	(1<<7)
+#define ADIS16400_GLOB_CMD_P_AUTO_NULL	(1<<4)
+#define ADIS16400_GLOB_CMD_FLASH_UPD	(1<<3)
+#define ADIS16400_GLOB_CMD_DAC_LATCH	(1<<2)
+#define ADIS16400_GLOB_CMD_FAC_CALIB	(1<<1)
+#define ADIS16400_GLOB_CMD_AUTO_NULL	(1<<0)
+
+/* SLP_CNT */
+#define ADIS16400_SLP_CNT_POWER_OFF	(1<<8)
+
+#define ADIS16334_RATE_DIV_SHIFT 8
+#define ADIS16334_RATE_INT_CLK BIT(0)
+
+#define ADIS16400_SPI_SLOW	(u32)(300 * 1000)
+#define ADIS16400_SPI_BURST	(u32)(1000 * 1000)
+#define ADIS16400_SPI_FAST	(u32)(2000 * 1000)
+
+#define ADIS16400_HAS_PROD_ID		BIT(0)
+#define ADIS16400_NO_BURST		BIT(1)
+#define ADIS16400_HAS_SLOW_MODE		BIT(2)
+#define ADIS16400_HAS_SERIAL_NUMBER	BIT(3)
+#define ADIS16400_BURST_DIAG_STAT	BIT(4)
+
+struct adis16400_state;
+
+struct adis16400_chip_info {
+	const struct iio_chan_spec *channels;
+	const int num_channels;
+	const long flags;
+	unsigned int gyro_scale_micro;
+	unsigned int accel_scale_micro;
+	int temp_scale_nano;
+	int temp_offset;
+	int (*set_freq)(struct adis16400_state *st, unsigned int freq);
+	int (*get_freq)(struct adis16400_state *st);
+};
+
+/**
+ * struct adis16400_state - device instance specific data
+ * @variant:	chip variant info
+ * @filt_int:	integer part of requested filter frequency
+ * @adis:	adis device
+ **/
+struct adis16400_state {
+	struct adis16400_chip_info	*variant;
+	int				filt_int;
+
+	struct adis adis;
+	unsigned long avail_scan_mask[2];
+};
+
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum {
+	ADIS16400_SCAN_SUPPLY,
+	ADIS16400_SCAN_GYRO_X,
+	ADIS16400_SCAN_GYRO_Y,
+	ADIS16400_SCAN_GYRO_Z,
+	ADIS16400_SCAN_ACC_X,
+	ADIS16400_SCAN_ACC_Y,
+	ADIS16400_SCAN_ACC_Z,
+	ADIS16400_SCAN_MAGN_X,
+	ADIS16400_SCAN_MAGN_Y,
+	ADIS16400_SCAN_MAGN_Z,
+	ADIS16400_SCAN_BARO,
+	ADIS16350_SCAN_TEMP_X,
+	ADIS16350_SCAN_TEMP_Y,
+	ADIS16350_SCAN_TEMP_Z,
+	ADIS16300_SCAN_INCLI_X,
+	ADIS16300_SCAN_INCLI_Y,
+	ADIS16400_SCAN_ADC,
+	ADIS16400_SCAN_TIMESTAMP,
+};
+
+#ifdef CONFIG_IIO_BUFFER
+
+ssize_t adis16400_read_data_from_ring(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf);
+
+
+int adis16400_update_scan_mode(struct iio_dev *indio_dev,
+	const unsigned long *scan_mask);
+irqreturn_t adis16400_trigger_handler(int irq, void *p);
+
+#else /* CONFIG_IIO_BUFFER */
+
+#define adis16400_update_scan_mode NULL
+#define adis16400_trigger_handler NULL
+
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* SPI_ADIS16400_H_ */
diff --git a/drivers/iio/imu/adis16400_buffer.c b/drivers/iio/imu/adis16400_buffer.c
new file mode 100644
index 0000000..e70a533
--- /dev/null
+++ b/drivers/iio/imu/adis16400_buffer.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/export.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "adis16400.h"
+
+int adis16400_update_scan_mode(struct iio_dev *indio_dev,
+	const unsigned long *scan_mask)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	unsigned int burst_length;
+	u8 *tx;
+
+	if (st->variant->flags & ADIS16400_NO_BURST)
+		return adis_update_scan_mode(indio_dev, scan_mask);
+
+	kfree(adis->xfer);
+	kfree(adis->buffer);
+
+	/* All but the timestamp channel */
+	burst_length = (indio_dev->num_channels - 1) * sizeof(u16);
+	if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+		burst_length += sizeof(u16);
+
+	adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
+	if (!adis->xfer)
+		return -ENOMEM;
+
+	adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL);
+	if (!adis->buffer)
+		return -ENOMEM;
+
+	tx = adis->buffer + burst_length;
+	tx[0] = ADIS_READ_REG(ADIS16400_GLOB_CMD);
+	tx[1] = 0;
+
+	adis->xfer[0].tx_buf = tx;
+	adis->xfer[0].bits_per_word = 8;
+	adis->xfer[0].len = 2;
+	adis->xfer[1].rx_buf = adis->buffer;
+	adis->xfer[1].bits_per_word = 8;
+	adis->xfer[1].len = burst_length;
+
+	spi_message_init(&adis->msg);
+	spi_message_add_tail(&adis->xfer[0], &adis->msg);
+	spi_message_add_tail(&adis->xfer[1], &adis->msg);
+
+	return 0;
+}
+
+irqreturn_t adis16400_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct adis16400_state *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	u32 old_speed_hz = st->adis.spi->max_speed_hz;
+	void *buffer;
+	int ret;
+
+	if (!adis->buffer)
+		return -ENOMEM;
+
+	if (!(st->variant->flags & ADIS16400_NO_BURST) &&
+		st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) {
+		st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST;
+		spi_setup(st->adis.spi);
+	}
+
+	ret = spi_sync(adis->spi, &adis->msg);
+	if (ret)
+		dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
+
+	if (!(st->variant->flags & ADIS16400_NO_BURST)) {
+		st->adis.spi->max_speed_hz = old_speed_hz;
+		spi_setup(st->adis.spi);
+	}
+
+	if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+		buffer = adis->buffer + sizeof(u16);
+	else
+		buffer = adis->buffer;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+		pf->timestamp);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c
new file mode 100644
index 0000000..46a5690
--- /dev/null
+++ b/drivers/iio/imu/adis16400_core.c
@@ -0,0 +1,1001 @@
+/*
+ * adis16400.c	support Analog Devices ADIS16400/5
+ *		3d 2g Linear Accelerometers,
+ *		3d Gyroscopes,
+ *		3d Magnetometers via SPI
+ *
+ * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/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/debugfs.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+#include "adis16400.h"
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t adis16400_show_serial_number(struct file *file,
+		char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct adis16400_state *st = file->private_data;
+	u16 lot1, lot2, serial_number;
+	char buf[16];
+	size_t len;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID1, &lot1);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID2, &lot2);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16334_SERIAL_NUMBER,
+			&serial_number);
+	if (ret < 0)
+		return ret;
+
+	len = snprintf(buf, sizeof(buf), "%.4x-%.4x-%.4x\n", lot1, lot2,
+			serial_number);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations adis16400_serial_number_fops = {
+	.open = simple_open,
+	.read = adis16400_show_serial_number,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static int adis16400_show_product_id(void *arg, u64 *val)
+{
+	struct adis16400_state *st = arg;
+	uint16_t prod_id;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16400_PRODUCT_ID, &prod_id);
+	if (ret < 0)
+		return ret;
+
+	*val = prod_id;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16400_product_id_fops,
+	adis16400_show_product_id, NULL, "%lld\n");
+
+static int adis16400_show_flash_count(void *arg, u64 *val)
+{
+	struct adis16400_state *st = arg;
+	uint16_t flash_count;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16400_FLASH_CNT, &flash_count);
+	if (ret < 0)
+		return ret;
+
+	*val = flash_count;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16400_flash_count_fops,
+	adis16400_show_flash_count, NULL, "%lld\n");
+
+static int adis16400_debugfs_init(struct iio_dev *indio_dev)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+
+	if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER)
+		debugfs_create_file("serial_number", 0400,
+			indio_dev->debugfs_dentry, st,
+			&adis16400_serial_number_fops);
+	if (st->variant->flags & ADIS16400_HAS_PROD_ID)
+		debugfs_create_file("product_id", 0400,
+			indio_dev->debugfs_dentry, st,
+			&adis16400_product_id_fops);
+	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
+		st, &adis16400_flash_count_fops);
+
+	return 0;
+}
+
+#else
+
+static int adis16400_debugfs_init(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+#endif
+
+enum adis16400_chip_variant {
+	ADIS16300,
+	ADIS16334,
+	ADIS16350,
+	ADIS16360,
+	ADIS16362,
+	ADIS16364,
+	ADIS16367,
+	ADIS16400,
+	ADIS16445,
+	ADIS16448,
+};
+
+static int adis16334_get_freq(struct adis16400_state *st)
+{
+	int ret;
+	uint16_t t;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
+	if (ret < 0)
+		return ret;
+
+	t >>= ADIS16334_RATE_DIV_SHIFT;
+
+	return 819200 >> t;
+}
+
+static int adis16334_set_freq(struct adis16400_state *st, unsigned int freq)
+{
+	unsigned int t;
+
+	if (freq < 819200)
+		t = ilog2(819200 / freq);
+	else
+		t = 0;
+
+	if (t > 0x31)
+		t = 0x31;
+
+	t <<= ADIS16334_RATE_DIV_SHIFT;
+	t |= ADIS16334_RATE_INT_CLK;
+
+	return adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t);
+}
+
+static int adis16400_get_freq(struct adis16400_state *st)
+{
+	int sps, ret;
+	uint16_t t;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
+	if (ret < 0)
+		return ret;
+
+	sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 52851 : 1638404;
+	sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1;
+
+	return sps;
+}
+
+static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
+{
+	unsigned int t;
+	uint8_t val = 0;
+
+	t = 1638404 / freq;
+	if (t >= 128) {
+		val |= ADIS16400_SMPL_PRD_TIME_BASE;
+		t = 52851 / freq;
+		if (t >= 128)
+			t = 127;
+	} else if (t != 0) {
+		t--;
+	}
+
+	val |= t;
+
+	if (t >= 0x0A || (val & ADIS16400_SMPL_PRD_TIME_BASE))
+		st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
+	else
+		st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
+
+	return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
+}
+
+static const unsigned int adis16400_3db_divisors[] = {
+	[0] = 2, /* Special case */
+	[1] = 6,
+	[2] = 12,
+	[3] = 25,
+	[4] = 50,
+	[5] = 100,
+	[6] = 200,
+	[7] = 200, /* Not a valid setting */
+};
+
+static int adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	uint16_t val16;
+	int i, ret;
+
+	for (i = ARRAY_SIZE(adis16400_3db_divisors) - 1; i >= 1; i--) {
+		if (sps / adis16400_3db_divisors[i] >= val)
+			break;
+	}
+
+	ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
+					 (val16 & ~0x07) | i);
+	return ret;
+}
+
+/* Power down the device */
+static int adis16400_stop_device(struct iio_dev *indio_dev)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	int ret;
+
+	ret = adis_write_reg_16(&st->adis, ADIS16400_SLP_CNT,
+			ADIS16400_SLP_CNT_POWER_OFF);
+	if (ret)
+		dev_err(&indio_dev->dev,
+			"problem with turning device off: SLP_CNT");
+
+	return ret;
+}
+
+static int adis16400_initial_setup(struct iio_dev *indio_dev)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	uint16_t prod_id, smp_prd;
+	unsigned int device_id;
+	int ret;
+
+	/* use low spi speed for init if the device has a slow mode */
+	if (st->variant->flags & ADIS16400_HAS_SLOW_MODE)
+		st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
+	else
+		st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
+	st->adis.spi->mode = SPI_MODE_3;
+	spi_setup(st->adis.spi);
+
+	ret = adis_initial_startup(&st->adis);
+	if (ret)
+		return ret;
+
+	if (st->variant->flags & ADIS16400_HAS_PROD_ID) {
+		ret = adis_read_reg_16(&st->adis,
+						ADIS16400_PRODUCT_ID, &prod_id);
+		if (ret)
+			goto err_ret;
+
+		ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
+		if (ret != 1) {
+			ret = -EINVAL;
+			goto err_ret;
+		}
+
+		if (prod_id != device_id)
+			dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+					device_id, prod_id);
+
+		dev_info(&indio_dev->dev, "%s: prod_id 0x%04x at CS%d (irq %d)\n",
+			indio_dev->name, prod_id,
+			st->adis.spi->chip_select, st->adis.spi->irq);
+	}
+	/* use high spi speed if possible */
+	if (st->variant->flags & ADIS16400_HAS_SLOW_MODE) {
+		ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &smp_prd);
+		if (ret)
+			goto err_ret;
+
+		if ((smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) {
+			st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
+			spi_setup(st->adis.spi);
+		}
+	}
+
+err_ret:
+	return ret;
+}
+
+static const uint8_t adis16400_addresses[] = {
+	[ADIS16400_SCAN_GYRO_X] = ADIS16400_XGYRO_OFF,
+	[ADIS16400_SCAN_GYRO_Y] = ADIS16400_YGYRO_OFF,
+	[ADIS16400_SCAN_GYRO_Z] = ADIS16400_ZGYRO_OFF,
+	[ADIS16400_SCAN_ACC_X] = ADIS16400_XACCL_OFF,
+	[ADIS16400_SCAN_ACC_Y] = ADIS16400_YACCL_OFF,
+	[ADIS16400_SCAN_ACC_Z] = ADIS16400_ZACCL_OFF,
+};
+
+static int adis16400_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	int ret, sps;
+
+	switch (info) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		mutex_lock(&indio_dev->mlock);
+		ret = adis_write_reg_16(&st->adis,
+				adis16400_addresses[chan->scan_index], val);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		/*
+		 * Need to cache values so we can update if the frequency
+		 * changes.
+		 */
+		mutex_lock(&indio_dev->mlock);
+		st->filt_int = val;
+		/* Work out update to current value */
+		sps = st->variant->get_freq(st);
+		if (sps < 0) {
+			mutex_unlock(&indio_dev->mlock);
+			return sps;
+		}
+
+		ret = adis16400_set_filter(indio_dev, sps,
+			val * 1000 + val2 / 1000);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		sps = val * 1000 + val2 / 1000;
+
+		if (sps <= 0)
+			return -EINVAL;
+
+		mutex_lock(&indio_dev->mlock);
+		ret = st->variant->set_freq(st, sps);
+		mutex_unlock(&indio_dev->mlock);
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adis16400_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+	struct adis16400_state *st = iio_priv(indio_dev);
+	int16_t val16;
+	int ret;
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		return adis_single_conversion(indio_dev, chan, 0, val);
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = 0;
+			*val2 = st->variant->gyro_scale_micro;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_VOLTAGE:
+			*val = 0;
+			if (chan->channel == 0) {
+				*val = 2;
+				*val2 = 418000; /* 2.418 mV */
+			} else {
+				*val = 0;
+				*val2 = 805800; /* 805.8 uV */
+			}
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_ACCEL:
+			*val = 0;
+			*val2 = st->variant->accel_scale_micro;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_MAGN:
+			*val = 0;
+			*val2 = 500; /* 0.5 mgauss */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_TEMP:
+			*val = st->variant->temp_scale_nano / 1000000;
+			*val2 = (st->variant->temp_scale_nano % 1000000);
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_PRESSURE:
+			/* 20 uBar = 0.002kPascal */
+			*val = 0;
+			*val2 = 2000;
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_CALIBBIAS:
+		mutex_lock(&indio_dev->mlock);
+		ret = adis_read_reg_16(&st->adis,
+				adis16400_addresses[chan->scan_index], &val16);
+		mutex_unlock(&indio_dev->mlock);
+		if (ret)
+			return ret;
+		val16 = sign_extend32(val16, 11);
+		*val = val16;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_OFFSET:
+		/* currently only temperature */
+		*val = st->variant->temp_offset;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		mutex_lock(&indio_dev->mlock);
+		/* Need both the number of taps and the sampling frequency */
+		ret = adis_read_reg_16(&st->adis,
+						ADIS16400_SENS_AVG,
+						&val16);
+		if (ret < 0) {
+			mutex_unlock(&indio_dev->mlock);
+			return ret;
+		}
+		ret = st->variant->get_freq(st);
+		if (ret >= 0) {
+			ret /= adis16400_3db_divisors[val16 & 0x07];
+			*val = ret / 1000;
+			*val2 = (ret % 1000) * 1000;
+		}
+		mutex_unlock(&indio_dev->mlock);
+		if (ret < 0)
+			return ret;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = st->variant->get_freq(st);
+		if (ret < 0)
+			return ret;
+		*val = ret / 1000;
+		*val2 = (ret % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si, chn) { \
+	.type = IIO_VOLTAGE, \
+	.indexed = 1, \
+	.channel = chn, \
+	.extend_name = name, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+		BIT(IIO_CHAN_INFO_SCALE), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (addr), \
+	.scan_index = (si), \
+	.scan_type = { \
+		.sign = 'u', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16400_SUPPLY_CHAN(addr, bits) \
+	ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY, 0)
+
+#define ADIS16400_AUX_ADC_CHAN(addr, bits) \
+	ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC, 1)
+
+#define ADIS16400_GYRO_CHAN(mod, addr, bits) { \
+	.type = IIO_ANGL_VEL, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## mod, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+		BIT(IIO_CHAN_INFO_CALIBBIAS),		  \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+		BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = addr, \
+	.scan_index = ADIS16400_SCAN_GYRO_ ## mod, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16400_ACCEL_CHAN(mod, addr, bits) { \
+	.type = IIO_ACCEL, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## mod, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+		BIT(IIO_CHAN_INFO_CALIBBIAS), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+		BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (addr), \
+	.scan_index = ADIS16400_SCAN_ACC_ ## mod, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16400_MAGN_CHAN(mod, addr, bits) { \
+	.type = IIO_MAGN, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## mod, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+		BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (addr), \
+	.scan_index = ADIS16400_SCAN_MAGN_ ## mod, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16400_MOD_TEMP_NAME_X "x"
+#define ADIS16400_MOD_TEMP_NAME_Y "y"
+#define ADIS16400_MOD_TEMP_NAME_Z "z"
+
+#define ADIS16400_MOD_TEMP_CHAN(mod, addr, bits) { \
+	.type = IIO_TEMP, \
+	.indexed = 1, \
+	.channel = 0, \
+	.extend_name = ADIS16400_MOD_TEMP_NAME_ ## mod, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+		BIT(IIO_CHAN_INFO_OFFSET) | \
+		BIT(IIO_CHAN_INFO_SCALE), \
+	.info_mask_shared_by_type = \
+		BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (addr), \
+	.scan_index = ADIS16350_SCAN_TEMP_ ## mod, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16400_TEMP_CHAN(addr, bits) { \
+	.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), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (addr), \
+	.scan_index = ADIS16350_SCAN_TEMP_X, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16400_INCLI_CHAN(mod, addr, bits) { \
+	.type = IIO_INCLI, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## mod, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (addr), \
+	.scan_index = ADIS16300_SCAN_INCLI_ ## mod, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (bits), \
+		.storagebits = 16, \
+		.shift = 0, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+static const struct iio_chan_spec adis16400_channels[] = {
+	ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 14),
+	ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+	ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
+	ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
+	ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+	ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
+	ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
+	ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
+	ADIS16400_TEMP_CHAN(ADIS16400_TEMP_OUT, 12),
+	ADIS16400_AUX_ADC_CHAN(ADIS16400_AUX_ADC, 12),
+	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16445_channels[] = {
+	ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
+	ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
+	ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 16),
+	ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 16),
+	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 16),
+	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 16),
+	ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12),
+	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16448_channels[] = {
+	ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
+	ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
+	ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 16),
+	ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 16),
+	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 16),
+	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 16),
+	ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 16),
+	ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 16),
+	ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 16),
+	{
+		.type = IIO_PRESSURE,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.address = ADIS16448_BARO_OUT,
+		.scan_index = ADIS16400_SCAN_BARO,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 16,
+			.storagebits = 16,
+			.endianness = IIO_BE,
+		},
+	},
+	ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12),
+	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16350_channels[] = {
+	ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
+	ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+	ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
+	ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
+	ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+	ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
+	ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
+	ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
+	ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
+	ADIS16400_MOD_TEMP_CHAN(X, ADIS16350_XTEMP_OUT, 12),
+	ADIS16400_MOD_TEMP_CHAN(Y, ADIS16350_YTEMP_OUT, 12),
+	ADIS16400_MOD_TEMP_CHAN(Z, ADIS16350_ZTEMP_OUT, 12),
+	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16300_channels[] = {
+	ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
+	ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+	ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+	ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
+	ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
+	ADIS16400_INCLI_CHAN(X, ADIS16300_PITCH_OUT, 13),
+	ADIS16400_INCLI_CHAN(Y, ADIS16300_ROLL_OUT, 13),
+	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static const struct iio_chan_spec adis16334_channels[] = {
+	ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
+	ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
+	ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
+	ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
+	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
+	ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
+	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
+};
+
+static struct adis16400_chip_info adis16400_chips[] = {
+	[ADIS16300] = {
+		.channels = adis16300_channels,
+		.num_channels = ARRAY_SIZE(adis16300_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+				ADIS16400_HAS_SERIAL_NUMBER,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+		.accel_scale_micro = 5884,
+		.temp_scale_nano = 140000000, /* 0.14 C */
+		.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16334] = {
+		.channels = adis16334_channels,
+		.num_channels = ARRAY_SIZE(adis16334_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_NO_BURST |
+				ADIS16400_HAS_SERIAL_NUMBER,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
+		.temp_scale_nano = 67850000, /* 0.06785 C */
+		.temp_offset = 25000000 / 67850, /* 25 C = 0x00 */
+		.set_freq = adis16334_set_freq,
+		.get_freq = adis16334_get_freq,
+	},
+	[ADIS16350] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(73260), /* 0.07326 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(2522), /* 0.002522 g */
+		.temp_scale_nano = 145300000, /* 0.1453 C */
+		.temp_offset = 25000000 / 145300, /* 25 C = 0x00 */
+		.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16360] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+				ADIS16400_HAS_SERIAL_NUMBER,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
+		.temp_scale_nano = 136000000, /* 0.136 C */
+		.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16362] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+				ADIS16400_HAS_SERIAL_NUMBER,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(333), /* 0.333 mg */
+		.temp_scale_nano = 136000000, /* 0.136 C */
+		.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16364] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+				ADIS16400_HAS_SERIAL_NUMBER,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
+		.temp_scale_nano = 136000000, /* 0.136 C */
+		.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16367] = {
+		.channels = adis16350_channels,
+		.num_channels = ARRAY_SIZE(adis16350_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
+				ADIS16400_HAS_SERIAL_NUMBER,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(2000), /* 0.2 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
+		.temp_scale_nano = 136000000, /* 0.136 C */
+		.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16400] = {
+		.channels = adis16400_channels,
+		.num_channels = ARRAY_SIZE(adis16400_channels),
+		.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
+		.temp_scale_nano = 140000000, /* 0.14 C */
+		.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
+		.set_freq = adis16400_set_freq,
+		.get_freq = adis16400_get_freq,
+	},
+	[ADIS16445] = {
+		.channels = adis16445_channels,
+		.num_channels = ARRAY_SIZE(adis16445_channels),
+		.flags = ADIS16400_HAS_PROD_ID |
+				ADIS16400_HAS_SERIAL_NUMBER |
+				ADIS16400_BURST_DIAG_STAT,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(250), /* 1/4000 g */
+		.temp_scale_nano = 73860000, /* 0.07386 C */
+		.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
+		.set_freq = adis16334_set_freq,
+		.get_freq = adis16334_get_freq,
+	},
+	[ADIS16448] = {
+		.channels = adis16448_channels,
+		.num_channels = ARRAY_SIZE(adis16448_channels),
+		.flags = ADIS16400_HAS_PROD_ID |
+				ADIS16400_HAS_SERIAL_NUMBER |
+				ADIS16400_BURST_DIAG_STAT,
+		.gyro_scale_micro = IIO_DEGREE_TO_RAD(40000), /* 0.04 deg/s */
+		.accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
+		.temp_scale_nano = 73860000, /* 0.07386 C */
+		.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
+		.set_freq = adis16334_set_freq,
+		.get_freq = adis16334_get_freq,
+	}
+};
+
+static const struct iio_info adis16400_info = {
+	.read_raw = &adis16400_read_raw,
+	.write_raw = &adis16400_write_raw,
+	.update_scan_mode = adis16400_update_scan_mode,
+	.debugfs_reg_access = adis_debugfs_reg_access,
+};
+
+static const char * const adis16400_status_error_msgs[] = {
+	[ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
+	[ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
+	[ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
+	[ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
+	[ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
+	[ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
+	[ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active",
+	[ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active",
+	[ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error",
+	[ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error",
+	[ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange",
+	[ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure",
+	[ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed",
+	[ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V",
+	[ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V",
+};
+
+static const struct adis_data adis16400_data = {
+	.msc_ctrl_reg = ADIS16400_MSC_CTRL,
+	.glob_cmd_reg = ADIS16400_GLOB_CMD,
+	.diag_stat_reg = ADIS16400_DIAG_STAT,
+
+	.read_delay = 50,
+	.write_delay = 50,
+
+	.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
+	.startup_delay = ADIS16400_STARTUP_DELAY,
+
+	.status_error_msgs = adis16400_status_error_msgs,
+	.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_ALARM2) |
+		BIT(ADIS16400_DIAG_STAT_ALARM1) |
+		BIT(ADIS16400_DIAG_STAT_FLASH_CHK) |
+		BIT(ADIS16400_DIAG_STAT_SELF_TEST) |
+		BIT(ADIS16400_DIAG_STAT_OVERFLOW) |
+		BIT(ADIS16400_DIAG_STAT_SPI_FAIL) |
+		BIT(ADIS16400_DIAG_STAT_FLASH_UPT) |
+		BIT(ADIS16400_DIAG_STAT_POWER_HIGH) |
+		BIT(ADIS16400_DIAG_STAT_POWER_LOW),
+};
+
+static void adis16400_setup_chan_mask(struct adis16400_state *st)
+{
+	const struct adis16400_chip_info *chip_info = st->variant;
+	unsigned int i;
+
+	for (i = 0; i < chip_info->num_channels; i++) {
+		const struct iio_chan_spec *ch = &chip_info->channels[i];
+
+		if (ch->scan_index >= 0 &&
+		    ch->scan_index != ADIS16400_SCAN_TIMESTAMP)
+			st->avail_scan_mask[0] |= BIT(ch->scan_index);
+	}
+}
+
+static int adis16400_probe(struct spi_device *spi)
+{
+	struct adis16400_state *st;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	/* this is only used for removal purposes */
+	spi_set_drvdata(spi, indio_dev);
+
+	/* setup the industrialio driver allocated elements */
+	st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data];
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->channels = st->variant->channels;
+	indio_dev->num_channels = st->variant->num_channels;
+	indio_dev->info = &adis16400_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	if (!(st->variant->flags & ADIS16400_NO_BURST)) {
+		adis16400_setup_chan_mask(st);
+		indio_dev->available_scan_masks = st->avail_scan_mask;
+	}
+
+	ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
+	if (ret)
+		return ret;
+
+	ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev,
+			adis16400_trigger_handler);
+	if (ret)
+		return ret;
+
+	/* Get the device into a sane initial state */
+	ret = adis16400_initial_setup(indio_dev);
+	if (ret)
+		goto error_cleanup_buffer;
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_cleanup_buffer;
+
+	adis16400_debugfs_init(indio_dev);
+	return 0;
+
+error_cleanup_buffer:
+	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+	return ret;
+}
+
+static int adis16400_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct adis16400_state *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	adis16400_stop_device(indio_dev);
+
+	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adis16400_id[] = {
+	{"adis16300", ADIS16300},
+	{"adis16305", ADIS16300},
+	{"adis16334", ADIS16334},
+	{"adis16350", ADIS16350},
+	{"adis16354", ADIS16350},
+	{"adis16355", ADIS16350},
+	{"adis16360", ADIS16360},
+	{"adis16362", ADIS16362},
+	{"adis16364", ADIS16364},
+	{"adis16365", ADIS16360},
+	{"adis16367", ADIS16367},
+	{"adis16400", ADIS16400},
+	{"adis16405", ADIS16400},
+	{"adis16445", ADIS16445},
+	{"adis16448", ADIS16448},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, adis16400_id);
+
+static struct spi_driver adis16400_driver = {
+	.driver = {
+		.name = "adis16400",
+	},
+	.id_table = adis16400_id,
+	.probe = adis16400_probe,
+	.remove = adis16400_remove,
+};
+module_spi_driver(adis16400_driver);
+
+MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
new file mode 100644
index 0000000..a27fe20
--- /dev/null
+++ b/drivers/iio/imu/adis16480.c
@@ -0,0 +1,913 @@
+/*
+ * ADIS16480 and similar IMUs driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/interrupt.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/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include <linux/debugfs.h>
+
+#define ADIS16480_PAGE_SIZE 0x80
+
+#define ADIS16480_REG(page, reg) ((page) * ADIS16480_PAGE_SIZE + (reg))
+
+#define ADIS16480_REG_PAGE_ID 0x00 /* Same address on each page */
+#define ADIS16480_REG_SEQ_CNT			ADIS16480_REG(0x00, 0x06)
+#define ADIS16480_REG_SYS_E_FLA			ADIS16480_REG(0x00, 0x08)
+#define ADIS16480_REG_DIAG_STS			ADIS16480_REG(0x00, 0x0A)
+#define ADIS16480_REG_ALM_STS			ADIS16480_REG(0x00, 0x0C)
+#define ADIS16480_REG_TEMP_OUT			ADIS16480_REG(0x00, 0x0E)
+#define ADIS16480_REG_X_GYRO_OUT		ADIS16480_REG(0x00, 0x10)
+#define ADIS16480_REG_Y_GYRO_OUT		ADIS16480_REG(0x00, 0x14)
+#define ADIS16480_REG_Z_GYRO_OUT		ADIS16480_REG(0x00, 0x18)
+#define ADIS16480_REG_X_ACCEL_OUT		ADIS16480_REG(0x00, 0x1C)
+#define ADIS16480_REG_Y_ACCEL_OUT		ADIS16480_REG(0x00, 0x20)
+#define ADIS16480_REG_Z_ACCEL_OUT		ADIS16480_REG(0x00, 0x24)
+#define ADIS16480_REG_X_MAGN_OUT		ADIS16480_REG(0x00, 0x28)
+#define ADIS16480_REG_Y_MAGN_OUT		ADIS16480_REG(0x00, 0x2A)
+#define ADIS16480_REG_Z_MAGN_OUT		ADIS16480_REG(0x00, 0x2C)
+#define ADIS16480_REG_BAROM_OUT			ADIS16480_REG(0x00, 0x2E)
+#define ADIS16480_REG_X_DELTAANG_OUT		ADIS16480_REG(0x00, 0x40)
+#define ADIS16480_REG_Y_DELTAANG_OUT		ADIS16480_REG(0x00, 0x44)
+#define ADIS16480_REG_Z_DELTAANG_OUT		ADIS16480_REG(0x00, 0x48)
+#define ADIS16480_REG_X_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x4C)
+#define ADIS16480_REG_Y_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x50)
+#define ADIS16480_REG_Z_DELTAVEL_OUT		ADIS16480_REG(0x00, 0x54)
+#define ADIS16480_REG_PROD_ID			ADIS16480_REG(0x00, 0x7E)
+
+#define ADIS16480_REG_X_GYRO_SCALE		ADIS16480_REG(0x02, 0x04)
+#define ADIS16480_REG_Y_GYRO_SCALE		ADIS16480_REG(0x02, 0x06)
+#define ADIS16480_REG_Z_GYRO_SCALE		ADIS16480_REG(0x02, 0x08)
+#define ADIS16480_REG_X_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0A)
+#define ADIS16480_REG_Y_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0C)
+#define ADIS16480_REG_Z_ACCEL_SCALE		ADIS16480_REG(0x02, 0x0E)
+#define ADIS16480_REG_X_GYRO_BIAS		ADIS16480_REG(0x02, 0x10)
+#define ADIS16480_REG_Y_GYRO_BIAS		ADIS16480_REG(0x02, 0x14)
+#define ADIS16480_REG_Z_GYRO_BIAS		ADIS16480_REG(0x02, 0x18)
+#define ADIS16480_REG_X_ACCEL_BIAS		ADIS16480_REG(0x02, 0x1C)
+#define ADIS16480_REG_Y_ACCEL_BIAS		ADIS16480_REG(0x02, 0x20)
+#define ADIS16480_REG_Z_ACCEL_BIAS		ADIS16480_REG(0x02, 0x24)
+#define ADIS16480_REG_X_HARD_IRON		ADIS16480_REG(0x02, 0x28)
+#define ADIS16480_REG_Y_HARD_IRON		ADIS16480_REG(0x02, 0x2A)
+#define ADIS16480_REG_Z_HARD_IRON		ADIS16480_REG(0x02, 0x2C)
+#define ADIS16480_REG_BAROM_BIAS		ADIS16480_REG(0x02, 0x40)
+#define ADIS16480_REG_FLASH_CNT			ADIS16480_REG(0x02, 0x7C)
+
+#define ADIS16480_REG_GLOB_CMD			ADIS16480_REG(0x03, 0x02)
+#define ADIS16480_REG_FNCTIO_CTRL		ADIS16480_REG(0x03, 0x06)
+#define ADIS16480_REG_GPIO_CTRL			ADIS16480_REG(0x03, 0x08)
+#define ADIS16480_REG_CONFIG			ADIS16480_REG(0x03, 0x0A)
+#define ADIS16480_REG_DEC_RATE			ADIS16480_REG(0x03, 0x0C)
+#define ADIS16480_REG_SLP_CNT			ADIS16480_REG(0x03, 0x10)
+#define ADIS16480_REG_FILTER_BNK0		ADIS16480_REG(0x03, 0x16)
+#define ADIS16480_REG_FILTER_BNK1		ADIS16480_REG(0x03, 0x18)
+#define ADIS16480_REG_ALM_CNFG0			ADIS16480_REG(0x03, 0x20)
+#define ADIS16480_REG_ALM_CNFG1			ADIS16480_REG(0x03, 0x22)
+#define ADIS16480_REG_ALM_CNFG2			ADIS16480_REG(0x03, 0x24)
+#define ADIS16480_REG_XG_ALM_MAGN		ADIS16480_REG(0x03, 0x28)
+#define ADIS16480_REG_YG_ALM_MAGN		ADIS16480_REG(0x03, 0x2A)
+#define ADIS16480_REG_ZG_ALM_MAGN		ADIS16480_REG(0x03, 0x2C)
+#define ADIS16480_REG_XA_ALM_MAGN		ADIS16480_REG(0x03, 0x2E)
+#define ADIS16480_REG_YA_ALM_MAGN		ADIS16480_REG(0x03, 0x30)
+#define ADIS16480_REG_ZA_ALM_MAGN		ADIS16480_REG(0x03, 0x32)
+#define ADIS16480_REG_XM_ALM_MAGN		ADIS16480_REG(0x03, 0x34)
+#define ADIS16480_REG_YM_ALM_MAGN		ADIS16480_REG(0x03, 0x36)
+#define ADIS16480_REG_ZM_ALM_MAGN		ADIS16480_REG(0x03, 0x38)
+#define ADIS16480_REG_BR_ALM_MAGN		ADIS16480_REG(0x03, 0x3A)
+#define ADIS16480_REG_FIRM_REV			ADIS16480_REG(0x03, 0x78)
+#define ADIS16480_REG_FIRM_DM			ADIS16480_REG(0x03, 0x7A)
+#define ADIS16480_REG_FIRM_Y			ADIS16480_REG(0x03, 0x7C)
+
+#define ADIS16480_REG_SERIAL_NUM		ADIS16480_REG(0x04, 0x20)
+
+/* Each filter coefficent bank spans two pages */
+#define ADIS16480_FIR_COEF(page) (x < 60 ? ADIS16480_REG(page, (x) + 8) : \
+		ADIS16480_REG((page) + 1, (x) - 60 + 8))
+#define ADIS16480_FIR_COEF_A(x)			ADIS16480_FIR_COEF(0x05, (x))
+#define ADIS16480_FIR_COEF_B(x)			ADIS16480_FIR_COEF(0x07, (x))
+#define ADIS16480_FIR_COEF_C(x)			ADIS16480_FIR_COEF(0x09, (x))
+#define ADIS16480_FIR_COEF_D(x)			ADIS16480_FIR_COEF(0x0B, (x))
+
+struct adis16480_chip_info {
+	unsigned int num_channels;
+	const struct iio_chan_spec *channels;
+	unsigned int gyro_max_val;
+	unsigned int gyro_max_scale;
+	unsigned int accel_max_val;
+	unsigned int accel_max_scale;
+};
+
+struct adis16480 {
+	const struct adis16480_chip_info *chip_info;
+
+	struct adis adis;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static ssize_t adis16480_show_firmware_revision(struct file *file,
+		char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct adis16480 *adis16480 = file->private_data;
+	char buf[7];
+	size_t len;
+	u16 rev;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_REV, &rev);
+	if (ret < 0)
+		return ret;
+
+	len = scnprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations adis16480_firmware_revision_fops = {
+	.open = simple_open,
+	.read = adis16480_show_firmware_revision,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t adis16480_show_firmware_date(struct file *file,
+		char __user *userbuf, size_t count, loff_t *ppos)
+{
+	struct adis16480 *adis16480 = file->private_data;
+	u16 md, year;
+	char buf[12];
+	size_t len;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_Y, &year);
+	if (ret < 0)
+		return ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_FIRM_DM, &md);
+	if (ret < 0)
+		return ret;
+
+	len = snprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n",
+			md >> 8, md & 0xff, year);
+
+	return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations adis16480_firmware_date_fops = {
+	.open = simple_open,
+	.read = adis16480_show_firmware_date,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static int adis16480_show_serial_number(void *arg, u64 *val)
+{
+	struct adis16480 *adis16480 = arg;
+	u16 serial;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_SERIAL_NUM,
+		&serial);
+	if (ret < 0)
+		return ret;
+
+	*val = serial;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16480_serial_number_fops,
+	adis16480_show_serial_number, NULL, "0x%.4llx\n");
+
+static int adis16480_show_product_id(void *arg, u64 *val)
+{
+	struct adis16480 *adis16480 = arg;
+	u16 prod_id;
+	int ret;
+
+	ret = adis_read_reg_16(&adis16480->adis, ADIS16480_REG_PROD_ID,
+		&prod_id);
+	if (ret < 0)
+		return ret;
+
+	*val = prod_id;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16480_product_id_fops,
+	adis16480_show_product_id, NULL, "%llu\n");
+
+static int adis16480_show_flash_count(void *arg, u64 *val)
+{
+	struct adis16480 *adis16480 = arg;
+	u32 flash_count;
+	int ret;
+
+	ret = adis_read_reg_32(&adis16480->adis, ADIS16480_REG_FLASH_CNT,
+		&flash_count);
+	if (ret < 0)
+		return ret;
+
+	*val = flash_count;
+
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16480_flash_count_fops,
+	adis16480_show_flash_count, NULL, "%lld\n");
+
+static int adis16480_debugfs_init(struct iio_dev *indio_dev)
+{
+	struct adis16480 *adis16480 = iio_priv(indio_dev);
+
+	debugfs_create_file_unsafe("firmware_revision", 0400,
+		indio_dev->debugfs_dentry, adis16480,
+		&adis16480_firmware_revision_fops);
+	debugfs_create_file_unsafe("firmware_date", 0400,
+		indio_dev->debugfs_dentry, adis16480,
+		&adis16480_firmware_date_fops);
+	debugfs_create_file_unsafe("serial_number", 0400,
+		indio_dev->debugfs_dentry, adis16480,
+		&adis16480_serial_number_fops);
+	debugfs_create_file_unsafe("product_id", 0400,
+		indio_dev->debugfs_dentry, adis16480,
+		&adis16480_product_id_fops);
+	debugfs_create_file_unsafe("flash_count", 0400,
+		indio_dev->debugfs_dentry, adis16480,
+		&adis16480_flash_count_fops);
+
+	return 0;
+}
+
+#else
+
+static int adis16480_debugfs_init(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+
+#endif
+
+static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	unsigned int t;
+
+	t =  val * 1000 + val2 / 1000;
+	if (t <= 0)
+		return -EINVAL;
+
+	t = 2460000 / t;
+	if (t > 2048)
+		t = 2048;
+
+	if (t != 0)
+		t--;
+
+	return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t);
+}
+
+static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t t;
+	int ret;
+	unsigned freq;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t);
+	if (ret < 0)
+		return ret;
+
+	freq = 2460000 / (t + 1);
+	*val = freq / 1000;
+	*val2 = (freq % 1000) * 1000;
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+enum {
+	ADIS16480_SCAN_GYRO_X,
+	ADIS16480_SCAN_GYRO_Y,
+	ADIS16480_SCAN_GYRO_Z,
+	ADIS16480_SCAN_ACCEL_X,
+	ADIS16480_SCAN_ACCEL_Y,
+	ADIS16480_SCAN_ACCEL_Z,
+	ADIS16480_SCAN_MAGN_X,
+	ADIS16480_SCAN_MAGN_Y,
+	ADIS16480_SCAN_MAGN_Z,
+	ADIS16480_SCAN_BARO,
+	ADIS16480_SCAN_TEMP,
+};
+
+static const unsigned int adis16480_calibbias_regs[] = {
+	[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_BIAS,
+	[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_BIAS,
+	[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_BIAS,
+	[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_BIAS,
+	[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_BIAS,
+	[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_BIAS,
+	[ADIS16480_SCAN_MAGN_X] = ADIS16480_REG_X_HARD_IRON,
+	[ADIS16480_SCAN_MAGN_Y] = ADIS16480_REG_Y_HARD_IRON,
+	[ADIS16480_SCAN_MAGN_Z] = ADIS16480_REG_Z_HARD_IRON,
+	[ADIS16480_SCAN_BARO] = ADIS16480_REG_BAROM_BIAS,
+};
+
+static const unsigned int adis16480_calibscale_regs[] = {
+	[ADIS16480_SCAN_GYRO_X] = ADIS16480_REG_X_GYRO_SCALE,
+	[ADIS16480_SCAN_GYRO_Y] = ADIS16480_REG_Y_GYRO_SCALE,
+	[ADIS16480_SCAN_GYRO_Z] = ADIS16480_REG_Z_GYRO_SCALE,
+	[ADIS16480_SCAN_ACCEL_X] = ADIS16480_REG_X_ACCEL_SCALE,
+	[ADIS16480_SCAN_ACCEL_Y] = ADIS16480_REG_Y_ACCEL_SCALE,
+	[ADIS16480_SCAN_ACCEL_Z] = ADIS16480_REG_Z_ACCEL_SCALE,
+};
+
+static int adis16480_set_calibbias(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int bias)
+{
+	unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_PRESSURE:
+		if (bias < -0x8000 || bias >= 0x8000)
+			return -EINVAL;
+		return adis_write_reg_16(&st->adis, reg, bias);
+	case IIO_ANGL_VEL:
+	case IIO_ACCEL:
+		return adis_write_reg_32(&st->adis, reg, bias);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static int adis16480_get_calibbias(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *bias)
+{
+	unsigned int reg = adis16480_calibbias_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t val16;
+	uint32_t val32;
+	int ret;
+
+	switch (chan->type) {
+	case IIO_MAGN:
+	case IIO_PRESSURE:
+		ret = adis_read_reg_16(&st->adis, reg, &val16);
+		*bias = sign_extend32(val16, 15);
+		break;
+	case IIO_ANGL_VEL:
+	case IIO_ACCEL:
+		ret = adis_read_reg_32(&st->adis, reg, &val32);
+		*bias = sign_extend32(val32, 31);
+		break;
+	default:
+			ret = -EINVAL;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return IIO_VAL_INT;
+}
+
+static int adis16480_set_calibscale(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int scale)
+{
+	unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	if (scale < -0x8000 || scale >= 0x8000)
+		return -EINVAL;
+
+	return adis_write_reg_16(&st->adis, reg, scale);
+}
+
+static int adis16480_get_calibscale(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *scale)
+{
+	unsigned int reg = adis16480_calibscale_regs[chan->scan_index];
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t val16;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, reg, &val16);
+	if (ret < 0)
+		return ret;
+
+	*scale = sign_extend32(val16, 15);
+	return IIO_VAL_INT;
+}
+
+static const unsigned int adis16480_def_filter_freqs[] = {
+	310,
+	55,
+	275,
+	63,
+};
+
+static const unsigned int ad16480_filter_data[][2] = {
+	[ADIS16480_SCAN_GYRO_X]		= { ADIS16480_REG_FILTER_BNK0, 0 },
+	[ADIS16480_SCAN_GYRO_Y]		= { ADIS16480_REG_FILTER_BNK0, 3 },
+	[ADIS16480_SCAN_GYRO_Z]		= { ADIS16480_REG_FILTER_BNK0, 6 },
+	[ADIS16480_SCAN_ACCEL_X]	= { ADIS16480_REG_FILTER_BNK0, 9 },
+	[ADIS16480_SCAN_ACCEL_Y]	= { ADIS16480_REG_FILTER_BNK0, 12 },
+	[ADIS16480_SCAN_ACCEL_Z]	= { ADIS16480_REG_FILTER_BNK1, 0 },
+	[ADIS16480_SCAN_MAGN_X]		= { ADIS16480_REG_FILTER_BNK1, 3 },
+	[ADIS16480_SCAN_MAGN_Y]		= { ADIS16480_REG_FILTER_BNK1, 6 },
+	[ADIS16480_SCAN_MAGN_Z]		= { ADIS16480_REG_FILTER_BNK1, 9 },
+};
+
+static int adis16480_get_filter_freq(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *freq)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	unsigned int enable_mask, offset, reg;
+	uint16_t val;
+	int ret;
+
+	reg = ad16480_filter_data[chan->scan_index][0];
+	offset = ad16480_filter_data[chan->scan_index][1];
+	enable_mask = BIT(offset + 2);
+
+	ret = adis_read_reg_16(&st->adis, reg, &val);
+	if (ret < 0)
+		return ret;
+
+	if (!(val & enable_mask))
+		*freq = 0;
+	else
+		*freq = adis16480_def_filter_freqs[(val >> offset) & 0x3];
+
+	return IIO_VAL_INT;
+}
+
+static int adis16480_set_filter_freq(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int freq)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	unsigned int enable_mask, offset, reg;
+	unsigned int diff, best_diff;
+	unsigned int i, best_freq;
+	uint16_t val;
+	int ret;
+
+	reg = ad16480_filter_data[chan->scan_index][0];
+	offset = ad16480_filter_data[chan->scan_index][1];
+	enable_mask = BIT(offset + 2);
+
+	ret = adis_read_reg_16(&st->adis, reg, &val);
+	if (ret < 0)
+		return ret;
+
+	if (freq == 0) {
+		val &= ~enable_mask;
+	} else {
+		best_freq = 0;
+		best_diff = 310;
+		for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) {
+			if (adis16480_def_filter_freqs[i] >= freq) {
+				diff = adis16480_def_filter_freqs[i] - freq;
+				if (diff < best_diff) {
+					best_diff = diff;
+					best_freq = i;
+				}
+			}
+		}
+
+		val &= ~(0x3 << offset);
+		val |= best_freq << offset;
+		val |= enable_mask;
+	}
+
+	return adis_write_reg_16(&st->adis, reg, val);
+}
+
+static int adis16480_read_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		return adis_single_conversion(indio_dev, chan, 0, val);
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = st->chip_info->gyro_max_scale;
+			*val2 = st->chip_info->gyro_max_val;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_ACCEL:
+			*val = st->chip_info->accel_max_scale;
+			*val2 = st->chip_info->accel_max_val;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_MAGN:
+			*val = 0;
+			*val2 = 100; /* 0.0001 gauss */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_TEMP:
+			*val = 5;
+			*val2 = 650000; /* 5.65 milli degree Celsius */
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_PRESSURE:
+			*val = 0;
+			*val2 = 4000; /* 40ubar = 0.004 kPa */
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_OFFSET:
+		/* Only the temperature channel has a offset */
+		*val = 4425; /* 25 degree Celsius = 0x0000 */
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return adis16480_get_calibbias(indio_dev, chan, val);
+	case IIO_CHAN_INFO_CALIBSCALE:
+		return adis16480_get_calibscale(indio_dev, chan, val);
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		return adis16480_get_filter_freq(indio_dev, chan, val);
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return adis16480_get_freq(indio_dev, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adis16480_write_raw(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+	switch (info) {
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return adis16480_set_calibbias(indio_dev, chan, val);
+	case IIO_CHAN_INFO_CALIBSCALE:
+		return adis16480_set_calibscale(indio_dev, chan, val);
+	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+		return adis16480_set_filter_freq(indio_dev, chan, val);
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return adis16480_set_freq(indio_dev, val, val2);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+#define ADIS16480_MOD_CHANNEL(_type, _mod, _address, _si, _info_sep, _bits) \
+	{ \
+		.type = (_type), \
+		.modified = 1, \
+		.channel2 = (_mod), \
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+			_info_sep, \
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+		.address = (_address), \
+		.scan_index = (_si), \
+		.scan_type = { \
+			.sign = 's', \
+			.realbits = (_bits), \
+			.storagebits = (_bits), \
+			.endianness = IIO_BE, \
+		}, \
+	}
+
+#define ADIS16480_GYRO_CHANNEL(_mod) \
+	ADIS16480_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
+	ADIS16480_REG_ ## _mod ## _GYRO_OUT, ADIS16480_SCAN_GYRO_ ## _mod, \
+	BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
+	BIT(IIO_CHAN_INFO_CALIBSCALE), \
+	32)
+
+#define ADIS16480_ACCEL_CHANNEL(_mod) \
+	ADIS16480_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
+	ADIS16480_REG_ ## _mod ## _ACCEL_OUT, ADIS16480_SCAN_ACCEL_ ## _mod, \
+	BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
+	BIT(IIO_CHAN_INFO_CALIBSCALE), \
+	32)
+
+#define ADIS16480_MAGN_CHANNEL(_mod) \
+	ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \
+	ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \
+	BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+	16)
+
+#define ADIS16480_PRESSURE_CHANNEL() \
+	{ \
+		.type = IIO_PRESSURE, \
+		.indexed = 1, \
+		.channel = 0, \
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+			BIT(IIO_CHAN_INFO_SCALE), \
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+		.address = ADIS16480_REG_BAROM_OUT, \
+		.scan_index = ADIS16480_SCAN_BARO, \
+		.scan_type = { \
+			.sign = 's', \
+			.realbits = 32, \
+			.storagebits = 32, \
+			.endianness = IIO_BE, \
+		}, \
+	}
+
+#define ADIS16480_TEMP_CHANNEL() { \
+		.type = IIO_TEMP, \
+		.indexed = 1, \
+		.channel = 0, \
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			BIT(IIO_CHAN_INFO_SCALE) | \
+			BIT(IIO_CHAN_INFO_OFFSET), \
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+		.address = ADIS16480_REG_TEMP_OUT, \
+		.scan_index = ADIS16480_SCAN_TEMP, \
+		.scan_type = { \
+			.sign = 's', \
+			.realbits = 16, \
+			.storagebits = 16, \
+			.endianness = IIO_BE, \
+		}, \
+	}
+
+static const struct iio_chan_spec adis16480_channels[] = {
+	ADIS16480_GYRO_CHANNEL(X),
+	ADIS16480_GYRO_CHANNEL(Y),
+	ADIS16480_GYRO_CHANNEL(Z),
+	ADIS16480_ACCEL_CHANNEL(X),
+	ADIS16480_ACCEL_CHANNEL(Y),
+	ADIS16480_ACCEL_CHANNEL(Z),
+	ADIS16480_MAGN_CHANNEL(X),
+	ADIS16480_MAGN_CHANNEL(Y),
+	ADIS16480_MAGN_CHANNEL(Z),
+	ADIS16480_PRESSURE_CHANNEL(),
+	ADIS16480_TEMP_CHANNEL(),
+	IIO_CHAN_SOFT_TIMESTAMP(11)
+};
+
+static const struct iio_chan_spec adis16485_channels[] = {
+	ADIS16480_GYRO_CHANNEL(X),
+	ADIS16480_GYRO_CHANNEL(Y),
+	ADIS16480_GYRO_CHANNEL(Z),
+	ADIS16480_ACCEL_CHANNEL(X),
+	ADIS16480_ACCEL_CHANNEL(Y),
+	ADIS16480_ACCEL_CHANNEL(Z),
+	ADIS16480_TEMP_CHANNEL(),
+	IIO_CHAN_SOFT_TIMESTAMP(7)
+};
+
+enum adis16480_variant {
+	ADIS16375,
+	ADIS16480,
+	ADIS16485,
+	ADIS16488,
+};
+
+static const struct adis16480_chip_info adis16480_chip_info[] = {
+	[ADIS16375] = {
+		.channels = adis16485_channels,
+		.num_channels = ARRAY_SIZE(adis16485_channels),
+		/*
+		 * storing the value in rad/degree and the scale in degree
+		 * gives us the result in rad and better precession than
+		 * storing the scale directly in rad.
+		 */
+		.gyro_max_val = IIO_RAD_TO_DEGREE(22887),
+		.gyro_max_scale = 300,
+		.accel_max_val = IIO_M_S_2_TO_G(21973),
+		.accel_max_scale = 18,
+	},
+	[ADIS16480] = {
+		.channels = adis16480_channels,
+		.num_channels = ARRAY_SIZE(adis16480_channels),
+		.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
+		.gyro_max_scale = 450,
+		.accel_max_val = IIO_M_S_2_TO_G(12500),
+		.accel_max_scale = 10,
+	},
+	[ADIS16485] = {
+		.channels = adis16485_channels,
+		.num_channels = ARRAY_SIZE(adis16485_channels),
+		.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
+		.gyro_max_scale = 450,
+		.accel_max_val = IIO_M_S_2_TO_G(20000),
+		.accel_max_scale = 5,
+	},
+	[ADIS16488] = {
+		.channels = adis16480_channels,
+		.num_channels = ARRAY_SIZE(adis16480_channels),
+		.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
+		.gyro_max_scale = 450,
+		.accel_max_val = IIO_M_S_2_TO_G(22500),
+		.accel_max_scale = 18,
+	},
+};
+
+static const struct iio_info adis16480_info = {
+	.read_raw = &adis16480_read_raw,
+	.write_raw = &adis16480_write_raw,
+	.update_scan_mode = adis_update_scan_mode,
+};
+
+static int adis16480_stop_device(struct iio_dev *indio_dev)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	int ret;
+
+	ret = adis_write_reg_16(&st->adis, ADIS16480_REG_SLP_CNT, BIT(9));
+	if (ret)
+		dev_err(&indio_dev->dev,
+			"Could not power down device: %d\n", ret);
+
+	return ret;
+}
+
+static int adis16480_enable_irq(struct adis *adis, bool enable)
+{
+	return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL,
+		enable ? BIT(3) : 0);
+}
+
+static int adis16480_initial_setup(struct iio_dev *indio_dev)
+{
+	struct adis16480 *st = iio_priv(indio_dev);
+	uint16_t prod_id;
+	unsigned int device_id;
+	int ret;
+
+	adis_reset(&st->adis);
+	msleep(70);
+
+	ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1));
+	if (ret)
+		return ret;
+	msleep(30);
+
+	ret = adis_check_status(&st->adis);
+	if (ret)
+		return ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id);
+	if (ret)
+		return ret;
+
+	ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
+	if (ret != 1)
+		return -EINVAL;
+
+	if (prod_id != device_id)
+		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+				device_id, prod_id);
+
+	return 0;
+}
+
+#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
+#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1
+#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2
+#define ADIS16480_DIAG_STAT_XACCL_FAIL 3
+#define ADIS16480_DIAG_STAT_YACCL_FAIL 4
+#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5
+#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8
+#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9
+#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10
+#define ADIS16480_DIAG_STAT_BARO_FAIL 11
+
+static const char * const adis16480_status_error_msgs[] = {
+	[ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
+	[ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
+	[ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
+	[ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
+	[ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
+	[ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
+	[ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure",
+	[ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure",
+	[ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure",
+	[ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure",
+};
+
+static const struct adis_data adis16480_data = {
+	.diag_stat_reg = ADIS16480_REG_DIAG_STS,
+	.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,
+	.has_paging = true,
+
+	.read_delay = 5,
+	.write_delay = 5,
+
+	.status_error_msgs = adis16480_status_error_msgs,
+	.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) |
+		BIT(ADIS16480_DIAG_STAT_BARO_FAIL),
+
+	.enable_irq = adis16480_enable_irq,
+};
+
+static int adis16480_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	struct iio_dev *indio_dev;
+	struct adis16480 *st;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, indio_dev);
+
+	st = iio_priv(indio_dev);
+
+	st->chip_info = &adis16480_chip_info[id->driver_data];
+	indio_dev->dev.parent = &spi->dev;
+	indio_dev->name = spi_get_device_id(spi)->name;
+	indio_dev->channels = st->chip_info->channels;
+	indio_dev->num_channels = st->chip_info->num_channels;
+	indio_dev->info = &adis16480_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = adis_init(&st->adis, indio_dev, spi, &adis16480_data);
+	if (ret)
+		return ret;
+
+	ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
+	if (ret)
+		return ret;
+
+	ret = adis16480_initial_setup(indio_dev);
+	if (ret)
+		goto error_cleanup_buffer;
+
+	ret = iio_device_register(indio_dev);
+	if (ret)
+		goto error_stop_device;
+
+	adis16480_debugfs_init(indio_dev);
+
+	return 0;
+
+error_stop_device:
+	adis16480_stop_device(indio_dev);
+error_cleanup_buffer:
+	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+	return ret;
+}
+
+static int adis16480_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	struct adis16480 *st = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	adis16480_stop_device(indio_dev);
+
+	adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adis16480_ids[] = {
+	{ "adis16375", ADIS16375 },
+	{ "adis16480", ADIS16480 },
+	{ "adis16485", ADIS16485 },
+	{ "adis16488", ADIS16488 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adis16480_ids);
+
+static struct spi_driver adis16480_driver = {
+	.driver = {
+		.name = "adis16480",
+	},
+	.id_table = adis16480_ids,
+	.probe = adis16480_probe,
+	.remove = adis16480_remove,
+};
+module_spi_driver(adis16480_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
new file mode 100644
index 0000000..76643c5
--- /dev/null
+++ b/drivers/iio/imu/adis_buffer.c
@@ -0,0 +1,171 @@
+/*
+ * Common library for ADIS16XXX devices
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/imu/adis.h>
+
+int adis_update_scan_mode(struct iio_dev *indio_dev,
+	const unsigned long *scan_mask)
+{
+	struct adis *adis = iio_device_get_drvdata(indio_dev);
+	const struct iio_chan_spec *chan;
+	unsigned int scan_count;
+	unsigned int i, j;
+	__be16 *tx, *rx;
+
+	kfree(adis->xfer);
+	kfree(adis->buffer);
+
+	scan_count = indio_dev->scan_bytes / 2;
+
+	adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL);
+	if (!adis->xfer)
+		return -ENOMEM;
+
+	adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL);
+	if (!adis->buffer)
+		return -ENOMEM;
+
+	rx = adis->buffer;
+	tx = rx + scan_count;
+
+	spi_message_init(&adis->msg);
+
+	for (j = 0; j <= scan_count; j++) {
+		adis->xfer[j].bits_per_word = 8;
+		if (j != scan_count)
+			adis->xfer[j].cs_change = 1;
+		adis->xfer[j].len = 2;
+		adis->xfer[j].delay_usecs = adis->data->read_delay;
+		if (j < scan_count)
+			adis->xfer[j].tx_buf = &tx[j];
+		if (j >= 1)
+			adis->xfer[j].rx_buf = &rx[j - 1];
+		spi_message_add_tail(&adis->xfer[j], &adis->msg);
+	}
+
+	chan = indio_dev->channels;
+	for (i = 0; i < indio_dev->num_channels; i++, chan++) {
+		if (!test_bit(chan->scan_index, scan_mask))
+			continue;
+		if (chan->scan_type.storagebits == 32)
+			*tx++ = cpu_to_be16((chan->address + 2) << 8);
+		*tx++ = cpu_to_be16(chan->address << 8);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adis_update_scan_mode);
+
+static irqreturn_t adis_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct adis *adis = iio_device_get_drvdata(indio_dev);
+	int ret;
+
+	if (!adis->buffer)
+		return -ENOMEM;
+
+	if (adis->data->has_paging) {
+		mutex_lock(&adis->txrx_lock);
+		if (adis->current_page != 0) {
+			adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
+			adis->tx[1] = 0;
+			spi_write(adis->spi, adis->tx, 2);
+		}
+	}
+
+	ret = spi_sync(adis->spi, &adis->msg);
+	if (ret)
+		dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
+
+
+	if (adis->data->has_paging) {
+		adis->current_page = 0;
+		mutex_unlock(&adis->txrx_lock);
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
+		pf->timestamp);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device
+ * @adis: The adis device.
+ * @indio_dev: The IIO device.
+ * @trigger_handler: Optional trigger handler, may be NULL.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ *
+ * This function sets up the buffer and trigger for a adis devices.  If
+ * 'trigger_handler' is NULL the default trigger handler will be used. The
+ * default trigger handler will simply read the registers assigned to the
+ * currently active channels.
+ *
+ * adis_cleanup_buffer_and_trigger() should be called to free the resources
+ * allocated by this function.
+ */
+int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
+	irqreturn_t (*trigger_handler)(int, void *))
+{
+	int ret;
+
+	if (!trigger_handler)
+		trigger_handler = adis_trigger_handler;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		trigger_handler, NULL);
+	if (ret)
+		return ret;
+
+	if (adis->spi->irq) {
+		ret = adis_probe_trigger(adis, indio_dev);
+		if (ret)
+			goto error_buffer_cleanup;
+	}
+	return 0;
+
+error_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger);
+
+/**
+ * adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources
+ * @adis: The adis device.
+ * @indio_dev: The IIO device.
+ *
+ * Frees resources allocated by adis_setup_buffer_and_trigger()
+ */
+void adis_cleanup_buffer_and_trigger(struct adis *adis,
+	struct iio_dev *indio_dev)
+{
+	if (adis->spi->irq)
+		adis_remove_trigger(adis);
+	kfree(adis->buffer);
+	kfree(adis->xfer);
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger);
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
new file mode 100644
index 0000000..457372f
--- /dev/null
+++ b/drivers/iio/imu/adis_trigger.c
@@ -0,0 +1,89 @@
+/*
+ * Common library for ADIS16XXX devices
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/export.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/imu/adis.h>
+
+static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						bool state)
+{
+	struct adis *adis = iio_trigger_get_drvdata(trig);
+
+	return adis_enable_irq(adis, state);
+}
+
+static const struct iio_trigger_ops adis_trigger_ops = {
+	.set_trigger_state = &adis_data_rdy_trigger_set_state,
+};
+
+/**
+ * adis_probe_trigger() - Sets up trigger for a adis device
+ * @adis: The adis device
+ * @indio_dev: The IIO device
+ *
+ * Returns 0 on success or a negative error code
+ *
+ * adis_remove_trigger() should be used to free the trigger.
+ */
+int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
+{
+	int ret;
+
+	adis->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
+					indio_dev->id);
+	if (adis->trig == NULL)
+		return -ENOMEM;
+
+	adis->trig->dev.parent = &adis->spi->dev;
+	adis->trig->ops = &adis_trigger_ops;
+	iio_trigger_set_drvdata(adis->trig, adis);
+
+	ret = request_irq(adis->spi->irq,
+			  &iio_trigger_generic_data_rdy_poll,
+			  IRQF_TRIGGER_RISING,
+			  indio_dev->name,
+			  adis->trig);
+	if (ret)
+		goto error_free_trig;
+
+	ret = iio_trigger_register(adis->trig);
+
+	indio_dev->trig = iio_trigger_get(adis->trig);
+	if (ret)
+		goto error_free_irq;
+
+	return 0;
+
+error_free_irq:
+	free_irq(adis->spi->irq, adis->trig);
+error_free_trig:
+	iio_trigger_free(adis->trig);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(adis_probe_trigger);
+
+/**
+ * adis_remove_trigger() - Remove trigger for a adis devices
+ * @adis: The adis device
+ *
+ * Removes the trigger previously registered with adis_probe_trigger().
+ */
+void adis_remove_trigger(struct adis *adis)
+{
+	iio_trigger_unregister(adis->trig);
+	free_irq(adis->spi->irq, adis->trig);
+	iio_trigger_free(adis->trig);
+}
+EXPORT_SYMBOL_GPL(adis_remove_trigger);
diff --git a/drivers/iio/imu/bmi160/Kconfig b/drivers/iio/imu/bmi160/Kconfig
new file mode 100644
index 0000000..005c17c
--- /dev/null
+++ b/drivers/iio/imu/bmi160/Kconfig
@@ -0,0 +1,32 @@
+#
+# BMI160 IMU driver
+#
+
+config BMI160
+	tristate
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+
+config BMI160_I2C
+	tristate "Bosch BMI160 I2C driver"
+	depends on I2C
+	select BMI160
+	select REGMAP_I2C
+	help
+	  If you say yes here you get support for BMI160 IMU on I2C with
+	  accelerometer, gyroscope and external BMG160 magnetometer.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bmi160_i2c.
+
+config BMI160_SPI
+	tristate "Bosch BMI160 SPI driver"
+	depends on SPI
+	select BMI160
+	select REGMAP_SPI
+	help
+	  If you say yes here you get support for BMI160 IMU on SPI with
+	  accelerometer, gyroscope and external BMG160 magnetometer.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called bmi160_spi.
diff --git a/drivers/iio/imu/bmi160/Makefile b/drivers/iio/imu/bmi160/Makefile
new file mode 100644
index 0000000..10365e4
--- /dev/null
+++ b/drivers/iio/imu/bmi160/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Bosch BMI160 IMU
+#
+obj-$(CONFIG_BMI160) += bmi160_core.o
+obj-$(CONFIG_BMI160_I2C) += bmi160_i2c.o
+obj-$(CONFIG_BMI160_SPI) += bmi160_spi.o
diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
new file mode 100644
index 0000000..e7b11e7
--- /dev/null
+++ b/drivers/iio/imu/bmi160/bmi160.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef BMI160_H_
+#define BMI160_H_
+
+extern const struct regmap_config bmi160_regmap_config;
+
+int bmi160_core_probe(struct device *dev, struct regmap *regmap,
+		      const char *name, bool use_spi);
+void bmi160_core_remove(struct device *dev);
+
+#endif  /* BMI160_H_ */
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
new file mode 100644
index 0000000..c85659c
--- /dev/null
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -0,0 +1,611 @@
+/*
+ * BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO core driver for BMI160, with support for I2C/SPI busses
+ *
+ * TODO: magnetometer, interrupts, hardware FIFO
+ */
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+
+#include "bmi160.h"
+
+#define BMI160_REG_CHIP_ID	0x00
+#define BMI160_CHIP_ID_VAL	0xD1
+
+#define BMI160_REG_PMU_STATUS	0x03
+
+/* X axis data low byte address, the rest can be obtained using axis offset */
+#define BMI160_REG_DATA_MAGN_XOUT_L	0x04
+#define BMI160_REG_DATA_GYRO_XOUT_L	0x0C
+#define BMI160_REG_DATA_ACCEL_XOUT_L	0x12
+
+#define BMI160_REG_ACCEL_CONFIG		0x40
+#define BMI160_ACCEL_CONFIG_ODR_MASK	GENMASK(3, 0)
+#define BMI160_ACCEL_CONFIG_BWP_MASK	GENMASK(6, 4)
+
+#define BMI160_REG_ACCEL_RANGE		0x41
+#define BMI160_ACCEL_RANGE_2G		0x03
+#define BMI160_ACCEL_RANGE_4G		0x05
+#define BMI160_ACCEL_RANGE_8G		0x08
+#define BMI160_ACCEL_RANGE_16G		0x0C
+
+#define BMI160_REG_GYRO_CONFIG		0x42
+#define BMI160_GYRO_CONFIG_ODR_MASK	GENMASK(3, 0)
+#define BMI160_GYRO_CONFIG_BWP_MASK	GENMASK(5, 4)
+
+#define BMI160_REG_GYRO_RANGE		0x43
+#define BMI160_GYRO_RANGE_2000DPS	0x00
+#define BMI160_GYRO_RANGE_1000DPS	0x01
+#define BMI160_GYRO_RANGE_500DPS	0x02
+#define BMI160_GYRO_RANGE_250DPS	0x03
+#define BMI160_GYRO_RANGE_125DPS	0x04
+
+#define BMI160_REG_CMD			0x7E
+#define BMI160_CMD_ACCEL_PM_SUSPEND	0x10
+#define BMI160_CMD_ACCEL_PM_NORMAL	0x11
+#define BMI160_CMD_ACCEL_PM_LOW_POWER	0x12
+#define BMI160_CMD_GYRO_PM_SUSPEND	0x14
+#define BMI160_CMD_GYRO_PM_NORMAL	0x15
+#define BMI160_CMD_GYRO_PM_FAST_STARTUP	0x17
+#define BMI160_CMD_SOFTRESET		0xB6
+
+#define BMI160_REG_DUMMY		0x7F
+
+#define BMI160_ACCEL_PMU_MIN_USLEEP	3800
+#define BMI160_GYRO_PMU_MIN_USLEEP	80000
+#define BMI160_SOFTRESET_USLEEP		1000
+
+#define BMI160_CHANNEL(_type, _axis, _index) {			\
+	.type = _type,						\
+	.modified = 1,						\
+	.channel2 = IIO_MOD_##_axis,				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
+		BIT(IIO_CHAN_INFO_SAMP_FREQ),			\
+	.scan_index = _index,					\
+	.scan_type = {						\
+		.sign = 's',					\
+		.realbits = 16,					\
+		.storagebits = 16,				\
+		.endianness = IIO_LE,				\
+	},							\
+}
+
+/* scan indexes follow DATA register order */
+enum bmi160_scan_axis {
+	BMI160_SCAN_EXT_MAGN_X = 0,
+	BMI160_SCAN_EXT_MAGN_Y,
+	BMI160_SCAN_EXT_MAGN_Z,
+	BMI160_SCAN_RHALL,
+	BMI160_SCAN_GYRO_X,
+	BMI160_SCAN_GYRO_Y,
+	BMI160_SCAN_GYRO_Z,
+	BMI160_SCAN_ACCEL_X,
+	BMI160_SCAN_ACCEL_Y,
+	BMI160_SCAN_ACCEL_Z,
+	BMI160_SCAN_TIMESTAMP,
+};
+
+enum bmi160_sensor_type {
+	BMI160_ACCEL	= 0,
+	BMI160_GYRO,
+	BMI160_EXT_MAGN,
+	BMI160_NUM_SENSORS /* must be last */
+};
+
+struct bmi160_data {
+	struct regmap *regmap;
+};
+
+const struct regmap_config bmi160_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+EXPORT_SYMBOL(bmi160_regmap_config);
+
+struct bmi160_regs {
+	u8 data; /* LSB byte register for X-axis */
+	u8 config;
+	u8 config_odr_mask;
+	u8 config_bwp_mask;
+	u8 range;
+	u8 pmu_cmd_normal;
+	u8 pmu_cmd_suspend;
+};
+
+static struct bmi160_regs bmi160_regs[] = {
+	[BMI160_ACCEL] = {
+		.data	= BMI160_REG_DATA_ACCEL_XOUT_L,
+		.config	= BMI160_REG_ACCEL_CONFIG,
+		.config_odr_mask = BMI160_ACCEL_CONFIG_ODR_MASK,
+		.config_bwp_mask = BMI160_ACCEL_CONFIG_BWP_MASK,
+		.range	= BMI160_REG_ACCEL_RANGE,
+		.pmu_cmd_normal = BMI160_CMD_ACCEL_PM_NORMAL,
+		.pmu_cmd_suspend = BMI160_CMD_ACCEL_PM_SUSPEND,
+	},
+	[BMI160_GYRO] = {
+		.data	= BMI160_REG_DATA_GYRO_XOUT_L,
+		.config	= BMI160_REG_GYRO_CONFIG,
+		.config_odr_mask = BMI160_GYRO_CONFIG_ODR_MASK,
+		.config_bwp_mask = BMI160_GYRO_CONFIG_BWP_MASK,
+		.range	= BMI160_REG_GYRO_RANGE,
+		.pmu_cmd_normal = BMI160_CMD_GYRO_PM_NORMAL,
+		.pmu_cmd_suspend = BMI160_CMD_GYRO_PM_SUSPEND,
+	},
+};
+
+static unsigned long bmi160_pmu_time[] = {
+	[BMI160_ACCEL] = BMI160_ACCEL_PMU_MIN_USLEEP,
+	[BMI160_GYRO] = BMI160_GYRO_PMU_MIN_USLEEP,
+};
+
+struct bmi160_scale {
+	u8 bits;
+	int uscale;
+};
+
+struct bmi160_odr {
+	u8 bits;
+	int odr;
+	int uodr;
+};
+
+static const struct bmi160_scale bmi160_accel_scale[] = {
+	{ BMI160_ACCEL_RANGE_2G, 598},
+	{ BMI160_ACCEL_RANGE_4G, 1197},
+	{ BMI160_ACCEL_RANGE_8G, 2394},
+	{ BMI160_ACCEL_RANGE_16G, 4788},
+};
+
+static const struct bmi160_scale bmi160_gyro_scale[] = {
+	{ BMI160_GYRO_RANGE_2000DPS, 1065},
+	{ BMI160_GYRO_RANGE_1000DPS, 532},
+	{ BMI160_GYRO_RANGE_500DPS, 266},
+	{ BMI160_GYRO_RANGE_250DPS, 133},
+	{ BMI160_GYRO_RANGE_125DPS, 66},
+};
+
+struct bmi160_scale_item {
+	const struct bmi160_scale *tbl;
+	int num;
+};
+
+static const struct  bmi160_scale_item bmi160_scale_table[] = {
+	[BMI160_ACCEL] = {
+		.tbl	= bmi160_accel_scale,
+		.num	= ARRAY_SIZE(bmi160_accel_scale),
+	},
+	[BMI160_GYRO] = {
+		.tbl	= bmi160_gyro_scale,
+		.num	= ARRAY_SIZE(bmi160_gyro_scale),
+	},
+};
+
+static const struct bmi160_odr bmi160_accel_odr[] = {
+	{0x01, 0, 781250},
+	{0x02, 1, 562500},
+	{0x03, 3, 125000},
+	{0x04, 6, 250000},
+	{0x05, 12, 500000},
+	{0x06, 25, 0},
+	{0x07, 50, 0},
+	{0x08, 100, 0},
+	{0x09, 200, 0},
+	{0x0A, 400, 0},
+	{0x0B, 800, 0},
+	{0x0C, 1600, 0},
+};
+
+static const struct bmi160_odr bmi160_gyro_odr[] = {
+	{0x06, 25, 0},
+	{0x07, 50, 0},
+	{0x08, 100, 0},
+	{0x09, 200, 0},
+	{0x0A, 400, 0},
+	{0x0B, 800, 0},
+	{0x0C, 1600, 0},
+	{0x0D, 3200, 0},
+};
+
+struct bmi160_odr_item {
+	const struct bmi160_odr *tbl;
+	int num;
+};
+
+static const struct  bmi160_odr_item bmi160_odr_table[] = {
+	[BMI160_ACCEL] = {
+		.tbl	= bmi160_accel_odr,
+		.num	= ARRAY_SIZE(bmi160_accel_odr),
+	},
+	[BMI160_GYRO] = {
+		.tbl	= bmi160_gyro_odr,
+		.num	= ARRAY_SIZE(bmi160_gyro_odr),
+	},
+};
+
+static const struct iio_chan_spec bmi160_channels[] = {
+	BMI160_CHANNEL(IIO_ACCEL, X, BMI160_SCAN_ACCEL_X),
+	BMI160_CHANNEL(IIO_ACCEL, Y, BMI160_SCAN_ACCEL_Y),
+	BMI160_CHANNEL(IIO_ACCEL, Z, BMI160_SCAN_ACCEL_Z),
+	BMI160_CHANNEL(IIO_ANGL_VEL, X, BMI160_SCAN_GYRO_X),
+	BMI160_CHANNEL(IIO_ANGL_VEL, Y, BMI160_SCAN_GYRO_Y),
+	BMI160_CHANNEL(IIO_ANGL_VEL, Z, BMI160_SCAN_GYRO_Z),
+	IIO_CHAN_SOFT_TIMESTAMP(BMI160_SCAN_TIMESTAMP),
+};
+
+static enum bmi160_sensor_type bmi160_to_sensor(enum iio_chan_type iio_type)
+{
+	switch (iio_type) {
+	case IIO_ACCEL:
+		return BMI160_ACCEL;
+	case IIO_ANGL_VEL:
+		return BMI160_GYRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static
+int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
+		    bool mode)
+{
+	int ret;
+	u8 cmd;
+
+	if (mode)
+		cmd = bmi160_regs[t].pmu_cmd_normal;
+	else
+		cmd = bmi160_regs[t].pmu_cmd_suspend;
+
+	ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
+
+	return 0;
+}
+
+static
+int bmi160_set_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
+		     int uscale)
+{
+	int i;
+
+	for (i = 0; i < bmi160_scale_table[t].num; i++)
+		if (bmi160_scale_table[t].tbl[i].uscale == uscale)
+			break;
+
+	if (i == bmi160_scale_table[t].num)
+		return -EINVAL;
+
+	return regmap_write(data->regmap, bmi160_regs[t].range,
+			    bmi160_scale_table[t].tbl[i].bits);
+}
+
+static
+int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
+		     int *uscale)
+{
+	int i, ret, val;
+
+	ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < bmi160_scale_table[t].num; i++)
+		if (bmi160_scale_table[t].tbl[i].bits == val) {
+			*uscale = bmi160_scale_table[t].tbl[i].uscale;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static int bmi160_get_data(struct bmi160_data *data, int chan_type,
+			   int axis, int *val)
+{
+	u8 reg;
+	int ret;
+	__le16 sample;
+	enum bmi160_sensor_type t = bmi160_to_sensor(chan_type);
+
+	reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
+
+	ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
+	if (ret < 0)
+		return ret;
+
+	*val = sign_extend32(le16_to_cpu(sample), 15);
+
+	return 0;
+}
+
+static
+int bmi160_set_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
+		   int odr, int uodr)
+{
+	int i;
+
+	for (i = 0; i < bmi160_odr_table[t].num; i++)
+		if (bmi160_odr_table[t].tbl[i].odr == odr &&
+		    bmi160_odr_table[t].tbl[i].uodr == uodr)
+			break;
+
+	if (i >= bmi160_odr_table[t].num)
+		return -EINVAL;
+
+	return regmap_update_bits(data->regmap,
+				  bmi160_regs[t].config,
+				  bmi160_regs[t].config_odr_mask,
+				  bmi160_odr_table[t].tbl[i].bits);
+}
+
+static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
+			  int *odr, int *uodr)
+{
+	int i, val, ret;
+
+	ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
+	if (ret < 0)
+		return ret;
+
+	val &= bmi160_regs[t].config_odr_mask;
+
+	for (i = 0; i < bmi160_odr_table[t].num; i++)
+		if (val == bmi160_odr_table[t].tbl[i].bits)
+			break;
+
+	if (i >= bmi160_odr_table[t].num)
+		return -EINVAL;
+
+	*odr = bmi160_odr_table[t].tbl[i].odr;
+	*uodr = bmi160_odr_table[t].tbl[i].uodr;
+
+	return 0;
+}
+
+static irqreturn_t bmi160_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct bmi160_data *data = iio_priv(indio_dev);
+	__le16 buf[16];
+	/* 3 sens x 3 axis x __le16 + 3 x __le16 pad + 4 x __le16 tstamp */
+	int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
+	__le16 sample;
+
+	for_each_set_bit(i, indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
+				       &sample, sizeof(sample));
+		if (ret < 0)
+			goto done;
+		buf[j++] = sample;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buf,
+					   iio_get_time_ns(indio_dev));
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int bmi160_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val, int *val2, long mask)
+{
+	int ret;
+	struct bmi160_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = bmi160_get_data(data, chan->type, chan->channel2, val);
+		if (ret < 0)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		ret = bmi160_get_scale(data,
+				       bmi160_to_sensor(chan->type), val2);
+		return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
+				     val, val2);
+		return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int bmi160_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct bmi160_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		return bmi160_set_scale(data,
+					bmi160_to_sensor(chan->type), val2);
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return bmi160_set_odr(data, bmi160_to_sensor(chan->type),
+				      val, val2);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static
+IIO_CONST_ATTR(in_accel_sampling_frequency_available,
+	       "0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600");
+static
+IIO_CONST_ATTR(in_anglvel_sampling_frequency_available,
+	       "25 50 100 200 400 800 1600 3200");
+static
+IIO_CONST_ATTR(in_accel_scale_available,
+	       "0.000598 0.001197 0.002394 0.004788");
+static
+IIO_CONST_ATTR(in_anglvel_scale_available,
+	       "0.001065 0.000532 0.000266 0.000133 0.000066");
+
+static struct attribute *bmi160_attrs[] = {
+	&iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr,
+	&iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr,
+	&iio_const_attr_in_accel_scale_available.dev_attr.attr,
+	&iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group bmi160_attrs_group = {
+	.attrs = bmi160_attrs,
+};
+
+static const struct iio_info bmi160_info = {
+	.read_raw = bmi160_read_raw,
+	.write_raw = bmi160_write_raw,
+	.attrs = &bmi160_attrs_group,
+};
+
+static const char *bmi160_match_acpi_device(struct device *dev)
+{
+	const struct acpi_device_id *id;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return NULL;
+
+	return dev_name(dev);
+}
+
+static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
+{
+	int ret;
+	unsigned int val;
+	struct device *dev = regmap_get_device(data->regmap);
+
+	ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
+
+	/*
+	 * CS rising edge is needed before starting SPI, so do a dummy read
+	 * See Section 3.2.1, page 86 of the datasheet
+	 */
+	if (use_spi) {
+		ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
+	if (ret < 0) {
+		dev_err(dev, "Error reading chip id\n");
+		return ret;
+	}
+	if (val != BMI160_CHIP_ID_VAL) {
+		dev_err(dev, "Wrong chip id, got %x expected %x\n",
+			val, BMI160_CHIP_ID_VAL);
+		return -ENODEV;
+	}
+
+	ret = bmi160_set_mode(data, BMI160_ACCEL, true);
+	if (ret < 0)
+		return ret;
+
+	ret = bmi160_set_mode(data, BMI160_GYRO, true);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void bmi160_chip_uninit(struct bmi160_data *data)
+{
+	bmi160_set_mode(data, BMI160_GYRO, false);
+	bmi160_set_mode(data, BMI160_ACCEL, false);
+}
+
+int bmi160_core_probe(struct device *dev, struct regmap *regmap,
+		      const char *name, bool use_spi)
+{
+	struct iio_dev *indio_dev;
+	struct bmi160_data *data;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	dev_set_drvdata(dev, indio_dev);
+	data->regmap = regmap;
+
+	ret = bmi160_chip_init(data, use_spi);
+	if (ret < 0)
+		return ret;
+
+	if (!name && ACPI_HANDLE(dev))
+		name = bmi160_match_acpi_device(dev);
+
+	indio_dev->dev.parent = dev;
+	indio_dev->channels = bmi160_channels;
+	indio_dev->num_channels = ARRAY_SIZE(bmi160_channels);
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &bmi160_info;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					 bmi160_trigger_handler, NULL);
+	if (ret < 0)
+		goto uninit;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto buffer_cleanup;
+
+	return 0;
+buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+uninit:
+	bmi160_chip_uninit(data);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(bmi160_core_probe);
+
+void bmi160_core_remove(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct bmi160_data *data = iio_priv(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	bmi160_chip_uninit(data);
+}
+EXPORT_SYMBOL_GPL(bmi160_core_remove);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
+MODULE_DESCRIPTION("Bosch BMI160 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c
new file mode 100644
index 0000000..155a31f
--- /dev/null
+++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
@@ -0,0 +1,82 @@
+/*
+ * BMI160 - Bosch IMU, I2C bits
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * 7-bit I2C slave address is:
+ *      - 0x68 if SDO is pulled to GND
+ *      - 0x69 if SDO is pulled to VDDIO
+ */
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "bmi160.h"
+
+static int bmi160_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct regmap *regmap;
+	const char *name = NULL;
+
+	regmap = devm_regmap_init_i2c(client, &bmi160_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+
+	if (id)
+		name = id->name;
+
+	return bmi160_core_probe(&client->dev, regmap, name, false);
+}
+
+static int bmi160_i2c_remove(struct i2c_client *client)
+{
+	bmi160_core_remove(&client->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id bmi160_i2c_id[] = {
+	{"bmi160", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id);
+
+static const struct acpi_device_id bmi160_acpi_match[] = {
+	{"BMI0160", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bmi160_of_match[] = {
+	{ .compatible = "bosch,bmi160" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bmi160_of_match);
+#endif
+
+static struct i2c_driver bmi160_i2c_driver = {
+	.driver = {
+		.name			= "bmi160_i2c",
+		.acpi_match_table	= ACPI_PTR(bmi160_acpi_match),
+		.of_match_table		= of_match_ptr(bmi160_of_match),
+	},
+	.probe		= bmi160_i2c_probe,
+	.remove		= bmi160_i2c_remove,
+	.id_table	= bmi160_i2c_id,
+};
+module_i2c_driver(bmi160_i2c_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("BMI160 I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
new file mode 100644
index 0000000..d34dfdf
--- /dev/null
+++ b/drivers/iio/imu/bmi160/bmi160_spi.c
@@ -0,0 +1,73 @@
+/*
+ * BMI160 - Bosch IMU, SPI bits
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ */
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "bmi160.h"
+
+static int bmi160_spi_probe(struct spi_device *spi)
+{
+	struct regmap *regmap;
+	const struct spi_device_id *id = spi_get_device_id(spi);
+
+	regmap = devm_regmap_init_spi(spi, &bmi160_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+	return bmi160_core_probe(&spi->dev, regmap, id->name, true);
+}
+
+static int bmi160_spi_remove(struct spi_device *spi)
+{
+	bmi160_core_remove(&spi->dev);
+
+	return 0;
+}
+
+static const struct spi_device_id bmi160_spi_id[] = {
+	{"bmi160", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, bmi160_spi_id);
+
+static const struct acpi_device_id bmi160_acpi_match[] = {
+	{"BMI0160", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bmi160_of_match[] = {
+	{ .compatible = "bosch,bmi160" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bmi160_of_match);
+#endif
+
+static struct spi_driver bmi160_spi_driver = {
+	.probe		= bmi160_spi_probe,
+	.remove		= bmi160_spi_remove,
+	.id_table	= bmi160_spi_id,
+	.driver = {
+		.acpi_match_table	= ACPI_PTR(bmi160_acpi_match),
+		.of_match_table		= of_match_ptr(bmi160_of_match),
+		.name			= "bmi160_spi",
+	},
+};
+module_spi_driver(bmi160_spi_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
+MODULE_DESCRIPTION("Bosch BMI160 SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig
new file mode 100644
index 0000000..5483b2e
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/Kconfig
@@ -0,0 +1,30 @@
+#
+# inv-mpu6050 drivers for Invensense MPU devices and combos
+#
+
+config INV_MPU6050_IIO
+	tristate
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+
+config INV_MPU6050_I2C
+	tristate "Invensense MPU6050 devices (I2C)"
+	depends on I2C_MUX
+	select INV_MPU6050_IIO
+	select REGMAP_I2C
+	help
+	  This driver supports the Invensense MPU6050/6500/9150 and ICM20608
+	  motion tracking devices over I2C.
+	  This driver can be built as a module. The module will be called
+	  inv-mpu6050-i2c.
+
+config INV_MPU6050_SPI
+	tristate "Invensense MPU6050 devices (SPI)"
+	depends on SPI_MASTER
+	select INV_MPU6050_IIO
+	select REGMAP_SPI
+	help
+	  This driver supports the Invensense MPU6050/6500/9150 and ICM20608
+	  motion tracking devices over SPI.
+	  This driver can be built as a module. The module will be called
+	  inv-mpu6050-spi.
diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile
new file mode 100644
index 0000000..70ffe0d
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Invensense MPU6050 device.
+#
+
+obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
+inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
+
+obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o
+inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o
+
+obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o
+inv-mpu6050-spi-objs := inv_mpu_spi.o
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
new file mode 100644
index 0000000..d78a104
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
@@ -0,0 +1,213 @@
+/*
+ * inv_mpu_acpi: ACPI processing for creating client devices
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifdef CONFIG_ACPI
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include "inv_mpu_iio.h"
+
+enum inv_mpu_product_name {
+	INV_MPU_NOT_MATCHED,
+	INV_MPU_ASUS_T100TA,
+};
+
+static enum inv_mpu_product_name matched_product_name;
+
+static int __init asus_t100_matched(const struct dmi_system_id *d)
+{
+	matched_product_name = INV_MPU_ASUS_T100TA;
+
+	return 0;
+}
+
+static const struct dmi_system_id inv_mpu_dev_list[] = {
+	{
+	.callback = asus_t100_matched,
+	.ident = "Asus Transformer Book T100",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
+		},
+	},
+	/* Add more matching tables here..*/
+	{}
+};
+
+static int asus_acpi_get_sensor_info(struct acpi_device *adev,
+				     struct i2c_client *client,
+				     struct i2c_board_info *info)
+{
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	int i;
+	acpi_status status;
+	union acpi_object *cpm;
+	int ret;
+
+	status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	cpm = buffer.pointer;
+	for (i = 0; i < cpm->package.count; ++i) {
+		union acpi_object *elem;
+		int j;
+
+		elem = &cpm->package.elements[i];
+		for (j = 0; j < elem->package.count; ++j) {
+			union acpi_object *sub_elem;
+
+			sub_elem = &elem->package.elements[j];
+			if (sub_elem->type == ACPI_TYPE_STRING)
+				strlcpy(info->type, sub_elem->string.pointer,
+					sizeof(info->type));
+			else if (sub_elem->type == ACPI_TYPE_INTEGER) {
+				if (sub_elem->integer.value != client->addr) {
+					info->addr = sub_elem->integer.value;
+					break; /* Not a MPU6500 primary */
+				}
+			}
+		}
+	}
+	ret = cpm->package.count;
+	kfree(buffer.pointer);
+
+	return ret;
+}
+
+static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
+{
+	u32 *addr = data;
+
+	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+		struct acpi_resource_i2c_serialbus *sb;
+
+		sb = &ares->data.i2c_serial_bus;
+		if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
+			if (*addr)
+				*addr |= (sb->slave_address << 16);
+			else
+				*addr = sb->slave_address;
+		}
+	}
+
+	/* Tell the ACPI core that we already copied this address */
+	return 1;
+}
+
+static int inv_mpu_process_acpi_config(struct i2c_client *client,
+				       unsigned short *primary_addr,
+				       unsigned short *secondary_addr)
+{
+	const struct acpi_device_id *id;
+	struct acpi_device *adev;
+	u32 i2c_addr = 0;
+	LIST_HEAD(resources);
+	int ret;
+
+	id = acpi_match_device(client->dev.driver->acpi_match_table,
+			       &client->dev);
+	if (!id)
+		return -ENODEV;
+
+	adev = ACPI_COMPANION(&client->dev);
+	if (!adev)
+		return -ENODEV;
+
+	ret = acpi_dev_get_resources(adev, &resources,
+				     acpi_i2c_check_resource, &i2c_addr);
+	if (ret < 0)
+		return ret;
+
+	acpi_dev_free_resource_list(&resources);
+	*primary_addr = i2c_addr & 0x0000ffff;
+	*secondary_addr = (i2c_addr & 0xffff0000) >> 16;
+
+	return 0;
+}
+
+int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
+{
+	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
+
+	st->mux_client = NULL;
+	if (ACPI_HANDLE(&client->dev)) {
+		struct i2c_board_info info;
+		struct acpi_device *adev;
+		int ret = -1;
+
+		adev = ACPI_COMPANION(&client->dev);
+		memset(&info, 0, sizeof(info));
+
+		dmi_check_system(inv_mpu_dev_list);
+		switch (matched_product_name) {
+		case INV_MPU_ASUS_T100TA:
+			ret = asus_acpi_get_sensor_info(adev, client,
+							&info);
+			break;
+		/* Add more matched product processing here */
+		default:
+			break;
+		}
+
+		if (ret < 0) {
+			/* No matching DMI, so create device on INV6XX type */
+			unsigned short primary, secondary;
+
+			ret = inv_mpu_process_acpi_config(client, &primary,
+							  &secondary);
+			if (!ret && secondary) {
+				char *name;
+
+				info.addr = secondary;
+				strlcpy(info.type, dev_name(&adev->dev),
+					sizeof(info.type));
+				name = strchr(info.type, ':');
+				if (name)
+					*name = '\0';
+				strlcat(info.type, "-client",
+					sizeof(info.type));
+			} else
+				return 0; /* no secondary addr, which is OK */
+		}
+		st->mux_client = i2c_new_device(st->muxc->adapter[0], &info);
+		if (!st->mux_client)
+			return -ENODEV;
+	}
+
+	return 0;
+}
+
+void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
+{
+	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
+
+	i2c_unregister_device(st->mux_client);
+}
+#else
+
+#include "inv_mpu_iio.h"
+
+int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
+{
+	return 0;
+}
+
+void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
+{
+}
+#endif
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
new file mode 100644
index 0000000..d80ef46
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -0,0 +1,1078 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include "inv_mpu_iio.h"
+
+/*
+ * this is the gyro scale translated from dynamic range plus/minus
+ * {250, 500, 1000, 2000} to rad/s
+ */
+static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
+
+/*
+ * this is the accel scale translated from dynamic range plus/minus
+ * {2, 4, 8, 16} to m/s^2
+ */
+static const int accel_scale[] = {598, 1196, 2392, 4785};
+
+static const struct inv_mpu6050_reg_map reg_set_6500 = {
+	.sample_rate_div	= INV_MPU6050_REG_SAMPLE_RATE_DIV,
+	.lpf                    = INV_MPU6050_REG_CONFIG,
+	.accel_lpf              = INV_MPU6500_REG_ACCEL_CONFIG_2,
+	.user_ctrl              = INV_MPU6050_REG_USER_CTRL,
+	.fifo_en                = INV_MPU6050_REG_FIFO_EN,
+	.gyro_config            = INV_MPU6050_REG_GYRO_CONFIG,
+	.accl_config            = INV_MPU6050_REG_ACCEL_CONFIG,
+	.fifo_count_h           = INV_MPU6050_REG_FIFO_COUNT_H,
+	.fifo_r_w               = INV_MPU6050_REG_FIFO_R_W,
+	.raw_gyro               = INV_MPU6050_REG_RAW_GYRO,
+	.raw_accl               = INV_MPU6050_REG_RAW_ACCEL,
+	.temperature            = INV_MPU6050_REG_TEMPERATURE,
+	.int_enable             = INV_MPU6050_REG_INT_ENABLE,
+	.int_status             = INV_MPU6050_REG_INT_STATUS,
+	.pwr_mgmt_1             = INV_MPU6050_REG_PWR_MGMT_1,
+	.pwr_mgmt_2             = INV_MPU6050_REG_PWR_MGMT_2,
+	.int_pin_cfg		= INV_MPU6050_REG_INT_PIN_CFG,
+	.accl_offset		= INV_MPU6500_REG_ACCEL_OFFSET,
+	.gyro_offset		= INV_MPU6050_REG_GYRO_OFFSET,
+};
+
+static const struct inv_mpu6050_reg_map reg_set_6050 = {
+	.sample_rate_div	= INV_MPU6050_REG_SAMPLE_RATE_DIV,
+	.lpf                    = INV_MPU6050_REG_CONFIG,
+	.user_ctrl              = INV_MPU6050_REG_USER_CTRL,
+	.fifo_en                = INV_MPU6050_REG_FIFO_EN,
+	.gyro_config            = INV_MPU6050_REG_GYRO_CONFIG,
+	.accl_config            = INV_MPU6050_REG_ACCEL_CONFIG,
+	.fifo_count_h           = INV_MPU6050_REG_FIFO_COUNT_H,
+	.fifo_r_w               = INV_MPU6050_REG_FIFO_R_W,
+	.raw_gyro               = INV_MPU6050_REG_RAW_GYRO,
+	.raw_accl               = INV_MPU6050_REG_RAW_ACCEL,
+	.temperature            = INV_MPU6050_REG_TEMPERATURE,
+	.int_enable             = INV_MPU6050_REG_INT_ENABLE,
+	.pwr_mgmt_1             = INV_MPU6050_REG_PWR_MGMT_1,
+	.pwr_mgmt_2             = INV_MPU6050_REG_PWR_MGMT_2,
+	.int_pin_cfg		= INV_MPU6050_REG_INT_PIN_CFG,
+	.accl_offset		= INV_MPU6050_REG_ACCEL_OFFSET,
+	.gyro_offset		= INV_MPU6050_REG_GYRO_OFFSET,
+};
+
+static const struct inv_mpu6050_chip_config chip_config_6050 = {
+	.fsr = INV_MPU6050_FSR_2000DPS,
+	.lpf = INV_MPU6050_FILTER_20HZ,
+	.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
+	.gyro_fifo_enable = false,
+	.accl_fifo_enable = false,
+	.accl_fs = INV_MPU6050_FS_02G,
+	.user_ctrl = 0,
+};
+
+/* Indexed by enum inv_devices */
+static const struct inv_mpu6050_hw hw_info[] = {
+	{
+		.whoami = INV_MPU6050_WHOAMI_VALUE,
+		.name = "MPU6050",
+		.reg = &reg_set_6050,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_MPU6500_WHOAMI_VALUE,
+		.name = "MPU6500",
+		.reg = &reg_set_6500,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_MPU6515_WHOAMI_VALUE,
+		.name = "MPU6515",
+		.reg = &reg_set_6500,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_MPU6000_WHOAMI_VALUE,
+		.name = "MPU6000",
+		.reg = &reg_set_6050,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_MPU9150_WHOAMI_VALUE,
+		.name = "MPU9150",
+		.reg = &reg_set_6050,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_MPU9250_WHOAMI_VALUE,
+		.name = "MPU9250",
+		.reg = &reg_set_6500,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_MPU9255_WHOAMI_VALUE,
+		.name = "MPU9255",
+		.reg = &reg_set_6500,
+		.config = &chip_config_6050,
+	},
+	{
+		.whoami = INV_ICM20608_WHOAMI_VALUE,
+		.name = "ICM20608",
+		.reg = &reg_set_6500,
+		.config = &chip_config_6050,
+	},
+};
+
+int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
+{
+	unsigned int d, mgmt_1;
+	int result;
+	/*
+	 * switch clock needs to be careful. Only when gyro is on, can
+	 * clock source be switched to gyro. Otherwise, it must be set to
+	 * internal clock
+	 */
+	if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
+		result = regmap_read(st->map, st->reg->pwr_mgmt_1, &mgmt_1);
+		if (result)
+			return result;
+
+		mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK;
+	}
+
+	if ((mask == INV_MPU6050_BIT_PWR_GYRO_STBY) && (!en)) {
+		/*
+		 * turning off gyro requires switch to internal clock first.
+		 * Then turn off gyro engine
+		 */
+		mgmt_1 |= INV_CLK_INTERNAL;
+		result = regmap_write(st->map, st->reg->pwr_mgmt_1, mgmt_1);
+		if (result)
+			return result;
+	}
+
+	result = regmap_read(st->map, st->reg->pwr_mgmt_2, &d);
+	if (result)
+		return result;
+	if (en)
+		d &= ~mask;
+	else
+		d |= mask;
+	result = regmap_write(st->map, st->reg->pwr_mgmt_2, d);
+	if (result)
+		return result;
+
+	if (en) {
+		/* Wait for output to stabilize */
+		msleep(INV_MPU6050_TEMP_UP_TIME);
+		if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
+			/* switch internal clock to PLL */
+			mgmt_1 |= INV_CLK_PLL;
+			result = regmap_write(st->map,
+					      st->reg->pwr_mgmt_1, mgmt_1);
+			if (result)
+				return result;
+		}
+	}
+
+	return 0;
+}
+
+int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
+{
+	int result;
+
+	if (power_on) {
+		if (!st->powerup_count) {
+			result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0);
+			if (result)
+				return result;
+			usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
+				     INV_MPU6050_REG_UP_TIME_MAX);
+		}
+		st->powerup_count++;
+	} else {
+		if (st->powerup_count == 1) {
+			result = regmap_write(st->map, st->reg->pwr_mgmt_1,
+					      INV_MPU6050_BIT_SLEEP);
+			if (result)
+				return result;
+		}
+		st->powerup_count--;
+	}
+
+	dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n",
+		power_on, st->powerup_count);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg);
+
+/**
+ *  inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
+ *
+ *  MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
+ *  MPU6500 and above have a dedicated register for accelerometer
+ */
+static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
+				    enum inv_mpu6050_filter_e val)
+{
+	int result;
+
+	result = regmap_write(st->map, st->reg->lpf, val);
+	if (result)
+		return result;
+
+	switch (st->chip_type) {
+	case INV_MPU6050:
+	case INV_MPU6000:
+	case INV_MPU9150:
+		/* old chips, nothing to do */
+		result = 0;
+		break;
+	default:
+		/* set accel lpf */
+		result = regmap_write(st->map, st->reg->accel_lpf, val);
+		break;
+	}
+
+	return result;
+}
+
+/**
+ *  inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
+ *
+ *  Initial configuration:
+ *  FSR: ± 2000DPS
+ *  DLPF: 20Hz
+ *  FIFO rate: 50Hz
+ *  Clock source: Gyro PLL
+ */
+static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
+{
+	int result;
+	u8 d;
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+	result = inv_mpu6050_set_power_itg(st, true);
+	if (result)
+		return result;
+	d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+	result = regmap_write(st->map, st->reg->gyro_config, d);
+	if (result)
+		goto error_power_off;
+
+	result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ);
+	if (result)
+		goto error_power_off;
+
+	d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE);
+	result = regmap_write(st->map, st->reg->sample_rate_div, d);
+	if (result)
+		goto error_power_off;
+
+	d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+	result = regmap_write(st->map, st->reg->accl_config, d);
+	if (result)
+		goto error_power_off;
+
+	result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
+	if (result)
+		return result;
+
+	memcpy(&st->chip_config, hw_info[st->chip_type].config,
+	       sizeof(struct inv_mpu6050_chip_config));
+
+	/*
+	 * Internal chip period is 1ms (1kHz).
+	 * Let's use at the beginning the theorical value before measuring
+	 * with interrupt timestamps.
+	 */
+	st->chip_period = NSEC_PER_MSEC;
+
+	return inv_mpu6050_set_power_itg(st, false);
+
+error_power_off:
+	inv_mpu6050_set_power_itg(st, false);
+	return result;
+}
+
+static int inv_mpu6050_sensor_set(struct inv_mpu6050_state  *st, int reg,
+				int axis, int val)
+{
+	int ind, result;
+	__be16 d = cpu_to_be16(val);
+
+	ind = (axis - IIO_MOD_X) * 2;
+	result = regmap_bulk_write(st->map, reg + ind, (u8 *)&d, 2);
+	if (result)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int inv_mpu6050_sensor_show(struct inv_mpu6050_state  *st, int reg,
+				   int axis, int *val)
+{
+	int ind, result;
+	__be16 d;
+
+	ind = (axis - IIO_MOD_X) * 2;
+	result = regmap_bulk_read(st->map, reg + ind, (u8 *)&d, 2);
+	if (result)
+		return -EINVAL;
+	*val = (short)be16_to_cpup(&d);
+
+	return IIO_VAL_INT;
+}
+
+static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
+					 struct iio_chan_spec const *chan,
+					 int *val)
+{
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+	int result;
+	int ret;
+
+	result = inv_mpu6050_set_power_itg(st, true);
+	if (result)
+		return result;
+
+	switch (chan->type) {
+	case IIO_ANGL_VEL:
+		result = inv_mpu6050_switch_engine(st, true,
+				INV_MPU6050_BIT_PWR_GYRO_STBY);
+		if (result)
+			goto error_power_off;
+		ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
+					      chan->channel2, val);
+		result = inv_mpu6050_switch_engine(st, false,
+				INV_MPU6050_BIT_PWR_GYRO_STBY);
+		if (result)
+			goto error_power_off;
+		break;
+	case IIO_ACCEL:
+		result = inv_mpu6050_switch_engine(st, true,
+				INV_MPU6050_BIT_PWR_ACCL_STBY);
+		if (result)
+			goto error_power_off;
+		ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
+					      chan->channel2, val);
+		result = inv_mpu6050_switch_engine(st, false,
+				INV_MPU6050_BIT_PWR_ACCL_STBY);
+		if (result)
+			goto error_power_off;
+		break;
+	case IIO_TEMP:
+		/* wait for stablization */
+		msleep(INV_MPU6050_SENSOR_UP_TIME);
+		ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
+					      IIO_MOD_X, val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	result = inv_mpu6050_set_power_itg(st, false);
+	if (result)
+		goto error_power_off;
+
+	return ret;
+
+error_power_off:
+	inv_mpu6050_set_power_itg(st, false);
+	return result;
+}
+
+static int
+inv_mpu6050_read_raw(struct iio_dev *indio_dev,
+		     struct iio_chan_spec const *chan,
+		     int *val, int *val2, long mask)
+{
+	struct inv_mpu6050_state  *st = iio_priv(indio_dev);
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+		mutex_lock(&st->lock);
+		ret = inv_mpu6050_read_channel_data(indio_dev, chan, val);
+		mutex_unlock(&st->lock);
+		iio_device_release_direct_mode(indio_dev);
+		return ret;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			mutex_lock(&st->lock);
+			*val  = 0;
+			*val2 = gyro_scale_6050[st->chip_config.fsr];
+			mutex_unlock(&st->lock);
+
+			return IIO_VAL_INT_PLUS_NANO;
+		case IIO_ACCEL:
+			mutex_lock(&st->lock);
+			*val = 0;
+			*val2 = accel_scale[st->chip_config.accl_fs];
+			mutex_unlock(&st->lock);
+
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_TEMP:
+			*val = 0;
+			*val2 = INV_MPU6050_TEMP_SCALE;
+
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_OFFSET:
+		switch (chan->type) {
+		case IIO_TEMP:
+			*val = INV_MPU6050_TEMP_OFFSET;
+
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_CALIBBIAS:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			mutex_lock(&st->lock);
+			ret = inv_mpu6050_sensor_show(st, st->reg->gyro_offset,
+						chan->channel2, val);
+			mutex_unlock(&st->lock);
+			return IIO_VAL_INT;
+		case IIO_ACCEL:
+			mutex_lock(&st->lock);
+			ret = inv_mpu6050_sensor_show(st, st->reg->accl_offset,
+						chan->channel2, val);
+			mutex_unlock(&st->lock);
+			return IIO_VAL_INT;
+
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val)
+{
+	int result, i;
+	u8 d;
+
+	for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
+		if (gyro_scale_6050[i] == val) {
+			d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+			result = regmap_write(st->map, st->reg->gyro_config, d);
+			if (result)
+				return result;
+
+			st->chip_config.fsr = i;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int inv_write_raw_get_fmt(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan, long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			return IIO_VAL_INT_PLUS_NANO;
+		default:
+			return IIO_VAL_INT_PLUS_MICRO;
+		}
+	default:
+		return IIO_VAL_INT_PLUS_MICRO;
+	}
+
+	return -EINVAL;
+}
+
+static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
+{
+	int result, i;
+	u8 d;
+
+	for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) {
+		if (accel_scale[i] == val) {
+			d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+			result = regmap_write(st->map, st->reg->accl_config, d);
+			if (result)
+				return result;
+
+			st->chip_config.accl_fs = i;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int val, int val2, long mask)
+{
+	struct inv_mpu6050_state  *st = iio_priv(indio_dev);
+	int result;
+
+	/*
+	 * we should only update scale when the chip is disabled, i.e.
+	 * not running
+	 */
+	result = iio_device_claim_direct_mode(indio_dev);
+	if (result)
+		return result;
+
+	mutex_lock(&st->lock);
+	result = inv_mpu6050_set_power_itg(st, true);
+	if (result)
+		goto error_write_raw_unlock;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			result = inv_mpu6050_write_gyro_scale(st, val2);
+			break;
+		case IIO_ACCEL:
+			result = inv_mpu6050_write_accel_scale(st, val2);
+			break;
+		default:
+			result = -EINVAL;
+			break;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			result = inv_mpu6050_sensor_set(st,
+							st->reg->gyro_offset,
+							chan->channel2, val);
+			break;
+		case IIO_ACCEL:
+			result = inv_mpu6050_sensor_set(st,
+							st->reg->accl_offset,
+							chan->channel2, val);
+			break;
+		default:
+			result = -EINVAL;
+			break;
+		}
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+
+	result |= inv_mpu6050_set_power_itg(st, false);
+error_write_raw_unlock:
+	mutex_unlock(&st->lock);
+	iio_device_release_direct_mode(indio_dev);
+
+	return result;
+}
+
+/**
+ *  inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
+ *
+ *                  Based on the Nyquist principle, the sampling rate must
+ *                  exceed twice of the bandwidth of the signal, or there
+ *                  would be alising. This function basically search for the
+ *                  correct low pass parameters based on the fifo rate, e.g,
+ *                  sampling frequency.
+ *
+ *  lpf is set automatically when setting sampling rate to avoid any aliases.
+ */
+static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
+{
+	static const int hz[] = {188, 98, 42, 20, 10, 5};
+	static const int d[] = {
+		INV_MPU6050_FILTER_188HZ, INV_MPU6050_FILTER_98HZ,
+		INV_MPU6050_FILTER_42HZ, INV_MPU6050_FILTER_20HZ,
+		INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ
+	};
+	int i, h, result;
+	u8 data;
+
+	h = (rate >> 1);
+	i = 0;
+	while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
+		i++;
+	data = d[i];
+	result = inv_mpu6050_set_lpf_regs(st, data);
+	if (result)
+		return result;
+	st->chip_config.lpf = data;
+
+	return 0;
+}
+
+/**
+ * inv_mpu6050_fifo_rate_store() - Set fifo rate.
+ */
+static ssize_t
+inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	int fifo_rate;
+	u8 d;
+	int result;
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+	if (kstrtoint(buf, 10, &fifo_rate))
+		return -EINVAL;
+	if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE ||
+	    fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
+		return -EINVAL;
+
+	result = iio_device_claim_direct_mode(indio_dev);
+	if (result)
+		return result;
+
+	/* compute the chip sample rate divider */
+	d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
+	/* compute back the fifo rate to handle truncation cases */
+	fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d);
+
+	mutex_lock(&st->lock);
+	if (d == st->chip_config.divider) {
+		result = 0;
+		goto fifo_rate_fail_unlock;
+	}
+	result = inv_mpu6050_set_power_itg(st, true);
+	if (result)
+		goto fifo_rate_fail_unlock;
+
+	result = regmap_write(st->map, st->reg->sample_rate_div, d);
+	if (result)
+		goto fifo_rate_fail_power_off;
+	st->chip_config.divider = d;
+
+	result = inv_mpu6050_set_lpf(st, fifo_rate);
+	if (result)
+		goto fifo_rate_fail_power_off;
+
+fifo_rate_fail_power_off:
+	result |= inv_mpu6050_set_power_itg(st, false);
+fifo_rate_fail_unlock:
+	mutex_unlock(&st->lock);
+	iio_device_release_direct_mode(indio_dev);
+	if (result)
+		return result;
+
+	return count;
+}
+
+/**
+ * inv_fifo_rate_show() - Get the current sampling rate.
+ */
+static ssize_t
+inv_fifo_rate_show(struct device *dev, struct device_attribute *attr,
+		   char *buf)
+{
+	struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
+	unsigned fifo_rate;
+
+	mutex_lock(&st->lock);
+	fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
+	mutex_unlock(&st->lock);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", fifo_rate);
+}
+
+/**
+ * inv_attr_show() - calling this function will show current
+ *                    parameters.
+ *
+ * Deprecated in favor of IIO mounting matrix API.
+ *
+ * See inv_get_mount_matrix()
+ */
+static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	s8 *m;
+
+	switch (this_attr->address) {
+	/*
+	 * In MPU6050, the two matrix are the same because gyro and accel
+	 * are integrated in one chip
+	 */
+	case ATTR_GYRO_MATRIX:
+	case ATTR_ACCL_MATRIX:
+		m = st->plat_data.orientation;
+
+		return scnprintf(buf, PAGE_SIZE,
+			"%d, %d, %d; %d, %d, %d; %d, %d, %d\n",
+			m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * inv_mpu6050_validate_trigger() - validate_trigger callback for invensense
+ *                                  MPU6050 device.
+ * @indio_dev: The IIO device
+ * @trig: The new trigger
+ *
+ * Returns: 0 if the 'trig' matches the trigger registered by the MPU6050
+ * device, -EINVAL otherwise.
+ */
+static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
+					struct iio_trigger *trig)
+{
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+	if (st->trig != trig)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct iio_mount_matrix *
+inv_get_mount_matrix(const struct iio_dev *indio_dev,
+		     const struct iio_chan_spec *chan)
+{
+	return &((struct inv_mpu6050_state *)iio_priv(indio_dev))->orientation;
+}
+
+static const struct iio_chan_spec_ext_info inv_ext_info[] = {
+	IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, inv_get_mount_matrix),
+	{ },
+};
+
+#define INV_MPU6050_CHAN(_type, _channel2, _index)                    \
+	{                                                             \
+		.type = _type,                                        \
+		.modified = 1,                                        \
+		.channel2 = _channel2,                                \
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	      \
+				      BIT(IIO_CHAN_INFO_CALIBBIAS),   \
+		.scan_index = _index,                                 \
+		.scan_type = {                                        \
+				.sign = 's',                          \
+				.realbits = 16,                       \
+				.storagebits = 16,                    \
+				.shift = 0,                           \
+				.endianness = IIO_BE,                 \
+			     },                                       \
+		.ext_info = inv_ext_info,                             \
+	}
+
+static const struct iio_chan_spec inv_mpu_channels[] = {
+	IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
+	/*
+	 * Note that temperature should only be via polled reading only,
+	 * not the final scan elements output.
+	 */
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+				| BIT(IIO_CHAN_INFO_OFFSET)
+				| BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = -1,
+	},
+	INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
+	INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
+	INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
+
+	INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
+	INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
+	INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+};
+
+/*
+ * The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and
+ * INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the
+ * low-pass filter. Specifically, each of these sampling rates are about twice
+ * the bandwidth of a corresponding low-pass filter, which should eliminate
+ * aliasing following the Nyquist principle. By picking a frequency different
+ * from these, the user risks aliasing effects.
+ */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 20 50 100 200 500");
+static IIO_CONST_ATTR(in_anglvel_scale_available,
+					  "0.000133090 0.000266181 0.000532362 0.001064724");
+static IIO_CONST_ATTR(in_accel_scale_available,
+					  "0.000598 0.001196 0.002392 0.004785");
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show,
+	inv_mpu6050_fifo_rate_store);
+
+/* Deprecated: kept for userspace backward compatibility. */
+static IIO_DEVICE_ATTR(in_gyro_matrix, S_IRUGO, inv_attr_show, NULL,
+	ATTR_GYRO_MATRIX);
+static IIO_DEVICE_ATTR(in_accel_matrix, S_IRUGO, inv_attr_show, NULL,
+	ATTR_ACCL_MATRIX);
+
+static struct attribute *inv_attributes[] = {
+	&iio_dev_attr_in_gyro_matrix.dev_attr.attr,  /* deprecated */
+	&iio_dev_attr_in_accel_matrix.dev_attr.attr, /* deprecated */
+	&iio_dev_attr_sampling_frequency.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_const_attr_in_accel_scale_available.dev_attr.attr,
+	&iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group inv_attribute_group = {
+	.attrs = inv_attributes
+};
+
+static const struct iio_info mpu_info = {
+	.read_raw = &inv_mpu6050_read_raw,
+	.write_raw = &inv_mpu6050_write_raw,
+	.write_raw_get_fmt = &inv_write_raw_get_fmt,
+	.attrs = &inv_attribute_group,
+	.validate_trigger = inv_mpu6050_validate_trigger,
+};
+
+/**
+ *  inv_check_and_setup_chip() - check and setup chip.
+ */
+static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
+{
+	int result;
+	unsigned int regval;
+	int i;
+
+	st->hw  = &hw_info[st->chip_type];
+	st->reg = hw_info[st->chip_type].reg;
+
+	/* check chip self-identification */
+	result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, &regval);
+	if (result)
+		return result;
+	if (regval != st->hw->whoami) {
+		/* check whoami against all possible values */
+		for (i = 0; i < INV_NUM_PARTS; ++i) {
+			if (regval == hw_info[i].whoami) {
+				dev_warn(regmap_get_device(st->map),
+					"whoami mismatch got %#02x (%s)"
+					"expected %#02hhx (%s)\n",
+					regval, hw_info[i].name,
+					st->hw->whoami, st->hw->name);
+				break;
+			}
+		}
+		if (i >= INV_NUM_PARTS) {
+			dev_err(regmap_get_device(st->map),
+				"invalid whoami %#02x expected %#02hhx (%s)\n",
+				regval, st->hw->whoami, st->hw->name);
+			return -ENODEV;
+		}
+	}
+
+	/* reset to make sure previous state are not there */
+	result = regmap_write(st->map, st->reg->pwr_mgmt_1,
+			      INV_MPU6050_BIT_H_RESET);
+	if (result)
+		return result;
+	msleep(INV_MPU6050_POWER_UP_TIME);
+
+	/*
+	 * Turn power on. After reset, the sleep bit could be on
+	 * or off depending on the OTP settings. Turning power on
+	 * make it in a definite state as well as making the hardware
+	 * state align with the software state
+	 */
+	result = inv_mpu6050_set_power_itg(st, true);
+	if (result)
+		return result;
+
+	result = inv_mpu6050_switch_engine(st, false,
+					   INV_MPU6050_BIT_PWR_ACCL_STBY);
+	if (result)
+		goto error_power_off;
+	result = inv_mpu6050_switch_engine(st, false,
+					   INV_MPU6050_BIT_PWR_GYRO_STBY);
+	if (result)
+		goto error_power_off;
+
+	return inv_mpu6050_set_power_itg(st, false);
+
+error_power_off:
+	inv_mpu6050_set_power_itg(st, false);
+	return result;
+}
+
+int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
+		int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
+{
+	struct inv_mpu6050_state *st;
+	struct iio_dev *indio_dev;
+	struct inv_mpu6050_platform_data *pdata;
+	struct device *dev = regmap_get_device(regmap);
+	int result;
+	struct irq_data *desc;
+	int irq_type;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	BUILD_BUG_ON(ARRAY_SIZE(hw_info) != INV_NUM_PARTS);
+	if (chip_type < 0 || chip_type >= INV_NUM_PARTS) {
+		dev_err(dev, "Bad invensense chip_type=%d name=%s\n",
+				chip_type, name);
+		return -ENODEV;
+	}
+	st = iio_priv(indio_dev);
+	mutex_init(&st->lock);
+	st->chip_type = chip_type;
+	st->powerup_count = 0;
+	st->irq = irq;
+	st->map = regmap;
+
+	pdata = dev_get_platdata(dev);
+	if (!pdata) {
+		result = of_iio_read_mount_matrix(dev, "mount-matrix",
+						  &st->orientation);
+		if (result) {
+			dev_err(dev, "Failed to retrieve mounting matrix %d\n",
+				result);
+			return result;
+		}
+	} else {
+		st->plat_data = *pdata;
+	}
+
+	desc = irq_get_irq_data(irq);
+	if (!desc) {
+		dev_err(dev, "Could not find IRQ %d\n", irq);
+		return -EINVAL;
+	}
+
+	irq_type = irqd_get_trigger_type(desc);
+	if (!irq_type)
+		irq_type = IRQF_TRIGGER_RISING;
+	if (irq_type == IRQF_TRIGGER_RISING)
+		st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
+	else if (irq_type == IRQF_TRIGGER_FALLING)
+		st->irq_mask = INV_MPU6050_ACTIVE_LOW;
+	else if (irq_type == IRQF_TRIGGER_HIGH)
+		st->irq_mask = INV_MPU6050_ACTIVE_HIGH |
+			INV_MPU6050_LATCH_INT_EN;
+	else if (irq_type == IRQF_TRIGGER_LOW)
+		st->irq_mask = INV_MPU6050_ACTIVE_LOW |
+			INV_MPU6050_LATCH_INT_EN;
+	else {
+		dev_err(dev, "Invalid interrupt type 0x%x specified\n",
+			irq_type);
+		return -EINVAL;
+	}
+
+	/* power is turned on inside check chip type*/
+	result = inv_check_and_setup_chip(st);
+	if (result)
+		return result;
+
+	result = inv_mpu6050_init_config(indio_dev);
+	if (result) {
+		dev_err(dev, "Could not initialize device.\n");
+		return result;
+	}
+
+	if (inv_mpu_bus_setup)
+		inv_mpu_bus_setup(indio_dev);
+
+	dev_set_drvdata(dev, indio_dev);
+	indio_dev->dev.parent = dev;
+	/* name will be NULL when enumerated via ACPI */
+	if (name)
+		indio_dev->name = name;
+	else
+		indio_dev->name = dev_name(dev);
+	indio_dev->channels = inv_mpu_channels;
+	indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+	indio_dev->info = &mpu_info;
+	indio_dev->modes = INDIO_BUFFER_TRIGGERED;
+
+	result = devm_iio_triggered_buffer_setup(dev, indio_dev,
+						 iio_pollfunc_store_time,
+						 inv_mpu6050_read_fifo,
+						 NULL);
+	if (result) {
+		dev_err(dev, "configure buffer fail %d\n", result);
+		return result;
+	}
+	result = inv_mpu6050_probe_trigger(indio_dev, irq_type);
+	if (result) {
+		dev_err(dev, "trigger probe fail %d\n", result);
+		return result;
+	}
+
+	result = devm_iio_device_register(dev, indio_dev);
+	if (result) {
+		dev_err(dev, "IIO register fail %d\n", result);
+		return result;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(inv_mpu_core_probe);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int inv_mpu_resume(struct device *dev)
+{
+	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
+	int result;
+
+	mutex_lock(&st->lock);
+	result = inv_mpu6050_set_power_itg(st, true);
+	mutex_unlock(&st->lock);
+
+	return result;
+}
+
+static int inv_mpu_suspend(struct device *dev)
+{
+	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
+	int result;
+
+	mutex_lock(&st->lock);
+	result = inv_mpu6050_set_power_itg(st, false);
+	mutex_unlock(&st->lock);
+
+	return result;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
+EXPORT_SYMBOL_GPL(inv_mpu_pmops);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device MPU6050 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
new file mode 100644
index 0000000..dd758e3
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
@@ -0,0 +1,243 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "inv_mpu_iio.h"
+
+static const struct regmap_config inv_mpu_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
+{
+	struct iio_dev *indio_dev = i2c_mux_priv(muxc);
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->lock);
+
+	ret = inv_mpu6050_set_power_itg(st, true);
+	if (ret)
+		goto error_unlock;
+
+	ret = regmap_write(st->map, st->reg->int_pin_cfg,
+			   st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
+
+error_unlock:
+	mutex_unlock(&st->lock);
+
+	return ret;
+}
+
+static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
+{
+	struct iio_dev *indio_dev = i2c_mux_priv(muxc);
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+	mutex_lock(&st->lock);
+
+	/* It doesn't really matter if any of the calls fail */
+	regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
+	inv_mpu6050_set_power_itg(st, false);
+
+	mutex_unlock(&st->lock);
+
+	return 0;
+}
+
+static const char *inv_mpu_match_acpi_device(struct device *dev,
+					     enum inv_devices *chip_id)
+{
+	const struct acpi_device_id *id;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return NULL;
+
+	*chip_id = (int)id->driver_data;
+
+	return dev_name(dev);
+}
+
+/**
+ *  inv_mpu_probe() - probe function.
+ *  @client:          i2c client.
+ *  @id:              i2c device id.
+ *
+ *  Returns 0 on success, a negative error code otherwise.
+ */
+static int inv_mpu_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct inv_mpu6050_state *st;
+	int result;
+	enum inv_devices chip_type;
+	struct regmap *regmap;
+	const char *name;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_I2C_BLOCK))
+		return -EOPNOTSUPP;
+
+	if (client->dev.of_node) {
+		chip_type = (enum inv_devices)
+			of_device_get_match_data(&client->dev);
+		name = client->name;
+	} else if (id) {
+		chip_type = (enum inv_devices)
+			id->driver_data;
+		name = id->name;
+	} else if (ACPI_HANDLE(&client->dev)) {
+		name = inv_mpu_match_acpi_device(&client->dev, &chip_type);
+		if (!name)
+			return -ENODEV;
+	} else {
+		return -ENOSYS;
+	}
+
+	regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+
+	result = inv_mpu_core_probe(regmap, client->irq, name,
+				    NULL, chip_type);
+	if (result < 0)
+		return result;
+
+	st = iio_priv(dev_get_drvdata(&client->dev));
+	switch (st->chip_type) {
+	case INV_ICM20608:
+		/* no i2c auxiliary bus on the chip */
+		break;
+	default:
+		/* declare i2c auxiliary bus */
+		st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
+					 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
+					 inv_mpu6050_select_bypass,
+					 inv_mpu6050_deselect_bypass);
+		if (!st->muxc)
+			return -ENOMEM;
+		st->muxc->priv = dev_get_drvdata(&client->dev);
+		result = i2c_mux_add_adapter(st->muxc, 0, 0, 0);
+		if (result)
+			return result;
+		result = inv_mpu_acpi_create_mux_client(client);
+		if (result)
+			goto out_del_mux;
+		break;
+	}
+
+	return 0;
+
+out_del_mux:
+	i2c_mux_del_adapters(st->muxc);
+	return result;
+}
+
+static int inv_mpu_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+	if (st->muxc) {
+		inv_mpu_acpi_delete_mux_client(client);
+		i2c_mux_del_adapters(st->muxc);
+	}
+
+	return 0;
+}
+
+/*
+ * device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id inv_mpu_id[] = {
+	{"mpu6050", INV_MPU6050},
+	{"mpu6500", INV_MPU6500},
+	{"mpu6515", INV_MPU6515},
+	{"mpu9150", INV_MPU9150},
+	{"mpu9250", INV_MPU9250},
+	{"mpu9255", INV_MPU9255},
+	{"icm20608", INV_ICM20608},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+
+static const struct of_device_id inv_of_match[] = {
+	{
+		.compatible = "invensense,mpu6050",
+		.data = (void *)INV_MPU6050
+	},
+	{
+		.compatible = "invensense,mpu6500",
+		.data = (void *)INV_MPU6500
+	},
+	{
+		.compatible = "invensense,mpu6515",
+		.data = (void *)INV_MPU6515
+	},
+	{
+		.compatible = "invensense,mpu9150",
+		.data = (void *)INV_MPU9150
+	},
+	{
+		.compatible = "invensense,mpu9250",
+		.data = (void *)INV_MPU9250
+	},
+	{
+		.compatible = "invensense,mpu9255",
+		.data = (void *)INV_MPU9255
+	},
+	{
+		.compatible = "invensense,icm20608",
+		.data = (void *)INV_ICM20608
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, inv_of_match);
+
+static const struct acpi_device_id inv_acpi_match[] = {
+	{"INVN6500", INV_MPU6500},
+	{ },
+};
+
+MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
+
+static struct i2c_driver inv_mpu_driver = {
+	.probe		=	inv_mpu_probe,
+	.remove		=	inv_mpu_remove,
+	.id_table	=	inv_mpu_id,
+	.driver = {
+		.of_match_table = inv_of_match,
+		.acpi_match_table = ACPI_PTR(inv_acpi_match),
+		.name	=	"inv-mpu6050-i2c",
+		.pm     =       &inv_mpu_pmops,
+	},
+};
+
+module_i2c_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Invensense device MPU6050 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
new file mode 100644
index 0000000..e69a596
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -0,0 +1,324 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/mutex.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/regmap.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/platform_data/invensense_mpu6050.h>
+
+/**
+ *  struct inv_mpu6050_reg_map - Notable registers.
+ *  @sample_rate_div:	Divider applied to gyro output rate.
+ *  @lpf:		Configures internal low pass filter.
+ *  @accel_lpf:		Configures accelerometer low pass filter.
+ *  @user_ctrl:		Enables/resets the FIFO.
+ *  @fifo_en:		Determines which data will appear in FIFO.
+ *  @gyro_config:	gyro config register.
+ *  @accl_config:	accel config register
+ *  @fifo_count_h:	Upper byte of FIFO count.
+ *  @fifo_r_w:		FIFO register.
+ *  @raw_gyro:		Address of first gyro register.
+ *  @raw_accl:		Address of first accel register.
+ *  @temperature:	temperature register
+ *  @int_enable:	Interrupt enable register.
+ *  @int_status:	Interrupt status register.
+ *  @pwr_mgmt_1:	Controls chip's power state and clock source.
+ *  @pwr_mgmt_2:	Controls power state of individual sensors.
+ *  @int_pin_cfg;	Controls interrupt pin configuration.
+ *  @accl_offset:	Controls the accelerometer calibration offset.
+ *  @gyro_offset:	Controls the gyroscope calibration offset.
+ */
+struct inv_mpu6050_reg_map {
+	u8 sample_rate_div;
+	u8 lpf;
+	u8 accel_lpf;
+	u8 user_ctrl;
+	u8 fifo_en;
+	u8 gyro_config;
+	u8 accl_config;
+	u8 fifo_count_h;
+	u8 fifo_r_w;
+	u8 raw_gyro;
+	u8 raw_accl;
+	u8 temperature;
+	u8 int_enable;
+	u8 int_status;
+	u8 pwr_mgmt_1;
+	u8 pwr_mgmt_2;
+	u8 int_pin_cfg;
+	u8 accl_offset;
+	u8 gyro_offset;
+};
+
+/*device enum */
+enum inv_devices {
+	INV_MPU6050,
+	INV_MPU6500,
+	INV_MPU6515,
+	INV_MPU6000,
+	INV_MPU9150,
+	INV_MPU9250,
+	INV_MPU9255,
+	INV_ICM20608,
+	INV_NUM_PARTS
+};
+
+/**
+ *  struct inv_mpu6050_chip_config - Cached chip configuration data.
+ *  @fsr:		Full scale range.
+ *  @lpf:		Digital low pass filter frequency.
+ *  @accl_fs:		accel full scale range.
+ *  @accl_fifo_enable:	enable accel data output
+ *  @gyro_fifo_enable:	enable gyro data output
+ *  @divider:		chip sample rate divider (sample rate divider - 1)
+ */
+struct inv_mpu6050_chip_config {
+	unsigned int fsr:2;
+	unsigned int lpf:3;
+	unsigned int accl_fs:2;
+	unsigned int accl_fifo_enable:1;
+	unsigned int gyro_fifo_enable:1;
+	u8 divider;
+	u8 user_ctrl;
+};
+
+/**
+ *  struct inv_mpu6050_hw - Other important hardware information.
+ *  @whoami:	Self identification byte from WHO_AM_I register
+ *  @name:      name of the chip.
+ *  @reg:   register map of the chip.
+ *  @config:    configuration of the chip.
+ */
+struct inv_mpu6050_hw {
+	u8 whoami;
+	u8 *name;
+	const struct inv_mpu6050_reg_map *reg;
+	const struct inv_mpu6050_chip_config *config;
+};
+
+/*
+ *  struct inv_mpu6050_state - Driver state variables.
+ *  @lock:              Chip access lock.
+ *  @trig:              IIO trigger.
+ *  @chip_config:	Cached attribute information.
+ *  @reg:		Map of important registers.
+ *  @hw:		Other hardware-specific information.
+ *  @chip_type:		chip type.
+ *  @plat_data:		platform data (deprecated in favor of @orientation).
+ *  @orientation:	sensor chip orientation relative to main hardware.
+ *  @map		regmap pointer.
+ *  @irq		interrupt number.
+ *  @irq_mask		the int_pin_cfg mask to configure interrupt type.
+ *  @chip_period:	chip internal period estimation (~1kHz).
+ *  @it_timestamp:	timestamp from previous interrupt.
+ *  @data_timestamp:	timestamp for next data sample.
+ */
+struct inv_mpu6050_state {
+	struct mutex lock;
+	struct iio_trigger  *trig;
+	struct inv_mpu6050_chip_config chip_config;
+	const struct inv_mpu6050_reg_map *reg;
+	const struct inv_mpu6050_hw *hw;
+	enum   inv_devices chip_type;
+	struct i2c_mux_core *muxc;
+	struct i2c_client *mux_client;
+	unsigned int powerup_count;
+	struct inv_mpu6050_platform_data plat_data;
+	struct iio_mount_matrix orientation;
+	struct regmap *map;
+	int irq;
+	u8 irq_mask;
+	unsigned skip_samples;
+	s64 chip_period;
+	s64 it_timestamp;
+	s64 data_timestamp;
+};
+
+/*register and associated bit definition*/
+#define INV_MPU6050_REG_ACCEL_OFFSET        0x06
+#define INV_MPU6050_REG_GYRO_OFFSET         0x13
+
+#define INV_MPU6050_REG_SAMPLE_RATE_DIV     0x19
+#define INV_MPU6050_REG_CONFIG              0x1A
+#define INV_MPU6050_REG_GYRO_CONFIG         0x1B
+#define INV_MPU6050_REG_ACCEL_CONFIG        0x1C
+
+#define INV_MPU6050_REG_FIFO_EN             0x23
+#define INV_MPU6050_BIT_ACCEL_OUT           0x08
+#define INV_MPU6050_BITS_GYRO_OUT           0x70
+
+#define INV_MPU6050_REG_INT_ENABLE          0x38
+#define INV_MPU6050_BIT_DATA_RDY_EN         0x01
+#define INV_MPU6050_BIT_DMP_INT_EN          0x02
+
+#define INV_MPU6050_REG_RAW_ACCEL           0x3B
+#define INV_MPU6050_REG_TEMPERATURE         0x41
+#define INV_MPU6050_REG_RAW_GYRO            0x43
+
+#define INV_MPU6050_REG_INT_STATUS          0x3A
+#define INV_MPU6050_BIT_FIFO_OVERFLOW_INT   0x10
+#define INV_MPU6050_BIT_RAW_DATA_RDY_INT    0x01
+
+#define INV_MPU6050_REG_USER_CTRL           0x6A
+#define INV_MPU6050_BIT_FIFO_RST            0x04
+#define INV_MPU6050_BIT_DMP_RST             0x08
+#define INV_MPU6050_BIT_I2C_MST_EN          0x20
+#define INV_MPU6050_BIT_FIFO_EN             0x40
+#define INV_MPU6050_BIT_DMP_EN              0x80
+#define INV_MPU6050_BIT_I2C_IF_DIS          0x10
+
+#define INV_MPU6050_REG_PWR_MGMT_1          0x6B
+#define INV_MPU6050_BIT_H_RESET             0x80
+#define INV_MPU6050_BIT_SLEEP               0x40
+#define INV_MPU6050_BIT_CLK_MASK            0x7
+
+#define INV_MPU6050_REG_PWR_MGMT_2          0x6C
+#define INV_MPU6050_BIT_PWR_ACCL_STBY       0x38
+#define INV_MPU6050_BIT_PWR_GYRO_STBY       0x07
+
+#define INV_MPU6050_REG_FIFO_COUNT_H        0x72
+#define INV_MPU6050_REG_FIFO_R_W            0x74
+
+#define INV_MPU6050_BYTES_PER_3AXIS_SENSOR   6
+#define INV_MPU6050_FIFO_COUNT_BYTE          2
+
+/* mpu6500 registers */
+#define INV_MPU6500_REG_ACCEL_CONFIG_2      0x1D
+#define INV_MPU6500_REG_ACCEL_OFFSET        0x77
+
+/* delay time in milliseconds */
+#define INV_MPU6050_POWER_UP_TIME            100
+#define INV_MPU6050_TEMP_UP_TIME             100
+#define INV_MPU6050_SENSOR_UP_TIME           30
+
+/* delay time in microseconds */
+#define INV_MPU6050_REG_UP_TIME_MIN          5000
+#define INV_MPU6050_REG_UP_TIME_MAX          10000
+
+#define INV_MPU6050_TEMP_OFFSET	             12421
+#define INV_MPU6050_TEMP_SCALE               2941
+#define INV_MPU6050_MAX_GYRO_FS_PARAM        3
+#define INV_MPU6050_MAX_ACCL_FS_PARAM        3
+#define INV_MPU6050_THREE_AXIS               3
+#define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT    3
+#define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT    3
+
+/* 6 + 6 round up and plus 8 */
+#define INV_MPU6050_OUTPUT_DATA_SIZE         24
+
+#define INV_MPU6050_REG_INT_PIN_CFG	0x37
+#define INV_MPU6050_ACTIVE_HIGH		0x00
+#define INV_MPU6050_ACTIVE_LOW		0x80
+/* enable level triggering */
+#define INV_MPU6050_LATCH_INT_EN	0x20
+#define INV_MPU6050_BIT_BYPASS_EN	0x2
+
+/* Allowed timestamp period jitter in percent */
+#define INV_MPU6050_TS_PERIOD_JITTER	4
+
+/* init parameters */
+#define INV_MPU6050_INIT_FIFO_RATE           50
+#define INV_MPU6050_MAX_FIFO_RATE            1000
+#define INV_MPU6050_MIN_FIFO_RATE            4
+
+/* chip internal frequency: 1KHz */
+#define INV_MPU6050_INTERNAL_FREQ_HZ		1000
+/* return the frequency divider (chip sample rate divider + 1) */
+#define INV_MPU6050_FREQ_DIVIDER(st)					\
+	((st)->chip_config.divider + 1)
+/* chip sample rate divider to fifo rate */
+#define INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate)			\
+	((INV_MPU6050_INTERNAL_FREQ_HZ / (fifo_rate)) - 1)
+#define INV_MPU6050_DIVIDER_TO_FIFO_RATE(divider)			\
+	(INV_MPU6050_INTERNAL_FREQ_HZ / ((divider) + 1))
+
+#define INV_MPU6050_REG_WHOAMI			117
+
+#define INV_MPU6000_WHOAMI_VALUE		0x68
+#define INV_MPU6050_WHOAMI_VALUE		0x68
+#define INV_MPU6500_WHOAMI_VALUE		0x70
+#define INV_MPU9150_WHOAMI_VALUE		0x68
+#define INV_MPU9250_WHOAMI_VALUE		0x71
+#define INV_MPU9255_WHOAMI_VALUE		0x73
+#define INV_MPU6515_WHOAMI_VALUE		0x74
+#define INV_ICM20608_WHOAMI_VALUE		0xAF
+
+/* scan element definition */
+enum inv_mpu6050_scan {
+	INV_MPU6050_SCAN_ACCL_X,
+	INV_MPU6050_SCAN_ACCL_Y,
+	INV_MPU6050_SCAN_ACCL_Z,
+	INV_MPU6050_SCAN_GYRO_X,
+	INV_MPU6050_SCAN_GYRO_Y,
+	INV_MPU6050_SCAN_GYRO_Z,
+	INV_MPU6050_SCAN_TIMESTAMP,
+};
+
+enum inv_mpu6050_filter_e {
+	INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
+	INV_MPU6050_FILTER_188HZ,
+	INV_MPU6050_FILTER_98HZ,
+	INV_MPU6050_FILTER_42HZ,
+	INV_MPU6050_FILTER_20HZ,
+	INV_MPU6050_FILTER_10HZ,
+	INV_MPU6050_FILTER_5HZ,
+	INV_MPU6050_FILTER_2100HZ_NOLPF,
+	NUM_MPU6050_FILTER
+};
+
+/* IIO attribute address */
+enum INV_MPU6050_IIO_ATTR_ADDR {
+	ATTR_GYRO_MATRIX,
+	ATTR_ACCL_MATRIX,
+};
+
+enum inv_mpu6050_accl_fs_e {
+	INV_MPU6050_FS_02G = 0,
+	INV_MPU6050_FS_04G,
+	INV_MPU6050_FS_08G,
+	INV_MPU6050_FS_16G,
+	NUM_ACCL_FSR
+};
+
+enum inv_mpu6050_fsr_e {
+	INV_MPU6050_FSR_250DPS = 0,
+	INV_MPU6050_FSR_500DPS,
+	INV_MPU6050_FSR_1000DPS,
+	INV_MPU6050_FSR_2000DPS,
+	NUM_MPU6050_FSR
+};
+
+enum inv_mpu6050_clock_sel_e {
+	INV_CLK_INTERNAL = 0,
+	INV_CLK_PLL,
+	NUM_CLK
+};
+
+irqreturn_t inv_mpu6050_read_fifo(int irq, void *p);
+int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type);
+int inv_reset_fifo(struct iio_dev *indio_dev);
+int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
+int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
+int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
+int inv_mpu_acpi_create_mux_client(struct i2c_client *client);
+void inv_mpu_acpi_delete_mux_client(struct i2c_client *client);
+int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
+		int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type);
+extern const struct dev_pm_ops inv_mpu_pmops;
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
new file mode 100644
index 0000000..548e042
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -0,0 +1,249 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/math64.h>
+#include <asm/unaligned.h>
+#include "inv_mpu_iio.h"
+
+/**
+ *  inv_mpu6050_update_period() - Update chip internal period estimation
+ *
+ *  @st:		driver state
+ *  @timestamp:		the interrupt timestamp
+ *  @nb:		number of data set in the fifo
+ *
+ *  This function uses interrupt timestamps to estimate the chip period and
+ *  to choose the data timestamp to come.
+ */
+static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
+				      s64 timestamp, size_t nb)
+{
+	/* Period boundaries for accepting timestamp */
+	const s64 period_min =
+		(NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
+	const s64 period_max =
+		(NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
+	const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
+	s64 delta, interval;
+	bool use_it_timestamp = false;
+
+	if (st->it_timestamp == 0) {
+		/* not initialized, forced to use it_timestamp */
+		use_it_timestamp = true;
+	} else if (nb == 1) {
+		/*
+		 * Validate the use of it timestamp by checking if interrupt
+		 * has been delayed.
+		 * nb > 1 means interrupt was delayed for more than 1 sample,
+		 * so it's obviously not good.
+		 * Compute the chip period between 2 interrupts for validating.
+		 */
+		delta = div_s64(timestamp - st->it_timestamp, divider);
+		if (delta > period_min && delta < period_max) {
+			/* update chip period and use it timestamp */
+			st->chip_period = (st->chip_period + delta) / 2;
+			use_it_timestamp = true;
+		}
+	}
+
+	if (use_it_timestamp) {
+		/*
+		 * Manage case of multiple samples in the fifo (nb > 1):
+		 * compute timestamp corresponding to the first sample using
+		 * estimated chip period.
+		 */
+		interval = (nb - 1) * st->chip_period * divider;
+		st->data_timestamp = timestamp - interval;
+	}
+
+	/* save it timestamp */
+	st->it_timestamp = timestamp;
+}
+
+/**
+ *  inv_mpu6050_get_timestamp() - Return the current data timestamp
+ *
+ *  @st:		driver state
+ *  @return:		current data timestamp
+ *
+ *  This function returns the current data timestamp and prepares for next one.
+ */
+static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
+{
+	s64 ts;
+
+	/* return current data timestamp and increment */
+	ts = st->data_timestamp;
+	st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
+
+	return ts;
+}
+
+int inv_reset_fifo(struct iio_dev *indio_dev)
+{
+	int result;
+	u8 d;
+	struct inv_mpu6050_state  *st = iio_priv(indio_dev);
+
+	/* reset it timestamp validation */
+	st->it_timestamp = 0;
+
+	/* disable interrupt */
+	result = regmap_write(st->map, st->reg->int_enable, 0);
+	if (result) {
+		dev_err(regmap_get_device(st->map), "int_enable failed %d\n",
+			result);
+		return result;
+	}
+	/* disable the sensor output to FIFO */
+	result = regmap_write(st->map, st->reg->fifo_en, 0);
+	if (result)
+		goto reset_fifo_fail;
+	/* disable fifo reading */
+	result = regmap_write(st->map, st->reg->user_ctrl,
+			      st->chip_config.user_ctrl);
+	if (result)
+		goto reset_fifo_fail;
+
+	/* reset FIFO*/
+	d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
+	result = regmap_write(st->map, st->reg->user_ctrl, d);
+	if (result)
+		goto reset_fifo_fail;
+
+	/* enable interrupt */
+	if (st->chip_config.accl_fifo_enable ||
+	    st->chip_config.gyro_fifo_enable) {
+		result = regmap_write(st->map, st->reg->int_enable,
+				      INV_MPU6050_BIT_DATA_RDY_EN);
+		if (result)
+			return result;
+	}
+	/* enable FIFO reading */
+	d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN;
+	result = regmap_write(st->map, st->reg->user_ctrl, d);
+	if (result)
+		goto reset_fifo_fail;
+	/* enable sensor output to FIFO */
+	d = 0;
+	if (st->chip_config.gyro_fifo_enable)
+		d |= INV_MPU6050_BITS_GYRO_OUT;
+	if (st->chip_config.accl_fifo_enable)
+		d |= INV_MPU6050_BIT_ACCEL_OUT;
+	result = regmap_write(st->map, st->reg->fifo_en, d);
+	if (result)
+		goto reset_fifo_fail;
+
+	return 0;
+
+reset_fifo_fail:
+	dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
+	result = regmap_write(st->map, st->reg->int_enable,
+			      INV_MPU6050_BIT_DATA_RDY_EN);
+
+	return result;
+}
+
+/**
+ * inv_mpu6050_read_fifo() - Transfer data from hardware FIFO to KFIFO.
+ */
+irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+	size_t bytes_per_datum;
+	int result;
+	u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
+	u16 fifo_count;
+	s64 timestamp;
+	int int_status;
+	size_t i, nb;
+
+	mutex_lock(&st->lock);
+
+	/* ack interrupt and check status */
+	result = regmap_read(st->map, st->reg->int_status, &int_status);
+	if (result) {
+		dev_err(regmap_get_device(st->map),
+			"failed to ack interrupt\n");
+		goto flush_fifo;
+	}
+	/* handle fifo overflow by reseting fifo */
+	if (int_status & INV_MPU6050_BIT_FIFO_OVERFLOW_INT)
+		goto flush_fifo;
+	if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
+		dev_warn(regmap_get_device(st->map),
+			"spurious interrupt with status 0x%x\n", int_status);
+		goto end_session;
+	}
+
+	if (!(st->chip_config.accl_fifo_enable |
+		st->chip_config.gyro_fifo_enable))
+		goto end_session;
+	bytes_per_datum = 0;
+	if (st->chip_config.accl_fifo_enable)
+		bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
+
+	if (st->chip_config.gyro_fifo_enable)
+		bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
+
+	/*
+	 * read fifo_count register to know how many bytes are inside the FIFO
+	 * right now
+	 */
+	result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data,
+				  INV_MPU6050_FIFO_COUNT_BYTE);
+	if (result)
+		goto end_session;
+	fifo_count = get_unaligned_be16(&data[0]);
+	/* compute and process all complete datum */
+	nb = fifo_count / bytes_per_datum;
+	inv_mpu6050_update_period(st, pf->timestamp, nb);
+	for (i = 0; i < nb; ++i) {
+		result = regmap_bulk_read(st->map, st->reg->fifo_r_w,
+					  data, bytes_per_datum);
+		if (result)
+			goto flush_fifo;
+		/* skip first samples if needed */
+		if (st->skip_samples) {
+			st->skip_samples--;
+			continue;
+		}
+		timestamp = inv_mpu6050_get_timestamp(st);
+		iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
+	}
+
+end_session:
+	mutex_unlock(&st->lock);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+
+flush_fifo:
+	/* Flush HW and SW FIFOs. */
+	inv_reset_fifo(indio_dev);
+	mutex_unlock(&st->lock);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
new file mode 100644
index 0000000..227f50a
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
@@ -0,0 +1,109 @@
+/*
+* Copyright (C) 2015 Intel Corporation Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include "inv_mpu_iio.h"
+
+static const struct regmap_config inv_mpu_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
+{
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+	int ret = 0;
+
+	ret = inv_mpu6050_set_power_itg(st, true);
+	if (ret)
+		return ret;
+
+	st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
+	ret = regmap_write(st->map, st->reg->user_ctrl,
+			   st->chip_config.user_ctrl);
+	if (ret) {
+		inv_mpu6050_set_power_itg(st, false);
+		return ret;
+	}
+
+	return inv_mpu6050_set_power_itg(st, false);
+}
+
+static int inv_mpu_probe(struct spi_device *spi)
+{
+	struct regmap *regmap;
+	const struct spi_device_id *spi_id;
+	const struct acpi_device_id *acpi_id;
+	const char *name = NULL;
+	enum inv_devices chip_type;
+
+	if ((spi_id = spi_get_device_id(spi))) {
+		chip_type = (enum inv_devices)spi_id->driver_data;
+		name = spi_id->name;
+	} else if ((acpi_id = acpi_match_device(spi->dev.driver->acpi_match_table, &spi->dev))) {
+		chip_type = (enum inv_devices)acpi_id->driver_data;
+	} else {
+		return -ENODEV;
+	}
+
+	regmap = devm_regmap_init_spi(spi, &inv_mpu_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+
+	return inv_mpu_core_probe(regmap, spi->irq, name,
+				  inv_mpu_i2c_disable, chip_type);
+}
+
+/*
+ * device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct spi_device_id inv_mpu_id[] = {
+	{"mpu6000", INV_MPU6000},
+	{"mpu6500", INV_MPU6500},
+	{"mpu9150", INV_MPU9150},
+	{"mpu9250", INV_MPU9250},
+	{"mpu9255", INV_MPU9255},
+	{"icm20608", INV_ICM20608},
+	{}
+};
+
+MODULE_DEVICE_TABLE(spi, inv_mpu_id);
+
+static const struct acpi_device_id inv_acpi_match[] = {
+	{"INVN6000", INV_MPU6000},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
+
+static struct spi_driver inv_mpu_driver = {
+	.probe		=	inv_mpu_probe,
+	.id_table	=	inv_mpu_id,
+	.driver = {
+		.acpi_match_table = ACPI_PTR(inv_acpi_match),
+		.name	=	"inv-mpu6000-spi",
+		.pm     =       &inv_mpu_pmops,
+	},
+};
+
+module_spi_driver(inv_mpu_driver);
+
+MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>");
+MODULE_DESCRIPTION("Invensense device MPU6000 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
new file mode 100644
index 0000000..6c3e165
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
@@ -0,0 +1,168 @@
+/*
+* Copyright (C) 2012 Invensense, Inc.
+*
+* This software is licensed under the terms of the GNU General Public
+* License version 2, as published by the Free Software Foundation, and
+* may be copied, distributed, and modified under those terms.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*/
+
+#include "inv_mpu_iio.h"
+
+static void inv_scan_query(struct iio_dev *indio_dev)
+{
+	struct inv_mpu6050_state  *st = iio_priv(indio_dev);
+
+	st->chip_config.gyro_fifo_enable =
+		test_bit(INV_MPU6050_SCAN_GYRO_X,
+			 indio_dev->active_scan_mask) ||
+		test_bit(INV_MPU6050_SCAN_GYRO_Y,
+			 indio_dev->active_scan_mask) ||
+		test_bit(INV_MPU6050_SCAN_GYRO_Z,
+			 indio_dev->active_scan_mask);
+
+	st->chip_config.accl_fifo_enable =
+		test_bit(INV_MPU6050_SCAN_ACCL_X,
+			 indio_dev->active_scan_mask) ||
+		test_bit(INV_MPU6050_SCAN_ACCL_Y,
+			 indio_dev->active_scan_mask) ||
+		test_bit(INV_MPU6050_SCAN_ACCL_Z,
+			 indio_dev->active_scan_mask);
+}
+
+/**
+ *  inv_mpu6050_set_enable() - enable chip functions.
+ *  @indio_dev:	Device driver instance.
+ *  @enable: enable/disable
+ */
+static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
+{
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+	int result;
+
+	if (enable) {
+		result = inv_mpu6050_set_power_itg(st, true);
+		if (result)
+			return result;
+		inv_scan_query(indio_dev);
+		st->skip_samples = 0;
+		if (st->chip_config.gyro_fifo_enable) {
+			result = inv_mpu6050_switch_engine(st, true,
+					INV_MPU6050_BIT_PWR_GYRO_STBY);
+			if (result)
+				goto error_power_off;
+			/* gyro first sample is out of specs, skip it */
+			st->skip_samples = 1;
+		}
+		if (st->chip_config.accl_fifo_enable) {
+			result = inv_mpu6050_switch_engine(st, true,
+					INV_MPU6050_BIT_PWR_ACCL_STBY);
+			if (result)
+				goto error_gyro_off;
+		}
+		result = inv_reset_fifo(indio_dev);
+		if (result)
+			goto error_accl_off;
+	} else {
+		result = regmap_write(st->map, st->reg->fifo_en, 0);
+		if (result)
+			goto error_accl_off;
+
+		result = regmap_write(st->map, st->reg->int_enable, 0);
+		if (result)
+			goto error_accl_off;
+
+		result = regmap_write(st->map, st->reg->user_ctrl,
+				      st->chip_config.user_ctrl);
+		if (result)
+			goto error_accl_off;
+
+		result = inv_mpu6050_switch_engine(st, false,
+					INV_MPU6050_BIT_PWR_ACCL_STBY);
+		if (result)
+			goto error_accl_off;
+
+		result = inv_mpu6050_switch_engine(st, false,
+					INV_MPU6050_BIT_PWR_GYRO_STBY);
+		if (result)
+			goto error_gyro_off;
+
+		result = inv_mpu6050_set_power_itg(st, false);
+		if (result)
+			goto error_power_off;
+	}
+
+	return 0;
+
+error_accl_off:
+	if (st->chip_config.accl_fifo_enable)
+		inv_mpu6050_switch_engine(st, false,
+					  INV_MPU6050_BIT_PWR_ACCL_STBY);
+error_gyro_off:
+	if (st->chip_config.gyro_fifo_enable)
+		inv_mpu6050_switch_engine(st, false,
+					  INV_MPU6050_BIT_PWR_GYRO_STBY);
+error_power_off:
+	inv_mpu6050_set_power_itg(st, false);
+	return result;
+}
+
+/**
+ * inv_mpu_data_rdy_trigger_set_state() - set data ready interrupt state
+ * @trig: Trigger instance
+ * @state: Desired trigger state
+ */
+static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
+					      bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+	int result;
+
+	mutex_lock(&st->lock);
+	result = inv_mpu6050_set_enable(indio_dev, state);
+	mutex_unlock(&st->lock);
+
+	return result;
+}
+
+static const struct iio_trigger_ops inv_mpu_trigger_ops = {
+	.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
+};
+
+int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
+{
+	int ret;
+	struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+	st->trig = devm_iio_trigger_alloc(&indio_dev->dev,
+					  "%s-dev%d",
+					  indio_dev->name,
+					  indio_dev->id);
+	if (!st->trig)
+		return -ENOMEM;
+
+	ret = devm_request_irq(&indio_dev->dev, st->irq,
+			       &iio_trigger_generic_data_rdy_poll,
+			       irq_type,
+			       "inv_mpu",
+			       st->trig);
+	if (ret)
+		return ret;
+
+	st->trig->dev.parent = regmap_get_device(st->map);
+	st->trig->ops = &inv_mpu_trigger_ops;
+	iio_trigger_set_drvdata(st->trig, indio_dev);
+
+	ret = devm_iio_trigger_register(&indio_dev->dev, st->trig);
+	if (ret)
+		return ret;
+
+	indio_dev->trig = iio_trigger_get(st->trig);
+
+	return 0;
+}
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c
new file mode 100644
index 0000000..44b3f53
--- /dev/null
+++ b/drivers/iio/imu/kmx61.c
@@ -0,0 +1,1545 @@
+/*
+ * KMX61 - Kionix 6-axis Accelerometer/Magnetometer
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for KMX61 (7-bit I2C slave address 0x0E or 0x0F).
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define KMX61_DRV_NAME "kmx61"
+#define KMX61_IRQ_NAME "kmx61_event"
+
+#define KMX61_REG_WHO_AM_I	0x00
+#define KMX61_REG_INS1		0x01
+#define KMX61_REG_INS2		0x02
+
+/*
+ * three 16-bit accelerometer output registers for X/Y/Z axis
+ * we use only XOUT_L as a base register, all other addresses
+ * can be obtained by applying an offset and are provided here
+ * only for clarity.
+ */
+#define KMX61_ACC_XOUT_L	0x0A
+#define KMX61_ACC_XOUT_H	0x0B
+#define KMX61_ACC_YOUT_L	0x0C
+#define KMX61_ACC_YOUT_H	0x0D
+#define KMX61_ACC_ZOUT_L	0x0E
+#define KMX61_ACC_ZOUT_H	0x0F
+
+/*
+ * one 16-bit temperature output register
+ */
+#define KMX61_TEMP_L		0x10
+#define KMX61_TEMP_H		0x11
+
+/*
+ * three 16-bit magnetometer output registers for X/Y/Z axis
+ */
+#define KMX61_MAG_XOUT_L	0x12
+#define KMX61_MAG_XOUT_H	0x13
+#define KMX61_MAG_YOUT_L	0x14
+#define KMX61_MAG_YOUT_H	0x15
+#define KMX61_MAG_ZOUT_L	0x16
+#define KMX61_MAG_ZOUT_H	0x17
+
+#define KMX61_REG_INL		0x28
+#define KMX61_REG_STBY		0x29
+#define KMX61_REG_CTRL1		0x2A
+#define KMX61_REG_CTRL2		0x2B
+#define KMX61_REG_ODCNTL	0x2C
+#define KMX61_REG_INC1		0x2D
+
+#define KMX61_REG_WUF_THRESH	0x3D
+#define KMX61_REG_WUF_TIMER	0x3E
+
+#define KMX61_ACC_STBY_BIT	BIT(0)
+#define KMX61_MAG_STBY_BIT	BIT(1)
+#define KMX61_ACT_STBY_BIT	BIT(7)
+
+#define KMX61_ALL_STBY		(KMX61_ACC_STBY_BIT | KMX61_MAG_STBY_BIT)
+
+#define KMX61_REG_INS1_BIT_WUFS		BIT(1)
+
+#define KMX61_REG_INS2_BIT_ZP		BIT(0)
+#define KMX61_REG_INS2_BIT_ZN		BIT(1)
+#define KMX61_REG_INS2_BIT_YP		BIT(2)
+#define KMX61_REG_INS2_BIT_YN		BIT(3)
+#define KMX61_REG_INS2_BIT_XP		BIT(4)
+#define KMX61_REG_INS2_BIT_XN		BIT(5)
+
+#define KMX61_REG_CTRL1_GSEL_MASK	0x03
+
+#define KMX61_REG_CTRL1_BIT_RES		BIT(4)
+#define KMX61_REG_CTRL1_BIT_DRDYE	BIT(5)
+#define KMX61_REG_CTRL1_BIT_WUFE	BIT(6)
+#define KMX61_REG_CTRL1_BIT_BTSE	BIT(7)
+
+#define KMX61_REG_INC1_BIT_WUFS		BIT(0)
+#define KMX61_REG_INC1_BIT_DRDYM	BIT(1)
+#define KMX61_REG_INC1_BIT_DRDYA	BIT(2)
+#define KMX61_REG_INC1_BIT_IEN		BIT(5)
+
+#define KMX61_ACC_ODR_SHIFT	0
+#define KMX61_MAG_ODR_SHIFT	4
+#define KMX61_ACC_ODR_MASK	0x0F
+#define KMX61_MAG_ODR_MASK	0xF0
+
+#define KMX61_OWUF_MASK		0x7
+
+#define KMX61_DEFAULT_WAKE_THRESH	1
+#define KMX61_DEFAULT_WAKE_DURATION	1
+
+#define KMX61_SLEEP_DELAY_MS	2000
+
+#define KMX61_CHIP_ID		0x12
+
+/* KMX61 devices */
+#define KMX61_ACC	0x01
+#define KMX61_MAG	0x02
+
+struct kmx61_data {
+	struct i2c_client *client;
+
+	/* serialize access to non-atomic ops, e.g set_mode */
+	struct mutex lock;
+
+	/* standby state */
+	bool acc_stby;
+	bool mag_stby;
+
+	/* power state */
+	bool acc_ps;
+	bool mag_ps;
+
+	/* config bits */
+	u8 range;
+	u8 odr_bits;
+	u8 wake_thresh;
+	u8 wake_duration;
+
+	/* accelerometer specific data */
+	struct iio_dev *acc_indio_dev;
+	struct iio_trigger *acc_dready_trig;
+	struct iio_trigger *motion_trig;
+	bool acc_dready_trig_on;
+	bool motion_trig_on;
+	bool ev_enable_state;
+
+	/* magnetometer specific data */
+	struct iio_dev *mag_indio_dev;
+	struct iio_trigger *mag_dready_trig;
+	bool mag_dready_trig_on;
+};
+
+enum kmx61_range {
+	KMX61_RANGE_2G,
+	KMX61_RANGE_4G,
+	KMX61_RANGE_8G,
+};
+
+enum kmx61_axis {
+	KMX61_AXIS_X,
+	KMX61_AXIS_Y,
+	KMX61_AXIS_Z,
+};
+
+static const u16 kmx61_uscale_table[] = {9582, 19163, 38326};
+
+static const struct {
+	int val;
+	int val2;
+} kmx61_samp_freq_table[] = { {12, 500000},
+			{25, 0},
+			{50, 0},
+			{100, 0},
+			{200, 0},
+			{400, 0},
+			{800, 0},
+			{1600, 0},
+			{0, 781000},
+			{1, 563000},
+			{3, 125000},
+			{6, 250000} };
+
+static const struct {
+	int val;
+	int val2;
+	int odr_bits;
+} kmx61_wake_up_odr_table[] = { {0, 781000, 0x00},
+				 {1, 563000, 0x01},
+				 {3, 125000, 0x02},
+				 {6, 250000, 0x03},
+				 {12, 500000, 0x04},
+				 {25, 0, 0x05},
+				 {50, 0, 0x06},
+				 {100, 0, 0x06},
+				 {200, 0, 0x06},
+				 {400, 0, 0x06},
+				 {800, 0, 0x06},
+				 {1600, 0, 0x06} };
+
+static IIO_CONST_ATTR(accel_scale_available, "0.009582 0.019163 0.038326");
+static IIO_CONST_ATTR(magn_scale_available, "0.001465");
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+	"0.781000 1.563000 3.125000 6.250000 12.500000 25 50 100 200 400 800");
+
+static struct attribute *kmx61_acc_attributes[] = {
+	&iio_const_attr_accel_scale_available.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *kmx61_mag_attributes[] = {
+	&iio_const_attr_magn_scale_available.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group kmx61_acc_attribute_group = {
+	.attrs = kmx61_acc_attributes,
+};
+
+static const struct attribute_group kmx61_mag_attribute_group = {
+	.attrs = kmx61_mag_attributes,
+};
+
+static const struct iio_event_spec kmx61_event = {
+	.type = IIO_EV_TYPE_THRESH,
+	.dir = IIO_EV_DIR_EITHER,
+	.mask_separate = BIT(IIO_EV_INFO_VALUE) |
+			 BIT(IIO_EV_INFO_ENABLE) |
+			 BIT(IIO_EV_INFO_PERIOD),
+};
+
+#define KMX61_ACC_CHAN(_axis) { \
+	.type = IIO_ACCEL, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## _axis, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+				BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = KMX61_ACC, \
+	.scan_index = KMX61_AXIS_ ## _axis, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = 12, \
+		.storagebits = 16, \
+		.shift = 4, \
+		.endianness = IIO_LE, \
+	}, \
+	.event_spec = &kmx61_event, \
+	.num_event_specs = 1 \
+}
+
+#define KMX61_MAG_CHAN(_axis) { \
+	.type = IIO_MAGN, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## _axis, \
+	.address = KMX61_MAG, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+				BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.scan_index = KMX61_AXIS_ ## _axis, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = 14, \
+		.storagebits = 16, \
+		.shift = 2, \
+		.endianness = IIO_LE, \
+	}, \
+}
+
+static const struct iio_chan_spec kmx61_acc_channels[] = {
+	KMX61_ACC_CHAN(X),
+	KMX61_ACC_CHAN(Y),
+	KMX61_ACC_CHAN(Z),
+};
+
+static const struct iio_chan_spec kmx61_mag_channels[] = {
+	KMX61_MAG_CHAN(X),
+	KMX61_MAG_CHAN(Y),
+	KMX61_MAG_CHAN(Z),
+};
+
+static void kmx61_set_data(struct iio_dev *indio_dev, struct kmx61_data *data)
+{
+	struct kmx61_data **priv = iio_priv(indio_dev);
+
+	*priv = data;
+}
+
+static struct kmx61_data *kmx61_get_data(struct iio_dev *indio_dev)
+{
+	return *(struct kmx61_data **)iio_priv(indio_dev);
+}
+
+static int kmx61_convert_freq_to_bit(int val, int val2)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
+		if (val == kmx61_samp_freq_table[i].val &&
+		    val2 == kmx61_samp_freq_table[i].val2)
+			return i;
+	return -EINVAL;
+}
+
+static int kmx61_convert_wake_up_odr_to_bit(int val, int val2)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(kmx61_wake_up_odr_table); ++i)
+		if (kmx61_wake_up_odr_table[i].val == val &&
+			kmx61_wake_up_odr_table[i].val2 == val2)
+				return kmx61_wake_up_odr_table[i].odr_bits;
+	return -EINVAL;
+}
+
+/**
+ * kmx61_set_mode() - set KMX61 device operating mode
+ * @data - kmx61 device private data pointer
+ * @mode - bitmask, indicating operating mode for @device
+ * @device - bitmask, indicating device for which @mode needs to be set
+ * @update - update stby bits stored in device's private  @data
+ *
+ * For each sensor (accelerometer/magnetometer) there are two operating modes
+ * STANDBY and OPERATION. Neither accel nor magn can be disabled independently
+ * if they are both enabled. Internal sensors state is saved in acc_stby and
+ * mag_stby members of driver's private @data.
+ */
+static int kmx61_set_mode(struct kmx61_data *data, u8 mode, u8 device,
+			  bool update)
+{
+	int ret;
+	int acc_stby = -1, mag_stby = -1;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_stby\n");
+		return ret;
+	}
+	if (device & KMX61_ACC) {
+		if (mode & KMX61_ACC_STBY_BIT) {
+			ret |= KMX61_ACC_STBY_BIT;
+			acc_stby = 1;
+		} else {
+			ret &= ~KMX61_ACC_STBY_BIT;
+			acc_stby = 0;
+		}
+	}
+
+	if (device & KMX61_MAG) {
+		if (mode & KMX61_MAG_STBY_BIT) {
+			ret |= KMX61_MAG_STBY_BIT;
+			mag_stby = 1;
+		} else {
+			ret &= ~KMX61_MAG_STBY_BIT;
+			mag_stby = 0;
+		}
+	}
+
+	if (mode & KMX61_ACT_STBY_BIT)
+		ret |= KMX61_ACT_STBY_BIT;
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_STBY, ret);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error writing reg_stby\n");
+		return ret;
+	}
+
+	if (acc_stby != -1 && update)
+		data->acc_stby = acc_stby;
+	if (mag_stby != -1 && update)
+		data->mag_stby = mag_stby;
+
+	return 0;
+}
+
+static int kmx61_get_mode(struct kmx61_data *data, u8 *mode, u8 device)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_stby\n");
+		return ret;
+	}
+	*mode = 0;
+
+	if (device & KMX61_ACC) {
+		if (ret & KMX61_ACC_STBY_BIT)
+			*mode |= KMX61_ACC_STBY_BIT;
+		else
+			*mode &= ~KMX61_ACC_STBY_BIT;
+	}
+
+	if (device & KMX61_MAG) {
+		if (ret & KMX61_MAG_STBY_BIT)
+			*mode |= KMX61_MAG_STBY_BIT;
+		else
+			*mode &= ~KMX61_MAG_STBY_BIT;
+	}
+
+	return 0;
+}
+
+static int kmx61_set_wake_up_odr(struct kmx61_data *data, int val, int val2)
+{
+	int ret, odr_bits;
+
+	odr_bits = kmx61_convert_wake_up_odr_to_bit(val, val2);
+	if (odr_bits < 0)
+		return odr_bits;
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL2,
+					odr_bits);
+	if (ret < 0)
+		dev_err(&data->client->dev, "Error writing reg_ctrl2\n");
+	return ret;
+}
+
+static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device)
+{
+	int ret;
+	u8 mode;
+	int lodr_bits, odr_bits;
+
+	ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG);
+	if (ret < 0)
+		return ret;
+
+	lodr_bits = kmx61_convert_freq_to_bit(val, val2);
+	if (lodr_bits < 0)
+		return lodr_bits;
+
+	/* To change ODR, accel and magn must be in STDBY */
+	ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG,
+			     true);
+	if (ret < 0)
+		return ret;
+
+	odr_bits = 0;
+	if (device & KMX61_ACC)
+		odr_bits |= lodr_bits << KMX61_ACC_ODR_SHIFT;
+	if (device & KMX61_MAG)
+		odr_bits |= lodr_bits << KMX61_MAG_ODR_SHIFT;
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_ODCNTL,
+					odr_bits);
+	if (ret < 0)
+		return ret;
+
+	data->odr_bits = odr_bits;
+
+	if (device & KMX61_ACC) {
+		ret = kmx61_set_wake_up_odr(data, val, val2);
+		if (ret)
+			return ret;
+	}
+
+	return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true);
+}
+
+static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2,
+			 u8 device)
+{
+	u8 lodr_bits;
+
+	if (device & KMX61_ACC)
+		lodr_bits = (data->odr_bits >> KMX61_ACC_ODR_SHIFT) &
+			     KMX61_ACC_ODR_MASK;
+	else if (device & KMX61_MAG)
+		lodr_bits = (data->odr_bits >> KMX61_MAG_ODR_SHIFT) &
+			     KMX61_MAG_ODR_MASK;
+	else
+		return -EINVAL;
+
+	if (lodr_bits >= ARRAY_SIZE(kmx61_samp_freq_table))
+		return -EINVAL;
+
+	*val = kmx61_samp_freq_table[lodr_bits].val;
+	*val2 = kmx61_samp_freq_table[lodr_bits].val2;
+
+	return 0;
+}
+
+static int kmx61_set_range(struct kmx61_data *data, u8 range)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+		return ret;
+	}
+
+	ret &= ~KMX61_REG_CTRL1_GSEL_MASK;
+	ret |= range & KMX61_REG_CTRL1_GSEL_MASK;
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+		return ret;
+	}
+
+	data->range = range;
+
+	return 0;
+}
+
+static int kmx61_set_scale(struct kmx61_data *data, u16 uscale)
+{
+	int ret, i;
+	u8  mode;
+
+	for (i = 0; i < ARRAY_SIZE(kmx61_uscale_table); i++) {
+		if (kmx61_uscale_table[i] == uscale) {
+			ret = kmx61_get_mode(data, &mode,
+					     KMX61_ACC | KMX61_MAG);
+			if (ret < 0)
+				return ret;
+
+			ret = kmx61_set_mode(data, KMX61_ALL_STBY,
+					     KMX61_ACC | KMX61_MAG, true);
+			if (ret < 0)
+				return ret;
+
+			ret = kmx61_set_range(data, i);
+			if (ret < 0)
+				return ret;
+
+			return  kmx61_set_mode(data, mode,
+					       KMX61_ACC | KMX61_MAG, true);
+		}
+	}
+	return -EINVAL;
+}
+
+static int kmx61_chip_init(struct kmx61_data *data)
+{
+	int ret, val, val2;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_WHO_AM_I);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading who_am_i\n");
+		return ret;
+	}
+
+	if (ret != KMX61_CHIP_ID) {
+		dev_err(&data->client->dev,
+			"Wrong chip id, got %x expected %x\n",
+			 ret, KMX61_CHIP_ID);
+		return -EINVAL;
+	}
+
+	/* set accel 12bit, 4g range */
+	ret = kmx61_set_range(data, KMX61_RANGE_4G);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_ODCNTL);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_odcntl\n");
+		return ret;
+	}
+	data->odr_bits = ret;
+
+	/*
+	 * set output data rate for wake up (motion detection) function
+	 * to match data rate for accelerometer sampling
+	 */
+	ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC);
+	if (ret < 0)
+		return ret;
+
+	ret = kmx61_set_wake_up_odr(data, val, val2);
+	if (ret < 0)
+		return ret;
+
+	/* set acc/magn to OPERATION mode */
+	ret = kmx61_set_mode(data, 0, KMX61_ACC | KMX61_MAG, true);
+	if (ret < 0)
+		return ret;
+
+	data->wake_thresh = KMX61_DEFAULT_WAKE_THRESH;
+	data->wake_duration = KMX61_DEFAULT_WAKE_DURATION;
+
+	return 0;
+}
+
+static int kmx61_setup_new_data_interrupt(struct kmx61_data *data,
+					  bool status, u8 device)
+{
+	u8 mode;
+	int ret;
+
+	ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG);
+	if (ret < 0)
+		return ret;
+
+	ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+		return ret;
+	}
+
+	if (status) {
+		ret |= KMX61_REG_INC1_BIT_IEN;
+		if (device & KMX61_ACC)
+			ret |= KMX61_REG_INC1_BIT_DRDYA;
+		if (device & KMX61_MAG)
+			ret |=  KMX61_REG_INC1_BIT_DRDYM;
+	} else {
+		ret &= ~KMX61_REG_INC1_BIT_IEN;
+		if (device & KMX61_ACC)
+			ret &= ~KMX61_REG_INC1_BIT_DRDYA;
+		if (device & KMX61_MAG)
+			ret &= ~KMX61_REG_INC1_BIT_DRDYM;
+	}
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
+		return ret;
+	}
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+		return ret;
+	}
+
+	if (status)
+		ret |= KMX61_REG_CTRL1_BIT_DRDYE;
+	else
+		ret &= ~KMX61_REG_CTRL1_BIT_DRDYE;
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+		return ret;
+	}
+
+	return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true);
+}
+
+static int kmx61_chip_update_thresholds(struct kmx61_data *data)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(data->client,
+					KMX61_REG_WUF_TIMER,
+					data->wake_duration);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Errow writing reg_wuf_timer\n");
+		return ret;
+	}
+
+	ret = i2c_smbus_write_byte_data(data->client,
+					KMX61_REG_WUF_THRESH,
+					data->wake_thresh);
+	if (ret < 0)
+		dev_err(&data->client->dev, "Error writing reg_wuf_thresh\n");
+
+	return ret;
+}
+
+static int kmx61_setup_any_motion_interrupt(struct kmx61_data *data,
+					    bool status)
+{
+	u8 mode;
+	int ret;
+
+	ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG);
+	if (ret < 0)
+		return ret;
+
+	ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+	if (ret < 0)
+		return ret;
+
+	ret = kmx61_chip_update_thresholds(data);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_inc1\n");
+		return ret;
+	}
+	if (status)
+		ret |= (KMX61_REG_INC1_BIT_IEN | KMX61_REG_INC1_BIT_WUFS);
+	else
+		ret &= ~(KMX61_REG_INC1_BIT_IEN | KMX61_REG_INC1_BIT_WUFS);
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error writing reg_inc1\n");
+		return ret;
+	}
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+		return ret;
+	}
+
+	if (status)
+		ret |= KMX61_REG_CTRL1_BIT_WUFE | KMX61_REG_CTRL1_BIT_BTSE;
+	else
+		ret &= ~(KMX61_REG_CTRL1_BIT_WUFE | KMX61_REG_CTRL1_BIT_BTSE);
+
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+		return ret;
+	}
+	mode |= KMX61_ACT_STBY_BIT;
+	return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true);
+}
+
+/**
+ * kmx61_set_power_state() - set power state for kmx61 @device
+ * @data - kmx61 device private pointer
+ * @on - power state to be set for @device
+ * @device - bitmask indicating device for which @on state needs to be set
+ *
+ * Notice that when ACC power state needs to be set to ON and MAG is in
+ * OPERATION then we know that kmx61_runtime_resume was already called
+ * so we must set ACC OPERATION mode here. The same happens when MAG power
+ * state needs to be set to ON and ACC is in OPERATION.
+ */
+static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device)
+{
+#ifdef CONFIG_PM
+	int ret;
+
+	if (device & KMX61_ACC) {
+		if (on && !data->acc_ps && !data->mag_stby) {
+			ret = kmx61_set_mode(data, 0, KMX61_ACC, true);
+			if (ret < 0)
+				return ret;
+		}
+		data->acc_ps = on;
+	}
+	if (device & KMX61_MAG) {
+		if (on && !data->mag_ps && !data->acc_stby) {
+			ret = kmx61_set_mode(data, 0, KMX61_MAG, true);
+			if (ret < 0)
+				return ret;
+		}
+		data->mag_ps = on;
+	}
+
+	if (on) {
+		ret = pm_runtime_get_sync(&data->client->dev);
+	} else {
+		pm_runtime_mark_last_busy(&data->client->dev);
+		ret = pm_runtime_put_autosuspend(&data->client->dev);
+	}
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"Failed: kmx61_set_power_state for %d, ret %d\n",
+			on, ret);
+		if (on)
+			pm_runtime_put_noidle(&data->client->dev);
+
+		return ret;
+	}
+#endif
+	return 0;
+}
+
+static int kmx61_read_measurement(struct kmx61_data *data, u8 base, u8 offset)
+{
+	int ret;
+	u8 reg = base + offset * 2;
+
+	ret = i2c_smbus_read_word_data(data->client, reg);
+	if (ret < 0)
+		dev_err(&data->client->dev, "failed to read reg at %x\n", reg);
+
+	return ret;
+}
+
+static int kmx61_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan, int *val,
+			  int *val2, long mask)
+{
+	int ret;
+	u8 base_reg;
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->type) {
+		case IIO_ACCEL:
+			base_reg = KMX61_ACC_XOUT_L;
+			break;
+		case IIO_MAGN:
+			base_reg = KMX61_MAG_XOUT_L;
+			break;
+		default:
+			return -EINVAL;
+		}
+		mutex_lock(&data->lock);
+
+		ret = kmx61_set_power_state(data, true, chan->address);
+		if (ret) {
+			mutex_unlock(&data->lock);
+			return ret;
+		}
+
+		ret = kmx61_read_measurement(data, base_reg, chan->scan_index);
+		if (ret < 0) {
+			kmx61_set_power_state(data, false, chan->address);
+			mutex_unlock(&data->lock);
+			return ret;
+		}
+		*val = sign_extend32(ret >> chan->scan_type.shift,
+				     chan->scan_type.realbits - 1);
+		ret = kmx61_set_power_state(data, false, chan->address);
+
+		mutex_unlock(&data->lock);
+		if (ret)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ACCEL:
+			*val = 0;
+			*val2 = kmx61_uscale_table[data->range];
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_MAGN:
+			/* 14 bits res, 1465 microGauss per magn count */
+			*val = 0;
+			*val2 = 1465;
+			return IIO_VAL_INT_PLUS_MICRO;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN)
+			return -EINVAL;
+
+		mutex_lock(&data->lock);
+		ret = kmx61_get_odr(data, val, val2, chan->address);
+		mutex_unlock(&data->lock);
+		if (ret)
+			return -EINVAL;
+		return IIO_VAL_INT_PLUS_MICRO;
+	}
+	return -EINVAL;
+}
+
+static int kmx61_write_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan, int val,
+			   int val2, long mask)
+{
+	int ret;
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN)
+			return -EINVAL;
+
+		mutex_lock(&data->lock);
+		ret = kmx61_set_odr(data, val, val2, chan->address);
+		mutex_unlock(&data->lock);
+		return ret;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ACCEL:
+			if (val != 0)
+				return -EINVAL;
+			mutex_lock(&data->lock);
+			ret = kmx61_set_scale(data, val2);
+			mutex_unlock(&data->lock);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int kmx61_read_event(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 kmx61_data *data = kmx61_get_data(indio_dev);
+
+	*val2 = 0;
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		*val = data->wake_thresh;
+		return IIO_VAL_INT;
+	case IIO_EV_INFO_PERIOD:
+		*val = data->wake_duration;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int kmx61_write_event(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 kmx61_data *data = kmx61_get_data(indio_dev);
+
+	if (data->ev_enable_state)
+		return -EBUSY;
+
+	switch (info) {
+	case IIO_EV_INFO_VALUE:
+		data->wake_thresh = val;
+		return IIO_VAL_INT;
+	case IIO_EV_INFO_PERIOD:
+		data->wake_duration = val;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int kmx61_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 kmx61_data *data = kmx61_get_data(indio_dev);
+
+	return data->ev_enable_state;
+}
+
+static int kmx61_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 kmx61_data *data = kmx61_get_data(indio_dev);
+	int ret = 0;
+
+	if (state && data->ev_enable_state)
+		return 0;
+
+	mutex_lock(&data->lock);
+
+	if (!state && data->motion_trig_on) {
+		data->ev_enable_state = false;
+		goto err_unlock;
+	}
+
+	ret = kmx61_set_power_state(data, state, KMX61_ACC);
+	if (ret < 0)
+		goto err_unlock;
+
+	ret = kmx61_setup_any_motion_interrupt(data, state);
+	if (ret < 0) {
+		kmx61_set_power_state(data, false, KMX61_ACC);
+		goto err_unlock;
+	}
+
+	data->ev_enable_state = state;
+
+err_unlock:
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static int kmx61_acc_validate_trigger(struct iio_dev *indio_dev,
+				      struct iio_trigger *trig)
+{
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+	if (data->acc_dready_trig != trig && data->motion_trig != trig)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int kmx61_mag_validate_trigger(struct iio_dev *indio_dev,
+				      struct iio_trigger *trig)
+{
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+	if (data->mag_dready_trig != trig)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct iio_info kmx61_acc_info = {
+	.read_raw		= kmx61_read_raw,
+	.write_raw		= kmx61_write_raw,
+	.attrs			= &kmx61_acc_attribute_group,
+	.read_event_value	= kmx61_read_event,
+	.write_event_value	= kmx61_write_event,
+	.read_event_config	= kmx61_read_event_config,
+	.write_event_config	= kmx61_write_event_config,
+	.validate_trigger	= kmx61_acc_validate_trigger,
+};
+
+static const struct iio_info kmx61_mag_info = {
+	.read_raw		= kmx61_read_raw,
+	.write_raw		= kmx61_write_raw,
+	.attrs			= &kmx61_mag_attribute_group,
+	.validate_trigger	= kmx61_mag_validate_trigger,
+};
+
+
+static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig,
+					    bool state)
+{
+	int ret = 0;
+	u8 device;
+
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+	mutex_lock(&data->lock);
+
+	if (!state && data->ev_enable_state && data->motion_trig_on) {
+		data->motion_trig_on = false;
+		goto err_unlock;
+	}
+
+	if (data->acc_dready_trig == trig || data->motion_trig == trig)
+		device = KMX61_ACC;
+	else
+		device = KMX61_MAG;
+
+	ret = kmx61_set_power_state(data, state, device);
+	if (ret < 0)
+		goto err_unlock;
+
+	if (data->acc_dready_trig == trig || data->mag_dready_trig == trig)
+		ret = kmx61_setup_new_data_interrupt(data, state, device);
+	else
+		ret = kmx61_setup_any_motion_interrupt(data, state);
+	if (ret < 0) {
+		kmx61_set_power_state(data, false, device);
+		goto err_unlock;
+	}
+
+	if (data->acc_dready_trig == trig)
+		data->acc_dready_trig_on = state;
+	else if (data->mag_dready_trig == trig)
+		data->mag_dready_trig_on = state;
+	else
+		data->motion_trig_on = state;
+err_unlock:
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static int kmx61_trig_try_reenable(struct iio_trigger *trig)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INL);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_inl\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct iio_trigger_ops kmx61_trigger_ops = {
+	.set_trigger_state = kmx61_data_rdy_trigger_set_state,
+	.try_reenable = kmx61_trig_try_reenable,
+};
+
+static irqreturn_t kmx61_event_handler(int irq, void *private)
+{
+	struct kmx61_data *data = private;
+	struct iio_dev *indio_dev = data->acc_indio_dev;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INS1);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading reg_ins1\n");
+		goto ack_intr;
+	}
+
+	if (ret & KMX61_REG_INS1_BIT_WUFS) {
+		ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INS2);
+		if (ret < 0) {
+			dev_err(&data->client->dev, "Error reading reg_ins2\n");
+			goto ack_intr;
+		}
+
+		if (ret & KMX61_REG_INS2_BIT_XN)
+			iio_push_event(indio_dev,
+				       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+				       0,
+				       IIO_MOD_X,
+				       IIO_EV_TYPE_THRESH,
+				       IIO_EV_DIR_FALLING),
+				       0);
+
+		if (ret & KMX61_REG_INS2_BIT_XP)
+			iio_push_event(indio_dev,
+				       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+				       0,
+				       IIO_MOD_X,
+				       IIO_EV_TYPE_THRESH,
+				       IIO_EV_DIR_RISING),
+				       0);
+
+		if (ret & KMX61_REG_INS2_BIT_YN)
+			iio_push_event(indio_dev,
+				       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+				       0,
+				       IIO_MOD_Y,
+				       IIO_EV_TYPE_THRESH,
+				       IIO_EV_DIR_FALLING),
+				       0);
+
+		if (ret & KMX61_REG_INS2_BIT_YP)
+			iio_push_event(indio_dev,
+				       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+				       0,
+				       IIO_MOD_Y,
+				       IIO_EV_TYPE_THRESH,
+				       IIO_EV_DIR_RISING),
+				       0);
+
+		if (ret & KMX61_REG_INS2_BIT_ZN)
+			iio_push_event(indio_dev,
+				       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+				       0,
+				       IIO_MOD_Z,
+				       IIO_EV_TYPE_THRESH,
+				       IIO_EV_DIR_FALLING),
+				       0);
+
+		if (ret & KMX61_REG_INS2_BIT_ZP)
+			iio_push_event(indio_dev,
+				       IIO_MOD_EVENT_CODE(IIO_ACCEL,
+				       0,
+				       IIO_MOD_Z,
+				       IIO_EV_TYPE_THRESH,
+				       IIO_EV_DIR_RISING),
+				       0);
+	}
+
+ack_intr:
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+	if (ret < 0)
+		dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+
+	ret |= KMX61_REG_CTRL1_BIT_RES;
+	ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+	if (ret < 0)
+		dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+
+	ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INL);
+	if (ret < 0)
+		dev_err(&data->client->dev, "Error reading reg_inl\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t kmx61_data_rdy_trig_poll(int irq, void *private)
+{
+	struct kmx61_data *data = private;
+
+	if (data->acc_dready_trig_on)
+		iio_trigger_poll(data->acc_dready_trig);
+	if (data->mag_dready_trig_on)
+		iio_trigger_poll(data->mag_dready_trig);
+
+	if (data->motion_trig_on)
+		iio_trigger_poll(data->motion_trig);
+
+	if (data->ev_enable_state)
+		return IRQ_WAKE_THREAD;
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t kmx61_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct kmx61_data *data = kmx61_get_data(indio_dev);
+	int bit, ret, i = 0;
+	u8 base;
+	s16 buffer[8];
+
+	if (indio_dev == data->acc_indio_dev)
+		base = KMX61_ACC_XOUT_L;
+	else
+		base = KMX61_MAG_XOUT_L;
+
+	mutex_lock(&data->lock);
+	for_each_set_bit(bit, indio_dev->active_scan_mask,
+			 indio_dev->masklength) {
+		ret = kmx61_read_measurement(data, base, bit);
+		if (ret < 0) {
+			mutex_unlock(&data->lock);
+			goto err;
+		}
+		buffer[i++] = ret;
+	}
+	mutex_unlock(&data->lock);
+
+	iio_push_to_buffers(indio_dev, buffer);
+err:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static const char *kmx61_match_acpi_device(struct device *dev)
+{
+	const struct acpi_device_id *id;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return NULL;
+	return dev_name(dev);
+}
+
+static struct iio_dev *kmx61_indiodev_setup(struct kmx61_data *data,
+					    const struct iio_info *info,
+					    const struct iio_chan_spec *chan,
+					    int num_channels,
+					    const char *name)
+{
+	struct iio_dev *indio_dev;
+
+	indio_dev = devm_iio_device_alloc(&data->client->dev, sizeof(data));
+	if (!indio_dev)
+		return ERR_PTR(-ENOMEM);
+
+	kmx61_set_data(indio_dev, data);
+
+	indio_dev->dev.parent = &data->client->dev;
+	indio_dev->channels = chan;
+	indio_dev->num_channels = num_channels;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = info;
+
+	return indio_dev;
+}
+
+static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data,
+					       struct iio_dev *indio_dev,
+					       const char *tag)
+{
+	struct iio_trigger *trig;
+	int ret;
+
+	trig = devm_iio_trigger_alloc(&data->client->dev,
+				      "%s-%s-dev%d",
+				      indio_dev->name,
+				      tag,
+				      indio_dev->id);
+	if (!trig)
+		return ERR_PTR(-ENOMEM);
+
+	trig->dev.parent = &data->client->dev;
+	trig->ops = &kmx61_trigger_ops;
+	iio_trigger_set_drvdata(trig, indio_dev);
+
+	ret = iio_trigger_register(trig);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return trig;
+}
+
+static int kmx61_probe(struct i2c_client *client,
+		       const struct i2c_device_id *id)
+{
+	int ret;
+	struct kmx61_data *data;
+	const char *name = NULL;
+
+	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, data);
+	data->client = client;
+
+	mutex_init(&data->lock);
+
+	if (id)
+		name = id->name;
+	else if (ACPI_HANDLE(&client->dev))
+		name = kmx61_match_acpi_device(&client->dev);
+	else
+		return -ENODEV;
+
+	data->acc_indio_dev =
+		kmx61_indiodev_setup(data, &kmx61_acc_info,
+				     kmx61_acc_channels,
+				     ARRAY_SIZE(kmx61_acc_channels),
+				     name);
+	if (IS_ERR(data->acc_indio_dev))
+		return PTR_ERR(data->acc_indio_dev);
+
+	data->mag_indio_dev =
+		kmx61_indiodev_setup(data, &kmx61_mag_info,
+				     kmx61_mag_channels,
+				     ARRAY_SIZE(kmx61_mag_channels),
+				     name);
+	if (IS_ERR(data->mag_indio_dev))
+		return PTR_ERR(data->mag_indio_dev);
+
+	ret = kmx61_chip_init(data);
+	if (ret < 0)
+		return ret;
+
+	if (client->irq > 0) {
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+						kmx61_data_rdy_trig_poll,
+						kmx61_event_handler,
+						IRQF_TRIGGER_RISING,
+						KMX61_IRQ_NAME,
+						data);
+		if (ret)
+			goto err_chip_uninit;
+
+		data->acc_dready_trig =
+			kmx61_trigger_setup(data, data->acc_indio_dev,
+					    "dready");
+		if (IS_ERR(data->acc_dready_trig)) {
+			ret = PTR_ERR(data->acc_dready_trig);
+			goto err_chip_uninit;
+		}
+
+		data->mag_dready_trig =
+			kmx61_trigger_setup(data, data->mag_indio_dev,
+					    "dready");
+		if (IS_ERR(data->mag_dready_trig)) {
+			ret = PTR_ERR(data->mag_dready_trig);
+			goto err_trigger_unregister_acc_dready;
+		}
+
+		data->motion_trig =
+			kmx61_trigger_setup(data, data->acc_indio_dev,
+					    "any-motion");
+		if (IS_ERR(data->motion_trig)) {
+			ret = PTR_ERR(data->motion_trig);
+			goto err_trigger_unregister_mag_dready;
+		}
+
+		ret = iio_triggered_buffer_setup(data->acc_indio_dev,
+						 &iio_pollfunc_store_time,
+						 kmx61_trigger_handler,
+						 NULL);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"Failed to setup acc triggered buffer\n");
+			goto err_trigger_unregister_motion;
+		}
+
+		ret = iio_triggered_buffer_setup(data->mag_indio_dev,
+						 &iio_pollfunc_store_time,
+						 kmx61_trigger_handler,
+						 NULL);
+		if (ret < 0) {
+			dev_err(&data->client->dev,
+				"Failed to setup mag triggered buffer\n");
+			goto err_buffer_cleanup_acc;
+		}
+	}
+
+	ret = pm_runtime_set_active(&client->dev);
+	if (ret < 0)
+		goto err_buffer_cleanup_mag;
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS);
+	pm_runtime_use_autosuspend(&client->dev);
+
+	ret = iio_device_register(data->acc_indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to register acc iio device\n");
+		goto err_buffer_cleanup_mag;
+	}
+
+	ret = iio_device_register(data->mag_indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to register mag iio device\n");
+		goto err_iio_unregister_acc;
+	}
+
+	return 0;
+
+err_iio_unregister_acc:
+	iio_device_unregister(data->acc_indio_dev);
+err_buffer_cleanup_mag:
+	if (client->irq > 0)
+		iio_triggered_buffer_cleanup(data->mag_indio_dev);
+err_buffer_cleanup_acc:
+	if (client->irq > 0)
+		iio_triggered_buffer_cleanup(data->acc_indio_dev);
+err_trigger_unregister_motion:
+	iio_trigger_unregister(data->motion_trig);
+err_trigger_unregister_mag_dready:
+	iio_trigger_unregister(data->mag_dready_trig);
+err_trigger_unregister_acc_dready:
+	iio_trigger_unregister(data->acc_dready_trig);
+err_chip_uninit:
+	kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+	return ret;
+}
+
+static int kmx61_remove(struct i2c_client *client)
+{
+	struct kmx61_data *data = i2c_get_clientdata(client);
+
+	iio_device_unregister(data->acc_indio_dev);
+	iio_device_unregister(data->mag_indio_dev);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+
+	if (client->irq > 0) {
+		iio_triggered_buffer_cleanup(data->acc_indio_dev);
+		iio_triggered_buffer_cleanup(data->mag_indio_dev);
+		iio_trigger_unregister(data->acc_dready_trig);
+		iio_trigger_unregister(data->mag_dready_trig);
+		iio_trigger_unregister(data->motion_trig);
+	}
+
+	mutex_lock(&data->lock);
+	kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+	mutex_unlock(&data->lock);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kmx61_suspend(struct device *dev)
+{
+	int ret;
+	struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+	mutex_lock(&data->lock);
+	ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG,
+			     false);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static int kmx61_resume(struct device *dev)
+{
+	u8 stby = 0;
+	struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+	if (data->acc_stby)
+		stby |= KMX61_ACC_STBY_BIT;
+	if (data->mag_stby)
+		stby |= KMX61_MAG_STBY_BIT;
+
+	return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int kmx61_runtime_suspend(struct device *dev)
+{
+	struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static int kmx61_runtime_resume(struct device *dev)
+{
+	struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+	u8 stby = 0;
+
+	if (!data->acc_ps)
+		stby |= KMX61_ACC_STBY_BIT;
+	if (!data->mag_ps)
+		stby |= KMX61_MAG_STBY_BIT;
+
+	return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true);
+}
+#endif
+
+static const struct dev_pm_ops kmx61_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume)
+	SET_RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id kmx61_acpi_match[] = {
+	{"KMX61021", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, kmx61_acpi_match);
+
+static const struct i2c_device_id kmx61_id[] = {
+	{"kmx611021", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, kmx61_id);
+
+static struct i2c_driver kmx61_driver = {
+	.driver = {
+		.name = KMX61_DRV_NAME,
+		.acpi_match_table = ACPI_PTR(kmx61_acpi_match),
+		.pm = &kmx61_pm_ops,
+	},
+	.probe		= kmx61_probe,
+	.remove		= kmx61_remove,
+	.id_table	= kmx61_id,
+};
+
+module_i2c_driver(kmx61_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("KMX61 accelerometer/magnetometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig
new file mode 100644
index 0000000..ccc817e
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/Kconfig
@@ -0,0 +1,25 @@
+
+config IIO_ST_LSM6DSX
+	tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
+	depends on (I2C || SPI)
+	select IIO_BUFFER
+	select IIO_KFIFO_BUF
+	select IIO_ST_LSM6DSX_I2C if (I2C)
+	select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
+	help
+	  Say yes here to build support for STMicroelectronics LSM6DSx imu
+	  sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
+	  ism330dlc
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called st_lsm6dsx.
+
+config IIO_ST_LSM6DSX_I2C
+	tristate
+	depends on IIO_ST_LSM6DSX
+	select REGMAP_I2C
+
+config IIO_ST_LSM6DSX_SPI
+	tristate
+	depends on IIO_ST_LSM6DSX
+	select REGMAP_SPI
diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
new file mode 100644
index 0000000..35919fe
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/Makefile
@@ -0,0 +1,5 @@
+st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
+
+obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
+obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
+obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
new file mode 100644
index 0000000..edcd838
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -0,0 +1,179 @@
+/*
+ * STMicroelectronics st_lsm6dsx sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_LSM6DSX_H
+#define ST_LSM6DSX_H
+
+#include <linux/device.h>
+
+#define ST_LSM6DS3_DEV_NAME	"lsm6ds3"
+#define ST_LSM6DS3H_DEV_NAME	"lsm6ds3h"
+#define ST_LSM6DSL_DEV_NAME	"lsm6dsl"
+#define ST_LSM6DSM_DEV_NAME	"lsm6dsm"
+#define ST_ISM330DLC_DEV_NAME	"ism330dlc"
+
+enum st_lsm6dsx_hw_id {
+	ST_LSM6DS3_ID,
+	ST_LSM6DS3H_ID,
+	ST_LSM6DSL_ID,
+	ST_LSM6DSM_ID,
+	ST_ISM330DLC_ID,
+	ST_LSM6DSX_MAX_ID,
+};
+
+#define ST_LSM6DSX_BUFF_SIZE		400
+#define ST_LSM6DSX_CHAN_SIZE		2
+#define ST_LSM6DSX_SAMPLE_SIZE		6
+#define ST_LSM6DSX_MAX_WORD_LEN		((32 / ST_LSM6DSX_SAMPLE_SIZE) * \
+					 ST_LSM6DSX_SAMPLE_SIZE)
+#define ST_LSM6DSX_SHIFT_VAL(val, mask)	(((val) << __ffs(mask)) & (mask))
+
+struct st_lsm6dsx_reg {
+	u8 addr;
+	u8 mask;
+};
+
+/**
+ * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
+ * @fifo_th: FIFO threshold register info (addr + mask).
+ * @fifo_diff: FIFO diff status register info (addr + mask).
+ * @th_wl: FIFO threshold word length.
+ */
+struct st_lsm6dsx_fifo_ops {
+	struct {
+		u8 addr;
+		u16 mask;
+	} fifo_th;
+	struct {
+		u8 addr;
+		u16 mask;
+	} fifo_diff;
+	u8 th_wl;
+};
+
+/**
+ * struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings
+ * @timer_en: Hw timer enable register info (addr + mask).
+ * @hr_timer: Hw timer resolution register info (addr + mask).
+ * @fifo_en: Hw timer FIFO enable register info (addr + mask).
+ * @decimator: Hw timer FIFO decimator register info (addr + mask).
+ */
+struct st_lsm6dsx_hw_ts_settings {
+	struct st_lsm6dsx_reg timer_en;
+	struct st_lsm6dsx_reg hr_timer;
+	struct st_lsm6dsx_reg fifo_en;
+	struct st_lsm6dsx_reg decimator;
+};
+
+/**
+ * struct st_lsm6dsx_settings - ST IMU sensor settings
+ * @wai: Sensor WhoAmI default value.
+ * @max_fifo_size: Sensor max fifo length in FIFO words.
+ * @id: List of hw id supported by the driver configuration.
+ * @decimator: List of decimator register info (addr + mask).
+ * @fifo_ops: Sensor hw FIFO parameters.
+ * @ts_settings: Hw timer related settings.
+ */
+struct st_lsm6dsx_settings {
+	u8 wai;
+	u16 max_fifo_size;
+	enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
+	struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
+	struct st_lsm6dsx_fifo_ops fifo_ops;
+	struct st_lsm6dsx_hw_ts_settings ts_settings;
+};
+
+enum st_lsm6dsx_sensor_id {
+	ST_LSM6DSX_ID_ACC,
+	ST_LSM6DSX_ID_GYRO,
+	ST_LSM6DSX_ID_MAX,
+};
+
+enum st_lsm6dsx_fifo_mode {
+	ST_LSM6DSX_FIFO_BYPASS = 0x0,
+	ST_LSM6DSX_FIFO_CONT = 0x6,
+};
+
+/**
+ * struct st_lsm6dsx_sensor - ST IMU sensor instance
+ * @name: Sensor name.
+ * @id: Sensor identifier.
+ * @hw: Pointer to instance of struct st_lsm6dsx_hw.
+ * @gain: Configured sensor sensitivity.
+ * @odr: Output data rate of the sensor [Hz].
+ * @watermark: Sensor watermark level.
+ * @sip: Number of samples in a given pattern.
+ * @decimator: FIFO decimation factor.
+ * @ts_ref: Sensor timestamp reference for hw one.
+ */
+struct st_lsm6dsx_sensor {
+	char name[32];
+	enum st_lsm6dsx_sensor_id id;
+	struct st_lsm6dsx_hw *hw;
+
+	u32 gain;
+	u16 odr;
+
+	u16 watermark;
+	u8 sip;
+	u8 decimator;
+	s64 ts_ref;
+};
+
+/**
+ * struct st_lsm6dsx_hw - ST IMU MEMS hw instance
+ * @dev: Pointer to instance of struct device (I2C or SPI).
+ * @regmap: Register map of the device.
+ * @irq: Device interrupt line (I2C or SPI).
+ * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
+ * @conf_lock: Mutex to prevent concurrent FIFO configuration update.
+ * @fifo_mode: FIFO operating mode supported by the device.
+ * @enable_mask: Enabled sensor bitmask.
+ * @ts_sip: Total number of timestamp samples in a given pattern.
+ * @sip: Total number of samples (acc/gyro/ts) in a given pattern.
+ * @buff: Device read buffer.
+ * @iio_devs: Pointers to acc/gyro iio_dev instances.
+ * @settings: Pointer to the specific sensor settings in use.
+ */
+struct st_lsm6dsx_hw {
+	struct device *dev;
+	struct regmap *regmap;
+	int irq;
+
+	struct mutex fifo_lock;
+	struct mutex conf_lock;
+
+	enum st_lsm6dsx_fifo_mode fifo_mode;
+	u8 enable_mask;
+	u8 ts_sip;
+	u8 sip;
+
+	u8 *buff;
+
+	struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX];
+
+	const struct st_lsm6dsx_settings *settings;
+};
+
+extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
+
+int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
+		     struct regmap *regmap);
+int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
+int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
+int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
+int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
+				u16 watermark);
+int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
+int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
+			     enum st_lsm6dsx_fifo_mode fifo_mode);
+
+#endif /* ST_LSM6DSX_H */
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
new file mode 100644
index 0000000..631360b
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -0,0 +1,569 @@
+/*
+ * STMicroelectronics st_lsm6dsx FIFO buffer library driver
+ *
+ * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be
+ * configured to store data from gyroscope and accelerometer. Samples are
+ * queued without any tag according to a specific pattern based on
+ * 'FIFO data sets' (6 bytes each):
+ *  - 1st data set is reserved for gyroscope data
+ *  - 2nd data set is reserved for accelerometer data
+ * The FIFO pattern changes depending on the ODRs and decimation factors
+ * assigned to the FIFO data sets. The first sequence of data stored in FIFO
+ * buffer contains the data of all the enabled FIFO data sets
+ * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
+ * value of the decimation factor and ODR set for each FIFO data set.
+ * FIFO supported modes:
+ *  - BYPASS: FIFO disabled
+ *  - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index
+ *    restarts from the beginning and the oldest sample is overwritten
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+
+#include <linux/platform_data/st_sensors_pdata.h>
+
+#include "st_lsm6dsx.h"
+
+#define ST_LSM6DSX_REG_HLACTIVE_ADDR		0x12
+#define ST_LSM6DSX_REG_HLACTIVE_MASK		BIT(5)
+#define ST_LSM6DSX_REG_PP_OD_ADDR		0x12
+#define ST_LSM6DSX_REG_PP_OD_MASK		BIT(4)
+#define ST_LSM6DSX_REG_FIFO_MODE_ADDR		0x0a
+#define ST_LSM6DSX_FIFO_MODE_MASK		GENMASK(2, 0)
+#define ST_LSM6DSX_FIFO_ODR_MASK		GENMASK(6, 3)
+#define ST_LSM6DSX_FIFO_EMPTY_MASK		BIT(12)
+#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR		0x3e
+#define ST_LSM6DSX_REG_TS_RESET_ADDR		0x42
+
+#define ST_LSM6DSX_MAX_FIFO_ODR_VAL		0x08
+
+#define ST_LSM6DSX_TS_SENSITIVITY		25000UL /* 25us */
+#define ST_LSM6DSX_TS_RESET_VAL			0xaa
+
+struct st_lsm6dsx_decimator_entry {
+	u8 decimator;
+	u8 val;
+};
+
+static const
+struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
+	{  0, 0x0 },
+	{  1, 0x1 },
+	{  2, 0x2 },
+	{  3, 0x3 },
+	{  4, 0x4 },
+	{  8, 0x5 },
+	{ 16, 0x6 },
+	{ 32, 0x7 },
+};
+
+static int st_lsm6dsx_get_decimator_val(u8 val)
+{
+	const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table);
+	int i;
+
+	for (i = 0; i < max_size; i++)
+		if (st_lsm6dsx_decimator_table[i].decimator == val)
+			break;
+
+	return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val;
+}
+
+static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
+				       u16 *max_odr, u16 *min_odr)
+{
+	struct st_lsm6dsx_sensor *sensor;
+	int i;
+
+	*max_odr = 0, *min_odr = ~0;
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		sensor = iio_priv(hw->iio_devs[i]);
+
+		if (!(hw->enable_mask & BIT(sensor->id)))
+			continue;
+
+		*max_odr = max_t(u16, *max_odr, sensor->odr);
+		*min_odr = min_t(u16, *min_odr, sensor->odr);
+	}
+}
+
+static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
+{
+	u16 max_odr, min_odr, sip = 0, ts_sip = 0;
+	const struct st_lsm6dsx_reg *ts_dec_reg;
+	struct st_lsm6dsx_sensor *sensor;
+	int err = 0, i;
+	u8 data;
+
+	st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		const struct st_lsm6dsx_reg *dec_reg;
+
+		sensor = iio_priv(hw->iio_devs[i]);
+		/* update fifo decimators and sample in pattern */
+		if (hw->enable_mask & BIT(sensor->id)) {
+			sensor->sip = sensor->odr / min_odr;
+			sensor->decimator = max_odr / sensor->odr;
+			data = st_lsm6dsx_get_decimator_val(sensor->decimator);
+		} else {
+			sensor->sip = 0;
+			sensor->decimator = 0;
+			data = 0;
+		}
+		ts_sip = max_t(u16, ts_sip, sensor->sip);
+
+		dec_reg = &hw->settings->decimator[sensor->id];
+		if (dec_reg->addr) {
+			int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask);
+
+			err = regmap_update_bits(hw->regmap, dec_reg->addr,
+						 dec_reg->mask, val);
+			if (err < 0)
+				return err;
+		}
+		sip += sensor->sip;
+	}
+	hw->sip = sip + ts_sip;
+	hw->ts_sip = ts_sip;
+
+	/*
+	 * update hw ts decimator if necessary. Decimator for hw timestamp
+	 * is always 1 or 0 in order to have a ts sample for each data
+	 * sample in FIFO
+	 */
+	ts_dec_reg = &hw->settings->ts_settings.decimator;
+	if (ts_dec_reg->addr) {
+		int val, ts_dec = !!hw->ts_sip;
+
+		val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
+		err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
+					 ts_dec_reg->mask, val);
+	}
+	return err;
+}
+
+int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
+			     enum st_lsm6dsx_fifo_mode fifo_mode)
+{
+	int err;
+
+	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+				 ST_LSM6DSX_FIFO_MODE_MASK,
+				 FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK,
+					    fifo_mode));
+	if (err < 0)
+		return err;
+
+	hw->fifo_mode = fifo_mode;
+
+	return 0;
+}
+
+static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor,
+				   bool enable)
+{
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	u8 data;
+
+	data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0;
+	return regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+				 ST_LSM6DSX_FIFO_ODR_MASK,
+				 FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, data));
+}
+
+int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
+{
+	u16 fifo_watermark = ~0, cur_watermark, fifo_th_mask;
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	struct st_lsm6dsx_sensor *cur_sensor;
+	int i, err, data;
+	__le16 wdata;
+
+	if (!hw->sip)
+		return 0;
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		cur_sensor = iio_priv(hw->iio_devs[i]);
+
+		if (!(hw->enable_mask & BIT(cur_sensor->id)))
+			continue;
+
+		cur_watermark = (cur_sensor == sensor) ? watermark
+						       : cur_sensor->watermark;
+
+		fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
+	}
+
+	fifo_watermark = max_t(u16, fifo_watermark, hw->sip);
+	fifo_watermark = (fifo_watermark / hw->sip) * hw->sip;
+	fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl;
+
+	err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1,
+			  &data);
+	if (err < 0)
+		return err;
+
+	fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask;
+	fifo_watermark = ((data << 8) & ~fifo_th_mask) |
+			 (fifo_watermark & fifo_th_mask);
+
+	wdata = cpu_to_le16(fifo_watermark);
+	return regmap_bulk_write(hw->regmap,
+				 hw->settings->fifo_ops.fifo_th.addr,
+				 &wdata, sizeof(wdata));
+}
+
+static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
+{
+	struct st_lsm6dsx_sensor *sensor;
+	int i, err;
+
+	/* reset hw ts counter */
+	err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
+			   ST_LSM6DSX_TS_RESET_VAL);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		sensor = iio_priv(hw->iio_devs[i]);
+		/*
+		 * store enable buffer timestamp as reference for
+		 * hw timestamp
+		 */
+		sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]);
+	}
+	return 0;
+}
+
+/*
+ * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid
+ * a kmalloc for each bus access
+ */
+static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
+					unsigned int data_len)
+{
+	unsigned int word_len, read_len = 0;
+	int err;
+
+	while (read_len < data_len) {
+		word_len = min_t(unsigned int, data_len - read_len,
+				 ST_LSM6DSX_MAX_WORD_LEN);
+		err = regmap_bulk_read(hw->regmap,
+				       ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
+				       data + read_len, word_len);
+		if (err < 0)
+			return err;
+		read_len += word_len;
+	}
+	return 0;
+}
+
+#define ST_LSM6DSX_IIO_BUFF_SIZE	(ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \
+					       sizeof(s64)) + sizeof(s64))
+/**
+ * st_lsm6dsx_read_fifo() - hw FIFO read routine
+ * @hw: Pointer to instance of struct st_lsm6dsx_hw.
+ *
+ * Read samples from the hw FIFO and push them to IIO buffers.
+ *
+ * Return: Number of bytes read from the FIFO
+ */
+static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
+{
+	u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
+	u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
+	int err, acc_sip, gyro_sip, ts_sip, read_len, offset;
+	struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
+	u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
+	u8 acc_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
+	bool reset_ts = false;
+	__le16 fifo_status;
+	s64 ts = 0;
+
+	err = regmap_bulk_read(hw->regmap,
+			       hw->settings->fifo_ops.fifo_diff.addr,
+			       &fifo_status, sizeof(fifo_status));
+	if (err < 0) {
+		dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
+			err);
+		return err;
+	}
+
+	if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK))
+		return 0;
+
+	fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) *
+		   ST_LSM6DSX_CHAN_SIZE;
+	fifo_len = (fifo_len / pattern_len) * pattern_len;
+
+	acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+	gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
+
+	for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
+		err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
+		if (err < 0) {
+			dev_err(hw->dev,
+				"failed to read pattern from fifo (err=%d)\n",
+				err);
+			return err;
+		}
+
+		/*
+		 * Data are written to the FIFO with a specific pattern
+		 * depending on the configured ODRs. The first sequence of data
+		 * stored in FIFO contains the data of all enabled sensors
+		 * (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated
+		 * depending on the value of the decimation factor set for each
+		 * sensor.
+		 *
+		 * Supposing the FIFO is storing data from gyroscope and
+		 * accelerometer at different ODRs:
+		 *   - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
+		 * Since the gyroscope ODR is twice the accelerometer one, the
+		 * following pattern is repeated every 9 samples:
+		 *   - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, ..
+		 */
+		gyro_sip = gyro_sensor->sip;
+		acc_sip = acc_sensor->sip;
+		ts_sip = hw->ts_sip;
+		offset = 0;
+
+		while (acc_sip > 0 || gyro_sip > 0) {
+			if (gyro_sip > 0) {
+				memcpy(gyro_buff, &hw->buff[offset],
+				       ST_LSM6DSX_SAMPLE_SIZE);
+				offset += ST_LSM6DSX_SAMPLE_SIZE;
+			}
+			if (acc_sip > 0) {
+				memcpy(acc_buff, &hw->buff[offset],
+				       ST_LSM6DSX_SAMPLE_SIZE);
+				offset += ST_LSM6DSX_SAMPLE_SIZE;
+			}
+
+			if (ts_sip-- > 0) {
+				u8 data[ST_LSM6DSX_SAMPLE_SIZE];
+
+				memcpy(data, &hw->buff[offset], sizeof(data));
+				/*
+				 * hw timestamp is 3B long and it is stored
+				 * in FIFO using 6B as 4th FIFO data set
+				 * according to this schema:
+				 * B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0]
+				 */
+				ts = data[1] << 16 | data[0] << 8 | data[3];
+				/*
+				 * check if hw timestamp engine is going to
+				 * reset (the sensor generates an interrupt
+				 * to signal the hw timestamp will reset in
+				 * 1.638s)
+				 */
+				if (!reset_ts && ts >= 0xff0000)
+					reset_ts = true;
+				ts *= ST_LSM6DSX_TS_SENSITIVITY;
+
+				offset += ST_LSM6DSX_SAMPLE_SIZE;
+			}
+
+			if (gyro_sip-- > 0)
+				iio_push_to_buffers_with_timestamp(
+					hw->iio_devs[ST_LSM6DSX_ID_GYRO],
+					gyro_buff, gyro_sensor->ts_ref + ts);
+			if (acc_sip-- > 0)
+				iio_push_to_buffers_with_timestamp(
+					hw->iio_devs[ST_LSM6DSX_ID_ACC],
+					acc_buff, acc_sensor->ts_ref + ts);
+		}
+	}
+
+	if (unlikely(reset_ts)) {
+		err = st_lsm6dsx_reset_hw_ts(hw);
+		if (err < 0) {
+			dev_err(hw->dev, "failed to reset hw ts (err=%d)\n",
+				err);
+			return err;
+		}
+	}
+	return read_len;
+}
+
+int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
+{
+	int err;
+
+	mutex_lock(&hw->fifo_lock);
+
+	st_lsm6dsx_read_fifo(hw);
+	err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
+
+	mutex_unlock(&hw->fifo_lock);
+
+	return err;
+}
+
+static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	int err;
+
+	mutex_lock(&hw->conf_lock);
+
+	if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) {
+		err = st_lsm6dsx_flush_fifo(hw);
+		if (err < 0)
+			goto out;
+	}
+
+	if (enable) {
+		err = st_lsm6dsx_sensor_enable(sensor);
+		if (err < 0)
+			goto out;
+	} else {
+		err = st_lsm6dsx_sensor_disable(sensor);
+		if (err < 0)
+			goto out;
+	}
+
+	err = st_lsm6dsx_set_fifo_odr(sensor, enable);
+	if (err < 0)
+		goto out;
+
+	err = st_lsm6dsx_update_decimators(hw);
+	if (err < 0)
+		goto out;
+
+	err = st_lsm6dsx_update_watermark(sensor, sensor->watermark);
+	if (err < 0)
+		goto out;
+
+	if (hw->enable_mask) {
+		/* reset hw ts counter */
+		err = st_lsm6dsx_reset_hw_ts(hw);
+		if (err < 0)
+			goto out;
+
+		err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
+	}
+
+out:
+	mutex_unlock(&hw->conf_lock);
+
+	return err;
+}
+
+static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
+{
+	struct st_lsm6dsx_hw *hw = private;
+
+	return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE;
+}
+
+static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
+{
+	struct st_lsm6dsx_hw *hw = private;
+	int count;
+
+	mutex_lock(&hw->fifo_lock);
+	count = st_lsm6dsx_read_fifo(hw);
+	mutex_unlock(&hw->fifo_lock);
+
+	return !count ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
+{
+	return st_lsm6dsx_update_fifo(iio_dev, true);
+}
+
+static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
+{
+	return st_lsm6dsx_update_fifo(iio_dev, false);
+}
+
+static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
+	.preenable = st_lsm6dsx_buffer_preenable,
+	.postdisable = st_lsm6dsx_buffer_postdisable,
+};
+
+int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
+{
+	struct device_node *np = hw->dev->of_node;
+	struct st_sensors_platform_data *pdata;
+	struct iio_buffer *buffer;
+	unsigned long irq_type;
+	bool irq_active_low;
+	int i, err;
+
+	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
+
+	switch (irq_type) {
+	case IRQF_TRIGGER_HIGH:
+	case IRQF_TRIGGER_RISING:
+		irq_active_low = false;
+		break;
+	case IRQF_TRIGGER_LOW:
+	case IRQF_TRIGGER_FALLING:
+		irq_active_low = true;
+		break;
+	default:
+		dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
+		return -EINVAL;
+	}
+
+	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_HLACTIVE_ADDR,
+				 ST_LSM6DSX_REG_HLACTIVE_MASK,
+				 FIELD_PREP(ST_LSM6DSX_REG_HLACTIVE_MASK,
+					    irq_active_low));
+	if (err < 0)
+		return err;
+
+	pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
+	if ((np && of_property_read_bool(np, "drive-open-drain")) ||
+	    (pdata && pdata->open_drain)) {
+		err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_PP_OD_ADDR,
+					 ST_LSM6DSX_REG_PP_OD_MASK,
+					 FIELD_PREP(ST_LSM6DSX_REG_PP_OD_MASK,
+						    1));
+		if (err < 0)
+			return err;
+
+		irq_type |= IRQF_SHARED;
+	}
+
+	err = devm_request_threaded_irq(hw->dev, hw->irq,
+					st_lsm6dsx_handler_irq,
+					st_lsm6dsx_handler_thread,
+					irq_type | IRQF_ONESHOT,
+					"lsm6dsx", hw);
+	if (err) {
+		dev_err(hw->dev, "failed to request trigger irq %d\n",
+			hw->irq);
+		return err;
+	}
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		buffer = devm_iio_kfifo_allocate(hw->dev);
+		if (!buffer)
+			return -ENOMEM;
+
+		iio_device_attach_buffer(hw->iio_devs[i], buffer);
+		hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
+		hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
+	}
+
+	return 0;
+}
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
new file mode 100644
index 0000000..aebbe0d
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -0,0 +1,915 @@
+/*
+ * STMicroelectronics st_lsm6dsx sensor driver
+ *
+ * The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer
+ * and 3D digital gyroscope system-in-package with a digital I2C/SPI serial
+ * interface standard output.
+ * LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale
+ * acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of
+ * +-125/+-245/+-500/+-1000/+-2000 dps
+ * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer
+ * allowing dynamic batching of sensor data.
+ *
+ * Supported sensors:
+ * - LSM6DS3:
+ *   - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
+ *   - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
+ *   - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
+ *   - FIFO size: 8KB
+ *
+ * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC:
+ *   - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
+ *   - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
+ *   - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
+ *   - FIFO size: 4KB
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+
+#include <linux/platform_data/st_sensors_pdata.h>
+
+#include "st_lsm6dsx.h"
+
+#define ST_LSM6DSX_REG_INT1_ADDR		0x0d
+#define ST_LSM6DSX_REG_INT2_ADDR		0x0e
+#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK	BIT(3)
+#define ST_LSM6DSX_REG_WHOAMI_ADDR		0x0f
+#define ST_LSM6DSX_REG_RESET_ADDR		0x12
+#define ST_LSM6DSX_REG_RESET_MASK		BIT(0)
+#define ST_LSM6DSX_REG_BDU_ADDR			0x12
+#define ST_LSM6DSX_REG_BDU_MASK			BIT(6)
+#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR	0x13
+#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK	BIT(5)
+
+#define ST_LSM6DSX_REG_ACC_ODR_ADDR		0x10
+#define ST_LSM6DSX_REG_ACC_ODR_MASK		GENMASK(7, 4)
+#define ST_LSM6DSX_REG_ACC_FS_ADDR		0x10
+#define ST_LSM6DSX_REG_ACC_FS_MASK		GENMASK(3, 2)
+#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR		0x28
+#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR		0x2a
+#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR		0x2c
+
+#define ST_LSM6DSX_REG_GYRO_ODR_ADDR		0x11
+#define ST_LSM6DSX_REG_GYRO_ODR_MASK		GENMASK(7, 4)
+#define ST_LSM6DSX_REG_GYRO_FS_ADDR		0x11
+#define ST_LSM6DSX_REG_GYRO_FS_MASK		GENMASK(3, 2)
+#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR	0x22
+#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR	0x24
+#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR	0x26
+
+#define ST_LSM6DSX_ACC_FS_2G_GAIN		IIO_G_TO_M_S_2(61)
+#define ST_LSM6DSX_ACC_FS_4G_GAIN		IIO_G_TO_M_S_2(122)
+#define ST_LSM6DSX_ACC_FS_8G_GAIN		IIO_G_TO_M_S_2(244)
+#define ST_LSM6DSX_ACC_FS_16G_GAIN		IIO_G_TO_M_S_2(488)
+
+#define ST_LSM6DSX_GYRO_FS_245_GAIN		IIO_DEGREE_TO_RAD(8750)
+#define ST_LSM6DSX_GYRO_FS_500_GAIN		IIO_DEGREE_TO_RAD(17500)
+#define ST_LSM6DSX_GYRO_FS_1000_GAIN		IIO_DEGREE_TO_RAD(35000)
+#define ST_LSM6DSX_GYRO_FS_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
+
+struct st_lsm6dsx_odr {
+	u16 hz;
+	u8 val;
+};
+
+#define ST_LSM6DSX_ODR_LIST_SIZE	6
+struct st_lsm6dsx_odr_table_entry {
+	struct st_lsm6dsx_reg reg;
+	struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
+};
+
+static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
+	[ST_LSM6DSX_ID_ACC] = {
+		.reg = {
+			.addr = ST_LSM6DSX_REG_ACC_ODR_ADDR,
+			.mask = ST_LSM6DSX_REG_ACC_ODR_MASK,
+		},
+		.odr_avl[0] = {  13, 0x01 },
+		.odr_avl[1] = {  26, 0x02 },
+		.odr_avl[2] = {  52, 0x03 },
+		.odr_avl[3] = { 104, 0x04 },
+		.odr_avl[4] = { 208, 0x05 },
+		.odr_avl[5] = { 416, 0x06 },
+	},
+	[ST_LSM6DSX_ID_GYRO] = {
+		.reg = {
+			.addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR,
+			.mask = ST_LSM6DSX_REG_GYRO_ODR_MASK,
+		},
+		.odr_avl[0] = {  13, 0x01 },
+		.odr_avl[1] = {  26, 0x02 },
+		.odr_avl[2] = {  52, 0x03 },
+		.odr_avl[3] = { 104, 0x04 },
+		.odr_avl[4] = { 208, 0x05 },
+		.odr_avl[5] = { 416, 0x06 },
+	}
+};
+
+struct st_lsm6dsx_fs {
+	u32 gain;
+	u8 val;
+};
+
+#define ST_LSM6DSX_FS_LIST_SIZE		4
+struct st_lsm6dsx_fs_table_entry {
+	struct st_lsm6dsx_reg reg;
+	struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
+};
+
+static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
+	[ST_LSM6DSX_ID_ACC] = {
+		.reg = {
+			.addr = ST_LSM6DSX_REG_ACC_FS_ADDR,
+			.mask = ST_LSM6DSX_REG_ACC_FS_MASK,
+		},
+		.fs_avl[0] = {  ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 },
+		.fs_avl[1] = {  ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 },
+		.fs_avl[2] = {  ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 },
+		.fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 },
+	},
+	[ST_LSM6DSX_ID_GYRO] = {
+		.reg = {
+			.addr = ST_LSM6DSX_REG_GYRO_FS_ADDR,
+			.mask = ST_LSM6DSX_REG_GYRO_FS_MASK,
+		},
+		.fs_avl[0] = {  ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 },
+		.fs_avl[1] = {  ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 },
+		.fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 },
+		.fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 },
+	}
+};
+
+static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
+	{
+		.wai = 0x69,
+		.max_fifo_size = 1365,
+		.id = {
+			[0] = ST_LSM6DS3_ID,
+		},
+		.decimator = {
+			[ST_LSM6DSX_ID_ACC] = {
+				.addr = 0x08,
+				.mask = GENMASK(2, 0),
+			},
+			[ST_LSM6DSX_ID_GYRO] = {
+				.addr = 0x08,
+				.mask = GENMASK(5, 3),
+			},
+		},
+		.fifo_ops = {
+			.fifo_th = {
+				.addr = 0x06,
+				.mask = GENMASK(11, 0),
+			},
+			.fifo_diff = {
+				.addr = 0x3a,
+				.mask = GENMASK(11, 0),
+			},
+			.th_wl = 3, /* 1LSB = 2B */
+		},
+		.ts_settings = {
+			.timer_en = {
+				.addr = 0x58,
+				.mask = BIT(7),
+			},
+			.hr_timer = {
+				.addr = 0x5c,
+				.mask = BIT(4),
+			},
+			.fifo_en = {
+				.addr = 0x07,
+				.mask = BIT(7),
+			},
+			.decimator = {
+				.addr = 0x09,
+				.mask = GENMASK(5, 3),
+			},
+		},
+	},
+	{
+		.wai = 0x69,
+		.max_fifo_size = 682,
+		.id = {
+			[0] = ST_LSM6DS3H_ID,
+		},
+		.decimator = {
+			[ST_LSM6DSX_ID_ACC] = {
+				.addr = 0x08,
+				.mask = GENMASK(2, 0),
+			},
+			[ST_LSM6DSX_ID_GYRO] = {
+				.addr = 0x08,
+				.mask = GENMASK(5, 3),
+			},
+		},
+		.fifo_ops = {
+			.fifo_th = {
+				.addr = 0x06,
+				.mask = GENMASK(11, 0),
+			},
+			.fifo_diff = {
+				.addr = 0x3a,
+				.mask = GENMASK(11, 0),
+			},
+			.th_wl = 3, /* 1LSB = 2B */
+		},
+		.ts_settings = {
+			.timer_en = {
+				.addr = 0x58,
+				.mask = BIT(7),
+			},
+			.hr_timer = {
+				.addr = 0x5c,
+				.mask = BIT(4),
+			},
+			.fifo_en = {
+				.addr = 0x07,
+				.mask = BIT(7),
+			},
+			.decimator = {
+				.addr = 0x09,
+				.mask = GENMASK(5, 3),
+			},
+		},
+	},
+	{
+		.wai = 0x6a,
+		.max_fifo_size = 682,
+		.id = {
+			[0] = ST_LSM6DSL_ID,
+			[1] = ST_LSM6DSM_ID,
+			[2] = ST_ISM330DLC_ID,
+		},
+		.decimator = {
+			[ST_LSM6DSX_ID_ACC] = {
+				.addr = 0x08,
+				.mask = GENMASK(2, 0),
+			},
+			[ST_LSM6DSX_ID_GYRO] = {
+				.addr = 0x08,
+				.mask = GENMASK(5, 3),
+			},
+		},
+		.fifo_ops = {
+			.fifo_th = {
+				.addr = 0x06,
+				.mask = GENMASK(10, 0),
+			},
+			.fifo_diff = {
+				.addr = 0x3a,
+				.mask = GENMASK(10, 0),
+			},
+			.th_wl = 3, /* 1LSB = 2B */
+		},
+		.ts_settings = {
+			.timer_en = {
+				.addr = 0x19,
+				.mask = BIT(5),
+			},
+			.hr_timer = {
+				.addr = 0x5c,
+				.mask = BIT(4),
+			},
+			.fifo_en = {
+				.addr = 0x07,
+				.mask = BIT(7),
+			},
+			.decimator = {
+				.addr = 0x09,
+				.mask = GENMASK(5, 3),
+			},
+		},
+	},
+};
+
+#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)		\
+{									\
+	.type = chan_type,						\
+	.address = addr,						\
+	.modified = 1,							\
+	.channel2 = mod,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE),			\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = scan_idx,						\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 16,						\
+		.storagebits = 16,					\
+		.endianness = IIO_LE,					\
+	},								\
+}
+
+static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
+	ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
+			   IIO_MOD_X, 0),
+	ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR,
+			   IIO_MOD_Y, 1),
+	ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR,
+			   IIO_MOD_Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
+	ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR,
+			   IIO_MOD_X, 0),
+	ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR,
+			   IIO_MOD_Y, 1),
+	ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR,
+			   IIO_MOD_Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
+{
+	int err, i, j, data;
+
+	for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
+		for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) {
+			if (id == st_lsm6dsx_sensor_settings[i].id[j])
+				break;
+		}
+		if (j < ST_LSM6DSX_MAX_ID)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) {
+		dev_err(hw->dev, "unsupported hw id [%02x]\n", id);
+		return -ENODEV;
+	}
+
+	err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data);
+	if (err < 0) {
+		dev_err(hw->dev, "failed to read whoami register\n");
+		return err;
+	}
+
+	if (data != st_lsm6dsx_sensor_settings[i].wai) {
+		dev_err(hw->dev, "unsupported whoami [%02x]\n", data);
+		return -ENODEV;
+	}
+
+	hw->settings = &st_lsm6dsx_sensor_settings[i];
+
+	return 0;
+}
+
+static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
+				     u32 gain)
+{
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	const struct st_lsm6dsx_reg *reg;
+	int i, err;
+	u8 val;
+
+	for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
+		if (st_lsm6dsx_fs_table[sensor->id].fs_avl[i].gain == gain)
+			break;
+
+	if (i == ST_LSM6DSX_FS_LIST_SIZE)
+		return -EINVAL;
+
+	val = st_lsm6dsx_fs_table[sensor->id].fs_avl[i].val;
+	reg = &st_lsm6dsx_fs_table[sensor->id].reg;
+	err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
+				 ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
+	if (err < 0)
+		return err;
+
+	sensor->gain = gain;
+
+	return 0;
+}
+
+static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr,
+				u8 *val)
+{
+	int i;
+
+	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
+		if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
+			break;
+
+	if (i == ST_LSM6DSX_ODR_LIST_SIZE)
+		return -EINVAL;
+
+	*val = st_lsm6dsx_odr_table[sensor->id].odr_avl[i].val;
+
+	return 0;
+}
+
+static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
+{
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	const struct st_lsm6dsx_reg *reg;
+	int err;
+	u8 val;
+
+	err = st_lsm6dsx_check_odr(sensor, odr, &val);
+	if (err < 0)
+		return err;
+
+	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
+	return regmap_update_bits(hw->regmap, reg->addr, reg->mask,
+				  ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
+}
+
+int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
+{
+	int err;
+
+	err = st_lsm6dsx_set_odr(sensor, sensor->odr);
+	if (err < 0)
+		return err;
+
+	sensor->hw->enable_mask |= BIT(sensor->id);
+
+	return 0;
+}
+
+int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
+{
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	const struct st_lsm6dsx_reg *reg;
+	int err;
+
+	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
+	err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
+				 ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
+	if (err < 0)
+		return err;
+
+	sensor->hw->enable_mask &= ~BIT(sensor->id);
+
+	return 0;
+}
+
+static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
+				   u8 addr, int *val)
+{
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	int err, delay;
+	__le16 data;
+
+	err = st_lsm6dsx_sensor_enable(sensor);
+	if (err < 0)
+		return err;
+
+	delay = 1000000 / sensor->odr;
+	usleep_range(delay, 2 * delay);
+
+	err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
+	if (err < 0)
+		return err;
+
+	st_lsm6dsx_sensor_disable(sensor);
+
+	*val = (s16)le16_to_cpu(data);
+
+	return IIO_VAL_INT;
+}
+
+static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev,
+			       struct iio_chan_spec const *ch,
+			       int *val, int *val2, long mask)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(iio_dev);
+		if (ret)
+			break;
+
+		ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val);
+		iio_device_release_direct_mode(iio_dev);
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = sensor->odr;
+		ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = sensor->gain;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
+				struct iio_chan_spec const *chan,
+				int val, int val2, long mask)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+	int err;
+
+	err = iio_device_claim_direct_mode(iio_dev);
+	if (err)
+		return err;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		err = st_lsm6dsx_set_full_scale(sensor, val2);
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ: {
+		u8 data;
+
+		err = st_lsm6dsx_check_odr(sensor, val, &data);
+		if (!err)
+			sensor->odr = val;
+		break;
+	}
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	iio_device_release_direct_mode(iio_dev);
+
+	return err;
+}
+
+static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	int err;
+
+	if (val < 1 || val > hw->settings->max_fifo_size)
+		return -EINVAL;
+
+	mutex_lock(&hw->conf_lock);
+
+	err = st_lsm6dsx_update_watermark(sensor, val);
+
+	mutex_unlock(&hw->conf_lock);
+
+	if (err < 0)
+		return err;
+
+	sensor->watermark = val;
+
+	return 0;
+}
+
+static ssize_t
+st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+	enum st_lsm6dsx_sensor_id id = sensor->id;
+	int i, len = 0;
+
+	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+				 st_lsm6dsx_odr_table[id].odr_avl[i].hz);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+	enum st_lsm6dsx_sensor_id id = sensor->id;
+	int i, len = 0;
+
+	for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
+				 st_lsm6dsx_fs_table[id].fs_avl[i].gain);
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail);
+static IIO_DEVICE_ATTR(in_accel_scale_available, 0444,
+		       st_lsm6dsx_sysfs_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444,
+		       st_lsm6dsx_sysfs_scale_avail, NULL, 0);
+
+static struct attribute *st_lsm6dsx_acc_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_acc_attribute_group = {
+	.attrs = st_lsm6dsx_acc_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_acc_info = {
+	.attrs = &st_lsm6dsx_acc_attribute_group,
+	.read_raw = st_lsm6dsx_read_raw,
+	.write_raw = st_lsm6dsx_write_raw,
+	.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
+static struct attribute *st_lsm6dsx_gyro_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_gyro_attribute_group = {
+	.attrs = st_lsm6dsx_gyro_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_gyro_info = {
+	.attrs = &st_lsm6dsx_gyro_attribute_group,
+	.read_raw = st_lsm6dsx_read_raw,
+	.write_raw = st_lsm6dsx_write_raw,
+	.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
+static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
+
+static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
+{
+	struct device_node *np = hw->dev->of_node;
+
+	if (!np)
+		return -EINVAL;
+
+	return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
+}
+
+static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
+{
+	int err = 0, drdy_pin;
+
+	if (st_lsm6dsx_of_get_drdy_pin(hw, &drdy_pin) < 0) {
+		struct st_sensors_platform_data *pdata;
+		struct device *dev = hw->dev;
+
+		pdata = (struct st_sensors_platform_data *)dev->platform_data;
+		drdy_pin = pdata ? pdata->drdy_int_pin : 1;
+	}
+
+	switch (drdy_pin) {
+	case 1:
+		*drdy_reg = ST_LSM6DSX_REG_INT1_ADDR;
+		break;
+	case 2:
+		*drdy_reg = ST_LSM6DSX_REG_INT2_ADDR;
+		break;
+	default:
+		dev_err(hw->dev, "unsupported data ready pin\n");
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
+{
+	const struct st_lsm6dsx_hw_ts_settings *ts_settings;
+	int err, val;
+
+	ts_settings = &hw->settings->ts_settings;
+	/* enable hw timestamp generation if necessary */
+	if (ts_settings->timer_en.addr) {
+		val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask);
+		err = regmap_update_bits(hw->regmap,
+					 ts_settings->timer_en.addr,
+					 ts_settings->timer_en.mask, val);
+		if (err < 0)
+			return err;
+	}
+
+	/* enable high resolution for hw ts timer if necessary */
+	if (ts_settings->hr_timer.addr) {
+		val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask);
+		err = regmap_update_bits(hw->regmap,
+					 ts_settings->hr_timer.addr,
+					 ts_settings->hr_timer.mask, val);
+		if (err < 0)
+			return err;
+	}
+
+	/* enable ts queueing in FIFO if necessary */
+	if (ts_settings->fifo_en.addr) {
+		val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask);
+		err = regmap_update_bits(hw->regmap,
+					 ts_settings->fifo_en.addr,
+					 ts_settings->fifo_en.mask, val);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
+{
+	u8 drdy_int_reg;
+	int err;
+
+	err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
+			   ST_LSM6DSX_REG_RESET_MASK);
+	if (err < 0)
+		return err;
+
+	msleep(200);
+
+	/* enable Block Data Update */
+	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,
+				 ST_LSM6DSX_REG_BDU_MASK,
+				 FIELD_PREP(ST_LSM6DSX_REG_BDU_MASK, 1));
+	if (err < 0)
+		return err;
+
+	/* enable FIFO watermak interrupt */
+	err = st_lsm6dsx_get_drdy_reg(hw, &drdy_int_reg);
+	if (err < 0)
+		return err;
+
+	err = regmap_update_bits(hw->regmap, drdy_int_reg,
+				 ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
+				 FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
+					    1));
+	if (err < 0)
+		return err;
+
+	return st_lsm6dsx_init_hw_timer(hw);
+}
+
+static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
+					       enum st_lsm6dsx_sensor_id id,
+					       const char *name)
+{
+	struct st_lsm6dsx_sensor *sensor;
+	struct iio_dev *iio_dev;
+
+	iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
+	if (!iio_dev)
+		return NULL;
+
+	iio_dev->modes = INDIO_DIRECT_MODE;
+	iio_dev->dev.parent = hw->dev;
+	iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
+
+	sensor = iio_priv(iio_dev);
+	sensor->id = id;
+	sensor->hw = hw;
+	sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz;
+	sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain;
+	sensor->watermark = 1;
+
+	switch (id) {
+	case ST_LSM6DSX_ID_ACC:
+		iio_dev->channels = st_lsm6dsx_acc_channels;
+		iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
+		iio_dev->info = &st_lsm6dsx_acc_info;
+
+		scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
+			  name);
+		break;
+	case ST_LSM6DSX_ID_GYRO:
+		iio_dev->channels = st_lsm6dsx_gyro_channels;
+		iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
+		iio_dev->info = &st_lsm6dsx_gyro_info;
+
+		scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
+			  name);
+		break;
+	default:
+		return NULL;
+	}
+	iio_dev->name = sensor->name;
+
+	return iio_dev;
+}
+
+int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
+		     struct regmap *regmap)
+{
+	struct st_lsm6dsx_hw *hw;
+	int i, err;
+
+	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, (void *)hw);
+
+	mutex_init(&hw->fifo_lock);
+	mutex_init(&hw->conf_lock);
+
+	hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL);
+	if (!hw->buff)
+		return -ENOMEM;
+
+	hw->dev = dev;
+	hw->irq = irq;
+	hw->regmap = regmap;
+
+	err = st_lsm6dsx_check_whoami(hw, hw_id);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
+		if (!hw->iio_devs[i])
+			return -ENOMEM;
+	}
+
+	err = st_lsm6dsx_init_device(hw);
+	if (err < 0)
+		return err;
+
+	if (hw->irq > 0) {
+		err = st_lsm6dsx_fifo_setup(hw);
+		if (err < 0)
+			return err;
+	}
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(st_lsm6dsx_probe);
+
+static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
+{
+	struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev);
+	struct st_lsm6dsx_sensor *sensor;
+	const struct st_lsm6dsx_reg *reg;
+	int i, err = 0;
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		sensor = iio_priv(hw->iio_devs[i]);
+		if (!(hw->enable_mask & BIT(sensor->id)))
+			continue;
+
+		reg = &st_lsm6dsx_odr_table[sensor->id].reg;
+		err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
+					 ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
+		if (err < 0)
+			return err;
+	}
+
+	if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS)
+		err = st_lsm6dsx_flush_fifo(hw);
+
+	return err;
+}
+
+static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
+{
+	struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev);
+	struct st_lsm6dsx_sensor *sensor;
+	int i, err = 0;
+
+	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		sensor = iio_priv(hw->iio_devs[i]);
+		if (!(hw->enable_mask & BIT(sensor->id)))
+			continue;
+
+		err = st_lsm6dsx_set_odr(sensor, sensor->odr);
+		if (err < 0)
+			return err;
+	}
+
+	if (hw->enable_mask)
+		err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
+
+	return err;
+}
+
+const struct dev_pm_ops st_lsm6dsx_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(st_lsm6dsx_suspend, st_lsm6dsx_resume)
+};
+EXPORT_SYMBOL(st_lsm6dsx_pm_ops);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
new file mode 100644
index 0000000..377c4e9
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
@@ -0,0 +1,92 @@
+/*
+ * STMicroelectronics st_lsm6dsx i2c driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "st_lsm6dsx.h"
+
+static const struct regmap_config st_lsm6dsx_i2c_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	int hw_id = id->driver_data;
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+
+	return st_lsm6dsx_probe(&client->dev, client->irq,
+				hw_id, id->name, regmap);
+}
+
+static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
+	{
+		.compatible = "st,lsm6ds3",
+		.data = (void *)ST_LSM6DS3_ID,
+	},
+	{
+		.compatible = "st,lsm6ds3h",
+		.data = (void *)ST_LSM6DS3H_ID,
+	},
+	{
+		.compatible = "st,lsm6dsl",
+		.data = (void *)ST_LSM6DSL_ID,
+	},
+	{
+		.compatible = "st,lsm6dsm",
+		.data = (void *)ST_LSM6DSM_ID,
+	},
+	{
+		.compatible = "st,ism330dlc",
+		.data = (void *)ST_ISM330DLC_ID,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
+
+static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
+	{ ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
+	{ ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
+	{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
+	{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
+	{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
+
+static struct i2c_driver st_lsm6dsx_driver = {
+	.driver = {
+		.name = "st_lsm6dsx_i2c",
+		.pm = &st_lsm6dsx_pm_ops,
+		.of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match),
+	},
+	.probe = st_lsm6dsx_i2c_probe,
+	.id_table = st_lsm6dsx_i2c_id_table,
+};
+module_i2c_driver(st_lsm6dsx_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
new file mode 100644
index 0000000..fec5c6c
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
@@ -0,0 +1,92 @@
+/*
+ * STMicroelectronics st_lsm6dsx spi driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "st_lsm6dsx.h"
+
+static const struct regmap_config st_lsm6dsx_spi_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+static int st_lsm6dsx_spi_probe(struct spi_device *spi)
+{
+	const struct spi_device_id *id = spi_get_device_id(spi);
+	int hw_id = id->driver_data;
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init_spi(spi, &st_lsm6dsx_spi_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+			(int)PTR_ERR(regmap));
+		return PTR_ERR(regmap);
+	}
+
+	return st_lsm6dsx_probe(&spi->dev, spi->irq,
+				hw_id, id->name, regmap);
+}
+
+static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
+	{
+		.compatible = "st,lsm6ds3",
+		.data = (void *)ST_LSM6DS3_ID,
+	},
+	{
+		.compatible = "st,lsm6ds3h",
+		.data = (void *)ST_LSM6DS3H_ID,
+	},
+	{
+		.compatible = "st,lsm6dsl",
+		.data = (void *)ST_LSM6DSL_ID,
+	},
+	{
+		.compatible = "st,lsm6dsm",
+		.data = (void *)ST_LSM6DSM_ID,
+	},
+	{
+		.compatible = "st,ism330dlc",
+		.data = (void *)ST_ISM330DLC_ID,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
+
+static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
+	{ ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID },
+	{ ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID },
+	{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
+	{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
+	{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
+
+static struct spi_driver st_lsm6dsx_driver = {
+	.driver = {
+		.name = "st_lsm6dsx_spi",
+		.pm = &st_lsm6dsx_pm_ops,
+		.of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match),
+	},
+	.probe = st_lsm6dsx_spi_probe,
+	.id_table = st_lsm6dsx_spi_id_table,
+};
+module_spi_driver(st_lsm6dsx_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx spi driver");
+MODULE_LICENSE("GPL v2");