File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
new file mode 100644
index 0000000..868abad
--- /dev/null
+++ b/drivers/iio/magnetometer/Kconfig
@@ -0,0 +1,108 @@
+#
+# Magnetometer sensors
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Magnetometer sensors"
+
+config AK8975
+	tristate "Asahi Kasei AK 3-Axis Magnetometer"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	help
+	  Say yes here to build support for Asahi Kasei AK8975, AK8963,
+	  AK09911 or AK09912 3-Axis Magnetometer.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ak8975.
+
+config AK09911
+	tristate "Asahi Kasei AK09911 3-axis Compass"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	select AK8975
+	help
+	  Deprecated: AK09911 is now supported by AK8975 driver.
+
+config BMC150_MAGN
+	tristate "Bosch BMC150 Magnetometer Driver"
+	depends on I2C
+	select REGMAP_I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for the BMC150 magnetometer.
+
+	  Currently this only supports the device via an i2c interface.
+
+	  This is a combo module with both accelerometer and magnetometer.
+	  This driver is only implementing magnetometer part, which has
+	  its own address and register map.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called bmc150_magn.
+
+config MAG3110
+	tristate "Freescale MAG3110 3-Axis Magnetometer"
+	depends on I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for the Freescale MAG3110 3-Axis
+	  magnetometer.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called mag3110.
+
+config HID_SENSOR_MAGNETOMETER_3D
+	depends on HID_SENSOR_HUB
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	select HID_SENSOR_IIO_COMMON
+	select HID_SENSOR_IIO_TRIGGER
+	tristate "HID Magenetometer 3D"
+	help
+	  Say yes here to build support for the HID SENSOR
+	  Magnetometer 3D.
+
+config MMC35240
+	tristate "MEMSIC MMC35240 3-axis magnetic sensor"
+	select REGMAP_I2C
+	depends on I2C
+	help
+	  Say yes here to build support for the MEMSIC MMC35240 3-axis
+	  magnetic sensor.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called mmc35240.
+
+config IIO_ST_MAGN_3AXIS
+	tristate "STMicroelectronics magnetometers 3-Axis Driver"
+	depends on (I2C || SPI_MASTER) && SYSFS
+	select IIO_ST_SENSORS_CORE
+	select IIO_ST_MAGN_I2C_3AXIS if (I2C)
+	select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER)
+	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
+	help
+	  Say yes here to build support for STMicroelectronics magnetometers:
+	  LSM303DLHC, LSM303DLM, LIS3MDL.
+
+	  This driver can also be built as a module. If so, these modules
+	  will be created:
+	  - st_magn (core functions for the driver [it is mandatory]);
+	  - st_magn_i2c (necessary for the I2C devices [optional*]);
+	  - st_magn_spi (necessary for the SPI devices [optional*]);
+
+	  (*) one of these is necessary to do something.
+
+config IIO_ST_MAGN_I2C_3AXIS
+	tristate
+	depends on IIO_ST_MAGN_3AXIS
+	depends on IIO_ST_SENSORS_I2C
+
+config IIO_ST_MAGN_SPI_3AXIS
+	tristate
+	depends on IIO_ST_MAGN_3AXIS
+	depends on IIO_ST_SENSORS_SPI
+
+endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
new file mode 100644
index 0000000..2c72df4
--- /dev/null
+++ b/drivers/iio/magnetometer/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for industrial I/O Magnetometer sensor drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AK8975)	+= ak8975.o
+obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
+obj-$(CONFIG_MAG3110)	+= mag3110.o
+obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
+obj-$(CONFIG_MMC35240)	+= mmc35240.o
+
+obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
+st_magn-y := st_magn_core.o
+st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
+
+obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
+obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
new file mode 100644
index 0000000..f2a7f72
--- /dev/null
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -0,0 +1,850 @@
+/*
+ * A sensor driver for the magnetometer AK8975.
+ *
+ * Magnetic compass sensor driver for monitoring magnetic flux information.
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/acpi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+/*
+ * Register definitions, as well as various shifts and masks to get at the
+ * individual fields of the registers.
+ */
+#define AK8975_REG_WIA			0x00
+#define AK8975_DEVICE_ID		0x48
+
+#define AK8975_REG_INFO			0x01
+
+#define AK8975_REG_ST1			0x02
+#define AK8975_REG_ST1_DRDY_SHIFT	0
+#define AK8975_REG_ST1_DRDY_MASK	(1 << AK8975_REG_ST1_DRDY_SHIFT)
+
+#define AK8975_REG_HXL			0x03
+#define AK8975_REG_HXH			0x04
+#define AK8975_REG_HYL			0x05
+#define AK8975_REG_HYH			0x06
+#define AK8975_REG_HZL			0x07
+#define AK8975_REG_HZH			0x08
+#define AK8975_REG_ST2			0x09
+#define AK8975_REG_ST2_DERR_SHIFT	2
+#define AK8975_REG_ST2_DERR_MASK	(1 << AK8975_REG_ST2_DERR_SHIFT)
+
+#define AK8975_REG_ST2_HOFL_SHIFT	3
+#define AK8975_REG_ST2_HOFL_MASK	(1 << AK8975_REG_ST2_HOFL_SHIFT)
+
+#define AK8975_REG_CNTL			0x0A
+#define AK8975_REG_CNTL_MODE_SHIFT	0
+#define AK8975_REG_CNTL_MODE_MASK	(0xF << AK8975_REG_CNTL_MODE_SHIFT)
+#define AK8975_REG_CNTL_MODE_POWER_DOWN	0x00
+#define AK8975_REG_CNTL_MODE_ONCE	0x01
+#define AK8975_REG_CNTL_MODE_SELF_TEST	0x08
+#define AK8975_REG_CNTL_MODE_FUSE_ROM	0x0F
+
+#define AK8975_REG_RSVC			0x0B
+#define AK8975_REG_ASTC			0x0C
+#define AK8975_REG_TS1			0x0D
+#define AK8975_REG_TS2			0x0E
+#define AK8975_REG_I2CDIS		0x0F
+#define AK8975_REG_ASAX			0x10
+#define AK8975_REG_ASAY			0x11
+#define AK8975_REG_ASAZ			0x12
+
+#define AK8975_MAX_REGS			AK8975_REG_ASAZ
+
+/*
+ * AK09912 Register definitions
+ */
+#define AK09912_REG_WIA1		0x00
+#define AK09912_REG_WIA2		0x01
+#define AK09912_DEVICE_ID		0x04
+#define AK09911_DEVICE_ID		0x05
+
+#define AK09911_REG_INFO1		0x02
+#define AK09911_REG_INFO2		0x03
+
+#define AK09912_REG_ST1			0x10
+
+#define AK09912_REG_ST1_DRDY_SHIFT	0
+#define AK09912_REG_ST1_DRDY_MASK	(1 << AK09912_REG_ST1_DRDY_SHIFT)
+
+#define AK09912_REG_HXL			0x11
+#define AK09912_REG_HXH			0x12
+#define AK09912_REG_HYL			0x13
+#define AK09912_REG_HYH			0x14
+#define AK09912_REG_HZL			0x15
+#define AK09912_REG_HZH			0x16
+#define AK09912_REG_TMPS		0x17
+
+#define AK09912_REG_ST2			0x18
+#define AK09912_REG_ST2_HOFL_SHIFT	3
+#define AK09912_REG_ST2_HOFL_MASK	(1 << AK09912_REG_ST2_HOFL_SHIFT)
+
+#define AK09912_REG_CNTL1		0x30
+
+#define AK09912_REG_CNTL2		0x31
+#define AK09912_REG_CNTL_MODE_POWER_DOWN	0x00
+#define AK09912_REG_CNTL_MODE_ONCE	0x01
+#define AK09912_REG_CNTL_MODE_SELF_TEST	0x10
+#define AK09912_REG_CNTL_MODE_FUSE_ROM	0x1F
+#define AK09912_REG_CNTL2_MODE_SHIFT	0
+#define AK09912_REG_CNTL2_MODE_MASK	(0x1F << AK09912_REG_CNTL2_MODE_SHIFT)
+
+#define AK09912_REG_CNTL3		0x32
+
+#define AK09912_REG_TS1			0x33
+#define AK09912_REG_TS2			0x34
+#define AK09912_REG_TS3			0x35
+#define AK09912_REG_I2CDIS		0x36
+#define AK09912_REG_TS4			0x37
+
+#define AK09912_REG_ASAX		0x60
+#define AK09912_REG_ASAY		0x61
+#define AK09912_REG_ASAZ		0x62
+
+#define AK09912_MAX_REGS		AK09912_REG_ASAZ
+
+/*
+ * Miscellaneous values.
+ */
+#define AK8975_MAX_CONVERSION_TIMEOUT	500
+#define AK8975_CONVERSION_DONE_POLL_TIME 10
+#define AK8975_DATA_READY_TIMEOUT	((100*HZ)/1000)
+
+/*
+ * Precalculate scale factor (in Gauss units) for each axis and
+ * store in the device data.
+ *
+ * This scale factor is axis-dependent, and is derived from 3 calibration
+ * factors ASA(x), ASA(y), and ASA(z).
+ *
+ * These ASA values are read from the sensor device at start of day, and
+ * cached in the device context struct.
+ *
+ * Adjusting the flux value with the sensitivity adjustment value should be
+ * done via the following formula:
+ *
+ * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
+ * where H is the raw value, ASA is the sensitivity adjustment, and Hadj
+ * is the resultant adjusted value.
+ *
+ * We reduce the formula to:
+ *
+ * Hadj = H * (ASA + 128) / 256
+ *
+ * H is in the range of -4096 to 4095.  The magnetometer has a range of
+ * +-1229uT.  To go from the raw value to uT is:
+ *
+ * HuT = H * 1229/4096, or roughly, 3/10.
+ *
+ * Since 1uT = 0.01 gauss, our final scale factor becomes:
+ *
+ * Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
+ * Hadj = H * ((ASA + 128) * 0.003) / 256
+ *
+ * Since ASA doesn't change, we cache the resultant scale factor into the
+ * device context in ak8975_setup().
+ *
+ * Given we use IIO_VAL_INT_PLUS_MICRO bit when displaying the scale, we
+ * multiply the stored scale value by 1e6.
+ */
+static long ak8975_raw_to_gauss(u16 data)
+{
+	return (((long)data + 128) * 3000) / 256;
+}
+
+/*
+ * For AK8963 and AK09911, same calculation, but the device is less sensitive:
+ *
+ * H is in the range of +-8190.  The magnetometer has a range of
+ * +-4912uT.  To go from the raw value to uT is:
+ *
+ * HuT = H * 4912/8190, or roughly, 6/10, instead of 3/10.
+ */
+
+static long ak8963_09911_raw_to_gauss(u16 data)
+{
+	return (((long)data + 128) * 6000) / 256;
+}
+
+/*
+ * For AK09912, same calculation, except the device is more sensitive:
+ *
+ * H is in the range of -32752 to 32752.  The magnetometer has a range of
+ * +-4912uT.  To go from the raw value to uT is:
+ *
+ * HuT = H * 4912/32752, or roughly, 3/20, instead of 3/10.
+ */
+static long ak09912_raw_to_gauss(u16 data)
+{
+	return (((long)data + 128) * 1500) / 256;
+}
+
+/* Compatible Asahi Kasei Compass parts */
+enum asahi_compass_chipset {
+	AK8975,
+	AK8963,
+	AK09911,
+	AK09912,
+	AK_MAX_TYPE
+};
+
+enum ak_ctrl_reg_addr {
+	ST1,
+	ST2,
+	CNTL,
+	ASA_BASE,
+	MAX_REGS,
+	REGS_END,
+};
+
+enum ak_ctrl_reg_mask {
+	ST1_DRDY,
+	ST2_HOFL,
+	ST2_DERR,
+	CNTL_MODE,
+	MASK_END,
+};
+
+enum ak_ctrl_mode {
+	POWER_DOWN,
+	MODE_ONCE,
+	SELF_TEST,
+	FUSE_ROM,
+	MODE_END,
+};
+
+struct ak_def {
+	enum asahi_compass_chipset type;
+	long (*raw_to_gauss)(u16 data);
+	u16 range;
+	u8 ctrl_regs[REGS_END];
+	u8 ctrl_masks[MASK_END];
+	u8 ctrl_modes[MODE_END];
+	u8 data_regs[3];
+};
+
+static struct ak_def ak_def_array[AK_MAX_TYPE] = {
+	{
+		.type = AK8975,
+		.raw_to_gauss = ak8975_raw_to_gauss,
+		.range = 4096,
+		.ctrl_regs = {
+			AK8975_REG_ST1,
+			AK8975_REG_ST2,
+			AK8975_REG_CNTL,
+			AK8975_REG_ASAX,
+			AK8975_MAX_REGS},
+		.ctrl_masks = {
+			AK8975_REG_ST1_DRDY_MASK,
+			AK8975_REG_ST2_HOFL_MASK,
+			AK8975_REG_ST2_DERR_MASK,
+			AK8975_REG_CNTL_MODE_MASK},
+		.ctrl_modes = {
+			AK8975_REG_CNTL_MODE_POWER_DOWN,
+			AK8975_REG_CNTL_MODE_ONCE,
+			AK8975_REG_CNTL_MODE_SELF_TEST,
+			AK8975_REG_CNTL_MODE_FUSE_ROM},
+		.data_regs = {
+			AK8975_REG_HXL,
+			AK8975_REG_HYL,
+			AK8975_REG_HZL},
+	},
+	{
+		.type = AK8963,
+		.raw_to_gauss = ak8963_09911_raw_to_gauss,
+		.range = 8190,
+		.ctrl_regs = {
+			AK8975_REG_ST1,
+			AK8975_REG_ST2,
+			AK8975_REG_CNTL,
+			AK8975_REG_ASAX,
+			AK8975_MAX_REGS},
+		.ctrl_masks = {
+			AK8975_REG_ST1_DRDY_MASK,
+			AK8975_REG_ST2_HOFL_MASK,
+			0,
+			AK8975_REG_CNTL_MODE_MASK},
+		.ctrl_modes = {
+			AK8975_REG_CNTL_MODE_POWER_DOWN,
+			AK8975_REG_CNTL_MODE_ONCE,
+			AK8975_REG_CNTL_MODE_SELF_TEST,
+			AK8975_REG_CNTL_MODE_FUSE_ROM},
+		.data_regs = {
+			AK8975_REG_HXL,
+			AK8975_REG_HYL,
+			AK8975_REG_HZL},
+	},
+	{
+		.type = AK09911,
+		.raw_to_gauss = ak8963_09911_raw_to_gauss,
+		.range = 8192,
+		.ctrl_regs = {
+			AK09912_REG_ST1,
+			AK09912_REG_ST2,
+			AK09912_REG_CNTL2,
+			AK09912_REG_ASAX,
+			AK09912_MAX_REGS},
+		.ctrl_masks = {
+			AK09912_REG_ST1_DRDY_MASK,
+			AK09912_REG_ST2_HOFL_MASK,
+			0,
+			AK09912_REG_CNTL2_MODE_MASK},
+		.ctrl_modes = {
+			AK09912_REG_CNTL_MODE_POWER_DOWN,
+			AK09912_REG_CNTL_MODE_ONCE,
+			AK09912_REG_CNTL_MODE_SELF_TEST,
+			AK09912_REG_CNTL_MODE_FUSE_ROM},
+		.data_regs = {
+			AK09912_REG_HXL,
+			AK09912_REG_HYL,
+			AK09912_REG_HZL},
+	},
+	{
+		.type = AK09912,
+		.raw_to_gauss = ak09912_raw_to_gauss,
+		.range = 32752,
+		.ctrl_regs = {
+			AK09912_REG_ST1,
+			AK09912_REG_ST2,
+			AK09912_REG_CNTL2,
+			AK09912_REG_ASAX,
+			AK09912_MAX_REGS},
+		.ctrl_masks = {
+			AK09912_REG_ST1_DRDY_MASK,
+			AK09912_REG_ST2_HOFL_MASK,
+			0,
+			AK09912_REG_CNTL2_MODE_MASK},
+		.ctrl_modes = {
+			AK09912_REG_CNTL_MODE_POWER_DOWN,
+			AK09912_REG_CNTL_MODE_ONCE,
+			AK09912_REG_CNTL_MODE_SELF_TEST,
+			AK09912_REG_CNTL_MODE_FUSE_ROM},
+		.data_regs = {
+			AK09912_REG_HXL,
+			AK09912_REG_HYL,
+			AK09912_REG_HZL},
+	}
+};
+
+/*
+ * Per-instance context data for the device.
+ */
+struct ak8975_data {
+	struct i2c_client	*client;
+	struct ak_def		*def;
+	struct attribute_group	attrs;
+	struct mutex		lock;
+	u8			asa[3];
+	long			raw_to_gauss[3];
+	int			eoc_gpio;
+	int			eoc_irq;
+	wait_queue_head_t	data_ready_queue;
+	unsigned long		flags;
+	u8			cntl_cache;
+};
+
+/*
+ * Return 0 if the i2c device is the one we expect.
+ * return a negative error number otherwise
+ */
+static int ak8975_who_i_am(struct i2c_client *client,
+			   enum asahi_compass_chipset type)
+{
+	u8 wia_val[2];
+	int ret;
+
+	/*
+	 * Signature for each device:
+	 * Device   |  WIA1      |  WIA2
+	 * AK09912  |  DEVICE_ID |  AK09912_DEVICE_ID
+	 * AK09911  |  DEVICE_ID |  AK09911_DEVICE_ID
+	 * AK8975   |  DEVICE_ID |  NA
+	 * AK8963   |  DEVICE_ID |  NA
+	 */
+	ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1,
+					    2, wia_val);
+	if (ret < 0) {
+		dev_err(&client->dev, "Error reading WIA\n");
+		return ret;
+	}
+
+	if (wia_val[0] != AK8975_DEVICE_ID)
+		return -ENODEV;
+
+	switch (type) {
+	case AK8975:
+	case AK8963:
+		return 0;
+	case AK09911:
+		if (wia_val[1] == AK09911_DEVICE_ID)
+			return 0;
+		break;
+	case AK09912:
+		if (wia_val[1] == AK09912_DEVICE_ID)
+			return 0;
+		break;
+	default:
+		dev_err(&client->dev, "Type %d unknown\n", type);
+	}
+	return -ENODEV;
+}
+
+/*
+ * Helper function to write to CNTL register.
+ */
+static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode)
+{
+	u8 regval;
+	int ret;
+
+	regval = (data->cntl_cache & ~data->def->ctrl_masks[CNTL_MODE]) |
+		 data->def->ctrl_modes[mode];
+	ret = i2c_smbus_write_byte_data(data->client,
+					data->def->ctrl_regs[CNTL], regval);
+	if (ret < 0) {
+		return ret;
+	}
+	data->cntl_cache = regval;
+	/* After mode change wait atleast 100us */
+	usleep_range(100, 500);
+
+	return 0;
+}
+
+/*
+ * Handle data ready irq
+ */
+static irqreturn_t ak8975_irq_handler(int irq, void *data)
+{
+	struct ak8975_data *ak8975 = data;
+
+	set_bit(0, &ak8975->flags);
+	wake_up(&ak8975->data_ready_queue);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Install data ready interrupt handler
+ */
+static int ak8975_setup_irq(struct ak8975_data *data)
+{
+	struct i2c_client *client = data->client;
+	int rc;
+	int irq;
+
+	init_waitqueue_head(&data->data_ready_queue);
+	clear_bit(0, &data->flags);
+	if (client->irq)
+		irq = client->irq;
+	else
+		irq = gpio_to_irq(data->eoc_gpio);
+
+	rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
+			      IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+			      dev_name(&client->dev), data);
+	if (rc < 0) {
+		dev_err(&client->dev,
+			"irq %d request failed, (gpio %d): %d\n",
+			irq, data->eoc_gpio, rc);
+		return rc;
+	}
+
+	data->eoc_irq = irq;
+
+	return rc;
+}
+
+
+/*
+ * Perform some start-of-day setup, including reading the asa calibration
+ * values and caching them.
+ */
+static int ak8975_setup(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct ak8975_data *data = iio_priv(indio_dev);
+	int ret;
+
+	/* Write the fused rom access mode. */
+	ret = ak8975_set_mode(data, FUSE_ROM);
+	if (ret < 0) {
+		dev_err(&client->dev, "Error in setting fuse access mode\n");
+		return ret;
+	}
+
+	/* Get asa data and store in the device data. */
+	ret = i2c_smbus_read_i2c_block_data(client,
+					    data->def->ctrl_regs[ASA_BASE],
+					    3, data->asa);
+	if (ret < 0) {
+		dev_err(&client->dev, "Not able to read asa data\n");
+		return ret;
+	}
+
+	/* After reading fuse ROM data set power-down mode */
+	ret = ak8975_set_mode(data, POWER_DOWN);
+	if (ret < 0) {
+		dev_err(&client->dev, "Error in setting power-down mode\n");
+		return ret;
+	}
+
+	if (data->eoc_gpio > 0 || client->irq > 0) {
+		ret = ak8975_setup_irq(data);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"Error setting data ready interrupt\n");
+			return ret;
+		}
+	}
+
+	data->raw_to_gauss[0] = data->def->raw_to_gauss(data->asa[0]);
+	data->raw_to_gauss[1] = data->def->raw_to_gauss(data->asa[1]);
+	data->raw_to_gauss[2] = data->def->raw_to_gauss(data->asa[2]);
+
+	return 0;
+}
+
+static int wait_conversion_complete_gpio(struct ak8975_data *data)
+{
+	struct i2c_client *client = data->client;
+	u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT;
+	int ret;
+
+	/* Wait for the conversion to complete. */
+	while (timeout_ms) {
+		msleep(AK8975_CONVERSION_DONE_POLL_TIME);
+		if (gpio_get_value(data->eoc_gpio))
+			break;
+		timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME;
+	}
+	if (!timeout_ms) {
+		dev_err(&client->dev, "Conversion timeout happened\n");
+		return -EINVAL;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]);
+	if (ret < 0)
+		dev_err(&client->dev, "Error in reading ST1\n");
+
+	return ret;
+}
+
+static int wait_conversion_complete_polled(struct ak8975_data *data)
+{
+	struct i2c_client *client = data->client;
+	u8 read_status;
+	u32 timeout_ms = AK8975_MAX_CONVERSION_TIMEOUT;
+	int ret;
+
+	/* Wait for the conversion to complete. */
+	while (timeout_ms) {
+		msleep(AK8975_CONVERSION_DONE_POLL_TIME);
+		ret = i2c_smbus_read_byte_data(client,
+					       data->def->ctrl_regs[ST1]);
+		if (ret < 0) {
+			dev_err(&client->dev, "Error in reading ST1\n");
+			return ret;
+		}
+		read_status = ret;
+		if (read_status)
+			break;
+		timeout_ms -= AK8975_CONVERSION_DONE_POLL_TIME;
+	}
+	if (!timeout_ms) {
+		dev_err(&client->dev, "Conversion timeout happened\n");
+		return -EINVAL;
+	}
+
+	return read_status;
+}
+
+/* Returns 0 if the end of conversion interrupt occured or -ETIME otherwise */
+static int wait_conversion_complete_interrupt(struct ak8975_data *data)
+{
+	int ret;
+
+	ret = wait_event_timeout(data->data_ready_queue,
+				 test_bit(0, &data->flags),
+				 AK8975_DATA_READY_TIMEOUT);
+	clear_bit(0, &data->flags);
+
+	return ret > 0 ? 0 : -ETIME;
+}
+
+/*
+ * Emits the raw flux value for the x, y, or z axis.
+ */
+static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
+{
+	struct ak8975_data *data = iio_priv(indio_dev);
+	struct i2c_client *client = data->client;
+	int ret;
+
+	mutex_lock(&data->lock);
+
+	/* Set up the device for taking a sample. */
+	ret = ak8975_set_mode(data, MODE_ONCE);
+	if (ret < 0) {
+		dev_err(&client->dev, "Error in setting operating mode\n");
+		goto exit;
+	}
+
+	/* Wait for the conversion to complete. */
+	if (data->eoc_irq)
+		ret = wait_conversion_complete_interrupt(data);
+	else if (gpio_is_valid(data->eoc_gpio))
+		ret = wait_conversion_complete_gpio(data);
+	else
+		ret = wait_conversion_complete_polled(data);
+	if (ret < 0)
+		goto exit;
+
+	/* This will be executed only for non-interrupt based waiting case */
+	if (ret & data->def->ctrl_masks[ST1_DRDY]) {
+		ret = i2c_smbus_read_byte_data(client,
+					       data->def->ctrl_regs[ST2]);
+		if (ret < 0) {
+			dev_err(&client->dev, "Error in reading ST2\n");
+			goto exit;
+		}
+		if (ret & (data->def->ctrl_masks[ST2_DERR] |
+			   data->def->ctrl_masks[ST2_HOFL])) {
+			dev_err(&client->dev, "ST2 status error 0x%x\n", ret);
+			ret = -EINVAL;
+			goto exit;
+		}
+	}
+
+	/* Read the flux value from the appropriate register
+	   (the register is specified in the iio device attributes). */
+	ret = i2c_smbus_read_word_data(client, data->def->data_regs[index]);
+	if (ret < 0) {
+		dev_err(&client->dev, "Read axis data fails\n");
+		goto exit;
+	}
+
+	mutex_unlock(&data->lock);
+
+	/* Clamp to valid range. */
+	*val = clamp_t(s16, ret, -data->def->range, data->def->range);
+	return IIO_VAL_INT;
+
+exit:
+	mutex_unlock(&data->lock);
+	return ret;
+}
+
+static int ak8975_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan,
+			   int *val, int *val2,
+			   long mask)
+{
+	struct ak8975_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		return ak8975_read_axis(indio_dev, chan->address, val);
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = data->raw_to_gauss[chan->address];
+		return IIO_VAL_INT_PLUS_MICRO;
+	}
+	return -EINVAL;
+}
+
+#define AK8975_CHANNEL(axis, index)					\
+	{								\
+		.type = IIO_MAGN,					\
+		.modified = 1,						\
+		.channel2 = IIO_MOD_##axis,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
+			     BIT(IIO_CHAN_INFO_SCALE),			\
+		.address = index,					\
+	}
+
+static const struct iio_chan_spec ak8975_channels[] = {
+	AK8975_CHANNEL(X, 0), AK8975_CHANNEL(Y, 1), AK8975_CHANNEL(Z, 2),
+};
+
+static const struct iio_info ak8975_info = {
+	.read_raw = &ak8975_read_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const struct acpi_device_id ak_acpi_match[] = {
+	{"AK8975", AK8975},
+	{"AK8963", AK8963},
+	{"INVN6500", AK8963},
+	{"AK09911", AK09911},
+	{"AK09912", AK09912},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
+
+static const char *ak8975_match_acpi_device(struct device *dev,
+					    enum asahi_compass_chipset *chipset)
+{
+	const struct acpi_device_id *id;
+
+	id = acpi_match_device(dev->driver->acpi_match_table, dev);
+	if (!id)
+		return NULL;
+	*chipset = (int)id->driver_data;
+
+	return dev_name(dev);
+}
+
+static int ak8975_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct ak8975_data *data;
+	struct iio_dev *indio_dev;
+	int eoc_gpio;
+	int err;
+	const char *name = NULL;
+	enum asahi_compass_chipset chipset = AK_MAX_TYPE;
+
+	/* Grab and set up the supplied GPIO. */
+	if (client->dev.platform_data)
+		eoc_gpio = *(int *)(client->dev.platform_data);
+	else if (client->dev.of_node)
+		eoc_gpio = of_get_gpio(client->dev.of_node, 0);
+	else
+		eoc_gpio = -1;
+
+	if (eoc_gpio == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	/* We may not have a GPIO based IRQ to scan, that is fine, we will
+	   poll if so */
+	if (gpio_is_valid(eoc_gpio)) {
+		err = devm_gpio_request_one(&client->dev, eoc_gpio,
+							GPIOF_IN, "ak_8975");
+		if (err < 0) {
+			dev_err(&client->dev,
+				"failed to request GPIO %d, error %d\n",
+							eoc_gpio, err);
+			return err;
+		}
+	}
+
+	/* Register with IIO */
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	i2c_set_clientdata(client, indio_dev);
+
+	data->client = client;
+	data->eoc_gpio = eoc_gpio;
+	data->eoc_irq = 0;
+
+	/* id will be NULL when enumerated via ACPI */
+	if (id) {
+		chipset = (enum asahi_compass_chipset)(id->driver_data);
+		name = id->name;
+	} else if (ACPI_HANDLE(&client->dev))
+		name = ak8975_match_acpi_device(&client->dev, &chipset);
+	else
+		return -ENOSYS;
+
+	if (chipset >= AK_MAX_TYPE) {
+		dev_err(&client->dev, "AKM device type unsupported: %d\n",
+			chipset);
+		return -ENODEV;
+	}
+
+	data->def = &ak_def_array[chipset];
+	err = ak8975_who_i_am(client, data->def->type);
+	if (err < 0) {
+		dev_err(&client->dev, "Unexpected device\n");
+		return err;
+	}
+	dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
+
+	/* Perform some basic start-of-day setup of the device. */
+	err = ak8975_setup(client);
+	if (err < 0) {
+		dev_err(&client->dev, "%s initialization fails\n", name);
+		return err;
+	}
+
+	mutex_init(&data->lock);
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->channels = ak8975_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);
+	indio_dev->info = &ak8975_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = name;
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ak8975_id[] = {
+	{"ak8975", AK8975},
+	{"ak8963", AK8963},
+	{"AK8963", AK8963},
+	{"ak09911", AK09911},
+	{"ak09912", AK09912},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak8975_id);
+
+static const struct of_device_id ak8975_of_match[] = {
+	{ .compatible = "asahi-kasei,ak8975", },
+	{ .compatible = "ak8975", },
+	{ .compatible = "asahi-kasei,ak8963", },
+	{ .compatible = "ak8963", },
+	{ .compatible = "asahi-kasei,ak09911", },
+	{ .compatible = "ak09911", },
+	{ .compatible = "asahi-kasei,ak09912", },
+	{ .compatible = "ak09912", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ak8975_of_match);
+
+static struct i2c_driver ak8975_driver = {
+	.driver = {
+		.name	= "ak8975",
+		.of_match_table = of_match_ptr(ak8975_of_match),
+		.acpi_match_table = ACPI_PTR(ak_acpi_match),
+	},
+	.probe		= ak8975_probe,
+	.id_table	= ak8975_id,
+};
+module_i2c_driver(ak8975_driver);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("AK8975 magnetometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
new file mode 100644
index 0000000..1615b23
--- /dev/null
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -0,0 +1,1085 @@
+/*
+ * Bosch BMC150 three-axis magnetic field sensor driver
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This code is based on bmm050_api.c authored by contact@bosch.sensortec.com:
+ *
+ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+
+#define BMC150_MAGN_DRV_NAME			"bmc150_magn"
+#define BMC150_MAGN_IRQ_NAME			"bmc150_magn_event"
+
+#define BMC150_MAGN_REG_CHIP_ID			0x40
+#define BMC150_MAGN_CHIP_ID_VAL			0x32
+
+#define BMC150_MAGN_REG_X_L			0x42
+#define BMC150_MAGN_REG_X_M			0x43
+#define BMC150_MAGN_REG_Y_L			0x44
+#define BMC150_MAGN_REG_Y_M			0x45
+#define BMC150_MAGN_SHIFT_XY_L			3
+#define BMC150_MAGN_REG_Z_L			0x46
+#define BMC150_MAGN_REG_Z_M			0x47
+#define BMC150_MAGN_SHIFT_Z_L			1
+#define BMC150_MAGN_REG_RHALL_L			0x48
+#define BMC150_MAGN_REG_RHALL_M			0x49
+#define BMC150_MAGN_SHIFT_RHALL_L		2
+
+#define BMC150_MAGN_REG_INT_STATUS		0x4A
+
+#define BMC150_MAGN_REG_POWER			0x4B
+#define BMC150_MAGN_MASK_POWER_CTL		BIT(0)
+
+#define BMC150_MAGN_REG_OPMODE_ODR		0x4C
+#define BMC150_MAGN_MASK_OPMODE			GENMASK(2, 1)
+#define BMC150_MAGN_SHIFT_OPMODE		1
+#define BMC150_MAGN_MODE_NORMAL			0x00
+#define BMC150_MAGN_MODE_FORCED			0x01
+#define BMC150_MAGN_MODE_SLEEP			0x03
+#define BMC150_MAGN_MASK_ODR			GENMASK(5, 3)
+#define BMC150_MAGN_SHIFT_ODR			3
+
+#define BMC150_MAGN_REG_INT			0x4D
+
+#define BMC150_MAGN_REG_INT_DRDY		0x4E
+#define BMC150_MAGN_MASK_DRDY_EN		BIT(7)
+#define BMC150_MAGN_SHIFT_DRDY_EN		7
+#define BMC150_MAGN_MASK_DRDY_INT3		BIT(6)
+#define BMC150_MAGN_MASK_DRDY_Z_EN		BIT(5)
+#define BMC150_MAGN_MASK_DRDY_Y_EN		BIT(4)
+#define BMC150_MAGN_MASK_DRDY_X_EN		BIT(3)
+#define BMC150_MAGN_MASK_DRDY_DR_POLARITY	BIT(2)
+#define BMC150_MAGN_MASK_DRDY_LATCHING		BIT(1)
+#define BMC150_MAGN_MASK_DRDY_INT3_POLARITY	BIT(0)
+
+#define BMC150_MAGN_REG_LOW_THRESH		0x4F
+#define BMC150_MAGN_REG_HIGH_THRESH		0x50
+#define BMC150_MAGN_REG_REP_XY			0x51
+#define BMC150_MAGN_REG_REP_Z			0x52
+#define BMC150_MAGN_REG_REP_DATAMASK		GENMASK(7, 0)
+
+#define BMC150_MAGN_REG_TRIM_START		0x5D
+#define BMC150_MAGN_REG_TRIM_END		0x71
+
+#define BMC150_MAGN_XY_OVERFLOW_VAL		-4096
+#define BMC150_MAGN_Z_OVERFLOW_VAL		-16384
+
+/* Time from SUSPEND to SLEEP */
+#define BMC150_MAGN_START_UP_TIME_MS		3
+
+#define BMC150_MAGN_AUTO_SUSPEND_DELAY_MS	2000
+
+#define BMC150_MAGN_REGVAL_TO_REPXY(regval) (((regval) * 2) + 1)
+#define BMC150_MAGN_REGVAL_TO_REPZ(regval) ((regval) + 1)
+#define BMC150_MAGN_REPXY_TO_REGVAL(rep) (((rep) - 1) / 2)
+#define BMC150_MAGN_REPZ_TO_REGVAL(rep) ((rep) - 1)
+
+enum bmc150_magn_axis {
+	AXIS_X,
+	AXIS_Y,
+	AXIS_Z,
+	RHALL,
+	AXIS_XYZ_MAX = RHALL,
+	AXIS_XYZR_MAX,
+};
+
+enum bmc150_magn_power_modes {
+	BMC150_MAGN_POWER_MODE_SUSPEND,
+	BMC150_MAGN_POWER_MODE_SLEEP,
+	BMC150_MAGN_POWER_MODE_NORMAL,
+};
+
+struct bmc150_magn_trim_regs {
+	s8 x1;
+	s8 y1;
+	__le16 reserved1;
+	u8 reserved2;
+	__le16 z4;
+	s8 x2;
+	s8 y2;
+	__le16 reserved3;
+	__le16 z2;
+	__le16 z1;
+	__le16 xyz1;
+	__le16 z3;
+	s8 xy2;
+	u8 xy1;
+} __packed;
+
+struct bmc150_magn_data {
+	struct i2c_client *client;
+	/*
+	 * 1. Protect this structure.
+	 * 2. Serialize sequences that power on/off the device and access HW.
+	 */
+	struct mutex mutex;
+	struct regmap *regmap;
+	/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
+	s32 buffer[6];
+	struct iio_trigger *dready_trig;
+	bool dready_trigger_on;
+	int max_odr;
+};
+
+static const struct {
+	int freq;
+	u8 reg_val;
+} bmc150_magn_samp_freq_table[] = { {2, 0x01},
+				    {6, 0x02},
+				    {8, 0x03},
+				    {10, 0x00},
+				    {15, 0x04},
+				    {20, 0x05},
+				    {25, 0x06},
+				    {30, 0x07} };
+
+enum bmc150_magn_presets {
+	LOW_POWER_PRESET,
+	REGULAR_PRESET,
+	ENHANCED_REGULAR_PRESET,
+	HIGH_ACCURACY_PRESET
+};
+
+static const struct bmc150_magn_preset {
+	u8 rep_xy;
+	u8 rep_z;
+	u8 odr;
+} bmc150_magn_presets_table[] = {
+	[LOW_POWER_PRESET] = {3, 3, 10},
+	[REGULAR_PRESET] =  {9, 15, 10},
+	[ENHANCED_REGULAR_PRESET] =  {15, 27, 10},
+	[HIGH_ACCURACY_PRESET] =  {47, 83, 20},
+};
+
+#define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET
+
+static bool bmc150_magn_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case BMC150_MAGN_REG_POWER:
+	case BMC150_MAGN_REG_OPMODE_ODR:
+	case BMC150_MAGN_REG_INT:
+	case BMC150_MAGN_REG_INT_DRDY:
+	case BMC150_MAGN_REG_LOW_THRESH:
+	case BMC150_MAGN_REG_HIGH_THRESH:
+	case BMC150_MAGN_REG_REP_XY:
+	case BMC150_MAGN_REG_REP_Z:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool bmc150_magn_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case BMC150_MAGN_REG_X_L:
+	case BMC150_MAGN_REG_X_M:
+	case BMC150_MAGN_REG_Y_L:
+	case BMC150_MAGN_REG_Y_M:
+	case BMC150_MAGN_REG_Z_L:
+	case BMC150_MAGN_REG_Z_M:
+	case BMC150_MAGN_REG_RHALL_L:
+	case BMC150_MAGN_REG_RHALL_M:
+	case BMC150_MAGN_REG_INT_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config bmc150_magn_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = BMC150_MAGN_REG_TRIM_END,
+	.cache_type = REGCACHE_RBTREE,
+
+	.writeable_reg = bmc150_magn_is_writeable_reg,
+	.volatile_reg = bmc150_magn_is_volatile_reg,
+};
+
+static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data,
+				      enum bmc150_magn_power_modes mode,
+				      bool state)
+{
+	int ret;
+
+	switch (mode) {
+	case BMC150_MAGN_POWER_MODE_SUSPEND:
+		ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_POWER,
+					 BMC150_MAGN_MASK_POWER_CTL, !state);
+		if (ret < 0)
+			return ret;
+		usleep_range(BMC150_MAGN_START_UP_TIME_MS * 1000, 20000);
+		return 0;
+	case BMC150_MAGN_POWER_MODE_SLEEP:
+		return regmap_update_bits(data->regmap,
+					  BMC150_MAGN_REG_OPMODE_ODR,
+					  BMC150_MAGN_MASK_OPMODE,
+					  BMC150_MAGN_MODE_SLEEP <<
+					  BMC150_MAGN_SHIFT_OPMODE);
+	case BMC150_MAGN_POWER_MODE_NORMAL:
+		return regmap_update_bits(data->regmap,
+					  BMC150_MAGN_REG_OPMODE_ODR,
+					  BMC150_MAGN_MASK_OPMODE,
+					  BMC150_MAGN_MODE_NORMAL <<
+					  BMC150_MAGN_SHIFT_OPMODE);
+	}
+
+	return -EINVAL;
+}
+
+static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on)
+{
+#ifdef CONFIG_PM
+	int ret;
+
+	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 to change power state to %d\n", on);
+		if (on)
+			pm_runtime_put_noidle(&data->client->dev);
+
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val)
+{
+	int ret, reg_val;
+	u8 i, odr_val;
+
+	ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, &reg_val);
+	if (ret < 0)
+		return ret;
+	odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR;
+
+	for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++)
+		if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) {
+			*val = bmc150_magn_samp_freq_table[i].freq;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val)
+{
+	int ret;
+	u8 i;
+
+	for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
+		if (bmc150_magn_samp_freq_table[i].freq == val) {
+			ret = regmap_update_bits(data->regmap,
+						 BMC150_MAGN_REG_OPMODE_ODR,
+						 BMC150_MAGN_MASK_ODR,
+						 bmc150_magn_samp_freq_table[i].
+						 reg_val <<
+						 BMC150_MAGN_SHIFT_ODR);
+			if (ret < 0)
+				return ret;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int bmc150_magn_set_max_odr(struct bmc150_magn_data *data, int rep_xy,
+				   int rep_z, int odr)
+{
+	int ret, reg_val, max_odr;
+
+	if (rep_xy <= 0) {
+		ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY,
+				  &reg_val);
+		if (ret < 0)
+			return ret;
+		rep_xy = BMC150_MAGN_REGVAL_TO_REPXY(reg_val);
+	}
+	if (rep_z <= 0) {
+		ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z,
+				  &reg_val);
+		if (ret < 0)
+			return ret;
+		rep_z = BMC150_MAGN_REGVAL_TO_REPZ(reg_val);
+	}
+	if (odr <= 0) {
+		ret = bmc150_magn_get_odr(data, &odr);
+		if (ret < 0)
+			return ret;
+	}
+	/* the maximum selectable read-out frequency from datasheet */
+	max_odr = 1000000 / (145 * rep_xy + 500 * rep_z + 980);
+	if (odr > max_odr) {
+		dev_err(&data->client->dev,
+			"Can't set oversampling with sampling freq %d\n",
+			odr);
+		return -EINVAL;
+	}
+	data->max_odr = max_odr;
+
+	return 0;
+}
+
+static s32 bmc150_magn_compensate_x(struct bmc150_magn_trim_regs *tregs, s16 x,
+				    u16 rhall)
+{
+	s16 val;
+	u16 xyz1 = le16_to_cpu(tregs->xyz1);
+
+	if (x == BMC150_MAGN_XY_OVERFLOW_VAL)
+		return S32_MIN;
+
+	if (!rhall)
+		rhall = xyz1;
+
+	val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000)));
+	val = ((s16)((((s32)x) * ((((((((s32)tregs->xy2) * ((((s32)val) *
+	      ((s32)val)) >> 7)) + (((s32)val) *
+	      ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) *
+	      ((s32)(((s16)tregs->x2) + ((s16)0xA0)))) >> 12)) >> 13)) +
+	      (((s16)tregs->x1) << 3);
+
+	return (s32)val;
+}
+
+static s32 bmc150_magn_compensate_y(struct bmc150_magn_trim_regs *tregs, s16 y,
+				    u16 rhall)
+{
+	s16 val;
+	u16 xyz1 = le16_to_cpu(tregs->xyz1);
+
+	if (y == BMC150_MAGN_XY_OVERFLOW_VAL)
+		return S32_MIN;
+
+	if (!rhall)
+		rhall = xyz1;
+
+	val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000)));
+	val = ((s16)((((s32)y) * ((((((((s32)tregs->xy2) * ((((s32)val) *
+	      ((s32)val)) >> 7)) + (((s32)val) *
+	      ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) *
+	      ((s32)(((s16)tregs->y2) + ((s16)0xA0)))) >> 12)) >> 13)) +
+	      (((s16)tregs->y1) << 3);
+
+	return (s32)val;
+}
+
+static s32 bmc150_magn_compensate_z(struct bmc150_magn_trim_regs *tregs, s16 z,
+				    u16 rhall)
+{
+	s32 val;
+	u16 xyz1 = le16_to_cpu(tregs->xyz1);
+	u16 z1 = le16_to_cpu(tregs->z1);
+	s16 z2 = le16_to_cpu(tregs->z2);
+	s16 z3 = le16_to_cpu(tregs->z3);
+	s16 z4 = le16_to_cpu(tregs->z4);
+
+	if (z == BMC150_MAGN_Z_OVERFLOW_VAL)
+		return S32_MIN;
+
+	val = (((((s32)(z - z4)) << 15) - ((((s32)z3) * ((s32)(((s16)rhall) -
+	      ((s16)xyz1)))) >> 2)) / (z2 + ((s16)(((((s32)z1) *
+	      ((((s16)rhall) << 1))) + (1 << 15)) >> 16))));
+
+	return val;
+}
+
+static int bmc150_magn_read_xyz(struct bmc150_magn_data *data, s32 *buffer)
+{
+	int ret;
+	__le16 values[AXIS_XYZR_MAX];
+	s16 raw_x, raw_y, raw_z;
+	u16 rhall;
+	struct bmc150_magn_trim_regs tregs;
+
+	ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_X_L,
+			       values, sizeof(values));
+	if (ret < 0)
+		return ret;
+
+	raw_x = (s16)le16_to_cpu(values[AXIS_X]) >> BMC150_MAGN_SHIFT_XY_L;
+	raw_y = (s16)le16_to_cpu(values[AXIS_Y]) >> BMC150_MAGN_SHIFT_XY_L;
+	raw_z = (s16)le16_to_cpu(values[AXIS_Z]) >> BMC150_MAGN_SHIFT_Z_L;
+	rhall = le16_to_cpu(values[RHALL]) >> BMC150_MAGN_SHIFT_RHALL_L;
+
+	ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_TRIM_START,
+			       &tregs, sizeof(tregs));
+	if (ret < 0)
+		return ret;
+
+	buffer[AXIS_X] = bmc150_magn_compensate_x(&tregs, raw_x, rhall);
+	buffer[AXIS_Y] = bmc150_magn_compensate_y(&tregs, raw_y, rhall);
+	buffer[AXIS_Z] = bmc150_magn_compensate_z(&tregs, raw_z, rhall);
+
+	return 0;
+}
+
+static int bmc150_magn_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan,
+				int *val, int *val2, long mask)
+{
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret, tmp;
+	s32 values[AXIS_XYZ_MAX];
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (iio_buffer_enabled(indio_dev))
+			return -EBUSY;
+		mutex_lock(&data->mutex);
+
+		ret = bmc150_magn_set_power_state(data, true);
+		if (ret < 0) {
+			mutex_unlock(&data->mutex);
+			return ret;
+		}
+
+		ret = bmc150_magn_read_xyz(data, values);
+		if (ret < 0) {
+			bmc150_magn_set_power_state(data, false);
+			mutex_unlock(&data->mutex);
+			return ret;
+		}
+		*val = values[chan->scan_index];
+
+		ret = bmc150_magn_set_power_state(data, false);
+		if (ret < 0) {
+			mutex_unlock(&data->mutex);
+			return ret;
+		}
+
+		mutex_unlock(&data->mutex);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		/*
+		 * The API/driver performs an off-chip temperature
+		 * compensation and outputs x/y/z magnetic field data in
+		 * 16 LSB/uT to the upper application layer.
+		 */
+		*val = 0;
+		*val2 = 625;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = bmc150_magn_get_odr(data, val);
+		if (ret < 0)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		switch (chan->channel2) {
+		case IIO_MOD_X:
+		case IIO_MOD_Y:
+			ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY,
+					  &tmp);
+			if (ret < 0)
+				return ret;
+			*val = BMC150_MAGN_REGVAL_TO_REPXY(tmp);
+			return IIO_VAL_INT;
+		case IIO_MOD_Z:
+			ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z,
+					  &tmp);
+			if (ret < 0)
+				return ret;
+			*val = BMC150_MAGN_REGVAL_TO_REPZ(tmp);
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bmc150_magn_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan,
+				 int val, int val2, long mask)
+{
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (val > data->max_odr)
+			return -EINVAL;
+		mutex_lock(&data->mutex);
+		ret = bmc150_magn_set_odr(data, val);
+		mutex_unlock(&data->mutex);
+		return ret;
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		switch (chan->channel2) {
+		case IIO_MOD_X:
+		case IIO_MOD_Y:
+			if (val < 1 || val > 511)
+				return -EINVAL;
+			mutex_lock(&data->mutex);
+			ret = bmc150_magn_set_max_odr(data, val, 0, 0);
+			if (ret < 0) {
+				mutex_unlock(&data->mutex);
+				return ret;
+			}
+			ret = regmap_update_bits(data->regmap,
+						 BMC150_MAGN_REG_REP_XY,
+						 BMC150_MAGN_REG_REP_DATAMASK,
+						 BMC150_MAGN_REPXY_TO_REGVAL
+						 (val));
+			mutex_unlock(&data->mutex);
+			return ret;
+		case IIO_MOD_Z:
+			if (val < 1 || val > 256)
+				return -EINVAL;
+			mutex_lock(&data->mutex);
+			ret = bmc150_magn_set_max_odr(data, 0, val, 0);
+			if (ret < 0) {
+				mutex_unlock(&data->mutex);
+				return ret;
+			}
+			ret = regmap_update_bits(data->regmap,
+						 BMC150_MAGN_REG_REP_Z,
+						 BMC150_MAGN_REG_REP_DATAMASK,
+						 BMC150_MAGN_REPZ_TO_REGVAL
+						 (val));
+			mutex_unlock(&data->mutex);
+			return ret;
+		default:
+			return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static ssize_t bmc150_magn_show_samp_freq_avail(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	size_t len = 0;
+	u8 i;
+
+	for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
+		if (bmc150_magn_samp_freq_table[i].freq > data->max_odr)
+			break;
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+				 bmc150_magn_samp_freq_table[i].freq);
+	}
+	/* replace last space with a newline */
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(bmc150_magn_show_samp_freq_avail);
+
+static struct attribute *bmc150_magn_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group bmc150_magn_attrs_group = {
+	.attrs = bmc150_magn_attributes,
+};
+
+#define BMC150_MAGN_CHANNEL(_axis) {					\
+	.type = IIO_MAGN,						\
+	.modified = 1,							\
+	.channel2 = IIO_MOD_##_axis,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),	\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |	\
+				    BIT(IIO_CHAN_INFO_SCALE),		\
+	.scan_index = AXIS_##_axis,					\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 32,						\
+		.storagebits = 32,					\
+		.endianness = IIO_LE					\
+	},								\
+}
+
+static const struct iio_chan_spec bmc150_magn_channels[] = {
+	BMC150_MAGN_CHANNEL(X),
+	BMC150_MAGN_CHANNEL(Y),
+	BMC150_MAGN_CHANNEL(Z),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_info bmc150_magn_info = {
+	.attrs = &bmc150_magn_attrs_group,
+	.read_raw = bmc150_magn_read_raw,
+	.write_raw = bmc150_magn_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const unsigned long bmc150_magn_scan_masks[] = {
+					BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
+					0};
+
+static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = bmc150_magn_read_xyz(data, data->buffer);
+	if (ret < 0)
+		goto err;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+					   pf->timestamp);
+
+err:
+	mutex_unlock(&data->mutex);
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static int bmc150_magn_init(struct bmc150_magn_data *data)
+{
+	int ret, chip_id;
+	struct bmc150_magn_preset preset;
+
+	ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
+					 false);
+	if (ret < 0) {
+		dev_err(&data->client->dev,
+			"Failed to bring up device from suspend mode\n");
+		return ret;
+	}
+
+	ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed reading chip id\n");
+		goto err_poweroff;
+	}
+	if (chip_id != BMC150_MAGN_CHIP_ID_VAL) {
+		dev_err(&data->client->dev, "Invalid chip id 0x%x\n", chip_id);
+		ret = -ENODEV;
+		goto err_poweroff;
+	}
+	dev_dbg(&data->client->dev, "Chip id %x\n", chip_id);
+
+	preset = bmc150_magn_presets_table[BMC150_MAGN_DEFAULT_PRESET];
+	ret = bmc150_magn_set_odr(data, preset.odr);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed to set ODR to %d\n",
+			preset.odr);
+		goto err_poweroff;
+	}
+
+	ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_XY,
+			   BMC150_MAGN_REPXY_TO_REGVAL(preset.rep_xy));
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed to set REP XY to %d\n",
+			preset.rep_xy);
+		goto err_poweroff;
+	}
+
+	ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_Z,
+			   BMC150_MAGN_REPZ_TO_REGVAL(preset.rep_z));
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed to set REP Z to %d\n",
+			preset.rep_z);
+		goto err_poweroff;
+	}
+
+	ret = bmc150_magn_set_max_odr(data, preset.rep_xy, preset.rep_z,
+				      preset.odr);
+	if (ret < 0)
+		goto err_poweroff;
+
+	ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+					 true);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Failed to power on device\n");
+		goto err_poweroff;
+	}
+
+	return 0;
+
+err_poweroff:
+	bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+	return ret;
+}
+
+static int bmc150_magn_reset_intr(struct bmc150_magn_data *data)
+{
+	int tmp;
+
+	/*
+	 * Data Ready (DRDY) is always cleared after
+	 * readout of data registers ends.
+	 */
+	return regmap_read(data->regmap, BMC150_MAGN_REG_X_L, &tmp);
+}
+
+static int bmc150_magn_trig_try_reen(struct iio_trigger *trig)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret;
+
+	if (!data->dready_trigger_on)
+		return 0;
+
+	mutex_lock(&data->mutex);
+	ret = bmc150_magn_reset_intr(data);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig,
+						  bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret = 0;
+
+	mutex_lock(&data->mutex);
+	if (state == data->dready_trigger_on)
+		goto err_unlock;
+
+	ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY,
+				 BMC150_MAGN_MASK_DRDY_EN,
+				 state << BMC150_MAGN_SHIFT_DRDY_EN);
+	if (ret < 0)
+		goto err_unlock;
+
+	data->dready_trigger_on = state;
+
+	if (state) {
+		ret = bmc150_magn_reset_intr(data);
+		if (ret < 0)
+			goto err_unlock;
+	}
+	mutex_unlock(&data->mutex);
+
+	return 0;
+
+err_unlock:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static const struct iio_trigger_ops bmc150_magn_trigger_ops = {
+	.set_trigger_state = bmc150_magn_data_rdy_trigger_set_state,
+	.try_reenable = bmc150_magn_trig_try_reen,
+	.owner = THIS_MODULE,
+};
+
+static int bmc150_magn_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+	return bmc150_magn_set_power_state(data, true);
+}
+
+static int bmc150_magn_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+	return bmc150_magn_set_power_state(data, false);
+}
+
+static const struct iio_buffer_setup_ops bmc150_magn_buffer_setup_ops = {
+	.preenable = bmc150_magn_buffer_preenable,
+	.postenable = iio_triggered_buffer_postenable,
+	.predisable = iio_triggered_buffer_predisable,
+	.postdisable = bmc150_magn_buffer_postdisable,
+};
+
+static const char *bmc150_magn_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 bmc150_magn_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct bmc150_magn_data *data;
+	struct iio_dev *indio_dev;
+	const char *name = NULL;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	i2c_set_clientdata(client, indio_dev);
+	data->client = client;
+
+	if (id)
+		name = id->name;
+	else if (ACPI_HANDLE(&client->dev))
+		name = bmc150_magn_match_acpi_device(&client->dev);
+	else
+		return -ENOSYS;
+
+	mutex_init(&data->mutex);
+	data->regmap = devm_regmap_init_i2c(client, &bmc150_magn_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		dev_err(&client->dev, "Failed to allocate register map\n");
+		return PTR_ERR(data->regmap);
+	}
+
+	ret = bmc150_magn_init(data);
+	if (ret < 0)
+		return ret;
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->channels = bmc150_magn_channels;
+	indio_dev->num_channels = ARRAY_SIZE(bmc150_magn_channels);
+	indio_dev->available_scan_masks = bmc150_magn_scan_masks;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &bmc150_magn_info;
+
+	if (client->irq > 0) {
+		data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+							   "%s-dev%d",
+							   indio_dev->name,
+							   indio_dev->id);
+		if (!data->dready_trig) {
+			ret = -ENOMEM;
+			dev_err(&client->dev, "iio trigger alloc failed\n");
+			goto err_poweroff;
+		}
+
+		data->dready_trig->dev.parent = &client->dev;
+		data->dready_trig->ops = &bmc150_magn_trigger_ops;
+		iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+		ret = iio_trigger_register(data->dready_trig);
+		if (ret) {
+			dev_err(&client->dev, "iio trigger register failed\n");
+			goto err_poweroff;
+		}
+
+		ret = request_threaded_irq(client->irq,
+					   iio_trigger_generic_data_rdy_poll,
+					   NULL,
+					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					   BMC150_MAGN_IRQ_NAME,
+					   data->dready_trig);
+		if (ret < 0) {
+			dev_err(&client->dev, "request irq %d failed\n",
+				client->irq);
+			goto err_trigger_unregister;
+		}
+	}
+
+	ret = iio_triggered_buffer_setup(indio_dev,
+					 iio_pollfunc_store_time,
+					 bmc150_magn_trigger_handler,
+					 &bmc150_magn_buffer_setup_ops);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"iio triggered buffer setup failed\n");
+		goto err_free_irq;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to register iio device\n");
+		goto err_buffer_cleanup;
+	}
+
+	ret = pm_runtime_set_active(&client->dev);
+	if (ret)
+		goto err_iio_unregister;
+
+	pm_runtime_enable(&client->dev);
+	pm_runtime_set_autosuspend_delay(&client->dev,
+					 BMC150_MAGN_AUTO_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&client->dev);
+
+	dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+
+	return 0;
+
+err_iio_unregister:
+	iio_device_unregister(indio_dev);
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+err_free_irq:
+	if (client->irq > 0)
+		free_irq(client->irq, data->dready_trig);
+err_trigger_unregister:
+	if (data->dready_trig)
+		iio_trigger_unregister(data->dready_trig);
+err_poweroff:
+	bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+	return ret;
+}
+
+static int bmc150_magn_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	if (client->irq > 0)
+		free_irq(data->client->irq, data->dready_trig);
+
+	if (data->dready_trig)
+		iio_trigger_unregister(data->dready_trig);
+
+	mutex_lock(&data->mutex);
+	bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+	mutex_unlock(&data->mutex);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bmc150_magn_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP,
+					 true);
+	mutex_unlock(&data->mutex);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "powering off device failed\n");
+		return ret;
+	}
+	return 0;
+}
+
+/*
+ * Should be called with data->mutex held.
+ */
+static int bmc150_magn_runtime_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+	return bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+					  true);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int bmc150_magn_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP,
+					 true);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+
+static int bmc150_magn_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct bmc150_magn_data *data = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&data->mutex);
+	ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+					 true);
+	mutex_unlock(&data->mutex);
+
+	return ret;
+}
+#endif
+
+static const struct dev_pm_ops bmc150_magn_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(bmc150_magn_suspend, bmc150_magn_resume)
+	SET_RUNTIME_PM_OPS(bmc150_magn_runtime_suspend,
+			   bmc150_magn_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id bmc150_magn_acpi_match[] = {
+	{"BMC150B", 0},
+	{"BMC156B", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
+
+static const struct i2c_device_id bmc150_magn_id[] = {
+	{"bmc150_magn", 0},
+	{"bmc156_magn", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, bmc150_magn_id);
+
+static struct i2c_driver bmc150_magn_driver = {
+	.driver = {
+		   .name = BMC150_MAGN_DRV_NAME,
+		   .acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
+		   .pm = &bmc150_magn_pm_ops,
+		   },
+	.probe = bmc150_magn_probe,
+	.remove = bmc150_magn_remove,
+	.id_table = bmc150_magn_id,
+};
+module_i2c_driver(bmc150_magn_driver);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 magnetometer driver");
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
new file mode 100644
index 0000000..d8a0c8d
--- /dev/null
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -0,0 +1,535 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+enum magn_3d_channel {
+	CHANNEL_SCAN_INDEX_X,
+	CHANNEL_SCAN_INDEX_Y,
+	CHANNEL_SCAN_INDEX_Z,
+	CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP,
+	CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
+	CHANNEL_SCAN_INDEX_NORTH_MAGN,
+	CHANNEL_SCAN_INDEX_NORTH_TRUE,
+	MAGN_3D_CHANNEL_MAX,
+};
+
+struct magn_3d_state {
+	struct hid_sensor_hub_callbacks callbacks;
+	struct hid_sensor_common common_attributes;
+	struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
+
+	/* dynamically sized array to hold sensor values */
+	u32 *iio_vals;
+	/* array of pointers to sensor value */
+	u32 *magn_val_addr[MAGN_3D_CHANNEL_MAX];
+
+	int scale_pre_decml;
+	int scale_post_decml;
+	int scale_precision;
+	int value_offset;
+};
+
+static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
+	HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
+	HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
+	HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS,
+	HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
+	HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
+	HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
+	HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec magn_3d_channels[] = {
+	{
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_X,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Y,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}, {
+		.type = IIO_MAGN,
+		.modified = 1,
+		.channel2 = IIO_MOD_Z,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}, {
+		.type = IIO_ROT,
+		.modified = 1,
+		.channel2 = IIO_MOD_NORTH_MAGN_TILT_COMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}, {
+		.type = IIO_ROT,
+		.modified = 1,
+		.channel2 = IIO_MOD_NORTH_TRUE_TILT_COMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}, {
+		.type = IIO_ROT,
+		.modified = 1,
+		.channel2 = IIO_MOD_NORTH_MAGN,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}, {
+		.type = IIO_ROT,
+		.modified = 1,
+		.channel2 = IIO_MOD_NORTH_TRUE,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+		BIT(IIO_CHAN_INFO_SCALE) |
+		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+		BIT(IIO_CHAN_INFO_HYSTERESIS),
+	}
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+						int channel, int size)
+{
+	channels[channel].scan_type.sign = 's';
+	/* Real storage bits will change based on the report desc. */
+	channels[channel].scan_type.realbits = size * 8;
+	/* Maximum size of a sample to capture is u32 */
+	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int magn_3d_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2,
+			      long mask)
+{
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+	int report_id = -1;
+	u32 address;
+	int ret_type;
+
+	*val = 0;
+	*val2 = 0;
+	switch (mask) {
+	case 0:
+		hid_sensor_power_state(&magn_state->common_attributes, true);
+		report_id =
+			magn_state->magn[chan->address].report_id;
+		address = magn_3d_addresses[chan->address];
+		if (report_id >= 0)
+			*val = sensor_hub_input_attr_get_raw_value(
+				magn_state->common_attributes.hsdev,
+				HID_USAGE_SENSOR_COMPASS_3D, address,
+				report_id,
+				SENSOR_HUB_SYNC);
+		else {
+			*val = 0;
+			hid_sensor_power_state(&magn_state->common_attributes,
+						false);
+			return -EINVAL;
+		}
+		hid_sensor_power_state(&magn_state->common_attributes, false);
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = magn_state->scale_pre_decml;
+		*val2 = magn_state->scale_post_decml;
+		ret_type = magn_state->scale_precision;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = magn_state->value_offset;
+		ret_type = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret_type = hid_sensor_read_samp_freq_value(
+			&magn_state->common_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret_type = hid_sensor_read_raw_hyst_value(
+			&magn_state->common_attributes, val, val2);
+		break;
+	default:
+		ret_type = -EINVAL;
+		break;
+	}
+
+	return ret_type;
+}
+
+/* Channel write_raw handler */
+static int magn_3d_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+	int ret = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = hid_sensor_write_samp_freq_value(
+				&magn_state->common_attributes, val, val2);
+		break;
+	case IIO_CHAN_INFO_HYSTERESIS:
+		ret = hid_sensor_write_raw_hyst_value(
+				&magn_state->common_attributes, val, val2);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct iio_info magn_3d_info = {
+	.driver_module = THIS_MODULE,
+	.read_raw = &magn_3d_read_raw,
+	.write_raw = &magn_3d_write_raw,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data)
+{
+	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+	iio_push_to_buffers(indio_dev, data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
+	if (atomic_read(&magn_state->common_attributes.data_ready))
+		hid_sensor_push_data(indio_dev, magn_state->iio_vals);
+
+	return 0;
+}
+
+/* Capture samples in local storage */
+static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+				unsigned usage_id,
+				size_t raw_len, char *raw_data,
+				void *priv)
+{
+	struct iio_dev *indio_dev = platform_get_drvdata(priv);
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+	int offset;
+	int ret = 0;
+	u32 *iio_val = NULL;
+
+	switch (usage_id) {
+	case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
+	case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
+	case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
+		offset = (usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS)
+				+ CHANNEL_SCAN_INDEX_X;
+	break;
+	case HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH:
+	case HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH:
+	case HID_USAGE_SENSOR_ORIENT_MAGN_NORTH:
+	case HID_USAGE_SENSOR_ORIENT_TRUE_NORTH:
+		offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
+				+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	iio_val = magn_state->magn_val_addr[offset];
+
+	if (iio_val != NULL)
+		*iio_val = *((u32 *)raw_data);
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int magn_3d_parse_report(struct platform_device *pdev,
+				struct hid_sensor_hub_device *hsdev,
+				struct iio_chan_spec **channels,
+				int *chan_count,
+				unsigned usage_id,
+				struct magn_3d_state *st)
+{
+	int i;
+	int attr_count = 0;
+	struct iio_chan_spec *_channels;
+
+	/* Scan for each usage attribute supported */
+	for (i = 0; i < MAGN_3D_CHANNEL_MAX; i++) {
+		int status;
+		u32 address = magn_3d_addresses[i];
+
+		/* Check if usage attribute exists in the sensor hub device */
+		status = sensor_hub_input_get_attribute_info(hsdev,
+			HID_INPUT_REPORT,
+			usage_id,
+			address,
+			&(st->magn[i]));
+		if (!status)
+			attr_count++;
+	}
+
+	if (attr_count <= 0) {
+		dev_err(&pdev->dev,
+			"failed to find any supported usage attributes in report\n");
+		return  -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n",
+			attr_count);
+	dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n",
+			st->magn[0].index,
+			st->magn[0].report_id,
+			st->magn[1].index, st->magn[1].report_id,
+			st->magn[2].index, st->magn[2].report_id);
+
+	/* Setup IIO channel array */
+	_channels = devm_kcalloc(&pdev->dev, attr_count,
+				sizeof(struct iio_chan_spec),
+				GFP_KERNEL);
+	if (!_channels) {
+		dev_err(&pdev->dev,
+			"failed to allocate space for iio channels\n");
+		return -ENOMEM;
+	}
+
+	st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
+				sizeof(u32),
+				GFP_KERNEL);
+	if (!st->iio_vals) {
+		dev_err(&pdev->dev,
+			"failed to allocate space for iio values array\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0, *chan_count = 0;
+	i < MAGN_3D_CHANNEL_MAX && *chan_count < attr_count;
+	i++){
+		if (st->magn[i].index >= 0) {
+			/* Setup IIO channel struct */
+			(_channels[*chan_count]) = magn_3d_channels[i];
+			(_channels[*chan_count]).scan_index = *chan_count;
+			(_channels[*chan_count]).address = i;
+
+			/* Set magn_val_addr to iio value address */
+			st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
+			magn_3d_adjust_channel_bit_mask(_channels,
+							*chan_count,
+							st->magn[i].size);
+			(*chan_count)++;
+		}
+	}
+
+	if (*chan_count <= 0) {
+		dev_err(&pdev->dev,
+			"failed to find any magnetic channels setup\n");
+		return -EINVAL;
+	}
+
+	*channels = _channels;
+
+	dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
+			*chan_count);
+
+	st->scale_precision = hid_sensor_format_scale(
+				HID_USAGE_SENSOR_COMPASS_3D,
+				&st->magn[CHANNEL_SCAN_INDEX_X],
+				&st->scale_pre_decml, &st->scale_post_decml);
+
+	/* Set Sensitivity field ids, when there is no individual modifier */
+	if (st->common_attributes.sensitivity.index < 0) {
+		sensor_hub_input_get_attribute_info(hsdev,
+			HID_FEATURE_REPORT, usage_id,
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
+			HID_USAGE_SENSOR_DATA_ORIENTATION,
+			&st->common_attributes.sensitivity);
+		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
+			st->common_attributes.sensitivity.index,
+			st->common_attributes.sensitivity.report_id);
+	}
+
+	return 0;
+}
+
+/* Function to initialize the processing for usage id */
+static int hid_magn_3d_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	static char *name = "magn_3d";
+	struct iio_dev *indio_dev;
+	struct magn_3d_state *magn_state;
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_chan_spec *channels;
+	int chan_count = 0;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev,
+					  sizeof(struct magn_3d_state));
+	if (indio_dev == NULL)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	magn_state = iio_priv(indio_dev);
+	magn_state->common_attributes.hsdev = hsdev;
+	magn_state->common_attributes.pdev = pdev;
+
+	ret = hid_sensor_parse_common_attributes(hsdev,
+				HID_USAGE_SENSOR_COMPASS_3D,
+				&magn_state->common_attributes);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup common attributes\n");
+		return ret;
+	}
+
+	ret = magn_3d_parse_report(pdev, hsdev,
+				&channels, &chan_count,
+				HID_USAGE_SENSOR_COMPASS_3D, magn_state);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse report\n");
+		return ret;
+	}
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels = chan_count;
+	indio_dev->dev.parent = &pdev->dev;
+	indio_dev->info = &magn_3d_info;
+	indio_dev->name = name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		NULL, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+		return ret;
+	}
+	atomic_set(&magn_state->common_attributes.data_ready, 0);
+	ret = hid_sensor_setup_trigger(indio_dev, name,
+					&magn_state->common_attributes);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "trigger setup failed\n");
+		goto error_unreg_buffer_funcs;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_trigger;
+	}
+
+	magn_state->callbacks.send_event = magn_3d_proc_event;
+	magn_state->callbacks.capture_sample = magn_3d_capture_sample;
+	magn_state->callbacks.pdev = pdev;
+	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
+					&magn_state->callbacks);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "callback reg failed\n");
+		goto error_iio_unreg;
+	}
+
+	return ret;
+
+error_iio_unreg:
+	iio_device_unregister(indio_dev);
+error_remove_trigger:
+	hid_sensor_remove_trigger(&magn_state->common_attributes);
+error_unreg_buffer_funcs:
+	iio_triggered_buffer_cleanup(indio_dev);
+	return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int hid_magn_3d_remove(struct platform_device *pdev)
+{
+	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+	struct magn_3d_state *magn_state = iio_priv(indio_dev);
+
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
+	iio_device_unregister(indio_dev);
+	hid_sensor_remove_trigger(&magn_state->common_attributes);
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	return 0;
+}
+
+static const struct platform_device_id hid_magn_3d_ids[] = {
+	{
+		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
+		.name = "HID-SENSOR-200083",
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, hid_magn_3d_ids);
+
+static struct platform_driver hid_magn_3d_platform_driver = {
+	.id_table = hid_magn_3d_ids,
+	.driver = {
+		.name	= KBUILD_MODNAME,
+		.pm	= &hid_sensor_pm_ops,
+	},
+	.probe		= hid_magn_3d_probe,
+	.remove		= hid_magn_3d_remove,
+};
+module_platform_driver(hid_magn_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c
new file mode 100644
index 0000000..261d517
--- /dev/null
+++ b/drivers/iio/magnetometer/mag3110.c
@@ -0,0 +1,440 @@
+/*
+ * mag3110.c - Support for Freescale MAG3110 magnetometer sensor
+ *
+ * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * 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 0x0e)
+ *
+ * TODO: irq, user offset, oversampling, continuous mode
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/delay.h>
+
+#define MAG3110_STATUS 0x00
+#define MAG3110_OUT_X 0x01 /* MSB first */
+#define MAG3110_OUT_Y 0x03
+#define MAG3110_OUT_Z 0x05
+#define MAG3110_WHO_AM_I 0x07
+#define MAG3110_OFF_X 0x09 /* MSB first */
+#define MAG3110_OFF_Y 0x0b
+#define MAG3110_OFF_Z 0x0d
+#define MAG3110_DIE_TEMP 0x0f
+#define MAG3110_CTRL_REG1 0x10
+#define MAG3110_CTRL_REG2 0x11
+
+#define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
+
+#define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5))
+#define MAG3110_CTRL_DR_SHIFT 5
+#define MAG3110_CTRL_DR_DEFAULT 0
+
+#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */
+#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */
+
+#define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */
+#define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */
+
+#define MAG3110_DEVICE_ID 0xc4
+
+/* Each client has this additional data */
+struct mag3110_data {
+	struct i2c_client *client;
+	struct mutex lock;
+	u8 ctrl_reg1;
+};
+
+static int mag3110_request(struct mag3110_data *data)
+{
+	int ret, tries = 150;
+
+	/* trigger measurement */
+	ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
+		data->ctrl_reg1 | MAG3110_CTRL_TM);
+	if (ret < 0)
+		return ret;
+
+	while (tries-- > 0) {
+		ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS);
+		if (ret < 0)
+			return ret;
+		/* wait for data ready */
+		if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY)
+			break;
+		msleep(20);
+	}
+
+	if (tries < 0) {
+		dev_err(&data->client->dev, "data not ready\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mag3110_read(struct mag3110_data *data, __be16 buf[3])
+{
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = mag3110_request(data);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+	ret = i2c_smbus_read_i2c_block_data(data->client,
+		MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static ssize_t mag3110_show_int_plus_micros(char *buf,
+	const int (*vals)[2], int n)
+{
+	size_t len = 0;
+
+	while (n-- > 0)
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+			"%d.%06d ", vals[n][0], vals[n][1]);
+
+	/* replace trailing space by newline */
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static int mag3110_get_int_plus_micros_index(const int (*vals)[2], int n,
+					int val, int val2)
+{
+	while (n-- > 0)
+		if (val == vals[n][0] && val2 == vals[n][1])
+			return n;
+
+	return -EINVAL;
+}
+
+static const int mag3110_samp_freq[8][2] = {
+	{80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000},
+	{1, 250000}, {0, 625000}
+};
+
+static ssize_t mag3110_show_samp_freq_avail(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8);
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail);
+
+static int mag3110_get_samp_freq_index(struct mag3110_data *data,
+	int val, int val2)
+{
+	return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val,
+		val2);
+}
+
+static int mag3110_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct mag3110_data *data = iio_priv(indio_dev);
+	__be16 buffer[3];
+	int i, ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (iio_buffer_enabled(indio_dev))
+			return -EBUSY;
+
+		switch (chan->type) {
+		case IIO_MAGN: /* in 0.1 uT / LSB */
+			ret = mag3110_read(data, buffer);
+			if (ret < 0)
+				return ret;
+			*val = sign_extend32(
+				be16_to_cpu(buffer[chan->scan_index]), 15);
+			return IIO_VAL_INT;
+		case IIO_TEMP: /* in 1 C / LSB */
+			mutex_lock(&data->lock);
+			ret = mag3110_request(data);
+			if (ret < 0) {
+				mutex_unlock(&data->lock);
+				return ret;
+			}
+			ret = i2c_smbus_read_byte_data(data->client,
+				MAG3110_DIE_TEMP);
+			mutex_unlock(&data->lock);
+			if (ret < 0)
+				return ret;
+			*val = sign_extend32(ret, 7);
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_MAGN:
+			*val = 0;
+			*val2 = 1000;
+			return IIO_VAL_INT_PLUS_MICRO;
+		case IIO_TEMP:
+			*val = 1000;
+			return IIO_VAL_INT;
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
+		*val = mag3110_samp_freq[i][0];
+		*val2 = mag3110_samp_freq[i][1];
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		ret = i2c_smbus_read_word_swapped(data->client,
+			MAG3110_OFF_X +	2 * chan->scan_index);
+		if (ret < 0)
+			return ret;
+		*val = sign_extend32(ret >> 1, 14);
+		return IIO_VAL_INT;
+	}
+	return -EINVAL;
+}
+
+static int mag3110_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	struct mag3110_data *data = iio_priv(indio_dev);
+	int rate;
+
+	if (iio_buffer_enabled(indio_dev))
+		return -EBUSY;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		rate = mag3110_get_samp_freq_index(data, val, val2);
+		if (rate < 0)
+			return -EINVAL;
+
+		data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
+		data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
+		return i2c_smbus_write_byte_data(data->client,
+			MAG3110_CTRL_REG1, data->ctrl_reg1);
+	case IIO_CHAN_INFO_CALIBBIAS:
+		if (val < -10000 || val > 10000)
+			return -EINVAL;
+		return i2c_smbus_write_word_swapped(data->client,
+			MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
+	default:
+		return -EINVAL;
+	}
+}
+
+static irqreturn_t mag3110_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct mag3110_data *data = iio_priv(indio_dev);
+	u8 buffer[16]; /* 3 16-bit channels + 1 byte temp + padding + ts */
+	int ret;
+
+	ret = mag3110_read(data, (__be16 *) buffer);
+	if (ret < 0)
+		goto done;
+
+	if (test_bit(3, indio_dev->active_scan_mask)) {
+		ret = i2c_smbus_read_byte_data(data->client,
+			MAG3110_DIE_TEMP);
+		if (ret < 0)
+			goto done;
+		buffer[6] = ret;
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+		iio_get_time_ns());
+
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+#define MAG3110_CHANNEL(axis, idx) { \
+	.type = IIO_MAGN, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_##axis, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+		BIT(IIO_CHAN_INFO_CALIBBIAS), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+		BIT(IIO_CHAN_INFO_SCALE), \
+	.scan_index = idx, \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = 16, \
+		.storagebits = 16, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+static const struct iio_chan_spec mag3110_channels[] = {
+	MAG3110_CHANNEL(X, 0),
+	MAG3110_CHANNEL(Y, 1),
+	MAG3110_CHANNEL(Z, 2),
+	{
+		.type = IIO_TEMP,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			BIT(IIO_CHAN_INFO_SCALE),
+		.scan_index = 3,
+		.scan_type = {
+			.sign = 's',
+			.realbits = 8,
+			.storagebits = 8,
+			},
+	},
+	IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static struct attribute *mag3110_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group mag3110_group = {
+	.attrs = mag3110_attributes,
+};
+
+static const struct iio_info mag3110_info = {
+	.attrs = &mag3110_group,
+	.read_raw = &mag3110_read_raw,
+	.write_raw = &mag3110_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
+
+static int mag3110_standby(struct mag3110_data *data)
+{
+	return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
+		data->ctrl_reg1 & ~MAG3110_CTRL_AC);
+}
+
+static int mag3110_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct mag3110_data *data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
+	if (ret < 0)
+		return ret;
+	if (ret != MAG3110_DEVICE_ID)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+	mutex_init(&data->lock);
+
+	i2c_set_clientdata(client, indio_dev);
+	indio_dev->info = &mag3110_info;
+	indio_dev->name = id->name;
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = mag3110_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mag3110_channels);
+	indio_dev->available_scan_masks = mag3110_scan_masks;
+
+	data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT;
+	ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG1,
+		data->ctrl_reg1);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
+		MAG3110_CTRL_AUTO_MRST_EN);
+	if (ret < 0)
+		goto standby_on_error;
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+		mag3110_trigger_handler, NULL);
+	if (ret < 0)
+		goto standby_on_error;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		goto buffer_cleanup;
+	return 0;
+
+buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+standby_on_error:
+	mag3110_standby(iio_priv(indio_dev));
+	return ret;
+}
+
+static int mag3110_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	mag3110_standby(iio_priv(indio_dev));
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mag3110_suspend(struct device *dev)
+{
+	return mag3110_standby(iio_priv(i2c_get_clientdata(
+		to_i2c_client(dev))));
+}
+
+static int mag3110_resume(struct device *dev)
+{
+	struct mag3110_data *data = iio_priv(i2c_get_clientdata(
+		to_i2c_client(dev)));
+
+	return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
+		data->ctrl_reg1);
+}
+
+static SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume);
+#define MAG3110_PM_OPS (&mag3110_pm_ops)
+#else
+#define MAG3110_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id mag3110_id[] = {
+	{ "mag3110", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mag3110_id);
+
+static struct i2c_driver mag3110_driver = {
+	.driver = {
+		.name	= "mag3110",
+		.pm	= MAG3110_PM_OPS,
+	},
+	.probe = mag3110_probe,
+	.remove = mag3110_remove,
+	.id_table = mag3110_id,
+};
+module_i2c_driver(mag3110_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c
new file mode 100644
index 0000000..176e14a
--- /dev/null
+++ b/drivers/iio/magnetometer/mmc35240.c
@@ -0,0 +1,595 @@
+/*
+ * MMC35240 - MEMSIC 3-axis Magnetic Sensor
+ *
+ * Copyright (c) 2015, 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 MMC35240 (7-bit I2C slave address 0x30).
+ *
+ * TODO: offset, ACPI, continuous measurement mode, PM
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <linux/pm.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define MMC35240_DRV_NAME "mmc35240"
+#define MMC35240_REGMAP_NAME "mmc35240_regmap"
+
+#define MMC35240_REG_XOUT_L	0x00
+#define MMC35240_REG_XOUT_H	0x01
+#define MMC35240_REG_YOUT_L	0x02
+#define MMC35240_REG_YOUT_H	0x03
+#define MMC35240_REG_ZOUT_L	0x04
+#define MMC35240_REG_ZOUT_H	0x05
+
+#define MMC35240_REG_STATUS	0x06
+#define MMC35240_REG_CTRL0	0x07
+#define MMC35240_REG_CTRL1	0x08
+
+#define MMC35240_REG_ID		0x20
+
+#define MMC35240_STATUS_MEAS_DONE_BIT	BIT(0)
+
+#define MMC35240_CTRL0_REFILL_BIT	BIT(7)
+#define MMC35240_CTRL0_RESET_BIT	BIT(6)
+#define MMC35240_CTRL0_SET_BIT		BIT(5)
+#define MMC35240_CTRL0_CMM_BIT		BIT(1)
+#define MMC35240_CTRL0_TM_BIT		BIT(0)
+
+/* output resolution bits */
+#define MMC35240_CTRL1_BW0_BIT		BIT(0)
+#define MMC35240_CTRL1_BW1_BIT		BIT(1)
+
+#define MMC35240_CTRL1_BW_MASK	 (MMC35240_CTRL1_BW0_BIT | \
+		 MMC35240_CTRL1_BW1_BIT)
+#define MMC35240_CTRL1_BW_SHIFT		0
+
+#define MMC35240_WAIT_CHARGE_PUMP	50000	/* us */
+#define MMC53240_WAIT_SET_RESET		1000	/* us */
+
+/*
+ * Memsic OTP process code piece is put here for reference:
+ *
+ * #define OTP_CONVERT(REG)  ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006
+ * 1) For X axis, the COEFFICIENT is always 1.
+ * 2) For Y axis, the COEFFICIENT is as below:
+ *    f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) |
+ *                                   (reg_data[2] >> 4)) + 1.0;
+ * 3) For Z axis, the COEFFICIENT is as below:
+ *    f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35;
+ * We implemented the OTP logic into driver.
+ */
+
+/* scale = 1000 here for Y otp */
+#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6)
+
+/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */
+#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81)
+
+#define MMC35240_X_COEFF(x)	(x)
+#define MMC35240_Y_COEFF(y)	(y + 1000)
+#define MMC35240_Z_COEFF(z)	(z + 13500)
+
+#define MMC35240_OTP_START_ADDR		0x1B
+
+enum mmc35240_resolution {
+	MMC35240_16_BITS_SLOW = 0, /* 7.92 ms */
+	MMC35240_16_BITS_FAST,     /* 4.08 ms */
+	MMC35240_14_BITS,          /* 2.16 ms */
+	MMC35240_12_BITS,          /* 1.20 ms */
+};
+
+enum mmc35240_axis {
+	AXIS_X = 0,
+	AXIS_Y,
+	AXIS_Z,
+};
+
+static const struct {
+	int sens[3]; /* sensitivity per X, Y, Z axis */
+	int nfo; /* null field output */
+} mmc35240_props_table[] = {
+	/* 16 bits, 125Hz ODR */
+	{
+		{1024, 1024, 1024},
+		32768,
+	},
+	/* 16 bits, 250Hz ODR */
+	{
+		{1024, 1024, 770},
+		32768,
+	},
+	/* 14 bits, 450Hz ODR */
+	{
+		{256, 256, 193},
+		8192,
+	},
+	/* 12 bits, 800Hz ODR */
+	{
+		{64, 64, 48},
+		2048,
+	},
+};
+
+struct mmc35240_data {
+	struct i2c_client *client;
+	struct mutex mutex;
+	struct regmap *regmap;
+	enum mmc35240_resolution res;
+
+	/* OTP compensation */
+	int axis_coef[3];
+	int axis_scale[3];
+};
+
+static const struct {
+	int val;
+	int val2;
+} mmc35240_samp_freq[] = { {1, 500000},
+			   {13, 0},
+			   {25, 0},
+			   {50, 0} };
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1.5 13 25 50");
+
+#define MMC35240_CHANNEL(_axis) { \
+	.type = IIO_MAGN, \
+	.modified = 1, \
+	.channel2 = IIO_MOD_ ## _axis, \
+	.address = AXIS_ ## _axis, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+			BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mmc35240_channels[] = {
+	MMC35240_CHANNEL(X),
+	MMC35240_CHANNEL(Y),
+	MMC35240_CHANNEL(Z),
+};
+
+static struct attribute *mmc35240_attributes[] = {
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static const struct attribute_group mmc35240_attribute_group = {
+	.attrs = mmc35240_attributes,
+};
+
+static int mmc35240_get_samp_freq_index(struct mmc35240_data *data,
+					int val, int val2)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++)
+		if (mmc35240_samp_freq[i].val == val &&
+		    mmc35240_samp_freq[i].val2 == val2)
+			return i;
+	return -EINVAL;
+}
+
+static int mmc35240_hw_set(struct mmc35240_data *data, bool set)
+{
+	int ret;
+	u8 coil_bit;
+
+	/*
+	 * Recharge the capacitor at VCAP pin, requested to be issued
+	 * before a SET/RESET command.
+	 */
+	ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
+				 MMC35240_CTRL0_REFILL_BIT,
+				 MMC35240_CTRL0_REFILL_BIT);
+	if (ret < 0)
+		return ret;
+	usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1);
+
+	if (set)
+		coil_bit = MMC35240_CTRL0_SET_BIT;
+	else
+		coil_bit = MMC35240_CTRL0_RESET_BIT;
+
+	return regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
+				  coil_bit, coil_bit);
+
+}
+
+static int mmc35240_init(struct mmc35240_data *data)
+{
+	int ret, y_convert, z_convert;
+	unsigned int reg_id;
+	u8 otp_data[6];
+
+	ret = regmap_read(data->regmap, MMC35240_REG_ID, &reg_id);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "Error reading product id\n");
+		return ret;
+	}
+
+	dev_dbg(&data->client->dev, "MMC35240 chip id %x\n", reg_id);
+
+	/*
+	 * make sure we restore sensor characteristics, by doing
+	 * a SET/RESET sequence, the axis polarity being naturally
+	 * aligned after RESET
+	 */
+	ret = mmc35240_hw_set(data, true);
+	if (ret < 0)
+		return ret;
+	usleep_range(MMC53240_WAIT_SET_RESET, MMC53240_WAIT_SET_RESET + 1);
+
+	ret = mmc35240_hw_set(data, false);
+	if (ret < 0)
+		return ret;
+
+	/* set default sampling frequency */
+	ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
+				 MMC35240_CTRL1_BW_MASK,
+				 data->res << MMC35240_CTRL1_BW_SHIFT);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR,
+			       (u8 *)otp_data, sizeof(otp_data));
+	if (ret < 0)
+		return ret;
+
+	y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) |
+					   (otp_data[2] >> 4));
+	z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f);
+
+	data->axis_coef[0] = MMC35240_X_COEFF(1);
+	data->axis_coef[1] = MMC35240_Y_COEFF(y_convert);
+	data->axis_coef[2] = MMC35240_Z_COEFF(z_convert);
+
+	data->axis_scale[0] = 1;
+	data->axis_scale[1] = 1000;
+	data->axis_scale[2] = 10000;
+
+	return 0;
+}
+
+static int mmc35240_take_measurement(struct mmc35240_data *data)
+{
+	int ret, tries = 100;
+	unsigned int reg_status;
+
+	ret = regmap_write(data->regmap, MMC35240_REG_CTRL0,
+			   MMC35240_CTRL0_TM_BIT);
+	if (ret < 0)
+		return ret;
+
+	while (tries-- > 0) {
+		ret = regmap_read(data->regmap, MMC35240_REG_STATUS,
+				  &reg_status);
+		if (ret < 0)
+			return ret;
+		if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT)
+			break;
+		/* minimum wait time to complete measurement is 10 ms */
+		usleep_range(10000, 11000);
+	}
+
+	if (tries < 0) {
+		dev_err(&data->client->dev, "data not ready\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
+{
+	int ret;
+
+	ret = mmc35240_take_measurement(data);
+	if (ret < 0)
+		return ret;
+
+	return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, (u8 *)buf,
+				3 * sizeof(__le16));
+}
+
+/**
+ * mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply
+			    compensation for output value.
+ *
+ * @data: device private data
+ * @index: axis index for which we want the conversion
+ * @buf: raw data to be converted, 2 bytes in little endian format
+ * @val: compensated output reading (unit is milli gauss)
+ *
+ * Returns: 0 in case of success, -EINVAL when @index is not valid
+ */
+static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
+				  __le16 buf[], int *val)
+{
+	int raw[3];
+	int sens[3];
+	int nfo;
+
+	raw[AXIS_X] = le16_to_cpu(buf[AXIS_X]);
+	raw[AXIS_Y] = le16_to_cpu(buf[AXIS_Y]);
+	raw[AXIS_Z] = le16_to_cpu(buf[AXIS_Z]);
+
+	sens[AXIS_X] = mmc35240_props_table[data->res].sens[AXIS_X];
+	sens[AXIS_Y] = mmc35240_props_table[data->res].sens[AXIS_Y];
+	sens[AXIS_Z] = mmc35240_props_table[data->res].sens[AXIS_Z];
+
+	nfo = mmc35240_props_table[data->res].nfo;
+
+	switch (index) {
+	case AXIS_X:
+		*val = (raw[AXIS_X] - nfo) * 1000 / sens[AXIS_X];
+		break;
+	case AXIS_Y:
+		*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] -
+			(raw[AXIS_Z] - nfo)  * 1000 / sens[AXIS_Z];
+		break;
+	case AXIS_Z:
+		*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] +
+			(raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z];
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* apply OTP compensation */
+	*val = (*val) * data->axis_coef[index] / data->axis_scale[index];
+
+	return 0;
+}
+
+static int mmc35240_read_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int *val,
+			     int *val2, long mask)
+{
+	struct mmc35240_data *data = iio_priv(indio_dev);
+	int ret, i;
+	unsigned int reg;
+	__le16 buf[3];
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&data->mutex);
+		ret = mmc35240_read_measurement(data, buf);
+		mutex_unlock(&data->mutex);
+		if (ret < 0)
+			return ret;
+		ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val);
+		if (ret < 0)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		mutex_lock(&data->mutex);
+		ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, &reg);
+		mutex_unlock(&data->mutex);
+		if (ret < 0)
+			return ret;
+
+		i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT;
+		if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq))
+			return -EINVAL;
+
+		*val = mmc35240_samp_freq[i].val;
+		*val2 = mmc35240_samp_freq[i].val2;
+		return IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mmc35240_write_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan, int val,
+			      int val2, long mask)
+{
+	struct mmc35240_data *data = iio_priv(indio_dev);
+	int i, ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		i = mmc35240_get_samp_freq_index(data, val, val2);
+		if (i < 0)
+			return -EINVAL;
+		mutex_lock(&data->mutex);
+		ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
+					 MMC35240_CTRL1_BW_MASK,
+					 i << MMC35240_CTRL1_BW_SHIFT);
+		mutex_unlock(&data->mutex);
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info mmc35240_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw	= mmc35240_read_raw,
+	.write_raw	= mmc35240_write_raw,
+	.attrs		= &mmc35240_attribute_group,
+};
+
+static bool mmc35240_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MMC35240_REG_CTRL0:
+	case MMC35240_REG_CTRL1:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool mmc35240_is_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MMC35240_REG_XOUT_L:
+	case MMC35240_REG_XOUT_H:
+	case MMC35240_REG_YOUT_L:
+	case MMC35240_REG_YOUT_H:
+	case MMC35240_REG_ZOUT_L:
+	case MMC35240_REG_ZOUT_H:
+	case MMC35240_REG_STATUS:
+	case MMC35240_REG_ID:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool mmc35240_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MMC35240_REG_CTRL0:
+	case MMC35240_REG_CTRL1:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static struct reg_default mmc35240_reg_defaults[] = {
+	{ MMC35240_REG_CTRL0,  0x00 },
+	{ MMC35240_REG_CTRL1,  0x00 },
+};
+
+static const struct regmap_config mmc35240_regmap_config = {
+	.name = MMC35240_REGMAP_NAME,
+
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = MMC35240_REG_ID,
+	.cache_type = REGCACHE_FLAT,
+
+	.writeable_reg = mmc35240_is_writeable_reg,
+	.readable_reg = mmc35240_is_readable_reg,
+	.volatile_reg = mmc35240_is_volatile_reg,
+
+	.reg_defaults = mmc35240_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(mmc35240_reg_defaults),
+};
+
+static int mmc35240_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct mmc35240_data *data;
+	struct iio_dev *indio_dev;
+	struct regmap *regmap;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	regmap = devm_regmap_init_i2c(client, &mmc35240_regmap_config);
+	if (IS_ERR(regmap)) {
+		dev_err(&client->dev, "regmap initialization failed\n");
+		return PTR_ERR(regmap);
+	}
+
+	data = iio_priv(indio_dev);
+	i2c_set_clientdata(client, indio_dev);
+	data->client = client;
+	data->regmap = regmap;
+	data->res = MMC35240_16_BITS_SLOW;
+
+	mutex_init(&data->mutex);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &mmc35240_info;
+	indio_dev->name = MMC35240_DRV_NAME;
+	indio_dev->channels = mmc35240_channels;
+	indio_dev->num_channels = ARRAY_SIZE(mmc35240_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = mmc35240_init(data);
+	if (ret < 0) {
+		dev_err(&client->dev, "mmc35240 chip init failed\n");
+		return ret;
+	}
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mmc35240_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mmc35240_data *data = iio_priv(indio_dev);
+
+	regcache_cache_only(data->regmap, true);
+
+	return 0;
+}
+
+static int mmc35240_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct mmc35240_data *data = iio_priv(indio_dev);
+	int ret;
+
+	regcache_mark_dirty(data->regmap);
+	ret = regcache_sync_region(data->regmap, MMC35240_REG_CTRL0,
+				   MMC35240_REG_CTRL1);
+	if (ret < 0)
+		dev_err(dev, "Failed to restore control registers\n");
+
+	regcache_cache_only(data->regmap, false);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops mmc35240_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume)
+};
+
+static const struct of_device_id mmc35240_of_match[] = {
+	{ .compatible = "memsic,mmc35240", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mmc35240_of_match);
+
+static const struct acpi_device_id mmc35240_acpi_match[] = {
+	{"MMC35240", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match);
+
+static const struct i2c_device_id mmc35240_id[] = {
+	{"mmc35240", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, mmc35240_id);
+
+static struct i2c_driver mmc35240_driver = {
+	.driver = {
+		.name = MMC35240_DRV_NAME,
+		.of_match_table = mmc35240_of_match,
+		.pm = &mmc35240_pm_ops,
+		.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
+	},
+	.probe		= mmc35240_probe,
+	.id_table	= mmc35240_id,
+};
+
+module_i2c_driver(mmc35240_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("MEMSIC MMC35240 magnetic sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h
new file mode 100644
index 0000000..9daca46
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -0,0 +1,50 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ * Licensed under the GPL-2.
+ */
+
+#ifndef ST_MAGN_H
+#define ST_MAGN_H
+
+#include <linux/types.h>
+#include <linux/iio/common/st_sensors.h>
+
+#define LSM303DLH_MAGN_DEV_NAME		"lsm303dlh_magn"
+#define LSM303DLHC_MAGN_DEV_NAME	"lsm303dlhc_magn"
+#define LSM303DLM_MAGN_DEV_NAME		"lsm303dlm_magn"
+#define LIS3MDL_MAGN_DEV_NAME		"lis3mdl"
+#define LSM303AGR_MAGN_DEV_NAME		"lsm303agr_magn"
+
+int st_magn_common_probe(struct iio_dev *indio_dev);
+void st_magn_common_remove(struct iio_dev *indio_dev);
+
+#ifdef CONFIG_IIO_BUFFER
+int st_magn_allocate_ring(struct iio_dev *indio_dev);
+void st_magn_deallocate_ring(struct iio_dev *indio_dev);
+int st_magn_trig_set_state(struct iio_trigger *trig, bool state);
+#define ST_MAGN_TRIGGER_SET_STATE (&st_magn_trig_set_state)
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_magn_probe_trigger(struct iio_dev *indio_dev, int irq)
+{
+	return 0;
+}
+static inline void st_magn_remove_trigger(struct iio_dev *indio_dev, int irq)
+{
+	return;
+}
+static inline int st_magn_allocate_ring(struct iio_dev *indio_dev)
+{
+	return 0;
+}
+static inline void st_magn_deallocate_ring(struct iio_dev *indio_dev)
+{
+}
+#define ST_MAGN_TRIGGER_SET_STATE NULL
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_MAGN_H */
diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c
new file mode 100644
index 0000000..ecd3bd0
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_buffer.c
@@ -0,0 +1,96 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.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/common/st_sensors.h>
+#include "st_magn.h"
+
+int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+
+	return st_sensors_set_dataready_irq(indio_dev, state);
+}
+
+static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
+{
+	return st_sensors_set_enable(indio_dev, true);
+}
+
+static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	mdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+	if (mdata->buffer_data == NULL) {
+		err = -ENOMEM;
+		goto allocate_memory_error;
+	}
+
+	err = iio_triggered_buffer_postenable(indio_dev);
+	if (err < 0)
+		goto st_magn_buffer_postenable_error;
+
+	return err;
+
+st_magn_buffer_postenable_error:
+	kfree(mdata->buffer_data);
+allocate_memory_error:
+	return err;
+}
+
+static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	err = iio_triggered_buffer_predisable(indio_dev);
+	if (err < 0)
+		goto st_magn_buffer_predisable_error;
+
+	err = st_sensors_set_enable(indio_dev, false);
+
+st_magn_buffer_predisable_error:
+	kfree(mdata->buffer_data);
+	return err;
+}
+
+static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
+	.preenable = &st_magn_buffer_preenable,
+	.postenable = &st_magn_buffer_postenable,
+	.predisable = &st_magn_buffer_predisable,
+};
+
+int st_magn_allocate_ring(struct iio_dev *indio_dev)
+{
+	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+		&st_sensors_trigger_handler, &st_magn_buffer_setup_ops);
+}
+
+void st_magn_deallocate_ring(struct iio_dev *indio_dev)
+{
+	iio_triggered_buffer_cleanup(indio_dev);
+}
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers buffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
new file mode 100644
index 0000000..b27f014
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -0,0 +1,653 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include "st_magn.h"
+
+#define ST_MAGN_NUMBER_DATA_CHANNELS		3
+
+/* DEFAULT VALUE FOR SENSORS */
+#define ST_MAGN_DEFAULT_OUT_X_H_ADDR		0X03
+#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR		0X07
+#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR		0X05
+
+/* FULLSCALE */
+#define ST_MAGN_FS_AVL_1300MG			1300
+#define ST_MAGN_FS_AVL_1900MG			1900
+#define ST_MAGN_FS_AVL_2500MG			2500
+#define ST_MAGN_FS_AVL_4000MG			4000
+#define ST_MAGN_FS_AVL_4700MG			4700
+#define ST_MAGN_FS_AVL_5600MG			5600
+#define ST_MAGN_FS_AVL_8000MG			8000
+#define ST_MAGN_FS_AVL_8100MG			8100
+#define ST_MAGN_FS_AVL_12000MG			12000
+#define ST_MAGN_FS_AVL_15000MG			15000
+#define ST_MAGN_FS_AVL_16000MG			16000
+
+/* CUSTOM VALUES FOR SENSOR 0 */
+#define ST_MAGN_0_ODR_ADDR			0x00
+#define ST_MAGN_0_ODR_MASK			0x1c
+#define ST_MAGN_0_ODR_AVL_1HZ_VAL		0x00
+#define ST_MAGN_0_ODR_AVL_2HZ_VAL		0x01
+#define ST_MAGN_0_ODR_AVL_3HZ_VAL		0x02
+#define ST_MAGN_0_ODR_AVL_8HZ_VAL		0x03
+#define ST_MAGN_0_ODR_AVL_15HZ_VAL		0x04
+#define ST_MAGN_0_ODR_AVL_30HZ_VAL		0x05
+#define ST_MAGN_0_ODR_AVL_75HZ_VAL		0x06
+#define ST_MAGN_0_ODR_AVL_220HZ_VAL		0x07
+#define ST_MAGN_0_PW_ADDR			0x02
+#define ST_MAGN_0_PW_MASK			0x03
+#define ST_MAGN_0_PW_ON				0x00
+#define ST_MAGN_0_PW_OFF			0x03
+#define ST_MAGN_0_FS_ADDR			0x01
+#define ST_MAGN_0_FS_MASK			0xe0
+#define ST_MAGN_0_FS_AVL_1300_VAL		0x01
+#define ST_MAGN_0_FS_AVL_1900_VAL		0x02
+#define ST_MAGN_0_FS_AVL_2500_VAL		0x03
+#define ST_MAGN_0_FS_AVL_4000_VAL		0x04
+#define ST_MAGN_0_FS_AVL_4700_VAL		0x05
+#define ST_MAGN_0_FS_AVL_5600_VAL		0x06
+#define ST_MAGN_0_FS_AVL_8100_VAL		0x07
+#define ST_MAGN_0_FS_AVL_1300_GAIN_XY		1100
+#define ST_MAGN_0_FS_AVL_1900_GAIN_XY		855
+#define ST_MAGN_0_FS_AVL_2500_GAIN_XY		670
+#define ST_MAGN_0_FS_AVL_4000_GAIN_XY		450
+#define ST_MAGN_0_FS_AVL_4700_GAIN_XY		400
+#define ST_MAGN_0_FS_AVL_5600_GAIN_XY		330
+#define ST_MAGN_0_FS_AVL_8100_GAIN_XY		230
+#define ST_MAGN_0_FS_AVL_1300_GAIN_Z		980
+#define ST_MAGN_0_FS_AVL_1900_GAIN_Z		760
+#define ST_MAGN_0_FS_AVL_2500_GAIN_Z		600
+#define ST_MAGN_0_FS_AVL_4000_GAIN_Z		400
+#define ST_MAGN_0_FS_AVL_4700_GAIN_Z		355
+#define ST_MAGN_0_FS_AVL_5600_GAIN_Z		295
+#define ST_MAGN_0_FS_AVL_8100_GAIN_Z		205
+#define ST_MAGN_0_MULTIREAD_BIT			false
+
+/* CUSTOM VALUES FOR SENSOR 1 */
+#define ST_MAGN_1_WAI_EXP			0x3c
+#define ST_MAGN_1_ODR_ADDR			0x00
+#define ST_MAGN_1_ODR_MASK			0x1c
+#define ST_MAGN_1_ODR_AVL_1HZ_VAL		0x00
+#define ST_MAGN_1_ODR_AVL_2HZ_VAL		0x01
+#define ST_MAGN_1_ODR_AVL_3HZ_VAL		0x02
+#define ST_MAGN_1_ODR_AVL_8HZ_VAL		0x03
+#define ST_MAGN_1_ODR_AVL_15HZ_VAL		0x04
+#define ST_MAGN_1_ODR_AVL_30HZ_VAL		0x05
+#define ST_MAGN_1_ODR_AVL_75HZ_VAL		0x06
+#define ST_MAGN_1_ODR_AVL_220HZ_VAL		0x07
+#define ST_MAGN_1_PW_ADDR			0x02
+#define ST_MAGN_1_PW_MASK			0x03
+#define ST_MAGN_1_PW_ON				0x00
+#define ST_MAGN_1_PW_OFF			0x03
+#define ST_MAGN_1_FS_ADDR			0x01
+#define ST_MAGN_1_FS_MASK			0xe0
+#define ST_MAGN_1_FS_AVL_1300_VAL		0x01
+#define ST_MAGN_1_FS_AVL_1900_VAL		0x02
+#define ST_MAGN_1_FS_AVL_2500_VAL		0x03
+#define ST_MAGN_1_FS_AVL_4000_VAL		0x04
+#define ST_MAGN_1_FS_AVL_4700_VAL		0x05
+#define ST_MAGN_1_FS_AVL_5600_VAL		0x06
+#define ST_MAGN_1_FS_AVL_8100_VAL		0x07
+#define ST_MAGN_1_FS_AVL_1300_GAIN_XY		909
+#define ST_MAGN_1_FS_AVL_1900_GAIN_XY		1169
+#define ST_MAGN_1_FS_AVL_2500_GAIN_XY		1492
+#define ST_MAGN_1_FS_AVL_4000_GAIN_XY		2222
+#define ST_MAGN_1_FS_AVL_4700_GAIN_XY		2500
+#define ST_MAGN_1_FS_AVL_5600_GAIN_XY		3030
+#define ST_MAGN_1_FS_AVL_8100_GAIN_XY		4347
+#define ST_MAGN_1_FS_AVL_1300_GAIN_Z		1020
+#define ST_MAGN_1_FS_AVL_1900_GAIN_Z		1315
+#define ST_MAGN_1_FS_AVL_2500_GAIN_Z		1666
+#define ST_MAGN_1_FS_AVL_4000_GAIN_Z		2500
+#define ST_MAGN_1_FS_AVL_4700_GAIN_Z		2816
+#define ST_MAGN_1_FS_AVL_5600_GAIN_Z		3389
+#define ST_MAGN_1_FS_AVL_8100_GAIN_Z		4878
+#define ST_MAGN_1_MULTIREAD_BIT			false
+
+/* CUSTOM VALUES FOR SENSOR 2 */
+#define ST_MAGN_2_WAI_EXP			0x3d
+#define ST_MAGN_2_ODR_ADDR			0x20
+#define ST_MAGN_2_ODR_MASK			0x1c
+#define ST_MAGN_2_ODR_AVL_1HZ_VAL		0x00
+#define ST_MAGN_2_ODR_AVL_2HZ_VAL		0x01
+#define ST_MAGN_2_ODR_AVL_3HZ_VAL		0x02
+#define ST_MAGN_2_ODR_AVL_5HZ_VAL		0x03
+#define ST_MAGN_2_ODR_AVL_10HZ_VAL		0x04
+#define ST_MAGN_2_ODR_AVL_20HZ_VAL		0x05
+#define ST_MAGN_2_ODR_AVL_40HZ_VAL		0x06
+#define ST_MAGN_2_ODR_AVL_80HZ_VAL		0x07
+#define ST_MAGN_2_PW_ADDR			0x22
+#define ST_MAGN_2_PW_MASK			0x03
+#define ST_MAGN_2_PW_ON				0x00
+#define ST_MAGN_2_PW_OFF			0x03
+#define ST_MAGN_2_FS_ADDR			0x21
+#define ST_MAGN_2_FS_MASK			0x60
+#define ST_MAGN_2_FS_AVL_4000_VAL		0x00
+#define ST_MAGN_2_FS_AVL_8000_VAL		0x01
+#define ST_MAGN_2_FS_AVL_12000_VAL		0x02
+#define ST_MAGN_2_FS_AVL_16000_VAL		0x03
+#define ST_MAGN_2_FS_AVL_4000_GAIN		146
+#define ST_MAGN_2_FS_AVL_8000_GAIN		292
+#define ST_MAGN_2_FS_AVL_12000_GAIN		438
+#define ST_MAGN_2_FS_AVL_16000_GAIN		584
+#define ST_MAGN_2_MULTIREAD_BIT			false
+#define ST_MAGN_2_OUT_X_L_ADDR			0x28
+#define ST_MAGN_2_OUT_Y_L_ADDR			0x2a
+#define ST_MAGN_2_OUT_Z_L_ADDR			0x2c
+
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_MAGN_3_WAI_ADDR			0x4f
+#define ST_MAGN_3_WAI_EXP			0x40
+#define ST_MAGN_3_ODR_ADDR			0x60
+#define ST_MAGN_3_ODR_MASK			0x0c
+#define ST_MAGN_3_ODR_AVL_10HZ_VAL		0x00
+#define ST_MAGN_3_ODR_AVL_20HZ_VAL		0x01
+#define ST_MAGN_3_ODR_AVL_50HZ_VAL		0x02
+#define ST_MAGN_3_ODR_AVL_100HZ_VAL		0x03
+#define ST_MAGN_3_PW_ADDR			0x60
+#define ST_MAGN_3_PW_MASK			0x03
+#define ST_MAGN_3_PW_ON				0x00
+#define ST_MAGN_3_PW_OFF			0x03
+#define ST_MAGN_3_BDU_ADDR			0x62
+#define ST_MAGN_3_BDU_MASK			0x10
+#define ST_MAGN_3_DRDY_IRQ_ADDR			0x62
+#define ST_MAGN_3_DRDY_INT_MASK			0x01
+#define ST_MAGN_3_FS_AVL_15000_GAIN		1500
+#define ST_MAGN_3_MULTIREAD_BIT			false
+#define ST_MAGN_3_OUT_X_L_ADDR			0x68
+#define ST_MAGN_3_OUT_Y_L_ADDR			0x6a
+#define ST_MAGN_3_OUT_Z_L_ADDR			0x6c
+
+static const struct iio_chan_spec st_magn_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_BE, 16, 16,
+			ST_MAGN_DEFAULT_OUT_X_H_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_BE, 16, 16,
+			ST_MAGN_DEFAULT_OUT_Y_H_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_BE, 16, 16,
+			ST_MAGN_DEFAULT_OUT_Z_H_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+			ST_MAGN_2_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+			ST_MAGN_2_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+			ST_MAGN_2_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_magn_3_16bit_channels[] = {
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
+			ST_MAGN_3_OUT_X_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
+			ST_MAGN_3_OUT_Y_L_ADDR),
+	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
+			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+			ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
+			ST_MAGN_3_OUT_Z_L_ADDR),
+	IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_sensor_settings st_magn_sensors_settings[] = {
+	{
+		.wai = 0, /* This sensor has no valid WhoAmI report 0 */
+		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+		.sensors_supported = {
+			[0] = LSM303DLH_MAGN_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
+		.odr = {
+			.addr = ST_MAGN_0_ODR_ADDR,
+			.mask = ST_MAGN_0_ODR_MASK,
+			.odr_avl = {
+				{ 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
+				{ 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
+				{ 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
+				{ 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
+				{ 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
+				{ 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
+				{ 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_MAGN_0_PW_ADDR,
+			.mask = ST_MAGN_0_PW_MASK,
+			.value_on = ST_MAGN_0_PW_ON,
+			.value_off = ST_MAGN_0_PW_OFF,
+		},
+		.fs = {
+			.addr = ST_MAGN_0_FS_ADDR,
+			.mask = ST_MAGN_0_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_1300MG,
+					.value = ST_MAGN_0_FS_AVL_1300_VAL,
+					.gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_1900MG,
+					.value = ST_MAGN_0_FS_AVL_1900_VAL,
+					.gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_2500MG,
+					.value = ST_MAGN_0_FS_AVL_2500_VAL,
+					.gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = ST_MAGN_0_FS_AVL_4000_VAL,
+					.gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
+				},
+				[4] = {
+					.num = ST_MAGN_FS_AVL_4700MG,
+					.value = ST_MAGN_0_FS_AVL_4700_VAL,
+					.gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
+				},
+				[5] = {
+					.num = ST_MAGN_FS_AVL_5600MG,
+					.value = ST_MAGN_0_FS_AVL_5600_VAL,
+					.gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
+				},
+				[6] = {
+					.num = ST_MAGN_FS_AVL_8100MG,
+					.value = ST_MAGN_0_FS_AVL_8100_VAL,
+					.gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
+					.gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
+				},
+			},
+		},
+		.multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_MAGN_1_WAI_EXP,
+		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+		.sensors_supported = {
+			[0] = LSM303DLHC_MAGN_DEV_NAME,
+			[1] = LSM303DLM_MAGN_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
+		.odr = {
+			.addr = ST_MAGN_1_ODR_ADDR,
+			.mask = ST_MAGN_1_ODR_MASK,
+			.odr_avl = {
+				{ 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, },
+				{ 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, },
+				{ 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, },
+				{ 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, },
+				{ 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, },
+				{ 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, },
+				{ 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, },
+				{ 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_MAGN_1_PW_ADDR,
+			.mask = ST_MAGN_1_PW_MASK,
+			.value_on = ST_MAGN_1_PW_ON,
+			.value_off = ST_MAGN_1_PW_OFF,
+		},
+		.fs = {
+			.addr = ST_MAGN_1_FS_ADDR,
+			.mask = ST_MAGN_1_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_1300MG,
+					.value = ST_MAGN_1_FS_AVL_1300_VAL,
+					.gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_1900MG,
+					.value = ST_MAGN_1_FS_AVL_1900_VAL,
+					.gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_2500MG,
+					.value = ST_MAGN_1_FS_AVL_2500_VAL,
+					.gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = ST_MAGN_1_FS_AVL_4000_VAL,
+					.gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z,
+				},
+				[4] = {
+					.num = ST_MAGN_FS_AVL_4700MG,
+					.value = ST_MAGN_1_FS_AVL_4700_VAL,
+					.gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z,
+				},
+				[5] = {
+					.num = ST_MAGN_FS_AVL_5600MG,
+					.value = ST_MAGN_1_FS_AVL_5600_VAL,
+					.gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z,
+				},
+				[6] = {
+					.num = ST_MAGN_FS_AVL_8100MG,
+					.value = ST_MAGN_1_FS_AVL_8100_VAL,
+					.gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY,
+					.gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z,
+				},
+			},
+		},
+		.multi_read_bit = ST_MAGN_1_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_MAGN_2_WAI_EXP,
+		.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+		.sensors_supported = {
+			[0] = LIS3MDL_MAGN_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
+		.odr = {
+			.addr = ST_MAGN_2_ODR_ADDR,
+			.mask = ST_MAGN_2_ODR_MASK,
+			.odr_avl = {
+				{ 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, },
+				{ 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, },
+				{ 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, },
+				{ 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, },
+				{ 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, },
+				{ 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, },
+				{ 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, },
+				{ 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_MAGN_2_PW_ADDR,
+			.mask = ST_MAGN_2_PW_MASK,
+			.value_on = ST_MAGN_2_PW_ON,
+			.value_off = ST_MAGN_2_PW_OFF,
+		},
+		.fs = {
+			.addr = ST_MAGN_2_FS_ADDR,
+			.mask = ST_MAGN_2_FS_MASK,
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_4000MG,
+					.value = ST_MAGN_2_FS_AVL_4000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_4000_GAIN,
+				},
+				[1] = {
+					.num = ST_MAGN_FS_AVL_8000MG,
+					.value = ST_MAGN_2_FS_AVL_8000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_8000_GAIN,
+				},
+				[2] = {
+					.num = ST_MAGN_FS_AVL_12000MG,
+					.value = ST_MAGN_2_FS_AVL_12000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_12000_GAIN,
+				},
+				[3] = {
+					.num = ST_MAGN_FS_AVL_16000MG,
+					.value = ST_MAGN_2_FS_AVL_16000_VAL,
+					.gain = ST_MAGN_2_FS_AVL_16000_GAIN,
+				},
+			},
+		},
+		.multi_read_bit = ST_MAGN_2_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+	{
+		.wai = ST_MAGN_3_WAI_EXP,
+		.wai_addr = ST_MAGN_3_WAI_ADDR,
+		.sensors_supported = {
+			[0] = LSM303AGR_MAGN_DEV_NAME,
+		},
+		.ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
+		.odr = {
+			.addr = ST_MAGN_3_ODR_ADDR,
+			.mask = ST_MAGN_3_ODR_MASK,
+			.odr_avl = {
+				{ 10, ST_MAGN_3_ODR_AVL_10HZ_VAL, },
+				{ 20, ST_MAGN_3_ODR_AVL_20HZ_VAL, },
+				{ 50, ST_MAGN_3_ODR_AVL_50HZ_VAL, },
+				{ 100, ST_MAGN_3_ODR_AVL_100HZ_VAL, },
+			},
+		},
+		.pw = {
+			.addr = ST_MAGN_3_PW_ADDR,
+			.mask = ST_MAGN_3_PW_MASK,
+			.value_on = ST_MAGN_3_PW_ON,
+			.value_off = ST_MAGN_3_PW_OFF,
+		},
+		.fs = {
+			.fs_avl = {
+				[0] = {
+					.num = ST_MAGN_FS_AVL_15000MG,
+					.gain = ST_MAGN_3_FS_AVL_15000_GAIN,
+				},
+			},
+		},
+		.bdu = {
+			.addr = ST_MAGN_3_BDU_ADDR,
+			.mask = ST_MAGN_3_BDU_MASK,
+		},
+		.drdy_irq = {
+			.addr = ST_MAGN_3_DRDY_IRQ_ADDR,
+			.mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
+		},
+		.multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
+		.bootime = 2,
+	},
+};
+
+static int st_magn_read_raw(struct iio_dev *indio_dev,
+			struct iio_chan_spec const *ch, int *val,
+							int *val2, long mask)
+{
+	int err;
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		err = st_sensors_read_info_raw(indio_dev, ch, val);
+		if (err < 0)
+			goto read_error;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		if ((ch->scan_index == ST_SENSORS_SCAN_Z) &&
+					(mdata->current_fullscale->gain2 != 0))
+			*val2 = mdata->current_fullscale->gain2;
+		else
+			*val2 = mdata->current_fullscale->gain;
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = mdata->odr;
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+
+read_error:
+	return err;
+}
+
+static int st_magn_write_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	int err;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		err = st_sensors_set_fullscale_by_gain(indio_dev, val2);
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (val2)
+			return -EINVAL;
+		mutex_lock(&indio_dev->mlock);
+		err = st_sensors_set_odr(indio_dev, val);
+		mutex_unlock(&indio_dev->mlock);
+		return err;
+	default:
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
+static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_magn_scale_available);
+
+static struct attribute *st_magn_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_magn_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_magn_attribute_group = {
+	.attrs = st_magn_attributes,
+};
+
+static const struct iio_info magn_info = {
+	.driver_module = THIS_MODULE,
+	.attrs = &st_magn_attribute_group,
+	.read_raw = &st_magn_read_raw,
+	.write_raw = &st_magn_write_raw,
+	.debugfs_reg_access = &st_sensors_debugfs_reg_access,
+};
+
+#ifdef CONFIG_IIO_TRIGGER
+static const struct iio_trigger_ops st_magn_trigger_ops = {
+	.owner = THIS_MODULE,
+	.set_trigger_state = ST_MAGN_TRIGGER_SET_STATE,
+};
+#define ST_MAGN_TRIGGER_OPS (&st_magn_trigger_ops)
+#else
+#define ST_MAGN_TRIGGER_OPS NULL
+#endif
+
+int st_magn_common_probe(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+	int irq = mdata->get_irq_data_ready(indio_dev);
+	int err;
+
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &magn_info;
+	mutex_init(&mdata->tb.buf_lock);
+
+	st_sensors_power_enable(indio_dev);
+
+	err = st_sensors_check_device_support(indio_dev,
+					ARRAY_SIZE(st_magn_sensors_settings),
+					st_magn_sensors_settings);
+	if (err < 0)
+		return err;
+
+	mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
+	mdata->multiread_bit = mdata->sensor_settings->multi_read_bit;
+	indio_dev->channels = mdata->sensor_settings->ch;
+	indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
+
+	mdata->current_fullscale = (struct st_sensor_fullscale_avl *)
+					&mdata->sensor_settings->fs.fs_avl[0];
+	mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz;
+
+	err = st_sensors_init_sensor(indio_dev, NULL);
+	if (err < 0)
+		return err;
+
+	err = st_magn_allocate_ring(indio_dev);
+	if (err < 0)
+		return err;
+
+	if (irq > 0) {
+		err = st_sensors_allocate_trigger(indio_dev,
+						ST_MAGN_TRIGGER_OPS);
+		if (err < 0)
+			goto st_magn_probe_trigger_error;
+	}
+
+	err = iio_device_register(indio_dev);
+	if (err)
+		goto st_magn_device_register_error;
+
+	dev_info(&indio_dev->dev, "registered magnetometer %s\n",
+		 indio_dev->name);
+
+	return 0;
+
+st_magn_device_register_error:
+	if (irq > 0)
+		st_sensors_deallocate_trigger(indio_dev);
+st_magn_probe_trigger_error:
+	st_magn_deallocate_ring(indio_dev);
+
+	return err;
+}
+EXPORT_SYMBOL(st_magn_common_probe);
+
+void st_magn_common_remove(struct iio_dev *indio_dev)
+{
+	struct st_sensor_data *mdata = iio_priv(indio_dev);
+
+	st_sensors_power_disable(indio_dev);
+
+	iio_device_unregister(indio_dev);
+	if (mdata->get_irq_data_ready(indio_dev) > 0)
+		st_sensors_deallocate_trigger(indio_dev);
+
+	st_magn_deallocate_ring(indio_dev);
+}
+EXPORT_SYMBOL(st_magn_common_remove);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
new file mode 100644
index 0000000..8aa37af
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -0,0 +1,104 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include <linux/iio/common/st_sensors_i2c.h>
+#include "st_magn.h"
+
+#ifdef CONFIG_OF
+static const struct of_device_id st_magn_of_match[] = {
+	{
+		.compatible = "st,lsm303dlh-magn",
+		.data = LSM303DLH_MAGN_DEV_NAME,
+	},
+	{
+		.compatible = "st,lsm303dlhc-magn",
+		.data = LSM303DLHC_MAGN_DEV_NAME,
+	},
+	{
+		.compatible = "st,lsm303dlm-magn",
+		.data = LSM303DLM_MAGN_DEV_NAME,
+	},
+	{
+		.compatible = "st,lis3mdl-magn",
+		.data = LIS3MDL_MAGN_DEV_NAME,
+	},
+	{
+		.compatible = "st,lsm303agr-magn",
+		.data = LSM303AGR_MAGN_DEV_NAME,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_magn_of_match);
+#else
+#define st_magn_of_match NULL
+#endif
+
+static int st_magn_i2c_probe(struct i2c_client *client,
+						const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *mdata;
+	int err;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mdata));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	mdata = iio_priv(indio_dev);
+	st_sensors_of_i2c_probe(client, st_magn_of_match);
+
+	st_sensors_i2c_configure(indio_dev, client, mdata);
+
+	err = st_magn_common_probe(indio_dev);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int st_magn_i2c_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	st_magn_common_remove(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id st_magn_id_table[] = {
+	{ LSM303DLH_MAGN_DEV_NAME },
+	{ LSM303DLHC_MAGN_DEV_NAME },
+	{ LSM303DLM_MAGN_DEV_NAME },
+	{ LIS3MDL_MAGN_DEV_NAME },
+	{ LSM303AGR_MAGN_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
+
+static struct i2c_driver st_magn_driver = {
+	.driver = {
+		.name = "st-magn-i2c",
+		.of_match_table = of_match_ptr(st_magn_of_match),
+	},
+	.probe = st_magn_i2c_probe,
+	.remove = st_magn_i2c_remove,
+	.id_table = st_magn_id_table,
+};
+module_i2c_driver(st_magn_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
new file mode 100644
index 0000000..6325e7d
--- /dev/null
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -0,0 +1,71 @@
+/*
+ * STMicroelectronics magnetometers driver
+ *
+ * Copyright 2012-2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include <linux/iio/common/st_sensors_spi.h>
+#include "st_magn.h"
+
+static int st_magn_spi_probe(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev;
+	struct st_sensor_data *mdata;
+	int err;
+
+	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*mdata));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	mdata = iio_priv(indio_dev);
+
+	st_sensors_spi_configure(indio_dev, spi, mdata);
+
+	err = st_magn_common_probe(indio_dev);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int st_magn_spi_remove(struct spi_device *spi)
+{
+	struct iio_dev *indio_dev = spi_get_drvdata(spi);
+	st_magn_common_remove(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id st_magn_id_table[] = {
+	{ LSM303DLHC_MAGN_DEV_NAME },
+	{ LSM303DLM_MAGN_DEV_NAME },
+	{ LIS3MDL_MAGN_DEV_NAME },
+	{ LSM303AGR_MAGN_DEV_NAME },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, st_magn_id_table);
+
+static struct spi_driver st_magn_driver = {
+	.driver = {
+		.name = "st-magn-spi",
+	},
+	.probe = st_magn_spi_probe,
+	.remove = st_magn_spi_remove,
+	.id_table = st_magn_id_table,
+};
+module_spi_driver(st_magn_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver");
+MODULE_LICENSE("GPL v2");